-
Notifications
You must be signed in to change notification settings - Fork 1
Home
nickjer edited this page Jul 2, 2016
·
50 revisions
Ideas...
# acl.rb
require 'openstruct'
class ACL
attr_reader :entries
def initialize(entries:)
@entries = entries
end
def allow?(principle:)
ordered_check_w_default_deny(principle: principle)
end
private
def ordered_check_w_default_deny(**kwargs)
ordered_check(default: false, **kwargs)
end
def ordered_check_w_default_allow(**kwargs)
ordered_check(default: true, **kwargs)
end
def ordered_check(default:, **kwargs)
entries.each do |entry|
if entry.match(**kwargs)
# Check if its an allow or deny acl entry (may not be both)
return true if entry.is_allow?
return false if entry.is_deny?
end
end
return default
end
end
# acl_entry.rb
class ACLEntry
attr_reader :principle
def self.parse(entry)
new(principle: entry.to_s.strip)
end
def initialize(principle:)
@principle = principle.to_s
end
def is_allow?
true
end
def is_deny?
!is_allow?
end
def match(principle:)
self.principle == principle
end
def to_s
principle
end
def ==(other)
self.to_s == other
end
def eql?(other)
[self.class, principle] == [other.class, other.principle]
end
def hash
[self.class, principle].hash
end
end
# acls/nfs4.rb
module ACLs
class NFS4 < ACL
attr_reader :owner, :group
def self.get_facl(file:)
# Handle errors here (e.g., file doesn't exist, ...)
stat = Pathname.new(file).stat
parse(acl: `nfs4_getfacl "#{file}"`, owner: User.new(stat.uid), group: Group.new(stat.gid))
end
def self.parse(acl:, **kwargs)
entries = []
acl.to_s.strip.split(/\n|,/).collect(&:strip).grep(/^[^#]/) do |entry|
entries << NFS4ACLEntry.parse entry
end
new(entries: entries, **kwargs)
end
def self.add_facl(file:, entry:)
`nfs4_setfacl -a "#{entry}" "#{file}"`
end
def self.rem_facl(file:, entry:)
`nfs4_setfacl -x "#{entry}" "#{file}"`
end
def self.set_facl(file:, acl:)
`nfs4_setfacl -s "#{acl}" "#{file}"`
end
def initialize(owner:, group:, **kwargs)
super(kwargs)
@owner = owner.to_s
@group = group.to_s
end
def allow?(principle:, permission:)
ordered_check_w_default_deny(
principle: principle,
permission: permission,
owner: owner,
group: group
)
end
def to_s
entries.join("\n")
end
end
class NFS4Entry < ACLEntry
VALID_TYPE = %i[ A U D L ]
VALID_FLAG = %i[ f d p i S F g ]
VALID_PERMISSION = %i[ r w a x d D t T n N c C o y ]
REGEX_PATTERN = %r[^(?<type>[#{VALID_TYPE.join}]):(?<flags>[#{VALID_FLAG.join}]*):(?<principle>\w+)@(?<domain>\w*):(?<permissions>[#{VALID_PERMISSION.join}]+)$]
attr_reader :type, :flags, :domain, :permissions
def self.parse(entry)
# Create new acl entry from string
entry = REGEX_PATTERN.match(entry.to_s.strip) do |m|
new(
type: m[:type],
flags: m[:flags].chars,
principle: m[:principle],
domain: m[:domain],
permissions: m[:permissions].chars
)
end
entry ? entry : raise InvalidFormat
end
def initialize(type:, flags:, domain:, permissions:, owner:, group:, **kwargs)
super(kwargs)
@type = type.to_sym
@flags = flags.map(&:to_sym)
@domain = domain.to_s
@permissions = permissions.map(&:to_sym)
# Check for invalid values!
end
def is_allow?
type == :A
end
def is_deny?
type == :D
end
def group_acl?
flags.include? :g
end
def match(principle:, permission:, owner:, group:)
# FIXME: EVERYONE@ -- This is a special catch-all principal which applies to any entity that is not matched by any of the above.
entry_principle = self.principle
entry_principle = owner if self.principle == "OWNER"
entry_principle = group if self.principle == "GROUP"
entry_principle = principle if self.principle == "EVERYONE"
if principle.is_a?(User) && group_acl?
principle.groups.include?(entry_principle) && permissions.include?(permission.to_sym)
elsif principle.is_a?(User) || (principle.is_a?(Group) && group_acl?)
entry_principle == principle && permissions.include?(permission.to_sym)
else
false
end
end
def to_s
"#{type}:#{flags.join}:#{principle}@#{domain}:#{permissions.join}"
end
def ==(other)
self.to_s == other
end
def eql?(other)
[self.class, type, flags.sort, principle, domain, permissions.sort] == [other.class, other.type, other.flags.sort, other.principle, other.domain, other.permissions.sort]
end
def hash
[self.class, type, flags.sort, principle, domain, permissions.sort].hash
end
end
end
acl = OodSupport::ACLs::NFS4.get_facl("/path/to/file")
# Check if user "jnicklas" has read permissions to file
user = OodSupport::User.new "jnicklas"
acl.allow?(principle: user, permission: :r)
#=> false
# Add "rx" permissions for user "jnicklas" to file
entry = OodSupport::ACLs::NFS4Entry.new(type: :A, flags: [], principle: "jnicklas", domain: "osc.edu", permissions: [:r, :x])
OodSupport::ACLs::NFS4.add_facl("/path/to/file", entry)
acl = OodSupport::ACLs::NFS4.get_facl("/path/to/file")
acl.allow?(principle: user, permission: :r)
#=> true