Ejemplo n.º 1
0
 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)
Ejemplo n.º 2
0
 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')
Ejemplo n.º 3
0
 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)
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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))