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

        self.assertIn(service_key['key_id'], storage._service_keys)
        storage.revoke_service_key(TEST_USER_ID, service_key['key_id'])
        self.assertNotIn(service_key['key_id'], storage._service_keys)
Example #2
0
    def get_potential_service_key(self, assertion):
        """Try to fetch the service key the JWT grant claims to be tied to.

        In order for that to happen, we need to decode the JWT first without
        verifying its signature, to extract the client_id that identifies the
        service_key that the JWT claims to be tied to.

        If that key can be found, it then can be used in a later step to
        actually verify the JWT's signature.
        """
        unverified_header = jwt.get_unverified_header(assertion)
        if unverified_header.get('alg') != u'RS256':
            raise InvalidRequest("Only RS256 signature algorithm is supported")

        unverified_claimset = jwt.decode(assertion, verify=False)

        client_id = unverified_claimset['iss']  # Issuer / Client-ID

        storage = CredentialStorage(self.plugin)
        service_key = storage.get_service_key_for_client_id(client_id)

        if service_key is None:
            raise InvalidGrant('No associated key found')

        return service_key
Example #3
0
    def test_issuing_key_displays_private_key_for_download(self, browser):
        browser.login().open(view='@@manage-service-keys')
        browser.find('Issue new service key').click()

        browser.fill({
            'Title': 'My new key',
            'IP Range': '192.168.0.0/16',
        }).find('Issue key').click()

        self.assertEqual(1, len(info_messages()))
        match = re.match('Key created: (.*)', info_messages()[0])
        self.assertTrue(match)

        storage = CredentialStorage(self.plugin)
        self.assertEqual(1, len(storage.list_service_keys(TEST_USER_ID)))
        key = storage.list_service_keys(TEST_USER_ID)[0]

        self.assertTrue('Download your service key.' in browser.contents)
        self.assertTrue('My new key' in browser.contents)

        json_keyfile = browser.css('.json-keyfile').first
        keyfile_data = json.loads(json_keyfile.text)
        self.assertEquals(
            set([
                'key_id', 'client_id', 'issued', 'user_id', 'token_uri',
                'private_key'
            ]), set(keyfile_data.keys()))

        # TODO: Assert on private key contents, if possible
        self.assertEqual(key['key_id'], keyfile_data['key_id'])
        self.assertEqual(key['issued'].isoformat(), keyfile_data['issued'])
        self.assertEqual(TEST_USER_ID, keyfile_data['user_id'])
        self.assertEqual('http://nohost/plone/@@oauth2-token',
                         keyfile_data['token_uri'])
Example #4
0
    def test_get_usage_logs(self):
        storage = CredentialStorage(self.plugin)
        service_key = create(Builder('service_key'))

        self.request._client_addr = '192.168.1.1'
        self.request.environ['HTTP_USER_AGENT'] = 'python-requests/2.18.4'

        with freeze(datetime(2018, 1, 10, 15, 30)) as clock:
            create(Builder('access_token').from_key(service_key))
            clock.backward(hours=1)
            create(Builder('access_token').from_key(service_key))

        # Should be ordered by 'issued'
        self.assertEqual([
            {
                'issued': datetime(2018, 1, 10, 14, 30),
                'user_id': 'test_user_1_',
                'ip_address': '192.168.1.1',
                'user_agent': 'python-requests/2.18.4'
            },
            {
                'issued': datetime(2018, 1, 10, 15, 30),
                'user_id': 'test_user_1_',
                'ip_address': '192.168.1.1',
                'user_agent': 'python-requests/2.18.4'
            },
        ], storage.get_usage_logs(service_key['key_id']))
Example #5
0
    def test_edit_key_form_validates_constraints(self, browser):
        create(
            Builder('service_key').having(title='Some key',
                                          ip_range='192.168.0.0/16'))
        transaction.commit()

        browser.login().open(view='@@manage-service-keys')
        edit_link = browser.css('#table-service-keys tr')[-1].find('Edit')
        edit_link.click()

        browser.fill({
            'Title': '',
            'IP Range': '10.0.5.5/24',
        }).find('Save').click()

        self.assertEqual(['There were some errors.'], error_messages())

        self.assertEqual(
            {
                'IP Range Allowed IP range specification in CIDR notation. '
                'Multiple comma-separated addresses / networks may be supplied.':
                ['Invalid IP range: 10.0.5.5/24 has host bits set'],
                'Title': ['Required input is missing.']
            }, erroneous_fields(browser.forms['form']))

        storage = CredentialStorage(self.plugin)
        users_keys = storage.list_service_keys(TEST_USER_ID)
        self.assertEqual(1, len(users_keys))
        service_key = users_keys[0]

        # Key shouldn't have been updated
        self.assertEqual('Some key', service_key['title'])
        self.assertEqual('192.168.0.0/16', service_key['ip_range'])
