Skip to content
nickjer edited this page Jun 30, 2016 · 50 revisions

Ideas...

# facl.rb

class FACL
  attr_reader :file, :acls

  def initialize(file:, acls:)
    @file = file
    @acls = acls
  end

  def add(acl)
    FACL.new(file: file, acls: acls + [acl])
  end

  def rem(acl)
    FACL.new(file: file, acls: acls - [acl])
  end

  # @example Check if user 'bob' has 'r' access
  #   my_facl.allow?(principle: User.new('bob'), permission: :r)
  #   #=> true
  def allow?(principle:, permission:)
    acls.each do |acl|
      if acl.match(principle: principle, permission: permission)
        # Check if its an allow or deny acl (may not be both)
        return true  if acl.allow?
        return false if acl.deny?
      end
    end
    return false # default deny
  end
end
# facl_entry.rb

class FACLEntry
  def initialize(principle:, permissions:)
    @principle = principle.to_s
    @permissions = permissions.map(&:to_sym)
  end

  def allow?
    true
  end

  def deny?
    !allow?
  end

  def match(principle:, permission:)
    self.principle == principle && self.permissions.include?(permission.to_sym)
  end

  def ==(other)
    [self.class, principle, permissions.sort] == [other.class, other.principle, other.permissions.sort]
  end

  alias_method :eql, :==

  def hash
    [self.class, principle, permissions.sort].hash
  end
end
# facls/nfs4.rb

module FACLs
  class NFS4FACL < FACL
    def self.getfacl(file)
      # Handle errors here (e.g., file doesn't exist, ...)
      parse `nfs4_getfacl #{file}`
    end

    def self.parse(acl_list)
      acls = []
      acl_list.split("\n").grep(/^[#{NFS4Entry::VALID_TYPE.join}]:/) do |entry|
        acls << NFS4Entry.parse entry
      end
      new(acls: acls)
    end

    def add(acl)
      `nfs4_setfacl -a #{acl} #{file}` # do error checking
      NFS4FACL.getfacl(file)
    end

    def rem(acl)
      `nfs4_setfacl -x #{acl} #{file}` # do error checking
      NFS4FACL.getfacl(file)
    end
  end

  class NFS4FACLEntry < FACLEntry
    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 ]
    DEFAULT_DOMAIN = "osc.edu"

    attr_reader :type, :flags, :principle, :permissions, :domain

    def self.parse(acl_string)
      # Create new NFS4Entry from string
      type, flags, principle, permissions = acl_string.strip.split(":")
      flags = flags.chars
      permissions = permissions.chars
      principle, domain = principle.split("@")
      new(type: type, flags: flags, principle: principle, domain: domain, permissions: permissions)
    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 allow?
      type == :A
    end

    def deny?
      type == :D
    end

    def group_acl?
      flags.include? :g
    end

    def match(principle:, permission:)
      if principle.is_a?(User) && group_acl?
        principle.groups.include?(self.principle) && self.permissions.include?(permission.to_sym)
      elsif principle.is_a?(User) || (principle.is_a?(Group) && group_acl?)
        self.principle == principle && self.permissions.include?(permission.to_sym)
      else
        false
      end
    end

    def to_s
      "#{type}:#{flags.join}:#{principle}@#{domain || DEFAULT_DOMAIN}:#{permissions.join}"
    end

    def ==(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
facl = OodSupport::FACLs::NFS4FACL.getfacl("/path/to/file")

# Check if user "jnicklas" has read permissions to file
user = OodSupport::User.new "jnicklas"
facl.allow?(principle: user, permission: :r)
#=> false

# Add "rx" permissions for user "jnicklas" to file
entry = OodSupport::FACLs::NFS4FACLEntry.new(type: :A, flags: [], principle: "jnicklas", domain: "osc.edu", permissions: [:r, :x])
new_facl = facl.add(entry)
new_facl.allow?(principle: user, permission: :r)
#=> true
Clone this wiki locally