コード例 #1
0
 def _cache_set(self, key, value):
     """Set an item in the cache.  It gets a default ttl."""
     key = self.cache_prefix + key
     with self.cache_pool.reserve() as mc:
         try:
             if not mc.set(key, json.dumps(value), self.cache_timeout):
                 raise BackendError('memcached error')
         except pylibmc.Error, err:
             raise BackendError(str(err))
    def create_user(self, user_name, password, email):
        """Creates a user"""
        if not self.allow_new_users:
            raise BackendError("Creation of new users is disabled")

        user = User()
        if user_name in self._users:
            return False
        id_ = random.randint(1, 2000)
        ids = self._users.values()
        while id_ in ids:
            id_ = random.randint(1, 2000)

        self._users[user_name] = {
            'userid': id_,
            'password': password,
            'mail': email,
            'username': user_name,
            'primaryNode': ''
        }

        user['username'] = user_name
        user['userid'] = id_
        user['mail'] = email
        return user
    def create_user(self, username, password, email, **extra_fields):
        """Creates a user. Returns True on success."""
        if not self.allow_new_users:
            raise BackendError("Creation of new users is disabled")

        password_hash = sscrypt(password)
        values = {
            'username': username,
            'password': password_hash,
            'mail': email,
        }
        for field in ('userid', 'accountStatus', 'mailVerified', 'syncNode'):
            if field in extra_fields:
                values[field] = extra_fields[field]
        query = insert(users).values(**values)
        try:
            res = safe_execute(self._engine, query)
        except IntegrityError:
            #Name already exists
            return False

        if res.rowcount != 1:
            return False

        #need a copy with some of the info for the return value
        userobj = User()
        userobj['username'] = username
        userobj['userid'] = res.lastrowid
        userobj['mail'] = email

        return userobj
コード例 #4
0
    def admin_update_password(self, user, new_password, key=None):
        """Change the user password.

        Uses the admin bind or the user bind if the old password is provided.

        Args:
            user_id: user id
            password: new password
            key: the reset code if needed for proxying

        Returns:
            True if the change was successful, False otherwise
        """
        payload = {'reset_code': key, 'password': new_password}
        username = user.get('username')
        if username is None:
            return False

        url = self._generate_url(username, 'password')
        status, body = self._proxy('POST', url, payload)
        if status == 200:
            return body == 0
        elif status == 400:
            if body == ERROR_INVALID_RESET_CODE:
                raise InvalidCodeError()

        msg = 'Unable to change the user password via sreg. '
        msg += 'Received body:\n%s\n' % str(body)
        msg += 'Received status: %d' % status
        raise BackendError(msg, server=url)
コード例 #5
0
    def delete_user(self, user, credentials=None):
        """Deletes the user

        Args:
            user_id: user id
            credentials: user authentication credentials

        Returns:
            True if the change was successful, False otherwise
        """
        if credentials is None:
            return False
        password = credentials.get("password")
        if password is None:
            return False

        payload = {'password': password}
        username = user.get('username')
        if username is None:
            return False

        url = self._generate_url(username)
        status, body = self._proxy('DELETE', url, payload)
        if status != 200:
            msg = 'Unable to delete the user via sreg. '
            msg += 'Received body:\n%s\n' % str(body)
            msg += 'Received status: %d' % status
            raise BackendError(msg, server=url)

        return body == 0
    def generate_reset_code(self, user, overwrite=False):
        """Returns the a reset code for user.

        Args:
            user: the user object. Must have a value for user[userid]
            overwrite: if set to False, returns the current key if already
                generated

        Returns:
            The reset code, or None if there's a problem.
        """

        username = user.get('username', None)
        if not username:
            raise NoUserIDError()

        status, body = self._proxy(
            'GET', self._generate_url(username, 'password_reset_code'))
        if status == 200 and body == 0:
            raise AlreadySentError()

        if status == 400:
            if body == ERROR_NO_EMAIL_ADDRESS:
                raise NoEmailError()

        raise BackendError()
コード例 #7
0
def safe_execute(engine, *args, **kwargs):
    """Execution wrapper that will raise a HTTPServiceUnavailableError
    on any OperationalError errors and log it.
    """
    try:
        # It's possible for the backend to raise a "connection invalided" error
        # if e.g. the server timed out the connection.  SQLAlchemy purges the
        # the whole connection pool if this happens, so one retry is enough.
        try:
            return execute_with_cleanup(engine, *args, **kwargs)
        except DBAPIError, exc:
            if _is_retryable_db_error(engine, exc):
                logger = CLIENT_HOLDER.default_client
                logger.incr('services.util.safe_execute.retry')
                logger.debug('retrying due to db error %r', exc)
                return execute_with_cleanup(engine, *args, **kwargs)
            else:
                raise
    except Exception, exc:
        if not _is_operational_db_error(engine, exc):
            raise
        err = traceback.format_exc()
        logger = CLIENT_HOLDER.default_client
        logger.error(err)
        # Due to an apparent bug in SQLAlchemy, it's possible for some kinds
        # of connection failure to leave zombies checked out of the pool.
        # Ref:  http://www.sqlalchemy.org/trac/ticket/2695
        # We employ a rather brutal workaround: re-create the entire pool.
        if _get_mysql_error_code(engine, exc) in (2006, 2013, 2014):
            if not exc.connection_invalidated:
                engine.dispose()
        raise BackendError(str(exc))
    def create_user(self, username, password, email):
        """Creates a user. Returns True on success."""
        payload = {'password': password, 'email': email}
        url = self.generate_url(username)
        status, body = self._proxy('PUT', url, payload)
        if status != 200:
            raise BackendError()

        # the result is the username on success
        return body == username
