Add iam.update_account_password_policy
This commit is contained in:
parent
e00af2f73c
commit
06581391bd
@ -3387,7 +3387,7 @@
|
|||||||
- [X] untag_role
|
- [X] untag_role
|
||||||
- [ ] untag_user
|
- [ ] untag_user
|
||||||
- [X] update_access_key
|
- [X] update_access_key
|
||||||
- [ ] update_account_password_policy
|
- [X] update_account_password_policy
|
||||||
- [ ] update_assume_role_policy
|
- [ ] update_assume_role_policy
|
||||||
- [ ] update_group
|
- [ ] update_group
|
||||||
- [X] update_login_profile
|
- [X] update_login_profile
|
||||||
|
@ -652,6 +652,71 @@ class User(BaseModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AccountPasswordPolicy(BaseModel):
|
||||||
|
|
||||||
|
def __init__(self, allow_change_password, hard_expiry, max_password_age, minimum_password_length,
|
||||||
|
password_reuse_prevention, require_lowercase_characters, require_numbers,
|
||||||
|
require_symbols, require_uppercase_characters):
|
||||||
|
self._errors = []
|
||||||
|
self._validate(max_password_age, minimum_password_length, password_reuse_prevention)
|
||||||
|
|
||||||
|
self.allow_users_to_change_password = allow_change_password
|
||||||
|
self.hard_expiry = hard_expiry
|
||||||
|
self.max_password_age = max_password_age
|
||||||
|
self.minimum_password_length = minimum_password_length
|
||||||
|
self.password_reuse_prevention = password_reuse_prevention
|
||||||
|
self.require_lowercase_characters = require_lowercase_characters
|
||||||
|
self.require_numbers = require_numbers
|
||||||
|
self.require_symbols = require_symbols
|
||||||
|
self.require_uppercase_characters = require_uppercase_characters
|
||||||
|
|
||||||
|
@property
|
||||||
|
def expire_passwords(self):
|
||||||
|
return True if self.max_password_age and self.max_password_age > 0 else False
|
||||||
|
|
||||||
|
def _validate(self, max_password_age, minimum_password_length, password_reuse_prevention):
|
||||||
|
if minimum_password_length > 128:
|
||||||
|
self._errors.append(self._format_error(
|
||||||
|
key='minimumPasswordLength',
|
||||||
|
value=minimum_password_length,
|
||||||
|
constraint='Member must have value less than or equal to 128'
|
||||||
|
))
|
||||||
|
|
||||||
|
if password_reuse_prevention and password_reuse_prevention > 24:
|
||||||
|
self._errors.append(self._format_error(
|
||||||
|
key='passwordReusePrevention',
|
||||||
|
value=password_reuse_prevention,
|
||||||
|
constraint='Member must have value less than or equal to 24'
|
||||||
|
))
|
||||||
|
|
||||||
|
if max_password_age and max_password_age > 1095:
|
||||||
|
self._errors.append(self._format_error(
|
||||||
|
key='maxPasswordAge',
|
||||||
|
value=max_password_age,
|
||||||
|
constraint='Member must have value less than or equal to 1095'
|
||||||
|
))
|
||||||
|
|
||||||
|
self._raise_errors()
|
||||||
|
|
||||||
|
def _format_error(self, key, value, constraint):
|
||||||
|
return 'Value "{value}" at "{key}" failed to satisfy constraint: {constraint}'.format(
|
||||||
|
constraint=constraint,
|
||||||
|
key=key,
|
||||||
|
value=value,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _raise_errors(self):
|
||||||
|
if self._errors:
|
||||||
|
count = len(self._errors)
|
||||||
|
plural = "s" if len(self._errors) > 1 else ""
|
||||||
|
errors = "; ".join(self._errors)
|
||||||
|
self._errors = [] # reset collected errors
|
||||||
|
|
||||||
|
raise ValidationError('{count} validation error{plural} detected: {errors}'.format(
|
||||||
|
count=count, plural=plural, errors=errors,
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
class IAMBackend(BaseBackend):
|
class IAMBackend(BaseBackend):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.instance_profiles = {}
|
self.instance_profiles = {}
|
||||||
@ -666,6 +731,7 @@ class IAMBackend(BaseBackend):
|
|||||||
self.open_id_providers = {}
|
self.open_id_providers = {}
|
||||||
self.policy_arn_regex = re.compile(r"^arn:aws:iam::[0-9]*:policy/.*$")
|
self.policy_arn_regex = re.compile(r"^arn:aws:iam::[0-9]*:policy/.*$")
|
||||||
self.virtual_mfa_devices = {}
|
self.virtual_mfa_devices = {}
|
||||||
|
self.account_password_policy = None
|
||||||
super(IAMBackend, self).__init__()
|
super(IAMBackend, self).__init__()
|
||||||
|
|
||||||
def _init_managed_policies(self):
|
def _init_managed_policies(self):
|
||||||
@ -1590,5 +1656,20 @@ class IAMBackend(BaseBackend):
|
|||||||
def list_open_id_connect_providers(self):
|
def list_open_id_connect_providers(self):
|
||||||
return list(self.open_id_providers.keys())
|
return list(self.open_id_providers.keys())
|
||||||
|
|
||||||
|
def update_account_password_policy(self, allow_change_password, hard_expiry, max_password_age, minimum_password_length,
|
||||||
|
password_reuse_prevention, require_lowercase_characters, require_numbers,
|
||||||
|
require_symbols, require_uppercase_characters):
|
||||||
|
self.account_password_policy = AccountPasswordPolicy(
|
||||||
|
allow_change_password,
|
||||||
|
hard_expiry,
|
||||||
|
max_password_age,
|
||||||
|
minimum_password_length,
|
||||||
|
password_reuse_prevention,
|
||||||
|
require_lowercase_characters,
|
||||||
|
require_numbers,
|
||||||
|
require_symbols,
|
||||||
|
require_uppercase_characters
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
iam_backend = IAMBackend()
|
iam_backend = IAMBackend()
|
||||||
|
@ -838,6 +838,25 @@ class IamResponse(BaseResponse):
|
|||||||
template = self.response_template(LIST_OPEN_ID_CONNECT_PROVIDERS_TEMPLATE)
|
template = self.response_template(LIST_OPEN_ID_CONNECT_PROVIDERS_TEMPLATE)
|
||||||
return template.render(open_id_provider_arns=open_id_provider_arns)
|
return template.render(open_id_provider_arns=open_id_provider_arns)
|
||||||
|
|
||||||
|
def update_account_password_policy(self):
|
||||||
|
allow_change_password = self._get_bool_param('AllowUsersToChangePassword', False)
|
||||||
|
hard_expiry = self._get_bool_param('HardExpiry')
|
||||||
|
max_password_age = self._get_int_param('MaxPasswordAge')
|
||||||
|
minimum_password_length = self._get_int_param('MinimumPasswordLength', 6)
|
||||||
|
password_reuse_prevention = self._get_int_param('PasswordReusePrevention')
|
||||||
|
require_lowercase_characters = self._get_bool_param('RequireLowercaseCharacters', False)
|
||||||
|
require_numbers = self._get_bool_param('RequireNumbers', False)
|
||||||
|
require_symbols = self._get_bool_param('RequireSymbols', False)
|
||||||
|
require_uppercase_characters = self._get_bool_param('RequireUppercaseCharacters', False)
|
||||||
|
|
||||||
|
iam_backend.update_account_password_policy(
|
||||||
|
allow_change_password, hard_expiry, max_password_age, minimum_password_length,
|
||||||
|
password_reuse_prevention, require_lowercase_characters, require_numbers,
|
||||||
|
require_symbols, require_uppercase_characters)
|
||||||
|
|
||||||
|
template = self.response_template(UPDATE_ACCOUNT_PASSWORD_POLICY_TEMPLATE)
|
||||||
|
return template.render()
|
||||||
|
|
||||||
|
|
||||||
LIST_ENTITIES_FOR_POLICY_TEMPLATE = """<ListEntitiesForPolicyResponse>
|
LIST_ENTITIES_FOR_POLICY_TEMPLATE = """<ListEntitiesForPolicyResponse>
|
||||||
<ListEntitiesForPolicyResult>
|
<ListEntitiesForPolicyResult>
|
||||||
@ -2170,3 +2189,10 @@ LIST_OPEN_ID_CONNECT_PROVIDERS_TEMPLATE = """<ListOpenIDConnectProvidersResponse
|
|||||||
<RequestId>de2c0228-4f63-11e4-aefa-bfd6aEXAMPLE</RequestId>
|
<RequestId>de2c0228-4f63-11e4-aefa-bfd6aEXAMPLE</RequestId>
|
||||||
</ResponseMetadata>
|
</ResponseMetadata>
|
||||||
</ListOpenIDConnectProvidersResponse>"""
|
</ListOpenIDConnectProvidersResponse>"""
|
||||||
|
|
||||||
|
|
||||||
|
UPDATE_ACCOUNT_PASSWORD_POLICY_TEMPLATE = """<UpdateAccountPasswordPolicyResponse xmlns="https://iam.amazonaws.com/doc/2010-05-08/">
|
||||||
|
<ResponseMetadata>
|
||||||
|
<RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>
|
||||||
|
</ResponseMetadata>
|
||||||
|
</UpdateAccountPasswordPolicyResponse>"""
|
||||||
|
@ -2195,3 +2195,29 @@ def test_list_open_id_connect_providers():
|
|||||||
sorted(response["OpenIDConnectProviderList"], key=lambda i: i["Arn"]).should.equal(
|
sorted(response["OpenIDConnectProviderList"], key=lambda i: i["Arn"]).should.equal(
|
||||||
[{"Arn": open_id_arn_1}, {"Arn": open_id_arn_2}, {"Arn": open_id_arn_3}]
|
[{"Arn": open_id_arn_1}, {"Arn": open_id_arn_2}, {"Arn": open_id_arn_3}]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_iam
|
||||||
|
def test_update_account_password_policy():
|
||||||
|
client = boto3.client('iam', region_name='us-east-1')
|
||||||
|
client.update_account_password_policy()
|
||||||
|
|
||||||
|
|
||||||
|
@mock_iam
|
||||||
|
def test_update_account_password_policy_errors():
|
||||||
|
client = boto3.client('iam', region_name='us-east-1')
|
||||||
|
|
||||||
|
client.update_account_password_policy.when.called_with(
|
||||||
|
MaxPasswordAge = 1096,
|
||||||
|
MinimumPasswordLength = 129,
|
||||||
|
PasswordReusePrevention = 25
|
||||||
|
).should.throw(
|
||||||
|
ClientError,
|
||||||
|
'3 validation errors detected: '
|
||||||
|
'Value "129" at "minimumPasswordLength" failed to satisfy constraint: '
|
||||||
|
'Member must have value less than or equal to 128; '
|
||||||
|
'Value "25" at "passwordReusePrevention" failed to satisfy constraint: '
|
||||||
|
'Member must have value less than or equal to 24; '
|
||||||
|
'Value "1096" at "maxPasswordAge" failed to satisfy constraint: '
|
||||||
|
'Member must have value less than or equal to 1095'
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user