gam7 7.10.10__py3-none-any.whl → 7.12.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of gam7 might be problematic. Click here for more details.

gam/__init__.py CHANGED
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
25
25
  """
26
26
 
27
27
  __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
28
- __version__ = '7.10.10'
28
+ __version__ = '7.12.00'
29
29
  __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
30
30
 
31
31
  #pylint: disable=wrong-import-position
@@ -337,6 +337,7 @@ ENTITY_IS_A_GROUP_RC = 22
337
337
  ENTITY_IS_A_GROUP_ALIAS_RC = 23
338
338
  ENTITY_IS_AN_UNMANAGED_ACCOUNT_RC = 24
339
339
  ORGUNIT_NOT_EMPTY_RC = 25
340
+ USER_SUSPENDED_RC = 26
340
341
  CHECK_USER_GROUPS_ERROR_RC = 29
341
342
  ORPHANS_COLLECTED_RC = 30
342
343
  # Warnings/Errors
@@ -634,7 +635,8 @@ def accessErrorMessage(cd, errMsg=None):
634
635
  cd = buildGAPIObject(API.DIRECTORY)
635
636
  try:
636
637
  callGAPI(cd.customers(), 'get',
637
- throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
638
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND,
639
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
638
640
  customerKey=GC.Values[GC.CUSTOMER_ID], fields='id')
639
641
  except (GAPI.badRequest, GAPI.invalidInput):
640
642
  return formatKeyValueList('',
@@ -671,14 +673,16 @@ def accessErrorExitNonDirectory(api, errMsg):
671
673
  ''))
672
674
 
673
675
  def ClientAPIAccessDeniedExit(errMsg=None):
674
- stderrErrorMsg(Msg.API_ACCESS_DENIED)
675
- if errMsg:
676
+ if errMsg is None:
677
+ stderrErrorMsg(Msg.API_ACCESS_DENIED)
678
+ missingScopes = API.getClientScopesSet(GM.Globals[GM.CURRENT_CLIENT_API])-GM.Globals[GM.CURRENT_CLIENT_API_SCOPES]
679
+ if missingScopes:
680
+ writeStderr(Msg.API_CHECK_CLIENT_AUTHORIZATION.format(GM.Globals[GM.OAUTH2_CLIENT_ID],
681
+ ','.join(sorted(missingScopes))))
682
+ systemErrorExit(API_ACCESS_DENIED_RC, None)
683
+ else:
676
684
  stderrErrorMsg(errMsg)
677
- missingScopes = API.getClientScopesSet(GM.Globals[GM.CURRENT_CLIENT_API])-GM.Globals[GM.CURRENT_CLIENT_API_SCOPES]
678
- if missingScopes:
679
- writeStderr(Msg.API_CHECK_CLIENT_AUTHORIZATION.format(GM.Globals[GM.OAUTH2_CLIENT_ID],
680
- ','.join(sorted(missingScopes))))
681
- systemErrorExit(API_ACCESS_DENIED_RC, None)
685
+ systemErrorExit(API_ACCESS_DENIED_RC, Msg.REAUTHENTICATION_IS_NEEDED)
682
686
 
683
687
  def SvcAcctAPIAccessDenied():
684
688
  _getSvcAcctData()
@@ -4458,6 +4462,8 @@ def handleOAuthTokenError(e, softErrors, displayError=False, i=0, count=0):
4458
4462
  errMsg.startswith('invalid_request: Invalid impersonation &quot;sub&quot; field')):
4459
4463
  if not GM.Globals[GM.CURRENT_SVCACCT_USER]:
4460
4464
  ClientAPIAccessDeniedExit()
4465
+ # 403 Forbidden, API disabled, user not enabled
4466
+ # 400 Bad Request, user not defined
4461
4467
  if softErrors:
4462
4468
  entityActionFailedWarning([Ent.USER, GM.Globals[GM.CURRENT_SVCACCT_USER], Ent.USER, None], errMsg, i, count)
4463
4469
  return None
@@ -4465,6 +4471,7 @@ def handleOAuthTokenError(e, softErrors, displayError=False, i=0, count=0):
4465
4471
  if errMsg in API.OAUTH2_UNAUTHORIZED_ERRORS:
4466
4472
  if not GM.Globals[GM.CURRENT_SVCACCT_USER]:
4467
4473
  ClientAPIAccessDeniedExit()
4474
+ # 401 Unauthorized, API disabled, user enabled
4468
4475
  if softErrors:
4469
4476
  if displayError:
4470
4477
  apiOrScopes = API.getAPIName(GM.Globals[GM.CURRENT_SVCACCT_API]) if GM.Globals[GM.CURRENT_SVCACCT_API] else ','.join(sorted(GM.Globals[GM.CURRENT_SVCACCT_API_SCOPES]))
@@ -6515,7 +6522,8 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6515
6522
  printGettingAllEntityItemsForWhom(Ent.TEACHER, removeCourseIdScope(courseId), entityType=Ent.COURSE)
6516
6523
  result = callGAPIpages(courseInfo['croom'].courses().teachers(), 'list', 'teachers',
6517
6524
  pageMessage=getPageMessageForWhom(),
6518
- throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.BAD_REQUEST, GAPI.SERVICE_NOT_AVAILABLE],
6525
+ throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.SERVICE_NOT_AVAILABLE,
6526
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
6519
6527
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
6520
6528
  courseId=courseId, fields='nextPageToken,teachers/profile/emailAddress',
6521
6529
  pageSize=GC.Values[GC.CLASSROOM_MAX_RESULTS])
@@ -6528,7 +6536,8 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6528
6536
  printGettingAllEntityItemsForWhom(Ent.STUDENT, removeCourseIdScope(courseId), entityType=Ent.COURSE)
6529
6537
  result = callGAPIpages(courseInfo['croom'].courses().students(), 'list', 'students',
6530
6538
  pageMessage=getPageMessageForWhom(),
6531
- throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.BAD_REQUEST, GAPI.SERVICE_NOT_AVAILABLE],
6539
+ throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.SERVICE_NOT_AVAILABLE,
6540
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
6532
6541
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
6533
6542
  courseId=courseId, fields='nextPageToken,students/profile/emailAddress',
6534
6543
  pageSize=GC.Values[GC.CLASSROOM_MAX_RESULTS])
@@ -6544,8 +6553,8 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
6544
6553
  entityActionNotPerformedWarning([Ent.COURSE, removeCourseIdScope(courseId)], str(e))
6545
6554
  GM.Globals[GM.CLASSROOM_SERVICE_NOT_AVAILABLE] = True
6546
6555
  break
6547
- except (GAPI.forbidden, GAPI.badRequest):
6548
- ClientAPIAccessDeniedExit()
6556
+ except (GAPI.forbidden, GAPI.permissionDenied, GAPI.badRequest) as e:
6557
+ ClientAPIAccessDeniedExit(str(e))
6549
6558
  elif entityType == Cmd.ENTITY_CROS:
6550
6559
  buildGAPIObject(API.DIRECTORY)
6551
6560
  result = convertEntityToList(entity)
@@ -9023,7 +9032,7 @@ class CSVPrintFile():
9023
9032
  normalizeSortHeaders()
9024
9033
  if self.outputTranspose:
9025
9034
  newRows = []
9026
- newTitlesList = [i for i in range(len(self.rows)+1)]
9035
+ newTitlesList = list(range(len(self.rows) + 1))
9027
9036
  for title in titlesList:
9028
9037
  i = 0
9029
9038
  newRow = {i: title}
@@ -15065,7 +15074,7 @@ def getRecipients():
15065
15074
  # (embedimage <FileName> <String>)*
15066
15075
  # [newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>]
15067
15076
  # (<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
15068
- # gam <UserTypeEntity> sendemail recipient <RecipientEntity> [replyto <EmailAddress>]
15077
+ # gam <UserTypeEntity> sendemail recipient|to <RecipientEntity> [replyto <EmailAddress>]
15069
15078
  # [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
15070
15079
  # [subject <String>]
15071
15080
  # [<MessageContent>]
@@ -15098,11 +15107,12 @@ def doSendEmail(users=None):
15098
15107
  if checkArgumentPresent({'recipient', 'recipients', 'to'}):
15099
15108
  msgFroms = [normalizeEmailAddressOrUID(entity) for entity in entityList]
15100
15109
  recipients = getRecipients()
15101
- else:
15102
- if checkArgumentPresent({'from'}):
15103
- msgFroms = [getString(Cmd.OB_EMAIL_ADDRESS)]
15104
- count = 1
15110
+ elif checkArgumentPresent({'from'}):
15105
15111
  recipients = [normalizeEmailAddressOrUID(entity) for entity in entityList]
15112
+ msgFroms = [getString(Cmd.OB_EMAIL_ADDRESS)]
15113
+ count = 1
15114
+ else:
15115
+ missingArgumentExit('recipient|to|from')
15106
15116
  msgHeaders = {}
15107
15117
  ccRecipients = []
15108
15118
  bccRecipients = []
@@ -16010,8 +16020,9 @@ def doCreateDomainAlias():
16010
16020
  checkForExtraneousArguments()
16011
16021
  try:
16012
16022
  callGAPI(cd.domainAliases(), 'insert',
16013
- throwReasons=[GAPI.DOMAIN_NOT_FOUND, GAPI.DUPLICATE, GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
16014
- GAPI.FORBIDDEN, GAPI.CONFLICT],
16023
+ throwReasons=[GAPI.DOMAIN_NOT_FOUND, GAPI.DUPLICATE, GAPI.INVALID, GAPI.CONFLICT,
16024
+ GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
16025
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16015
16026
  customer=GC.Values[GC.CUSTOMER_ID], body=body, fields='')
16016
16027
  entityActionPerformed([Ent.DOMAIN, body['parentDomainName'], Ent.DOMAIN_ALIAS, body['domainAliasName']])
16017
16028
  except GAPI.domainNotFound:
@@ -16020,8 +16031,10 @@ def doCreateDomainAlias():
16020
16031
  entityActionFailedWarning([Ent.DOMAIN, body['parentDomainName'], Ent.DOMAIN_ALIAS, body['domainAliasName']], Msg.DUPLICATE)
16021
16032
  except (GAPI.invalid, GAPI.conflict) as e:
16022
16033
  entityActionFailedWarning([Ent.DOMAIN, body['parentDomainName'], Ent.DOMAIN_ALIAS, body['domainAliasName']], str(e))
16023
- except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
16034
+ except (GAPI.badRequest, GAPI.notFound) as e:
16024
16035
  accessErrorExit(cd, str(e))
16036
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16037
+ ClientAPIAccessDeniedExit(str(e))
16025
16038
 
16026
16039
  # gam delete domainalias|aliasdomain <DomainAlias>
16027
16040
  def doDeleteDomainAlias():
@@ -16030,13 +16043,16 @@ def doDeleteDomainAlias():
16030
16043
  checkForExtraneousArguments()
16031
16044
  try:
16032
16045
  callGAPI(cd.domainAliases(), 'delete',
16033
- throwReasons=[GAPI.DOMAIN_ALIAS_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
16046
+ throwReasons=[GAPI.DOMAIN_ALIAS_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
16047
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16034
16048
  customer=GC.Values[GC.CUSTOMER_ID], domainAliasName=domainAliasName)
16035
16049
  entityActionPerformed([Ent.DOMAIN_ALIAS, domainAliasName])
16036
16050
  except GAPI.domainAliasNotFound:
16037
16051
  entityActionFailedWarning([Ent.DOMAIN_ALIAS, domainAliasName], Msg.DOES_NOT_EXIST)
16038
- except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
16052
+ except (GAPI.badRequest, GAPI.notFound) as e:
16039
16053
  accessErrorExit(cd, str(e))
16054
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16055
+ ClientAPIAccessDeniedExit(str(e))
16040
16056
 
16041
16057
  DOMAIN_TIME_OBJECTS = {'creationTime'}
16042
16058
  DOMAIN_ALIAS_PRINT_ORDER = ['parentDomainName', 'creationTime', 'verified']
@@ -16064,14 +16080,17 @@ def doInfoDomainAlias():
16064
16080
  FJQC = FormatJSONQuoteChar(formatJSONOnly=True)
16065
16081
  try:
16066
16082
  result = callGAPI(cd.domainAliases(), 'get',
16067
- throwReasons=[GAPI.DOMAIN_ALIAS_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
16083
+ throwReasons=[GAPI.DOMAIN_ALIAS_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
16084
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16068
16085
  customer=GC.Values[GC.CUSTOMER_ID], domainAliasName=domainAliasName)
16069
16086
  aliasSkipObjects = DOMAIN_ALIAS_SKIP_OBJECTS
16070
16087
  _showDomainAlias(result, FJQC, aliasSkipObjects)
16071
16088
  except GAPI.domainAliasNotFound:
16072
16089
  entityActionFailedWarning([Ent.DOMAIN_ALIAS, domainAliasName], Msg.DOES_NOT_EXIST)
16073
- except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
16090
+ except (GAPI.badRequest, GAPI.notFound) as e:
16074
16091
  accessErrorExit(cd, str(e))
16092
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16093
+ ClientAPIAccessDeniedExit(str(e))
16075
16094
 
16076
16095
  def _printDomain(domain, csvPF):
16077
16096
  row = {}
@@ -16107,7 +16126,8 @@ def doPrintShowDomainAliases():
16107
16126
  FJQC.GetFormatJSONQuoteChar(myarg, True)
16108
16127
  try:
16109
16128
  domainAliases = callGAPIitems(cd.domainAliases(), 'list', 'domainAliases',
16110
- throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
16129
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
16130
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16111
16131
  customer=GC.Values[GC.CUSTOMER_ID])
16112
16132
  count = len(domainAliases)
16113
16133
  if showItemCountOnly:
@@ -16125,8 +16145,10 @@ def doPrintShowDomainAliases():
16125
16145
  csvPF.WriteRowNoFilter({'domainAliasName': domainAlias['domainAliasName'],
16126
16146
  'JSON': json.dumps(cleanJSON(domainAlias, timeObjects=DOMAIN_TIME_OBJECTS),
16127
16147
  ensure_ascii=False, sort_keys=True)})
16128
- except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
16148
+ except (GAPI.badRequest, GAPI.notFound) as e:
16129
16149
  accessErrorExit(cd, str(e))
16150
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16151
+ ClientAPIAccessDeniedExit(str(e))
16130
16152
  if csvPF:
16131
16153
  csvPF.writeCSVfile('Domain Aliases')
16132
16154
 
@@ -16137,15 +16159,19 @@ def doCreateDomain():
16137
16159
  checkForExtraneousArguments()
16138
16160
  try:
16139
16161
  callGAPI(cd.domains(), 'insert',
16140
- throwReasons=[GAPI.DUPLICATE, GAPI.DOMAIN_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.CONFLICT],
16162
+ throwReasons=[GAPI.DUPLICATE, GAPI.CONFLICT,
16163
+ GAPI.DOMAIN_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
16164
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16141
16165
  customer=GC.Values[GC.CUSTOMER_ID], body=body, fields='')
16142
16166
  entityActionPerformed([Ent.DOMAIN, body['domainName']])
16143
16167
  except GAPI.duplicate:
16144
16168
  entityDuplicateWarning([Ent.DOMAIN, body['domainName']])
16145
16169
  except GAPI.conflict as e:
16146
16170
  entityActionFailedWarning([Ent.DOMAIN, body['domainName']], str(e))
16147
- except (GAPI.domainNotFound, GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
16171
+ except (GAPI.domainNotFound, GAPI.badRequest, GAPI.notFound) as e:
16148
16172
  accessErrorExit(cd, str(e))
16173
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16174
+ ClientAPIAccessDeniedExit(str(e))
16149
16175
 
16150
16176
  # gam update domain <DomainName> primary
16151
16177
  def doUpdateDomain():
@@ -16162,13 +16188,17 @@ def doUpdateDomain():
16162
16188
  missingArgumentExit('primary')
16163
16189
  try:
16164
16190
  callGAPI(cd.customers(), 'update',
16165
- throwReasons=[GAPI.DOMAIN_NOT_VERIFIED_SECONDARY, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID_INPUT],
16191
+ throwReasons=[GAPI.DOMAIN_NOT_VERIFIED_SECONDARY, GAPI.BAD_REQUEST,
16192
+ GAPI.RESOURCE_NOT_FOUND, GAPI.INVALID_INPUT,
16193
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16166
16194
  customerKey=GC.Values[GC.CUSTOMER_ID], body=body, fields='')
16167
16195
  entityActionPerformedMessage([Ent.DOMAIN, domainName], Msg.NOW_THE_PRIMARY_DOMAIN)
16168
16196
  except GAPI.domainNotVerifiedSecondary:
16169
16197
  entityActionFailedWarning([Ent.DOMAIN, domainName], Msg.DOMAIN_NOT_VERIFIED_SECONDARY)
16170
- except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden, GAPI.invalidInput) as e:
16198
+ except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.invalidInput) as e:
16171
16199
  accessErrorExit(cd, str(e))
16200
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16201
+ ClientAPIAccessDeniedExit(str(e))
16172
16202
 
16173
16203
  # gam delete domain <DomainName>
16174
16204
  def doDeleteDomain():
@@ -16177,11 +16207,14 @@ def doDeleteDomain():
16177
16207
  checkForExtraneousArguments()
16178
16208
  try:
16179
16209
  callGAPI(cd.domains(), 'delete',
16180
- throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
16210
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
16211
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16181
16212
  customer=GC.Values[GC.CUSTOMER_ID], domainName=domainName)
16182
16213
  entityActionPerformed([Ent.DOMAIN, domainName])
16183
- except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden) as e:
16214
+ except (GAPI.badRequest, GAPI.notFound) as e:
16184
16215
  accessErrorExit(cd, str(e))
16216
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16217
+ ClientAPIAccessDeniedExit(str(e))
16185
16218
 
16186
16219
  CUSTOMER_LICENSE_MAP = {
16187
16220
  'accounts:num_users': 'Total Users',
@@ -16210,7 +16243,7 @@ def _showCustomerLicenseInfo(customerInfo, FJQC):
16210
16243
  while True:
16211
16244
  try:
16212
16245
  result = callGAPI(rep.customerUsageReports(), 'get',
16213
- throwReasons=[GAPI.INVALID, GAPI.FORBIDDEN],
16246
+ throwReasons=[GAPI.INVALID, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16214
16247
  date=tryDate, customerId=customerInfo['id'],
16215
16248
  fields='warnings,usageReports', parameters=parameters)
16216
16249
  usageReports = numUsersAvailable(result)
@@ -16228,8 +16261,8 @@ def _showCustomerLicenseInfo(customerInfo, FJQC):
16228
16261
  if not tryDate:
16229
16262
  return
16230
16263
  continue
16231
- except GAPI.forbidden:
16232
- return
16264
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16265
+ ClientAPIAccessDeniedExit(str(e))
16233
16266
  if not FJQC.formatJSON:
16234
16267
  printKeyValueList([f'User counts as of {tryDate}:'])
16235
16268
  Ind.Increment()
@@ -16250,12 +16283,15 @@ def setTrueCustomerId(cd=None):
16250
16283
  cd = buildGAPIObject(API.DIRECTORY)
16251
16284
  try:
16252
16285
  customerInfo = callGAPI(cd.customers(), 'get',
16253
- throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
16286
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND,
16287
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16254
16288
  customerKey=GC.MY_CUSTOMER,
16255
16289
  fields='id')
16256
16290
  GC.Values[GC.CUSTOMER_ID] = customerInfo['id']
16257
- except (GAPI.badRequest, GAPI.invalidInput, GAPI.resourceNotFound, GAPI.forbidden):
16291
+ except (GAPI.badRequest, GAPI.invalidInput, GAPI.resourceNotFound):
16258
16292
  pass
16293
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16294
+ ClientAPIAccessDeniedExit(str(e))
16259
16295
 
16260
16296
  def _getCustomerId():
16261
16297
  customerId = GC.Values[GC.CUSTOMER_ID]
@@ -16289,7 +16325,8 @@ def doInfoCustomer(returnCustomerInfo=None, FJQC=None):
16289
16325
  FJQC = FormatJSONQuoteChar(formatJSONOnly=True)
16290
16326
  try:
16291
16327
  customerInfo = callGAPI(cd.customers(), 'get',
16292
- throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
16328
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_INPUT, GAPI.RESOURCE_NOT_FOUND,
16329
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16293
16330
  customerKey=customerId)
16294
16331
  if 'customerCreationTime' in customerInfo:
16295
16332
  customerInfo['customerCreationTime'] = formatLocalTime(customerInfo['customerCreationTime'])
@@ -16298,7 +16335,8 @@ def doInfoCustomer(returnCustomerInfo=None, FJQC=None):
16298
16335
  primaryDomain = {'domainName': UNKNOWN, 'verified': UNKNOWN}
16299
16336
  try:
16300
16337
  domains = callGAPIitems(cd.domains(), 'list', 'domains',
16301
- throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
16338
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
16339
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16302
16340
  customer=customerInfo['id'], fields='domains(creationTime,domainName,isPrimary,verified)')
16303
16341
  for domain in domains:
16304
16342
  if domain.get('isPrimary'):
@@ -16334,8 +16372,8 @@ def doInfoCustomer(returnCustomerInfo=None, FJQC=None):
16334
16372
  _showCustomerLicenseInfo(customerInfo, FJQC)
16335
16373
  except (GAPI.badRequest, GAPI.invalidInput, GAPI.domainNotFound, GAPI.notFound, GAPI.resourceNotFound):
16336
16374
  accessErrorExit(cd)
16337
- except GAPI.forbidden as e:
16338
- entityActionFailedExit([Ent.CUSTOMER_ID, customerId], str(e))
16375
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16376
+ ClientAPIAccessDeniedExit(str(e))
16339
16377
 
16340
16378
  # gam update customer [primary <DomainName>] [adminsecondaryemail|alternateemail <EmailAddress>] [language <LanguageCode] [phone|phonenumber <String>]
16341
16379
  # [contact|contactname <String>] [name|organizationname <String>]
@@ -16363,15 +16401,18 @@ def doUpdateCustomer():
16363
16401
  if body:
16364
16402
  try:
16365
16403
  callGAPI(cd.customers(), 'patch',
16366
- throwReasons=[GAPI.DOMAIN_NOT_VERIFIED_SECONDARY, GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
16404
+ throwReasons=[GAPI.DOMAIN_NOT_VERIFIED_SECONDARY, GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND,
16405
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16367
16406
  customerKey=customerId, body=body, fields='')
16368
16407
  entityActionPerformed([Ent.CUSTOMER_ID, GC.Values[GC.CUSTOMER_ID]])
16369
16408
  except GAPI.domainNotVerifiedSecondary:
16370
16409
  entityActionFailedWarning([Ent.CUSTOMER_ID, GC.Values[GC.CUSTOMER_ID], Ent.DOMAIN, body['customerDomain']], Msg.DOMAIN_NOT_VERIFIED_SECONDARY)
16371
16410
  except (GAPI.invalid, GAPI.invalidInput) as e:
16372
16411
  entityActionFailedWarning([Ent.CUSTOMER_ID, GC.Values[GC.CUSTOMER_ID]], str(e))
16373
- except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
16412
+ except (GAPI.badRequest, GAPI.resourceNotFound):
16374
16413
  accessErrorExit(cd)
16414
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16415
+ ClientAPIAccessDeniedExit(str(e))
16375
16416
 
16376
16417
  # gam info instance [formatjson]
16377
16418
  def doInfoInstance():
@@ -16418,13 +16459,16 @@ def doInfoDomain():
16418
16459
  FJQC = FormatJSONQuoteChar(formatJSONOnly=True)
16419
16460
  try:
16420
16461
  result = callGAPI(cd.domains(), 'get',
16421
- throwReasons=[GAPI.DOMAIN_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.FORBIDDEN],
16462
+ throwReasons=[GAPI.DOMAIN_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.NOT_FOUND,
16463
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16422
16464
  customer=GC.Values[GC.CUSTOMER_ID], domainName=domainName)
16423
16465
  _showDomain(result, FJQC)
16424
16466
  except GAPI.domainNotFound:
16425
16467
  entityActionFailedWarning([Ent.DOMAIN, domainName], Msg.DOES_NOT_EXIST)
16426
- except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden):
16468
+ except (GAPI.badRequest, GAPI.notFound):
16427
16469
  accessErrorExit(cd)
16470
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16471
+ ClientAPIAccessDeniedExit(str(e))
16428
16472
 
16429
16473
  DOMAIN_SORT_TITLES = ['domainName', 'parentDomainName', 'creationTime', 'type', 'verified']
16430
16474
 
@@ -16472,8 +16516,10 @@ def doPrintShowDomains():
16472
16516
  csvPF.WriteRowNoFilter({'domainName': domain['domainName'],
16473
16517
  'JSON': json.dumps(cleanJSON(domain, timeObjects=DOMAIN_TIME_OBJECTS),
16474
16518
  ensure_ascii=False, sort_keys=True)})
16475
- except (GAPI.badRequest, GAPI.notFound, GAPI.forbidden, GAPI.domainNotFound) as e:
16519
+ except (GAPI.badRequest, GAPI.notFound, GAPI.domainNotFound) as e:
16476
16520
  accessErrorExit(cd, str(e))
16521
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16522
+ ClientAPIAccessDeniedExit(str(e))
16477
16523
  if csvPF:
16478
16524
  csvPF.writeCSVfile('Domains')
16479
16525
 
@@ -16483,10 +16529,13 @@ def _listPrivileges(cd):
16483
16529
  fields = f'items({",".join(PRINT_PRIVILEGES_FIELDS)})'
16484
16530
  try:
16485
16531
  return callGAPIitems(cd.privileges(), 'list', 'items',
16486
- throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND, GAPI.FORBIDDEN],
16532
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
16533
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16487
16534
  customer=GC.Values[GC.CUSTOMER_ID], fields=fields)
16488
- except (GAPI.badRequest, GAPI.customerNotFound, GAPI.forbidden):
16535
+ except (GAPI.badRequest, GAPI.customerNotFound):
16489
16536
  accessErrorExit(cd)
16537
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16538
+ ClientAPIAccessDeniedExit(str(e))
16490
16539
 
16491
16540
  # gam print privileges [todrive <ToDriveAttribute>*]
16492
16541
  # gam show privileges
@@ -16532,12 +16581,15 @@ def makeRoleIdNameMap():
16532
16581
  cd = buildGAPIObject(API.DIRECTORY)
16533
16582
  try:
16534
16583
  result = callGAPIpages(cd.roles(), 'list', 'items',
16535
- throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND, GAPI.FORBIDDEN],
16584
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
16585
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16536
16586
  customer=GC.Values[GC.CUSTOMER_ID],
16537
16587
  fields='nextPageToken,items(roleId,roleName)',
16538
16588
  maxResults=100)
16539
- except (GAPI.badRequest, GAPI.customerNotFound, GAPI.forbidden):
16589
+ except (GAPI.badRequest, GAPI.customerNotFound):
16540
16590
  accessErrorExit(cd)
16591
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16592
+ ClientAPIAccessDeniedExit(str(e))
16541
16593
  for role in result:
16542
16594
  GM.Globals[GM.MAP_ROLE_ID_TO_NAME][role['roleId']] = role['roleName']
16543
16595
  GM.Globals[GM.MAP_ROLE_NAME_TO_ID][role['roleName'].lower()] = role['roleId']
@@ -16627,19 +16679,23 @@ def doCreateUpdateAdminRoles():
16627
16679
  try:
16628
16680
  if not updateCmd:
16629
16681
  result = callGAPI(cd.roles(), 'insert',
16630
- throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND, GAPI.FORBIDDEN]+[GAPI.DUPLICATE],
16682
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
16683
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED]+[GAPI.DUPLICATE],
16631
16684
  customer=GC.Values[GC.CUSTOMER_ID], body=body, fields='roleId,roleName')
16632
16685
  else:
16633
16686
  result = callGAPI(cd.roles(), 'patch',
16634
- throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND, GAPI.FORBIDDEN]+[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION, GAPI.CONFLICT],
16687
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
16688
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED]+[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION, GAPI.CONFLICT],
16635
16689
  customer=GC.Values[GC.CUSTOMER_ID], roleId=roleId, body=body, fields='roleId,roleName')
16636
16690
  entityActionPerformed([Ent.ADMIN_ROLE, f"{result['roleName']}({result['roleId']})"])
16637
16691
  except GAPI.duplicate as e:
16638
16692
  entityActionFailedWarning([Ent.ADMIN_ROLE, f"{body['roleName']}"], str(e))
16639
- except (GAPI.notFound, GAPI.forbidden, GAPI.failedPrecondition, GAPI.conflict) as e:
16693
+ except (GAPI.notFound, GAPI.failedPrecondition, GAPI.conflict) as e:
16640
16694
  entityActionFailedWarning([Ent.ADMIN_ROLE, roleId], str(e))
16641
16695
  except (GAPI.badRequest, GAPI.customerNotFound):
16642
16696
  accessErrorExit(cd)
16697
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16698
+ ClientAPIAccessDeniedExit(str(e))
16643
16699
 
16644
16700
  # gam delete adminrole <RoleItem>
16645
16701
  def doDeleteAdminRole():
@@ -16648,13 +16704,16 @@ def doDeleteAdminRole():
16648
16704
  checkForExtraneousArguments()
16649
16705
  try:
16650
16706
  callGAPI(cd.roles(), 'delete',
16651
- throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND, GAPI.FORBIDDEN]+[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION],
16707
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
16708
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED]+[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION],
16652
16709
  customer=GC.Values[GC.CUSTOMER_ID], roleId=roleId)
16653
16710
  entityActionPerformed([Ent.ADMIN_ROLE, f"{role}({roleId})"])
16654
- except (GAPI.notFound, GAPI.forbidden, GAPI.failedPrecondition) as e:
16711
+ except (GAPI.notFound, GAPI.failedPrecondition) as e:
16655
16712
  entityActionFailedWarning([Ent.ADMIN_ROLE, roleId], str(e))
16656
16713
  except (GAPI.badRequest, GAPI.customerNotFound):
16657
16714
  accessErrorExit(cd)
16715
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16716
+ ClientAPIAccessDeniedExit(str(e))
16658
16717
 
16659
16718
  PRINT_ADMIN_ROLES_FIELDS = ['roleId', 'roleName', 'roleDescription', 'isSuperAdminRole', 'isSystemRole']
16660
16719
 
@@ -16715,18 +16774,22 @@ def doInfoPrintShowAdminRoles():
16715
16774
  printGettingAllAccountEntities(Ent.ADMIN_ROLE)
16716
16775
  roles = callGAPIpages(cd.roles(), 'list', 'items',
16717
16776
  pageMessage=getPageMessage(),
16718
- throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND, GAPI.FORBIDDEN],
16777
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
16778
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16719
16779
  customer=GC.Values[GC.CUSTOMER_ID], fields=fields)
16720
16780
  else:
16721
16781
  fields = getFieldsFromFieldsList(fieldsList)
16722
16782
  roles = [callGAPI(cd.roles(), 'get',
16723
- throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.FAILED_PRECONDITION,
16724
- GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND],
16783
+ throwReasons=[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION,
16784
+ GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
16785
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16725
16786
  customer=GC.Values[GC.CUSTOMER_ID], roleId=roleId, fields=fields)]
16726
- except (GAPI.notFound, GAPI.forbidden, GAPI.failedPrecondition) as e:
16787
+ except (GAPI.notFound, GAPI.failedPrecondition) as e:
16727
16788
  entityActionFailedWarning([Ent.ADMIN_ROLE, roleId], str(e))
16728
16789
  except (GAPI.badRequest, GAPI.customerNotFound):
16729
16790
  accessErrorExit(cd)
16791
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16792
+ ClientAPIAccessDeniedExit(str(e))
16730
16793
  for role in roles:
16731
16794
  role.setdefault('isSuperAdminRole', False)
16732
16795
  role.setdefault('isSystemRole', False)
@@ -16785,8 +16848,9 @@ def doCreateAdmin():
16785
16848
  try:
16786
16849
  result = callGAPI(cd.roleAssignments(), 'insert',
16787
16850
  throwReasons=[GAPI.INTERNAL_ERROR, GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
16788
- GAPI.FORBIDDEN, GAPI.CUSTOMER_EXCEEDED_ROLE_ASSIGNMENTS_LIMIT, GAPI.SERVICE_NOT_AVAILABLE,
16789
- GAPI.INVALID_ORGUNIT, GAPI.DUPLICATE, GAPI.CONDITION_NOT_MET],
16851
+ GAPI.CUSTOMER_EXCEEDED_ROLE_ASSIGNMENTS_LIMIT, GAPI.SERVICE_NOT_AVAILABLE,
16852
+ GAPI.INVALID_ORGUNIT, GAPI.DUPLICATE, GAPI.CONDITION_NOT_MET,
16853
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16790
16854
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
16791
16855
  customer=GC.Values[GC.CUSTOMER_ID], body=body, fields='roleAssignmentId,assigneeType')
16792
16856
  assigneeType = result.get('assigneeType')
@@ -16800,14 +16864,16 @@ def doCreateAdmin():
16800
16864
  f'{Ent.Singular(entityType)} {user}, {Ent.Singular(Ent.ADMIN_ROLE)} {role}, {Ent.Singular(Ent.SCOPE)} {scope}')
16801
16865
  except GAPI.internalError:
16802
16866
  pass
16803
- except (GAPI.badRequest, GAPI.customerNotFound):
16804
- accessErrorExit(cd)
16805
- except (GAPI.forbidden, GAPI.customerExceededRoleAssignmentsLimit, GAPI.serviceNotAvailable, GAPI.conditionNotMet) as e:
16867
+ except (GAPI.customerExceededRoleAssignmentsLimit, GAPI.serviceNotAvailable, GAPI.conditionNotMet) as e:
16806
16868
  entityActionFailedWarning([Ent.ADMINISTRATOR, user, Ent.ADMIN_ROLE, role], str(e))
16807
16869
  except GAPI.invalidOrgunit:
16808
16870
  entityActionFailedWarning([Ent.ADMINISTRATOR, user], Msg.INVALID_ORGUNIT)
16809
16871
  except GAPI.duplicate:
16810
16872
  entityActionFailedWarning([Ent.ADMINISTRATOR, user, Ent.ADMIN_ROLE, role], Msg.DUPLICATE)
16873
+ except (GAPI.badRequest, GAPI.customerNotFound):
16874
+ accessErrorExit(cd)
16875
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16876
+ ClientAPIAccessDeniedExit(str(e))
16811
16877
 
16812
16878
  # gam delete admin <RoleAssignmentId>
16813
16879
  def doDeleteAdmin():
@@ -16816,17 +16882,20 @@ def doDeleteAdmin():
16816
16882
  checkForExtraneousArguments()
16817
16883
  try:
16818
16884
  callGAPI(cd.roleAssignments(), 'delete',
16819
- throwReasons=[GAPI.NOT_FOUND, GAPI.OPERATION_NOT_SUPPORTED, GAPI.FORBIDDEN,
16885
+ throwReasons=[GAPI.NOT_FOUND, GAPI.OPERATION_NOT_SUPPORTED,
16820
16886
  GAPI.INVALID_INPUT, GAPI.SERVICE_NOT_AVAILABLE, GAPI.RESOURCE_NOT_FOUND,
16821
- GAPI.FAILED_PRECONDITION, GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND],
16887
+ GAPI.FAILED_PRECONDITION, GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
16888
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16822
16889
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
16823
16890
  customer=GC.Values[GC.CUSTOMER_ID], roleAssignmentId=roleAssignmentId)
16824
16891
  entityActionPerformed([Ent.ADMIN_ROLE_ASSIGNMENT, roleAssignmentId])
16825
- except (GAPI.notFound, GAPI.operationNotSupported, GAPI.forbidden,
16826
- GAPI.invalidInput, GAPI.serviceNotAvailable, GAPI.resourceNotFound, GAPI.failedPrecondition) as e:
16892
+ except (GAPI.notFound, GAPI.operationNotSupported, GAPI.invalidInput,
16893
+ GAPI.serviceNotAvailable, GAPI.resourceNotFound, GAPI.failedPrecondition) as e:
16827
16894
  entityActionFailedWarning([Ent.ADMIN_ROLE_ASSIGNMENT, roleAssignmentId], str(e))
16828
16895
  except (GAPI.badRequest, GAPI.customerNotFound):
16829
16896
  accessErrorExit(cd)
16897
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16898
+ ClientAPIAccessDeniedExit(str(e))
16830
16899
 
16831
16900
  ASSIGNEE_EMAILTYPE_TOFIELD_MAP = {
16832
16901
  'user': 'assignedToUser',
@@ -16850,17 +16919,20 @@ def doPrintShowAdmins():
16850
16919
  if roleId not in rolePrivileges:
16851
16920
  try:
16852
16921
  rolePrivileges[roleId] = callGAPI(cd.roles(), 'get',
16853
- throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.FAILED_PRECONDITION,
16854
- GAPI.SERVICE_NOT_AVAILABLE, GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND],
16922
+ throwReasons=[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION,
16923
+ GAPI.SERVICE_NOT_AVAILABLE, GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
16924
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16855
16925
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
16856
16926
  customer=GC.Values[GC.CUSTOMER_ID],
16857
16927
  roleId=roleId,
16858
16928
  fields='rolePrivileges')
16859
- except (GAPI.notFound, GAPI.forbidden, GAPI.failedPrecondition, GAPI.serviceNotAvailable) as e:
16929
+ except (GAPI.notFound, GAPI.failedPrecondition, GAPI.serviceNotAvailable) as e:
16860
16930
  entityActionFailedExit([Ent.USER, userKey, Ent.ADMIN_ROLE, admin['roleId']], str(e))
16861
16931
  rolePrivileges[roleId] = None
16862
16932
  except (GAPI.badRequest, GAPI.customerNotFound):
16863
16933
  accessErrorExit(cd)
16934
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
16935
+ ClientAPIAccessDeniedExit(str(e))
16864
16936
  return rolePrivileges[roleId]
16865
16937
 
16866
16938
  def _setNamesFromIds(admin, privileges):
@@ -16870,9 +16942,7 @@ def doPrintShowAdmins():
16870
16942
  if assignedTo not in assignedToIdEmailMap:
16871
16943
  assigneeType = admin.get('assigneeType')
16872
16944
  assignedToField = ASSIGNEE_EMAILTYPE_TOFIELD_MAP.get(assigneeType, None)
16873
- assigneeEmail, assigneeType = convertUIDtoEmailAddressWithType(f'uid:{assignedTo}',
16874
- cd,
16875
- sal,
16945
+ assigneeEmail, assigneeType = convertUIDtoEmailAddressWithType(f'uid:{assignedTo}', cd, sal,
16876
16946
  emailTypes=list(ASSIGNEE_EMAILTYPE_TOFIELD_MAP.keys()))
16877
16947
  if not assignedToField and assigneeType in ASSIGNEE_EMAILTYPE_TOFIELD_MAP:
16878
16948
  assignedToField = ASSIGNEE_EMAILTYPE_TOFIELD_MAP[assigneeType]
@@ -16929,16 +16999,19 @@ def doPrintShowAdmins():
16929
16999
  pageMessage=getPageMessage(),
16930
17000
  throwReasons=[GAPI.INVALID, GAPI.USER_NOT_FOUND,
16931
17001
  GAPI.FORBIDDEN, GAPI.SERVICE_NOT_AVAILABLE,
16932
- GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND],
17002
+ GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND,
17003
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
16933
17004
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
16934
17005
  customer=GC.Values[GC.CUSTOMER_ID], fields=fields, **kwargs)
16935
17006
  except (GAPI.invalid, GAPI.userNotFound):
16936
17007
  entityUnknownWarning(Ent.ADMINISTRATOR, userKey)
16937
17008
  return
16938
- except (GAPI.forbidden, GAPI.serviceNotAvailable) as e:
17009
+ except (GAPI.serviceNotAvailable) as e:
16939
17010
  entityActionFailedExit([Ent.ADMINISTRATOR, userKey, Ent.ADMIN_ROLE, roleId], str(e))
16940
17011
  except (GAPI.badRequest, GAPI.customerNotFound):
16941
17012
  accessErrorExit(cd)
17013
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
17014
+ ClientAPIAccessDeniedExit(str(e))
16942
17015
  if not csvPF:
16943
17016
  count = len(admins)
16944
17017
  performActionNumItems(count, Ent.ADMIN_ROLE_ASSIGNMENT)
@@ -21429,7 +21502,9 @@ def queryPeopleOtherContacts(people, contactQuery, fields, entityType, user, i=0
21429
21502
  return entityList
21430
21503
  except GAPI.permissionDenied as e:
21431
21504
  ClientAPIAccessDeniedExit(str(e))
21432
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
21505
+ except GAPI.forbidden:
21506
+ userPeopleServiceNotEnabledWarning(user, i, count)
21507
+ except GAPI.serviceNotAvailable:
21433
21508
  entityUnknownWarning(entityType, user, i, count)
21434
21509
  return None
21435
21510
 
@@ -21549,10 +21624,8 @@ def createUserPeopleContact(users):
21549
21624
  csvPF.WriteRow(row)
21550
21625
  except GAPI.invalidArgument as e:
21551
21626
  entityActionFailedWarning([entityType, user, peopleEntityType, None], str(e), i, count)
21552
- except (GAPI.permissionDenied, GAPI.failedPrecondition) as e:
21627
+ except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e:
21553
21628
  ClientAPIAccessDeniedExit(str(e))
21554
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
21555
- ClientAPIAccessDeniedExit()
21556
21629
  if csvPF:
21557
21630
  csvPF.writeCSVfile('People Contacts')
21558
21631
 
@@ -21714,10 +21787,8 @@ def _clearUpdatePeopleContacts(users, updateContacts):
21714
21787
  entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], str(e), j, jcount)
21715
21788
  except (GAPI.notFound, GAPI.internalError):
21716
21789
  entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount)
21717
- except (GAPI.permissionDenied, GAPI.failedPrecondition) as e:
21790
+ except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e:
21718
21791
  ClientAPIAccessDeniedExit(str(e))
21719
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
21720
- ClientAPIAccessDeniedExit()
21721
21792
  Ind.Decrement()
21722
21793
 
21723
21794
  # gam <UserTypeEntity> clear contacts <PeopleResourceNameEntity>|<PeopleUserContactSelection>
@@ -21864,10 +21935,8 @@ def dedupReplaceDomainUserPeopleContacts(users):
21864
21935
  entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], str(e), j, jcount)
21865
21936
  except (GAPI.notFound, GAPI.internalError):
21866
21937
  entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount)
21867
- except (GAPI.permissionDenied, GAPI.failedPrecondition) as e:
21938
+ except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e:
21868
21939
  ClientAPIAccessDeniedExit(str(e))
21869
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
21870
- ClientAPIAccessDeniedExit()
21871
21940
  Ind.Decrement()
21872
21941
 
21873
21942
  # gam <UserTypeEntity> delete contacts <PeopleResourceNameEntity>|<PeopleUserContactSelection>
@@ -21919,10 +21988,8 @@ def deleteUserPeopleContacts(users):
21919
21988
  entityActionPerformed([entityType, user, peopleEntityType, resourceName], j, jcount)
21920
21989
  except (GAPI.notFound, GAPI.internalError):
21921
21990
  entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount)
21922
- except (GAPI.permissionDenied, GAPI.failedPrecondition) as e:
21991
+ except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e:
21923
21992
  ClientAPIAccessDeniedExit(str(e))
21924
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
21925
- ClientAPIAccessDeniedExit()
21926
21993
  Ind.Decrement()
21927
21994
 
21928
21995
  def _initPersonMetadataParameters():
@@ -22216,10 +22283,8 @@ def _infoPeople(users, entityType, source):
22216
22283
  except GAPI.invalidArgument as e:
22217
22284
  entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], str(e), j, jcount)
22218
22285
  continue
22219
- except (GAPI.permissionDenied, GAPI.failedPrecondition) as e:
22286
+ except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e:
22220
22287
  ClientAPIAccessDeniedExit(str(e))
22221
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
22222
- ClientAPIAccessDeniedExit()
22223
22288
  if showContactGroups and contactGroupIDs:
22224
22289
  addContactGroupNamesToContacts([result], contactGroupIDs, False)
22225
22290
  _showPerson(entityType, user, peopleEntityType, result, j, jcount, FJQC, parameters)
@@ -22396,10 +22461,8 @@ def copyUserPeopleOtherContacts(users):
22396
22461
  except (GAPI.notFound, GAPI.internalError):
22397
22462
  entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount)
22398
22463
  continue
22399
- except GAPI.permissionDenied as e:
22464
+ except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e:
22400
22465
  ClientAPIAccessDeniedExit(str(e))
22401
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
22402
- ClientAPIAccessDeniedExit()
22403
22466
  Ind.Decrement()
22404
22467
 
22405
22468
  # gam <UserTypeEntity> delete othercontacts
@@ -22512,10 +22575,8 @@ def processUserPeopleOtherContacts(users):
22512
22575
  except (GAPI.notFound, GAPI.internalError):
22513
22576
  entityActionFailedWarning([entityType, user, peopleEntityType, resourceName], Msg.DOES_NOT_EXIST, j, jcount)
22514
22577
  continue
22515
- except GAPI.permissionDenied as e:
22578
+ except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e:
22516
22579
  ClientAPIAccessDeniedExit(str(e))
22517
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
22518
- ClientAPIAccessDeniedExit()
22519
22580
  Ind.Decrement()
22520
22581
 
22521
22582
  # gam <UserTypeEntity> print othercontacts [todrive <ToDriveAttribute>*] <OtherContactSelection>
@@ -22631,10 +22692,8 @@ def _printShowPeople(source):
22631
22692
  pageSize=GC.Values[GC.PEOPLE_MAX_RESULTS],
22632
22693
  sources=sources, mergeSources=mergeSources,
22633
22694
  readMask=fields, fields='nextPageToken,people', **kwargs)
22634
- except (GAPI.permissionDenied, GAPI.failedPrecondition) as e:
22695
+ except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e:
22635
22696
  ClientAPIAccessDeniedExit(str(e))
22636
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
22637
- ClientAPIAccessDeniedExit()
22638
22697
  if not countsOnly:
22639
22698
  _printPersonEntityList(peopleEntityType, entityList, Ent.DOMAIN, GC.Values[GC.DOMAIN], 0, 0, csvPF, FJQC, parameters, None)
22640
22699
  else:
@@ -22753,10 +22812,8 @@ def printShowUserPeopleProfiles(users):
22753
22812
  except GAPI.notFound:
22754
22813
  entityUnknownWarning(Ent.PEOPLE_PROFILE, user, i, count)
22755
22814
  continue
22756
- except (GAPI.permissionDenied, GAPI.failedPrecondition) as e:
22815
+ except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e:
22757
22816
  ClientAPIAccessDeniedExit(str(e))
22758
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
22759
- ClientAPIAccessDeniedExit()
22760
22817
  if not csvPF:
22761
22818
  _showPerson(entityType, user, Ent.PEOPLE_PROFILE, result, i, count, FJQC, parameters)
22762
22819
  else:
@@ -22909,10 +22966,8 @@ def _processPeopleContactPhotos(users, function):
22909
22966
  entityDoesNotHaveItemWarning([entityType, user, peopleEntityType, resourceName, Ent.PHOTO, filename], j, jcount)
22910
22967
  except (GAPI.invalidArgument, OSError, IOError) as e:
22911
22968
  entityActionFailedWarning([entityType, user, peopleEntityType, resourceName, Ent.PHOTO, filename], str(e), j, jcount)
22912
- except (GAPI.permissionDenied, GAPI.failedPrecondition) as e:
22969
+ except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied, GAPI.failedPrecondition) as e:
22913
22970
  ClientAPIAccessDeniedExit(str(e))
22914
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
22915
- ClientAPIAccessDeniedExit()
22916
22971
  break
22917
22972
  Ind.Decrement()
22918
22973
 
@@ -23234,10 +23289,8 @@ def printShowUserPeopleContactGroups(users):
23234
23289
  throwReasons=GAPI.PEOPLE_ACCESS_THROW_REASONS,
23235
23290
  pageSize=GC.Values[GC.PEOPLE_MAX_RESULTS],
23236
23291
  groupFields=fields, fields='nextPageToken,contactGroups')
23237
- except GAPI.permissionDenied as e:
23292
+ except (GAPI.serviceNotAvailable, GAPI.forbidden, GAPI.permissionDenied) as e:
23238
23293
  ClientAPIAccessDeniedExit(str(e))
23239
- except (GAPI.serviceNotAvailable, GAPI.forbidden):
23240
- ClientAPIAccessDeniedExit()
23241
23294
  _printPersonEntityList(Ent.PEOPLE_CONTACT_GROUP, entityList, entityType, user, i, count, csvPF, FJQC, parameters, None)
23242
23295
  if csvPF:
23243
23296
  csvPF.writeCSVfile('People Contact Groups')
@@ -31782,15 +31835,18 @@ def getMobileDeviceEntity():
31782
31835
  printGettingAllAccountEntities(Ent.MOBILE_DEVICE, query)
31783
31836
  devices = callGAPIpages(cd.mobiledevices(), 'list', 'mobiledevices',
31784
31837
  pageMessage=getPageMessage(),
31785
- throwReasons=[GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
31838
+ throwReasons=[GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND,
31839
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
31786
31840
  customerId=GC.Values[GC.CUSTOMER_ID], query=query,
31787
31841
  fields='nextPageToken,mobiledevices(resourceId,email)')
31842
+ return ([{'resourceId': device['resourceId'], 'email': device.get('email', [])} for device in devices], cd, False)
31788
31843
  except GAPI.invalidInput:
31789
31844
  Cmd.Backup()
31790
31845
  usageErrorExit(Msg.INVALID_QUERY)
31791
- except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
31846
+ except (GAPI.badRequest, GAPI.resourceNotFound):
31792
31847
  accessErrorExit(cd)
31793
- return ([{'resourceId': device['resourceId'], 'email': device.get('email', [])} for device in devices], cd, False)
31848
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
31849
+ ClientAPIAccessDeniedExit(str(e))
31794
31850
 
31795
31851
  def _getUpdateDeleteMobileOptions(myarg, options):
31796
31852
  if myarg in {'matchusers', 'ifusers'}:
@@ -31843,14 +31899,17 @@ def doUpdateMobileDevices():
31843
31899
  callGAPI(cd.mobiledevices(), 'action',
31844
31900
  bailOnInternalError=True,
31845
31901
  throwReasons=[GAPI.INTERNAL_ERROR, GAPI.RESOURCE_ID_NOT_FOUND,
31846
- GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
31902
+ GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND,
31903
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
31847
31904
  customerId=GC.Values[GC.CUSTOMER_ID], resourceId=resourceId, body=body)
31848
31905
  printEntityKVList([Ent.MOBILE_DEVICE, resourceId, Ent.USER, deviceUser],
31849
31906
  [Msg.ACTION_APPLIED, body['action']], i, count)
31850
31907
  except GAPI.internalError:
31851
31908
  entityActionFailedWarning([Ent.MOBILE_DEVICE, resourceId], Msg.DOES_NOT_EXIST, i, count)
31852
- except (GAPI.resourceIdNotFound, GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden) as e:
31909
+ except (GAPI.resourceIdNotFound, GAPI.badRequest, GAPI.resourceNotFound) as e:
31853
31910
  entityActionFailedWarning([Ent.MOBILE_DEVICE, resourceId], str(e), i, count)
31911
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
31912
+ ClientAPIAccessDeniedExit(str(e))
31854
31913
 
31855
31914
  # gam delete mobile|mobiles <MobileDeviceEntity>
31856
31915
  # [doit] [matchusers <UserTypeEntity>]
@@ -31874,14 +31933,16 @@ def doDeleteMobileDevices():
31874
31933
  try:
31875
31934
  callGAPI(cd.mobiledevices(), 'delete',
31876
31935
  bailOnInternalError=True,
31877
- throwReasons=[GAPI.INTERNAL_ERROR, GAPI.RESOURCE_ID_NOT_FOUND, GAPI.BAD_REQUEST,
31878
- GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
31936
+ throwReasons=[GAPI.INTERNAL_ERROR, GAPI.RESOURCE_ID_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND,
31937
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
31879
31938
  customerId=GC.Values[GC.CUSTOMER_ID], resourceId=resourceId)
31880
31939
  entityActionPerformed([Ent.MOBILE_DEVICE, resourceId, Ent.USER, deviceUser], i, count)
31881
31940
  except GAPI.internalError:
31882
31941
  entityActionFailedWarning([Ent.MOBILE_DEVICE, resourceId], Msg.DOES_NOT_EXIST, i, count)
31883
- except (GAPI.resourceIdNotFound, GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden) as e:
31942
+ except (GAPI.resourceIdNotFound, GAPI.badRequest, GAPI.resourceNotFound) as e:
31884
31943
  entityActionFailedWarning([Ent.MOBILE_DEVICE, resourceId], str(e), i, count)
31944
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
31945
+ ClientAPIAccessDeniedExit(str(e))
31885
31946
 
31886
31947
  MOBILE_FIELDS_CHOICE_MAP = {
31887
31948
  'adbstatus': 'adbStatus',
@@ -31963,8 +32024,8 @@ def doInfoMobileDevices():
31963
32024
  try:
31964
32025
  mobile = callGAPI(cd.mobiledevices(), 'get',
31965
32026
  bailOnInternalError=True,
31966
- throwReasons=[GAPI.INTERNAL_ERROR, GAPI.RESOURCE_ID_NOT_FOUND,
31967
- GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
32027
+ throwReasons=[GAPI.INTERNAL_ERROR, GAPI.RESOURCE_ID_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND,
32028
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
31968
32029
  customerId=GC.Values[GC.CUSTOMER_ID], resourceId=resourceId, projection=parameters['projection'], fields=fields)
31969
32030
  if FJQC.formatJSON:
31970
32031
  printLine(json.dumps(cleanJSON(mobile, timeObjects=MOBILE_TIME_OBJECTS), ensure_ascii=False, sort_keys=True))
@@ -31981,8 +32042,10 @@ def doInfoMobileDevices():
31981
32042
  Ind.Decrement()
31982
32043
  except GAPI.internalError:
31983
32044
  entityActionFailedWarning([Ent.MOBILE_DEVICE, resourceId], Msg.DOES_NOT_EXIST, i, count)
31984
- except (GAPI.resourceIdNotFound, GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden) as e:
32045
+ except (GAPI.resourceIdNotFound, GAPI.badRequest, GAPI.resourceNotFound) as e:
31985
32046
  entityActionFailedWarning([Ent.MOBILE_DEVICE, resourceId], str(e), i, count)
32047
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
32048
+ ClientAPIAccessDeniedExit(str(e))
31986
32049
 
31987
32050
  MOBILE_ORDERBY_CHOICE_MAP = {
31988
32051
  'deviceid': 'deviceId',
@@ -32111,7 +32174,8 @@ def doPrintMobileDevices():
32111
32174
  try:
32112
32175
  feed = yieldGAPIpages(cd.mobiledevices(), 'list', 'mobiledevices',
32113
32176
  pageMessage=pageMessage,
32114
- throwReasons=[GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
32177
+ throwReasons=[GAPI.INVALID_INPUT, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND,
32178
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
32115
32179
  customerId=GC.Values[GC.CUSTOMER_ID], query=query, projection=parameters['projection'],
32116
32180
  orderBy=orderBy, sortOrder=sortOrder, fields=fields, maxResults=GC.Values[GC.MOBILE_MAX_RESULTS])
32117
32181
  for mobiles in feed:
@@ -32125,8 +32189,10 @@ def doPrintMobileDevices():
32125
32189
  except GAPI.invalidInput:
32126
32190
  entityActionFailedWarning([Ent.MOBILE_DEVICE, None], invalidQuery(query))
32127
32191
  return
32128
- except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
32192
+ except (GAPI.badRequest, GAPI.resourceNotFound):
32129
32193
  accessErrorExit(cd)
32194
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
32195
+ ClientAPIAccessDeniedExit(str(e))
32130
32196
  if showItemCountOnly:
32131
32197
  writeStdout(f'{itemCount}\n')
32132
32198
  return
@@ -36452,8 +36518,8 @@ def doPrintShowCIPolicies():
36452
36518
  sort_keys=True)})
36453
36519
 
36454
36520
  _checkPoliciesWithDASA()
36455
- groups_ci = buildGAPIObject(API.CLOUDIDENTITY_GROUPS)
36456
36521
  ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY)
36522
+ groups_ci = buildGAPIObject(API.CLOUDIDENTITY_GROUPS)
36457
36523
  cd = buildGAPIObject(API.DIRECTORY)
36458
36524
  csvPF = CSVPrintFile(['name']) if Act.csvFormat() else None
36459
36525
  FJQC = FormatJSONQuoteChar(csvPF)
@@ -38464,10 +38530,13 @@ def doPrintShowResourceCalendars():
38464
38530
  try:
38465
38531
  resources = callGAPIpages(cd.resources().calendars(), 'list', 'items',
38466
38532
  pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='resourceName',
38467
- throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID_INPUT],
38533
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN,
38534
+ GAPI.PERMISSION_DENIED, GAPI.INVALID_INPUT],
38468
38535
  query=query, customer=GC.Values[GC.CUSTOMER_ID], fields=fields)
38469
- except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
38536
+ except (GAPI.badRequest, GAPI.resourceNotFound):
38470
38537
  accessErrorExit(cd)
38538
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
38539
+ ClientAPIAccessDeniedExit(str(e))
38471
38540
  except GAPI.invalidInput as e:
38472
38541
  entityActionFailedWarning([Ent.RESOURCE_CALENDAR, ''], str(e))
38473
38542
  return
@@ -40706,7 +40775,8 @@ def doCreateUpdateUserSchemas():
40706
40775
  try:
40707
40776
  if updateCmd:
40708
40777
  oldBody = callGAPI(cd.schemas(), 'get',
40709
- throwReasons=[GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
40778
+ throwReasons=[GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND,
40779
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
40710
40780
  customerId=GC.Values[GC.CUSTOMER_ID], schemaKey=schemaName, fields='schemaName,displayName,fields')
40711
40781
  for field in oldBody['fields']:
40712
40782
  field.pop('etag', None)
@@ -40745,15 +40815,18 @@ def doCreateUpdateUserSchemas():
40745
40815
  addBody['schemaName'] = schemaName.replace(' ', '_')
40746
40816
  addBody['displayName'] = schemaDisplayName if schemaDisplayName else schemaName
40747
40817
  result = callGAPI(cd.schemas(), 'insert',
40748
- throwReasons=[GAPI.DUPLICATE, GAPI.CONDITION_NOT_MET, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
40818
+ throwReasons=[GAPI.DUPLICATE, GAPI.CONDITION_NOT_MET, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND,
40819
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
40749
40820
  customerId=GC.Values[GC.CUSTOMER_ID], body=addBody, fields='schemaName')
40750
40821
  entityActionPerformed([Ent.USER_SCHEMA, result['schemaName']], i, count)
40751
40822
  except GAPI.duplicate:
40752
40823
  entityDuplicateWarning([Ent.USER_SCHEMA, schemaName], i, count)
40753
- except (GAPI.conditionNotMet, GAPI.fieldInUse, GAPI.forbidden) as e:
40824
+ except (GAPI.conditionNotMet, GAPI.fieldInUse) as e:
40754
40825
  entityActionFailedWarning([Ent.USER_SCHEMA, schemaName], str(e), i, count)
40755
40826
  except (GAPI.badRequest, GAPI.resourceNotFound):
40756
40827
  checkEntityAFDNEorAccessErrorExit(cd, Ent.USER_SCHEMA, schemaName, i, count)
40828
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
40829
+ ClientAPIAccessDeniedExit(str(e))
40757
40830
 
40758
40831
  # gam delete schema|schemas <SchemaEntity>
40759
40832
  def doDeleteUserSchemas():
@@ -40766,13 +40839,16 @@ def doDeleteUserSchemas():
40766
40839
  i += 1
40767
40840
  try:
40768
40841
  callGAPI(cd.schemas(), 'delete',
40769
- throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN, GAPI.FIELD_IN_USE],
40842
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FIELD_IN_USE,
40843
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
40770
40844
  customerId=GC.Values[GC.CUSTOMER_ID], schemaKey=schemaKey)
40771
40845
  entityActionPerformed([Ent.USER_SCHEMA, schemaKey], i, count)
40772
40846
  except GAPI.fieldInUse as e:
40773
40847
  entityActionFailedWarning([Ent.USER_SCHEMA, schemaKey], str(e), i, count)
40774
- except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
40848
+ except (GAPI.badRequest, GAPI.resourceNotFound):
40775
40849
  checkEntityAFDNEorAccessErrorExit(cd, Ent.USER_SCHEMA, schemaKey, i, count)
40850
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
40851
+ ClientAPIAccessDeniedExit(str(e))
40776
40852
 
40777
40853
  # gam info schema|schemas <SchemaEntity>
40778
40854
  def doInfoUserSchemas():
@@ -40785,11 +40861,14 @@ def doInfoUserSchemas():
40785
40861
  i += 1
40786
40862
  try:
40787
40863
  schema = callGAPI(cd.schemas(), 'get',
40788
- throwReasons=[GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
40864
+ throwReasons=[GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND,
40865
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
40789
40866
  customerId=GC.Values[GC.CUSTOMER_ID], schemaKey=schemaKey)
40790
40867
  _showSchema(schema, i, count)
40791
- except (GAPI.invalid, GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
40868
+ except (GAPI.invalid, GAPI.badRequest, GAPI.resourceNotFound):
40792
40869
  checkEntityAFDNEorAccessErrorExit(cd, Ent.USER_SCHEMA, schemaKey, i, count)
40870
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
40871
+ ClientAPIAccessDeniedExit(str(e))
40793
40872
 
40794
40873
  SCHEMAS_SORT_TITLES = ['schemaId', 'schemaName', 'displayName']
40795
40874
  SCHEMAS_INDEXED_TITLES = ['fields']
@@ -40802,7 +40881,8 @@ def doPrintShowUserSchemas():
40802
40881
  getTodriveOnly(csvPF)
40803
40882
  try:
40804
40883
  result = callGAPI(cd.schemas(), 'list',
40805
- throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
40884
+ throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND,
40885
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
40806
40886
  customerId=GC.Values[GC.CUSTOMER_ID])
40807
40887
  jcount = len(result.get('schemas', [])) if (result) else 0
40808
40888
  if not csvPF:
@@ -40820,8 +40900,10 @@ def doPrintShowUserSchemas():
40820
40900
  else:
40821
40901
  for schema in result['schemas']:
40822
40902
  csvPF.WriteRowTitles(flattenJSON(schema))
40823
- except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
40903
+ except (GAPI.badRequest, GAPI.resourceNotFound):
40824
40904
  accessErrorExit(cd)
40905
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
40906
+ ClientAPIAccessDeniedExit(str(e))
40825
40907
  if csvPF:
40826
40908
  csvPF.writeCSVfile('User Schemas')
40827
40909
 
@@ -41082,11 +41164,14 @@ def convertExportNameToID(v, nameOrId, matterId, matterNameId):
41082
41164
  if cg:
41083
41165
  try:
41084
41166
  export = callGAPI(v.matters().exports(), 'get',
41085
- throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT],
41167
+ throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN,
41168
+ GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION],
41086
41169
  matterId=matterId, exportId=cg.group(1))
41087
41170
  return (export['id'], export['name'], formatVaultNameId(export['id'], export['name']))
41088
41171
  except (GAPI.notFound, GAPI.badRequest):
41089
41172
  entityDoesNotHaveItemExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, nameOrId])
41173
+ except (GAPI.failedPrecondition) as e:
41174
+ entityActionFailedExit([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, nameOrId], str(e))
41090
41175
  except (GAPI.forbidden, GAPI.invalidArgument) as e:
41091
41176
  ClientAPIAccessDeniedExit(str(e))
41092
41177
  nameOrIdlower = nameOrId.lower()
@@ -41582,10 +41667,11 @@ def doInfoVaultExport():
41582
41667
  fields = getFieldsFromFieldsList(fieldsList)
41583
41668
  try:
41584
41669
  export = callGAPI(v.matters().exports(), 'get',
41585
- throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT],
41670
+ throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN,
41671
+ GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION],
41586
41672
  matterId=matterId, exportId=exportId, fields=fields)
41587
41673
  _showVaultExport(matterNameId, export, cd, FJQC)
41588
- except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument) as e:
41674
+ except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument, GAPI.failedPrecondition) as e:
41589
41675
  entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId], str(e))
41590
41676
 
41591
41677
  VAULT_EXPORT_STATUS_MAP = {'completed': 'COMPLETED', 'failed': 'FAILED', 'inprogress': 'IN_PROGRESS'}
@@ -41778,9 +41864,10 @@ def doCopyVaultExport():
41778
41864
  while True:
41779
41865
  try:
41780
41866
  export = callGAPI(v.matters().exports(), 'get',
41781
- throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT],
41867
+ throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN,
41868
+ GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION],
41782
41869
  matterId=matterId, exportId=exportId)
41783
- except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument) as e:
41870
+ except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument, GAPI.failedPrecondition) as e:
41784
41871
  entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId], str(e))
41785
41872
  return
41786
41873
  if export['status'] == 'COMPLETED':
@@ -41878,9 +41965,10 @@ def doDownloadVaultExport():
41878
41965
  while True:
41879
41966
  try:
41880
41967
  export = callGAPI(v.matters().exports(), 'get',
41881
- throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN, GAPI.INVALID_ARGUMENT],
41968
+ throwReasons=[GAPI.NOT_FOUND, GAPI.BAD_REQUEST, GAPI.FORBIDDEN,
41969
+ GAPI.INVALID_ARGUMENT, GAPI.FAILED_PRECONDITION],
41882
41970
  matterId=matterId, exportId=exportId)
41883
- except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument) as e:
41971
+ except (GAPI.notFound, GAPI.badRequest, GAPI.forbidden, GAPI.invalidArgument, GAPI.failedPrecondition) as e:
41884
41972
  entityActionFailedWarning([Ent.VAULT_MATTER, matterNameId, Ent.VAULT_EXPORT, exportNameId], str(e))
41885
41973
  return
41886
41974
  if export['status'] == 'COMPLETED':
@@ -44306,6 +44394,28 @@ def doUndeleteUsers():
44306
44394
  def doUndeleteUser():
44307
44395
  undeleteUsers(getStringReturnInList(Cmd.OB_USER_ITEM))
44308
44396
 
44397
+ # gam check suspended <UserItem>
44398
+ def doCheckUserSuspended():
44399
+ cd = buildGAPIObject(API.DIRECTORY)
44400
+ user = getEmailAddress()
44401
+ checkForExtraneousArguments()
44402
+ try:
44403
+ result = callGAPI(cd.users(), 'get',
44404
+ throwReasons=GAPI.USER_GET_THROW_REASONS,
44405
+ userKey=user, fields='suspended,suspensionReason', projection='basic')
44406
+ except (GAPI.userNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden):
44407
+ entityDoesNotExistExit(Ent.USER, user)
44408
+ except (GAPI.badRequest, GAPI.invalidCustomerId, GAPI.loginRequired):
44409
+ accessErrorExit(cd)
44410
+ kvList = [Ent.Singular(Ent.USER), user]
44411
+ up = 'suspended'
44412
+ kvList.extend([UProp.PROPERTIES[up][UProp.TITLE], result[up]])
44413
+ if result[up]:
44414
+ up = 'suspensionReason'
44415
+ kvList.extend([UProp.PROPERTIES[up][UProp.TITLE], result[up]])
44416
+ setSysExitRC(USER_SUSPENDED_RC)
44417
+ printKeyValueList(kvList)
44418
+
44309
44419
  # gam <UserTypeEntity> suspend users [noactionifalias]
44310
44420
  # gam <UserTypeEntity> unsuspend users [noactionifalias]
44311
44421
  def suspendUnsuspendUsers(entityList):
@@ -44359,13 +44469,13 @@ def signoutTurnoff2SVUsers(entityList):
44359
44469
  callGAPI(service, function,
44360
44470
  throwReasons=[GAPI.NOT_FOUND, GAPI.USER_NOT_FOUND, GAPI.INVALID, GAPI.INVALID_INPUT,
44361
44471
  GAPI.DOMAIN_NOT_FOUND, GAPI.DOMAIN_CANNOT_USE_APIS,
44362
- GAPI.FORBIDDEN, GAPI.AUTH_ERROR],
44472
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.AUTH_ERROR],
44363
44473
  userKey=user)
44364
44474
  entityActionPerformed([Ent.USER, user], i, count)
44365
44475
  except (GAPI.notFound, GAPI.userNotFound):
44366
44476
  entityUnknownWarning(Ent.USER, user, i, count)
44367
44477
  except (GAPI.invalid, GAPI.invalidInput, GAPI.domainNotFound, GAPI.domainCannotUseApis,
44368
- GAPI.forbidden, GAPI.authError) as e:
44478
+ GAPI.forbidden, GAPI.permissionDenied, GAPI.authError) as e:
44369
44479
  entityActionFailedWarning([Ent.USER, user], str(e), i, count)
44370
44480
 
44371
44481
  # gam <UserTypeEntity> waitformailbox [retries <Number>]
@@ -46776,6 +46886,13 @@ COURSE_STATE_MAPS = {
46776
46886
  'published': 'PUBLISHED',
46777
46887
  'deleted': 'DELETED',
46778
46888
  },
46889
+ Cmd.OB_COURSE_ANNOUNCEMENT_ADD_STATE_LIST: {
46890
+ 'draft': 'DRAFT',
46891
+ 'published': 'PUBLISHED',
46892
+ },
46893
+ Cmd.OB_COURSE_ANNOUNCEMENT_UPDATE_STATE_LIST: {
46894
+ 'published': 'PUBLISHED',
46895
+ },
46779
46896
  Cmd.OB_COURSE_WORK_STATE_LIST: {
46780
46897
  'draft': 'DRAFT',
46781
46898
  'published': 'PUBLISHED',
@@ -47222,10 +47339,7 @@ class CourseAttributes():
47222
47339
  newTopicsByName[topicName] = result['topicId']
47223
47340
  entityModifierItemValueListActionPerformed([Ent.COURSE, newCourseId, Ent.COURSE_TOPIC, topicName], Act.MODIFIER_FROM,
47224
47341
  [Ent.COURSE, self.courseId], j, jcount)
47225
- except GAPI.notFound as e:
47226
- entityActionFailedWarning([Ent.COURSE, newCourseId], str(e), i, count)
47227
- return
47228
- except (GAPI.failedPrecondition, GAPI.invalidArgument, GAPI.forbidden, GAPI.serviceNotAvailable) as e:
47342
+ except (GAPI.notFound, GAPI.failedPrecondition, GAPI.invalidArgument, GAPI.forbidden, GAPI.serviceNotAvailable) as e:
47229
47343
  entityModifierItemValueListActionFailedWarning([Ent.COURSE, newCourseId], Act.MODIFIER_FROM,
47230
47344
  [Ent.COURSE, self.courseId, Ent.COURSE_TOPIC, topicName], str(e), j, jcount)
47231
47345
  if self.courseAnnouncements:
@@ -47248,10 +47362,7 @@ class CourseAttributes():
47248
47362
  courseId=newCourseId, body=body, fields='id')
47249
47363
  entityModifierItemValueListActionPerformed([Ent.COURSE, newCourseId, Ent.COURSE_ANNOUNCEMENT_ID, result['id']], Act.MODIFIER_FROM,
47250
47364
  [Ent.COURSE, self.courseId, Ent.COURSE_ANNOUNCEMENT, courseAnnouncementId], j, jcount)
47251
- except GAPI.notFound as e:
47252
- entityActionFailedWarning([Ent.COURSE, newCourseId], str(e), i, count)
47253
- return
47254
- except (GAPI.badRequest, GAPI.failedPrecondition, GAPI.backendError, GAPI.internalError,
47365
+ except (GAPI.notFound, GAPI.badRequest, GAPI.failedPrecondition, GAPI.backendError, GAPI.internalError,
47255
47366
  GAPI.permissionDenied, GAPI.forbidden, GAPI.serviceNotAvailable) as e:
47256
47367
  entityModifierItemValueListActionFailedWarning([Ent.COURSE, newCourseId], Act.MODIFIER_FROM,
47257
47368
  [Ent.COURSE, self.courseId, Ent.COURSE_ANNOUNCEMENT, courseAnnouncementId], str(e), j, jcount)
@@ -47283,10 +47394,7 @@ class CourseAttributes():
47283
47394
  courseId=newCourseId, body=body, fields='id')
47284
47395
  entityModifierItemValueListActionPerformed([Ent.COURSE, newCourseId, Ent.COURSE_MATERIAL_ID, result['id']], Act.MODIFIER_FROM,
47285
47396
  [Ent.COURSE, self.courseId, Ent.COURSE_MATERIAL, courseMaterialId], j, jcount)
47286
- except GAPI.notFound as e:
47287
- entityActionFailedWarning([Ent.COURSE, newCourseId], str(e), i, count)
47288
- return
47289
- except (GAPI.badRequest, GAPI.failedPrecondition, GAPI.backendError, GAPI.internalError,
47397
+ except (GAPI.notFound, GAPI.badRequest, GAPI.failedPrecondition, GAPI.backendError, GAPI.internalError,
47290
47398
  GAPI.permissionDenied, GAPI.forbidden, GAPI.serviceNotAvailable) as e:
47291
47399
  entityModifierItemValueListActionFailedWarning([Ent.COURSE, newCourseId], Act.MODIFIER_FROM,
47292
47400
  [Ent.COURSE, self.courseId, Ent.COURSE_MATERIAL, courseMaterialId], str(e), j, jcount)
@@ -47323,10 +47431,7 @@ class CourseAttributes():
47323
47431
  courseId=newCourseId, body=body, fields='id')
47324
47432
  entityModifierItemValueListActionPerformed([Ent.COURSE, newCourseId, Ent.COURSE_WORK_ID, result['id']], Act.MODIFIER_FROM,
47325
47433
  [Ent.COURSE, self.courseId, Ent.COURSE_WORK, courseWorkId], j, jcount)
47326
- except GAPI.notFound as e:
47327
- entityActionFailedWarning([Ent.COURSE, newCourseId], str(e), i, count)
47328
- return
47329
- except (GAPI.badRequest, GAPI.failedPrecondition, GAPI.backendError, GAPI.internalError, GAPI.invalidArgument,
47434
+ except (GAPI.notFound, GAPI.badRequest, GAPI.failedPrecondition, GAPI.backendError, GAPI.internalError, GAPI.invalidArgument,
47330
47435
  GAPI.permissionDenied, GAPI.forbidden, GAPI.serviceNotAvailable) as e:
47331
47436
  entityModifierItemValueListActionFailedWarning([Ent.COURSE, newCourseId], Act.MODIFIER_FROM,
47332
47437
  [Ent.COURSE, self.courseId, Ent.COURSE_WORK, courseWorkId], str(e), j, jcount)
@@ -47681,7 +47786,8 @@ def _getCoursesOwnerInfo(croom, courseIds, useOwnerAccess, addCIIdScope=True):
47681
47786
  if courseId not in coursesInfo:
47682
47787
  try:
47683
47788
  course = callGAPI(croom.courses(), 'get',
47684
- throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED, GAPI.SERVICE_NOT_AVAILABLE],
47789
+ throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE,
47790
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
47685
47791
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
47686
47792
  id=courseId, fields='name,ownerId')
47687
47793
  if useOwnerAccess:
@@ -47692,10 +47798,10 @@ def _getCoursesOwnerInfo(croom, courseIds, useOwnerAccess, addCIIdScope=True):
47692
47798
  coursesInfo[ciCourseId] = {'name': course['name'], 'croom': ocroom}
47693
47799
  except GAPI.notFound:
47694
47800
  entityDoesNotExistWarning(Ent.COURSE, courseId)
47695
- except (GAPI.permissionDenied, GAPI.serviceNotAvailable) as e:
47801
+ except GAPI.serviceNotAvailable as e:
47696
47802
  entityActionFailedWarning([Ent.COURSE, courseId], str(e))
47697
- except GAPI.forbidden:
47698
- ClientAPIAccessDeniedExit()
47803
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
47804
+ ClientAPIAccessDeniedExit(str(e))
47699
47805
  return 0, len(coursesInfo), coursesInfo
47700
47806
 
47701
47807
  def _getCourseAliasesMembers(croom, ocroom, courseId, courseShowProperties, teachersFields, studentsFields, showGettings=False, i=0, count=0):
@@ -47711,13 +47817,14 @@ def _getCourseAliasesMembers(croom, ocroom, courseId, courseShowProperties, teac
47711
47817
  try:
47712
47818
  aliases = callGAPIpages(croom.courses().aliases(), 'list', 'aliases',
47713
47819
  pageMessage=pageMessage,
47714
- throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.SERVICE_NOT_AVAILABLE, GAPI.NOT_IMPLEMENTED],
47820
+ throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE, GAPI.NOT_IMPLEMENTED,
47821
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
47715
47822
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
47716
47823
  courseId=courseId, pageSize=GC.Values[GC.CLASSROOM_MAX_RESULTS])
47717
47824
  except (GAPI.notFound, GAPI.serviceNotAvailable, GAPI.notImplemented):
47718
47825
  pass
47719
- except GAPI.forbidden:
47720
- ClientAPIAccessDeniedExit()
47826
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
47827
+ ClientAPIAccessDeniedExit(str(e))
47721
47828
  if courseShowProperties['members'] != 'none':
47722
47829
  if courseShowProperties['members'] != 'students':
47723
47830
  if showGettings:
@@ -47726,13 +47833,14 @@ def _getCourseAliasesMembers(croom, ocroom, courseId, courseShowProperties, teac
47726
47833
  try:
47727
47834
  teachers = callGAPIpages(ocroom.courses().teachers(), 'list', 'teachers',
47728
47835
  pageMessage=pageMessage,
47729
- throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.SERVICE_NOT_AVAILABLE],
47836
+ throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE,
47837
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
47730
47838
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
47731
47839
  courseId=courseId, fields=teachersFields, pageSize=GC.Values[GC.CLASSROOM_MAX_RESULTS])
47732
47840
  except (GAPI.notFound, GAPI.serviceNotAvailable):
47733
47841
  pass
47734
- except GAPI.forbidden:
47735
- ClientAPIAccessDeniedExit()
47842
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
47843
+ ClientAPIAccessDeniedExit(str(e))
47736
47844
  if courseShowProperties['members'] != 'teachers':
47737
47845
  if showGettings:
47738
47846
  printGettingEntityItemForWhom(Ent.STUDENT, formatKeyValueList('', [Ent.Singular(Ent.COURSE), courseId], currentCount(i, count)))
@@ -47740,13 +47848,14 @@ def _getCourseAliasesMembers(croom, ocroom, courseId, courseShowProperties, teac
47740
47848
  try:
47741
47849
  students = callGAPIpages(ocroom.courses().students(), 'list', 'students',
47742
47850
  pageMessage=pageMessage,
47743
- throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.SERVICE_NOT_AVAILABLE],
47851
+ throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE,
47852
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
47744
47853
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
47745
47854
  courseId=courseId, fields=studentsFields, pageSize=GC.Values[GC.CLASSROOM_MAX_RESULTS])
47746
47855
  except (GAPI.notFound, GAPI.serviceNotAvailable):
47747
47856
  pass
47748
- except GAPI.forbidden:
47749
- ClientAPIAccessDeniedExit()
47857
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
47858
+ ClientAPIAccessDeniedExit(str(e))
47750
47859
  return (aliases, teachers, students)
47751
47860
 
47752
47861
  def _doInfoCourses(courseIdList):
@@ -47842,10 +47951,10 @@ def _doInfoCourses(courseIdList):
47842
47951
  Ind.Decrement()
47843
47952
  except GAPI.notFound:
47844
47953
  entityActionFailedWarning([Ent.COURSE, removeCourseIdScope(courseId)], Msg.DOES_NOT_EXIST, i, count)
47845
- except (GAPI.permissionDenied, GAPI.serviceNotAvailable) as e:
47954
+ except GAPI.serviceNotAvailable as e:
47846
47955
  entityActionFailedWarning([Ent.COURSE, removeCourseIdScope(courseId)], str(e), i, count)
47847
- except GAPI.forbidden:
47848
- ClientAPIAccessDeniedExit()
47956
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
47957
+ ClientAPIAccessDeniedExit(str(e))
47849
47958
 
47850
47959
  # gam info courses <CourseEntity> [owneraccess]
47851
47960
  # [owneremail] [alias|aliases] [show none|all|students|teachers] [countsonly]
@@ -47952,7 +48061,8 @@ def _getCoursesInfo(croom, courseSelectionParameters, courseShowProperties, getO
47952
48061
  courseId = addCourseIdScope(courseId)
47953
48062
  try:
47954
48063
  info = callGAPI(croom.courses(), 'get',
47955
- throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.SERVICE_NOT_AVAILABLE],
48064
+ throwReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE,
48065
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
47956
48066
  retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
47957
48067
  id=courseId, fields=fields)
47958
48068
  coursesInfo.append(info)
@@ -47960,8 +48070,8 @@ def _getCoursesInfo(croom, courseSelectionParameters, courseShowProperties, getO
47960
48070
  entityDoesNotExistWarning(Ent.COURSE, courseId)
47961
48071
  except GAPI.serviceNotAvailable as e:
47962
48072
  entityActionFailedWarning([Ent.COURSE, courseId], str(e))
47963
- except GAPI.forbidden:
47964
- ClientAPIAccessDeniedExit()
48073
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
48074
+ ClientAPIAccessDeniedExit(str(e))
47965
48075
  return coursesInfo
47966
48076
 
47967
48077
  # gam print courses [todrive <ToDriveAttribute>*] (course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
@@ -48923,77 +49033,114 @@ def doPrintCourseParticipants():
48923
49033
  csvPF.SetSortTitles(COURSE_PARTICIPANTS_SORT_TITLES)
48924
49034
  csvPF.writeCSVfile('Course Participants')
48925
49035
 
48926
- def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
49036
+ def _batchAddItemsToCourse(croom, courseId, i, count, addItems, addType):
49037
+ def _addIdToResponse(response, riItem):
49038
+ if addType == Ent.COURSE_ANNOUNCEMENT:
49039
+ respId = response.get('id', '')
49040
+ elif addType == Ent.COURSE_TOPIC:
49041
+ respId = response.get('topicId', '')
49042
+ else:
49043
+ respId = ''
49044
+ if respId:
49045
+ return riItem + f'({respId})'
49046
+ return riItem
49047
+
48927
49048
  _ADD_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
48928
49049
  GAPI.ALREADY_EXISTS: Msg.DUPLICATE,
48929
49050
  GAPI.FAILED_PRECONDITION: Msg.NOT_ALLOWED}
48930
- def _callbackAddItemsToCourse(request_id, _, exception):
49051
+ def _callbackAddItemsToCourse(request_id, response, exception):
48931
49052
  ri = request_id.splitlines()
49053
+ if addType == Ent.COURSE_ANNOUNCEMENT:
49054
+ mg = re.match(r"^{'text': '(.+)'}$", ri[RI_ITEM])
49055
+ if mg:
49056
+ riText = mg.group(1)
49057
+ else:
49058
+ riText = ''
49059
+ if len(riText) > 100:
49060
+ riItem = riText[0:100]+'...'
49061
+ else:
49062
+ riItem = riText
49063
+ else:
49064
+ riItem = ri[RI_ITEM]
48932
49065
  if exception is None:
48933
- entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], int(ri[RI_J]), int(ri[RI_JCOUNT]))
49066
+ riItem = _addIdToResponse(response, riItem)
49067
+ entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], addType, riItem], int(ri[RI_J]), int(ri[RI_JCOUNT]))
48934
49068
  else:
48935
49069
  http_status, reason, message = checkGAPIError(exception)
48936
- if (reason not in {GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE}) and ((reason != GAPI.NOT_FOUND) or (ri[RI_ROLE] == Ent.COURSE_ALIAS)):
49070
+ if (reason not in {GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE}) and ((reason != GAPI.NOT_FOUND) or (addType == Ent.COURSE_ALIAS)):
48937
49071
  if reason in [GAPI.FORBIDDEN, GAPI.BACKEND_ERROR]:
48938
- errMsg = getPhraseDNEorSNA(ri[RI_ITEM])
49072
+ errMsg = getPhraseDNEorSNA(riItem)
48939
49073
  else:
48940
49074
  errMsg = getHTTPError(_ADD_PART_REASON_TO_MESSAGE_MAP, http_status, reason, message)
48941
- if (reason == GAPI.PERMISSION_DENIED) and (ri[RI_ROLE] in {Ent.STUDENT, Ent.TEACHER}) and ('CannotDirectAddUser' in errMsg):
48942
- errMsg += f' Add external user with: gam user {ri[RI_ITEM]} create classroominvitation courses {ri[RI_ENTITY]} role {Ent.Singular(ri[RI_ROLE])}'
48943
- entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], errMsg, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49075
+ if (reason == GAPI.PERMISSION_DENIED) and (addType in {Ent.STUDENT, Ent.TEACHER}) and ('CannotDirectAddUser' in errMsg):
49076
+ errMsg += f' Add external user with: gam user {riItem} create classroominvitation courses {ri[RI_ENTITY]} addType {Ent.Singular(addType)}'
49077
+ entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], addType, riItem], errMsg, int(ri[RI_J]), int(ri[RI_JCOUNT]))
48944
49078
  return
48945
49079
  waitOnFailure(1, 10, reason, message)
49080
+ if addType in {Ent.STUDENT, Ent.TEACHER, Ent.COURSE_TOPIC}:
49081
+ rbody = {attribute: riItem}
49082
+ elif addType == Ent.COURSE_ALIAS:
49083
+ rbody = {attribute: addCourseAliasScope(riItem)}
49084
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
49085
+ rbody = ri[RI_ITEM]
48946
49086
  try:
48947
- callGAPI(service, 'create',
48948
- throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.BACKEND_ERROR,
48949
- GAPI.ALREADY_EXISTS, GAPI.FAILED_PRECONDITION,
48950
- GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
48951
- retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE], triesLimit=0 if reason != GAPI.NOT_FOUND else 3,
48952
- courseId=addCourseIdScope(ri[RI_ENTITY]),
48953
- body={attribute: ri[RI_ITEM] if ri[RI_ROLE] != Ent.COURSE_ALIAS else addCourseAliasScope(ri[RI_ITEM])},
48954
- fields='')
49087
+ result = callGAPI(service, 'create',
49088
+ throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.BACKEND_ERROR,
49089
+ GAPI.ALREADY_EXISTS, GAPI.FAILED_PRECONDITION,
49090
+ GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
49091
+ retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE], triesLimit=0 if reason != GAPI.NOT_FOUND else 3,
49092
+ courseId=addCourseIdScope(ri[RI_ENTITY]), body=rbody, fields=returnFields)
49093
+ riItem = _addIdToResponse(result, riItem)
48955
49094
  except (GAPI.notFound, GAPI.backendError, GAPI.forbidden):
48956
- entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], getPhraseDNEorSNA(ri[RI_ITEM]), int(ri[RI_J]), int(ri[RI_JCOUNT]))
49095
+ entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], addType, riItem], getPhraseDNEorSNA(riItem), int(ri[RI_J]), int(ri[RI_JCOUNT]))
48957
49096
  except GAPI.alreadyExists:
48958
- entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.DUPLICATE, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49097
+ entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], addType, riItem], Msg.DUPLICATE, int(ri[RI_J]), int(ri[RI_JCOUNT]))
48959
49098
  except GAPI.failedPrecondition:
48960
- entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.NOT_ALLOWED, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49099
+ entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], addType, riItem], Msg.NOT_ALLOWED, int(ri[RI_J]), int(ri[RI_JCOUNT]))
48961
49100
  except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
48962
- entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
49101
+ entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], addType, riItem], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
48963
49102
 
48964
- if role == Ent.STUDENT:
49103
+ returnFields = ''
49104
+ if addType == Ent.STUDENT:
48965
49105
  service = croom.courses().students()
48966
49106
  attribute = 'userId'
48967
- elif role == Ent.TEACHER:
49107
+ elif addType == Ent.TEACHER:
48968
49108
  service = croom.courses().teachers()
48969
49109
  attribute = 'userId'
48970
- elif role == Ent.COURSE_ALIAS:
49110
+ elif addType == Ent.COURSE_ALIAS:
48971
49111
  service = croom.courses().aliases()
48972
49112
  attribute = 'alias'
48973
- else: # role == Ent.COURSE_TOPIC:
49113
+ elif addType == Ent.COURSE_TOPIC:
48974
49114
  service = croom.courses().topics()
48975
49115
  attribute = 'name'
49116
+ returnFields = 'topicId'
49117
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
49118
+ service = croom.courses().announcements()
49119
+ attribute = 'text'
49120
+ returnFields = 'id'
48976
49121
  method = getattr(service, 'create')
48977
49122
  Act.Set(Act.ADD)
48978
- jcount = len(addParticipants)
49123
+ jcount = len(addItems)
48979
49124
  noScopeCourseId = removeCourseIdScope(courseId)
48980
- entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, role, i, count)
49125
+ entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, addType, i, count)
48981
49126
  Ind.Increment()
48982
- svcargs = dict([('courseId', courseId), ('body', {attribute: None}), ('fields', '')]+GM.Globals[GM.EXTRA_ARGS_LIST])
49127
+ svcargs = dict([('courseId', courseId), ('body', {attribute: None}), ('fields', returnFields)]+GM.Globals[GM.EXTRA_ARGS_LIST])
48983
49128
  dbatch = croom.new_batch_http_request(callback=_callbackAddItemsToCourse)
48984
49129
  bcount = 0
48985
49130
  j = 0
48986
- for participant in addParticipants:
49131
+ for addItem in addItems:
48987
49132
  j += 1
48988
49133
  svcparms = svcargs.copy()
48989
- if role in {Ent.STUDENT, Ent.TEACHER}:
48990
- svcparms['body'][attribute] = cleanItem = normalizeEmailAddressOrUID(participant)
48991
- elif role == Ent.COURSE_ALIAS:
48992
- svcparms['body'][attribute] = addCourseAliasScope(participant)
49134
+ if addType in {Ent.STUDENT, Ent.TEACHER}:
49135
+ svcparms['body'][attribute] = cleanItem = normalizeEmailAddressOrUID(addItem)
49136
+ elif addType == Ent.COURSE_ALIAS:
49137
+ svcparms['body'][attribute] = addCourseAliasScope(addItem)
48993
49138
  cleanItem = removeCourseAliasScope(svcparms['body'][attribute])
48994
- else: # role == Ent.COURSE_TOPIC:
48995
- svcparms['body'][attribute] = cleanItem = participant
48996
- dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, role))
49139
+ elif addType == Ent.COURSE_TOPIC:
49140
+ svcparms['body'][attribute] = cleanItem = addItem
49141
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
49142
+ svcparms['body'] = cleanItem = addItem
49143
+ dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, addType))
48997
49144
  bcount += 1
48998
49145
  if bcount >= GC.Values[GC.BATCH_SIZE]:
48999
49146
  executeBatch(dbatch)
@@ -49003,74 +49150,82 @@ def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
49003
49150
  dbatch.execute()
49004
49151
  Ind.Decrement()
49005
49152
 
49006
- def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeParticipants, role):
49153
+ def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeItems, removeType):
49007
49154
  _REMOVE_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
49008
49155
  GAPI.FORBIDDEN: Msg.FORBIDDEN,
49009
49156
  GAPI.PERMISSION_DENIED: Msg.PERMISSION_DENIED}
49010
49157
  def _callbackRemoveItemsFromCourse(request_id, _, exception):
49011
49158
  ri = request_id.splitlines()
49159
+ riItem = ri[RI_ITEM]
49012
49160
  if exception is None:
49013
- entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], int(ri[RI_J]), int(ri[RI_JCOUNT]))
49161
+ entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], int(ri[RI_J]), int(ri[RI_JCOUNT]))
49014
49162
  else:
49015
49163
  http_status, reason, message = checkGAPIError(exception)
49016
49164
  if reason not in {GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE}:
49017
- if reason == GAPI.NOT_FOUND and ri[RI_ROLE] != Ent.COURSE_ALIAS:
49018
- errMsg = f'{Msg.NOT_A} {Ent.Singular(ri[RI_ROLE])}'
49165
+ if reason == GAPI.NOT_FOUND and removeType != Ent.COURSE_ALIAS:
49166
+ errMsg = f'{Msg.NOT_A} {Ent.Singular(removeType)}'
49019
49167
  else:
49020
49168
  errMsg = getHTTPError(_REMOVE_PART_REASON_TO_MESSAGE_MAP, http_status, reason, message)
49021
- entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], errMsg, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49169
+ entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], errMsg, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49022
49170
  return
49023
49171
  waitOnFailure(1, 10, reason, message)
49172
+ if removeType in {Ent.STUDENT, Ent.TEACHER, Ent.COURSE_TOPIC, Ent.COURSE_ANNOUNCEMENT}:
49173
+ rbody = {attribute: riItem}
49174
+ else: # removeType == Ent.COURSE_ALIAS:
49175
+ rbody = {attribute: addCourseAliasScope(riItem)}
49024
49176
  try:
49025
49177
  callGAPI(service, 'delete',
49026
49178
  throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED,
49027
- GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
49179
+ GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE, GAPI.FAILED_PRECONDITION],
49028
49180
  retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE], triesLimit=0 if reason != GAPI.NOT_FOUND else 3,
49029
- courseId=addCourseIdScope(ri[RI_ENTITY]),
49030
- body={attribute: ri[RI_ITEM] if ri[RI_ROLE] != Ent.COURSE_ALIAS else addCourseAliasScope(ri[RI_ITEM])},
49031
- fields='')
49181
+ courseId=addCourseIdScope(ri[RI_ENTITY]), body=rbody, fields='')
49032
49182
  except GAPI.notFound:
49033
- entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.DOES_NOT_EXIST, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49183
+ entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], Msg.DOES_NOT_EXIST, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49034
49184
  except GAPI.forbidden:
49035
- entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.FORBIDDEN, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49185
+ entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], Msg.FORBIDDEN, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49036
49186
  except GAPI.permissionDenied:
49037
- entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], Msg.PERMISSION_DENIED, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49038
- except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
49039
- entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
49187
+ entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], Msg.PERMISSION_DENIED, int(ri[RI_J]), int(ri[RI_JCOUNT]))
49188
+ except (GAPI.quotaExceeded, GAPI.serviceNotAvailable, GAPI.failedPrecondition) as e:
49189
+ entityActionFailedWarning([Ent.COURSE, ri[RI_ENTITY], removeType, riItem], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
49040
49190
 
49041
- if role == Ent.STUDENT:
49191
+ if removeType == Ent.STUDENT:
49042
49192
  service = croom.courses().students()
49043
49193
  attribute = 'userId'
49044
- elif role == Ent.TEACHER:
49194
+ elif removeType == Ent.TEACHER:
49045
49195
  service = croom.courses().teachers()
49046
49196
  attribute = 'userId'
49047
- elif role == Ent.COURSE_ALIAS:
49197
+ elif removeType == Ent.COURSE_ALIAS:
49048
49198
  service = croom.courses().aliases()
49049
49199
  attribute = 'alias'
49050
- else: # role == Ent.COURSE_TOPIC:
49200
+ elif removeType == Ent.COURSE_TOPIC:
49051
49201
  service = croom.courses().topics()
49052
49202
  attribute = 'id'
49203
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49204
+ service = croom.courses().announcements()
49205
+ attribute = 'id'
49053
49206
  method = getattr(service, 'delete')
49054
49207
  Act.Set(Act.REMOVE)
49055
- jcount = len(removeParticipants)
49208
+ jcount = len(removeItems)
49056
49209
  noScopeCourseId = removeCourseIdScope(courseId)
49057
- entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, role, i, count)
49210
+ entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, removeType, i, count)
49058
49211
  Ind.Increment()
49059
49212
  svcargs = dict([('courseId', courseId), ('fields', ''), (attribute, None)]+GM.Globals[GM.EXTRA_ARGS_LIST])
49060
49213
  dbatch = croom.new_batch_http_request(callback=_callbackRemoveItemsFromCourse)
49061
49214
  bcount = 0
49062
49215
  j = 0
49063
- for participant in removeParticipants:
49216
+ for removeItem in removeItems:
49064
49217
  j += 1
49065
49218
  svcparms = svcargs.copy()
49066
- if role in {Ent.STUDENT, Ent.TEACHER}:
49067
- svcparms[attribute] = cleanItem = normalizeEmailAddressOrUID(participant)
49068
- elif role == Ent.COURSE_ALIAS:
49069
- svcparms[attribute] = addCourseAliasScope(participant)
49219
+ if removeType in {Ent.STUDENT, Ent.TEACHER}:
49220
+ svcparms[attribute] = cleanItem = normalizeEmailAddressOrUID(removeItem)
49221
+ elif removeType == Ent.COURSE_ALIAS:
49222
+ svcparms[attribute] = addCourseAliasScope(removeItem)
49070
49223
  cleanItem = removeCourseAliasScope(svcparms[attribute])
49071
- else: # role == Ent.COURSE_TOPIC:
49072
- svcparms[attribute] = cleanItem = participant
49073
- dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, role))
49224
+ elif removeType == Ent.COURSE_TOPIC:
49225
+ svcparms[attribute] = cleanItem = removeItem
49226
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49227
+ svcparms[attribute] = cleanItem = removeItem
49228
+ dbatch.add(method(**svcparms), request_id=batchRequestID(noScopeCourseId, 0, 0, j, jcount, cleanItem, removeType))
49074
49229
  bcount += 1
49075
49230
  if bcount >= GC.Values[GC.BATCH_SIZE]:
49076
49231
  executeBatch(dbatch)
@@ -49100,9 +49255,28 @@ def _updateCourseOwner(croom, courseId, owner, i, count):
49100
49255
  entityActionPerformedMessage([Ent.COURSE, removeCourseIdScope(courseId), Ent.TEACHER, owner], Msg.ALREADY_WAS_OWNER, i, count)
49101
49256
  Act.Set(action)
49102
49257
 
49103
- ADD_REMOVE_PARTICIPANT_TYPES_MAP = {
49258
+ def getCourseAnnouncement(createCmd):
49259
+ body = {}
49260
+ while Cmd.ArgumentsRemaining():
49261
+ myarg = getArgument()
49262
+ if myarg in SORF_TEXT_ARGUMENTS:
49263
+ body['text'] = getStringOrFile(myarg, minLen=1, unescapeCRLF=True)[0]
49264
+ elif myarg == 'scheduledtime':
49265
+ body['scheduledTime'] = getTimeOrDeltaFromNow()
49266
+ elif myarg == 'state':
49267
+ body['state'] = getChoice(COURSE_STATE_MAPS[Cmd.OB_COURSE_ANNOUNCEMENT_ADD_STATE_LIST if createCmd else Cmd.OB_COURSE_ANNOUNCEMENT_UPDATE_STATE_LIST],
49268
+ mapChoice=True)
49269
+ else:
49270
+ unknownArgumentExit()
49271
+ if createCmd and 'text' not in body:
49272
+ missingArgumentExit('text <String>')
49273
+ return body
49274
+
49275
+ ADD_REMOVE_UPDATE_ITEM_TYPES_MAP = {
49104
49276
  'alias': Ent.COURSE_ALIAS,
49105
49277
  'aliases': Ent.COURSE_ALIAS,
49278
+ 'announcement': Ent.COURSE_ANNOUNCEMENT,
49279
+ 'announcements': Ent.COURSE_ANNOUNCEMENT,
49106
49280
  'student': Ent.STUDENT,
49107
49281
  'students': Ent.STUDENT,
49108
49282
  'teacher': Ent.TEACHER,
@@ -49123,6 +49297,10 @@ PARTICIPANT_EN_MAP = {
49123
49297
 
49124
49298
  # gam courses <CourseEntity> create alias <CourseAliasEntity>
49125
49299
  # gam course <CourseID> create alias <CourseAlias>
49300
+ # gam courses <CourseEntity> create announcement
49301
+ # <CourseAnnouncementContent> [scheduledtime <Time>] [state draft|published]
49302
+ # gam course <CourseID> create announcement
49303
+ # <CourseAnnouncementContent> [scheduledtime <Time>] [state draft|published]
49126
49304
  # gam courses <CourseEntity> create topic <CourseTopicEntity>
49127
49305
  # gam course <CourseID> create topic <CourseTopic>
49128
49306
  # gam courses <CourseEntity> create students <UserTypeEntity>
@@ -49131,35 +49309,39 @@ PARTICIPANT_EN_MAP = {
49131
49309
  # gam course <CourseID> create teacher [makefirstteacherowner] <EmailAddress>
49132
49310
  def doCourseAddItems(courseIdList, getEntityListArg):
49133
49311
  croom = buildGAPIObject(API.CLASSROOM)
49134
- role = getChoice(ADD_REMOVE_PARTICIPANT_TYPES_MAP, mapChoice=True)
49135
- if role == Ent.TEACHER:
49312
+ addType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
49313
+ if addType == Ent.TEACHER:
49136
49314
  makeFirstTeacherOwner = checkArgumentPresent(['makefirstteacherowner'])
49137
49315
  else:
49138
49316
  makeFirstTeacherOwner = False
49139
49317
  if not getEntityListArg:
49140
- if role in {Ent.STUDENT, Ent.TEACHER}:
49318
+ if addType in {Ent.STUDENT, Ent.TEACHER}:
49141
49319
  addItems = getStringReturnInList(Cmd.OB_EMAIL_ADDRESS)
49142
- elif role == Ent.COURSE_ALIAS:
49320
+ elif addType == Ent.COURSE_ALIAS:
49143
49321
  addItems = getStringReturnInList(Cmd.OB_COURSE_ALIAS)
49144
- else: # role == Ent.COURSE_TOPIC:
49322
+ elif addType == Ent.COURSE_TOPIC:
49145
49323
  addItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC)
49324
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
49325
+ addItems = [getCourseAnnouncement(True)]
49146
49326
  courseParticipantLists = None
49147
49327
  else:
49148
- if role in {Ent.STUDENT, Ent.TEACHER}:
49328
+ if addType in {Ent.STUDENT, Ent.TEACHER}:
49149
49329
  _, addItems = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS,
49150
- typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[role]},
49330
+ typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[addType]},
49151
49331
  isSuspended=False, isArchived=False)
49152
- elif role == Ent.COURSE_ALIAS:
49332
+ elif addType == Ent.COURSE_ALIAS:
49153
49333
  addItems = getEntityList(Cmd.OB_COURSE_ALIAS_ENTITY, shlexSplit=True)
49154
- else: # role == Ent.COURSE_TOPIC:
49334
+ elif addType == Ent.COURSE_TOPIC:
49155
49335
  addItems = getEntityList(Cmd.OB_COURSE_TOPIC_ENTITY, shlexSplit=True)
49336
+ else: # addType == Ent.COURSE_ANNOUNCEMENT:
49337
+ addItems = getCourseAnnouncement(True)
49156
49338
  courseParticipantLists = addItems if isinstance(addItems, dict) else None
49157
49339
  if courseParticipantLists is None:
49158
49340
  firstTeacher = None
49159
49341
  if makeFirstTeacherOwner and addItems:
49160
49342
  firstTeacher = normalizeEmailAddressOrUID(addItems[0])
49161
49343
  checkForExtraneousArguments()
49162
- i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, role == Ent.COURSE_TOPIC,
49344
+ i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, addType in {Ent.COURSE_TOPIC, Ent.COURSE_ANNOUNCEMENT},
49163
49345
  addCIIdScope=courseParticipantLists is None)
49164
49346
  for courseId, courseInfo in coursesInfo.items():
49165
49347
  i += 1
@@ -49169,43 +49351,51 @@ def doCourseAddItems(courseIdList, getEntityListArg):
49169
49351
  if makeFirstTeacherOwner and addItems:
49170
49352
  firstTeacher = normalizeEmailAddressOrUID(addItems[0])
49171
49353
  courseId = addCourseIdScope(courseId)
49172
- _batchAddItemsToCourse(courseInfo['croom'], courseId, i, count, addItems, role)
49354
+ _batchAddItemsToCourse(courseInfo['croom'], courseId, i, count, addItems, addType)
49173
49355
  if makeFirstTeacherOwner and firstTeacher:
49174
49356
  _updateCourseOwner(courseInfo['croom'], courseId, firstTeacher, i, count)
49175
49357
 
49176
49358
  # gam courses <CourseEntity> remove alias <CourseAliasEntity>
49177
49359
  # gam course <CourseID> remove alias <CourseAlias>
49360
+ # gam courses <CourseEntity> remove announcement <CourseAnnouncementIDEntity>
49361
+ # gam course <CourseID> remove announcement <CourseAnnouncementID>
49178
49362
  # gam courses <CourseEntity> remove topic <CourseTopicIDEntity>
49179
49363
  # gam course <CourseID> remove topic <CourseTopicID>
49180
49364
  # gam courses <CourseEntity> remove teachers|students [owneracccess] <UserTypeEntity>
49181
49365
  # gam course <CourseID> remove teacher|student [owneracccess] <EmailAddress>
49182
49366
  def doCourseRemoveItems(courseIdList, getEntityListArg):
49183
49367
  croom = buildGAPIObject(API.CLASSROOM)
49184
- role = getChoice(ADD_REMOVE_PARTICIPANT_TYPES_MAP, mapChoice=True)
49368
+ removeType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
49185
49369
  if not getEntityListArg:
49186
- if role in {Ent.STUDENT, Ent.TEACHER}:
49370
+ if removeType in {Ent.STUDENT, Ent.TEACHER}:
49187
49371
  useOwnerAccess = GC.Values[GC.USE_COURSE_OWNER_ACCESS]
49188
49372
  if checkArgumentPresent(OWNER_ACCESS_OPTIONS):
49189
49373
  useOwnerAccess = True
49190
49374
  removeItems = getStringReturnInList(Cmd.OB_EMAIL_ADDRESS)
49191
- elif role == Ent.COURSE_ALIAS:
49375
+ elif removeType == Ent.COURSE_ALIAS:
49192
49376
  useOwnerAccess = False
49193
49377
  removeItems = getStringReturnInList(Cmd.OB_COURSE_ALIAS)
49194
- else: # role == Ent.COURSE_TOPIC:
49378
+ elif removeType == Ent.COURSE_TOPIC:
49195
49379
  useOwnerAccess = True
49196
49380
  removeItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC_ID)
49381
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49382
+ useOwnerAccess = True
49383
+ removeItems = getStringReturnInList(Cmd.OB_COURSE_ANNOUNCEMENT_ID)
49197
49384
  courseParticipantLists = None
49198
49385
  else:
49199
- if role in {Ent.STUDENT, Ent.TEACHER}:
49386
+ if removeType in {Ent.STUDENT, Ent.TEACHER}:
49200
49387
  useOwnerAccess = checkArgumentPresent(OWNER_ACCESS_OPTIONS)
49201
49388
  _, removeItems = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS,
49202
- typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[role]})
49203
- elif role == Ent.COURSE_ALIAS:
49389
+ typeMap={Cmd.ENTITY_COURSEPARTICIPANTS: PARTICIPANT_EN_MAP[removeType]})
49390
+ elif removeType == Ent.COURSE_ALIAS:
49204
49391
  useOwnerAccess = False
49205
49392
  removeItems = getEntityList(Cmd.OB_COURSE_ALIAS_ENTITY, shlexSplit=True)
49206
- else: # role == Ent.COURSE_TOPIC:
49393
+ elif removeType == Ent.COURSE_TOPIC:
49207
49394
  useOwnerAccess = True
49208
49395
  removeItems = getEntityList(Cmd.OB_COURSE_TOPIC_ID_ENTITY, shlexSplit=True)
49396
+ else: # removeType == Ent.COURSE_ANNOUNCEMENT:
49397
+ useOwnerAccess = True
49398
+ removeItems = getEntityList(Cmd.OB_COURSE_ANNOUNCEMENT_ID_ENTITY, shlexSplit=True)
49209
49399
  courseParticipantLists = removeItems if isinstance(removeItems, dict) else None
49210
49400
  checkForExtraneousArguments()
49211
49401
  i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, useOwnerAccess,
@@ -49215,7 +49405,72 @@ def doCourseRemoveItems(courseIdList, getEntityListArg):
49215
49405
  if courseParticipantLists:
49216
49406
  removeItems = courseParticipantLists[courseId]
49217
49407
  courseId = addCourseIdScope(courseId)
49218
- _batchRemoveItemsFromCourse(courseInfo['croom'], courseId, i, count, removeItems, role)
49408
+ _batchRemoveItemsFromCourse(courseInfo['croom'], courseId, i, count, removeItems, removeType)
49409
+
49410
+ # gam courses <CourseEntity> update announcement <CourseAnnouncemntIDEntity>
49411
+ # [<CourseAnnouncementContent>] [scheduledtime <Time>] [state published]
49412
+ # gam course <CourseID> update announcement <CourseAnnouncementID>
49413
+ # [<CourseAnnouncementContent>] [scheduledtime <Time>] [state published]
49414
+ # gam courses <CourseEntity> update topic <CourseTopicIDEntity> <CourseTopic>
49415
+ # gam course <CourseID> update topic <CourseTopicID> <CourseTopic>
49416
+ def doCourseUpdateItems(courseIdList, getEntityListArg):
49417
+ croom = buildGAPIObject(API.CLASSROOM)
49418
+ updateType = getChoice(ADD_REMOVE_UPDATE_ITEM_TYPES_MAP, mapChoice=True)
49419
+ if not getEntityListArg:
49420
+ if updateType == Ent.COURSE_TOPIC:
49421
+ useOwnerAccess = True
49422
+ updateItems = getStringReturnInList(Cmd.OB_COURSE_TOPIC_ID)
49423
+ body = {'name': getString(Cmd.OB_COURSE_TOPIC)}
49424
+ else: # updateType == Ent.COURSE_ANNOUNCEMENT:
49425
+ useOwnerAccess = True
49426
+ updateItems = getStringReturnInList(Cmd.OB_COURSE_ANNOUNCEMENT_ID)
49427
+ body = getCourseAnnouncement(False)
49428
+ courseItemLists = None
49429
+ else:
49430
+ if updateType == Ent.COURSE_TOPIC:
49431
+ useOwnerAccess = True
49432
+ updateItems = getEntityList(Cmd.OB_COURSE_TOPIC_ID_ENTITY, shlexSplit=True)
49433
+ body = {'name': getString(Cmd.OB_COURSE_TOPIC)}
49434
+ else: # updateType == Ent.COURSE_ANNOUNCEMENT:
49435
+ useOwnerAccess = True
49436
+ updateItems = getEntityList(Cmd.OB_COURSE_ANNOUNCEMENT_ID_ENTITY, shlexSplit=True)
49437
+ body = getCourseAnnouncement(False)
49438
+ courseItemLists = updateItems if isinstance(updateItems, dict) else None
49439
+ checkForExtraneousArguments()
49440
+ i, count, coursesInfo = _getCoursesOwnerInfo(croom, courseIdList, useOwnerAccess,
49441
+ addCIIdScope=courseItemLists is None)
49442
+ for courseId, courseInfo in coursesInfo.items():
49443
+ i += 1
49444
+ if courseItemLists:
49445
+ updateItems = courseItemLists[courseId]
49446
+ courseId = addCourseIdScope(courseId)
49447
+ jcount = len(updateItems)
49448
+ noScopeCourseId = removeCourseIdScope(courseId)
49449
+ if updateType == Ent.COURSE_TOPIC:
49450
+ service = courseInfo['croom'].courses().topics()
49451
+ else: # updateType == Ent.COURSE_ANNOUNCEMENT:
49452
+ service = courseInfo['croom'].courses().announcements()
49453
+ entityPerformActionNumItems([Ent.COURSE, noScopeCourseId], jcount, updateType, i, count)
49454
+ Ind.Increment()
49455
+ j = 0
49456
+ for updateItem in updateItems:
49457
+ j += 1
49458
+ try:
49459
+ callGAPI(service, 'patch',
49460
+ throwReasons=[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED,
49461
+ GAPI.QUOTA_EXCEEDED, GAPI.SERVICE_NOT_AVAILABLE],
49462
+ retryReasons=[GAPI.NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE],
49463
+ courseId=addCourseIdScope(courseId), id=updateItem, updateMask=','.join(body.keys()), body=body, fields='')
49464
+ entityActionPerformed([Ent.COURSE, courseId, updateType, updateItem], j, jcount)
49465
+ except GAPI.notFound:
49466
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.DOES_NOT_EXIST, j, jcount)
49467
+ except GAPI.forbidden:
49468
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.FORBIDDEN, j, jcount)
49469
+ except GAPI.permissionDenied:
49470
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], Msg.PERMISSION_DENIED, j, jcount)
49471
+ except (GAPI.quotaExceeded, GAPI.serviceNotAvailable) as e:
49472
+ entityActionFailedWarning([Ent.COURSE, courseId, updateType, updateItem], str(e), j, jcount)
49473
+ Ind.Decrement()
49219
49474
 
49220
49475
  # gam courses <CourseEntity> clear teachers|students
49221
49476
  # gam course <CourseID> clear teacher|student
@@ -49403,7 +49658,8 @@ def _cancelGuardianInvitation(croom, studentId, invitationId, i=0, count=0, j=0,
49403
49658
  entityActionFailedWarning([Ent.STUDENT, studentId, Ent.GUARDIAN_INVITATION, invitationId], str(e), j, jcount)
49404
49659
  return -1
49405
49660
  except (GAPI.forbidden, GAPI.permissionDenied) as e:
49406
- studentUnknownWarning(studentId, str(e), i, count)
49661
+ ClientAPIAccessDeniedExit(str(e))
49662
+ # studentUnknownWarning(studentId, str(e), i, count)
49407
49663
  return -1
49408
49664
 
49409
49665
  # gam cancel guardianinvitation|guardianinvitations <GuardianInvitationID> <StudentItem>
@@ -49448,7 +49704,8 @@ def _deleteGuardian(croom, studentId, guardianId, guardianEmail, i, count, j, jc
49448
49704
  entityActionFailedWarning([Ent.STUDENT, studentId, Ent.GUARDIAN, guardianEmail], str(e), j, jcount)
49449
49705
  return -1
49450
49706
  except (GAPI.forbidden, GAPI.permissionDenied) as e:
49451
- studentUnknownWarning(studentId, str(e), i, count)
49707
+ ClientAPIAccessDeniedExit(str(e))
49708
+ # studentUnknownWarning(studentId, str(e), i, count)
49452
49709
  return -1
49453
49710
 
49454
49711
  def _doDeleteGuardian(croom, studentId, guardianId, guardianClass, i=0, count=0, j=0, jcount=0):
@@ -52558,7 +52815,8 @@ def doSharedDriveSearch(drive, user, i, count, query, useDomainAdminAccess):
52558
52815
  pageMessage=getPageMessageForWhom(),
52559
52816
  throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID,
52560
52817
  GAPI.QUERY_REQUIRES_ADMIN_CREDENTIALS,
52561
- GAPI.NO_LIST_TEAMDRIVES_ADMINISTRATOR_PRIVILEGE],
52818
+ GAPI.NO_LIST_TEAMDRIVES_ADMINISTRATOR_PRIVILEGE,
52819
+ GAPI.INSUFFICIENT_ADMINISTRATOR_PRIVILEGES],
52562
52820
  q=query, useDomainAdminAccess=useDomainAdminAccess,
52563
52821
  fields='nextPageToken,drives(id)', pageSize=100)
52564
52822
  if files:
@@ -52566,7 +52824,7 @@ def doSharedDriveSearch(drive, user, i, count, query, useDomainAdminAccess):
52566
52824
  entityActionNotPerformedWarning([Ent.USER, user, Ent.DRIVE_FILE, None], emptyQuery(query, Ent.SHAREDDRIVE), i, count)
52567
52825
  except (GAPI.invalidQuery, GAPI.invalid):
52568
52826
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE, None], invalidQuery(query), i, count)
52569
- except (GAPI.queryRequiresAdminCredentials, GAPI.noListTeamDrivesAdministratorPrivilege) as e:
52827
+ except (GAPI.queryRequiresAdminCredentials, GAPI.noListTeamDrivesAdministratorPrivilege, GAPI.insufficientAdministratorPrivileges) as e:
52570
52828
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE, None], str(e), i, count)
52571
52829
  except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
52572
52830
  userDriveServiceNotEnabledWarning(user, str(e), i, count)
@@ -52869,7 +53127,11 @@ def _convertSharedDriveNameToId(drive, user, i, count, fileIdEntity, useDomainAd
52869
53127
  else:
52870
53128
  name = fileIdEntity['shareddrivename'].replace("'", "\\'")
52871
53129
  tdlist = callGAPIpages(drive.drives(), 'list', 'drives',
52872
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS,
53130
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID,
53131
+ GAPI.QUERY_REQUIRES_ADMIN_CREDENTIALS,
53132
+ GAPI.NO_LIST_TEAMDRIVES_ADMINISTRATOR_PRIVILEGE,
53133
+ GAPI.INSUFFICIENT_ADMINISTRATOR_PRIVILEGES,
53134
+ GAPI.FILE_NOT_FOUND],
52873
53135
  useDomainAdminAccess=useDomainAdminAccess,
52874
53136
  q=f"name='{name}'",
52875
53137
  fields='nextPageToken,drives(id,name)', pageSize=100)
@@ -52878,13 +53140,19 @@ def _convertSharedDriveNameToId(drive, user, i, count, fileIdEntity, useDomainAd
52878
53140
  if not tdlist:
52879
53141
  name = fileIdEntity['shareddrivename'].lower()
52880
53142
  feed = callGAPIpages(drive.drives(), 'list', 'drives',
52881
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS,
53143
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NO_LIST_TEAMDRIVES_ADMINISTRATOR_PRIVILEGE,
53144
+ GAPI.INSUFFICIENT_ADMINISTRATOR_PRIVILEGES],
52882
53145
  useDomainAdminAccess=useDomainAdminAccess,
52883
53146
  fields='nextPageToken,drives(id,name)', pageSize=100)
52884
53147
  tdlist = [td for td in feed if td['name'].lower() == name]
52885
53148
  except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy):
52886
53149
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_NAME, fileIdEntity['shareddrivename']], Msg.DOES_NOT_EXIST, i, count)
52887
53150
  return False
53151
+ except (GAPI.invalidQuery, GAPI.invalid, GAPI.queryRequiresAdminCredentials,
53152
+ GAPI.noListTeamDrivesAdministratorPrivilege, GAPI.insufficientAdministratorPrivileges,
53153
+ GAPI.fileNotFound) as e:
53154
+ entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_NAME, fileIdEntity['shareddrivename']], str(e), i, count)
53155
+ return False
52888
53156
  jcount = len(tdlist)
52889
53157
  if jcount == 1:
52890
53158
  fileIdEntity['shareddrive']['driveId'] = tdlist[0]['id']
@@ -52902,10 +53170,10 @@ def _getSharedDriveNameFromId(drive, sharedDriveId, useDomainAdminAccess=False):
52902
53170
  if not sharedDriveName:
52903
53171
  try:
52904
53172
  sharedDriveName = callGAPI(drive.drives(), 'get',
52905
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
53173
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FILE_NOT_FOUND, GAPI.NOT_FOUND],
52906
53174
  useDomainAdminAccess=useDomainAdminAccess,
52907
53175
  driveId=sharedDriveId, fields='name')['name']
52908
- except (GAPI.notFound, GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy):
53176
+ except (GAPI.fileNotFound, GAPI.notFound, GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy):
52909
53177
  sharedDriveName = TEAM_DRIVE
52910
53178
  GM.Globals[GM.MAP_SHAREDDRIVE_ID_TO_NAME][sharedDriveId] = sharedDriveName
52911
53179
  return sharedDriveName
@@ -52926,7 +53194,7 @@ def _getDriveFileNameFromId(drive, fileId, combineTitleId=True, useDomainAdminAc
52926
53194
  if useDomainAdminAccess:
52927
53195
  try:
52928
53196
  result = callGAPI(drive.drives(), 'get',
52929
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
53197
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FILE_NOT_FOUND, GAPI.NOT_FOUND],
52930
53198
  useDomainAdminAccess=useDomainAdminAccess,
52931
53199
  driveId=fileId, fields='name')
52932
53200
  if result:
@@ -52934,7 +53202,7 @@ def _getDriveFileNameFromId(drive, fileId, combineTitleId=True, useDomainAdminAc
52934
53202
  if combineTitleId:
52935
53203
  fileName += '('+fileId+')'
52936
53204
  return (fileName, Ent.DRIVE_FOLDER, MIMETYPE_GA_FOLDER)
52937
- except GAPI.notFound:
53205
+ except (GAPI.fileNotFound, GAPI.notFound):
52938
53206
  pass
52939
53207
  except (GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.internalError,
52940
53208
  GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy):
@@ -53100,7 +53368,7 @@ def _getDriveFileParentInfo(drive, user, i, count, body, parameters, emptyQueryO
53100
53368
  _setSearchArgs(result['driveId'])
53101
53369
  else:
53102
53370
  result = callGAPI(drive.drives(), 'get',
53103
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
53371
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FILE_NOT_FOUND, GAPI.NOT_FOUND],
53104
53372
  driveId=parameters[DFA_SHAREDDRIVE_PARENTID], fields='id')
53105
53373
  parameters[DFA_KWARGS]['corpora'] = 'drive'
53106
53374
  parameters[DFA_KWARGS]['driveId'] = result['id']
@@ -54310,6 +54578,7 @@ DRIVE_CAPABILITIES_SUBFIELDS_CHOICE_MAP = {
54310
54578
  'canaddmydriveparent': 'canAddMyDriveParent',
54311
54579
  'canchangecopyrequireswriterpermission': 'canChangeCopyRequiresWriterPermission',
54312
54580
  'canchangecopyrequireswriterpermissionrestriction': 'canChangeCopyRequiresWriterPermissionRestriction',
54581
+ 'canchangedownloadrestriction': 'canChangeDownloadRestriction',
54313
54582
  'canchangedomainusersonlyrestriction': 'canChangeDomainUsersOnlyRestriction',
54314
54583
  'canchangedrivebackground': 'canChangeDriveBackground',
54315
54584
  'canchangedrivemembersonlyrestriction': 'canChangeDriveMembersOnlyRestriction',
@@ -55326,7 +55595,7 @@ def initFileTree(drive, shareddrive, DLP, shareddriveFields, showParent, user, i
55326
55595
  f_file['parents'] = []
55327
55596
  fileTree[f_file['id']] = {'info': f_file, 'noParents': True, 'children': []}
55328
55597
  name = callGAPI(drive.drives(), 'get',
55329
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
55598
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FILE_NOT_FOUND, GAPI.NOT_FOUND],
55330
55599
  driveId=fileId, fields='name')['name']
55331
55600
  fileTree[f_file['id']]['info']['name'] = f'{SHARED_DRIVES}/{name}'
55332
55601
  else:
@@ -56512,12 +56781,12 @@ def printFileList(users):
56512
56781
  rootFolderId = fileIdEntity['shareddrive']['driveId']
56513
56782
  if not fileIdEntity['shareddrivename']:
56514
56783
  fileIdEntity['shareddrivename'] = callGAPI(drive.drives(), 'get',
56515
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FILE_NOT_FOUND],
56784
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FILE_NOT_FOUND, GAPI.NOT_FOUND],
56516
56785
  driveId=rootFolderId, fields='name')['name']
56517
56786
  rootFolderName = fileIdEntity['shareddrivename']
56518
56787
  if not showParentsIdsAsList and DFF.parentsSubFields['isRoot']:
56519
56788
  DFF.parentsSubFields['rootFolderId'] = rootFolderId
56520
- except (GAPI.notFound, GAPI.fileNotFound) as e:
56789
+ except (GAPI.fileNotFound, GAPI.notFound) as e:
56521
56790
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, fileIdEntity['shareddrive']['driveId']], str(e), i, count)
56522
56791
  continue
56523
56792
  except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
@@ -65379,6 +65648,8 @@ SHAREDDRIVE_RESTRICTIONS_MAP = {
65379
65648
  'allowcontentmanagerstosharefolders': 'sharingFoldersRequiresOrganizerPermission',
65380
65649
  'copyrequireswriterpermission': 'copyRequiresWriterPermission',
65381
65650
  'domainusersonly': 'domainUsersOnly',
65651
+ 'downloadrestrictedforreaders': 'restrictedForReaders',
65652
+ 'downloadrestrictedforwriters': 'restrictedForWriters',
65382
65653
  'drivemembersonly': 'driveMembersOnly',
65383
65654
  'sharingfoldersrequiresorganizerpermission': 'sharingFoldersRequiresOrganizerPermission',
65384
65655
  'teammembersonly': 'driveMembersOnly',
@@ -65387,7 +65658,10 @@ SHAREDDRIVE_RESTRICTIONS_MAP = {
65387
65658
  def _getSharedDriveRestrictions(myarg, body):
65388
65659
  def _setRestriction(restriction):
65389
65660
  body.setdefault('restrictions', {})
65390
- if restriction != 'allowcontentmanagerstosharefolders':
65661
+ if restriction in {'downloadrestrictedforreaders', 'downloadrestrictedforwriters'}:
65662
+ body['restrictions'].setdefault('downloadRestriction', {})
65663
+ body['restrictions']['downloadRestriction'][SHAREDDRIVE_RESTRICTIONS_MAP[restriction]] = getBoolean()
65664
+ elif restriction != 'allowcontentmanagerstosharefolders':
65391
65665
  body['restrictions'][SHAREDDRIVE_RESTRICTIONS_MAP[restriction]] = getBoolean()
65392
65666
  else:
65393
65667
  body['restrictions'][SHAREDDRIVE_RESTRICTIONS_MAP[restriction]] = not getBoolean()
@@ -65540,12 +65814,12 @@ def createSharedDrive(users, useDomainAdminAccess=False):
65540
65814
  while not created:
65541
65815
  try:
65542
65816
  callGAPI(drive.drives(), 'get',
65543
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
65817
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FILE_NOT_FOUND, GAPI.NOT_FOUND],
65544
65818
  useDomainAdminAccess=useDomainAdminAccess,
65545
65819
  driveId=driveId, fields='id')
65546
65820
  created = True
65547
65821
  break
65548
- except GAPI.notFound as e:
65822
+ except (GAPI.fileNotFound, GAPI.notFound) as e:
65549
65823
  retry += 1
65550
65824
  if retry > errorRetries:
65551
65825
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, driveId], str(e), i, count)
@@ -65561,6 +65835,7 @@ def createSharedDrive(users, useDomainAdminAccess=False):
65561
65835
  bailOnInternalError=True,
65562
65836
  throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FORBIDDEN,
65563
65837
  GAPI.NO_MANAGE_TEAMDRIVE_ADMINISTRATOR_PRIVILEGE,
65838
+ GAPI.OUTSIDE_DOMAIN_MEMBER_CANNOT_CHANGE_TEAMDRIVE_RESTRICTIONS,
65564
65839
  GAPI.BAD_REQUEST, GAPI.INTERNAL_ERROR, GAPI.PERMISSION_DENIED,
65565
65840
  GAPI.FILE_NOT_FOUND],
65566
65841
  useDomainAdminAccess=useDomainAdminAccess, driveId=driveId, body=updateBody)
@@ -65580,7 +65855,8 @@ def createSharedDrive(users, useDomainAdminAccess=False):
65580
65855
  if orgUnit:
65581
65856
  waitingForCreationToComplete(moveToOrgUnitDelay)
65582
65857
  ci = _moveSharedDriveToOU(orgUnit, orgUnitId, driveId, user, i, count, ci, returnIdOnly or csvPF)
65583
- except (GAPI.notFound, GAPI.forbidden, GAPI.badRequest, GAPI.noManageTeamDriveAdministratorPrivilege) as e:
65858
+ except (GAPI.notFound, GAPI.forbidden, GAPI.badRequest,
65859
+ GAPI.noManageTeamDriveAdministratorPrivilege, GAPI.outsideDomainMemberCannotChangeTeamDriveRestrictions) as e:
65584
65860
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, driveId], str(e), i, count)
65585
65861
  except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
65586
65862
  userDriveServiceNotEnabledWarning(user, str(e), i, count)
@@ -65635,6 +65911,7 @@ def updateSharedDrive(users, useDomainAdminAccess=False):
65635
65911
  bailOnInternalError=True,
65636
65912
  throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.BAD_REQUEST,
65637
65913
  GAPI.NO_MANAGE_TEAMDRIVE_ADMINISTRATOR_PRIVILEGE,
65914
+ GAPI.OUTSIDE_DOMAIN_MEMBER_CANNOT_CHANGE_TEAMDRIVE_RESTRICTIONS,
65638
65915
  GAPI.INTERNAL_ERROR, GAPI.FILE_NOT_FOUND],
65639
65916
  useDomainAdminAccess=useDomainAdminAccess, driveId=driveId, body=body, fields='name')
65640
65917
  entityActionPerformed([Ent.USER, user, Ent.SHAREDDRIVE_NAME, result['name'], Ent.SHAREDDRIVE_ID, driveId], i, count)
@@ -65652,7 +65929,7 @@ def updateSharedDrive(users, useDomainAdminAccess=False):
65652
65929
  if orgUnit:
65653
65930
  ci = _moveSharedDriveToOU(orgUnit, orgUnitId, driveId, user, i, count, ci, False)
65654
65931
  except (GAPI.notFound, GAPI.forbidden, GAPI.badRequest, GAPI.internalError,
65655
- GAPI.noManageTeamDriveAdministratorPrivilege) as e:
65932
+ GAPI.noManageTeamDriveAdministratorPrivilege, GAPI.outsideDomainMemberCannotChangeTeamDriveRestrictions) as e:
65656
65933
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, driveId], str(e), i, count)
65657
65934
  except GAPI.fileNotFound as e:
65658
65935
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, driveId,
@@ -65781,14 +66058,6 @@ def _getSharedDriveRole(shareddrive):
65781
66058
  return role
65782
66059
 
65783
66060
  def _showSharedDrive(user, shareddrive, j, jcount, FJQC):
65784
- def _showCapabilitiesRestrictions(field):
65785
- if field in shareddrive:
65786
- printKeyValueList([field, ''])
65787
- Ind.Increment()
65788
- for capability in sorted(shareddrive[field]):
65789
- printKeyValueList([capability, shareddrive[field][capability]])
65790
- Ind.Decrement()
65791
-
65792
66061
  if FJQC.formatJSON:
65793
66062
  printLine(json.dumps(cleanJSON(shareddrive, timeObjects=SHAREDDRIVE_TIME_OBJECTS), ensure_ascii=False, sort_keys=True))
65794
66063
  return
@@ -65805,8 +66074,9 @@ def _showSharedDrive(user, shareddrive, j, jcount, FJQC):
65805
66074
  printKeyValueList([setting, shareddrive[setting]])
65806
66075
  if 'role' in shareddrive:
65807
66076
  printKeyValueList(['role', shareddrive['role']])
65808
- _showCapabilitiesRestrictions('capabilities')
65809
- _showCapabilitiesRestrictions('restrictions')
66077
+ for setting in ['capabilities', 'restrictions']:
66078
+ if setting in shareddrive:
66079
+ showJSON(setting, shareddrive[setting])
65810
66080
  Ind.Decrement()
65811
66081
 
65812
66082
  # gam <UserTypeEntity> info shareddrive <SharedDriveEntity>
@@ -65838,14 +66108,14 @@ def infoSharedDrive(users, useDomainAdminAccess=False):
65838
66108
  try:
65839
66109
  driveId = fileIdEntity['shareddrive']['driveId']
65840
66110
  shareddrive = callGAPI(drive.drives(), 'get',
65841
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
66111
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FILE_NOT_FOUND, GAPI.NOT_FOUND],
65842
66112
  useDomainAdminAccess=useDomainAdminAccess,
65843
66113
  driveId=driveId, fields=fields)
65844
66114
  role = _getSharedDriveRole(shareddrive)
65845
66115
  if role:
65846
66116
  shareddrive['role'] = role if not guiRoles else SHAREDDRIVE_API_GUI_ROLES_MAP[role]
65847
66117
  _showSharedDrive(user, shareddrive, i, count, FJQC)
65848
- except GAPI.notFound as e:
66118
+ except (GAPI.fileNotFound, GAPI.notFound) as e:
65849
66119
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, driveId], str(e), i, count)
65850
66120
  except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
65851
66121
  userDriveServiceNotEnabledWarning(user, str(e), i, count)
@@ -65999,11 +66269,13 @@ def printShowSharedDrives(users, useDomainAdminAccess=False):
65999
66269
  throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID,
66000
66270
  GAPI.QUERY_REQUIRES_ADMIN_CREDENTIALS,
66001
66271
  GAPI.NO_LIST_TEAMDRIVES_ADMINISTRATOR_PRIVILEGE,
66272
+ GAPI.INSUFFICIENT_ADMINISTRATOR_PRIVILEGES,
66002
66273
  GAPI.FILE_NOT_FOUND],
66003
66274
  q=query, useDomainAdminAccess=useDomainAdminAccess,
66004
66275
  fields='*', pageSize=100)
66005
66276
  except (GAPI.invalidQuery, GAPI.invalid, GAPI.queryRequiresAdminCredentials,
66006
- GAPI.noListTeamDrivesAdministratorPrivilege, GAPI.fileNotFound) as e:
66277
+ GAPI.noListTeamDrivesAdministratorPrivilege, GAPI.insufficientAdministratorPrivileges,
66278
+ GAPI.fileNotFound) as e:
66007
66279
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE, None], str(e), i, count)
66008
66280
  continue
66009
66281
  except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
@@ -66338,8 +66610,10 @@ def printShowSharedDriveACLs(users, useDomainAdminAccess=False):
66338
66610
  if userdrive is not None:
66339
66611
  try:
66340
66612
  feed = callGAPIpages(userdrive.drives(), 'list', 'drives',
66341
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS,
66613
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID, GAPI.NO_LIST_TEAMDRIVES_ADMINISTRATOR_PRIVILEGE],
66342
66614
  fields='nextPageToken,drives(id,name,createdTime,orgUnitId)', pageSize=100)
66615
+ except (GAPI.invalid, GAPI.noListTeamDrivesAdministratorPrivilege):
66616
+ pass
66343
66617
  except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy):
66344
66618
  pass
66345
66619
  if feed is None:
@@ -66354,10 +66628,12 @@ def printShowSharedDriveACLs(users, useDomainAdminAccess=False):
66354
66628
  pageMessage=pageMessage,
66355
66629
  throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID,
66356
66630
  GAPI.QUERY_REQUIRES_ADMIN_CREDENTIALS,
66357
- GAPI.NO_LIST_TEAMDRIVES_ADMINISTRATOR_PRIVILEGE],
66631
+ GAPI.NO_LIST_TEAMDRIVES_ADMINISTRATOR_PRIVILEGE,
66632
+ GAPI.INSUFFICIENT_ADMINISTRATOR_PRIVILEGES],
66358
66633
  q=query, useDomainAdminAccess=useDomainAdminAccess,
66359
66634
  fields='nextPageToken,drives(id,name,createdTime,orgUnitId)', pageSize=100)
66360
- except (GAPI.invalidQuery, GAPI.invalid, GAPI.queryRequiresAdminCredentials, GAPI.noListTeamDrivesAdministratorPrivilege) as e:
66635
+ except (GAPI.invalidQuery, GAPI.invalid, GAPI.queryRequiresAdminCredentials,
66636
+ GAPI.noListTeamDrivesAdministratorPrivilege, GAPI.insufficientAdministratorPrivileges) as e:
66361
66637
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE, None], str(e), i, count)
66362
66638
  continue
66363
66639
  except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
@@ -66588,11 +66864,11 @@ def printSharedDriveOrganizers(users, useDomainAdminAccess=False):
66588
66864
  throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.INVALID_QUERY, GAPI.INVALID,
66589
66865
  GAPI.QUERY_REQUIRES_ADMIN_CREDENTIALS,
66590
66866
  GAPI.NO_LIST_TEAMDRIVES_ADMINISTRATOR_PRIVILEGE,
66591
- GAPI.FILE_NOT_FOUND],
66867
+ GAPI.INSUFFICIENT_ADMINISTRATOR_PRIVILEGES],
66592
66868
  q=query, useDomainAdminAccess=useDomainAdminAccess,
66593
66869
  fields='nextPageToken,drives(id,name,createdTime,orgUnitId)', pageSize=100)
66594
66870
  except (GAPI.invalidQuery, GAPI.invalid, GAPI.queryRequiresAdminCredentials,
66595
- GAPI.noListTeamDrivesAdministratorPrivilege, GAPI.fileNotFound) as e:
66871
+ GAPI.noListTeamDrivesAdministratorPrivilege, GAPI.insufficientAdministratorPrivileges) as e:
66596
66872
  entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE, None], str(e), i, count)
66597
66873
  continue
66598
66874
  except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
@@ -66606,7 +66882,7 @@ def printSharedDriveOrganizers(users, useDomainAdminAccess=False):
66606
66882
  j +=1
66607
66883
  try:
66608
66884
  feed.append(callGAPI(drive.drives(), 'get',
66609
- throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
66885
+ throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FILE_NOT_FOUND, GAPI.NOT_FOUND],
66610
66886
  useDomainAdminAccess=useDomainAdminAccess,
66611
66887
  driveId=driveId, fields='id,name,createdTime,orgUnitId'))
66612
66888
  except (GAPI.fileNotFound, GAPI.notFound) as e:
@@ -67062,7 +67338,7 @@ def _addUserToGroups(cd, user, addGroupsSet, addGroups, i, count):
67062
67338
  retryReasons=GAPI.MEMBERS_RETRY_REASONS,
67063
67339
  groupKey=group, body=body, fields='')
67064
67340
  entityActionPerformed([Ent.GROUP, group, role, user], j, jcount)
67065
- except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.invalid, GAPI.forbidden):
67341
+ except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.invalid):
67066
67342
  entityUnknownWarning(Ent.GROUP, group, j, jcount)
67067
67343
  except (GAPI.duplicate, GAPI.cyclicMembershipsNotAllowed, GAPI.conditionNotMet, GAPI.serviceNotAvailable) as e:
67068
67344
  entityActionFailedWarning([Ent.GROUP, group, role, user], str(e), j, jcount)
@@ -67070,6 +67346,8 @@ def _addUserToGroups(cd, user, addGroupsSet, addGroups, i, count):
67070
67346
  entityActionPerformedMessage([Ent.GROUP, group, role, user], Msg.ACTION_MAY_BE_DELAYED, j, jcount)
67071
67347
  except (GAPI.memberNotFound, GAPI.resourceNotFound, GAPI.invalidMember) as e:
67072
67348
  entityActionFailedWarning([Ent.USER, user], str(e), i, count)
67349
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
67350
+ ClientAPIAccessDeniedExit(str(e))
67073
67351
  Ind.Decrement()
67074
67352
 
67075
67353
  # gam <UserTypeEntity> add group|groups
@@ -67129,12 +67407,14 @@ def _deleteUserFromGroups(cd, user, deleteGroupsSet, deleteGroups, i, count):
67129
67407
  retryReasons=GAPI.MEMBERS_RETRY_REASONS,
67130
67408
  groupKey=group, memberKey=user)
67131
67409
  entityActionPerformed([Ent.GROUP, group, role, user], j, jcount)
67132
- except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.invalid, GAPI.forbidden):
67410
+ except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.invalid):
67133
67411
  entityUnknownWarning(Ent.GROUP, group, j, jcount)
67134
67412
  except (GAPI.memberNotFound, GAPI.invalidMember, GAPI.conditionNotMet, GAPI.serviceNotAvailable) as e:
67135
67413
  entityActionFailedWarning([Ent.USER, user, Ent.GROUP, group], str(e), j, jcount)
67136
67414
  except GAPI.conflict:
67137
67415
  entityActionPerformedMessage([Ent.GROUP, group, role, user], Msg.ACTION_MAY_BE_DELAYED, j, jcount)
67416
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
67417
+ ClientAPIAccessDeniedExit(str(e))
67138
67418
  Ind.Decrement()
67139
67419
 
67140
67420
  def _getUserGroupOptionalDomainCustomerId():
@@ -67201,8 +67481,10 @@ def deleteUserFromGroups(users):
67201
67481
  except (GAPI.invalidMember, GAPI.invalidInput):
67202
67482
  badRequestWarning(Ent.GROUP, Ent.MEMBER, user)
67203
67483
  continue
67204
- except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
67484
+ except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.badRequest):
67205
67485
  accessErrorExit(cd)
67486
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
67487
+ ClientAPIAccessDeniedExit(str(e))
67206
67488
  deleteGroups = {}
67207
67489
  for group in result:
67208
67490
  if not matchPattern or checkUserGroupMatchPattern(group['email'], matchPattern):
@@ -67231,10 +67513,12 @@ def _updateUserGroups(cd, user, updateGroupsSet, updateGroups, i, count):
67231
67513
  retryReasons=GAPI.MEMBERS_RETRY_REASONS,
67232
67514
  groupKey=group, memberKey=user, body=body, fields='')
67233
67515
  entityActionPerformed([Ent.GROUP, group, role, user], j, jcount)
67234
- except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.invalid, GAPI.forbidden):
67516
+ except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.invalid):
67235
67517
  entityUnknownWarning(Ent.GROUP, group, j, jcount)
67236
67518
  except (GAPI.memberNotFound, GAPI.invalidMember, GAPI.conditionNotMet, GAPI.serviceNotAvailable) as e:
67237
67519
  entityActionFailedWarning([Ent.USER, user, Ent.GROUP, group], str(e), j, jcount)
67520
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
67521
+ ClientAPIAccessDeniedExit(str(e))
67238
67522
  Ind.Decrement()
67239
67523
 
67240
67524
  # gam <UserTypeEntity> update group|groups
@@ -67281,8 +67565,10 @@ def updateUserGroups(users):
67281
67565
  except (GAPI.invalidMember, GAPI.invalidInput):
67282
67566
  badRequestWarning(Ent.GROUP, Ent.MEMBER, user)
67283
67567
  continue
67284
- except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
67568
+ except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.badRequest):
67285
67569
  accessErrorExit(cd)
67570
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
67571
+ ClientAPIAccessDeniedExit(str(e))
67286
67572
  updateGroups = {}
67287
67573
  for group in result:
67288
67574
  updateGroups[group['email']] = {'role': baseRole, 'delivery_settings': baseDeliverySettings}
@@ -67356,8 +67642,10 @@ def syncUserWithGroups(users):
67356
67642
  except (GAPI.invalidMember, GAPI.invalidInput):
67357
67643
  badRequestWarning(Ent.GROUP, Ent.MEMBER, user)
67358
67644
  continue
67359
- except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
67645
+ except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.badRequest):
67360
67646
  accessErrorExit(cd)
67647
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
67648
+ ClientAPIAccessDeniedExit(str(e))
67361
67649
  for groupEntity in entityList:
67362
67650
  groupEmail = groupEntity['email']
67363
67651
  try:
@@ -67476,7 +67764,7 @@ def checkUserInGroups(users):
67476
67764
  retryReasons=GAPI.MEMBERS_RETRY_REASONS,
67477
67765
  groupKey=groupEmail, memberKey=user, fields='role')
67478
67766
  _checkMember(result)
67479
- except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.invalid, GAPI.forbidden):
67767
+ except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.invalid):
67480
67768
  entityUnknownWarning(Ent.GROUP, groupEmail, j, jcount)
67481
67769
  _setCheckError()
67482
67770
  except GAPI.memberNotFound:
@@ -67488,6 +67776,8 @@ def checkUserInGroups(users):
67488
67776
  except (GAPI.invalidMember, GAPI.conditionNotMet, GAPI.serviceNotAvailable) as e:
67489
67777
  entityActionFailedWarning([Ent.USER, user, Ent.GROUP, groupEmail], str(e), j, jcount)
67490
67778
  _setCheckError()
67779
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
67780
+ ClientAPIAccessDeniedExit(str(e))
67491
67781
  else:
67492
67782
  try:
67493
67783
  result = callGAPIpages(cd.members(), 'list', 'members',
@@ -67505,12 +67795,14 @@ def checkUserInGroups(users):
67505
67795
  else:
67506
67796
  csvPF.WriteRow({'user': user, 'group': groupEmail, 'role': notMemberOrRole})
67507
67797
  _setCheckError()
67508
- except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.invalid, GAPI.forbidden):
67798
+ except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.invalid):
67509
67799
  entityUnknownWarning(Ent.GROUP, groupEmail, j, jcount)
67510
67800
  _setCheckError()
67511
67801
  except (GAPI.invalidMember, GAPI.conditionNotMet, GAPI.serviceNotAvailable) as e:
67512
67802
  entityActionFailedWarning([Ent.USER, user, Ent.GROUP, groupEmail], str(e), j, jcount)
67513
67803
  _setCheckError()
67804
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
67805
+ ClientAPIAccessDeniedExit(str(e))
67514
67806
  Ind.Decrement()
67515
67807
  if csvPF:
67516
67808
  csvPF.writeCSVfile('User Check Groups')
@@ -67589,11 +67881,13 @@ def printShowUserGroups(users):
67589
67881
  except (GAPI.invalidMember, GAPI.invalidInput):
67590
67882
  badRequestWarning(Ent.GROUP, Ent.MEMBER, user)
67591
67883
  continue
67592
- except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
67884
+ except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.badRequest):
67593
67885
  if kwargs.get('domain'):
67594
67886
  badRequestWarning(Ent.GROUP, Ent.DOMAIN, kwargs['domain'])
67595
67887
  return
67596
67888
  accessErrorExit(cd)
67889
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
67890
+ ClientAPIAccessDeniedExit(str(e))
67597
67891
  jcount = len(entityList)
67598
67892
  if totalOnly:
67599
67893
  if not csvPF:
@@ -67723,6 +68017,8 @@ def printShowGroupTree(users):
67723
68017
  except (GAPI.invalidMember, GAPI.invalidInput):
67724
68018
  entityUnknownWarning(Ent.USER, user, i, count)
67725
68019
  continue
68020
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
68021
+ ClientAPIAccessDeniedExit(str(e))
67726
68022
  j = 0
67727
68023
  jcount = len(groups)
67728
68024
  if not csvPF and not FJQC.formatJSON:
@@ -67815,11 +68111,13 @@ def printUserGroupsList(users):
67815
68111
  except (GAPI.invalidMember, GAPI.invalidInput):
67816
68112
  badRequestWarning(Ent.GROUP, Ent.MEMBER, user)
67817
68113
  continue
67818
- except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
68114
+ except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.badRequest):
67819
68115
  if kwargs.get('domain'):
67820
68116
  badRequestWarning(Ent.GROUP, Ent.DOMAIN, kwargs['domain'])
67821
68117
  return
67822
68118
  accessErrorExit(cd)
68119
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
68120
+ ClientAPIAccessDeniedExit(str(e))
67823
68121
  csvPF.WriteRow({'User': user, 'Groups': len(entityList), 'GroupsList': delimiter.join([group['email'] for group in entityList])})
67824
68122
  csvPF.writeCSVfile('User GroupsList')
67825
68123
 
@@ -69002,19 +69300,23 @@ def deleteTokens(users):
69002
69300
  try:
69003
69301
  callGAPI(cd.tokens(), 'get',
69004
69302
  throwReasons=[GAPI.USER_NOT_FOUND, GAPI.DOMAIN_NOT_FOUND,
69005
- GAPI.DOMAIN_CANNOT_USE_APIS, GAPI.FORBIDDEN,
69006
- GAPI.NOT_FOUND, GAPI.RESOURCE_NOT_FOUND],
69303
+ GAPI.DOMAIN_CANNOT_USE_APIS,
69304
+ GAPI.NOT_FOUND, GAPI.RESOURCE_NOT_FOUND,
69305
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
69007
69306
  userKey=user, clientId=clientId, fields='')
69008
69307
  callGAPI(cd.tokens(), 'delete',
69009
69308
  throwReasons=[GAPI.USER_NOT_FOUND, GAPI.DOMAIN_NOT_FOUND,
69010
- GAPI.DOMAIN_CANNOT_USE_APIS, GAPI.FORBIDDEN,
69011
- GAPI.NOT_FOUND, GAPI.RESOURCE_NOT_FOUND],
69309
+ GAPI.DOMAIN_CANNOT_USE_APIS,
69310
+ GAPI.NOT_FOUND, GAPI.RESOURCE_NOT_FOUND,
69311
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
69012
69312
  userKey=user, clientId=clientId)
69013
69313
  entityActionPerformed([Ent.USER, user, Ent.ACCESS_TOKEN, clientId], i, count)
69014
69314
  except (GAPI.notFound, GAPI.resourceNotFound) as e:
69015
69315
  entityActionFailedWarning([Ent.USER, user, Ent.ACCESS_TOKEN, clientId], str(e), i, count)
69016
- except (GAPI.userNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden):
69316
+ except (GAPI.userNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis):
69017
69317
  entityUnknownWarning(Ent.USER, user, i, count)
69318
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
69319
+ ClientAPIAccessDeniedExit(str(e))
69018
69320
 
69019
69321
  TOKENS_FIELDS_TITLES = ['clientId', 'displayText', 'anonymous', 'nativeApp', 'userKey', 'scopes']
69020
69322
  TOKENS_AGGREGATE_FIELDS_TITLES = ['clientId', 'displayText', 'anonymous', 'nativeApp', 'users', 'scopes']
@@ -69108,13 +69410,15 @@ def _printShowTokens(entityType, users):
69108
69410
  if clientId:
69109
69411
  results = [callGAPI(cd.tokens(), 'get',
69110
69412
  throwReasons=[GAPI.USER_NOT_FOUND, GAPI.DOMAIN_NOT_FOUND,
69111
- GAPI.DOMAIN_CANNOT_USE_APIS, GAPI.FORBIDDEN, GAPI.BAD_REQUEST,
69112
- GAPI.NOT_FOUND, GAPI.RESOURCE_NOT_FOUND],
69413
+ GAPI.DOMAIN_CANNOT_USE_APIS, GAPI.BAD_REQUEST,
69414
+ GAPI.NOT_FOUND, GAPI.RESOURCE_NOT_FOUND,
69415
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
69113
69416
  userKey=user, clientId=clientId, fields=fields)]
69114
69417
  else:
69115
69418
  results = callGAPIitems(cd.tokens(), 'list', 'items',
69116
69419
  throwReasons=[GAPI.USER_NOT_FOUND, GAPI.DOMAIN_NOT_FOUND,
69117
- GAPI.DOMAIN_CANNOT_USE_APIS, GAPI.FORBIDDEN, GAPI.BAD_REQUEST],
69420
+ GAPI.DOMAIN_CANNOT_USE_APIS, GAPI.BAD_REQUEST,
69421
+ GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
69118
69422
  userKey=user, fields=f'items({fields})')
69119
69423
  jcount = len(results)
69120
69424
  if not aggregateUsersBy:
@@ -69154,8 +69458,10 @@ def _printShowTokens(entityType, users):
69154
69458
  aggregateTokensById[user] = jcount
69155
69459
  except (GAPI.notFound, GAPI.resourceNotFound) as e:
69156
69460
  entityActionFailedWarning([Ent.USER, user, Ent.ACCESS_TOKEN, clientId], str(e), i, count)
69157
- except (GAPI.userNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden, GAPI.badRequest):
69461
+ except (GAPI.userNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.badRequest):
69158
69462
  entityUnknownWarning(Ent.USER, user, i, count)
69463
+ except (GAPI.forbidden, GAPI.permissionDenied) as e:
69464
+ ClientAPIAccessDeniedExit(str(e))
69159
69465
  if aggregateUsersBy == 'clientId':
69160
69466
  if not csvPF:
69161
69467
  jcount = len(aggregateTokensById)
@@ -76370,6 +76676,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
76370
76676
  Cmd.ARG_USERINVITATION: doCheckCIUserInvitations,
76371
76677
  Cmd.ARG_ISINVITABLE: doCheckCIUserInvitations,
76372
76678
  Cmd.ARG_ORG: doCheckOrgUnit,
76679
+ Cmd.ARG_SUSPENDED: doCheckUserSuspended,
76373
76680
  }
76374
76681
  ),
76375
76682
  'clear':
@@ -77165,6 +77472,7 @@ COURSE_SUBCOMMANDS = {
77165
77472
  'add': (Act.ADD, doCourseAddItems),
77166
77473
  'clear': (Act.REMOVE, doCourseClearParticipants),
77167
77474
  'remove': (Act.REMOVE, doCourseRemoveItems),
77475
+ 'update': (Act.UPDATE, doCourseUpdateItems),
77168
77476
  'sync': (Act.SYNC, doCourseSyncParticipants),
77169
77477
  }
77170
77478