-
Notifications
You must be signed in to change notification settings - Fork 1
Home
nickjer edited this page Jul 1, 2016
·
50 revisions
Ideas...
# acl.rb
require 'openstruct'
class ACL
attr_reader :entries, :context
def initialize(entries:, context: {})
@entires = entries
@context = context
end
# @example Check if user 'bob' has 'r' access
# my_acl.allow?(principle: User.new('bob'), permission: :r)
# #=> true
def allow?(principle:, permission:, context: {})
entries.each do |entry|
if entry.match(principle: principle, permission: permission, context: self.context.merge(context))
# 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 false # default deny
end
end
# acl_entry.rb
class ACLEntry
attr_reader :principle, :permissions
def initialize(principle:, permissions:, type: :allow)
@principle = principle.to_s
@permissions = permissions.map(&:to_sym)
@type = type.to_sym
end
def is_allow?
type == :allow
end
def is_deny?
type == :deny
end
def match(principle:, permission:, context: {})
self.principle == principle && self.permissions.include?(permission.to_sym)
end
def ==(other)
[self.class, principle, permissions.sort, type] == [other.class, other.principle, other.permissions.sort, other.type]
end
alias_method :eql, :==
def hash
[self.class, principle, permissions.sort, type].hash
end
end
# acls/nfs4.rb
module ACLs
class NFS4 < ACL
def self.get_facl(file:, context: {})
# Handle errors here (e.g., file doesn't exist, ...)
stat = Pathname.new(file).stat
context = {owner: User.new(stat.uid).name, group: Group.new(stat.gid).name}.merge context
parse(file: `nfs4_getfacl "#{file}"`, context: context)
end
def self.parse(acl:, context: {})
entries = []
acl.to_s.split(/\n|,/).collect(&:strip).grep(/^[^#]/) do |entry|
entries << NFS4ACLEntry.parse entry
end
new(entries: entries, context: context)
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 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, :principle, :permissions, :domain
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: nil, **kwargs)
super(kwargs)
@type = type.to_sym
@flags = flags.map(&:to_sym)
@domain = domain.to_s
# 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:, context: {})
entry_principle = self.principle
entry_principle = context[:owner] if self.principle == "OWNER"
entry_principle = context[: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