Exemple #1
0
    def test_get_service_key(self):
        storage = CredentialStorage(self.plugin)
        service_key = create(Builder('service_key'))

        key_from_storage = storage.get_service_key(service_key['key_id'])
        self.assertIsInstance(key_from_storage, PersistentMapping)
        self.assertEqual(service_key, dict(key_from_storage))
class ViewUsageLogs(BrowserView):

    def __call__(self):
        acl_users = api.portal.get().acl_users
        plugin = acl_users['token_auth']
        self.storage = CredentialStorage(plugin)

        self.request.set('disable_border', True)
        self.key_id = self.request.form['key_id']
        key_title = self.get_key_title()
        options = {
            'key_title': key_title,
            'usage_log_retention_days': plugin.usage_log_retention_days,
        }
        return self.index(**options)

    def get_key_title(self):
        service_key = self.storage.get_service_key(self.key_id)
        return service_key['title']

    def get_usage_logs(self):
        """Return usage logs in reverse order (most recent first).

        This is a much more natural order for display (most recent at the top),
        but it means we're sorting twice.

        That should be a negligible penalty though, and we'd much rather incur
        a performance hit when displaying usage logs than when writing them.

        (Standard lists are optimized for appending to the end - inserts at
        the beginning don't perform well. There's deques for that, but the
        persistent implementation in `persistent.ring._DequeRing` isn't
        considered public).
        """
        entries = reversed(self.storage.get_usage_logs(self.key_id))
        return entries
Exemple #3
0
 def get_key(self):
     key_id = self.request.form['key_id']
     storage = CredentialStorage(self.get_plugin())
     key = storage.get_service_key(key_id)
     return key
Exemple #4
0
    def authenticateCredentials(self, credentials):
        """Authenticate a request that contains an OAuth2 bearer access token.

        Implementation of IAuthenticationPlugin that authenticates requests
        that contain a valid OAuth2 bearer access token.

        """
        # Ignore credentials that are not from our extractor
        extractor = credentials.get('extractor')
        if extractor != self.getId():
            # While RFC 6750 says that requests without authentication MUST
            # be answered with a WWW-Authenticate challenge, we can't do that
            # here, since OAuth2 isn't the only authentication mechanism we
            # need to support.
            # See: https://tools.ietf.org/html/rfc6750#section-3
            return None

        received_token = credentials['access_token']
        storage = CredentialStorage(self)

        # Reject unknown or revoked tokens
        if not storage.contains_access_token(received_token):
            # TODO: Should we send an 'invalid_token' error response here?
            return None

        stored_access_token = storage.get_access_token(received_token)

        # Reject expired tokens
        if self.is_expired(stored_access_token):
            # Token expired, send error response according to
            # https://tools.ietf.org/html/rfc6750#section-3.1
            response = getRequest().response
            body = json.dumps({
                'error': 'invalid_token',
                'error_description': 'Access token expired'
            })
            response.setBody(body, lock=True)
            response.setStatus(401, lock=True)

            # TODO: According to RFC 6750, we should also send a
            # WWW-Authenticate: Bearer realm="example"
            # here. Check whether we really want to send this challenge
            return None

        # Fetch service key that the token was tied to (by us)
        service_key = storage.get_service_key(stored_access_token['key_id'],
                                              unrestricted=True)

        ip_range = service_key['ip_range']
        if ip_range is not None:
            client_ip = self.REQUEST.getClientAddr()
            if not client_ip:
                # IP range limitations in place - require that we get a
                # client IP (trusted proxies need to be set up correctly)
                log.warn('Authentication attempt for key with IP range '
                         'restrictions, but failed to get client IP from '
                         'getClientAddr() - check trusted-proxy in zope.conf')
                return None

            if not permitted_ip(client_ip, ip_range):
                log.warn('Authentication attempt from '
                         'disallowed IP %s' % client_ip)
                return None

        # Fetch and verify the user associated with the stored access token
        user_id = stored_access_token['user_id']
        pas = self._getPAS()
        # This only works for users in Plone site, not Zope application root
        info = pas._verifyUser(pas.plugins, user_id=user_id)
        if info is None:
            return None

        mtool = getToolByName(getSite(), 'portal_membership')
        member = mtool.getMemberById(user_id)
        if member is None:
            return None

        return user_id, user_id