Move to password validation schema

This change moves the user api change password value check to
JSON schema, rather than manually checking for empty/invalid
values. After this, more of the password validation can
be moved up to schema from code.

Change-Id: I15b1df51af53d56293a7b1b2a06fda7f4e5d45eb
This commit is contained in:
Gage Hugo 2018-10-30 11:31:20 -05:00
parent d53ed61468
commit dec8c717c9
3 changed files with 74 additions and 16 deletions

View File

@ -199,26 +199,13 @@ class UserChangePasswordResource(ks_flask.ResourceBase):
@ks_flask.unenforced_api
def post(self, user_id):
user_data = self.request_body_json.get('user', {})
original_password = user_data.get('original_password')
new_password = user_data.get('password')
# TODO(morgan): Convert this to JSON Schema validation
if original_password is None:
raise ks_exception.ValidationError(
target='user',
attribute='original_password')
# TODO(morgan): Convert this to JSON Schema validation
if new_password is None:
raise ks_exception.ValidationError(
target='user',
attribute='password')
validation.lazy_validate(schema.password_change, user_data)
try:
PROVIDERS.identity_api.change_password(
user_id=user_id,
original_password=original_password,
new_password=new_password,
original_password=user_data['original_password'],
new_password=user_data['password'],
initiator=self.audit_initiator)
except AssertionError as e:
raise ks_exception.Unauthorized(

View File

@ -12,9 +12,13 @@
from keystone.common import validation
from keystone.common.validation import parameter_types
import keystone.conf
from keystone.identity.backends import resource_options as ro
CONF = keystone.conf.CONF
_identity_name = {
'type': 'string',
'minLength': 1,
@ -77,3 +81,27 @@ group_update = {
'minProperties': 1,
'additionalProperties': True
}
_password_change_properties = {
'original_password': {
'type': 'string'
},
'password': {
'type': 'string'
}
}
if getattr(CONF, 'strict_password_check', None):
_password_change_properties['password']['maxLength'] = \
CONF.identity.max_password_length
if getattr(CONF, 'security_compliance', None):
if getattr(CONF.security_compliance, 'password_regex', None):
_password_change_properties['password']['pattern'] = \
CONF.security_compliance.password_regex
password_change = {
'type': 'object',
'properties': _password_change_properties,
'required': ['original_password', 'password'],
'additionalProperties': False
}

View File

@ -2185,6 +2185,49 @@ class GroupValidationTestCase(unit.BaseTestCase):
request_to_validate)
class ChangePasswordValidationTestCase(unit.BaseTestCase):
"""Test for Change Password API validation."""
def setUp(self):
super(ChangePasswordValidationTestCase, self).setUp()
self.original_password = uuid.uuid4().hex
self.password = uuid.uuid4().hex
change = identity_schema.password_change
self.change_password_validator = validators.SchemaValidator(change)
def test_validate_password_change_request_succeeds(self):
"""Test that validating a password change request succeeds."""
request_to_validate = {'original_password': self.original_password,
'password': self.password}
self.change_password_validator.validate(request_to_validate)
def test_validate_password_change_fails_without_all_fields(self):
"""Test that validating a password change fails without all values."""
request_to_validate = {'original_password': self.original_password}
self.assertRaises(exception.SchemaValidationError,
self.change_password_validator.validate,
request_to_validate)
request_to_validate = {'password': self.password}
self.assertRaises(exception.SchemaValidationError,
self.change_password_validator.validate,
request_to_validate)
def test_validate_password_change_fails_with_invalid_values(self):
"""Test that validating a password change fails with bad values."""
request_to_validate = {'original_password': None,
'password': None}
self.assertRaises(exception.SchemaValidationError,
self.change_password_validator.validate,
request_to_validate)
request_to_validate = {'original_password': 42,
'password': True}
self.assertRaises(exception.SchemaValidationError,
self.change_password_validator.validate,
request_to_validate)
class IdentityProviderValidationTestCase(unit.BaseTestCase):
"""Test for V3 Identity Provider API validation."""