def get_key(self, username): """ See :py:meth:`.base.KeyStore.get_key`. """ connection = Connection.create(ServerPool(self.primary, self.replicas), user=self.user, password=self.password) with connection: query = Query(connection, self.base_dn) return next( iter(query.filter(cn=username).one().get('sshPublicKey', [])), None)
def __init__(self, server, user_base, bind_dn = None, bind_pass = None): self._user_base = user_base self._server = Server(server) # Create a base query self._query = Query( self._server.authenticate(bind_dn, bind_pass), user_base ).filter(objectClass = 'posixAccount')
def __init__(self, server, group_base, group_suffix, bind_dn = None, bind_pass = None): self._group_suffix = group_suffix # Create a new base query self._query = Query( Server(server).authenticate(bind_dn, bind_pass), group_base ).filter(objectClass = 'posixGroup') # We want to cache the results of our methods for the duration of the # request self.orgs_for_user = lru_cache(maxsize = 16)(self.orgs_for_user) self.members_for_org = lru_cache(maxsize = 16)(self.members_for_org)
class UserManager: """ Class for managing JASMIN users in LDAP. .. note:: An instance of this class can be accessed as a property of the Pyramid request object, i.e. ``r = request.users``. This property is reified, so it is only evaluated once per request. :param server: The address of the LDAP server :param user_base: The DN of the part of the tree to search for entries :param bind_dn: The DN to use when authenticating with the LDAP server (optional - if omitted, an anonymous connection is used, but some functionality may be unavailable) :param bind_pass: The password to use when authenticating with the LDAP server (optional - if ``bind_dn`` is not given, this is ignored) """ def __init__(self, server, user_base, bind_dn = None, bind_pass = None): self._user_base = user_base self._server = Server(server) # Create a base query self._query = Query( self._server.authenticate(bind_dn, bind_pass), user_base ).filter(objectClass = 'posixAccount') def authenticate(self, username, password): """ Attempts to authenticate a user with the given username and password. Returns ``True`` if authentication is successful, ``False`` otherwise. :param username: The username to authenticate :param password: The password to authenticate :returns: ``True`` on success, ``False`` on failure. """ user_dn = 'cn={},{}'.format(username, self._user_base) try: # Try to authenticate with the server as the user self._server.authenticate(user_dn, password).close() return True except AuthenticationError: return False def find_by_username(self, username): """ Returns the user with the given username, or ``None`` if none exists. :param username: The username to search for :returns: Dictionary of user details, or ``None`` """ user = self._query.filter(cn = username).one() if user: return { 'username' : user['cn'][0], 'full_name' : '{} {}'.format( next(iter(user.get('givenName', [])), ''), user['sn'][0] ).strip(), 'email' : next(iter(user.get('mail', [])), ''), 'ssh_key' : next(iter(user.get('sshPublicKey', [])), ''), } else: return None
class MembershipManager: """ Class for managing organisation memberships of JASMIN users. This implementation uses LDAP groups to find relationships between users and organisations. .. note:: An instance of this class can be accessed as a property of the Pyramid request object, i.e. ``r = request.memberships``. This property is reified, so it is only evaluated once per request. :param server: The address of the LDAP server :param group_base: The DN of the part of the tree to search for groups :param group_suffix: The suffix used in LDAP group names :param bind_dn: The DN to use when authenticating with the LDAP server (optional - if omitted, an anonymous connection is used, but some functionality may be unavailable) :param bind_pass: The password to use when authenticating with the LDAP server (optional - if ``bind_dn`` is not given, this is ignored) """ def __init__(self, server, group_base, group_suffix, bind_dn = None, bind_pass = None): self._group_suffix = group_suffix # Create a new base query self._query = Query( Server(server).authenticate(bind_dn, bind_pass), group_base ).filter(objectClass = 'posixGroup') # We want to cache the results of our methods for the duration of the # request self.orgs_for_user = lru_cache(maxsize = 16)(self.orgs_for_user) self.members_for_org = lru_cache(maxsize = 16)(self.members_for_org) def orgs_for_user(self, username): """ Returns a list of organisations for the given user. :param username: The username of the user to find organisations for :returns: List of organisation names """ # Perform a search to find all the groups under our base which have # the correct suffix for which the given username is in the memberUid # attribute q = self._query.filter(cn__endswith = self._group_suffix) \ .filter(memberUid = username) # We want to use the cn with the suffix removed for the organisation name return sorted(e['cn'][0].lower().replace(self._group_suffix, '') for e in q) def members_for_org(self, organisation): """ Returns a list of usernames of users that belong to the organisation. If the organisation doesn't exist, this will raise a :py:class:`NoSuchOrganisationError`. :param organisation: The organisation name :returns: List of usernames """ # Perform a search to find the group whose CN is the organisation with # the correct suffix q = self._query.filter(cn = organisation + self._group_suffix) # If the search result is empty, we want to raise an error # Otherwise, we want to return the values in the memberUid attribute try: return next(iter(q)).get('memberUid', []) except StopIteration: raise NoSuchOrganisationError('{} is not an organisation'.format(organisation))