Example #6
0
    def test_issuing_key_via_manage_service_keys_view(self, browser):
        browser.login().open(view='@@manage-service-keys')
        browser.find('Issue new service key').click()

        with freeze(datetime(2018, 1, 1, 15, 30)):
            browser.fill({
                'Title': 'My new key',
                'IP Range': '192.168.0.0/16',
            }).find('Issue key').click()

        self.assertEqual(1, len(info_messages()))
        match = re.match('Key created: (.*)', info_messages()[0])
        self.assertTrue(match)
        displayed_key_id = match.group(1)

        storage = CredentialStorage(self.plugin)
        self.assertEqual(1, len(storage.list_service_keys(TEST_USER_ID)))
        service_key = storage.list_service_keys(TEST_USER_ID)[0]

        self.assertEqual(displayed_key_id, service_key['key_id'])
        self.assertEqual('My new key', service_key['title'])
        self.assertEqual(datetime(2018, 1, 1, 15, 30), service_key['issued'])
        self.assertEqual(TEST_USER_ID, service_key['user_id'])
        self.assertIn('client_id', service_key)
        self.assertEqual('192.168.0.0/16', service_key['ip_range'])
        self.assertIn('public_key', service_key)
Example #7
0
    def test_lists_issued_keys(self, browser):
        with freeze(datetime(2017, 1, 1, 15, 30)):
            create(Builder('service_key').having(title='Key 1'))

        with freeze(datetime(2018, 5, 5, 12, 45)):
            create(
                Builder('service_key').having(title='Key 2',
                                              ip_range='192.168.0.0/16'))
        transaction.commit()

        storage = CredentialStorage(self.plugin)
        keys = storage.list_service_keys(TEST_USER_ID)
        client_ids = [k['client_id'] for k in keys]

        browser.login().open(view='@@manage-service-keys')
        table = browser.css('#table-service-keys').first.lists()

        self.assertEquals(
            ['', 'Title', 'Client-ID', 'IP Range', 'Issued', 'Last Used', ''],
            table[0])
        self.assertEquals(
            [
                '', 'Key 1', client_ids[0], '', 'Jan 01, 2017 03:30 PM', '',
                'Edit'
            ],  # noqa
            table[1])
        self.assertEquals(
            [
                '', 'Key 2', client_ids[1], '192.168.0.0/16',
                'May 05, 2018 12:45 PM', '', 'Edit'
            ],  # noqa
            table[2])
Example #8
0
    def test_cant_fetch_logs_for_other_users_key(self):
        storage = CredentialStorage(self.plugin)
        other_key = create(Builder('service_key').having(user_id='other.user'))
        create(Builder('access_token').from_key(other_key))

        with self.assertRaises(Unauthorized):
            storage.get_usage_logs(other_key['key_id'])
Example #9
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))
Example #10
0
    def test_get_service_key_for_client_id(self):
        storage = CredentialStorage(self.plugin)
        service_key = create(Builder('service_key'))

        key_from_storage = storage.get_service_key_for_client_id(
            service_key['client_id'])
        self.assertEqual(service_key, dict(key_from_storage))
Example #11
0
    def test_cant_revoke_other_users_keys(self):
        storage = CredentialStorage(self.plugin)
        other_key = create(Builder('service_key').having(user_id='other.user'))

        self.assertIn(other_key['key_id'], storage._service_keys)
        with self.assertRaises(Unauthorized):
            storage.revoke_service_key(TEST_USER_ID, other_key['key_id'])
Example #12
0
    def test_get_access_token(self):
        storage = CredentialStorage(self.plugin)
        access_token = create(Builder('access_token'))
        storage.add_access_token(access_token)

        token = access_token['token']
        access_token_from_storage = storage.get_access_token(token)
        self.assertEqual(access_token_from_storage,
                         storage._access_tokens[token])
Example #13
0
    def test_get_last_used(self):
        storage = CredentialStorage(self.plugin)
        service_key = create(Builder('service_key'))

        with freeze(datetime(2018, 1, 10, 15, 30)):
            create(Builder('access_token').from_key(service_key))

        self.assertEqual(datetime(2018, 1, 10, 15, 30),
                         storage.get_last_used(service_key['key_id']))