コード例 #9
0
    def authenticate_user(self, user, credentials, attrs=None):
        password = credentials.get("password")
        if not password:
            return None

        username = user.get("username")
        if username is None:
            return None

        code, headers, body = get_url(self.whoami_uri,
                                      "GET",
                                      user=username,
                                      password=password)
        if code == 401:
            return None
        if code != 200:
            logger = CLIENT_HOLDER.default_client
            logger.error("whoami API unexpected behaviour")
            logger.error("  code: %r", code)
            logger.error("  headers: %r", headers)
            logger.error("  body: %r", body)
            raise BackendError("whoami API unexpected behaviour")

        try:
            user_data = json.loads(body)
        except ValueError:
            logger = CLIENT_HOLDER.default_client
            logger.error("whoami API produced invalid JSON")
            logger.error("  code: %r", code)
            logger.error("  headers: %r", headers)
            logger.error("  body: %r", body)
            raise BackendError("whoami API produced invalid JSON")

        user.update({
            "userid": user_data["userid"],
            "username": username,
            "syncNode": user_data.get("syncNode", ""),
        })
        return user["userid"]
    def get_user_node(self, user_id, assign=True):
        if self.single_box:
            return None

        node = super(MozillaAuth, self).get_user_node(user_id, assign=False)
        if node is not None or assign is False:
            return node

        username = self._get_username(user_id)
        url = self.generate_url(username, 'node/weave')
        status, body = self._proxy('GET', url)
        if status != 200:
            raise BackendError()

        return body
コード例 #11
0
    def create_user(self, username, password, email):
        """Creates a user. Returns user on success, false otherwise."""
        if not self.allow_new_users:
            raise BackendError("Creation of new users is disabled")

        payload = {'password': password, 'email': email}
        url = self._generate_url(username)
        status, body = self._proxy('PUT', url, payload)
        if status != 200:
            if body == ERROR_INVALID_WRITE:
                return False
            msg = 'Unable to create the user via sreg. '
            msg += 'Received body:\n%s\n' % str(body)
            msg += 'Received status: %d' % status
            raise BackendError(msg, server=url)

        # the result is the username on success
        if body == username:
            user = User()
            user['username'] = username
            user['email'] = email
            return user
        else:
            return False
コード例 #12
0
    def _cache_get(self, *keys):
        """Get an item from the cache.

        If multiple arguments are given, attempt to load all those keys and
        return data from the first one successfully found.
        """
        keys = [self.cache_prefix + key for key in keys]
        with self.cache_pool.reserve() as mc:
            try:
                values = mc.get_multi(keys)
            except pylibmc.Error, err:
                raise BackendError(str(err))
            for key in keys:
                if key in values:
                    return json.loads(values[key])
            return None
    def clear_reset_code(self, user_id):
        """Clears the reset code

        Args:
            user_id: user id

        Returns:
            True if the change was successful, False otherwise
        """
        # handled by sreg
        username = self._get_username(user_id)
        status, body = self._proxy(
            'DELETE', self.generate_url(username, 'password_reset_code'))
        if status != 200:
            raise BackendError()

        return body == 0
    def generate_reset_code(self, user_id, overwrite=True):
        """Sends a reset code by e-mail

        Args:
            user_id: user id
            overwrite: if True, overwrites an existing code

        Returns:
            True if reset code was generated and sent to user, False otherwise
        """
        username = self._get_username(user_id)
        status, body = self._proxy(
            'GET', self.generate_url(username, 'password_reset_code'))
        if status == 200:
            return body == 0

        if status == 400:
            if body == ERROR_NO_EMAIL_ADDRESS:
                raise NoEmailError()

        raise BackendError()
    def delete_user(self, user_id, password=None):
        """Deletes the user

        Args:
            user_id: user id
            password: user password

        Returns:
            True if the change was successful, False otherwise
        """
        if password is None:
            return False

        payload = {'password': password}
        username = self._get_username(user_id)
        url = self.generate_url(username)
        status, body = self._proxy('DELETE', url, payload)
        if status != 200:
            raise BackendError()

        return body == 0
