osx-acl 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 125c0e75690ae256728513e5372e43e98b51fc20
4
+ data.tar.gz: 853389a6f082a954d68e17f748f31b736b1652f5
5
+ SHA512:
6
+ metadata.gz: 95bb8e5dcb64bdc878f842112685d09ed66734b8bccce8a0738d9e0bb16e037c97f2171c488dfcfb33a7856f036dbcf13cb5d58e0ef6fa9c19595f13d7c2b9b9
7
+ data.tar.gz: 585d3768458692b8c54637fa3e116b63807f773bf778fd870ec5b18387b74540c19ab19b848a92a8d3f41256b4abf18253d356d1f87aedaf534cbc5c934e8972
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.pkg
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in osx-acl.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Kyle Crawford
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # osx-acl
2
+
3
+ Tool and Ruby Library for managing ACLs on OS X
4
+
5
+ The acl_tool can be used to report on and remove orphaned or user-level entries.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'osx-acl'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install osx-acl
22
+
23
+ ## Building
24
+
25
+ `
26
+ rake build
27
+ fpm -s gem -t osxpkg --osxpkg-identifier-prefix org.rubygems.kcrawford
28
+ pkg/osx-acl-1.x.gem`
29
+
30
+ ## Usage
31
+
32
+ From the acl_tool
33
+
34
+ ```
35
+ Usage: /usr/bin/acl_tool [OPTIONS] path
36
+
37
+ Options
38
+ --dry-run outputs what would be done without modifying ACLs
39
+ --exclude x,y,z users to exclude from --remove-user-entries
40
+ --remove-orphans removes orphaned acl entries
41
+ --remove-user-entries removes user acl entries
42
+ --report report existing ACLs
43
+ --version outputs version information for this tool
44
+ --help outputs help information for this tool
45
+ ```
46
+
47
+ From ruby
48
+ ```
49
+ >> require 'acl'
50
+ => true
51
+ >> acl = OSX::ACL.of("tmp")
52
+ => #<OSX::ACL:0x007f92eaabc578 @path="tmp">
53
+ >> acl.entries
54
+ => [#<OSX::ACL::Entry:0x007f92eaaf7510 @components=["user", "FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000046", "_www", "70", "allow", "read"]>]
55
+ >> ace = acl.entries.first
56
+ => #<OSX::ACL::Entry:0x007f92eaaf7510 @components=["user", "FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000046", "_www", "70", "allow", "read"]>
57
+ >> ace.assignment
58
+ => #<OSX::ACL::Assignment:0x007f92ea2a0060 @type="user", @uuid="FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000046", @name="_www", @id="70">
59
+ >> ace.assignment.type
60
+ => "user"
61
+ >> ace.assignment.name
62
+ => "_www"
63
+ >> ace.rules
64
+ => ["allow"]
65
+ >> ace.permissions
66
+ => ["read"]
67
+ >> acl.remove_entry_at_index(0)
68
+ chmod -a# 0 tmp # user:FFFFEEEE-DDDD-CCCC-BBBB-AAAA00000046:_www:70:allow:read
69
+ => true
70
+ ```
71
+
72
+ ## Contributing
73
+
74
+ 1. Fork it ( https://github.com/[my-github-username]/osx-acl/fork )
75
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
76
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
77
+ 4. Push to the branch (`git push origin my-new-feature`)
78
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
data/bin/acl_tool ADDED
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'acl'
5
+ require 'set'
6
+
7
+ def process_recursive(entry, &block)
8
+ yield(entry)
9
+ process_directory(entry) {|next_entry| process_recursive(next_entry,&block) } if File.directory?(entry)
10
+ end
11
+
12
+ def process_directory(entry)
13
+ Dir.entries(entry).each do |sub_entry|
14
+ next if [".",".."].include?(sub_entry)
15
+ yield(File.join(entry, sub_entry))
16
+ end
17
+ end
18
+
19
+ def main
20
+ options = {:exclude => []}
21
+
22
+ opt_parser = OptionParser.new do |opt|
23
+ opt.banner = "Usage: #{$0} [OPTIONS] path"
24
+
25
+ opt.separator ""
26
+ opt.separator "Options"
27
+
28
+ opt.on("--dry-run", "outputs what would be done without modifying ACLs") do
29
+ ENV['OSX_ACL_NOOP'] = "yes"
30
+ end
31
+
32
+ opt.on("--exclude x,y,z", Array, "users to exclude from --remove-user-entries") do |exclusions|
33
+ options[:exclude] = exclusions.map(&:downcase)
34
+ end
35
+
36
+ opt.on("--remove-orphans", "removes orphaned acl entries") do
37
+ options[:action] = "remove_orphans"
38
+ end
39
+
40
+ opt.on("--remove-user-entries", "removes user acl entries") do
41
+ options[:action] = "remove_user_entries"
42
+ end
43
+
44
+ opt.on("--report", "report existing ACLs") do
45
+ options[:action] = "report"
46
+ end
47
+
48
+ opt.on("--version", "outputs version information for this tool") do
49
+ options[:action] = "version"
50
+ end
51
+
52
+ opt.on("--help", "outputs help information for this tool") do
53
+ options[:action] = "help"
54
+ end
55
+
56
+ end
57
+
58
+ opt_parser.parse!
59
+
60
+ if ARGV[0] && !File.exist?(ARGV[0])
61
+ puts opt_parser
62
+ exit 0
63
+ end
64
+ case options[:action]
65
+ when "version"
66
+ puts OSX::ACL::VERSION
67
+ when "help"
68
+ puts opt_parser
69
+ when "remove_orphans"
70
+ process_recursive(ARGV[0]) do |entry|
71
+ number_of_aces_removed = OSX::ACL.of(entry).remove_orphans!
72
+ if number_of_aces_removed > 0
73
+ puts "-#{number_of_aces_removed}: #{File.absolute_path(entry)}"
74
+ end
75
+ end
76
+ when "remove_user_entries"
77
+ process_recursive(ARGV[0]) do |entry|
78
+ begin
79
+ OSX::ACL.of(entry).entries.remove_if do |ace|
80
+ assignment = ace.assignment
81
+ assignment.type == "user" && !options[:exclude].include?(assignment.name.downcase)
82
+ end
83
+ rescue
84
+ $stderr.puts "### Failed to remove ace for #{entry}"
85
+ end
86
+ end
87
+ when "report"
88
+ path_count = 0
89
+ ace_count = 0
90
+ orphan_count = 0
91
+ explicit_paths = Set.new
92
+ user_acl_paths = Set.new
93
+ no_acl_paths = Set.new
94
+ process_recursive(ARGV[0]) do |path|
95
+ path_count += 1
96
+ acl = OSX::ACL.of(path)
97
+ entries = acl.entries
98
+ ace_count += entries.length
99
+ orphan_count += acl.orphans.length
100
+ if acl.entries.empty?
101
+ no_acl_paths << path
102
+ else
103
+ entries.each do |entry|
104
+ if entry.assignment.type == 'user'
105
+ user_acl_paths << path
106
+ end
107
+ if !entry.inherited?
108
+ explicit_paths << path
109
+ end
110
+ end
111
+ end
112
+
113
+ end
114
+ # It should output stats:
115
+ # total files
116
+ # total aces
117
+ # total orphans
118
+ # total explicit
119
+ # total without acl
120
+ # list of explicit
121
+ # list of paths without an acl
122
+
123
+ output = %{
124
+ Total paths: #{path_count}
125
+ Total aces: #{ace_count}
126
+ Total orphans: #{orphan_count}
127
+ Total explicit: #{explicit_paths.length}
128
+ Total without acl: #{no_acl_paths.length}
129
+ Total with user level: #{user_acl_paths.length}
130
+ List of explicit:
131
+ #{explicit_paths.to_a.join("\n")}
132
+ List of paths without an acl:
133
+ #{no_acl_paths.to_a.join("\n")}
134
+ List of user aces:
135
+ #{user_acl_paths.to_a.join("\n")}
136
+ }
137
+ puts output
138
+
139
+ else
140
+ puts opt_parser
141
+ end
142
+ end
143
+
144
+ main()
145
+
@@ -0,0 +1,16 @@
1
+ module OSX
2
+ class ACL
3
+ class Assignment
4
+ attr_accessor :type, :uuid, :name, :id
5
+
6
+ def initialize(components)
7
+ @type, @uuid, @name, @id = components
8
+ end
9
+
10
+ def orphan?
11
+ name.to_s == ""
12
+ end
13
+
14
+ end
15
+ end
16
+ end
data/lib/acl/entry.rb ADDED
@@ -0,0 +1,53 @@
1
+ require 'acl/assignment'
2
+ module OSX
3
+ class ACL
4
+ class Entry
5
+ include Comparable
6
+ attr_accessor :components
7
+
8
+ def self.from_text(text)
9
+ new.tap {|entry| entry.components = text.split(":") }
10
+ end
11
+
12
+ def <=>(other)
13
+ components <=> other.components
14
+ end
15
+
16
+ def to_s
17
+ components.join(":")
18
+ end
19
+
20
+ def inherited=(should_inherit)
21
+ if should_inherit && !inherited?
22
+ rules << "inherited"
23
+ elsif inherited? && !should_inherit
24
+ rules.delete("inherited")
25
+ end
26
+ end
27
+
28
+ def inherited?
29
+ rules.include? "inherited"
30
+ end
31
+
32
+ def orphaned?
33
+ assignment.orphan?
34
+ end
35
+
36
+ def assignment
37
+ ACL::Assignment.new(assignment_components)
38
+ end
39
+
40
+ def assignment_components
41
+ components[0..-3]
42
+ end
43
+
44
+ def permissions
45
+ components.last.split(",")
46
+ end
47
+
48
+ def rules
49
+ components[-2].split(",")
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ module OSX
2
+ class ACL
3
+ VERSION = "1.0.1"
4
+ end
5
+ end
data/lib/acl.rb ADDED
@@ -0,0 +1,137 @@
1
+ require "acl/version"
2
+ require "acl/entry"
3
+ require 'open3'
4
+ require 'delegate'
5
+ require 'shellwords'
6
+
7
+ module OSX
8
+
9
+ require 'ffi'
10
+ module API
11
+ extend FFI::Library
12
+ ffi_lib FFI::Library::LIBC
13
+ attach_function :acl_get_fd, [:int], :pointer
14
+ attach_function :acl_to_text, [:pointer, :pointer], :pointer
15
+ attach_function :acl_valid, [:pointer], :int
16
+ attach_function :acl_free, [:pointer], :int
17
+ end
18
+
19
+ class ACL
20
+
21
+ attr_accessor :path, :entries
22
+
23
+ def self.of(path)
24
+ new.tap {|acl| acl.path = path }
25
+ end
26
+
27
+ def entries
28
+ @entries ||= make_entries
29
+ end
30
+
31
+ def make_entries
32
+ Entries.new(self, entry_lines.map {|line| ACL::Entry.from_text(line) })
33
+ end
34
+
35
+ class Entries < SimpleDelegator
36
+ attr_reader :acl
37
+ def initialize(acl, entries)
38
+ @acl = acl
39
+ super(entries)
40
+ end
41
+ def as_inherited
42
+ map {|entry| entry.inherited = true }
43
+ end
44
+
45
+ def remove_if
46
+ removal_count = 0
47
+ # we reverse the order of the entries so we can remove entries without affecting the index of other entries
48
+ reverse.each_with_index do |entry,index|
49
+ if yield(entry)
50
+ # since entries are reversed, we calculate the actual index
51
+ actual_index = (length - 1) - index
52
+ if acl.remove_entry_at_index(actual_index)
53
+ removal_count += 1
54
+ else
55
+ raise "Failed to remove #{entry} from #{acl.path}"
56
+ end
57
+ end
58
+ end
59
+ removal_count
60
+ end
61
+ end
62
+
63
+ def entry_lines
64
+ file_descriptor, acl_text_ptr, acl_ptr = nil
65
+ begin
66
+ file_descriptor = File.open(path, "r")
67
+ rescue Errno::ELOOP
68
+ return []
69
+ rescue Errno::EOPNOTSUPP
70
+ return []
71
+ rescue Errno::ENOENT
72
+ return []
73
+ end
74
+ acl_ptr = api.acl_get_fd(file_descriptor.fileno)
75
+ acl_text_ptr = api.acl_to_text(acl_ptr, nil)
76
+ return [] if acl_text_ptr.null?
77
+ ace_lines = acl_text_ptr.read_string.split("\n")[1..-1]
78
+ ace_lines
79
+ ensure
80
+ api.acl_free(acl_text_ptr)
81
+ api.acl_free(acl_ptr)
82
+ file_descriptor.close if file_descriptor
83
+ end
84
+
85
+ def remove_orphans!
86
+ entries.remove_if {|entry| entry.orphaned? }
87
+ end
88
+
89
+ def orphans
90
+ entries.select {|e| e.orphaned? }
91
+ end
92
+
93
+ def file_flags
94
+ flags = ""
95
+ Open3.popen3("stat", "-f", "%f", path) do |stdin,stdout,stderr,thread|
96
+ flags = stdout.read
97
+ end
98
+ flags.to_i.to_s(8)
99
+ end
100
+
101
+ # Wraps a file action
102
+ # first removes file flags that would cause the action to fail
103
+ # then yields to the block to perform the action
104
+ # then restores the flags
105
+ def preserving_flags
106
+ original_file_flags = file_flags
107
+ if original_file_flags == "0"
108
+ yield
109
+ else
110
+ begin
111
+ system("chflags", "0", path)
112
+ yield
113
+ ensure
114
+ system("chflags", original_file_flags, path)
115
+ end
116
+ end
117
+ end
118
+
119
+ def remove_entry_at_index(index)
120
+ args = ["chmod", "-a#", index.to_s, path]
121
+ puts "#{args[0]} #{args[1]} #{args[2]} #{Shellwords.escape(args[3])} # #{entries[index].to_s}"
122
+ if ENV['OSX_ACL_NOOP'] == "yes"
123
+ true
124
+ else
125
+ preserving_flags do
126
+ system(*args)
127
+ end
128
+ end
129
+ end
130
+
131
+ def api
132
+ API
133
+ end
134
+
135
+ end
136
+
137
+ end
data/osx-acl.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'acl/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "osx-acl"
8
+ spec.version = OSX::ACL::VERSION
9
+ spec.authors = ["Kyle Crawford"]
10
+ spec.email = ["kcrwfrd@gmail.com"]
11
+ spec.summary = %q{OS X ACL reading and manipulation}
12
+ spec.description = %q{OS X ACL reading and munipluation using ffi and C acl API}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "ffi"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "pry"
27
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper.rb'
2
+
3
+ include OSX
4
+ require 'acl/assignment'
5
+
6
+ def sample_assignment
7
+ ACL::Assignment.new(["group", "ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000046", "_www", "70"])
8
+ end
9
+
10
+ describe ACL::Assignment do
11
+ describe '.new' do
12
+ it "instantiates with array of string components" do
13
+ sample_assignment
14
+ end
15
+ end
16
+
17
+ it "has a type" do
18
+ expect(sample_assignment.type).to eq("group")
19
+ end
20
+
21
+ it "has a uuid" do
22
+ expect(sample_assignment.uuid).to eq("ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000046")
23
+ end
24
+
25
+ it "has a name" do
26
+ expect(sample_assignment.name).to eq("_www")
27
+ end
28
+
29
+ it "has an id" do
30
+ expect(sample_assignment.id).to eq("70")
31
+ end
32
+
33
+ describe "#orphan?" do
34
+ it "is true when name is blank" do
35
+ orphaned_assignment = sample_assignment
36
+ orphaned_assignment.name = ""
37
+ expect(orphaned_assignment.orphan?).to eq(true)
38
+ end
39
+ it "is false when name is not blank" do
40
+ expect(sample_assignment.orphan?).to eq(false)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper.rb'
2
+
3
+ include OSX
4
+ require 'acl/entry'
5
+
6
+ def valid_acl
7
+ ACL::Entry.from_text("group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000046:_www:70:allow,inherited:read,write")
8
+ end
9
+
10
+ describe ACL::Entry do
11
+ describe '.from_text' do
12
+ it "instantiates with text" do
13
+ expect(ACL::Entry.from_text("doh")).to be_kind_of(ACL::Entry)
14
+ end
15
+ end
16
+
17
+ it "has an assignment" do
18
+ expect(valid_acl).to respond_to(:assignment)
19
+ end
20
+
21
+ it "has rules" do
22
+ expect(valid_acl.rules).to eq(["allow", "inherited"])
23
+ end
24
+
25
+ it "has permissions" do
26
+ expect(valid_acl.permissions).to eq(["read","write"])
27
+ end
28
+ end
data/spec/acl_spec.rb ADDED
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ include OSX
4
+ DIR_WITH_VALID_ACES = 'tmp/dir_with_two_aces'
5
+ DIR_WITH_ORPHAN_ACES = 'tmp/dir_with_orphan_aces'
6
+ LOCKED_DIR_WITH_ORPHANS = '/tmp/locked_dir_with_orphans'
7
+
8
+ def make_dir_with_two_aces
9
+ puts "creating dir with two aces..."
10
+ system(
11
+ "
12
+ rm -Rf 'tmp/dir_with_two_aces';
13
+ mkdir -p 'tmp/dir_with_two_aces';
14
+ chmod +a 'group:staff allow read' 'tmp/dir_with_two_aces';
15
+ chmod +a 'group:_www allow read' 'tmp/dir_with_two_aces';
16
+ "
17
+ )
18
+ end
19
+
20
+ def make_locked_dir_with_orphans
21
+ create_acl_with_orphans
22
+ puts "creating locked dir with orphans..."
23
+ system(
24
+ "
25
+ chflags -R 0 '#{LOCKED_DIR_WITH_ORPHANS}';
26
+ rm -Rf '#{LOCKED_DIR_WITH_ORPHANS}';
27
+ mv '#{DIR_WITH_ORPHAN_ACES}' '#{LOCKED_DIR_WITH_ORPHANS}';
28
+ chflags uchg '#{LOCKED_DIR_WITH_ORPHANS}';
29
+ "
30
+ )
31
+ end
32
+
33
+ def create_acl_with_orphans
34
+ make_dir_with_two_aces
35
+ puts "creating invalid aces..."
36
+ system(
37
+ "
38
+ rm -Rf '#{DIR_WITH_ORPHAN_ACES}';
39
+ mv 'tmp/dir_with_two_aces' 'tmp/dir_with_orphan_aces';
40
+ sudo dscl . -create /Users/_acl_orphan_user;
41
+ sudo dscl . -create /Users/_acl_orphan_user PrimaryGroupID 20;
42
+ sudo dscl . -create /Users/_acl_orphan_user UserShell /bin/false;
43
+ sudo dscl . -create /Users/_acl_orphan_user NFSHomeDirectory /dev/null;
44
+ sudo dscl . -create /Users/_acl_orphan_user RealName acl_orphan_user;
45
+ sudo dscl . -create /Users/_acl_orphan_user UniqueID 1020;
46
+ chmod +a 'user:_acl_orphan_user allow read' 'tmp/dir_with_orphan_aces';
47
+ chmod +a# 3 'user:_acl_orphan_user deny write' 'tmp/dir_with_orphan_aces';
48
+ sudo dscl . -delete /Users/_acl_orphan_user;
49
+ "
50
+ )
51
+ end
52
+
53
+ describe ACL do
54
+ it 'has a version number' do
55
+ expect(ACL::VERSION).not_to be nil
56
+ end
57
+
58
+ it 'can be intantiated from a path' do
59
+ expect(ACL.of("/")).to be_kind_of(ACL)
60
+ expect(ACL.of("/tmp").path).to eq("/tmp")
61
+ end
62
+
63
+ it "can read lines of acl entries" do
64
+ expect(ACL.of("/").entry_lines).to respond_to(:each)
65
+ make_dir_with_two_aces
66
+ expect(ACL.of("tmp/dir_with_two_aces").entry_lines.length).to eq(2)
67
+ expect(ACL.of("tmp/dir_with_two_aces").entry_lines.first).to eq("group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000046:_www:70:allow:read")
68
+ expect(ACL.of("tmp/dir_with_two_aces").entry_lines.last).to eq("group:ABCDEFAB-CDEF-ABCD-EFAB-CDEF00000014:staff:20:allow:read")
69
+ end
70
+
71
+ describe "its entries" do
72
+ it 'is an array' do
73
+ expect(ACL.of("/").entries).to respond_to(:each)
74
+ end
75
+
76
+ it 'returns entry instances' do
77
+ make_dir_with_two_aces
78
+ entries = ACL.of("tmp/dir_with_two_aces").entries
79
+ expect(entries.length).to eq(2)
80
+ expect(entries.first).to be_kind_of(ACL::Entry)
81
+ end
82
+
83
+ it "handles symlinks" do
84
+ # create a symlink that points nowhere so File.open fails
85
+ system("ln", "-sf", "/tmp/.would_not_exist_ever_ever", "tmp/symlink")
86
+ expect(ACL.of("tmp/symlink").entries.length).to eq(0)
87
+ end
88
+ end
89
+
90
+ describe "#remove_orphans!" do
91
+ it "returns number of orphans removed" do
92
+ create_acl_with_orphans
93
+ expect(ACL.of(DIR_WITH_ORPHAN_ACES).remove_orphans!).to eq(2)
94
+ make_dir_with_two_aces
95
+ expect(ACL.of(DIR_WITH_VALID_ACES).remove_orphans!).to eq(0)
96
+ end
97
+ it "only removes orphans" do
98
+ create_acl_with_orphans
99
+ ACL.of(DIR_WITH_ORPHAN_ACES).remove_orphans!
100
+ expect(ACL.of(DIR_WITH_ORPHAN_ACES).entries.length).to eq(2)
101
+ expect(ACL.of(DIR_WITH_ORPHAN_ACES).orphans.length).to eq(0)
102
+ end
103
+ it "preserves the order of remaining aces" do
104
+ create_acl_with_orphans
105
+ ACL.of(DIR_WITH_ORPHAN_ACES).remove_orphans!
106
+ expect(ACL.of(DIR_WITH_ORPHAN_ACES).entries.first.assignment.name).to eq("_www")
107
+ expect(ACL.of(DIR_WITH_ORPHAN_ACES).entries.last.assignment.name).to eq("staff")
108
+ end
109
+ it "does nothing if environment variable is set to NOOP" do
110
+ create_acl_with_orphans
111
+ ENV['OSX_ACL_NOOP'] = 'yes'
112
+ acl_with_orphans = ACL.of(DIR_WITH_ORPHAN_ACES)
113
+ acl_with_orphans.remove_orphans!
114
+ expect(acl_with_orphans.remove_orphans!).to eq(2)
115
+ ENV['OSX_ACL_NOOP'] = nil
116
+ end
117
+ it "handles locked files" do
118
+ make_locked_dir_with_orphans
119
+ expect(`stat -f %f #{LOCKED_DIR_WITH_ORPHANS}`.chomp).to eq("2")
120
+ acl = ACL.of(LOCKED_DIR_WITH_ORPHANS)
121
+ expect(acl.remove_orphans!).to eq(2)
122
+ expect(`stat -f %f #{LOCKED_DIR_WITH_ORPHANS}`.chomp).to eq("2")
123
+ end
124
+ end
125
+ end
126
+
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'acl'
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: osx-acl
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Kyle Crawford
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ffi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: OS X ACL reading and munipluation using ffi and C acl API
84
+ email:
85
+ - kcrwfrd@gmail.com
86
+ executables:
87
+ - acl_tool
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - .rspec
93
+ - .travis.yml
94
+ - Gemfile
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - bin/acl_tool
99
+ - lib/acl.rb
100
+ - lib/acl/assignment.rb
101
+ - lib/acl/entry.rb
102
+ - lib/acl/version.rb
103
+ - osx-acl.gemspec
104
+ - spec/acl/assignment_spec.rb
105
+ - spec/acl/entry_spec.rb
106
+ - spec/acl_spec.rb
107
+ - spec/spec_helper.rb
108
+ homepage: ''
109
+ licenses:
110
+ - MIT
111
+ metadata: {}
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 2.0.14
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: OS X ACL reading and manipulation
132
+ test_files:
133
+ - spec/acl/assignment_spec.rb
134
+ - spec/acl/entry_spec.rb
135
+ - spec/acl_spec.rb
136
+ - spec/spec_helper.rb