Merge "Add JSON Schema to application credentials and validation decorators to application credential resource."
This commit is contained in:
@@ -679,6 +679,12 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
|
||||
roles = token.roles
|
||||
return roles
|
||||
|
||||
@validation.request_query_schema(
|
||||
app_cred_schema.application_credential_index_request_query
|
||||
)
|
||||
@validation.response_body_schema(
|
||||
app_cred_schema.application_credential_index_response_body
|
||||
)
|
||||
def get(self, user_id):
|
||||
"""List application credentials for user.
|
||||
|
||||
@@ -693,6 +699,12 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
|
||||
refs = app_cred_api.list_application_credentials(user_id, hints=hints)
|
||||
return self.wrap_collection(refs, hints=hints)
|
||||
|
||||
@validation.request_body_schema(
|
||||
app_cred_schema.application_credential_create_request_body
|
||||
)
|
||||
@validation.response_body_schema(
|
||||
app_cred_schema.application_credential_create_response_body
|
||||
)
|
||||
def post(self, user_id):
|
||||
"""Create application credential.
|
||||
|
||||
@@ -702,9 +714,6 @@ class UserAppCredListCreateResource(ks_flask.ResourceBase):
|
||||
app_cred_data = self.request_body_json.get(
|
||||
'application_credential', {}
|
||||
)
|
||||
ks_validation.lazy_validate(
|
||||
app_cred_schema.application_credential_create, app_cred_data
|
||||
)
|
||||
token = self.auth_context['token']
|
||||
_check_unrestricted_application_credential(token)
|
||||
if self.oslo_context.user_id != user_id:
|
||||
@@ -754,6 +763,12 @@ class UserAppCredGetDeleteResource(ks_flask.ResourceBase):
|
||||
collection_key = 'application_credentials'
|
||||
member_key = 'application_credential'
|
||||
|
||||
@validation.request_body_schema(
|
||||
app_cred_schema.application_credential_request_body
|
||||
)
|
||||
@validation.response_body_schema(
|
||||
app_cred_schema.application_credential_response_body
|
||||
)
|
||||
def get(self, user_id, application_credential_id):
|
||||
"""Get application credential resource.
|
||||
|
||||
|
||||
@@ -12,26 +12,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
from typing import Any
|
||||
|
||||
from keystone.api.validation import parameter_types
|
||||
from keystone.api.validation import response_types
|
||||
from keystone.common import validation
|
||||
from keystone.common.validation import parameter_types as ks_parameter_types
|
||||
|
||||
_role_properties = {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'id': parameter_types.id_string,
|
||||
'name': parameter_types.name,
|
||||
},
|
||||
'minProperties': 1,
|
||||
'maxProperties': 1,
|
||||
'additionalProperties': False,
|
||||
},
|
||||
}
|
||||
|
||||
# Individual properties of 'Access Rule'
|
||||
_access_rules_properties = {
|
||||
@@ -80,7 +65,8 @@ access_rule_schema: dict[str, Any] = {
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
# Query parameters of the `/users/{user_d}/access_rules` API
|
||||
# Query parameters of the `/users/{user_id}/access_rules` and
|
||||
# `/application_credentials/{application_credential_id}` APIs
|
||||
index_request_query: dict[str, Any] = {
|
||||
"type": "object",
|
||||
"properties": {},
|
||||
@@ -118,19 +104,213 @@ rule_show_response_body: dict[str, Any] = {
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
# Individual properties of 'Application Credential'
|
||||
_application_credential_properties = {
|
||||
'name': ks_parameter_types.name,
|
||||
'description': validation.nullable(ks_parameter_types.description),
|
||||
'secret': {'type': ['null', 'string']},
|
||||
'expires_at': {'type': ['null', 'string']},
|
||||
'roles': _role_properties,
|
||||
'unrestricted': ks_parameter_types.boolean,
|
||||
'access_rules': _access_rules_properties,
|
||||
"name": {
|
||||
**parameter_types.name,
|
||||
"description": (
|
||||
"The name of the application credential. Must be unique to a user."
|
||||
),
|
||||
},
|
||||
"description": {
|
||||
"type": ["string", "null"],
|
||||
"description": (
|
||||
"A description of the application credential's purpose."
|
||||
),
|
||||
},
|
||||
"expires_at": {
|
||||
"type": ["string", "null"],
|
||||
"description": (
|
||||
"The expiration time of the application credential, if one "
|
||||
"was specified."
|
||||
),
|
||||
},
|
||||
"project_id": {
|
||||
"type": "string",
|
||||
"description": (
|
||||
"The ID of the project the application credential was "
|
||||
"created for and that authentication requests using this "
|
||||
"application credential will be scoped to."
|
||||
),
|
||||
},
|
||||
"access_rules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": parameter_types.id_string,
|
||||
**_access_rules_properties,
|
||||
},
|
||||
},
|
||||
"description": "A list of access_rules objects.",
|
||||
},
|
||||
"unrestricted": {
|
||||
"type": ["boolean", "null"],
|
||||
"description": (
|
||||
"A flag indicating whether the application credential "
|
||||
"may be used for creation or destruction of other "
|
||||
"application credentials or trusts."
|
||||
),
|
||||
},
|
||||
"system": {"type": ["string", "null"]},
|
||||
}
|
||||
|
||||
application_credential_create = {
|
||||
'type': 'object',
|
||||
'properties': _application_credential_properties,
|
||||
'required': ['name'],
|
||||
'additionalProperties': True,
|
||||
# Common schema of `Application Credential` resource
|
||||
application_credential_schema: dict[str, Any] = {
|
||||
"type": "object",
|
||||
"description": "An application credential object.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"readOnly": True,
|
||||
"description": "The UUID of the application credential",
|
||||
},
|
||||
"roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": parameter_types.id_string,
|
||||
"name": parameter_types.name,
|
||||
"domain_id": {
|
||||
"type": ["string", "null"],
|
||||
"description": "The ID of the domain of the role.",
|
||||
},
|
||||
"description": {
|
||||
"type": ["string", "null"],
|
||||
"description": "A description about the role.",
|
||||
},
|
||||
"options": {
|
||||
"type": ["object", "null"],
|
||||
"description": (
|
||||
"The resource options for the role. "
|
||||
"Available resource options are immutable."
|
||||
),
|
||||
},
|
||||
},
|
||||
"additionalProperties": False,
|
||||
},
|
||||
"description": (
|
||||
"A list of one or more roles that this application "
|
||||
"credential has associated with its project. A token "
|
||||
"using this application credential will have these "
|
||||
"same roles."
|
||||
),
|
||||
},
|
||||
"user_id": {"type": "string", "description": "The ID of the user."},
|
||||
"links": response_types.resource_links,
|
||||
**_application_credential_properties,
|
||||
},
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
# Query parameters of `/application_credentials` API
|
||||
application_credential_index_request_query: dict[str, Any] = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": (
|
||||
"The name of the application credential. "
|
||||
"Must be unique to a user."
|
||||
),
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
# Response of the `/application_credentials` API
|
||||
application_credential_index_response_body: dict[str, Any] = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"application_credentials": {
|
||||
"type": "array",
|
||||
"items": application_credential_schema,
|
||||
"description": "A list of application credentials",
|
||||
},
|
||||
"links": response_types.links,
|
||||
"truncated": response_types.truncated,
|
||||
},
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
application_credential_request_body: dict[str, Any] = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The UUID of the application credential",
|
||||
}
|
||||
},
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
# Response of `/application_credentials/{application_credential_id}`
|
||||
# API returning single access rule
|
||||
application_credential_response_body: dict[str, Any] = {
|
||||
"type": "object",
|
||||
"description": "An application credential object.",
|
||||
"properties": {"application_credential": application_credential_schema},
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
# Request body of the `POST /application_credentials` operation
|
||||
application_credential_create_request_body: dict[str, Any] = {
|
||||
"type": "object",
|
||||
"description": "An application credential object.",
|
||||
"properties": {
|
||||
"application_credential": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "The UUID for the credential.",
|
||||
},
|
||||
"secret": {
|
||||
"type": ["string", "null"],
|
||||
"description": (
|
||||
"The secret that the application credential "
|
||||
"will be created with. If not provided, one "
|
||||
"will be generated."
|
||||
),
|
||||
},
|
||||
**_application_credential_properties,
|
||||
"roles": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": parameter_types.id_string,
|
||||
"name": parameter_types.name,
|
||||
},
|
||||
"minProperties": 1,
|
||||
"maxProperties": 1,
|
||||
"additionalProperties": False,
|
||||
},
|
||||
"description": (
|
||||
"A list of one or more roles that this application "
|
||||
"credential has associated with its project. A token "
|
||||
"using this application credential will have these "
|
||||
"same roles."
|
||||
),
|
||||
},
|
||||
},
|
||||
"additionalProperties": False,
|
||||
"required": ["name"],
|
||||
}
|
||||
},
|
||||
"required": ["application_credential"],
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
application_credential_create_response_body = copy.deepcopy(
|
||||
application_credential_response_body
|
||||
)
|
||||
application_credential_create_response_body["properties"][
|
||||
"application_credential"
|
||||
]["properties"]["secret"] = {
|
||||
"type": "string",
|
||||
"description": (
|
||||
"The secret that the application credential will be created with."
|
||||
),
|
||||
}
|
||||
|
||||
@@ -3623,23 +3623,27 @@ class ApplicationCredentialValidatorTestCase(unit.TestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
create = app_cred_schema.application_credential_create
|
||||
create = app_cred_schema.application_credential_create_request_body
|
||||
self.create_app_cred_validator = validators.SchemaValidator(create)
|
||||
|
||||
def test_validate_app_cred_request(self):
|
||||
request_to_validate = {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
'expires_at': 'tomorrow',
|
||||
'application_credential': {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
'expires_at': 'tomorrow',
|
||||
}
|
||||
}
|
||||
self.create_app_cred_validator.validate(request_to_validate)
|
||||
|
||||
def test_validate_app_cred_request_without_name_fails(self):
|
||||
request_to_validate = {
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
'expires_at': 'tomorrow',
|
||||
'application_credential': {
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
'expires_at': 'tomorrow',
|
||||
}
|
||||
}
|
||||
self.assertRaises(
|
||||
exception.SchemaValidationError,
|
||||
@@ -3649,10 +3653,12 @@ class ApplicationCredentialValidatorTestCase(unit.TestCase):
|
||||
|
||||
def test_validate_app_cred_with_invalid_expires_at_fails(self):
|
||||
request_to_validate = {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
'expires_at': 3,
|
||||
'application_credential': {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
'expires_at': 3,
|
||||
}
|
||||
}
|
||||
self.assertRaises(
|
||||
exception.SchemaValidationError,
|
||||
@@ -3662,36 +3668,44 @@ class ApplicationCredentialValidatorTestCase(unit.TestCase):
|
||||
|
||||
def test_validate_app_cred_with_null_expires_at_succeeds(self):
|
||||
request_to_validate = {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
'application_credential': {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
}
|
||||
}
|
||||
self.create_app_cred_validator.validate(request_to_validate)
|
||||
|
||||
def test_validate_app_cred_with_unrestricted_flag_succeeds(self):
|
||||
request_to_validate = {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
'unrestricted': True,
|
||||
'application_credential': {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
'unrestricted': True,
|
||||
}
|
||||
}
|
||||
self.create_app_cred_validator.validate(request_to_validate)
|
||||
|
||||
def test_validate_app_cred_with_secret_succeeds(self):
|
||||
request_to_validate = {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
'secret': 'secretsecretsecretsecret',
|
||||
'application_credential': {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [{'name': 'member'}],
|
||||
'secret': 'secretsecretsecretsecret',
|
||||
}
|
||||
}
|
||||
self.create_app_cred_validator.validate(request_to_validate)
|
||||
|
||||
def test_validate_app_cred_invalid_roles_fails(self):
|
||||
for role in self._invalid_roles:
|
||||
request_to_validate = {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [role],
|
||||
'application_credential': {
|
||||
'name': 'myappcred',
|
||||
'description': 'My App Cred',
|
||||
'roles': [role],
|
||||
}
|
||||
}
|
||||
self.assertRaises(
|
||||
exception.SchemaValidationError,
|
||||
|
||||
Reference in New Issue
Block a user