Add API changes for app cred access rules

bp whitelist-extension-for-app-creds

Change-Id: Ie022e379d03d0309ec320b6947b987758d87fe5d
This commit is contained in:
Colleen Murphy 2019-01-03 13:37:52 +01:00
parent ee7315971c
commit 14c4b177ef
3 changed files with 130 additions and 3 deletions

View File

@ -545,7 +545,8 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
# secret is only exposed after create, it is not stored # secret is only exposed after create, it is not stored
'secret', 'secret',
'links', 'links',
'unrestricted' 'unrestricted',
'access_rules'
]) ])
@staticmethod @staticmethod
@ -608,6 +609,16 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
if app_cred_data.get('expires_at'): if app_cred_data.get('expires_at'):
app_cred_data['expires_at'] = utils.parse_expiration_date( app_cred_data['expires_at'] = utils.parse_expiration_date(
app_cred_data['expires_at']) app_cred_data['expires_at'])
if app_cred_data.get('access_rules'):
for access_rule in app_cred_data['access_rules']:
# If user provides an access rule by ID, it will be looked up
# by ID. If user provides an access rule that is identical to
# an existing one, the ID generated here will be ignored and
# the pre-existing access rule will be used.
if 'id' not in access_rule:
# Generate directly, rather than using _assign_unique_id,
# so that there is no deep copy made
access_rule['id'] = uuid.uuid4().hex
app_cred_data = self._normalize_dict(app_cred_data) app_cred_data = self._normalize_dict(app_cred_data)
app_cred_api = PROVIDERS.application_credential_api app_cred_api = PROVIDERS.application_credential_api

View File

@ -29,6 +29,28 @@ _role_properties = {
} }
} }
_access_rules_properties = {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'path': {
'type': 'string',
'minLength': 0,
'maxLength': 225,
'pattern': '^\/.*'
},
'method': {
'type': 'string',
'pattern': '^(POST|GET|HEAD|PATCH|PUT|DELETE)$'
},
'service': parameter_types.id_string,
'id': parameter_types.id_string,
},
'additionalProperties': False
}
}
_application_credential_properties = { _application_credential_properties = {
'name': parameter_types.name, 'name': parameter_types.name,
'description': validation.nullable(parameter_types.description), 'description': validation.nullable(parameter_types.description),
@ -39,7 +61,8 @@ _application_credential_properties = {
'type': ['null', 'string'] 'type': ['null', 'string']
}, },
'roles': _role_properties, 'roles': _role_properties,
'unrestricted': parameter_types.boolean 'unrestricted': parameter_types.boolean,
'access_rules': _access_rules_properties
} }
application_credential_create = { application_credential_create = {

View File

@ -35,7 +35,8 @@ class ApplicationCredentialTestCase(test_v3.RestfulTestCase):
self.config_fixture.config(group='auth', self.config_fixture.config(group='auth',
methods='password,application_credential') methods='password,application_credential')
def _app_cred_body(self, roles=None, name=None, expires=None, secret=None): def _app_cred_body(self, roles=None, name=None, expires=None, secret=None,
access_rules=None):
name = name or uuid.uuid4().hex name = name or uuid.uuid4().hex
description = 'Credential for backups' description = 'Credential for backups'
app_cred_data = { app_cred_data = {
@ -48,6 +49,8 @@ class ApplicationCredentialTestCase(test_v3.RestfulTestCase):
app_cred_data['expires_at'] = expires app_cred_data['expires_at'] = expires
if secret: if secret:
app_cred_data['secret'] = secret app_cred_data['secret'] = secret
if access_rules is not None:
app_cred_data['access_rules'] = access_rules
return {'application_credential': app_cred_data} return {'application_credential': app_cred_data}
def test_create_application_credential(self): def test_create_application_credential(self):
@ -187,6 +190,96 @@ class ApplicationCredentialTestCase(test_v3.RestfulTestCase):
expected_status_code=http_client.CREATED, expected_status_code=http_client.CREATED,
headers={'x-Auth-Token': token_data.headers['x-subject-token']}) headers={'x-Auth-Token': token_data.headers['x-subject-token']})
def test_create_application_credential_with_access_rules(self):
roles = [{'id': self.role_id}]
access_rules = [
{
'path': '/v3/projects',
'method': 'POST',
'service': 'identity',
}
]
app_cred_body = self._app_cred_body(roles=roles,
access_rules=access_rules)
with self.test_client() as c:
token = self.get_scoped_token()
resp = c.post('/v3/users/%s/application_credentials' % self.user_id,
headers={'X-Auth-Token': token},
json=app_cred_body,
expected_status_code=http_client.CREATED)
resp_access_rules = resp.json['application_credential']['access_rules']
self.assertIn('id', resp_access_rules[0])
resp_access_rules[0].pop('id')
self.assertEqual(access_rules[0], resp_access_rules[0])
def test_create_application_credential_with_duplicate_access_rule(self):
roles = [{'id': self.role_id}]
access_rules = [
{
'path': '/v3/projects',
'method': 'POST',
'service': 'identity',
}
]
app_cred_body_1 = self._app_cred_body(roles=roles,
access_rules=access_rules)
with self.test_client() as c:
token = self.get_scoped_token()
resp = c.post('/v3/users/%s/application_credentials' % self.user_id,
headers={'X-Auth-Token': token},
json=app_cred_body_1,
expected_status_code=http_client.CREATED)
resp_access_rules = resp.json['application_credential']['access_rules']
self.assertIn('id', resp_access_rules[0])
access_rule_id = resp_access_rules[0].pop('id')
self.assertEqual(access_rules[0], resp_access_rules[0])
app_cred_body_2 = self._app_cred_body(roles=roles,
access_rules=access_rules)
with self.test_client() as c:
token = self.get_scoped_token()
resp = c.post('/v3/users/%s/application_credentials' % self.user_id,
headers={'X-Auth-Token': token},
json=app_cred_body_2,
expected_status_code=http_client.CREATED)
resp_access_rules = resp.json['application_credential']['access_rules']
self.assertEqual(access_rule_id, resp_access_rules[0]['id'])
def test_create_application_credential_with_access_rule_by_id(self):
roles = [{'id': self.role_id}]
access_rules = [
{
'path': '/v3/projects',
'method': 'POST',
'service': 'identity',
}
]
app_cred_body_1 = self._app_cred_body(roles=roles,
access_rules=access_rules)
with self.test_client() as c:
token = self.get_scoped_token()
resp = c.post('/v3/users/%s/application_credentials' % self.user_id,
headers={'X-Auth-Token': token},
json=app_cred_body_1,
expected_status_code=http_client.CREATED)
resp_access_rules = resp.json['application_credential']['access_rules']
access_rule_id = resp_access_rules
self.assertIn('id', resp_access_rules[0])
access_rule_id = resp_access_rules[0].pop('id')
self.assertEqual(access_rules[0], resp_access_rules[0])
access_rules = [{'id': access_rule_id}]
app_cred_body_2 = self._app_cred_body(roles=roles,
access_rules=access_rules)
with self.test_client() as c:
token = self.get_scoped_token()
resp = c.post('/v3/users/%s/application_credentials' % self.user_id,
headers={'X-Auth-Token': token},
json=app_cred_body_2,
expected_status_code=http_client.CREATED)
resp_access_rules = resp.json['application_credential']['access_rules']
self.assertEqual(access_rule_id, resp_access_rules[0]['id'])
def test_list_application_credentials(self): def test_list_application_credentials(self):
with self.test_client() as c: with self.test_client() as c:
token = self.get_scoped_token() token = self.get_scoped_token()