Compare commits

...

2 Commits

Author SHA1 Message Date
Ross Scroggs
367c23a13c Updated gam create project to retry the following unexpected error:
Some checks failed
Build and test GAM / build (Win64, build, 10, VC-WIN64A, windows-2022) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 3, linux-aarch64, ubuntu-24.04-arm) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 4, linux-aarch64, ubuntu-22.04-arm) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 6, linux-aarch64, ubuntu-22.04-arm, yes) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 8, darwin64-arm64, macos-14) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 9, darwin64-arm64, macos-15) (push) Has been cancelled
Build and test GAM / build (x86_64, build, 1, linux-x86_64, ubuntu-22.04) (push) Has been cancelled
Build and test GAM / build (x86_64, build, 2, linux-x86_64, ubuntu-24.04) (push) Has been cancelled
Build and test GAM / build (x86_64, build, 5, linux-x86_64, ubuntu-22.04, yes) (push) Has been cancelled
Build and test GAM / build (x86_64, build, 7, darwin64-x86_64, macos-13) (push) Has been cancelled
Build and test GAM / build (x86_64, test, 11, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (x86_64, test, 12, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (x86_64, test, 13, ubuntu-24.04, 3.12) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Check for Google Root CA Updates / check-apis (push) Has been cancelled
Build and test GAM / merge (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
ERROR: 400 - invalidArgument - Service account gam-project-a1b2c@gam-project-a1b2c.iam.gserviceaccount.com does not exist.
2025-01-27 08:16:37 -08:00
Ross Scroggs
82e8977003 Updated gam create|use project to discontinue use of the Identity-Aware Proxy (IAP) OAuth Admin APIs
Some checks failed
Build and test GAM / build (Win64, build, 10, VC-WIN64A, windows-2022) (push) Waiting to run
Build and test GAM / build (aarch64, build, 3, linux-aarch64, ubuntu-24.04-arm) (push) Waiting to run
Build and test GAM / build (aarch64, build, 4, linux-aarch64, ubuntu-22.04-arm) (push) Waiting to run
Build and test GAM / build (aarch64, build, 6, linux-aarch64, ubuntu-22.04-arm, yes) (push) Waiting to run
Build and test GAM / build (aarch64, build, 8, darwin64-arm64, macos-14) (push) Waiting to run
Build and test GAM / build (aarch64, build, 9, darwin64-arm64, macos-15) (push) Waiting to run
Build and test GAM / build (x86_64, build, 1, linux-x86_64, ubuntu-22.04) (push) Waiting to run
Build and test GAM / build (x86_64, build, 2, linux-x86_64, ubuntu-24.04) (push) Waiting to run
Build and test GAM / build (x86_64, build, 5, linux-x86_64, ubuntu-22.04, yes) (push) Waiting to run
Build and test GAM / build (x86_64, build, 7, darwin64-x86_64, macos-13) (push) Waiting to run
Build and test GAM / build (x86_64, test, 11, ubuntu-24.04, 3.10) (push) Waiting to run
Build and test GAM / build (x86_64, test, 12, ubuntu-24.04, 3.11) (push) Waiting to run
Build and test GAM / build (x86_64, test, 13, ubuntu-24.04, 3.12) (push) Waiting to run
Build and test GAM / merge (push) Blocked by required conditions
Build and test GAM / publish (push) Blocked by required conditions
Check for Google Root CA Updates / check-apis (push) Waiting to run
CodeQL / Analyze (python) (push) Has been cancelled
2025-01-26 15:30:45 -08:00
5 changed files with 122 additions and 60 deletions

View File

@@ -1362,6 +1362,7 @@ gam create project [admin <EmailAddress>] [project <ProjectID>]
nokey] nokey]
gam use project [<EmailAddress>] [<ProjectID>] gam use project [<EmailAddress>] [<ProjectID>]
gam use project [admin <EmailAddress>] [project <ProjectID>] gam use project [admin <EmailAddress>] [project <ProjectID>]
[appname <String>] [supportemail <EmailAddress>]
[saname <ServiceAccountName>] [sadisplayname <ServiceAccountDisplayName>] [saname <ServiceAccountName>] [sadisplayname <ServiceAccountDisplayName>]
[sadescription <ServiceAccountDescription>] [sadescription <ServiceAccountDescription>]
[(algorithm KEY_ALG_RSA_1024|KEY_ALG_RSA_2048)| [(algorithm KEY_ALG_RSA_1024|KEY_ALG_RSA_2048)|
@@ -6575,6 +6576,8 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
[copysubfolderpermissions [<Boolean>]] [copysubfolderpermissions [<Boolean>]]
[copysubfolderinheritedpermissions [<Boolean>]] [copysubfolderinheritedpermissions [<Boolean>]]
[copysubfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders] [copysubfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders]
[copypermissionroles <DriveFileACLRoleList>]
[copypermissiontypes <DriveFileACLTypeList>]
[excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>] [excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>]
(mappermissionsdomain <DomainName> <DomainName>)* (mappermissionsdomain <DomainName> <DomainName>)*
[copysheetprotectedranges [<Boolean>]] [copysheetprotectedranges [<Boolean>]]

View File

@@ -1,3 +1,20 @@
7.03.01
Updated `gam create project` to retry the following unexpected error:
```
ERROR: 400 - invalidArgument - Service account gam-project-a1b2c@gam-project-a1b2c.iam.gserviceaccount.com does not exist.
```
7.03.00
Updated `gam create|use project` to discontinue use of the `Identity-Aware Proxy (IAP) OAuth Admin APIs`
that are being deprecated by Google. You will see a set of instructions detailing how to
configure the Oauth Consent screen and create the Oauth client.
Added options `copypermissionroles <DriveFileACLRoleList>` and `copypermissiontypes <DriveFileACLTypeList>`
to `gam <UserTypeEntity> copy drivefile` that provide more control over what permissions are copied
from the source files/folders to the destination files/folders.
7.02.11 7.02.11
Updated `gam report <ActivityApplicationName>` to display `id:<actor.profileId>` in the `emailAddress` column Updated `gam report <ActivityApplicationName>` to display `id:<actor.profileId>` in the `emailAddress` column

View File

@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
""" """
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>' __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
__version__ = '7.02.11' __version__ = '7.03.01'
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
#pylint: disable=wrong-import-position #pylint: disable=wrong-import-position
@@ -4727,7 +4727,7 @@ def clearServiceCache(service):
DISCOVERY_URIS = [googleapiclient.discovery.V1_DISCOVERY_URI, googleapiclient.discovery.V2_DISCOVERY_URI] DISCOVERY_URIS = [googleapiclient.discovery.V1_DISCOVERY_URI, googleapiclient.discovery.V2_DISCOVERY_URI]
# Used for API.CLOUDRESOURCEMANAGER, API.SERVICEUSAGE, API.IAM, API.IAP # Used for API.CLOUDRESOURCEMANAGER, API.SERVICEUSAGE, API.IAM
def getAPIService(api, httpObj): def getAPIService(api, httpObj):
api, version, v2discovery = API.getVersion(api) api, version, v2discovery = API.getVersion(api)
return googleapiclient.discovery.build(api, version, http=httpObj, cache_discovery=False, return googleapiclient.discovery.build(api, version, http=httpObj, cache_discovery=False,
@@ -11361,13 +11361,32 @@ def doEnableAPIs():
url = f'https://console.cloud.google.com/apis/enableflow?apiid={apiid}&project={projectId}' url = f'https://console.cloud.google.com/apis/enableflow?apiid={apiid}&project={projectId}'
writeStdout(f' {url}\n\n') writeStdout(f' {url}\n\n')
def _waitForSvcAcctCompletion(i):
sleep_time = i*5
if i > 3:
sys.stdout.write(Msg.WAITING_FOR_ITEM_CREATION_TO_COMPLETE_SLEEPING.format(Ent.Singular(Ent.SVCACCT), sleep_time))
time.sleep(sleep_time)
def _grantRotateRights(iam, projectId, service_account, email, account_type='serviceAccount'): def _grantRotateRights(iam, projectId, service_account, email, account_type='serviceAccount'):
printEntityMessage([Ent.PROJECT, projectId, Ent.SVCACCT, email],
Msg.HAS_RIGHTS_TO_ROTATE_OWN_PRIVATE_KEY.format(email, service_account))
body = {'policy': {'bindings': [{'role': 'roles/iam.serviceAccountKeyAdmin', body = {'policy': {'bindings': [{'role': 'roles/iam.serviceAccountKeyAdmin',
'members': [f'{account_type}:{email}']}]}} 'members': [f'{account_type}:{email}']}]}}
maxRetries = 10
printEntityMessage([Ent.PROJECT, projectId, Ent.SVCACCT, email],
Msg.HAS_RIGHTS_TO_ROTATE_OWN_PRIVATE_KEY.format(email, service_account))
for retry in range(1, maxRetries+1):
try:
callGAPI(iam.projects().serviceAccounts(), 'setIamPolicy', callGAPI(iam.projects().serviceAccounts(), 'setIamPolicy',
throwReasons=[GAPI.INVALID_ARGUMENT],
resource=f'projects/{projectId}/serviceAccounts/{service_account}', body=body) resource=f'projects/{projectId}/serviceAccounts/{service_account}', body=body)
return True
except GAPI.invalidArgument as e:
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, service_account], str(e))
if 'does not exist' not in str(e) or retry == maxRetries:
return False
_waitForSvcAcctCompletion(retry)
except Exception as e:
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, service_account], str(e))
return False
def _createOauth2serviceJSON(httpObj, projectInfo, svcAcctInfo, create_key=True): def _createOauth2serviceJSON(httpObj, projectInfo, svcAcctInfo, create_key=True):
iam = getAPIService(API.IAM, httpObj) iam = getAPIService(API.IAM, httpObj)
@@ -11392,24 +11411,12 @@ def _createOauth2serviceJSON(httpObj, projectInfo, svcAcctInfo, create_key=True)
clientId=service_account['uniqueId']): clientId=service_account['uniqueId']):
return False return False
sa_email = service_account['name'].rsplit('/', 1)[-1] sa_email = service_account['name'].rsplit('/', 1)[-1]
_grantRotateRights(iam, projectInfo['projectId'], sa_email, sa_email) return _grantRotateRights(iam, projectInfo['projectId'], sa_email, sa_email)
return True
def setGAMProjectConsentScreen(httpObj, projectId, appInfo):
sys.stdout.write(Msg.SETTING_GAM_PROJECT_CONSENT_SCREEN)
iap = getAPIService(API.IAP, httpObj)
try:
callGAPI(iap.projects().brands(), 'create',
throwReasons=[GAPI.ALREADY_EXISTS, GAPI.INVALID_ARGUMENT],
parent=f'projects/{projectId}', body=appInfo)
except (GAPI.invalidArgument, GAPI.alreadyExists):
pass
def _createClientSecretsOauth2service(httpObj, login_hint, appInfo, projectInfo, svcAcctInfo, create_key=True): def _createClientSecretsOauth2service(httpObj, login_hint, appInfo, projectInfo, svcAcctInfo, create_key=True):
def _checkClientAndSecret(csHttpObj, client_id, client_secret): def _checkClientAndSecret(csHttpObj, client_id, client_secret):
post_data = {'client_id': client_id, 'client_secret': client_secret, post_data = {'client_id': client_id, 'client_secret': client_secret,
'code': 'ThisIsAnInvalidCodeOnlyBeingUsedToTestIfClientAndSecretAreValid', 'code': 'ThisIsAnInvalidCodeOnlyBeingUsedToTestIfClientAndSecretAreValid',
# 'redirect_uri': 'urn:ietf:wg:oauth:2.0:oob', 'grant_type': 'authorization_code'}
'redirect_uri': 'http://127.0.0.1:8080', 'grant_type': 'authorization_code'} 'redirect_uri': 'http://127.0.0.1:8080', 'grant_type': 'authorization_code'}
_, content = csHttpObj.request(API.GOOGLE_OAUTH2_TOKEN_ENDPOINT, 'POST', urlencode(post_data), _, content = csHttpObj.request(API.GOOGLE_OAUTH2_TOKEN_ENDPOINT, 'POST', urlencode(post_data),
headers={'Content-type': 'application/x-www-form-urlencoded'}) headers={'Content-type': 'application/x-www-form-urlencoded'})
@@ -11434,16 +11441,14 @@ def _createClientSecretsOauth2service(httpObj, login_hint, appInfo, projectInfo,
if not enableGAMProjectAPIs(httpObj, projectInfo['projectId'], login_hint, False): if not enableGAMProjectAPIs(httpObj, projectInfo['projectId'], login_hint, False):
return return
if appInfo: sys.stdout.write(Msg.SETTING_GAM_PROJECT_CONSENT_SCREEN_CREATING_CLIENT)
setGAMProjectConsentScreen(httpObj, projectInfo['projectId'], appInfo) console_url = f'https://console.cloud.google.com/auth/clients?project={projectInfo["projectId"]}&authuser={login_hint}'
console_url = f'https://console.cloud.google.com/apis/credentials/oauthclient?project={projectInfo["projectId"]}&authuser={login_hint}'
csHttpObj = getHttpObj() csHttpObj = getHttpObj()
while True: while True:
sys.stdout.write(Msg.CREATE_PROJECT_INSTRUCTIONS.format(console_url)) sys.stdout.write(Msg.CREATE_CLIENT_INSTRUCTIONS.format(console_url, appInfo['applicationTitle'], appInfo['supportEmail']))
client_id = readStdin(Msg.ENTER_YOUR_CLIENT_ID).strip() client_id = readStdin(Msg.ENTER_YOUR_CLIENT_ID).strip()
if not client_id: if not client_id:
client_id = readStdin('').strip() client_id = readStdin('').strip()
sys.stdout.write(Msg.GO_BACK_TO_YOUR_BROWSER_AND_COPY_YOUR_CLIENT_SECRET_VALUE)
client_secret = readStdin(Msg.ENTER_YOUR_CLIENT_SECRET).strip() client_secret = readStdin(Msg.ENTER_YOUR_CLIENT_SECRET).strip()
if not client_secret: if not client_secret:
client_secret = readStdin('').strip() client_secret = readStdin('').strip()
@@ -11451,7 +11456,6 @@ def _createClientSecretsOauth2service(httpObj, login_hint, appInfo, projectInfo,
if client_valid: if client_valid:
break break
sys.stdout.write('\n') sys.stdout.write('\n')
# Deleted: "redirect_uris": ["http://localhost", "urn:ietf:wg:oauth:2.0:oob"],
cs_data = f'''{{ cs_data = f'''{{
"installed": {{ "installed": {{
"auth_provider_x509_cert_url": "{API.GOOGLE_AUTH_PROVIDER_X509_CERT_URL}", "auth_provider_x509_cert_url": "{API.GOOGLE_AUTH_PROVIDER_X509_CERT_URL}",
@@ -11464,7 +11468,6 @@ def _createClientSecretsOauth2service(httpObj, login_hint, appInfo, projectInfo,
}} }}
}}''' }}'''
writeFile(GC.Values[GC.CLIENT_SECRETS_JSON], cs_data, continueOnError=False) writeFile(GC.Values[GC.CLIENT_SECRETS_JSON], cs_data, continueOnError=False)
sys.stdout.write(Msg.GO_BACK_TO_YOUR_BROWSER_AND_CLICK_OK_TO_CLOSE_THE_OAUTH_CLIENT_POPUP)
sys.stdout.write(Msg.TRUST_GAM_CLIENT_ID.format(GAM, client_id)) sys.stdout.write(Msg.TRUST_GAM_CLIENT_ID.format(GAM, client_id))
readStdin('') readStdin('')
if not _createOauth2serviceJSON(httpObj, projectInfo, svcAcctInfo, create_key): if not _createOauth2serviceJSON(httpObj, projectInfo, svcAcctInfo, create_key):
@@ -11590,7 +11593,7 @@ def _getLoginHintProjectInfo(createCmd):
_checkProjectName(projectInfo['name']) _checkProjectName(projectInfo['name'])
elif _getSvcAcctInfo(myarg, svcAcctInfo): elif _getSvcAcctInfo(myarg, svcAcctInfo):
pass pass
elif createCmd and _getAppInfo(myarg, appInfo): elif _getAppInfo(myarg, appInfo):
pass pass
elif myarg in {'algorithm', 'localkeysize', 'validityhours', 'yubikey'}: elif myarg in {'algorithm', 'localkeysize', 'validityhours', 'yubikey'}:
Cmd.Backup() Cmd.Backup()
@@ -11874,14 +11877,15 @@ def doCreateProject():
# gam use project [<EmailAddress>] [<ProjectID>] # gam use project [<EmailAddress>] [<ProjectID>]
# gam use project [admin <EmailAddress>] [project <ProjectID>] # gam use project [admin <EmailAddress>] [project <ProjectID>]
# [appname <String>] [supportemail <EmailAddress>]
# [saname <ServiceAccountName>] [sadisplayname <ServiceAccountDisplayName>] [sadescription <ServiceAccountDescription>] # [saname <ServiceAccountName>] [sadisplayname <ServiceAccountDisplayName>] [sadescription <ServiceAccountDescription>]
# [(algorithm KEY_ALG_RSA_1024|KEY_ALG_RSA_2048)| # [(algorithm KEY_ALG_RSA_1024|KEY_ALG_RSA_2048)|
# (localkeysize 1024|2048|4096 [validityhours <Number>])| # (localkeysize 1024|2048|4096 [validityhours <Number>])|
# (yubikey yubikey_pin yubikey_slot AUTHENTICATION yubikey_serialnumber <String>)] # (yubikey yubikey_pin yubikey_slot AUTHENTICATION yubikey_serialnumber <String>)]
def doUseProject(): def doUseProject():
_checkForExistingProjectFiles([GC.Values[GC.OAUTH2SERVICE_JSON], GC.Values[GC.CLIENT_SECRETS_JSON]]) _checkForExistingProjectFiles([GC.Values[GC.OAUTH2SERVICE_JSON], GC.Values[GC.CLIENT_SECRETS_JSON]])
_, httpObj, login_hint, _, projectInfo, svcAcctInfo, create_key = _getLoginHintProjectInfo(False) _, httpObj, login_hint, appInfo, projectInfo, svcAcctInfo, create_key = _getLoginHintProjectInfo(False)
_createClientSecretsOauth2service(httpObj, login_hint, {}, projectInfo, svcAcctInfo, create_key) _createClientSecretsOauth2service(httpObj, login_hint, appInfo, projectInfo, svcAcctInfo, create_key)
# gam update project [[admin] <EmailAddress>] [<ProjectIDEntity>] # gam update project [[admin] <EmailAddress>] [<ProjectIDEntity>]
def doUpdateProject(): def doUpdateProject():
@@ -12577,12 +12581,6 @@ def doProcessSvcAcctKeys(mode=None, iam=None, projectId=None, clientEmail=None,
else: else:
unknownArgumentExit() unknownArgumentExit()
def waitForCompletion(i):
sleep_time = i*5
if i > 3:
sys.stdout.write(Msg.WAITING_FOR_ITEM_CREATION_TO_COMPLETE_SLEEPING.format(Ent.Singular(Ent.SVCACCT), sleep_time))
time.sleep(sleep_time)
local_key_size = 2048 local_key_size = 2048
validityHours = 0 validityHours = 0
body = {} body = {}
@@ -12652,12 +12650,12 @@ def doProcessSvcAcctKeys(mode=None, iam=None, projectId=None, clientEmail=None,
if retry == maxRetries: if retry == maxRetries:
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], str(e)) entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], str(e))
return False return False
waitForCompletion(retry) _waitForSvcAcctCompletion(retry)
except GAPI.permissionDenied: except GAPI.permissionDenied:
if retry == maxRetries: if retry == maxRetries:
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], Msg.UPDATE_PROJECT_TO_VIEW_MANAGE_SAKEYS) entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], Msg.UPDATE_PROJECT_TO_VIEW_MANAGE_SAKEYS)
return False return False
waitForCompletion(retry) _waitForSvcAcctCompletion(retry)
except GAPI.badRequest as e: except GAPI.badRequest as e:
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], str(e)) entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], str(e))
return False return False
@@ -12670,7 +12668,7 @@ def doProcessSvcAcctKeys(mode=None, iam=None, projectId=None, clientEmail=None,
new_data['private_key'] = '' new_data['private_key'] = ''
newPrivateKeyId = '' newPrivateKeyId = ''
break break
waitForCompletion(retry) _waitForSvcAcctCompletion(retry)
new_data['private_key_id'] = newPrivateKeyId new_data['private_key_id'] = newPrivateKeyId
oauth2service_data = _formatOAuth2ServiceData(new_data) oauth2service_data = _formatOAuth2ServiceData(new_data)
else: else:
@@ -12687,7 +12685,7 @@ def doProcessSvcAcctKeys(mode=None, iam=None, projectId=None, clientEmail=None,
if retry == maxRetries: if retry == maxRetries:
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], Msg.UPDATE_PROJECT_TO_VIEW_MANAGE_SAKEYS) entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], Msg.UPDATE_PROJECT_TO_VIEW_MANAGE_SAKEYS)
return False return False
waitForCompletion(retry) _waitForSvcAcctCompletion(retry)
except GAPI.badRequest as e: except GAPI.badRequest as e:
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], str(e)) entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], str(e))
return False return False
@@ -12728,7 +12726,7 @@ def doProcessSvcAcctKeys(mode=None, iam=None, projectId=None, clientEmail=None,
if retry == maxRetries: if retry == maxRetries:
entityActionFailedWarning([Ent.SVCACCT_KEY, keyName], Msg.UPDATE_PROJECT_TO_VIEW_MANAGE_SAKEYS) entityActionFailedWarning([Ent.SVCACCT_KEY, keyName], Msg.UPDATE_PROJECT_TO_VIEW_MANAGE_SAKEYS)
break break
waitForCompletion(retry) _waitForSvcAcctCompletion(retry)
except GAPI.badRequest as e: except GAPI.badRequest as e:
entityActionFailedWarning([Ent.SVCACCT_KEY, keyName], str(e), i, count) entityActionFailedWarning([Ent.SVCACCT_KEY, keyName], str(e), i, count)
break break
@@ -58882,6 +58880,8 @@ def initCopyMoveOptions(copyCmd):
'fileMimeTypes': set(), 'fileMimeTypes': set(),
'notMimeTypes': False, 'notMimeTypes': False,
'copySubFilesOwnedBy': None, 'copySubFilesOwnedBy': None,
'copyPermissionRoles': set(DRIVEFILE_ACL_ROLES_MAP.values()),
'copyPermissionTypes': set(DRIVEFILE_ACL_PERMISSION_TYPES),
} }
DUPLICATE_FILE_CHOICES = { DUPLICATE_FILE_CHOICES = {
@@ -58970,6 +58970,20 @@ def getCopyMoveOptions(myarg, copyMoveOptions):
copyMoveOptions['copyFileInheritedPermissions'] = getBoolean() copyMoveOptions['copyFileInheritedPermissions'] = getBoolean()
elif myarg == 'copyfilenoninheritedpermissions': elif myarg == 'copyfilenoninheritedpermissions':
copyMoveOptions['copyFileNonInheritedPermissions'] = COPY_NONINHERITED_PERMISSIONS_ALWAYS if getBoolean() else COPY_NONINHERITED_PERMISSIONS_NEVER copyMoveOptions['copyFileNonInheritedPermissions'] = COPY_NONINHERITED_PERMISSIONS_ALWAYS if getBoolean() else COPY_NONINHERITED_PERMISSIONS_NEVER
elif myarg == 'copypermissionroles':
copyMoveOptions['copyPermissionRoles'] = set()
for prole in getString(Cmd.OB_PERMISSION_ROLE_LIST).lower().replace(',', ' ').split():
if prole in DRIVEFILE_ACL_ROLES_MAP:
copyMoveOptions['copyPermissionRoles'].add(DRIVEFILE_ACL_ROLES_MAP[prole])
else:
invalidChoiceExit(prole, DRIVEFILE_ACL_ROLES_MAP, True)
elif myarg == 'copypermissiontypes':
copyMoveOptions['copyPermissionTypes'] = set()
for ptype in getString(Cmd.OB_PERMISSION_TYPE_LIST).lower().replace(',', ' ').split():
if ptype in DRIVEFILE_ACL_PERMISSION_TYPES:
copyMoveOptions['copyPermissionTypes'].add(ptype)
else:
invalidChoiceExit(ptype, DRIVEFILE_ACL_PERMISSION_TYPES, True)
elif myarg == 'copysheetprotectedranges': elif myarg == 'copysheetprotectedranges':
if getBoolean(): if getBoolean():
copyMoveOptions['copySheetProtectedRangesInheritedPermissions'] = True copyMoveOptions['copySheetProtectedRangesInheritedPermissions'] = True
@@ -59083,15 +59097,20 @@ def _copyPermissions(drive, user, i, count, j, jcount,
def isPermissionCopyable(kvList, permission): def isPermissionCopyable(kvList, permission):
role = permission['role'] role = permission['role']
emailAddress = permission.get('emailAddress', '') emailAddress = permission.get('emailAddress', '')
permissionType = permission['type']
domain = '' domain = ''
if copyMoveOptions['excludePermissionsFromDomains'] or copyMoveOptions['includePermissionsFromDomains']: if copyMoveOptions['excludePermissionsFromDomains'] or copyMoveOptions['includePermissionsFromDomains']:
if permission['type'] in {'group', 'user'}: if permissionType in {'group', 'user'}:
atLoc = emailAddress.find('@') atLoc = emailAddress.find('@')
if atLoc > 0: if atLoc > 0:
domain = emailAddress[atLoc+1:] domain = emailAddress[atLoc+1:]
elif permission['type'] == 'domain': elif permissionType == 'domain':
domain = permission.get('domain', '') domain = permission.get('domain', '')
if permission['inherited'] and not copyMoveOptions[copyInherited]: if role not in copyMoveOptions['copyPermissionRoles']:
notCopiedMessage = f'role {role} not selected'
elif permissionType not in copyMoveOptions['copyPermissionTypes']:
notCopiedMessage = f'type {permissionType} not selected'
elif permission['inherited'] and not copyMoveOptions[copyInherited]:
notCopiedMessage = 'inherited not selected' notCopiedMessage = 'inherited not selected'
elif not permission['inherited'] and copyMoveOptions[copyNonInherited] == COPY_NONINHERITED_PERMISSIONS_NEVER: elif not permission['inherited'] and copyMoveOptions[copyNonInherited] == COPY_NONINHERITED_PERMISSIONS_NEVER:
notCopiedMessage = 'noninherited not selected' notCopiedMessage = 'noninherited not selected'
@@ -59107,8 +59126,8 @@ def _copyPermissions(drive, user, i, count, j, jcount,
notCopiedMessage = f'domain {domain} excluded' notCopiedMessage = f'domain {domain} excluded'
elif domain and copyMoveOptions['includePermissionsFromDomains'] and domain not in copyMoveOptions['includePermissionsFromDomains']: elif domain and copyMoveOptions['includePermissionsFromDomains'] and domain not in copyMoveOptions['includePermissionsFromDomains']:
notCopiedMessage = f'domain {domain} not included' notCopiedMessage = f'domain {domain} not included'
elif permission.pop('deleted', False) or (permission['type'] in {'group', 'user'} and not emailAddress): elif permission.pop('deleted', False) or (permissionType in {'group', 'user'} and not emailAddress):
notCopiedMessage = f"{permission['type']} deleted or has blank email address" notCopiedMessage = f"{permissionType} deleted or has blank email address"
elif ((copyInherited == 'copySheetProtectedRangesInheritedPermissions' and copyMoveOptions[copyInherited]) or elif ((copyInherited == 'copySheetProtectedRangesInheritedPermissions' and copyMoveOptions[copyInherited]) or
(copyNonInherited == 'copySheetProtectedRangesNonInheritedPermissions' and (copyNonInherited == 'copySheetProtectedRangesNonInheritedPermissions' and
copyMoveOptions[copyNonInherited] != COPY_NONINHERITED_PERMISSIONS_NEVER)): copyMoveOptions[copyNonInherited] != COPY_NONINHERITED_PERMISSIONS_NEVER)):
@@ -59546,6 +59565,8 @@ copyReturnItemMap = {
# [copysubfolderpermissions [<Boolean>]] # [copysubfolderpermissions [<Boolean>]]
# [copysubfolderinheritedpermissions [<Boolean>]] # [copysubfolderinheritedpermissions [<Boolean>]]
# [copysubfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders] # [copysubfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders]
# [copypermissionroles <DriveFileACLRoleList>]
# [copypermissiontypes <DriveFileACLTypeList>]
# [excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>] # [excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>]
# (mappermissionsdomain <DomainName> <DomainName>)* # (mappermissionsdomain <DomainName> <DomainName>)*
# [copysheetprotectedranges [<Boolean>]] # [copysheetprotectedranges [<Boolean>]]
@@ -60360,6 +60381,8 @@ def _updateMoveFilePermissions(drive, user, i, count,
# [copysubfolderpermissions [<Boolean>]] # [copysubfolderpermissions [<Boolean>]]
# [copysubfolderinheritedpermissions [<Boolean>]] # [copysubfolderinheritedpermissions [<Boolean>]]
# [copysubfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders] # [copysubfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders]
# [copypermissionroles <DriveFileACLRoleList>]
# [copypermissiontypes <DriveFileACLTypeList>]
# [synctopfoldernoniheritedpermissions [<Boolean>]] [syncsubfoldernoninheritedpermissions [<Boolean>]] # [synctopfoldernoniheritedpermissions [<Boolean>]] [syncsubfoldernoninheritedpermissions [<Boolean>]]
# [excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>] # [excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>]
# (mappermissionsdomain <DomainName> <DomainName>)* # (mappermissionsdomain <DomainName> <DomainName>)*

View File

@@ -71,7 +71,6 @@ GROUPSMIGRATION = 'groupsmigration'
GROUPSSETTINGS = 'groupssettings' GROUPSSETTINGS = 'groupssettings'
IAM = 'iam' IAM = 'iam'
IAM_CREDENTIALS = 'iamcredentials' IAM_CREDENTIALS = 'iamcredentials'
IAP = 'iap'
KEEP = 'keep' KEEP = 'keep'
LICENSING = 'licensing' LICENSING = 'licensing'
LOOKERSTUDIO = 'datastudio' LOOKERSTUDIO = 'datastudio'
@@ -185,7 +184,6 @@ PROJECT_APIS = [
'groupsmigration.googleapis.com', 'groupsmigration.googleapis.com',
'groupssettings.googleapis.com', 'groupssettings.googleapis.com',
'iam.googleapis.com', 'iam.googleapis.com',
'iap.googleapis.com',
'keep.googleapis.com', 'keep.googleapis.com',
'licensing.googleapis.com', 'licensing.googleapis.com',
'meet.googleapis.com', 'meet.googleapis.com',
@@ -250,7 +248,6 @@ _INFO = {
GROUPSSETTINGS: {'name': 'Groups Settings API', 'version': 'v1', 'v2discovery': True}, GROUPSSETTINGS: {'name': 'Groups Settings API', 'version': 'v1', 'v2discovery': True},
IAM: {'name': 'Identity and Access Management API', 'version': 'v1', 'v2discovery': True}, IAM: {'name': 'Identity and Access Management API', 'version': 'v1', 'v2discovery': True},
IAM_CREDENTIALS: {'name': 'Identity and Access Management Credentials API', 'version': 'v1', 'v2discovery': True}, IAM_CREDENTIALS: {'name': 'Identity and Access Management Credentials API', 'version': 'v1', 'v2discovery': True},
IAP: {'name': 'Cloud Identity-Aware Proxy API', 'version': 'v1', 'v2discovery': True},
KEEP: {'name': 'Keep API', 'version': 'v1', 'v2discovery': True}, KEEP: {'name': 'Keep API', 'version': 'v1', 'v2discovery': True},
LICENSING: {'name': 'License Manager API', 'version': 'v1', 'v2discovery': True}, LICENSING: {'name': 'License Manager API', 'version': 'v1', 'v2discovery': True},
LOOKERSTUDIO: {'name': 'Looker Studio API', 'version': 'v1', 'v2discovery': True, 'localjson': True}, LOOKERSTUDIO: {'name': 'Looker Studio API', 'version': 'v1', 'v2discovery': True, 'localjson': True},

View File

@@ -40,21 +40,43 @@ sign in as {0} and accept the Terms of Service (ToS). As soon as you've accepted
PROJECT_STILL_BEING_CREATED_SLEEPING = 'Project still being created. Sleeping {0} seconds\n' PROJECT_STILL_BEING_CREATED_SLEEPING = 'Project still being created. Sleeping {0} seconds\n'
FAILED_TO_CREATE_PROJECT = 'Failed to create project: {0}\n' FAILED_TO_CREATE_PROJECT = 'Failed to create project: {0}\n'
SETTING_GAM_PROJECT_CONSENT_SCREEN = 'Setting GAM project consent screen...\n' SETTING_GAM_PROJECT_CONSENT_SCREEN_CREATING_CLIENT = 'Setting GAM project consent screen, creating client...\n'
CREATE_PROJECT_INSTRUCTIONS = ''' CREATE_CLIENT_INSTRUCTIONS = '''
Please go to: Please go to:
{0} {0}
1. Choose "Desktop App" or "Other" for "Application type". 1. If "+ CREATE CLIENT" is on the screen, skip to step 14
2. Enter "GAM" or another desired value for "Name". 2. Click "GET STARTED"
3. Click the blue "Create" button. 3. Under "App Information", enter {1} or another value in "App name *"
4. Copy your "Client ID" value that shows on the next page. 4. Under "App Information", enter {2} in "User support email *"
5. Click "NEXT"
6. Under "Audience", choose INTERNAL
7. Click "NEXT"
8. Under, "Contact Information", enter an email address in "Email addresses *"
9. Click "NEXT"
10. Under "Finish", click "I agree to the Google API Services: User Data Policy."
11. Click "CONTINUE"
12. Click "CREATE"
13. Click "Clients" in the left-hand column
14. Click "+ CREATE CLIENT"
15. Choose "Desktop App" for "Application type"
16. Enter {1} or another value in "Name *"
17. Click "Create"
18. Under "Name", click your client name
19. Copy the "Client ID" value under "Additional information"
20. Paste it at the "Enter your Client ID: " prompt in your terminal
21. Press return/enter in your terminal
22. Switch back to the browser
23. Copy the "Client secret" value under "Client Secrets"
24. Paste it at the "Enter your Client Secret: " prompt in your terminal
25. Press return/enter in your terminal
26. Switch back to the browser
27. Click "CANCEL"
28. These steps are complete
''' '''
ENTER_YOUR_CLIENT_ID = '\nEnter your Client ID: ' ENTER_YOUR_CLIENT_ID = '\nEnter your Client ID: '
GO_BACK_TO_YOUR_BROWSER_AND_COPY_YOUR_CLIENT_SECRET_VALUE = '\n5. Go back to your browser and copy your "Client Secret" value.\n'
ENTER_YOUR_CLIENT_SECRET = '\nEnter your Client Secret: ' ENTER_YOUR_CLIENT_SECRET = '\nEnter your Client Secret: '
GO_BACK_TO_YOUR_BROWSER_AND_CLICK_OK_TO_CLOSE_THE_OAUTH_CLIENT_POPUP = '\n6. Go back to your browser and click OK to close the "OAuth client" popup if it\'s still open.\n'
IS_NOT_A_VALID_CLIENT_ID = ''' IS_NOT_A_VALID_CLIENT_ID = '''
{0} {0}
@@ -78,12 +100,12 @@ Please go to:
https://admin.google.com/ac/owl/list?tab=configuredApps https://admin.google.com/ac/owl/list?tab=configuredApps
1. Click on: Configure new app > OAuth App Name Or Client ID. 1. Click on: Configure new app
2. Enter the following Client ID value: 2. Enter the following Client ID value in Search for app:
{1} {1}
3. Press Search, select the {0} app, press Select, check the box and press Select. 3. Press Search, select the {0} app, click
4. Keep the default scope or select a preferred scope that includes your GAM admin. 4. Keep the default scope or select a preferred scope that includes your GAM admin.
5. Press Continue 5. Press Continue
6. Select Trusted radio button, press Continue and Finish. 6. Select Trusted radio button, press Continue and Finish.