aws-samples / iam-identity-center-team

Open-source temporary elevated access solution for AWS IAM Identity Center.
https://aws-samples.github.io/iam-identity-center-team/
MIT No Attribution
250 stars 59 forks source link

Revoked access does not publish to SNS queue #205

Closed robbycuenot closed 1 month ago

robbycuenot commented 3 months ago

Describe the bug I've built a wrapper for the SNS notifications, and revocation messages appear to be vanishing. I've invoked the teamNotifications-main Lambda with the payload from TEAM-Revoke-SM-main - Notify Requester Session Ended, and the lambda reports success, but the message never reaches SNS.

To Reproduce Steps to reproduce the behavior:

  1. Request access
  2. Approve access
  3. Wait for access to be assigned
  4. Revoke active access

Expected behavior An SNS message should be published to the topic TeamNotifications-main, with the details of the revocation.

Additional context It appears that the switch case in the lambda itself is missing the "revoked" status: https://github.com/aws-samples/iam-identity-center-team/blob/main/amplify/backend/function/teamNotifications/src/index.py

It would be helpful to add logging to this function as well

robbycuenot commented 3 months ago

This may be user error, though the behavior is tricky to trace down. I think the issue is related to this block:

    ended = (
        event.get("revoke", {})
        .get("AccountAssignmentDeletionStatus", {})
        .get("Status", "")
        == "IN_PROGRESS"
    )

When I opened this ticket, the payload being passed from the step function followed this format (note, this is sanitized to remove environment specific info):

{
  "email": "CURRENTUSEREMAIL",
  "username": "idc_CURRENTUSERUPN",
  "duration": "3600",
  "accountId": "012345678901",
  "status": "revoked",
  "accountName": "ExampleAccount",
  "id": "98765432-9876-9876-9876-987654321098",
  "role": "TEAM-ExampleAccess",
  "roleId": "arn:aws:sso:::permissionSet/ssoins-0123456789012345/ps-0123456789012345",
  "time": "1",
  "startTime": "2024-02-15T12:00:00-05:00",
  "justification": "Example Justification",
  "ticketNo": "1234",
  "approver": "CURRENTUSEREMAIL",
  "revoker": "CURRENTUSEREMAIL",
  "instanceARN": "arn:aws:sso:::instance/ssoins-0123456789012345",
  "approvers": [
    "CURRENTUSEREMAIL",
    "CURRENTUSEREMAIL"
  ],
  "expire": 86400,
  "approvalRequired": true,
  "userId": "12345678-1234-1234-1234-123456789012",
  "ses_notifications_enabled": false,
  "sns_notifications_enabled": true,
  "slack_notifications_enabled": false,
  "ses_source_email": "",
  "ses_source_arn": "",
  "notification_topic_arn": "arn:aws:sns:us-west-2:012345678901:TeamNotifications-main",
  "sso_login_url": "https://example.awsapps.com/start",
  "requests_table": "requests-abcdefghijklmnopqrstuvwxyz-main",
  "revoke_sm": "arn:aws:states:us-west-2:012345678901:stateMachine:TEAM-Revoke-SM-main",
  "grant_sm": "arn:aws:states:us-west-2:012345678901:stateMachine:TEAM-Grant-SM-main",
  "fn_teamstatus_arn": "arn:aws:lambda:us-west-2:012345678901:function:teamStatus-main",
  "fn_teamnotifications_arn": "arn:aws:lambda:us-west-2:012345678901:function:teamNotifications-main",
  "data": {
    "Item": {
      "role": {
        "S": "TEAM-ExampleAccess"
      },
      "accountName": {
        "S": "ExampleAccount"
      },
      "__typename": {
        "S": "requests"
      },
      "approvers": {
        "L": [
          {
            "S": "CURRENTUSEREMAIL"
          },
          {
            "S": "CURRENTUSEREMAIL"
          }
        ]
      },
      "revokerId": {
        "S": "idc_CURRENTUSERUPN"
      },
      "session_duration": {
        "S": "PT4H"
      },
      "duration": {
        "S": "1"
      },
      "createdAt": {
        "S": "2024-03-19T19:49:47.578Z"
      },
      "ticketNo": {
        "S": ""
      },
      "startTime": {
        "S": "2024-03-19T20:14:14.327Z"
      },
      "justification": {
        "S": "Example Justification"
      },
      "id": {
        "S": "12345678-1234-1234-1234-123456789012"
      },
      "email": {
        "S": "CURRENTUSEREMAIL"
      },
      "updatedAt": {
        "S": "2024-03-19T20:15:03.727Z"
      },
      "owner": {
        "S": "12345678-1234-1234-1234-123456789012::idc_CURRENTUSERUPN"
      },
      "approver": {
        "S": "CURRENTUSEREMAIL"
      },
      "approverId": {
        "S": "idc_CURRENTUSERUPN"
      },
      "roleId": {
        "S": "arn:aws:sso:::permissionSet/ssoins-0123456789012345/ps-0123456789012345"
      },
      "accountId": {
        "S": "012345678901"
      },
      "revokeComment": {
        "S": "Example Revoke Comment"
      },
      "approver_ids": {
        "L": [
          {
            "S": "idc_CURRENTUSERUPN"
          },
          {
            "S": "idc_CURRENTUSERUPN"
          }
        ]
      },
      "comment": {
        "S": "Example Comment"
      },
      "revoker": {
        "S": "CURRENTUSEREMAIL"
      },
      "status": {
        "S": "revoked"
      },
      "username": {
        "S": "idc_CURRENTUSERUPN"
      }
    },
    "SdkHttpMetadata": {
      "AllHttpHeaders": {
        "Server": [
          "Server"
        ],
        "Connection": [
          "keep-alive"
        ],
        "x-amzn-RequestId": [
          "G16IGTVHS3IIFMCNDNHUEJNKJASNDKJNKJASNDO5AEMVJF66Q9JG"
        ],
        "x-amz-crc32": [
          "0123456789"
        ],
        "Content-Length": [
          "1000"
        ],
        "Date": [
          "Tue, 19 Mar 2024 20:15:16 GMT"
        ],
        "Content-Type": [
          "application/x-amz-json-1.0"
        ]
      },
      "HttpHeaders": {
        "Connection": "keep-alive",
        "Content-Length": "1000",
        "Content-Type": "application/x-amz-json-1.0",
        "Date": "Tue, 19 Mar 2024 20:15:16 GMT",
        "Server": "Server",
        "x-amz-crc32": "0123456789",
        "x-amzn-RequestId": "G16IGTVHS3IIFMCNDNHUEJNKJASNDKJNKJASNDO5AEMVJF66Q9JG"
      },
      "HttpStatusCode": 200
    },
    "SdkResponseMetadata": {
      "RequestId": "G16IGTVHS3IIFMCNDNHUEJNKJASNDKJNKJASNDO5AEMVJF66Q9JG"
    }
  },
  "revoke": {
    "AccountAssignmentDeletionStatus": {
      "PermissionSetArn": "arn:aws:sso:::permissionSet/ssoins-0123456789012345/ps-0123456789012345",
      "PrincipalId": "9876543210-98765432-9876-9876-9876-987654321098",
      "PrincipalType": "USER",
      "RequestId": "98765432-9876-9876-9876-987654321098",
      "Status": "IN_PROGRESS",
      "TargetId": "012345678901",
      "TargetType": "AWS_ACCOUNT"
    }
  }
}

