|
|
|
|
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
|
|
|
|
__version__ = '7.03.00'
|
|
|
|
|
__version__ = '7.03.03'
|
|
|
|
|
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
|
|
|
|
|
|
|
|
|
#pylint: disable=wrong-import-position
|
|
|
|
|
@@ -11361,13 +11361,32 @@ def doEnableAPIs():
|
|
|
|
|
url = f'https://console.cloud.google.com/apis/enableflow?apiid={apiid}&project={projectId}'
|
|
|
|
|
writeStdout(f' {url}\n\n')
|
|
|
|
|
|
|
|
|
|
def _waitForSvcAcctCompletion(i):
|
|
|
|
|
sleep_time = i*5
|
|
|
|
|
if i > 3:
|
|
|
|
|
sys.stdout.write(Msg.WAITING_FOR_ITEM_CREATION_TO_COMPLETE_SLEEPING.format(Ent.Singular(Ent.SVCACCT), sleep_time))
|
|
|
|
|
time.sleep(sleep_time)
|
|
|
|
|
|
|
|
|
|
def _grantRotateRights(iam, projectId, service_account, email, account_type='serviceAccount'):
|
|
|
|
|
printEntityMessage([Ent.PROJECT, projectId, Ent.SVCACCT, email],
|
|
|
|
|
Msg.HAS_RIGHTS_TO_ROTATE_OWN_PRIVATE_KEY.format(email, service_account))
|
|
|
|
|
body = {'policy': {'bindings': [{'role': 'roles/iam.serviceAccountKeyAdmin',
|
|
|
|
|
'members': [f'{account_type}:{email}']}]}}
|
|
|
|
|
callGAPI(iam.projects().serviceAccounts(), 'setIamPolicy',
|
|
|
|
|
resource=f'projects/{projectId}/serviceAccounts/{service_account}', body=body)
|
|
|
|
|
maxRetries = 10
|
|
|
|
|
printEntityMessage([Ent.PROJECT, projectId, Ent.SVCACCT, email],
|
|
|
|
|
Msg.HAS_RIGHTS_TO_ROTATE_OWN_PRIVATE_KEY.format(email, service_account))
|
|
|
|
|
for retry in range(1, maxRetries+1):
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(iam.projects().serviceAccounts(), 'setIamPolicy',
|
|
|
|
|
throwReasons=[GAPI.INVALID_ARGUMENT],
|
|
|
|
|
resource=f'projects/{projectId}/serviceAccounts/{service_account}', body=body)
|
|
|
|
|
return True
|
|
|
|
|
except GAPI.invalidArgument as e:
|
|
|
|
|
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, service_account], str(e))
|
|
|
|
|
if 'does not exist' not in str(e) or retry == maxRetries:
|
|
|
|
|
return False
|
|
|
|
|
_waitForSvcAcctCompletion(retry)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, service_account], str(e))
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def _createOauth2serviceJSON(httpObj, projectInfo, svcAcctInfo, create_key=True):
|
|
|
|
|
iam = getAPIService(API.IAM, httpObj)
|
|
|
|
|
@@ -11392,8 +11411,7 @@ def _createOauth2serviceJSON(httpObj, projectInfo, svcAcctInfo, create_key=True)
|
|
|
|
|
clientId=service_account['uniqueId']):
|
|
|
|
|
return False
|
|
|
|
|
sa_email = service_account['name'].rsplit('/', 1)[-1]
|
|
|
|
|
_grantRotateRights(iam, projectInfo['projectId'], sa_email, sa_email)
|
|
|
|
|
return True
|
|
|
|
|
return _grantRotateRights(iam, projectInfo['projectId'], sa_email, sa_email)
|
|
|
|
|
|
|
|
|
|
def _createClientSecretsOauth2service(httpObj, login_hint, appInfo, projectInfo, svcAcctInfo, create_key=True):
|
|
|
|
|
def _checkClientAndSecret(csHttpObj, client_id, client_secret):
|
|
|
|
|
@@ -12563,12 +12581,6 @@ def doProcessSvcAcctKeys(mode=None, iam=None, projectId=None, clientEmail=None,
|
|
|
|
|
else:
|
|
|
|
|
unknownArgumentExit()
|
|
|
|
|
|
|
|
|
|
def waitForCompletion(i):
|
|
|
|
|
sleep_time = i*5
|
|
|
|
|
if i > 3:
|
|
|
|
|
sys.stdout.write(Msg.WAITING_FOR_ITEM_CREATION_TO_COMPLETE_SLEEPING.format(Ent.Singular(Ent.SVCACCT), sleep_time))
|
|
|
|
|
time.sleep(sleep_time)
|
|
|
|
|
|
|
|
|
|
local_key_size = 2048
|
|
|
|
|
validityHours = 0
|
|
|
|
|
body = {}
|
|
|
|
|
@@ -12638,12 +12650,12 @@ def doProcessSvcAcctKeys(mode=None, iam=None, projectId=None, clientEmail=None,
|
|
|
|
|
if retry == maxRetries:
|
|
|
|
|
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], str(e))
|
|
|
|
|
return False
|
|
|
|
|
waitForCompletion(retry)
|
|
|
|
|
_waitForSvcAcctCompletion(retry)
|
|
|
|
|
except GAPI.permissionDenied:
|
|
|
|
|
if retry == maxRetries:
|
|
|
|
|
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], Msg.UPDATE_PROJECT_TO_VIEW_MANAGE_SAKEYS)
|
|
|
|
|
return False
|
|
|
|
|
waitForCompletion(retry)
|
|
|
|
|
_waitForSvcAcctCompletion(retry)
|
|
|
|
|
except GAPI.badRequest as e:
|
|
|
|
|
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], str(e))
|
|
|
|
|
return False
|
|
|
|
|
@@ -12656,7 +12668,7 @@ def doProcessSvcAcctKeys(mode=None, iam=None, projectId=None, clientEmail=None,
|
|
|
|
|
new_data['private_key'] = ''
|
|
|
|
|
newPrivateKeyId = ''
|
|
|
|
|
break
|
|
|
|
|
waitForCompletion(retry)
|
|
|
|
|
_waitForSvcAcctCompletion(retry)
|
|
|
|
|
new_data['private_key_id'] = newPrivateKeyId
|
|
|
|
|
oauth2service_data = _formatOAuth2ServiceData(new_data)
|
|
|
|
|
else:
|
|
|
|
|
@@ -12673,7 +12685,7 @@ def doProcessSvcAcctKeys(mode=None, iam=None, projectId=None, clientEmail=None,
|
|
|
|
|
if retry == maxRetries:
|
|
|
|
|
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], Msg.UPDATE_PROJECT_TO_VIEW_MANAGE_SAKEYS)
|
|
|
|
|
return False
|
|
|
|
|
waitForCompletion(retry)
|
|
|
|
|
_waitForSvcAcctCompletion(retry)
|
|
|
|
|
except GAPI.badRequest as e:
|
|
|
|
|
entityActionFailedWarning([Ent.PROJECT, projectId, Ent.SVCACCT, clientEmail], str(e))
|
|
|
|
|
return False
|
|
|
|
|
@@ -12714,7 +12726,7 @@ def doProcessSvcAcctKeys(mode=None, iam=None, projectId=None, clientEmail=None,
|
|
|
|
|
if retry == maxRetries:
|
|
|
|
|
entityActionFailedWarning([Ent.SVCACCT_KEY, keyName], Msg.UPDATE_PROJECT_TO_VIEW_MANAGE_SAKEYS)
|
|
|
|
|
break
|
|
|
|
|
waitForCompletion(retry)
|
|
|
|
|
_waitForSvcAcctCompletion(retry)
|
|
|
|
|
except GAPI.badRequest as e:
|
|
|
|
|
entityActionFailedWarning([Ent.SVCACCT_KEY, keyName], str(e), i, count)
|
|
|
|
|
break
|
|
|
|
|
@@ -15098,11 +15110,11 @@ def doCreateResoldCustomer():
|
|
|
|
|
def doUpdateResoldCustomer():
|
|
|
|
|
res = buildGAPIObject(API.RESELLER)
|
|
|
|
|
customerId = getString(Cmd.OB_CUSTOMER_ID)
|
|
|
|
|
customerAuthToken, body = _getResoldCustomerAttr()
|
|
|
|
|
_, body = _getResoldCustomerAttr()
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(res.customers(), 'patch',
|
|
|
|
|
throwReasons=GAPI.RESELLER_THROW_REASONS,
|
|
|
|
|
customerId=customerId, body=body, customerAuthToken=customerAuthToken, fields='')
|
|
|
|
|
customerId=customerId, body=body, fields='')
|
|
|
|
|
entityActionPerformed([Ent.CUSTOMER_ID, customerId])
|
|
|
|
|
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden, GAPI.invalid) as e:
|
|
|
|
|
entityActionFailedWarning([Ent.CUSTOMER_ID, customerId], str(e))
|
|
|
|
|
@@ -31719,6 +31731,8 @@ def doCreateGroup(ciGroupsAPI=False):
|
|
|
|
|
'query': getString(Cmd.OB_QUERY)})
|
|
|
|
|
elif ciGroupsAPI and myarg == 'makeowner':
|
|
|
|
|
initialGroupConfig = 'WITH_INITIAL_OWNER'
|
|
|
|
|
elif ciGroupsAPI and myarg == 'security':
|
|
|
|
|
body['labels'][CIGROUP_SECURITY_LABEL] = ''
|
|
|
|
|
elif myarg == 'verifynotinvitable':
|
|
|
|
|
verifyNotInvitable = True
|
|
|
|
|
else:
|
|
|
|
|
@@ -34597,6 +34611,7 @@ def doPrintShowGroupTree():
|
|
|
|
|
|
|
|
|
|
# gam create cigroup <EmailAddress> [copyfrom <GroupItem>] <GroupAttribute>
|
|
|
|
|
# [makeowner] [alias|aliases <CIGroupAliasList>] [dynamic <QueryDynamicGroup>]
|
|
|
|
|
# [security]
|
|
|
|
|
def doCreateCIGroup():
|
|
|
|
|
doCreateGroup(ciGroupsAPI=True)
|
|
|
|
|
|
|
|
|
|
@@ -37431,7 +37446,7 @@ def _doDeleteResourceCalendars(entityList):
|
|
|
|
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
|
|
|
customer=GC.Values[GC.CUSTOMER_ID], calendarResourceId=resourceId)
|
|
|
|
|
entityActionPerformed([Ent.RESOURCE_CALENDAR, resourceId], i, count)
|
|
|
|
|
except GAPI.serviceNotAvailable as e:
|
|
|
|
|
except GAPI.serviceNotAvailable as e:
|
|
|
|
|
entityActionFailedWarning([Ent.RESOURCE_CALENDAR, resourceId], str(e), i, count)
|
|
|
|
|
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
|
|
|
|
|
checkEntityAFDNEorAccessErrorExit(cd, Ent.RESOURCE_CALENDAR, resourceId, i, count)
|
|
|
|
|
@@ -39150,10 +39165,12 @@ def _wipeCalendarEvents(user, origCal, calIds, count):
|
|
|
|
|
continue
|
|
|
|
|
try:
|
|
|
|
|
callGAPI(cal.calendars(), 'clear',
|
|
|
|
|
throwReasons=GAPI.CALENDAR_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID, GAPI.REQUIRED_ACCESS_LEVEL],
|
|
|
|
|
throwReasons=GAPI.CALENDAR_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID,
|
|
|
|
|
GAPI.REQUIRED_ACCESS_LEVEL, GAPI.SERVICE_NOT_AVAILABLE],
|
|
|
|
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
|
|
|
|
calendarId=calId)
|
|
|
|
|
entityActionPerformed([Ent.CALENDAR, calId], i, count)
|
|
|
|
|
except (GAPI.notFound, GAPI.forbidden, GAPI.invalid, GAPI.requiredAccessLevel) as e:
|
|
|
|
|
except (GAPI.notFound, GAPI.forbidden, GAPI.invalid, GAPI.requiredAccessLevel, GAPI.serviceNotAvailable) as e:
|
|
|
|
|
entityActionFailedWarning([Ent.CALENDAR, calId], str(e), i, count)
|
|
|
|
|
except GAPI.notACalendarUser:
|
|
|
|
|
userCalServiceNotEnabledWarning(calId, i, count)
|
|
|
|
|
@@ -69387,13 +69404,18 @@ LABEL_COUNTS_FIELDS = ','.join(LABEL_COUNTS_FIELDS_LIST)
|
|
|
|
|
def printShowLabels(users):
|
|
|
|
|
def _buildLabelTree(labels):
|
|
|
|
|
def _checkChildLabel(label):
|
|
|
|
|
if label.find('/') != -1:
|
|
|
|
|
(parent, base) = label.rsplit('/', 1)
|
|
|
|
|
labelItemList = label.split('/')
|
|
|
|
|
i = len(labelItemList)-1
|
|
|
|
|
while i > 0:
|
|
|
|
|
parent = '/'.join(labelItemList[:i])
|
|
|
|
|
base = '/'.join(labelItemList[i:])
|
|
|
|
|
if parent in labelTree:
|
|
|
|
|
if label in labelTree:
|
|
|
|
|
labelTree[label]['info']['base'] = base
|
|
|
|
|
labelTree[parent]['children'].append(labelTree.pop(label))
|
|
|
|
|
_checkChildLabel(parent)
|
|
|
|
|
return
|
|
|
|
|
i -= 1
|
|
|
|
|
|
|
|
|
|
labelTree = {}
|
|
|
|
|
for label in labels['labels']:
|
|
|
|
|
|