Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions lib/puppetserver/ca/utils/file_system.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,18 @@ def self.check_for_existing_files(one_or_more_paths)
def self.forcibly_symlink(source, link_target)
FileUtils.remove_dir(link_target, true)
FileUtils.symlink(source, link_target)
# Ensure the symlink has the same ownership as the source.
# This requires using `FileUtils.chown` rather than `File.chown`, as
# the latter will update the ownership of the source rather than the
# link itself.
# Symlink permissions are ignored in favor of the source's permissions,
# so we don't have to change those.
source_info = File.stat(source)
FileUtils.chown(source_info.uid, source_info.gid, link_target)

# Ensure the symlink has the same ownership as the source when running
# with privileges to change ownership.
if instance.running_as_root?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really only called when using the CLI? I didn't dig through the code yet, but I thought starting the openvoxserver also creates the symlink. And the process starts as user puppet, not root. But maybe I am wrong, the the symlink is created by different code.

# This requires using `FileUtils.chown` rather than `File.chown`, as
# the latter will update the ownership of the source rather than the
# link itself.
# Symlink permissions are ignored in favor of the source's permissions,
# so we don't have to change those.
source_info = File.stat(source)
FileUtils.chown(source_info.uid, source_info.gid, link_target)
end
end

def initialize
Expand Down
46 changes: 46 additions & 0 deletions spec/puppetserver/ca/utils/file_system_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
require 'spec_helper'
require 'tmpdir'
require 'fileutils'

require 'puppetserver/ca/utils/file_system'

RSpec.describe Puppetserver::Ca::Utils::FileSystem do
describe '.forcibly_symlink' do
it 'creates a symlink without changing ownership when not running as root' do
Dir.mktmpdir do |tmpdir|
source = File.join(tmpdir, 'new_cadir')
link_target = File.join(tmpdir, 'old_cadir')
FileUtils.mkdir_p(source)
FileUtils.mkdir_p(link_target)

allow(described_class.instance).to receive(:running_as_root?).and_return(false)

expect(FileUtils).not_to receive(:chown)

described_class.forcibly_symlink(source, link_target)

expect(File.symlink?(link_target)).to be(true)
expect(File.readlink(link_target)).to eq(source)
end
end

it 'changes symlink ownership to match the source when running as root' do
Dir.mktmpdir do |tmpdir|
source = File.join(tmpdir, 'new_cadir')
link_target = File.join(tmpdir, 'old_cadir')
FileUtils.mkdir_p(source)
FileUtils.mkdir_p(link_target)

allow(described_class.instance).to receive(:running_as_root?).and_return(true)
allow(File).to receive(:stat).and_call_original

source_info = instance_double(File::Stat, uid: 123, gid: 456)
allow(File).to receive(:stat).with(source).and_return(source_info)

expect(FileUtils).to receive(:chown).with(123, 456, link_target)

described_class.forcibly_symlink(source, link_target)
end
end
end
end