diff --git a/src/GamUpdate.txt b/src/GamUpdate.txt index c7439e95..01993905 100644 --- a/src/GamUpdate.txt +++ b/src/GamUpdate.txt @@ -1,11 +1,10 @@ 7.08.03 -Fixed bug in `gam check|update serviceaccount` where the first use of -the command after project creation enabled the following scopes that should be off by default. -``` -Identity and Access Management API -Youtube API - read only -``` +Removed the overly broad service account `IAM and Access Management API` scope `https://www.googleapis.com/auth/cloud-platform` +from DWD. The `gam check|Update serviceaccount` commands issue an error message if this scope +is enabled promptig you to update your service account authorization so that the scope can be removed. + +GAM commands that need IAM access now use the more limited scope `https://www.googleapis.com/auth/iam` in a non-DWD manner. 7.08.02 diff --git a/src/gam/__init__.py b/src/gam/__init__.py index 08d6a6c9..d861bd3b 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -10632,6 +10632,7 @@ def getOAuthClientIDAndSecret(): invalidClientSecretsJsonExit(str(e)) def getScopesFromUser(scopesList, clientAccess, currentScopes=None): + forceOffScopes = [] OAUTH2_CMDS = ['s', 'u', 'e', 'c'] oauth2_menu = '' numScopes = len(scopesList) @@ -10682,6 +10683,9 @@ Continue to authorization by entering a 'c' break i += 1 else: + for api in currentScopes: + if api in API.FORCE_OFF_SA_SCOPES: + forceOffScopes.extend(currentScopes[api]) i = 0 for a_scope in scopesList: selectedScopes[i] = ' ' @@ -10750,12 +10754,12 @@ Continue to authorization by entering a 'c' for i in range(numScopes): selectedScopes[i] = ' ' elif selection == 'e': - return None + return (None, None) break sys.stdout.write(f'{ERROR_PREFIX}Invalid input "{choice}"\n') if selection == 'c': break - return selectedScopes + return (selectedScopes, forceOffScopes) def _localhost_to_ip(): '''returns IPv4 or IPv6 loopback address which localhost resolves to. @@ -11102,7 +11106,7 @@ def doOAuthRequest(currentScopes, login_hint, verifyScopes=False): client_id, client_secret = getOAuthClientIDAndSecret() scopesList = API.getClientScopesList(GC.Values[GC.TODRIVE_CLIENTACCESS]) if not currentScopes or verifyScopes: - selectedScopes = getScopesFromUser(scopesList, True, currentScopes) + selectedScopes, _ = getScopesFromUser(scopesList, True, currentScopes) if selectedScopes is None: return False scopes = set(API.REQUIRED_SCOPES) @@ -12246,6 +12250,7 @@ def checkServiceAccount(users): checkScopesSet = set() saScopes = {} useColor = False + forceOffScopes = [] while Cmd.ArgumentsRemaining(): myarg = getArgument() if myarg in {'scope', 'scopes'}: @@ -12271,12 +12276,16 @@ def checkServiceAccount(users): testWarn = 'WARN' if Act.Get() == Act.CHECK: if not checkScopesSet: - for scope in iter(GM.Globals[GM.SVCACCT_SCOPES].values()): - checkScopesSet.update(scope) + currentScopes = GM.Globals[GM.SVCACCT_SCOPES] + for api in currentScopes: + if api in API.FORCE_OFF_SA_SCOPES: + forceOffScopes.extend(currentScopes[api]) + else: + checkScopesSet.update(currentScopes[api]) else: if not checkScopesSet: scopesList = API.getSvcAcctScopesList(GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY], True) - selectedScopes = getScopesFromUser(scopesList, False, GM.Globals[GM.SVCACCT_SCOPES] if GM.Globals[GM.SVCACCT_SCOPES_DEFINED] else None) + selectedScopes, forceOffScopes = getScopesFromUser(scopesList, False, GM.Globals[GM.SVCACCT_SCOPES] if GM.Globals[GM.SVCACCT_SCOPES_DEFINED] else None) if selectedScopes is None: return False i = 0 @@ -12399,6 +12408,17 @@ def checkServiceAccount(users): scopeStatus = testFail allScopesPass = False printPassFail(scope, f'{scopeStatus}{currentCount(j, jcount)}') + if forceOffScopes: + allScopesPass = False + if useColor: + scopeStatus = createRedText('DISABLE') + else: + scopeStatus = 'DISABLE' + jcount = len(forceOffScopes) + j = 0 + for scope in forceOffScopes: + j +=1 + printPassFail(scope, f'{scopeStatus}{currentCount(j, jcount)}') Ind.Decrement() service_account = GM.Globals[GM.OAUTH2SERVICE_JSON_DATA]['client_id'] if allScopesPass: diff --git a/src/gam/gamlib/glapi.py b/src/gam/gamlib/glapi.py index 4aaa9b6d..2856c57a 100644 --- a/src/gam/gamlib/glapi.py +++ b/src/gam/gamlib/glapi.py @@ -118,6 +118,7 @@ JWT_APIS = { ACCESSCONTEXTMANAGER: [CLOUD_PLATFORM_SCOPE], CHAT: ['https://www.googleapis.com/auth/chat.bot'], CLOUDRESOURCEMANAGER: [CLOUD_PLATFORM_SCOPE], + IAM: [IAM_SCOPE], ORGPOLICY: [CLOUD_PLATFORM_SCOPE], } # @@ -131,6 +132,8 @@ APIS_NEEDING_ACCESS_TOKEN = { CBCM: ['https://www.googleapis.com/auth/admin.directory.device.chromebrowsers'] } # +FORCE_OFF_SA_SCOPES = {IAM} +# REFRESH_PERM_ERRORS = [ 'invalid_grant: reauth related error (rapt_required)', # no way to reauth today 'invalid_grant: Token has been expired or revoked', @@ -645,11 +648,11 @@ _SVCACCT_SCOPES = [ 'api': GMAIL, 'subscopes': [], 'scope': 'https://www.googleapis.com/auth/gmail.settings.sharing'}, - {'name': 'Identity and Access Management API', - 'api': IAM, - 'offByDefault': True, - 'subscopes': [], - 'scope': IAM_SCOPE}, +# {'name': 'Identity and Access Management API', +# 'api': IAM, +# 'offByDefault': True, +# 'subscopes': [], +# 'scope': IAM_SCOPE}, {'name': 'Keep API', 'api': KEEP, 'subscopes': READONLY, diff --git a/src/gam/gamlib/glmsgs.py b/src/gam/gamlib/glmsgs.py index df67c35a..7ac82ab4 100644 --- a/src/gam/gamlib/glmsgs.py +++ b/src/gam/gamlib/glmsgs.py @@ -145,7 +145,7 @@ SCOPE_AUTHORIZATION_PASSED = '''All scopes PASSED! Service Account Client name: {0} is fully authorized. ''' SCOPE_AUTHORIZATION_UPDATE_PASSED = '''All scopes PASSED! -To authorize them (in case some scopes were unselected), please go to the following link in your browser: +To update authorization (in case some scopes were unselected), please go to the following link in your browser: {0} {1} @@ -156,8 +156,8 @@ Click AUTHORIZE When the box closes you're done After authorizing it may take some time for this test to pass so wait a few moments and then try this command again. ''' -SCOPE_AUTHORIZATION_FAILED = '''Some scopes FAILED! -To authorize them, please go to the following link in your browser: +SCOPE_AUTHORIZATION_FAILED = '''Some scopes FAILED or should be DISABLED! +To update authorization, please go to the following link in your browser: {0} {1}