Example #14
0
    def test_cant_fetch_usage_logs_for_other_users(self):
        access_token = create(Builder('access_token'))

        # Attempt to access usage logs for a key issued for a different user
        john = create(Builder('user'))
        login(self.layer['portal'], john.id)

        storage = CredentialStorage(self.plugin)
        with self.assertRaises(Unauthorized):
            storage.get_usage_logs(access_token['key_id'])
    def revoke_selected_keys(self):
        user_id = api.user.get_current().id
        selected_keys = self.request.form.get('selected_keys', [])

        storage = CredentialStorage(self.plugin)
        for key_id in selected_keys:
            storage.revoke_service_key(user_id, key_id)

        api.portal.show_message(_('Keys revoked.'), getRequest())
        return self.request.RESPONSE.redirect(self.main_url)
Example #16
0
    def issue_keypair(self, user_id, title, ip_range=None):
        token_uri = self.get_token_uri()
        private_key, service_key = create_service_key_pair(user_id,
                                                           title,
                                                           token_uri,
                                                           ip_range=ip_range)

        storage = CredentialStorage(self)
        storage.add_service_key(service_key)
        return private_key, service_key
Example #17
0
    def test_add_access_token(self):
        storage = CredentialStorage(self.plugin)
        access_token = create(Builder('access_token'))
        storage.add_access_token(access_token)
        token_from_storage = storage._access_tokens[access_token['token']]
        self.assertIsInstance(token_from_storage, PersistentMapping)

        # raw token string is just used as key, not stored in metadata
        access_token.pop('token')
        self.assertEqual(access_token, dict(token_from_storage))
Example #18
0
    def test_list_service_keys(self):
        storage = CredentialStorage(self.plugin)

        users_keys = [
            create(Builder('service_key')),
            create(Builder('service_key')),
        ]
        create(Builder('service_key').having(user_id='other.user'))
        keys_from_storage = storage.list_service_keys(TEST_USER_ID)

        self.assertEqual(map(dict, keys_from_storage), users_keys)
Example #19
0
    def test_issuing_key_without_title_is_not_allowed(self, browser):
        browser.login().open(view='@@manage-service-keys')
        browser.find('Issue new service key').click()
        browser.find('Issue key').click()

        self.assertEqual(['There were some errors.'], error_messages())

        self.assertEqual({'Title': ['Required input is missing.']},
                         erroneous_fields(browser.forms['form']))

        storage = CredentialStorage(self.plugin)
        self.assertEqual(0, len(storage.list_service_keys(TEST_USER_ID)))
Example #20
0
    def create(self, **kwargs):
        service_key = self.get_or_create_key()
        access_token = self.plugin.issue_access_token(service_key['key_id'],
                                                      service_key['user_id'])

        if self.issued_at:
            # Set issue date of token in storage
            storage = CredentialStorage(self.plugin)
            token_in_storage = storage.get_access_token(access_token['token'])
            token_in_storage['issued'] = self.issued_at

        return access_token
    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)
Example #22
0
    def test_issuing_access_token_logs_key_usage(self):
        self.request._client_addr = '10.0.0.77'
        self.request.environ['HTTP_USER_AGENT'] = 'some-client/1.23.4'

        with freeze(datetime(2018, 1, 1, 15, 30)):
            access_token = create(Builder('access_token'))

        storage = CredentialStorage(self.plugin)
        usage_logs = storage.get_usage_logs(access_token['key_id'])
        self.assertEqual([{
            'issued': datetime(2018, 1, 1, 15, 30),
            'user_id': 'test_user_1_',
            'ip_address': '10.0.0.77',
            'user_agent': 'some-client/1.23.4'
        }], usage_logs)
Example #23
0
    def test_issuing_key_without_ip_range_is_allowed(self, browser):
        browser.login().open(view='@@manage-service-keys')
        browser.find('Issue new service key').click()

        browser.fill({'Title': 'Key without IP range'})
        browser.find('Issue key').click()

        assert_no_error_messages()

        storage = CredentialStorage(self.plugin)
        self.assertEqual(1, len(storage.list_service_keys(TEST_USER_ID)))
        key = storage.list_service_keys(TEST_USER_ID)[0]

        self.assertEqual('Key without IP range', key['title'])
        self.assertEqual(None, key['ip_range'])
