def _authorize_user(self, username, key, req):
        """Generates a new token and assigns it to a user.

        username - string
        key - string API key
        req - wsgi.Request object
        """
        ctxt = context.get_admin_context()

        project_id = req.headers.get('X-Auth-Project-Id')
        if project_id is None:
            # If the project_id is not provided in the headers, be forgiving to
            # the user and set project_id based on a valid project of theirs.
            user = self.auth.get_user_from_access_key(key)
            projects = self.auth.get_projects(user.id)
            if not projects:
                raise webob.exc.HTTPUnauthorized()
            project_id = projects[0].id

        try:
            user = self.auth.get_user_from_access_key(key)
        except exception.NotFound:
            LOG.warn(_("User not found with provided API key."))
            user = None

        if user and utils.strcmp_const_time(user.name, username):
            token_hash = hashlib.sha1(
                '%s%s%f' % (username, key, time.time())).hexdigest()
            token_dict = {}
            token_dict['token_hash'] = token_hash
            token_dict['cdn_management_url'] = ''
            os_url = req.url.strip('/')
            os_url += '/' + project_id
            token_dict['server_management_url'] = os_url
            token_dict['storage_url'] = ''
            token_dict['user_id'] = user.id
            token = self.db.auth_token_create(ctxt, token_dict)
            return token, user
        elif user and user.name != username:
            msg = _("Provided API key is valid, but not for user "
                    "'%(username)s'") % locals()
            LOG.warn(msg)

        return None, None
Exemple #2
0
    def _authorize_user(self, username, key, req):
        """Generates a new token and assigns it to a user.

        username - string
        key - string API key
        req - wsgi.Request object
        """
        ctxt = context.get_admin_context()

        project_id = req.headers.get('X-Auth-Project-Id')
        if project_id is None:
            # If the project_id is not provided in the headers, be forgiving to
            # the user and set project_id based on a valid project of theirs.
            user = self.auth.get_user_from_access_key(key)
            projects = self.auth.get_projects(user.id)
            if not projects:
                raise webob.exc.HTTPUnauthorized()
            project_id = projects[0].id

        try:
            user = self.auth.get_user_from_access_key(key)
        except exception.NotFound:
            LOG.warn(_("User not found with provided API key."))
            user = None

        if user and utils.strcmp_const_time(user.name, username):
            token_hash = hashlib.sha1('%s%s%f' % (username, key,
                time.time())).hexdigest()
            token_dict = {}
            token_dict['token_hash'] = token_hash
            token_dict['cdn_management_url'] = ''
            os_url = req.url.strip('/')
            os_url += '/' + project_id
            token_dict['server_management_url'] = os_url
            token_dict['storage_url'] = ''
            token_dict['user_id'] = user.id
            token = self.db.auth_token_create(ctxt, token_dict)
            return token, user
        elif user and user.name != username:
            msg = _("Provided API key is valid, but not for user "
                    "'%(username)s'") % locals()
            LOG.warn(msg)

        return None, None
Exemple #3
0
 def test_strcmp_const_time(self):
     self.assertTrue(utils.strcmp_const_time('abc123', 'abc123'))
     self.assertFalse(utils.strcmp_const_time('a', 'aaaaa'))
     self.assertFalse(utils.strcmp_const_time('ABC123', 'abc123'))
Exemple #4
0
    def authenticate(self, access, signature, params, verb='GET',
                     server_string='127.0.0.1:8773', path='/',
                     check_type='ec2', headers=None):
        """Authenticates AWS request using access key and signature

        If the project is not specified, attempts to authenticate to
        a project with the same name as the user. This way, older tools
        that have no project knowledge will still work.

        :type access: str
        :param access: Access key for user in the form "access:project".

        :type signature: str
        :param signature: Signature of the request.

        :type params: list of str
        :param params: Web paramaters used for the signature.

        :type verb: str
        :param verb: Web request verb ('GET' or 'POST').

        :type server_string: str
        :param server_string: Web request server string.

        :type path: str
        :param path: Web request path.

        :type check_type: str
        :param check_type: Type of signature to check. 'ec2' for EC2, 's3' for
                           S3. Any other value will cause signature not to be
                           checked.

        :type headers: list
        :param headers: HTTP headers passed with the request (only needed for
                        s3 signature checks)

        :rtype: tuple (User, Project)
        :return: User and project that the request represents.
        """
        # TODO(vish): check for valid timestamp
        (access_key, _sep, project_id) = access.partition(':')

        LOG.debug(_('Looking up user: %r'), access_key)
        user = self.get_user_from_access_key(access_key)
        LOG.debug('user: %r', user)
        if user is None:
            LOG.audit(_("Failed authorization for access key %s"), access_key)
            raise exception.AccessKeyNotFound(access_key=access_key)

        # NOTE(vish): if we stop using project name as id we need better
        #             logic to find a default project for user
        if project_id == '':
            LOG.debug(_("Using project name = user name (%s)"), user.name)
            project_id = user.name

        project = self.get_project(project_id)
        if project is None:
            pjid = project_id
            uname = user.name
            LOG.audit(_("failed authorization: no project named %(pjid)s"
                    " (user=%(uname)s)") % locals())
            raise exception.ProjectNotFound(project_id=project_id)
        if not self.is_admin(user) and not self.is_project_member(user,
                                                                  project):
            uname = user.name
            uid = user.id
            pjname = project.name
            pjid = project.id
            LOG.audit(_("Failed authorization: user %(uname)s not admin"
                    " and not member of project %(pjname)s") % locals())
            raise exception.ProjectMembershipNotFound(project_id=pjid,
                                                      user_id=uid)
        if check_type == 's3':
            sign = signer.Signer(user.secret.encode())
            expected_signature = sign.s3_authorization(headers, verb, path)
            LOG.debug(_('user.secret: %s'), user.secret)
            LOG.debug(_('expected_signature: %s'), expected_signature)
            LOG.debug(_('signature: %s'), signature)
            if not utils.strcmp_const_time(signature, expected_signature):
                LOG.audit(_("Invalid signature for user %s"), user.name)
                raise exception.InvalidSignature(signature=signature,
                                                 user=user)
        elif check_type == 'ec2':
            # NOTE(vish): hmac can't handle unicode, so encode ensures that
            #             secret isn't unicode
            expected_signature = signer.Signer(user.secret.encode()).generate(
                    params, verb, server_string, path)
            LOG.debug(_('user.secret: %s'), user.secret)
            LOG.debug(_('expected_signature: %s'), expected_signature)
            LOG.debug(_('signature: %s'), signature)
            if not utils.strcmp_const_time(signature, expected_signature):
                (addr_str, port_str) = utils.parse_server_string(server_string)
                # If the given server_string contains port num, try without it.
                if port_str != '':
                    host_only_signature = signer.Signer(
                        user.secret.encode()).generate(params, verb,
                                                       addr_str, path)
                    LOG.debug(_('host_only_signature: %s'),
                              host_only_signature)
                    if utils.strcmp_const_time(signature, host_only_signature):
                        return (user, project)
                LOG.audit(_("Invalid signature for user %s"), user.name)
                raise exception.InvalidSignature(signature=signature,
                                                 user=user)
        return (user, project)