コード例 #16
0
    def _set_reset_code(self, user_id):
        code = self._generate_reset_code()
        query = delete(reset_codes).where(
            and_(reset_codes.c.username == user_id,
                 reset_codes.c.product == self.product))
        self._engine.execute(query)

        expiration_time = datetime.datetime.now() + \
                            datetime.timedelta(seconds=self.expiration)

        query = insert(reset_codes).values(reset=code,
                                           expiration=expiration_time,
                                           product=self.product,
                                           username=user_id)

        res = safe_execute(self._engine, query)

        if res.rowcount != 1:
            raise BackendError('adding a reset code to the reset table failed')

        return code
    def admin_update_password(self, user_id, new_password, key):
        """Change the user password.

        Uses the admin bind or the user bind if the old password is provided.

        Args:
            user_id: user id
            password: new password
            key: the reset code

        Returns:
            True if the change was successful, False otherwise
        """
        payload = {'reset_code': key, 'password': new_password}
        username = self._get_username(user_id)
        url = self.generate_url(username, 'password')
        status, body = self._proxy('POST', url, payload)
        if status == 200:
            return body == 0
        elif status == 400:
            if body == ERROR_INVALID_RESET_CODE:
                raise InvalidCodeError()

        raise BackendError()
コード例 #18
0
 def delete_user(self, user, credentials=None):
     raise BackendError("Disabled in ProxyUser")
コード例 #19
0
 def admin_update_password(self, user, new_password, code=None):
     raise BackendError("Disabled in ProxyUser")
コード例 #20
0
 def update_password(self, user, credentials, new_password):
     raise BackendError("Disabled in ProxyUser")
コード例 #21
0
 def admin_update_field(self, user, key, value):
     raise BackendError("Disabled in ProxyUser")
コード例 #22
0
 def update_field(self, user, credentials, key, value):
     raise BackendError("Disabled in ProxyUser")
コード例 #23
0
 def get_user_info(self, user, attrs=None):
     raise BackendError("Disabled in ProxyUser")
class ConnectionManager(object):
    """LDAP Connection Manager.

    Provides a context manager for LDAP connectors.
    """
    def __init__(self, uri, bind=None, passwd=None, size=10, retry_max=3,
                 retry_delay=.1, use_tls=False, single_box=False, timeout=-1,
                 connector_cls=StateConnector, use_pool=False,
                 max_lifetime=600, **kw):
        self._pool = []
        self.size = size
        self.retry_max = retry_max
        self.retry_delay = retry_delay
        self.uri = uri
        self.bind = bind
        self.passwd = passwd
        self._pool_lock = RLock()
        self.use_tls = False
        self.timeout = timeout
        self.connector_cls = connector_cls
        self.use_pool = use_pool
        self.max_lifetime = max_lifetime

    def __len__(self):
        return len(self._pool)

    def _match(self, bind, passwd):
        if isinstance(passwd, unicode):
            passwd = passwd.encode('utf8')
        with self._pool_lock:
            inactives = []

            for conn in reversed(self._pool):
                # already in usage
                if conn.active:
                    continue

                # let's check the lifetime
                if conn.get_lifetime() > self.max_lifetime:
                    # this connector has lived for too long,
                    # we want to unbind it and remove it from the pool
                    try:
                        conn.unbind_s()
                    except Exception:
                        pass  # XXX we will see later

                    self._pool.remove(conn)
                    continue

                # we found a connector for this bind
                if conn.who == bind and conn.cred == passwd:
                    conn.active = True
                    return conn

                inactives.append(conn)

            # no connector was available, let's rebind the latest inactive one
            if len(inactives) > 0:
                for conn in inactives:
                    try:
                        self._bind(conn, bind, passwd)
                        return conn
                    except Exception:
                        self._pool.remove(conn)

                return None

        # There are no connector that match
        return None

    def _bind(self, conn, bind, passwd):
        # let's bind
        if self.use_tls:
            conn.start_tls_s()

        if bind is not None:
            conn.simple_bind_s(bind, passwd)

        conn.active = True

    def _create_connector(self, bind, passwd):
        """Creates a connector, binds it, and returns it

        Args:
            - bind: login
            - passwd: password
        """
        tries = 0
        connected = False
        exc = None
        if isinstance(passwd, unicode):
            passwd = passwd.encode('utf8')

        # trying retry_max times in a row with a fresh connector
        while tries < self.retry_max and not connected:
            try:
                conn = self.connector_cls(self.uri, retry_max=self.retry_max,
                                          retry_delay=self.retry_delay)
                conn.timeout = self.timeout
                self._bind(conn, bind, passwd)
                connected = True
            except ldap.LDAPError, exc:
                time.sleep(self.retry_delay)
                tries += 1

        if not connected:
            # pass through logic errors directly.
            if isinstance(exc, (ldap.NO_SUCH_OBJECT,
                                ldap.INVALID_CREDENTIALS,
                                ldap.INVALID_DN_SYNTAX)):
                raise exc

            # operational errors become a BackendError.
            msg = str(exc)
            if isinstance(exc, ldap.TIMEOUT):
                raise BackendTimeoutError(msg, server=self.uri)
            else:
                raise BackendError(msg, backend=conn)
        return conn
 def __call__(self, request):
     raise BackendError("meh", request=request)
コード例 #26
0
 def create_user(self, username, password, email):
     raise BackendError("Disabled in ProxyUser")
コード例 #27
0
 def _get_id(*args):
     raise BackendError()
コード例 #28
0
 def get_user_id(self, user):
     raise BackendError("Disabled in ProxyUser")