Example #24
0
    def test_rotates_usage_logs(self):
        storage = CredentialStorage(self.plugin)
        service_key = create(Builder('service_key'))

        with freeze(datetime(2018, 1, 10, 15, 30)) as clock:
            # First one should be rotated (older than 7 days)
            create(Builder('access_token').from_key(service_key))
            clock.forward(days=10)
            create(Builder('access_token').from_key(service_key))
            clock.forward(hours=2)
            create(Builder('access_token').from_key(service_key))

        self.assertEqual(
            {
                service_key['key_id']: [
                    {
                        'issued': datetime(2018, 1, 20, 15, 30),
                        'user_id': 'test_user_1_',
                        'ip_address': '',
                        'user_agent': None
                    },
                    {
                        'issued': datetime(2018, 1, 20, 17, 30),
                        'user_id': 'test_user_1_',
                        'ip_address': '',
                        'user_agent': None
                    },
                ]
            }, dict(storage._usage_logs))
    def get_key_infos(self):
        user_id = api.user.get_current().id
        storage = CredentialStorage(self.plugin)
        users_keys = storage.list_service_keys(user_id)

        # TODO: Should we maybe include the grant life time as well?
        # (client needs that to set 'exp' claim)

        key_infos = [{
            'client_id': key['client_id'],
            'key_id': key['key_id'],
            'title': key['title'],
            'ip_range': key['ip_range'],
            'issued': key['issued'],
            'last_used': self.get_last_used(key['key_id'])
        } for key in users_keys]

        return key_infos
Example #26
0
    def test_issuing_key_with_invalid_ip_range_is_rejected(self, browser):
        browser.login().open(view='@@manage-service-keys')
        browser.find('Issue new service key').click()
        browser.fill({
            'Title': 'Key with invalid IP range',
            'IP Range': '192.168.5.5/16',
        }).find('Issue key').click()

        self.assertEqual(['There were some errors.'], error_messages())

        self.assertEqual(
            {
                'IP Range Allowed IP range specification in CIDR notation. '
                'Multiple comma-separated addresses / networks may be supplied.':
                ['Invalid IP range: 192.168.5.5/16 has host bits set']
            }, erroneous_fields(browser.forms['form']))

        storage = CredentialStorage(self.plugin)
        self.assertEqual(0, len(storage.list_service_keys(TEST_USER_ID)))
Example #27
0
    def test_revoking_key_via_manage_service_keys_view(self, browser):
        service_key = create(Builder('service_key').having(title='My key'))
        transaction.commit()

        storage = CredentialStorage(self.plugin)
        users_keys = storage.list_service_keys(TEST_USER_ID)
        self.assertEqual(1, len(users_keys))
        stored_service_key = users_keys[0]

        # Guard assertion - make sure the issued key is actually in storage
        self.assertEqual(service_key['key_id'], stored_service_key['key_id'])
        self.assertEqual(service_key['public_key'],
                         stored_service_key['public_key'])

        # Revoke the key
        browser.login().open(view='@@manage-service-keys')
        browser.fill({'My key': True})
        browser.find('Revoke selected keys').click()

        # Got removed from storage
        self.assertEqual([], storage.list_service_keys(TEST_USER_ID))
Example #28
0
    def test_initialization_is_idempotent(self):
        storage = CredentialStorage(self.plugin)
        storage._service_keys['foo'] = 'bar'
        storage._access_tokens['foo'] = 'bar'
        storage._usage_logs['foo'] = 'bar'

        # Multiple initializations shouldn't remove existing data
        storage._initialize_storage()
        self.assertEqual(storage._service_keys['foo'], 'bar')
        self.assertEqual(storage._access_tokens['foo'], 'bar')
        self.assertEqual(storage._usage_logs['foo'], 'bar')
Example #29
0
    def test_editing_key_metadata(self, browser):
        create(Builder('service_key'))
        transaction.commit()

        browser.login().open(view='@@manage-service-keys')
        edit_link = browser.css('#table-service-keys tr')[-1].find('Edit')
        edit_link.click()

        browser.fill({
            'Title': 'New title',
            'IP Range': '10.0.0.0/24',
        }).find('Save').click()

        self.assertEqual(['Data successfully updated.'], info_messages())

        storage = CredentialStorage(self.plugin)
        users_keys = storage.list_service_keys(TEST_USER_ID)
        self.assertEqual(1, len(users_keys))
        key = users_keys[0]

        self.assertEqual('New title', key['title'])
        self.assertEqual('10.0.0.0/24', key['ip_range'])
Example #30
0
    def test_rotate_usage_logs_never_removes_most_recent_entry(self):
        storage = CredentialStorage(self.plugin)
        service_key = create(Builder('service_key'))

        with freeze(datetime(2018, 1, 10, 15, 30)) as clock:
            # Expired enty, but shouldn't be rotated because it's the most
            # recent one
            create(Builder('access_token').from_key(service_key))

            clock.forward(days=60)
            storage.rotate_usage_logs()

        self.assertEqual(
            {
                service_key['key_id']: [
                    {
                        'issued': datetime(2018, 1, 10, 15, 30),
                        'user_id': 'test_user_1_',
                        'ip_address': '',
                        'user_agent': None
                    },
                ]
            }, dict(storage._usage_logs))