Exemple #5
0
    def authenticate(self, access, signature, params, verb='GET',
                     server_string='127.0.0.1:8773', path='/',
                     check_type='ec2', headers=None):
        """Authenticates AWS request using access key and signature

        If the project is not specified, attempts to authenticate to
        a project with the same name as the user. This way, older tools
        that have no project knowledge will still work.

        :type access: str
        :param access: Access key for user in the form "access:project".

        :type signature: str
        :param signature: Signature of the request.

        :type params: list of str
        :param params: Web paramaters used for the signature.

        :type verb: str
        :param verb: Web request verb ('GET' or 'POST').

        :type server_string: str
        :param server_string: Web request server string.

        :type path: str
        :param path: Web request path.

        :type check_type: str
        :param check_type: Type of signature to check. 'ec2' for EC2, 's3' for
                           S3. Any other value will cause signature not to be
                           checked.

        :type headers: list
        :param headers: HTTP headers passed with the request (only needed for
                        s3 signature checks)

        :rtype: tuple (User, Project)
        :return: User and project that the request represents.
        """
        # TODO(vish): check for valid timestamp
        (access_key, _sep, project_id) = access.partition(':')

        LOG.debug(_('Looking up user: %r'), access_key)
        user = self.get_user_from_access_key(access_key)
        LOG.debug('user: %r', user)
        if user is None:
            LOG.audit(_("Failed authorization for access key %s"), access_key)
            raise exception.AccessKeyNotFound(access_key=access_key)

        # NOTE(vish): if we stop using project name as id we need better
        #             logic to find a default project for user
        if project_id == '':
            LOG.debug(_("Using project name = user name (%s)"), user.name)
            project_id = user.name

        project = self.get_project(project_id)
        if project is None:
            pjid = project_id
            uname = user.name
            LOG.audit(_("failed authorization: no project named %(pjid)s"
                    " (user=%(uname)s)") % locals())
            raise exception.ProjectNotFound(project_id=project_id)
        if not self.is_admin(user) and not self.is_project_member(user,
                                                                  project):
            uname = user.name
            uid = user.id
            pjname = project.name
            pjid = project.id
            LOG.audit(_("Failed authorization: user %(uname)s not admin"
                    " and not member of project %(pjname)s") % locals())
            raise exception.ProjectMembershipNotFound(project_id=pjid,
                                                      user_id=uid)
        if check_type == 's3':
            sign = signer.Signer(user.secret.encode())
            expected_signature = sign.s3_authorization(headers, verb, path)
            LOG.debug(_('user.secret: %s'), user.secret)
            LOG.debug(_('expected_signature: %s'), expected_signature)
            LOG.debug(_('signature: %s'), signature)
            if not utils.strcmp_const_time(signature, expected_signature):
                LOG.audit(_("Invalid signature for user %s"), user.name)
                raise exception.InvalidSignature(signature=signature,
                                                 user=user)
        elif check_type == 'ec2':
            # NOTE(vish): hmac can't handle unicode, so encode ensures that
            #             secret isn't unicode
            expected_signature = signer.Signer(user.secret.encode()).generate(
                    params, verb, server_string, path)
            LOG.debug(_('user.secret: %s'), user.secret)
            LOG.debug(_('expected_signature: %s'), expected_signature)
            LOG.debug(_('signature: %s'), signature)
            if not utils.strcmp_const_time(signature, expected_signature):
                (addr_str, port_str) = utils.parse_server_string(server_string)
                # If the given server_string contains port num, try without it.
                if port_str != '':
                    host_only_signature = signer.Signer(
                        user.secret.encode()).generate(params, verb,
                                                       addr_str, path)
                    LOG.debug(_('host_only_signature: %s'),
                              host_only_signature)
                    if utils.strcmp_const_time(signature, host_only_signature):
                        return (user, project)
                LOG.audit(_("Invalid signature for user %s"), user.name)
                raise exception.InvalidSignature(signature=signature,
                                                 user=user)
        return (user, project)
Exemple #6
0
 def test_strcmp_const_time(self):
     self.assertTrue(utils.strcmp_const_time("abc123", "abc123"))
     self.assertFalse(utils.strcmp_const_time("a", "aaaaa"))
     self.assertFalse(utils.strcmp_const_time("ABC123", "abc123"))