mirror of
https://github.com/GAM-team/GAM.git
synced 2025-07-10 06:33:34 +00:00
Merge branch 'master' of https://github.com/jay0lee/GAM
This commit is contained in:
187
src/gam.py
187
src/gam.py
@ -836,6 +836,17 @@ def getOauth2TxtStorageCredentials():
|
||||
except (KeyError, ValueError):
|
||||
return (storage, None)
|
||||
|
||||
def getValidOauth2TxtCredentials():
|
||||
"""Gets OAuth2 credentials which are guaranteed to be fresh and valid."""
|
||||
storage, credentials = getOauth2TxtStorageCredentials()
|
||||
if credentials is None or credentials.invalid:
|
||||
doRequestOAuth()
|
||||
credentials = storage.get()
|
||||
elif credentials.access_token_expired:
|
||||
http = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL])
|
||||
credentials.refresh(http)
|
||||
return credentials
|
||||
|
||||
def getService(api, http):
|
||||
api, version, api_version = getAPIVersion(api)
|
||||
retries = 3
|
||||
@ -873,10 +884,7 @@ def getService(api, http):
|
||||
|
||||
def buildGAPIObject(api):
|
||||
GM_Globals[GM_CURRENT_API_USER] = None
|
||||
storage, credentials = getOauth2TxtStorageCredentials()
|
||||
if not credentials or credentials.invalid:
|
||||
doRequestOAuth()
|
||||
credentials = storage.get()
|
||||
credentials = getValidOauth2TxtCredentials()
|
||||
credentials.user_agent = GAM_INFO
|
||||
http = credentials.authorize(httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL],
|
||||
cache=GM_Globals[GM_CACHE_DIR]))
|
||||
@ -899,7 +907,7 @@ def buildGAPIObject(api):
|
||||
except KeyError:
|
||||
GC_Values[GC_CUSTOMER_ID] = MY_CUSTOMER
|
||||
else:
|
||||
GC_Values[GC_DOMAIN] = credentials.id_token.get(u'hd', u'UNKNOWN').lower()
|
||||
GC_Values[GC_DOMAIN] = _getValueFromOAuth(u'hd', credentials=credentials)
|
||||
if not GC_Values[GC_CUSTOMER_ID]:
|
||||
GC_Values[GC_CUSTOMER_ID] = MY_CUSTOMER
|
||||
return service
|
||||
@ -3869,8 +3877,8 @@ def showDriveFileACL(users):
|
||||
if not drive:
|
||||
continue
|
||||
feed = callGAPIpages(drive.permissions(), u'list', u'permissions',
|
||||
fileId=fileId, fields=u'*', supportsTeamDrives=True,
|
||||
useDomainAdminAccess=useDomainAdminAccess)
|
||||
fileId=fileId, fields=u'*', supportsTeamDrives=True,
|
||||
useDomainAdminAccess=useDomainAdminAccess)
|
||||
for permission in feed:
|
||||
printPermission(permission)
|
||||
print u''
|
||||
@ -3908,8 +3916,8 @@ def delDriveFileACL(users):
|
||||
continue
|
||||
print u'Removing permission for %s from %s' % (permissionId, fileId)
|
||||
callGAPI(drive.permissions(), u'delete', fileId=fileId,
|
||||
permissionId=permissionId, supportsTeamDrives=True,
|
||||
useDomainAdminAccess=useDomainAdminAccess)
|
||||
permissionId=permissionId, supportsTeamDrives=True,
|
||||
useDomainAdminAccess=useDomainAdminAccess)
|
||||
|
||||
def addDriveFileACL(users):
|
||||
fileId = sys.argv[5]
|
||||
@ -7310,7 +7318,7 @@ def doGetTeamDriveInfo(users):
|
||||
print u'Failed to access Drive as %s' % user
|
||||
continue
|
||||
result = callGAPI(drive.teamdrives(), u'get', teamDriveId=teamDriveId,
|
||||
useDomainAdminAccess=useDomainAdminAccess, fields=u'*')
|
||||
useDomainAdminAccess=useDomainAdminAccess, fields=u'*')
|
||||
print_json(None, result)
|
||||
|
||||
def doCreateTeamDrive(users):
|
||||
@ -7322,7 +7330,6 @@ def doCreateTeamDrive(users):
|
||||
i += 2
|
||||
else:
|
||||
print u'ERROR: %s is not a valid argument to "gam <users> create teamdrive"' % sys.argv[i]
|
||||
print sys.argv
|
||||
sys.exit(3)
|
||||
for user in users:
|
||||
drive = buildGAPIServiceObject(u'drive3', user)
|
||||
@ -7396,8 +7403,8 @@ def printShowTeamDrives(users, csvFormat):
|
||||
if not drive:
|
||||
continue
|
||||
results = callGAPIpages(drive.teamdrives(), u'list', u'teamDrives',
|
||||
useDomainAdminAccess=useDomainAdminAccess, fields=u'*',
|
||||
q=q, soft_errors=True)
|
||||
useDomainAdminAccess=useDomainAdminAccess, fields=u'*',
|
||||
q=q, soft_errors=True)
|
||||
if not results:
|
||||
continue
|
||||
for td in results:
|
||||
@ -8662,11 +8669,8 @@ def doCreateResoldCustomer():
|
||||
result = callGAPI(res.customers(), u'insert', body=body, customerAuthToken=customerAuthToken, fields=u'customerId,customerDomain')
|
||||
print u'Created customer %s with id %s' % (result[u'customerDomain'], result[u'customerId'])
|
||||
|
||||
def _getValueFromOAuth(field):
|
||||
storage, credentials = getOauth2TxtStorageCredentials()
|
||||
if credentials is None or credentials.invalid:
|
||||
doRequestOAuth()
|
||||
credentials = storage.get()
|
||||
def _getValueFromOAuth(field, credentials=None):
|
||||
credentials = credentials if credentials is not None else getValidOauth2TxtCredentials()
|
||||
return credentials.id_token.get(field, u'Unknown')
|
||||
|
||||
def doGetUserInfo(user_email=None):
|
||||
@ -9485,14 +9489,16 @@ def doGetASPs(users):
|
||||
|
||||
def doDelASP(users):
|
||||
cd = buildGAPIObject(u'directory')
|
||||
codeIds = sys.argv[5].lower().split(u',')
|
||||
codeIdList = sys.argv[5].lower()
|
||||
if codeIdList == u'all':
|
||||
allCodeIds = True
|
||||
else:
|
||||
allCodeIds = False
|
||||
codeIds = codeIdList.replace(u',', u' ').split()
|
||||
for user in users:
|
||||
if codeIds == [u'all']:
|
||||
codeIds = []
|
||||
asps = callGAPIitems(cd.asps(), u'list', u'items', userKey=user)
|
||||
if asps:
|
||||
for asp in asps:
|
||||
codeIds.append(asp[u'codeId'])
|
||||
if allCodeIds:
|
||||
asps = callGAPIitems(cd.asps(), u'list', u'items', userKey=user, fields=u'items/codeId')
|
||||
codeIds = [asp[u'codeId'] for asp in asps]
|
||||
for codeId in codeIds:
|
||||
callGAPI(cd.asps(), u'delete', userKey=user, codeId=codeId)
|
||||
print u'deleted ASP %s for %s' % (codeId, user)
|
||||
@ -9938,13 +9944,15 @@ USER_ARGUMENT_TO_PROPERTY_MAP = {
|
||||
u'ipwhitelisted': [u'ipWhitelisted',],
|
||||
u'isadmin': [u'isAdmin', u'isDelegatedAdmin',],
|
||||
u'isdelegatedadmin': [u'isAdmin', u'isDelegatedAdmin',],
|
||||
u'isenforcedin2sv': [u'isEnforcedIn2Sv',],
|
||||
u'isenrolledin2sv': [u'isEnrolledIn2Sv',],
|
||||
u'is2svenforced': [u'isEnforcedIn2Sv',],
|
||||
u'is2svenrolled': [u'isEnrolledIn2Sv',],
|
||||
u'ismailboxsetup': [u'isMailboxSetup',],
|
||||
u'lastlogintime': [u'lastLoginTime',],
|
||||
u'lastname': [u'name.familyName',],
|
||||
u'location': [u'locations',],
|
||||
u'locations': [u'locations'],
|
||||
u'locations': [u'locations',],
|
||||
u'name': [u'name.givenName', u'name.familyName', u'name.fullName',],
|
||||
u'nicknames': [u'aliases', u'nonEditableAliases',],
|
||||
u'noneditablealiases': [u'aliases', u'nonEditableAliases',],
|
||||
@ -9991,6 +9999,8 @@ def doPrintUsers():
|
||||
customFieldMask = None
|
||||
sortHeaders = getGroupFeed = getLicenseFeed = email_parts = False
|
||||
viewType = deleted_only = orderBy = sortOrder = None
|
||||
groupDelimiter = u' '
|
||||
licenseDelimiter = u','
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace(u'_', u'')
|
||||
@ -10004,6 +10014,12 @@ def doPrintUsers():
|
||||
sortHeaders = True
|
||||
fieldsList = []
|
||||
i += 1
|
||||
elif myarg == u'delimiter':
|
||||
groupDelimiter = licenseDelimiter = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == u'sortheaders':
|
||||
sortHeaders = True
|
||||
i += 1
|
||||
elif myarg in [u'custom', u'schemas']:
|
||||
fieldsList.append(u'customSchemas')
|
||||
if sys.argv[i+1].lower() == u'all':
|
||||
@ -10099,12 +10115,7 @@ def doPrintUsers():
|
||||
user_email = user[u'primaryEmail']
|
||||
sys.stderr.write(u"Getting Group Membership for %s (%s/%s)\r\n" % (user_email, user_count, total_users))
|
||||
groups = callGAPIpages(cd.groups(), u'list', u'groups', userKey=user_email)
|
||||
grouplist = u''
|
||||
for groupname in groups:
|
||||
grouplist += groupname[u'email']+u' '
|
||||
if grouplist[-1:] == u' ':
|
||||
grouplist = grouplist[:-1]
|
||||
user.update(Groups=grouplist)
|
||||
user[u'Groups'] = groupDelimiter.join([groupname[u'email'] for groupname in groups])
|
||||
user_count += 1
|
||||
if getLicenseFeed:
|
||||
titles.append(u'Licenses')
|
||||
@ -10113,7 +10124,7 @@ def doPrintUsers():
|
||||
for user in csvRows:
|
||||
u_licenses = licenses.get(user[u'primaryEmail'].lower())
|
||||
if u_licenses:
|
||||
user[u'Licenses'] = u','.join([_skuIdToDisplayName(skuId) for skuId in u_licenses])
|
||||
user[u'Licenses'] = licenseDelimiter.join([_skuIdToDisplayName(skuId) for skuId in u_licenses])
|
||||
writeCSVfile(csvRows, titles, u'Users', todrive)
|
||||
|
||||
GROUP_ARGUMENT_TO_PROPERTY_TITLE_MAP = {
|
||||
@ -10208,6 +10219,9 @@ def doPrintGroups():
|
||||
for field in GROUP_ARGUMENT_TO_PROPERTY_TITLE_MAP:
|
||||
addFieldTitleToCSVfile(field, GROUP_ARGUMENT_TO_PROPERTY_TITLE_MAP, cdfieldsList, fieldsTitles, titles)
|
||||
i += 1
|
||||
elif myarg == u'sortheaders':
|
||||
sortHeaders = True
|
||||
i += 1
|
||||
elif myarg == u'fields':
|
||||
fieldNameList = sys.argv[i+1]
|
||||
for field in fieldNameList.lower().replace(u',', u' ').split():
|
||||
@ -10762,8 +10776,7 @@ def doPrintCrosActivity():
|
||||
startDate = endDate = None
|
||||
listLimit = 0
|
||||
delimiter = u','
|
||||
query = None
|
||||
orgUnitPath = None
|
||||
query = orgUnitPath = None
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace(u'_', u'')
|
||||
@ -11296,7 +11309,7 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
|
||||
for user in users:
|
||||
if user[:4] == u'uid:':
|
||||
full_users.append(user[4:])
|
||||
elif user != u'*' and user.find(u'@') == -1:
|
||||
elif user != u'*' and user != GC_Values[GC_CUSTOMER_ID] and user.find(u'@') == -1:
|
||||
full_users.append(u'%s@%s' % (user, GC_Values[GC_DOMAIN]))
|
||||
else:
|
||||
full_users.append(user)
|
||||
@ -11305,31 +11318,26 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
|
||||
return full_users
|
||||
|
||||
def OAuthInfo():
|
||||
credentials = None
|
||||
if len(sys.argv) > 3:
|
||||
access_token = sys.argv[3]
|
||||
else:
|
||||
storage, credentials = getOauth2TxtStorageCredentials()
|
||||
if credentials is None or credentials.invalid:
|
||||
doRequestOAuth()
|
||||
credentials = storage.get()
|
||||
credentials = getValidOauth2TxtCredentials()
|
||||
credentials.user_agent = GAM_INFO
|
||||
http = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL])
|
||||
if credentials.access_token_expired:
|
||||
credentials.refresh(http)
|
||||
access_token = credentials.access_token
|
||||
print u"\nOAuth File: %s" % GC_Values[GC_OAUTH2_TXT]
|
||||
|
||||
oa2 = buildGAPIObject(u'oauth2')
|
||||
token_info = callGAPI(oa2, u'tokeninfo', access_token=access_token)
|
||||
print u"Client ID: %s" % token_info[u'issued_to']
|
||||
try:
|
||||
if credentials is not None:
|
||||
print u"Secret: %s" % credentials.client_secret
|
||||
except UnboundLocalError:
|
||||
pass
|
||||
scopes = token_info[u'scope'].split(u' ')
|
||||
print u'Scopes (%s):' % len(scopes)
|
||||
for scope in sorted(scopes):
|
||||
print u' %s' % scope
|
||||
print u'G Suite Admin: %s' % credentials.id_token.get(u'email', u'Unknown')
|
||||
if credentials is not None:
|
||||
print u'G Suite Admin: %s' % _getValueFromOAuth(u'email', credentials=credentials)
|
||||
|
||||
def doDeleteOAuth():
|
||||
_, credentials = getOauth2TxtStorageCredentials()
|
||||
@ -11353,6 +11361,50 @@ def doDeleteOAuth():
|
||||
stderrErrorMsg(str(e))
|
||||
os.remove(GC_Values[GC_OAUTH2_TXT])
|
||||
|
||||
def doRequestOAuth(login_hint=None):
|
||||
storage, credentials = getOauth2TxtStorageCredentials()
|
||||
if credentials is None or credentials.invalid:
|
||||
http = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL])
|
||||
flags = cmd_flags(noLocalWebserver=GC_Values[GC_NO_BROWSER])
|
||||
scopes = getScopesFromUser()
|
||||
if scopes is None:
|
||||
return
|
||||
client_id, client_secret = getOAuthClientIDAndSecret()
|
||||
login_hint = getValidateLoginHint(login_hint)
|
||||
flow = oauth2client.client.OAuth2WebServerFlow(client_id=client_id,
|
||||
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)
|
||||
try:
|
||||
credentials = oauth2client.tools.run_flow(flow=flow, storage=storage, flags=flags, http=http)
|
||||
except httplib2.CertificateValidationUnsupported:
|
||||
noPythonSSLExit()
|
||||
else:
|
||||
print u'It looks like you\'ve already authorized GAM. Refusing to overwrite existing file:\n\n%s' % GC_Values[GC_OAUTH2_TXT]
|
||||
|
||||
def getOAuthClientIDAndSecret():
|
||||
"""Retrieves the OAuth client ID and client secret from JSON."""
|
||||
MISSING_CLIENT_SECRETS_MESSAGE = u'''To use GAM you need to create an API project. Please run:
|
||||
|
||||
gam create project
|
||||
'''
|
||||
|
||||
cs_data = readFile(GC_Values[GC_CLIENT_SECRETS_JSON], mode=u'rb', continueOnError=True, displayError=True, encoding=None)
|
||||
if not cs_data:
|
||||
systemErrorExit(14, MISSING_CLIENT_SECRETS_MESSAGE)
|
||||
try:
|
||||
cs_json = json.loads(cs_data)
|
||||
client_id = cs_json[u'installed'][u'client_id']
|
||||
# chop off .apps.googleusercontent.com suffix as it's not needed
|
||||
# and we need to keep things short for the Auth URL.
|
||||
client_id = re.sub(r'\.apps\.googleusercontent\.com$', u'', client_id)
|
||||
client_secret = cs_json[u'installed'][u'client_secret']
|
||||
except (ValueError, IndexError, KeyError):
|
||||
message = (u'ERROR: the format of your client secrets file:\n\n%s\n\n is '
|
||||
'incorrect. Please recreate the file.')
|
||||
systemErrorExit(3, message)
|
||||
|
||||
return (client_id, client_secret)
|
||||
|
||||
class cmd_flags(object):
|
||||
def __init__(self, noLocalWebserver):
|
||||
self.short_url = True
|
||||
@ -11462,7 +11514,8 @@ OAUTH2_MENU += '''
|
||||
OAUTH2_CMDS = [u's', u'u', u'e', u'c']
|
||||
MAXIMUM_SCOPES = 28 # max of 30 - 2 for email scope always included
|
||||
|
||||
def doRequestOAuth(login_hint=None):
|
||||
def getScopesFromUser():
|
||||
"""Prompts the user to choose from a list of scopes to authorize."""
|
||||
def _checkMakeScopesList(scopes):
|
||||
del scopes[:]
|
||||
for i in range(num_scopes):
|
||||
@ -11482,26 +11535,6 @@ def doRequestOAuth(login_hint=None):
|
||||
scopes.insert(0, u'email') # Email Display Scope, always included
|
||||
return (True, u'')
|
||||
|
||||
MISSING_CLIENT_SECRETS_MESSAGE = u'''To use GAM you need to create an API project. Please run:
|
||||
|
||||
gam create project
|
||||
'''
|
||||
|
||||
cs_data = readFile(GC_Values[GC_CLIENT_SECRETS_JSON], mode=u'rb', continueOnError=True, displayError=True, encoding=None)
|
||||
if not cs_data:
|
||||
systemErrorExit(14, MISSING_CLIENT_SECRETS_MESSAGE)
|
||||
try:
|
||||
cs_json = json.loads(cs_data)
|
||||
client_id = cs_json[u'installed'][u'client_id']
|
||||
# chop off .apps.googleusercontent.com suffix as it's not needed
|
||||
# and we need to keep things short for the Auth URL.
|
||||
client_id = re.sub(r'\.apps\.googleusercontent\.com$', u'', client_id)
|
||||
client_secret = cs_json[u'installed'][u'client_secret']
|
||||
except (ValueError, IndexError, KeyError):
|
||||
print u'ERROR: the format of your client secrets file:\n\n%s\n\n is incorrect. Please recreate the file.'
|
||||
sys.exit(3)
|
||||
|
||||
login_hint = getValidateLoginHint(login_hint)
|
||||
num_scopes = len(OAUTH2_SCOPES)
|
||||
menu = OAUTH2_MENU % tuple(range(num_scopes))
|
||||
selected_scopes = []
|
||||
@ -11556,26 +11589,14 @@ gam create project
|
||||
for i in range(num_scopes):
|
||||
selected_scopes[i] = u' '
|
||||
elif selection == u'e':
|
||||
return
|
||||
return None
|
||||
break
|
||||
sys.stdout.write(u'{0}Invalid input "{1}"\n'.format(ERROR_PREFIX, choice))
|
||||
if selection == u'c':
|
||||
status, message = _checkMakeScopesList(scopes)
|
||||
if status:
|
||||
break
|
||||
flow = oauth2client.client.OAuth2WebServerFlow(client_id=client_id,
|
||||
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)
|
||||
storage, credentials = getOauth2TxtStorageCredentials()
|
||||
if credentials is None or credentials.invalid:
|
||||
http = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL])
|
||||
flags = cmd_flags(noLocalWebserver=GC_Values[GC_NO_BROWSER])
|
||||
try:
|
||||
credentials = oauth2client.tools.run_flow(flow=flow, storage=storage, flags=flags, http=http)
|
||||
except httplib2.CertificateValidationUnsupported:
|
||||
noPythonSSLExit()
|
||||
else:
|
||||
print u'It looks like you\'ve already authorized GAM. Refusing to overwrite existing file:\n\n%s' % GC_Values[GC_OAUTH2_TXT]
|
||||
return scopes
|
||||
|
||||
def init_gam_worker():
|
||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
@ -12337,7 +12358,7 @@ def ProcessGAMCommand(args):
|
||||
elif addWhat == u'teamdrive':
|
||||
doCreateTeamDrive(users)
|
||||
else:
|
||||
print u'ERROR: %s is not a valid argument for "gam <users> add"' % addWhat
|
||||
print u'ERROR: %s is not a valid argument for "gam <users> %s"' % (addWhat, command)
|
||||
sys.exit(2)
|
||||
elif command == u'update':
|
||||
updateWhat = sys.argv[4].lower()
|
||||
|
Reference in New Issue
Block a user