At that time, no output was being sent to SNS, and I have no idea why. I tested again with the same payload this morning and the messages were being delivered to SNS. Weird. In any case, now that the messages are working, when this message is sent to the teamNotifications-main lambda, the status changes to ended, and the revoked status is nested under .data.Item.status.S. Here is the output from the lambda after the above json is passed from the step function:

{
  "email": "CURRENTUSEREMAIL",
  "username": "idc_CURRENTUSERUPN",
  "duration": "3600",
  "accountId": "012345678901",
  "status": "ended",
  "accountName": "ExampleAccount",
  "id": "98765432-9876-9876-9876-987654321098",
  "role": "TEAM-ExampleAccess",
  "roleId": "arn:aws:sso:::permissionSet/ssoins-0123456789012345/ps-0123456789012345",
  "time": "1",
  "startTime": "2024-02-15T12:00:00-05:00",
  "justification": "Example Justification",
  "ticketNo": "1234",
  "approver": "CURRENTUSEREMAIL",
  "revoker": "CURRENTUSEREMAIL",
  "instanceARN": "arn:aws:sso:::instance/ssoins-0123456789012345",
  "approvers": [
    "CURRENTUSEREMAIL",
    "CURRENTUSEREMAIL"
  ],
  "expire": 86400,
  "approvalRequired": true,
  "userId": "12345678-1234-1234-1234-123456789012",
  "ses_notifications_enabled": false,
  "sns_notifications_enabled": true,
  "slack_notifications_enabled": false,
  "ses_source_email": "",
  "ses_source_arn": "",
  "notification_topic_arn": "arn:aws:sns:us-west-2:012345678901:TeamNotifications-main",
  "sso_login_url": "https://example.awsapps.com/start",
  "requests_table": "requests-abcdefghijklmnopqrstuvwxyz-main",
  "revoke_sm": "arn:aws:states:us-west-2:012345678901:stateMachine:TEAM-Revoke-SM-main",
  "grant_sm": "arn:aws:states:us-west-2:012345678901:stateMachine:TEAM-Grant-SM-main",
  "fn_teamstatus_arn": "arn:aws:lambda:us-west-2:012345678901:function:teamStatus-main",
  "fn_teamnotifications_arn": "arn:aws:lambda:us-west-2:012345678901:function:teamNotifications-main",
  "data": {
    "Item": {
      "role": {
        "S": "TEAM-ExampleAccess"
      },
      "accountName": {
        "S": "ExampleAccount"
      },
      "__typename": {
        "S": "requests"
      },
      "approvers": {
        "L": [
          {
            "S": "CURRENTUSEREMAIL"
          },
          {
            "S": "CURRENTUSEREMAIL"
          }
        ]
      },
      "revokerId": {
        "S": "idc_CURRENTUSERUPN"
      },
      "session_duration": {
        "S": "PT4H"
      },
      "duration": {
        "S": "1"
      },
      "createdAt": {
        "S": "2024-03-19T19:49:47.578Z"
      },
      "ticketNo": {
        "S": ""
      },
      "startTime": {
        "S": "2024-03-19T20:14:14.327Z"
      },
      "justification": {
        "S": "Example Justification"
      },
      "id": {
        "S": "12345678-1234-1234-1234-123456789012"
      },
      "email": {
        "S": "CURRENTUSEREMAIL"
      },
      "updatedAt": {
        "S": "2024-03-19T20:15:03.727Z"
      },
      "owner": {
        "S": "12345678-1234-1234-1234-123456789012::idc_CURRENTUSERUPN"
      },
      "approver": {
        "S": "CURRENTUSEREMAIL"
      },
      "approverId": {
        "S": "idc_CURRENTUSERUPN"
      },
      "roleId": {
        "S": "arn:aws:sso:::permissionSet/ssoins-0123456789012345/ps-0123456789012345"
      },
      "accountId": {
        "S": "012345678901"
      },
      "revokeComment": {
        "S": "Example Revoke Comment"
      },
      "approver_ids": {
        "L": [
          {
            "S": "idc_CURRENTUSERUPN"
          },
          {
            "S": "idc_CURRENTUSERUPN"
          }
        ]
      },
      "comment": {
        "S": "Example Comment"
      },
      "revoker": {
        "S": "CURRENTUSEREMAIL"
      },
      "status": {
        "S": "revoked"
      },
      "username": {
        "S": "idc_CURRENTUSERUPN"
      }
    },
    "SdkHttpMetadata": {
      "AllHttpHeaders": {
        "Server": [
          "Server"
        ],
        "Connection": [
          "keep-alive"
        ],
        "x-amzn-RequestId": [
          "G16IGTVHS3IIFMCNDNHUEJNKJASNDKJNKJASNDO5AEMVJF66Q9JG"
        ],
        "x-amz-crc32": [
          "0123456789"
        ],
        "Content-Length": [
          "1000"
        ],
        "Date": [
          "Tue, 19 Mar 2024 20:15:16 GMT"
        ],
        "Content-Type": [
          "application/x-amz-json-1.0"
        ]
      },
      "HttpHeaders": {
        "Connection": "keep-alive",
        "Content-Length": "1000",
        "Content-Type": "application/x-amz-json-1.0",
        "Date": "Tue, 19 Mar 2024 20:15:16 GMT",
        "Server": "Server",
        "x-amz-crc32": "0123456789",
        "x-amzn-RequestId": "G16IGTVHS3IIFMCNDNHUEJNKJASNDKJNKJASNDO5AEMVJF66Q9JG"
      },
      "HttpStatusCode": 200
    },
    "SdkResponseMetadata": {
      "RequestId": "G16IGTVHS3IIFMCNDNHUEJNKJASNDKJNKJASNDO5AEMVJF66Q9JG"
    }
  },
  "revoke": {
    "AccountAssignmentDeletionStatus": {
      "PermissionSetArn": "arn:aws:sso:::permissionSet/ssoins-0123456789012345/ps-0123456789012345",
      "PrincipalId": "9876543210-98765432-9876-9876-9876-987654321098",
      "PrincipalType": "USER",
      "RequestId": "98765432-9876-9876-9876-987654321098",
      "Status": "IN_PROGRESS",
      "TargetId": "012345678901",
      "TargetType": "AWS_ACCOUNT"
    }
  }
}

Took me a while to track this down and write the logic that handles this case. It seems that some other statuses such as expired have a similar logic, where the parent status field shows pending but under Payload.Payload.body, there is a stringified json object where data.updateRequests.status is shown as expired. Definitely tricky to navigate through nested and stringified json objects to get this data.

I'm not sure what the resolution would be here, other than more consistency in the message formatting

tawoyinfa commented 3 months ago

@robbycuenot looking into this. I am also interested in the wrapper you have built for the SNS notification. How are you using it today ?

robbycuenot commented 3 months ago

@tawoyinfa We use the SNS notifications to build and send messages to MS Teams. I've alluded to sharing the code, I just need to make a sanitized version in my free-time. In short, we get the SNS notification, parse out the user, approver, and revoker details, and then send a message to an MS Teams channel webhook that @ mentions the involved parties

github-actions[bot] commented 1 month ago

Marking this issue as stale due to inactivity. This helps our maintainers find and focus on the active issues. If this issue receives no comments in the next 7 days it will automatically be closed.