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
23 changes: 13 additions & 10 deletions lib/puppetserver/ca/utils/file_system.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,17 @@ 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?
# 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 Expand Up @@ -93,14 +96,14 @@ def write_file(path, one_or_more_objects, mode)
f.puts object.to_s
end
end
FileUtils.chown(@user, @group, path)
FileUtils.chown(@user, @group, path) if running_as_root?
end

# Warning: directory mode should be specified in DIR_MODES above
def ensure_dir(directory)
if !File.exist?(directory)
FileUtils.mkdir_p(directory, mode: DIR_MODES[directory])
FileUtils.chown(@user, @group, directory)
FileUtils.chown(@user, @group, directory) if running_as_root?
end
end
end
Expand Down
104 changes: 104 additions & 0 deletions spec/puppetserver/ca/utils/file_system_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
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 calling chown 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 'creates a symlink and calls chown 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)

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

describe '#write_file' do
it 'writes the file without calling chown when not running as root' do
Dir.mktmpdir do |tmpdir|
path = File.join(tmpdir, 'test.pem')

instance = described_class.instance
allow(instance).to receive(:running_as_root?).and_return(false)
expect(FileUtils).not_to receive(:chown)

instance.write_file(path, 'test content', 0644)

expect(File.read(path)).to include('test content')
end
end

it 'writes the file and calls chown when running as root' do
Dir.mktmpdir do |tmpdir|
path = File.join(tmpdir, 'test.pem')

instance = described_class.instance
allow(instance).to receive(:running_as_root?).and_return(true)
expect(FileUtils).to receive(:chown)

instance.write_file(path, 'test content', 0644)
end
end
end

describe '#ensure_dir' do
it 'creates the directory without calling chown when not running as root' do
Dir.mktmpdir do |tmpdir|
directory = File.join(tmpdir, 'newdir')

instance = described_class.instance
allow(instance).to receive(:running_as_root?).and_return(false)
expect(FileUtils).not_to receive(:chown)

instance.ensure_dir(directory)

expect(File.directory?(directory)).to be(true)
end
end

it 'creates the directory and calls chown when running as root' do
Dir.mktmpdir do |tmpdir|
directory = File.join(tmpdir, 'newdir')

instance = described_class.instance
allow(instance).to receive(:running_as_root?).and_return(true)
expect(FileUtils).to receive(:chown)

instance.ensure_dir(directory)

expect(File.directory?(directory)).to be(true)
end
end
end
end
Loading