class GitHub::Ldap::MembershipValidators::Recursive

Validates membership recursively.

The first step checks whether the entry is a direct member of the given groups. If they are, then we've validated membership successfully.

If not, query for all of the groups that have our groups as members, then we check if the entry is a member of any of those.

This is repeated until the entry is found, recursing and requesting groups in bulk each iteration until we hit the maximum depth allowed and have to give up.

This results in a maximum of `depth` queries (per domain) to validate membership in a list of groups.

Constants

ATTRS
DEFAULT_MAX_DEPTH

Attributes

depth[R]

Internal: The maximum depth to search for membership.

Public Class Methods

new(ldap, groups, options = {}) click to toggle source

Public: Instantiate new search strategy.

  • ldap: GitHub::Ldap object

  • groups: Array of Net::LDAP::Entry group objects

  • options: Hash of options depth: Integer limit of recursion

NOTE: This overrides default behavior to configure `depth`.

# File lib/github/ldap/membership_validators/recursive.rb, line 35
def initialize(ldap, groups, options = {})
  super
  @depth = options[:depth] || DEFAULT_MAX_DEPTH
end

Public Instance Methods

group_dns() click to toggle source

Internal: the group DNs to check against.

Returns an Array of String DNs.

# File lib/github/ldap/membership_validators/recursive.rb, line 111
def group_dns
  @group_dns ||= groups.map(&:dn)
end
member_filter(entry_or_uid, uid = ldap.uid) click to toggle source

Internal: Construct a filter to find groups this entry is a direct member of.

Overloads the included `GitHub::Ldap::Filters#member_filter` method to inject `posixGroup` handling.

Returns a Net::LDAP::Filter object.

Calls superclass method GitHub::Ldap::Filter#member_filter
# File lib/github/ldap/membership_validators/recursive.rb, line 88
def member_filter(entry_or_uid, uid = ldap.uid)
  filter = super(entry_or_uid)

  if ldap.posix_support_enabled?
    if posix_filter = posix_member_filter(entry_or_uid, uid)
      filter |= posix_filter
    end
  end

  filter
end
membership_filter(groups) click to toggle source

Internal: Construct a filter to find groups whose members are the Array of String group DNs passed in.

Returns a String filter.

# File lib/github/ldap/membership_validators/recursive.rb, line 104
def membership_filter(groups)
  groups.map { |entry| member_filter(entry, :cn) }.reduce(:|)
end
perform(entry, depth_override = nil) click to toggle source
# File lib/github/ldap/membership_validators/recursive.rb, line 40
def perform(entry, depth_override = nil)
  if depth_override
    warn "DEPRECATION WARNING: Calling Recursive#perform with a second argument is deprecated."
    warn "Usage:"
    warn "  strategy = GitHub::Ldap::MembershipValidators::Recursive.new \\"
    warn "    ldap, depth: 5"
    warn "  strategy#perform(entry)"
  end

  # short circuit validation if there are no groups to check against
  return true if groups.empty?

  domains.each do |domain|
    # find groups entry is an immediate member of
    membership = domain.search(filter: member_filter(entry), attributes: ATTRS)

    # success if any of these groups match the restricted auth groups
    return true if membership.any? { |entry| group_dns.include?(entry.dn) }

    # give up if the entry has no memberships to recurse
    next if membership.empty?

    # recurse to at most `depth`
    (depth_override || depth).times do |n|
      # find groups whose members include membership groups
      membership = domain.search(filter: membership_filter(membership), attributes: ATTRS)

      # success if any of these groups match the restricted auth groups
      return true if membership.any? { |entry| group_dns.include?(entry.dn) }

      # give up if there are no more membersips to recurse
      break if membership.empty?
    end

    # give up on this base if there are no memberships to test
    next if membership.empty?
  end

  false
end