module GitHub class Ldap # A domain represents the base object for an ldap tree. # It encapsulates the operations that you can perform against a tree, authenticating users, for instance. # # This makes possible to reuse a server connection to perform operations with two different domain bases. # # To get a domain, you'll need to create a `Ldap` object and then call the method `domain` with the name of the base. # # For example: # # domain = GitHub::Ldap.new(options).domain("dc=github,dc=com") # class Domain include Filter def initialize(base_name, connection, uid) @base_name, @connection, @uid = base_name, connection, uid end # List the groups in the ldap server that match the configured ones. # # group_names: is an array of group CNs. # # Returns a list of ldap entries for the configured groups. def groups(group_names) search(filter: group_filter(group_names)) end # List the groups that a user is member of. # # user_dn: is the dn for the user ldap entry. # group_names: is an array of group CNs. # # Return an Array with the groups that the given user is member of that belong to the given group list. def membership(user_dn, group_names) search(filter: group_filter(group_names, user_dn)) end # Check if the user is include in any of the configured groups. # # user_dn: is the dn for the user ldap entry. # group_names: is an array of group CNs. # # Returns true if the user belongs to any of the groups. # Returns false otherwise. def is_member?(user_dn, group_names) return true if group_names.nil? return true if group_names.empty? user_membership = membership(user_dn, group_names) !user_membership.empty? end # Check if the user credentials are valid. # # login: is the user's login. # password: is the user's password. # # Returns a Ldap::Entry if the credentials are valid. # Returns nil if the credentials are invalid. def valid_login?(login, password) if user = user?(login) and auth(user, password) return user end end # Check if a user exists based in the `uid`. # # login: is the user's login # # Returns the user if the login matches any `uid`. # Returns nil if there are no matches. def user?(login) rs = search(limit: 1, filter: Net::LDAP::Filter.eq(@uid, login)) rs and rs.first end # Check if a user can be bound with a password. # # user: is a ldap entry representing the user. # password: is the user's password. # # Returns true if the user can be bound. def auth(user, password) @connection.bind(method: :simple, username: user.dn, password: password) end # Authenticate a user with the ldap server. # # login: is the user's login. This method doesn't accept email identifications. # password: is the user's password. # group_names: is an array of group CNs. # # Returns the user info if the credentials are valid and there are no groups configured. # Returns the user info if the credentials are valid and the user belongs to a configured group. # Returns nil if the credentials are invalid def authenticate!(login, password, group_names = nil) user = valid_login?(login, password) return user if user && is_member?(user.dn, group_names) end # Search entries using this domain as base. # # options: is a Hash with the options for the search. # The base option is always overriden. # # Returns an array with the entries found. # Returns nil if there are no entries. def search(options) options[:base] = @base_name options[:attributes] ||= %w{ou cn dn sAMAccountName member} @connection.search(options) end end end end