11import dataclasses
22import json
3+ import uuid
34from typing import Optional
45
56import boto3
7+ import botocore
68import requests
79
810from .constants import ISSUER
@@ -16,8 +18,7 @@ def get_console_url(session: boto3.Session) -> str:
1618def get_signin_token (session : boto3 .Session ) -> str :
1719 credentials = session .get_credentials ().get_frozen_credentials ()
1820 if not credentials .token :
19- # We assume we're using SSO or AssumeRole, these work out of the box
20- raise NotImplementedError ("We only support credentials from SSO or STS" )
21+ credentials = _get_federation_token (session )
2122
2223 parameters = {
2324 "Action" : "getSigninToken" ,
@@ -61,6 +62,21 @@ def _get_session() -> boto3.Session:
6162 return boto3 .Session ()
6263
6364
65+ def _get_federation_token (session : boto3 .Session ) -> botocore .credentials .ReadOnlyCredentials :
66+ # If we are starting form a User, we need to call GetFederationToken (GetSessionToken does not work here)
67+ # in the managed policies only the PowerUserAccess and AdministratorAccess allow this
68+ sts = session .client ("sts" )
69+ resp = sts .get_federation_token (
70+ Name = uuid .uuid4 ().hex ,
71+ PolicyArns = [{"arn" : "arn:aws:iam::aws:policy/AdministratorAccess" }],
72+ )["Credentials" ]
73+ return botocore .credentials .ReadOnlyCredentials (
74+ resp ["AccessKeyId" ],
75+ resp ["SecretAccessKey" ],
76+ resp ["SessionToken" ],
77+ )
78+
79+
6480def _signin_endpoint (session : boto3 .Session ) -> str :
6581 region = session .region_name
6682 if region and region .startswith ("us-gov" ):
@@ -82,12 +98,15 @@ def _console_endpoint(session: boto3.Session) -> str:
8298@dataclasses .dataclass
8399class Arn :
84100 "arn:aws:sts::123456789012:assumed-role/my-role-name/my-role-session-name"
101+ "arn:aws:iam::123456789012:user/user-name-with-path"
102+ "arn:aws:sts::123456789012:federated-user/user-name"
103+ "arn:aws:iam::123456789012:root"
85104 partition : str
86105 service : str
87106 region : Optional [str ]
88107 account_id : Optional [str ]
89108 resource_type : str
90- resource_id : str
109+ resource_id : Optional [ str ]
91110
92111 def __init__ (self , arn : str ):
93112 parts = arn .split (":" )
@@ -104,4 +123,7 @@ def __init__(self, arn: str):
104123 # arn:partition:service:region:account-id:resource-type/resource-id
105124 resource_parts = parts [5 ].split ("/" , 1 )
106125 self .resource_type = resource_parts [0 ]
107- self .resource_id = resource_parts [1 ]
126+ if self .resource_type == "root" :
127+ self .resource_id = None
128+ else :
129+ self .resource_id = resource_parts [1 ]
0 commit comments