Skip to content

Commit

Permalink
aws - iam-user - add login-profile filter (#7804)
Browse files Browse the repository at this point in the history
  • Loading branch information
StevenGunn authored and HappyKid117 committed Oct 16, 2022
1 parent ff5d154 commit 7d9e293
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 0 deletions.
52 changes: 52 additions & 0 deletions c7n/resources/iam.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ class resource_type(TypeInfo):
# Denotes this resource type exists across regions
global_resource = True
arn = 'Arn'
config_id = 'UserId'

source_mapping = {
'describe': DescribeUser,
Expand Down Expand Up @@ -1975,6 +1976,57 @@ def process(self, resources, event=None):
return matched


@User.filter_registry.register('login-profile')
class UserLoginProfile(ValueFilter):
"""Filter IAM users that have an associated login-profile
For quicker evaluation and reduced API traffic, it is recommended to
instead use the 'credential' filter with 'password_enabled': true when
a delay of up to four hours for credential report syncing is acceptable.
(https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_getting-report.html)
:example:
.. code-block: yaml
policies:
- name: iam-users-with-console-access
resource: iam-user
filters:
- type: login-profile
"""

schema = type_schema('login-profile', rinherit=ValueFilter.schema)
permissions = ('iam:GetLoginProfile',)
annotation_key = 'c7n:LoginProfile'

def user_login_profiles(self, user_set):
client = local_session(self.manager.session_factory).client('iam')
for u in user_set:
u[self.annotation_key] = False
try:
login_profile_resp = client.get_login_profile(UserName=u['UserName'])
if u['UserName'] == login_profile_resp['LoginProfile']['UserName']:
u[self.annotation_key] = True
except ClientError as e:
if e.response['Error']['Code'] not in ('NoSuchEntity',):
raise

def process(self, resources, event=None):
user_set = chunks(resources, size=50)
with self.executor_factory(max_workers=2) as w:
self.log.debug(
"Querying %d users for login profile" % len(resources))
list(w.map(self.user_login_profiles, user_set))

matched = []
for r in resources:
if r[self.annotation_key]:
matched.append(r)
return matched


# Mfa-device filter for iam-users
@User.filter_registry.register('mfa-device')
class UserMfaDevice(ValueFilter):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"status_code": 200,
"data": {
"LoginProfile": {
"UserName": "test-user-console-access",
"CreateDate": {
"__class__": "datetime",
"year": 2022,
"month": 9,
"day": 27,
"hour": 0,
"minute": 35,
"second": 38,
"microsecond": 0
},
"PasswordResetRequired": true
},
"ResponseMetadata": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"status_code": 404,
"data": {
"Error": {
"Type": "Sender",
"Code": "NoSuchEntity",
"Message": "Login Profile for User test-user-no-console-access cannot be found."
},
"ResponseMetadata": {}
}
}
22 changes: 22 additions & 0 deletions tests/data/placebo/test_iam_user_login_profile/iam.GetUser_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"status_code": 200,
"data": {
"User": {
"Path": "/",
"UserName": "test-user-console-access",
"UserId": "AIDARNRRQBPMCGMIQBMH4",
"Arn": "arn:aws:iam::644160558196:user/test-user-console-access",
"CreateDate": {
"__class__": "datetime",
"year": 2022,
"month": 9,
"day": 23,
"hour": 23,
"minute": 24,
"second": 30,
"microsecond": 0
}
},
"ResponseMetadata": {}
}
}
22 changes: 22 additions & 0 deletions tests/data/placebo/test_iam_user_login_profile/iam.GetUser_2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"status_code": 200,
"data": {
"User": {
"Path": "/",
"UserName": "test-user-no-console-access",
"UserId": "AIDARNRRQBPMMX4SVHZDJ",
"Arn": "arn:aws:iam::644160558196:user/test-user-no-console-access",
"CreateDate": {
"__class__": "datetime",
"year": 2022,
"month": 9,
"day": 23,
"hour": 23,
"minute": 26,
"second": 4,
"microsecond": 0
}
},
"ResponseMetadata": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"status_code": 200,
"data": {
"Users": [
{
"Path": "/",
"UserName": "test-user-console-access",
"UserId": "AIDARNRRQBPMCGMIQBMH4",
"Arn": "arn:aws:iam::644160558196:user/test-user-console-access",
"CreateDate": {
"__class__": "datetime",
"year": 2022,
"month": 9,
"day": 23,
"hour": 23,
"minute": 24,
"second": 30,
"microsecond": 0
}
},
{
"Path": "/",
"UserName": "test-user-no-console-access",
"UserId": "AIDARNRRQBPMMX4SVHZDJ",
"Arn": "arn:aws:iam::644160558196:user/test-user-no-console-access",
"CreateDate": {
"__class__": "datetime",
"year": 2022,
"month": 9,
"day": 23,
"hour": 23,
"minute": 26,
"second": 4,
"microsecond": 0
}
}
],
"IsTruncated": false,
"ResponseMetadata": {}
}
}
14 changes: 14 additions & 0 deletions tests/test_iam.py
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,20 @@ def test_iam_user_access_key_filter(self):
self.assertEqual(len(resources), 1)
self.assertEqual(resources[0]["UserName"], "alphabet_soup")

def test_iam_user_login_profile(self):
session_factory = self.replay_flight_data('test_iam_user_login_profile')
p = self.load_policy(
{
"name": "iam-users-with-console-access",
"resource": "iam-user",
"filters": [{"type": "login-profile"}],
},
session_factory=session_factory,
)
resources = p.run()
self.assertEqual(len(resources), 1)
self.assertEqual(resources[0]["UserName"], "test-user-console-access")


@terraform('iam_user', teardown=terraform.TEARDOWN_IGNORE)
def test_iam_user_disable_ssh_keys(test, iam_user):
Expand Down

0 comments on commit 7d9e293

Please sign in to comment.