noqdev / iambic

IAMbic is Version-Control for IAM. It centralizes and simplifies cloud access and permissions. It maintains an eventually consistent, human-readable, bi-directional representation of IAM in Git.
https://iambic.org
Apache License 2.0
279 stars 26 forks source link

AWS IdentityCenter with AD Azure Connector that is not a fully qualified domain fail to import #557

Closed smoy closed 1 year ago

smoy commented 1 year ago

Describe the bug AWS IdentityCenter with AD Azure Connector does not work out of the box for iambic

domains that is in .local (not fully qualified domain), for example: corp.local

To Reproduce Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error
User activity: imabic import
Exception: An error occurred (ValidationException) when calling the ListUsers operation: Filter is required
Traceback:
  File "/home/ssm-user/identity-center/venv/bin/iambic", line 8, in module
    sys.exit(cli())
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/iambic/main.py", line 447, in import_
    asyncio.run(config.run_import(exe_message, repo_dir))
  File "/usr/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/iambic/config/dynamic_config.py", line 268, in run_import
    await asyncio.gather(*tasks)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/iambic/plugins/v0_1_0/aws/handlers.py", line 462, in import_aws_resources
    await asyncio.gather(*tasks)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/iambic/plugins/v0_1_0/aws/handlers.py", line 379, in import_identity_center_resources
    await config.set_identity_center_details(exe_message.provider_id)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/iambic/plugins/v0_1_0/aws/iambic_plugin.py", line 146, in set_identity_center_details
    await asyncio.gather(
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/iambic/plugins/v0_1_0/aws/models.py", line 442, in set_identity_center_details
    users_and_groups = await asyncio.gather(
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/iambic/plugins/v0_1_0/aws/utils.py", line 245, in legacy_paginated_search
    response = await boto_crud_call(search_fnc, **search_kwargs)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/iambic/plugins/v0_1_0/aws/utils.py", line 110, in boto_crud_call
    return await aio_wrapper(boto_fnc, **kwargs)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/iambic/core/utils.py", line 184, in aio_wrapper
    return await sync_to_async(fnc, thread_sensitive=thread_sensitive)(*args, **kwargs)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/asgiref/sync.py", line 479, in __call__
    ret: _R = await loop.run_in_executor(
  File "/usr/lib/python3.10/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/asgiref/sync.py", line 538, in thread_handler
    return func(*args, **kwargs)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/botocore/client.py", line 535, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/home/ssm-user/identity-center/venv/lib/python3.10/site-packages/botocore/client.py", line 980, in _make_api_call
    raise error_class(parsed_response, operation_name)

Expected behavior A clear and concise description of what you expected to happen.

Screenshots If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

Additional context Add any other context about the problem here.

Community Engagement Your vote counts! Please support this bug report by adding a 👍 reaction to the original issue, which will aid the community and maintainers in addressing this problem.

Please refrain from adding "+1" or "me too" comments, as these create unnecessary noise for issue followers and do not help in prioritizing the issue. If you wish to contribute to solving this issue or have submitted a pull request, please leave a comment.

smoy commented 1 year ago

There are some relevant report regarding similar behavior with identity store with AD integration: https://repost.aws/questions/QUTzWLsQVDSwuPkPJmTa9I7Q/identitystore-listusers-and-listgroups-api-results-in-unknownoperationexception

smoy commented 1 year ago

even when you supply a Filter to give you all the results, it might throw an error like [ERROR] ValidationException: An error occurred (ValidationException) when calling the ListUsers operation: The AD Sync username does not match the expected format of UserName@Domain Here we are using the format firstname.lastname@domain

smoy commented 1 year ago

some example of working filters

for list_users

Filters=[
        {
            'AttributePath': 'UserName',
            'AttributeValue': '*'
        }
    ]

for list_groups:

{
            'AttributePath': 'DisplayName',
            'AttributeValue': 'random-group@domain.local'
        }
dushyantb commented 1 year ago

@smoy just to clearify. Below Filter gives the error Filters=[ { 'AttributePath': 'UserName', 'AttributeValue': '*' } ]

And below filter works. Filters=[ { 'AttributePath': 'UserName', 'AttributeValue': 'username@domain' } ]

I guess the API checks for exact Values and returns data only if it finds an exact match. I am not aware of any values that can help list all users. If i give a incorrect username (but matches the format username@domain), It'll return an empty list.

If I submit '*' as value, then it raise ValidationError as it does not match the format username@domain

Similar way for list_groups, if the value matches the format (groupname@domain), it returns status 200. Else, it raises error.

dushyantbhardwaj9 commented 1 year ago

Hey @smoy ,

I build a script using python to list users and groups from the identity center.

Please try using this to fetch details and also revert change #558 back to user name

`

import requests, json, boto3
from botocore.auth import SigV4Auth
import requests
from botocore.awsrequest import AWSRequest
from botocore.credentials import Credentials
import botocore.session

region = "ap-south-1"
directory_id = "d-1234567890"

url = f"https://up-sso.{region}.amazonaws.com/identitystore/"
payload = {"IdentityStoreId":directory_id, "MaxResults" : 100}

data = json.dumps(payload)

def list_users(url, payload, data, region):
        session = botocore.session.Session()
        sigv4 = SigV4Auth(session.get_credentials(), 'identitystore', region)

        headers = {'Content-Type': 'application/x-amz-json-1.1', 'X-Amz-Target': 'AWSIdentityStoreService.SearchUsers'}
        request = AWSRequest(method='POST', url=url, data=data, headers=headers)
        request.context["payload_signing_enabled"] = True
        sigv4.add_auth(request)

        prepped = request.prepare()
        response = requests.post(prepped.url, headers=prepped.headers, data=data)

        id_resp = json.loads(response.text)
        total_users = []
        total_users.extend(id_resp['Users'])

        while 'NextToken' in id_resp:
                payload['NextToken'] = id_resp['NextToken']
                data = json.dumps(payload)
                request = AWSRequest(method='POST', url=url, data=data, headers=headers)
                request.context["payload_signing_enabled"] = True
                sigv4.add_auth(request)

                prepped = request.prepare()
                response = requests.post(prepped.url, headers=prepped.headers, data=data)
                id_resp = json.loads(response.text)
                total_users.extend(id_resp['Users'])
        return total_users

def list_groups(url, payload, data, region):
        session = botocore.session.Session()
        sigv4 = SigV4Auth(session.get_credentials(), 'identitystore', region)

        headers = {'Content-Type': 'application/x-amz-json-1.1', 'X-Amz-Target': 'AWSIdentityStoreService.SearchGroups'}
        request = AWSRequest(method='POST', url=url, data=data, headers=headers)
        request.context["payload_signing_enabled"] = True
        sigv4.add_auth(request)

        prepped = request.prepare()
        response = requests.post(prepped.url, headers=prepped.headers, data=data)

        id_resp = json.loads(response.text)
        total_groups = []
        total_groups.extend(id_resp['Groups'])

        while 'NextToken' in id_resp:
                payload['NextToken'] = id_resp['NextToken']
                data = json.dumps(payload)
                request = AWSRequest(method='POST', url=url, data=data, headers=headers)
                request.context["payload_signing_enabled"] = True
                sigv4.add_auth(request)

                prepped = request.prepare()
                response = requests.post(prepped.url, headers=prepped.headers, data=data)
                id_resp = json.loads(response.text)
                total_groups.extend(id_resp['Groups'])

        return total_groups

users = list_users(url, payload, data, region)
print(len(users))

groups = list_groups(url, payload, data, region)
print(len(groups))`

And here is sample response for list_users

{ "NextToken": "nextTokenValue", "TotalUserCount": 388, "Users": [ { "Active": true, "Meta": { "CreatedAt": 1631871184.587, "CreatedBy": "Identity_Sync", "UpdatedAt": 1692299060.935, "UpdatedBy": "Identity_Sync" }, "UserAttributes": { "emails": { "ComplexListValue": [ { "type": { "StringValue": "work" }, "value": { "StringValue": "username@domain" }, "primary": { "BooleanValue": true } } ] }, "displayName": { "StringValue": "firstName lastName" }, "name": { "ComplexValue": { "givenName": { "StringValue": "firstName" }, "familyName": { "StringValue": "lastName" } } }, "externalIds": { "ComplexListValue": [ { "id": { "StringValue": "00bc7e05-1111-1111-1111-ebcdc7f86bd0" }, "issuer": { "StringValue": "arn:aws:ds:ap-south-1:123456789012:directory/d-1111111111" } } ] }, "externalId": { "StringValue": "00bc7e05-1111-1111-1111-ebcdc7f86bd0" }, "activeDirectory": { "ComplexValue": { "windowsUpn": { "StringValue": "username@domain" }, "domain": { "StringValue": "domain" }, "guid": { "StringValue": "00bc7e05-1111-1111-1111-ebcdc7f86bd0" }, "sid": { "StringValue": "S-1-5-21-123456789-1234567890-1234567890-1111" } } } }, "UserId": "9f673e3bdb-11111111-1111-1111-1111-ebcdc7f86bd0", "UserName": "username@domain" }, { "Active": true, "Meta": { "CreatedAt": 1688546051.099, "CreatedBy": "AD_SYNC", "UpdatedAt": 1692299056.688, "UpdatedBy": "Identity_Sync" }, "UserAttributes": { "emails": { "ComplexListValue": [ { "verificationStatus": { "StringValue": "NOT_VERIFIED" }, "type": { "StringValue": "work" }, "value": { "StringValue": "username@domain" }, "primary": { "BooleanValue": true } } ] }, "displayName": { "StringValue": "firstName lastName" }, "name": { "ComplexValue": { "givenName": { "StringValue": "firstName" }, "familyName": { "StringValue": "lastName" } } }, "externalIds": { "ComplexListValue": [ { "id": { "StringValue": "009bc8a3-1111-1111-1111-3ff81a068d53" }, "issuer": { "StringValue": "arn:aws:ds:ap-south-1:123456789012:directory/d-1111111111" } } ] }, "externalId": { "StringValue": "009bc8a3-1111-1111-1111-3ff81a068d53" }, "activeDirectory": { "ComplexValue": { "windowsUpn": { "StringValue": "username@domain" }, "domain": { "StringValue": "domain" }, "guid": { "StringValue": "009bc8a3-1111-1111-1111-3ff81a068d53" }, "sid": { "StringValue": "S-1-5-21-123456789-1234567890-1234567890-1111" } } } }, "UserId": "9f673e3bdb-11111111-1111-1111-1111-3ff81a068d53", "UserName": "username@domain" }, { "Active": true, "Meta": { "CreatedAt": 1664277233.823, "CreatedBy": "Identity_Sync", "UpdatedAt": 1692299068.591, "UpdatedBy": "Identity_Sync" }, "UserAttributes": { "emails": { "ComplexListValue": [ { "verificationStatus": { "StringValue": "NOT_VERIFIED" }, "type": { "StringValue": "work" }, "value": { "StringValue": "username@domain" }, "primary": { "BooleanValue": true } } ] }, "displayName": { "StringValue": "firstName lastName" }, "name": { "ComplexValue": { "givenName": { "StringValue": "firstName" }, "familyName": { "StringValue": "lastName" } } }, "externalIds": { "ComplexListValue": [ { "id": { "StringValue": "014b1c65-1111-1111-1111-e745d36db3c5" }, "issuer": { "StringValue": "arn:aws:ds:ap-south-1:123456789012:directory/d-1111111111" } } ] }, "externalId": { "StringValue": "014b1c65-1111-1111-1111-e745d36db3c5" }, "activeDirectory": { "ComplexValue": { "windowsUpn": { "StringValue": "username@domain" }, "domain": { "StringValue": "domain" }, "guid": { "StringValue": "014b1c65-1111-1111-1111-e745d36db3c5" }, "sid": { "StringValue": "S-1-5-21-123456789-1234567890-1234567890-1111" } } } }, "UserId": "9f673e3bdb-014b1c65-1111-1111-1111-e745d36db3c5", "UserName": "username@domain" } ] }

And sample of list_groups

{ "Groups": [ { "DisplayName": "groupName@domain", "GroupAttributes": { "externalIds": { "ComplexListValue": [ { "id": { "StringValue": "19c0c068-1111-1111-1111-bff5f170683a" }, "issuer": { "StringValue": "arn:aws:ds:ap-south-1:123456789012:directory/d-1111111111" } } ] }, "description": { "StringValue": "group description" }, "externalId": { "StringValue": "19c0c068-1111-1111-1111-bff5f170683a" }, "activeDirectory": { "ComplexValue": { "domain": { "StringValue": "domain" }, "sid": { "StringValue": "S-1-5-21-123456789-1234567890-1234567890-1111" } } } }, "GroupId": "9f673e3bdb-19c0c068-1111-1111-1111-bff5f170683a", "Meta": { "CreatedAt": 1686557190.166, "CreatedBy": "Identity_Sync", "UpdatedAt": 1692299191.001, "UpdatedBy": "Identity_Sync" } }, { "DisplayName": "groupname@domain", "GroupAttributes": { "externalIds": { "ComplexListValue": [ { "id": { "StringValue": "03aaf141-1111-1111-1111-5aa43f4b9b81" }, "issuer": { "StringValue": "arn:aws:ds:ap-south-1:123456789012:directory/d-1111111111" } } ] }, "description": { "StringValue": "group description" }, "externalId": { "StringValue": "03aaf141-1111-1111-1111-5aa43f4b9b81" }, "activeDirectory": { "ComplexValue": { "domain": { "StringValue": "domain" }, "sid": { "StringValue": "S-1-5-21-123456789-1234567890-1234567890-1111" } } } }, "GroupId": "9f673e3bdb-03aaf141-1111-1111-1111-5aa43f4b9b81", "Meta": { "CreatedAt": 1684141140.688, "CreatedBy": "Identity_Sync", "UpdatedAt": 1692299195.135, "UpdatedBy": "Identity_Sync" } }, { "DisplayName": "groupname@domain", "GroupAttributes": { "externalIds": { "ComplexListValue": [ { "id": { "StringValue": "0fddf922-1111-1111-1111-80f46f83191b" }, "issuer": { "StringValue": "arn:aws:ds:ap-south-1:123456789012:directory/d-1111111111" } } ] }, "description": { "StringValue": "group description" }, "externalId": { "StringValue": "0fddf922-1111-1111-1111-80f46f83191b" }, "activeDirectory": { "ComplexValue": { "domain": { "StringValue": "domain" }, "sid": { "StringValue": "S-1-5-21-123456789-1234567890-1234567890-1111" } } } }, "GroupId": "9f673e3bdb-0fddf922-1111-1111-1111-80f46f83191b", "Meta": { "CreatedAt": 1644258484.525, "CreatedBy": "Identity_Sync", "UpdatedAt": 1692299163.992, "UpdatedBy": "Identity_Sync" } } ], "NextToken": "next-token", "TotalGroupCount": 43 }

smoy commented 1 year ago

Notes when switching directory source in IdentityCenter:

You are changing your identity source to directory REDACTED (AWS Directory Service). The AWS access portal URL will change to enable your directory as your identity source. The current URL won't work. IAM Identity Center will permanently remove all current user and group assignments. Users and groups currently in Identity Center won't be available for use. If you switch back to IAM Identity Center as an identity source, these users and groups will be restored without assignments. All current permission sets and SAML 2.0 application configurations will be retained. You must manage all users and groups in your new directory in Active Directory. IAM Identity Center will start synchronizing users and groups with assignments from Active Directory (AD) through Active Directory sync. You can configure multi-factor authentication (MFA) in AWS Directory Service, or through the IAM Identity Center console. If you use other AWS applications with AWS Directory Service, we recommend that you configure MFA in AWS Directory Service. Users must sign in to the AWS access portal before you can view, manage, or assign them to Identity Center enabled applications. IAM Identity Center will keep your current configuration of attributes for access control. We recommend that you review your configuration and update it after you complete the identity source change.

smoy commented 1 year ago

We have a pull request being evaluate at the moment: https://github.com/noqdev/iambic/pull/591

smoy commented 1 year ago

I am closing this issue since we have merge multiple enhancement for Active Directory.

For future readers, please open new issue and mention to this one if it's relevant.