mirror of
https://github.com/GAM-team/GAM.git
synced 2025-05-12 12:17:20 +00:00
Extend project commands (#841)
gam create project [<EmailAddress>] [<ProjectID>] gam use project [<EmailAddress>] [<ProjectID>] gam update project [<EmailAddress>] [gam|<ProjectID>|(filter <String>)] gam delete project [<EmailAddress>] [gam|<ProjectID>|(filter <String>)] gam show projects [<EmailAddress>] [all|gam|<ProjectID>|(filter <String>)] gam print projects [<EmailAddress>] [all|gam|<ProjectID>|(filter <String>)] [todrive]
This commit is contained in:
parent
1cbe8297aa
commit
207eb0990c
@ -121,6 +121,7 @@ If an item contains spaces, it should be surrounded by ".
|
|||||||
<Year>-<Month>-<Day>(<Space>|T)<Hour>:<Minute>:<Second>[.<MilliSeconds>](Z|(+|-(<Hour>:<Minute>))) |
|
<Year>-<Month>-<Day>(<Space>|T)<Hour>:<Minute>:<Second>[.<MilliSeconds>](Z|(+|-(<Hour>:<Minute>))) |
|
||||||
(+|-)<Number>(m|h|d|w)
|
(+|-)<Number>(m|h|d|w)
|
||||||
<RegularExpression> ::= <Python Regular Expression, see: https://docs.python.org/2/library/re.html>
|
<RegularExpression> ::= <Python Regular Expression, see: https://docs.python.org/2/library/re.html>
|
||||||
|
<ProjectID> ::= <String> # Must match this Python Regular Expression: [a-z][a-z0-9-]{4,28}[a-z0-9]
|
||||||
<Tag> ::= <String>
|
<Tag> ::= <String>
|
||||||
<UniqueID> ::= uid:<String>
|
<UniqueID> ::= uid:<String>
|
||||||
|
|
||||||
@ -774,8 +775,12 @@ An argument containing instances of ~~xxx~~ has xxx replaced by the value of fie
|
|||||||
Example: gam csv Users.csv gam update user "~primaryEmail" address type work unstructured "~~Street~~, ~~City~~, ~~State~~ ~~ZIP~~"
|
Example: gam csv Users.csv gam update user "~primaryEmail" address type work unstructured "~~Street~~, ~~City~~, ~~State~~ ~~ZIP~~"
|
||||||
Each user (~primaryEmail, e.g. foo@bar.com) would have their work address updated
|
Each user (~primaryEmail, e.g. foo@bar.com) would have their work address updated
|
||||||
|
|
||||||
gam create project [<EmailAddress>]
|
gam create project [<EmailAddress>] [<ProjectID>]
|
||||||
gam update project [<EmailAddress>]
|
gam use project [<EmailAddress>] [<ProjectID>]
|
||||||
|
gam update project [<EmailAddress>] [gam|<ProjectID>|(filter <String>)]
|
||||||
|
gam delete project [<EmailAddress>] [gam|<ProjectID>|(filter <String>)]
|
||||||
|
gam show projects [<EmailAddress>] [all|gam|<ProjectID>|(filter <String>)]
|
||||||
|
gam print projects [<EmailAddress>] [all|gam|<ProjectID>|(filter <String>)] [todrive]
|
||||||
|
|
||||||
gam oauth|oauth2 create|request [<EmailAddress>]
|
gam oauth|oauth2 create|request [<EmailAddress>]
|
||||||
gam oauth|oauth2 delete|revoke
|
gam oauth|oauth2 delete|revoke
|
||||||
|
477
src/gam.py
477
src/gam.py
@ -817,6 +817,8 @@ class GAPI_notFound(Exception):
|
|||||||
pass
|
pass
|
||||||
class GAPI_notImplemented(Exception):
|
class GAPI_notImplemented(Exception):
|
||||||
pass
|
pass
|
||||||
|
class GAPI_permissionDenied(Exception):
|
||||||
|
pass
|
||||||
class GAPI_resourceNotFound(Exception):
|
class GAPI_resourceNotFound(Exception):
|
||||||
pass
|
pass
|
||||||
class GAPI_serviceNotAvailable(Exception):
|
class GAPI_serviceNotAvailable(Exception):
|
||||||
@ -842,6 +844,7 @@ GAPI_REASON_EXCEPTION_MAP = {
|
|||||||
GAPI_MEMBER_NOT_FOUND: GAPI_memberNotFound,
|
GAPI_MEMBER_NOT_FOUND: GAPI_memberNotFound,
|
||||||
GAPI_NOT_FOUND: GAPI_notFound,
|
GAPI_NOT_FOUND: GAPI_notFound,
|
||||||
GAPI_NOT_IMPLEMENTED: GAPI_notImplemented,
|
GAPI_NOT_IMPLEMENTED: GAPI_notImplemented,
|
||||||
|
GAPI_PERMISSION_DENIED: GAPI_permissionDenied,
|
||||||
GAPI_RESOURCE_NOT_FOUND: GAPI_resourceNotFound,
|
GAPI_RESOURCE_NOT_FOUND: GAPI_resourceNotFound,
|
||||||
GAPI_SERVICE_NOT_AVAILABLE: GAPI_serviceNotAvailable,
|
GAPI_SERVICE_NOT_AVAILABLE: GAPI_serviceNotAvailable,
|
||||||
GAPI_USER_NOT_FOUND: GAPI_userNotFound,
|
GAPI_USER_NOT_FOUND: GAPI_userNotFound,
|
||||||
@ -7113,19 +7116,6 @@ def getUserAttributes(i, cd, updateCmd):
|
|||||||
body[u'hashFunction'] = u'crypt'
|
body[u'hashFunction'] = u'crypt'
|
||||||
return body
|
return body
|
||||||
|
|
||||||
VALIDEMAIL_PATTERN = re.compile(r'^[^@]+@[^@]+\.[^@]+$')
|
|
||||||
|
|
||||||
def getValidateLoginHint(login_hint):
|
|
||||||
if login_hint:
|
|
||||||
login_hint = login_hint.strip()
|
|
||||||
if VALIDEMAIL_PATTERN.match(login_hint):
|
|
||||||
return login_hint
|
|
||||||
while True:
|
|
||||||
login_hint = raw_input(u'\nWhat is your G Suite admin email address? ').strip()
|
|
||||||
if VALIDEMAIL_PATTERN.match(login_hint):
|
|
||||||
return login_hint
|
|
||||||
print u'Error: that is not a valid email address'
|
|
||||||
|
|
||||||
def getCRMService(login_hint):
|
def getCRMService(login_hint):
|
||||||
scope = u'https://www.googleapis.com/auth/cloud-platform'
|
scope = u'https://www.googleapis.com/auth/cloud-platform'
|
||||||
client_id = u'297408095146-fug707qsjv4ikron0hugpevbrjhkmsk7.apps.googleusercontent.com'
|
client_id = u'297408095146-fug707qsjv4ikron0hugpevbrjhkmsk7.apps.googleusercontent.com'
|
||||||
@ -7142,69 +7132,63 @@ def getCRMService(login_hint):
|
|||||||
except httplib2.CertificateValidationUnsupported:
|
except httplib2.CertificateValidationUnsupported:
|
||||||
noPythonSSLExit()
|
noPythonSSLExit()
|
||||||
credentials.user_agent = GAM_INFO
|
credentials.user_agent = GAM_INFO
|
||||||
http = credentials.authorize(httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL],
|
http = credentials.authorize(httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL], cache=None))
|
||||||
cache=None))
|
|
||||||
return (googleapiclient.discovery.build(u'cloudresourcemanager', u'v1', http=http, cache_discovery=False), http)
|
return (googleapiclient.discovery.build(u'cloudresourcemanager', u'v1', http=http, cache_discovery=False), http)
|
||||||
|
|
||||||
def doDelProjects(login_hint=None):
|
def getGAMProjectAPIs():
|
||||||
# Leave undocumented. Most users should never need.
|
httpObj = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL])
|
||||||
# Deletes all projects with ID gam-project-*
|
_, c = httpObj.request(GAM_PROJECT_APIS, u'GET')
|
||||||
login_hint = getValidateLoginHint(login_hint)
|
return httpObj, c.splitlines()
|
||||||
crm, _ = getCRMService(login_hint)
|
|
||||||
projects = callGAPIpages(crm.projects(), u'list', u'projects')
|
|
||||||
for project in projects:
|
|
||||||
pid = project[u'projectId']
|
|
||||||
if pid.startswith(u'gam-project-'):
|
|
||||||
print u'Deleting %s...' % pid
|
|
||||||
try:
|
|
||||||
callGAPI(crm.projects(), u'delete', throw_reasons=[GAPI_FORBIDDEN], projectId=pid)
|
|
||||||
except GAPI_forbidden:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def enableProjectAPIs(simplehttp, httpObj, project_name, checkEnabled):
|
def enableGAMProjectAPIs(GAMProjectAPIs, httpObj, projectId, checkEnabled, i=0, count=0):
|
||||||
_, c = simplehttp.request(GAM_PROJECT_APIS, u'GET')
|
apis = GAMProjectAPIs[:]
|
||||||
apis = c.splitlines()
|
project_name = u'project:{0}'.format(projectId)
|
||||||
serveman = googleapiclient.discovery.build(u'servicemanagement', u'v1', http=httpObj, cache_discovery=False)
|
serveman = googleapiclient.discovery.build(u'servicemanagement', u'v1', http=httpObj, cache_discovery=False)
|
||||||
|
status = True
|
||||||
if checkEnabled:
|
if checkEnabled:
|
||||||
enabledServices = callGAPIpages(serveman.services(), u'list', u'services',
|
try:
|
||||||
consumerId=project_name, fields=u'nextPageToken,services(serviceName)')
|
services = callGAPIpages(serveman.services(), u'list', u'services',
|
||||||
for enabled in enabledServices:
|
throw_reasons=[GAPI_NOT_FOUND],
|
||||||
if u'serviceName' in enabled:
|
consumerId=project_name, fields=u'nextPageToken,services(serviceName)')
|
||||||
if enabled[u'serviceName'] in apis:
|
jcount = len(services)
|
||||||
print u' API %s already enabled...' % enabled[u'serviceName']
|
print u' Project: {0}, Check {1} APIs{2}'.format(projectId, jcount, currentCount(i, count))
|
||||||
apis.remove(enabled[u'serviceName'])
|
j = 0
|
||||||
else:
|
for service in sorted(services, key=lambda k: k[u'serviceName']):
|
||||||
print u' API %s (non-GAM) is enabled (which is fine)' % enabled[u'serviceName']
|
j += 1
|
||||||
for api in apis:
|
if u'serviceName' in service:
|
||||||
while True:
|
if service[u'serviceName'] in apis:
|
||||||
print u' enabling API %s...' % api
|
print u' API: {0}, Already enabled{1}'.format(service[u'serviceName'], currentCount(j, jcount))
|
||||||
try:
|
apis.remove(service[u'serviceName'])
|
||||||
callGAPI(serveman.services(), u'enable',
|
else:
|
||||||
throw_reasons=[GAPI_FAILED_PRECONDITION],
|
print u' API: {0}, Already enabled (non-GAM which is fine){1}'.format(service[u'serviceName'], currentCount(j, jcount))
|
||||||
serviceName=api, body={u'consumerId': project_name})
|
except GAPI_notFound as e:
|
||||||
break
|
print u' Project: {0}, Update Failed: {1}{2}'.format(projectId, str(e), currentCount(i, count))
|
||||||
except GAPI_failedPrecondition as e:
|
status = False
|
||||||
print u'\nThere was an error enabling %s. Please resolve error as described below:' % api
|
jcount = len(apis)
|
||||||
print
|
if status and jcount > 0:
|
||||||
print u'\n%s\n' % e
|
print u' Project: {0}, Enable {1} APIs{2}'.format(projectId, jcount, currentCount(i, count))
|
||||||
print
|
j = 0
|
||||||
raw_input(u'Press enter once resolved and we will try enabling the API again.')
|
for api in apis:
|
||||||
|
j += 1
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
callGAPI(serveman.services(), u'enable',
|
||||||
|
throw_reasons=[GAPI_FAILED_PRECONDITION, GAPI_FORBIDDEN, GAPI_PERMISSION_DENIED],
|
||||||
|
serviceName=api, body={u'consumerId': project_name})
|
||||||
|
print u' API: {0}, Enabled{1}'.format(api, currentCount(j, jcount))
|
||||||
|
break
|
||||||
|
except GAPI_failedPrecondition as e:
|
||||||
|
print u'\nThere was an error enabling %s. Please resolve error as described below:' % api
|
||||||
|
print
|
||||||
|
print u'\n%s\n' % e
|
||||||
|
print
|
||||||
|
raw_input(u'Press enter once resolved and we will try enabling the API again.')
|
||||||
|
except (GAPI_forbidden, GAPI_permissionDenied) as e:
|
||||||
|
print u' API: {0}, Enable Failed: {1}{2}'.format(api, str(e), currentCount(j, jcount))
|
||||||
|
status = False
|
||||||
|
return status
|
||||||
|
|
||||||
def doUpdateProject(login_hint=None):
|
def _createClientSecretsOauth2service(httpObj, projectId):
|
||||||
login_hint = getValidateLoginHint(login_hint)
|
|
||||||
_, httpObj = getCRMService(login_hint)
|
|
||||||
cs_data = readFile(GC_Values[GC_CLIENT_SECRETS_JSON], mode=u'rb', continueOnError=True, displayError=True, encoding=None)
|
|
||||||
if not cs_data:
|
|
||||||
systemErrorExit(14, u'Your client secrets file:\n\n%s\n\nis missing. Please recreate the file.' % GC_Values[GC_CLIENT_SECRETS_JSON])
|
|
||||||
try:
|
|
||||||
cs_json = json.loads(cs_data)
|
|
||||||
project_name = 'project:%s' % cs_json[u'installed'][u'project_id']
|
|
||||||
except (ValueError, IndexError, KeyError):
|
|
||||||
systemErrorExit(3, u'The format of your client secrets file:\n\n%s\n\nis incorrect. Please recreate the file.' % GC_Values[GC_CLIENT_SECRETS_JSON])
|
|
||||||
simplehttp = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL])
|
|
||||||
enableProjectAPIs(simplehttp, httpObj, project_name, True)
|
|
||||||
|
|
||||||
def doCreateProject(login_hint=None):
|
|
||||||
|
|
||||||
def _checkClientAndSecret(simplehttp, client_id, client_secret):
|
def _checkClientAndSecret(simplehttp, client_id, client_secret):
|
||||||
url = u'https://www.googleapis.com/oauth2/v4/token'
|
url = u'https://www.googleapis.com/oauth2/v4/token'
|
||||||
@ -7232,24 +7216,189 @@ def doCreateProject(login_hint=None):
|
|||||||
print u'Unknown error: %s' % content
|
print u'Unknown error: %s' % content
|
||||||
return False
|
return False
|
||||||
|
|
||||||
service_account_file = GC_Values[GC_OAUTH2SERVICE_JSON]
|
simplehttp, GAMProjectAPIs = getGAMProjectAPIs()
|
||||||
client_secrets_file = GC_Values[GC_CLIENT_SECRETS_JSON]
|
enableGAMProjectAPIs(GAMProjectAPIs, httpObj, projectId, False)
|
||||||
for a_file in [service_account_file, client_secrets_file]:
|
iam = googleapiclient.discovery.build(u'iam', u'v1', http=httpObj, cache_discovery=False)
|
||||||
if os.path.exists(a_file):
|
sa_list = callGAPI(iam.projects().serviceAccounts(), u'list',
|
||||||
systemErrorExit(5, '%s already exists. Please delete or rename it before attempting to create another project.' % a_file)
|
name=u'projects/%s' % projectId)
|
||||||
login_hint = getValidateLoginHint(login_hint)
|
service_account = None
|
||||||
login_domain = login_hint[login_hint.find(u'@')+1:]
|
if u'accounts' in sa_list:
|
||||||
|
for account in sa_list[u'accounts']:
|
||||||
|
sa_email = u'%s@%s.iam.gserviceaccount.com' % (projectId, projectId)
|
||||||
|
if sa_email in account[u'name']:
|
||||||
|
service_account = account
|
||||||
|
break
|
||||||
|
if not service_account:
|
||||||
|
print u'Creating Service Account'
|
||||||
|
service_account = callGAPI(iam.projects().serviceAccounts(), u'create',
|
||||||
|
name=u'projects/%s' % projectId,
|
||||||
|
body={u'accountId': projectId, u'serviceAccount': {u'displayName': u'GAM Project'}})
|
||||||
|
key = callGAPI(iam.projects().serviceAccounts().keys(), u'create',
|
||||||
|
name=service_account[u'name'], body={u'privateKeyType': u'TYPE_GOOGLE_CREDENTIALS_FILE', u'keyAlgorithm': u'KEY_ALG_RSA_2048'})
|
||||||
|
oauth2service_data = base64.b64decode(key[u'privateKeyData'])
|
||||||
|
writeFile(GC_Values[GC_OAUTH2SERVICE_JSON], oauth2service_data, continueOnError=False)
|
||||||
|
console_credentials_url = u'https://console.developers.google.com/apis/credentials?project=%s' % projectId
|
||||||
|
while True:
|
||||||
|
print u'''Please go to:
|
||||||
|
|
||||||
|
%s
|
||||||
|
|
||||||
|
1. Click the blue "Create credentials" button. Choose "OAuth client ID".
|
||||||
|
2. Click the blue "Configure consent screen" button. Enter "GAM" for "Application name".
|
||||||
|
3. Leave other fields blank. Click "Save" button.
|
||||||
|
3. Choose "Other". Enter a desired value for "Name". Click the blue "Create" button.
|
||||||
|
4. Copy your "client ID" value.
|
||||||
|
|
||||||
|
''' % console_credentials_url
|
||||||
|
# If you use Firefox to copy the Client ID and Secret, the data has leading and trailing newlines
|
||||||
|
# The first raw_input will get the leading newline, thus we have to issue another raw_input to get the data
|
||||||
|
# If the newlines are not present, the data is correctly read with the first raw_input
|
||||||
|
client_id = raw_input(u'Enter your Client ID: ').strip()
|
||||||
|
if not client_id:
|
||||||
|
client_id = raw_input().strip()
|
||||||
|
print u'\nNow go back to your browser and copy your client secret.'
|
||||||
|
client_secret = raw_input(u'Enter your Client Secret: ').strip()
|
||||||
|
if not client_secret:
|
||||||
|
client_secret = raw_input().strip()
|
||||||
|
client_valid = _checkClientAndSecret(simplehttp, client_id, client_secret)
|
||||||
|
if client_valid:
|
||||||
|
break
|
||||||
|
print
|
||||||
|
cs_data = u'''{
|
||||||
|
"installed": {
|
||||||
|
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||||
|
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
||||||
|
"client_id": "%s",
|
||||||
|
"client_secret": "%s",
|
||||||
|
"project_id": "%s",
|
||||||
|
"redirect_uris": [
|
||||||
|
"urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
"http://localhost"
|
||||||
|
` ],
|
||||||
|
"token_uri": "https://accounts.google.com/o/oauth2/token"
|
||||||
|
}
|
||||||
|
}''' % (client_id, client_secret, projectId)
|
||||||
|
writeFile(GC_Values[GC_CLIENT_SECRETS_JSON], cs_data, continueOnError=False)
|
||||||
|
print u'''Almost there! Now please switch back to your browser and:
|
||||||
|
|
||||||
|
1. Click OK to close "OAuth client" popup if it's still open.
|
||||||
|
2. Click "Manage service accounts" on the right of the screen.
|
||||||
|
3. Click the 3 dots to the right of your service account.
|
||||||
|
4. Choose Edit.
|
||||||
|
5. Click "Show Domain-Wide Delegation". Check "Enable G Suite Domain-wide Delegation", Click Save.
|
||||||
|
'''
|
||||||
|
raw_input(u'Press Enter when done...')
|
||||||
|
print u'That\'s it! Your GAM Project is created and ready to use.'
|
||||||
|
|
||||||
|
VALIDEMAIL_PATTERN = re.compile(r'^[^@]+@[^@]+\.[^@]+$')
|
||||||
|
|
||||||
|
def _getValidateLoginHint(login_hint):
|
||||||
|
while True:
|
||||||
|
if not login_hint:
|
||||||
|
login_hint = raw_input(u'\nWhat is your G Suite admin email address? ').strip()
|
||||||
|
if login_hint.find(u'@') == -1 and GC_Values[GC_DOMAIN]:
|
||||||
|
login_hint = u'{0}@{1}'.format(login_hint, GC_Values[GC_DOMAIN].lower())
|
||||||
|
if VALIDEMAIL_PATTERN.match(login_hint):
|
||||||
|
return login_hint
|
||||||
|
print '{0}Invalid email address: {1}'.format(ERROR_PREFIX, login_hint)
|
||||||
|
login_hint = None
|
||||||
|
|
||||||
|
def _getProjects(crm, pfilter):
|
||||||
|
try:
|
||||||
|
return callGAPIpages(crm.projects(), u'list', u'projects', throw_reasons=[GAPI_BAD_REQUEST], filter=pfilter)
|
||||||
|
except GAPI_badRequest as e:
|
||||||
|
systemErrorExit(2, u'Project: {0}, {1}'.format(pfilter, str(e)))
|
||||||
|
|
||||||
|
PROJECTID_PATTERN = re.compile(r'^[a-z][a-z0-9-]{4,28}[a-z0-9]$')
|
||||||
|
PROJECTID_FORMAT_REQUIRED = u'[a-z][a-z0-9-]{4,28}[a-z0-9]'
|
||||||
|
|
||||||
|
def _getLoginHintProjectId(createCmd):
|
||||||
|
login_hint = None
|
||||||
|
projectId = None
|
||||||
|
try:
|
||||||
|
login_hint = sys.argv[3]
|
||||||
|
projectId = sys.argv[4]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
login_hint = _getValidateLoginHint(login_hint)
|
||||||
|
if projectId:
|
||||||
|
if not PROJECTID_PATTERN.match(projectId):
|
||||||
|
systemErrorExit(2, 'Invalid Project ID: {0}, expected <{1}>'.format(projectId, PROJECTID_FORMAT_REQUIRED))
|
||||||
|
elif createCmd:
|
||||||
|
projectId = u'gam-project'
|
||||||
|
for _ in range(3):
|
||||||
|
projectId += u'-{0}'.format(u''.join(random.choice(string.digits + string.ascii_lowercase) for _ in range(3)))
|
||||||
|
else:
|
||||||
|
projectId = raw_input(u'\nWhat is your API project ID? ').strip()
|
||||||
|
if not PROJECTID_PATTERN.match(projectId):
|
||||||
|
systemErrorExit(2, 'Invalid Project ID: {0}, expected <{1}>'.format(projectId, PROJECTID_FORMAT_REQUIRED))
|
||||||
crm, httpObj = getCRMService(login_hint)
|
crm, httpObj = getCRMService(login_hint)
|
||||||
project_id = u'gam-project'
|
projects = _getProjects(crm, u'id:{0}'.format(projectId))
|
||||||
for i in range(3):
|
if not createCmd:
|
||||||
project_id += u'-%s' % ''.join(random.choice(string.digits + string.ascii_lowercase) for i in range(3))
|
if not projects:
|
||||||
project_name = u'project:%s' % project_id
|
systemErrorExit(2, 'User: {0}, Project ID: {1}, Does not exist'.format(login_hint, projectId))
|
||||||
body = {u'projectId': project_id, u'name': u'GAM Project'}
|
if projects[0][u'lifecycleState'] != u'ACTIVE':
|
||||||
|
systemErrorExit(2, 'User: {0}, Project ID: {1}, Not active'.format(login_hint, projectId))
|
||||||
|
else:
|
||||||
|
if projects:
|
||||||
|
systemErrorExit(2, 'User: {0}, Project ID: {1}, Duplicate'.format(login_hint, projectId))
|
||||||
|
return (crm, httpObj, login_hint, projectId)
|
||||||
|
|
||||||
|
PROJECTID_FILTER_REQUIRED = 'gam|<ProjectID>|(filter <String>)'
|
||||||
|
|
||||||
|
def _getLoginHintProjects(printShowCmd):
|
||||||
|
login_hint = None
|
||||||
|
pfilter = None
|
||||||
|
i = 3
|
||||||
|
try:
|
||||||
|
login_hint = sys.argv[i]
|
||||||
|
i += 1
|
||||||
|
pfilter = sys.argv[i]
|
||||||
|
i += 1
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
if not pfilter:
|
||||||
|
pfilter = u'current' if not printShowCmd else u'id:gam-project-*'
|
||||||
|
elif printShowCmd and pfilter.lower() == u'all':
|
||||||
|
pfilter = None
|
||||||
|
elif pfilter.lower() == u'gam':
|
||||||
|
pfilter = u'id:gam-project-*'
|
||||||
|
elif pfilter.lower() == u'filter':
|
||||||
|
pfilter = sys.argv[i]
|
||||||
|
i += 1
|
||||||
|
elif PROJECTID_PATTERN.match(pfilter):
|
||||||
|
pfilter = u'id:{0}'.format(pfilter)
|
||||||
|
else:
|
||||||
|
systemErrorExit(2, 'Invalid Project ID: {0}, expected <{1}{2}>'.format(pfilter, [u'', u'all|'][printShowCmd], PROJECTID_FILTER_REQUIRED))
|
||||||
|
login_hint = _getValidateLoginHint(login_hint)
|
||||||
|
crm, httpObj = getCRMService(login_hint)
|
||||||
|
if pfilter == u'current':
|
||||||
|
cs_data = readFile(GC_Values[GC_CLIENT_SECRETS_JSON], mode=u'rb', continueOnError=True, displayError=True, encoding=None)
|
||||||
|
if not cs_data:
|
||||||
|
systemErrorExit(14, u'Your client secrets file:\n\n%s\n\nis missing. Please recreate the file.' % GC_Values[GC_CLIENT_SECRETS_JSON])
|
||||||
|
try:
|
||||||
|
cs_json = json.loads(cs_data)
|
||||||
|
projects = [{u'projectId': cs_json[u'installed'][u'project_id']}]
|
||||||
|
except (ValueError, IndexError, KeyError):
|
||||||
|
systemErrorExit(3, u'The format of your client secrets file:\n\n%s\n\nis incorrect. Please recreate the file.' % GC_Values[GC_CLIENT_SECRETS_JSON])
|
||||||
|
else:
|
||||||
|
projects = _getProjects(crm, pfilter)
|
||||||
|
return (crm, httpObj, login_hint, projects, i)
|
||||||
|
|
||||||
|
def _checkForExistingProjectFiles():
|
||||||
|
for a_file in [GC_Values[GC_OAUTH2SERVICE_JSON], GC_Values[GC_CLIENT_SECRETS_JSON]]:
|
||||||
|
if os.path.exists(a_file):
|
||||||
|
systemErrorExit(5, '%s already exists. Please delete or rename it before attempting to use another project.' % a_file)
|
||||||
|
|
||||||
|
def doCreateProject():
|
||||||
|
_checkForExistingProjectFiles()
|
||||||
|
crm, httpObj, login_hint, projectId = _getLoginHintProjectId(True)
|
||||||
|
login_domain = login_hint[login_hint.find(u'@')+1:]
|
||||||
|
body = {u'projectId': projectId, u'name': u'GAM Project'}
|
||||||
while True:
|
while True:
|
||||||
create_again = False
|
create_again = False
|
||||||
print u'Creating project "%s"...' % body[u'name']
|
print u'Creating project "%s"...' % body[u'name']
|
||||||
create_operation = callGAPI(crm.projects(), u'create',
|
create_operation = callGAPI(crm.projects(), u'create', body=body)
|
||||||
body=body)
|
|
||||||
operation_name = create_operation[u'name']
|
operation_name = create_operation[u'name']
|
||||||
time.sleep(5) # Google recommends always waiting at least 5 seconds
|
time.sleep(5) # Google recommends always waiting at least 5 seconds
|
||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
@ -7314,69 +7463,76 @@ and accept the Terms of Service (ToS). As soon as you've accepted the ToS popup,
|
|||||||
elif u'error' in status:
|
elif u'error' in status:
|
||||||
systemErrorExit(2, status[u'error'])
|
systemErrorExit(2, status[u'error'])
|
||||||
break
|
break
|
||||||
simplehttp = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL])
|
_createClientSecretsOauth2service(httpObj, projectId)
|
||||||
enableProjectAPIs(simplehttp, httpObj, project_name, False)
|
|
||||||
iam = googleapiclient.discovery.build(u'iam', u'v1', http=httpObj, cache_discovery=False)
|
|
||||||
print u'Creating Service Account'
|
|
||||||
service_account = callGAPI(iam.projects().serviceAccounts(), u'create',
|
|
||||||
name=u'projects/%s' % project_id,
|
|
||||||
body={u'accountId': project_id, u'serviceAccount': {u'displayName': u'GAM Project'}})
|
|
||||||
key = callGAPI(iam.projects().serviceAccounts().keys(), u'create',
|
|
||||||
name=service_account[u'name'], body={u'privateKeyType': u'TYPE_GOOGLE_CREDENTIALS_FILE', u'keyAlgorithm': u'KEY_ALG_RSA_2048'})
|
|
||||||
oauth2service_data = base64.b64decode(key[u'privateKeyData'])
|
|
||||||
writeFile(service_account_file, oauth2service_data, continueOnError=False)
|
|
||||||
console_credentials_url = u'https://console.developers.google.com/apis/credentials?project=%s' % project_id
|
|
||||||
while True:
|
|
||||||
print u'''Please go to:
|
|
||||||
|
|
||||||
%s
|
def doUseProject():
|
||||||
|
_checkForExistingProjectFiles()
|
||||||
|
_, httpObj, _, projectId = _getLoginHintProjectId(False)
|
||||||
|
_createClientSecretsOauth2service(httpObj, projectId)
|
||||||
|
|
||||||
1. Click the blue "Create credentials" button. Choose "OAuth client ID".
|
def doUpdateProjects():
|
||||||
2. Click the blue "Configure consent screen" button. Enter "GAM" for "Application name".
|
_, httpObj, login_hint, projects, _ = _getLoginHintProjects(False)
|
||||||
3. Leave other fields blank. Click "Save" button.
|
_, GAMProjectAPIs = getGAMProjectAPIs()
|
||||||
3. Choose "Other". Enter a desired value for "Name". Click the blue "Create" button.
|
count = len(projects)
|
||||||
4. Copy your "client ID" value.
|
print u'User: {0}, Update {1} Projects'.format(login_hint, count)
|
||||||
|
i = 0
|
||||||
|
for project in projects:
|
||||||
|
i += 1
|
||||||
|
projectId = project[u'projectId']
|
||||||
|
enableGAMProjectAPIs(GAMProjectAPIs, httpObj, projectId, True, i, count)
|
||||||
|
|
||||||
''' % console_credentials_url
|
def doDelProjects():
|
||||||
# If you use Firefox to copy the Client ID and Secret, the data has leading and trailing newlines
|
crm, _, login_hint, projects, _ = _getLoginHintProjects(False)
|
||||||
# The first raw_input will get the leading newline, thus we have to issue another raw_input to get the data
|
count = len(projects)
|
||||||
# If the newlines are not present, the data is correctly read with the first raw_input
|
print u'User: {0}, Delete {1} Projects'.format(login_hint, count)
|
||||||
client_id = raw_input(u'Enter your Client ID: ').strip()
|
i = 0
|
||||||
if not client_id:
|
for project in projects:
|
||||||
client_id = raw_input().strip()
|
i += 1
|
||||||
print u'\nNow go back to your browser and copy your client secret.'
|
projectId = project[u'projectId']
|
||||||
client_secret = raw_input(u'Enter your Client Secret: ').strip()
|
try:
|
||||||
if not client_secret:
|
callGAPI(crm.projects(), u'delete', throw_reasons=[GAPI_FORBIDDEN], projectId=projectId)
|
||||||
client_secret = raw_input().strip()
|
print u' Project: {0} Deleted{1}'.format(projectId, currentCount(i, count))
|
||||||
client_valid = _checkClientAndSecret(simplehttp, client_id, client_secret)
|
except GAPI_forbidden as e:
|
||||||
if client_valid:
|
print u' Project: {0} Delete Failed: {1}{2}'.format(projectId, str(e), currentCount(i, count))
|
||||||
break
|
|
||||||
print
|
|
||||||
cs_data = u'''{
|
|
||||||
"installed": {
|
|
||||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
|
||||||
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
||||||
"client_id": "%s",
|
|
||||||
"client_secret": "%s",
|
|
||||||
"project_id": "%s",
|
|
||||||
"redirect_uris": [
|
|
||||||
"urn:ietf:wg:oauth:2.0:oob",
|
|
||||||
"http://localhost"
|
|
||||||
],
|
|
||||||
"token_uri": "https://accounts.google.com/o/oauth2/token"
|
|
||||||
}
|
|
||||||
}''' % (client_id, client_secret, project_id)
|
|
||||||
writeFile(client_secrets_file, cs_data, continueOnError=False)
|
|
||||||
print u'''Almost there! Now please switch back to your browser and:
|
|
||||||
|
|
||||||
1. Click OK to close "OAuth client" popup if it's still open.
|
|
||||||
2. Click "Manage service accounts" on the right of the screen.
|
def doPrintShowProjects(csvFormat):
|
||||||
3. Click the 3 dots to the right of your service account.
|
_, _, login_hint, projects, i = _getLoginHintProjects(True)
|
||||||
4. Choose Edit.
|
if csvFormat:
|
||||||
5. Click "Show Domain-Wide Delegation". Check "Enable G Suite Domain-wide Delegation". Click Save.
|
csvRows = []
|
||||||
'''
|
todrive = False
|
||||||
raw_input(u'Press Enter when done...')
|
titles = [u'User', u'projectId', u'projectNumber', u'name', u'createTime', u'lifecycleState']
|
||||||
print u'That\'s it! Your GAM Project is created and ready to use.'
|
while i < len(sys.argv):
|
||||||
|
myarg = sys.argv[i].lower()
|
||||||
|
if csvFormat and myarg == u'todrive':
|
||||||
|
todrive = True
|
||||||
|
i += 1
|
||||||
|
else:
|
||||||
|
systemErrorExit(2, '%s is not a valid argument for "gam %s projects"' % (myarg, [u'show', u'print'][csvFormat]))
|
||||||
|
if not csvFormat:
|
||||||
|
count = len(projects)
|
||||||
|
print u'User: {0}, Show {1} Projects'.format(login_hint, count)
|
||||||
|
i = 0
|
||||||
|
for project in projects:
|
||||||
|
i += 1
|
||||||
|
print u' Project: {0}{1}'.format(project[u'projectId'], currentCount(i, count))
|
||||||
|
print u' projectNumber: {0}'.format(project[u'projectNumber'])
|
||||||
|
print u' name: {0}'.format(project[u'name'])
|
||||||
|
print u' createTime: {0}'.format(project[u'createTime'])
|
||||||
|
print u' lifecycleState: {0}'.format(project[u'lifecycleState'])
|
||||||
|
jcount = len(project.get(u'labels', []))
|
||||||
|
if jcount > 0:
|
||||||
|
print u' labels:'
|
||||||
|
for k, v in project[u'labels'].items():
|
||||||
|
print u' {0}: {1}'.format(k, v)
|
||||||
|
if u'parent' in project:
|
||||||
|
print u' parent:'
|
||||||
|
print u' type: {0}'.format(project[u'parent'][u'type'])
|
||||||
|
print u' id: {0}'.format(project[u'parent'][u'id'])
|
||||||
|
else:
|
||||||
|
for project in projects:
|
||||||
|
addRowTitlesToCSVfile(flatten_json(project, flattened={u'User': login_hint}), csvRows, titles)
|
||||||
|
writeCSVfile(csvRows, titles, u'Projects', todrive)
|
||||||
|
|
||||||
def doGetTeamDriveInfo(users):
|
def doGetTeamDriveInfo(users):
|
||||||
teamDriveId = sys.argv[5]
|
teamDriveId = sys.argv[5]
|
||||||
@ -12201,7 +12357,7 @@ def doRequestOAuth(login_hint=None):
|
|||||||
if scopes is None:
|
if scopes is None:
|
||||||
systemErrorExit(0, u'')
|
systemErrorExit(0, u'')
|
||||||
client_id, client_secret = getOAuthClientIDAndSecret()
|
client_id, client_secret = getOAuthClientIDAndSecret()
|
||||||
login_hint = getValidateLoginHint(login_hint)
|
login_hint = _getValidateLoginHint(login_hint)
|
||||||
flow = oauth2client.client.OAuth2WebServerFlow(client_id=client_id,
|
flow = oauth2client.client.OAuth2WebServerFlow(client_id=client_id,
|
||||||
client_secret=client_secret, scope=scopes, redirect_uri=oauth2client.client.OOB_CALLBACK_URN,
|
client_secret=client_secret, scope=scopes, redirect_uri=oauth2client.client.OOB_CALLBACK_URN,
|
||||||
user_agent=GAM_INFO, response_type=u'code', login_hint=login_hint)
|
user_agent=GAM_INFO, response_type=u'code', login_hint=login_hint)
|
||||||
@ -12635,11 +12791,7 @@ def ProcessGAMCommand(args):
|
|||||||
elif argument in [u'guardianinvite', u'inviteguardian', u'guardian']:
|
elif argument in [u'guardianinvite', u'inviteguardian', u'guardian']:
|
||||||
doInviteGuardian()
|
doInviteGuardian()
|
||||||
elif argument in [u'project', u'apiproject']:
|
elif argument in [u'project', u'apiproject']:
|
||||||
try:
|
doCreateProject()
|
||||||
login_hint = sys.argv[3]
|
|
||||||
except IndexError:
|
|
||||||
login_hint = None
|
|
||||||
doCreateProject(login_hint)
|
|
||||||
elif argument in [u'resoldcustomer', u'resellercustomer']:
|
elif argument in [u'resoldcustomer', u'resellercustomer']:
|
||||||
doCreateResoldCustomer()
|
doCreateResoldCustomer()
|
||||||
elif argument in [u'resoldsubscription', u'resellersubscription']:
|
elif argument in [u'resoldsubscription', u'resellersubscription']:
|
||||||
@ -12657,6 +12809,13 @@ def ProcessGAMCommand(args):
|
|||||||
else:
|
else:
|
||||||
systemErrorExit(2, '%s is not a valid argument for "gam create"' % argument)
|
systemErrorExit(2, '%s is not a valid argument for "gam create"' % argument)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
elif command == u'use':
|
||||||
|
argument = sys.argv[2].lower()
|
||||||
|
if argument in [u'project', u'apiproject']:
|
||||||
|
doUseProject()
|
||||||
|
else:
|
||||||
|
systemErrorExit(2, '%s is not a valid argument for "gam use"' % argument)
|
||||||
|
sys.exit(0)
|
||||||
elif command == u'update':
|
elif command == u'update':
|
||||||
argument = sys.argv[2].lower()
|
argument = sys.argv[2].lower()
|
||||||
if argument == u'user':
|
if argument == u'user':
|
||||||
@ -12693,12 +12852,8 @@ def ProcessGAMCommand(args):
|
|||||||
doUpdateVaultMatter()
|
doUpdateVaultMatter()
|
||||||
elif argument in [u'hold', u'vaulthold']:
|
elif argument in [u'hold', u'vaulthold']:
|
||||||
doUpdateVaultHold()
|
doUpdateVaultHold()
|
||||||
elif argument in [u'project', u'apiproject']:
|
elif argument in [u'project', u'projects', u'apiproject']:
|
||||||
try:
|
doUpdateProjects()
|
||||||
login_hint = sys.argv[3]
|
|
||||||
except IndexError:
|
|
||||||
login_hint = None
|
|
||||||
doUpdateProject(login_hint)
|
|
||||||
elif argument in [u'building']:
|
elif argument in [u'building']:
|
||||||
doUpdateBuilding()
|
doUpdateBuilding()
|
||||||
elif argument in [u'feature']:
|
elif argument in [u'feature']:
|
||||||
@ -12793,11 +12948,7 @@ def ProcessGAMCommand(args):
|
|||||||
elif argument in [u'guardian', u'guardians']:
|
elif argument in [u'guardian', u'guardians']:
|
||||||
doDeleteGuardian()
|
doDeleteGuardian()
|
||||||
elif argument in [u'project', u'projects']:
|
elif argument in [u'project', u'projects']:
|
||||||
try:
|
doDelProjects()
|
||||||
login_hint = sys.argv[3]
|
|
||||||
except IndexError:
|
|
||||||
login_hint = None
|
|
||||||
doDelProjects(login_hint)
|
|
||||||
elif argument in [u'resoldsubscription', u'resellersubscription']:
|
elif argument in [u'resoldsubscription', u'resellersubscription']:
|
||||||
doDeleteResoldSubscription()
|
doDeleteResoldSubscription()
|
||||||
elif argument in [u'matter', u'vaultmatter']:
|
elif argument in [u'matter', u'vaultmatter']:
|
||||||
@ -12888,6 +13039,8 @@ def ProcessGAMCommand(args):
|
|||||||
doPrintBuildings()
|
doPrintBuildings()
|
||||||
elif argument in [u'feature', u'features']:
|
elif argument in [u'feature', u'features']:
|
||||||
doPrintFeatures()
|
doPrintFeatures()
|
||||||
|
elif argument in [u'project', u'projects']:
|
||||||
|
doPrintShowProjects(True)
|
||||||
else:
|
else:
|
||||||
systemErrorExit(2, '%s is not a valid argument for "gam print"' % argument)
|
systemErrorExit(2, '%s is not a valid argument for "gam print"' % argument)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@ -12899,6 +13052,8 @@ def ProcessGAMCommand(args):
|
|||||||
doPrintShowGuardians(False)
|
doPrintShowGuardians(False)
|
||||||
elif argument in [u'license', u'licenses', u'licence', u'licences']:
|
elif argument in [u'license', u'licenses', u'licence', u'licences']:
|
||||||
doShowLicenses()
|
doShowLicenses()
|
||||||
|
elif argument in [u'project', u'projects']:
|
||||||
|
doPrintShowProjects(False)
|
||||||
else:
|
else:
|
||||||
systemErrorExit(2, '%s is not a valid argument for "gam show"' % argument)
|
systemErrorExit(2, '%s is not a valid argument for "gam show"' % argument)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@ -12906,7 +13061,7 @@ def ProcessGAMCommand(args):
|
|||||||
argument = sys.argv[2].lower()
|
argument = sys.argv[2].lower()
|
||||||
if argument in [u'request', u'create']:
|
if argument in [u'request', u'create']:
|
||||||
try:
|
try:
|
||||||
login_hint = sys.argv[3]
|
login_hint = sys.argv[3].strip()
|
||||||
except IndexError:
|
except IndexError:
|
||||||
login_hint = None
|
login_hint = None
|
||||||
doRequestOAuth(login_hint)
|
doRequestOAuth(login_hint)
|
||||||
|
@ -844,6 +844,7 @@ GAPI_INVALID_MEMBER = u'invalidMember'
|
|||||||
GAPI_MEMBER_NOT_FOUND = u'memberNotFound'
|
GAPI_MEMBER_NOT_FOUND = u'memberNotFound'
|
||||||
GAPI_NOT_FOUND = u'notFound'
|
GAPI_NOT_FOUND = u'notFound'
|
||||||
GAPI_NOT_IMPLEMENTED = u'notImplemented'
|
GAPI_NOT_IMPLEMENTED = u'notImplemented'
|
||||||
|
GAPI_PERMISSION_DENIED = u'permissionDenied'
|
||||||
GAPI_QUOTA_EXCEEDED = u'quotaExceeded'
|
GAPI_QUOTA_EXCEEDED = u'quotaExceeded'
|
||||||
GAPI_RATE_LIMIT_EXCEEDED = u'rateLimitExceeded'
|
GAPI_RATE_LIMIT_EXCEEDED = u'rateLimitExceeded'
|
||||||
GAPI_RESOURCE_NOT_FOUND = u'resourceNotFound'
|
GAPI_RESOURCE_NOT_FOUND = u'resourceNotFound'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user