mirror of
https://github.com/GAM-team/GAM.git
synced 2025-07-08 13:43:35 +00:00
Service creation, dynamic scope cleanup
Make routine getAPIversionHttpService to handle all steps to get a service. Make routine handleOAuthTokenError to handle OAuth token errors. In doRequestOAuth, get admin email address from oauth2.txt if it exists, otherwise prompt for it. Move reading of gamscopes,json into SetGlobalVariables as a local routine _getScopesAdminDomainFromGamScopesJson.
This commit is contained in:
276
src/gam.py
276
src/gam.py
@ -181,7 +181,7 @@ GC_Defaults = {
|
|||||||
GC_CACHE_DIR: u'',
|
GC_CACHE_DIR: u'',
|
||||||
GC_CHARSET: u'utf-8',
|
GC_CHARSET: u'utf-8',
|
||||||
GC_CONFIG_DIR: u'',
|
GC_CONFIG_DIR: u'',
|
||||||
GC_CUSTOMER_ID: u'my_customer',
|
GC_CUSTOMER_ID: MY_CUSTOMER,
|
||||||
GC_DEBUG_LEVEL: 0,
|
GC_DEBUG_LEVEL: 0,
|
||||||
GC_DEVICE_MAX_RESULTS: 500,
|
GC_DEVICE_MAX_RESULTS: 500,
|
||||||
GC_DOMAIN: u'',
|
GC_DOMAIN: u'',
|
||||||
@ -238,8 +238,8 @@ GC_VAR_INFO = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MESSAGE_BATCH_CSV_DASH_DEBUG_INCOMPATIBLE = u'"gam {0} - ..." is not compatible with debugging. Disable debugging by deleting debug.gam and try again.'
|
MESSAGE_BATCH_CSV_DASH_DEBUG_INCOMPATIBLE = u'"gam {0} - ..." is not compatible with debugging. Disable debugging by deleting debug.gam and try again.'
|
||||||
MESSAGE_CLIENT_API_ACCESS_CONFIG = u'API access is configured in your Control Panel under: Security-Show more-Advanced settings-Manage API client access'
|
MESSAGE_API_ACCESS_CONFIG = u'API access is configured in your Control Panel under: Security-Show more-Advanced settings-Manage API client access'
|
||||||
MESSAGE_CLIENT_API_ACCESS_DENIED = u'API access denied. Please make sure the Service account Client ID: {0} is authorized for the API Scope(s): {1}'
|
MESSAGE_API_ACCESS_DENIED = u'API access denied.\n\nPlease make sure the Service account Client ID: {0} is authorized for the API Scope(s): {1}\n\nPlease make sure the Admin email address: {2} is valid'
|
||||||
MESSAGE_GAMSCOPES_JSON_INVALID = u'The file {0} has an invalid format.'
|
MESSAGE_GAMSCOPES_JSON_INVALID = u'The file {0} has an invalid format.'
|
||||||
MESSAGE_GAM_EXITING_FOR_UPDATE = u'GAM is now exiting so that you can overwrite this old version with the latest release'
|
MESSAGE_GAM_EXITING_FOR_UPDATE = u'GAM is now exiting so that you can overwrite this old version with the latest release'
|
||||||
MESSAGE_GAM_OUT_OF_MEMORY = u'GAM has run out of memory. If this is a large Google Apps instance, you should use a 64-bit version of GAM on Windows or a 64-bit version of Python on other systems.'
|
MESSAGE_GAM_OUT_OF_MEMORY = u'GAM has run out of memory. If this is a large Google Apps instance, you should use a 64-bit version of GAM on Windows or a 64-bit version of Python on other systems.'
|
||||||
@ -256,6 +256,8 @@ MESSAGE_REQUEST_NOT_COMPLETE = u'Request needs to be completed before downloadin
|
|||||||
MESSAGE_RESULTS_TOO_LARGE_FOR_GOOGLE_SPREADSHEET = u'Results are too large for Google Spreadsheets. Uploading as a regular CSV file.'
|
MESSAGE_RESULTS_TOO_LARGE_FOR_GOOGLE_SPREADSHEET = u'Results are too large for Google Spreadsheets. Uploading as a regular CSV file.'
|
||||||
MESSAGE_WIKI_INSTRUCTIONS_OAUTH2SERVICE_JSON = u'Please follow the instructions at this site to setup a Service account.'
|
MESSAGE_WIKI_INSTRUCTIONS_OAUTH2SERVICE_JSON = u'Please follow the instructions at this site to setup a Service account.'
|
||||||
|
|
||||||
|
OAUTH_TOKEN_ERRORS = [u'access_denied', u'unauthorized_client: Unauthorized client or scope in request.', u'access_denied: Requested client not authorized.', u'invalid_grant: Not a valid email.', u'invalid_request: Invalid impersonation prn email address.']
|
||||||
|
|
||||||
def convertUTF8(data):
|
def convertUTF8(data):
|
||||||
import collections
|
import collections
|
||||||
if isinstance(data, str):
|
if isinstance(data, str):
|
||||||
@ -355,6 +357,9 @@ def systemErrorExit(sysRC, message):
|
|||||||
sys.stderr.write(u'\n{0}{1}\n'.format(ERROR_PREFIX, message))
|
sys.stderr.write(u'\n{0}{1}\n'.format(ERROR_PREFIX, message))
|
||||||
sys.exit(sysRC)
|
sys.exit(sysRC)
|
||||||
|
|
||||||
|
def badGAMScopesExit():
|
||||||
|
systemErrorExit(19, MESSAGE_GAMSCOPES_JSON_INVALID.format(GC_Values[GC_GAMSCOPES_JSON]))
|
||||||
|
|
||||||
def noPythonSSLExit():
|
def noPythonSSLExit():
|
||||||
systemErrorExit(8, MESSAGE_NO_PYTHON_SSL)
|
systemErrorExit(8, MESSAGE_NO_PYTHON_SSL)
|
||||||
|
|
||||||
@ -416,7 +421,15 @@ def writeFile(filename, data, mode=u'wb', continueOnError=False, displayError=Tr
|
|||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, e))
|
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, e))
|
||||||
return False
|
return False
|
||||||
systemErrorExit(6, e)
|
systemErrorExit(6, e)
|
||||||
|
|
||||||
|
# Get global domain from global admin email address
|
||||||
#
|
#
|
||||||
|
def getDomainFromAdmin():
|
||||||
|
if GC_Values[GC_ADMIN]:
|
||||||
|
loc = GC_Values[GC_ADMIN].find(u'@')
|
||||||
|
if loc > 0:
|
||||||
|
GC_Values[GC_DOMAIN] = GC_Values[GC_ADMIN][loc+1:]
|
||||||
|
|
||||||
# Set global variables
|
# Set global variables
|
||||||
# Check for GAM updates based on status of noupdatecheck.txt
|
# Check for GAM updates based on status of noupdatecheck.txt
|
||||||
#
|
#
|
||||||
@ -440,19 +453,25 @@ def SetGlobalVariables():
|
|||||||
def _getOldSignalFile(itemName, trueValue=True, falseValue=False):
|
def _getOldSignalFile(itemName, trueValue=True, falseValue=False):
|
||||||
GC_Defaults[itemName] = trueValue if os.path.isfile(os.path.join(GC_Defaults[GC_CONFIG_DIR], GC_VAR_INFO[itemName][GC_VAR_ENVVAR_KEY])) else falseValue
|
GC_Defaults[itemName] = trueValue if os.path.isfile(os.path.join(GC_Defaults[GC_CONFIG_DIR], GC_VAR_INFO[itemName][GC_VAR_ENVVAR_KEY])) else falseValue
|
||||||
|
|
||||||
def _getAdminDomainFromOldOauth2Txt():
|
def _getScopesAdminDomainFromGamScopesJson():
|
||||||
if (not GC_Defaults[GC_ADMIN]) or (not GC_Defaults[GC_DOMAIN]):
|
GM_Globals[GM_GAMSCOPES_BY_API] = {}
|
||||||
srcFile = os.path.expanduser(os.environ.get(u'OAUTHFILE', u'oauth2.txt'))
|
json_string = readFile(GC_Values[GC_GAMSCOPES_JSON], continueOnError=True, displayError=False)
|
||||||
if not os.path.isabs(srcFile):
|
if json_string == None:
|
||||||
srcFile = os.path.expanduser(os.path.join(GC_Defaults[GC_CONFIG_DIR], srcFile))
|
return
|
||||||
if os.path.isfile(srcFile):
|
|
||||||
json_string = readFile(srcFile, continueOnError=True, displayError=False)
|
|
||||||
if json_string:
|
|
||||||
json_data = json.loads(json_string)
|
json_data = json.loads(json_string)
|
||||||
if not GC_Defaults[GC_ADMIN]:
|
if not isinstance(json_data, dict):
|
||||||
GC_Defaults[GC_ADMIN] = json_data.get(u'id_token', {}).get(u'email', u'')
|
badGAMScopesExit()
|
||||||
if not GC_Defaults[GC_DOMAIN]:
|
scopes = json_data.get(u'scopes', None)
|
||||||
GC_Defaults[GC_DOMAIN] = json_data.get(u'id_token', {}).get(u'hd', u'')
|
if (not scopes) or (not isinstance(scopes, dict)):
|
||||||
|
badGAMScopesExit()
|
||||||
|
for api, value in scopes.items():
|
||||||
|
if not isinstance(value, list):
|
||||||
|
badGAMScopesExit()
|
||||||
|
GM_Globals[GM_GAMSCOPES_BY_API][api] = list(set(value))
|
||||||
|
if not GC_Values[GC_ADMIN]:
|
||||||
|
GC_Values[GC_ADMIN] = json_data.get(u'admin', GC_Defaults[GC_ADMIN])
|
||||||
|
if not GC_Values[GC_DOMAIN]:
|
||||||
|
GC_Values[GC_DOMAIN] = json_data.get(u'domain', GC_Defaults[GC_DOMAIN])
|
||||||
|
|
||||||
def _getCfgDirectory(itemName):
|
def _getCfgDirectory(itemName):
|
||||||
return GC_Defaults[itemName]
|
return GC_Defaults[itemName]
|
||||||
@ -505,7 +524,6 @@ def SetGlobalVariables():
|
|||||||
_getOldSignalFile(GC_NO_BROWSER)
|
_getOldSignalFile(GC_NO_BROWSER)
|
||||||
_getOldSignalFile(GC_NO_CACHE)
|
_getOldSignalFile(GC_NO_CACHE)
|
||||||
_getOldSignalFile(GC_NO_UPDATE_CHECK)
|
_getOldSignalFile(GC_NO_UPDATE_CHECK)
|
||||||
_getAdminDomainFromOldOauth2Txt()
|
|
||||||
# Assign directories first
|
# Assign directories first
|
||||||
for itemName in GC_VAR_INFO:
|
for itemName in GC_VAR_INFO:
|
||||||
if GC_VAR_INFO[itemName][GC_VAR_TYPE_KEY] == GC_TYPE_DIRECTORY:
|
if GC_VAR_INFO[itemName][GC_VAR_TYPE_KEY] == GC_TYPE_DIRECTORY:
|
||||||
@ -519,10 +537,6 @@ def SetGlobalVariables():
|
|||||||
GM_Globals[GM_LAST_UPDATE_CHECK_TXT] = os.path.join(GC_Values[GC_CONFIG_DIR], FN_LAST_UPDATE_CHECK_TXT)
|
GM_Globals[GM_LAST_UPDATE_CHECK_TXT] = os.path.join(GC_Values[GC_CONFIG_DIR], FN_LAST_UPDATE_CHECK_TXT)
|
||||||
if not GC_Values[GC_NO_UPDATE_CHECK]:
|
if not GC_Values[GC_NO_UPDATE_CHECK]:
|
||||||
doGAMCheckForUpdates()
|
doGAMCheckForUpdates()
|
||||||
if (not GC_Values[GC_DOMAIN]) and GC_Values[GC_ADMIN]:
|
|
||||||
loc = GC_Values[GC_ADMIN].find(u'@')
|
|
||||||
if loc > 0:
|
|
||||||
GC_Values[GC_DOMAIN] = GC_Values[GC_ADMIN][loc+1:]
|
|
||||||
# Globals derived from config file values
|
# Globals derived from config file values
|
||||||
GM_Globals[GM_EXTRA_ARGS_DICT] = {u'prettyPrint': GC_Values[GC_DEBUG_LEVEL] > 0}
|
GM_Globals[GM_EXTRA_ARGS_DICT] = {u'prettyPrint': GC_Values[GC_DEBUG_LEVEL] > 0}
|
||||||
httplib2.debuglevel = GC_Values[GC_DEBUG_LEVEL]
|
httplib2.debuglevel = GC_Values[GC_DEBUG_LEVEL]
|
||||||
@ -535,10 +549,9 @@ def SetGlobalVariables():
|
|||||||
GM_Globals[GM_OAUTH2SERVICE_KEY] = None
|
GM_Globals[GM_OAUTH2SERVICE_KEY] = None
|
||||||
GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_EMAIL] = None
|
GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_EMAIL] = None
|
||||||
GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID] = None
|
GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID] = None
|
||||||
GM_Globals[GM_GAMSCOPES_BY_API] = {}
|
_getScopesAdminDomainFromGamScopesJson()
|
||||||
json_string = readFile(GC_Values[GC_GAMSCOPES_JSON], continueOnError=True, displayError=False)
|
if not GC_Values[GC_DOMAIN]:
|
||||||
if json_string and not validateSetGAMScopes(json.loads(json_string)):
|
getDomainFromAdmin()
|
||||||
systemErrorExit(19, MESSAGE_GAMSCOPES_JSON_INVALID.format(GC_Values[GC_GAMSCOPES_JSON]))
|
|
||||||
_chkCfgDirectories()
|
_chkCfgDirectories()
|
||||||
_chkCfgFiles()
|
_chkCfgFiles()
|
||||||
if GC_Values[GC_NO_CACHE]:
|
if GC_Values[GC_NO_CACHE]:
|
||||||
@ -595,6 +608,16 @@ def doGAMVersion():
|
|||||||
platform.platform(), platform.machine(),
|
platform.platform(), platform.machine(),
|
||||||
GM_Globals[GM_GAM_PATH])
|
GM_Globals[GM_GAM_PATH])
|
||||||
|
|
||||||
|
|
||||||
|
def handleOAuthTokenError(e, soft_errors):
|
||||||
|
if e.message in OAUTH_TOKEN_ERRORS:
|
||||||
|
if soft_errors:
|
||||||
|
return None
|
||||||
|
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, MESSAGE_API_ACCESS_DENIED.format(GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID],
|
||||||
|
u','.join(GM_Globals[GM_CURRENT_API_SCOPES]), GC_Values[GC_ADMIN])))
|
||||||
|
systemErrorExit(12, MESSAGE_API_ACCESS_CONFIG)
|
||||||
|
systemErrorExit(18, u'Authentication Token Error - {0}'.format(e))
|
||||||
|
|
||||||
def tryOAuth(gdataObject, soft_errors=False):
|
def tryOAuth(gdataObject, soft_errors=False):
|
||||||
credentials = oauth2client.client.SignedJwtAssertionCredentials(GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_EMAIL],
|
credentials = oauth2client.client.SignedJwtAssertionCredentials(GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_EMAIL],
|
||||||
GM_Globals[GM_OAUTH2SERVICE_KEY],
|
GM_Globals[GM_OAUTH2SERVICE_KEY],
|
||||||
@ -603,21 +626,11 @@ def tryOAuth(gdataObject, soft_errors=False):
|
|||||||
cache=GC_Values[GC_CACHE_DIR])
|
cache=GC_Values[GC_CACHE_DIR])
|
||||||
try:
|
try:
|
||||||
credentials.refresh(http)
|
credentials.refresh(http)
|
||||||
|
except httplib2.ServerNotFoundError as e:
|
||||||
|
systemErrorExit(4, e)
|
||||||
except oauth2client.client.AccessTokenRefreshError, e:
|
except oauth2client.client.AccessTokenRefreshError, e:
|
||||||
if e.message in [u'access_denied',
|
return handleOAuthTokenError(e, soft_errors)
|
||||||
u'unauthorized_client: Unauthorized client or scope in request.',
|
|
||||||
u'access_denied: Requested client not authorized.']:
|
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, MESSAGE_CLIENT_API_ACCESS_DENIED.format(GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID], u','.join(GM_Globals[GM_CURRENT_API_SCOPES]))))
|
|
||||||
systemErrorExit(5, MESSAGE_CLIENT_API_ACCESS_CONFIG)
|
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, e))
|
|
||||||
if soft_errors:
|
|
||||||
return False
|
|
||||||
sys.exit(4)
|
|
||||||
gdataObject.additional_headers = {u'Authorization': u'Bearer %s' % credentials.access_token}
|
gdataObject.additional_headers = {u'Authorization': u'Bearer %s' % credentials.access_token}
|
||||||
if not GC_Values[GC_DOMAIN]:
|
|
||||||
GC_Values[GC_DOMAIN] = GC_Values[GC_ADMIN][GC_Values[GC_ADMIN].find(u'@')+1:].lower()
|
|
||||||
if not GC_Values[GC_CUSTOMER_ID]:
|
|
||||||
GC_Values[GC_CUSTOMER_ID] = MY_CUSTOMER
|
|
||||||
gdataObject.domain = GC_Values[GC_DOMAIN]
|
gdataObject.domain = GC_Values[GC_DOMAIN]
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -699,6 +712,8 @@ def callGData(service, function, soft_errors=False, throw_errors=[], **kwargs):
|
|||||||
sys.stderr.write(u' - Giving up.\n')
|
sys.stderr.write(u' - Giving up.\n')
|
||||||
return None
|
return None
|
||||||
sys.exit(int(e.error_code))
|
sys.exit(int(e.error_code))
|
||||||
|
except oauth2client.client.AccessTokenRefreshError as e:
|
||||||
|
return handleOAuthTokenError(e, soft_errors)
|
||||||
|
|
||||||
def callGAPI(service, function, silent_errors=False, soft_errors=False, throw_reasons=[], retry_reasons=[], **kwargs):
|
def callGAPI(service, function, silent_errors=False, soft_errors=False, throw_reasons=[], retry_reasons=[], **kwargs):
|
||||||
method = getattr(service, function)
|
method = getattr(service, function)
|
||||||
@ -747,8 +762,7 @@ def callGAPI(service, function, silent_errors=False, soft_errors=False, throw_re
|
|||||||
return None
|
return None
|
||||||
sys.exit(int(http_status))
|
sys.exit(int(http_status))
|
||||||
except oauth2client.client.AccessTokenRefreshError, e:
|
except oauth2client.client.AccessTokenRefreshError, e:
|
||||||
sys.stderr.write(u'{0}Authentication Token Error: {1}\n'.format(ERROR_PREFIX, e))
|
return handleOAuthTokenError(e, False)
|
||||||
sys.exit(403)
|
|
||||||
except httplib2.CertificateValidationUnsupported:
|
except httplib2.CertificateValidationUnsupported:
|
||||||
noPythonSSLExit()
|
noPythonSSLExit()
|
||||||
except TypeError, e:
|
except TypeError, e:
|
||||||
@ -810,23 +824,11 @@ API_VER_MAPPING = {
|
|||||||
u'siteVerification': u'v1',
|
u'siteVerification': u'v1',
|
||||||
}
|
}
|
||||||
|
|
||||||
def getAPIVer(api):
|
def getAPIVersion(api):
|
||||||
return API_VER_MAPPING.get(api, u'v1')
|
version = API_VER_MAPPING.get(api, u'v1')
|
||||||
|
if api in [u'directory', u'reports', u'datatransfer']:
|
||||||
def getServiceFromDiscoveryDocument(api, version, http=None):
|
api = u'admin'
|
||||||
disc_filename = u'%s-%s.json' % (api, version)
|
return (api, version, u'{0}-{1}'.format(api, version))
|
||||||
disc_file = os.path.join(GC_Values[GC_SITE_DIR], disc_filename)
|
|
||||||
if hasattr(sys, '_MEIPASS'):
|
|
||||||
pyinstaller_disc_file = os.path.join(sys._MEIPASS, disc_filename)
|
|
||||||
else:
|
|
||||||
pyinstaller_disc_file = None
|
|
||||||
if os.path.isfile(disc_file):
|
|
||||||
discovery = readFile(disc_file)
|
|
||||||
elif pyinstaller_disc_file:
|
|
||||||
discovery = readFile(pyinstaller_disc_file)
|
|
||||||
else:
|
|
||||||
systemErrorExit(4, MESSAGE_NO_DISCOVERY_INFORMATION.format(disc_file))
|
|
||||||
return googleapiclient.discovery.build_from_document(discovery, base=u'https://www.googleapis.com', http=http)
|
|
||||||
|
|
||||||
def getOAuth2ServiceDetails():
|
def getOAuth2ServiceDetails():
|
||||||
if not GM_Globals[GM_OAUTH2SERVICE_KEY]:
|
if not GM_Globals[GM_OAUTH2SERVICE_KEY]:
|
||||||
@ -845,38 +847,46 @@ def getOAuth2ServiceDetails():
|
|||||||
printLine(GAM_WIKI_CREATE_CLIENT_SECRETS)
|
printLine(GAM_WIKI_CREATE_CLIENT_SECRETS)
|
||||||
systemErrorExit(17, MESSAGE_OAUTH2SERVICE_JSON_INVALID.format(GC_Values[GC_OAUTH2SERVICE_JSON]))
|
systemErrorExit(17, MESSAGE_OAUTH2SERVICE_JSON_INVALID.format(GC_Values[GC_OAUTH2SERVICE_JSON]))
|
||||||
|
|
||||||
def buildGAPIObject(api, act_as=None, soft_errors=False):
|
def getAPIversionHttpService(api):
|
||||||
svcsub = act_as if act_as else GC_Values[GC_ADMIN]
|
|
||||||
getOAuth2ServiceDetails()
|
getOAuth2ServiceDetails()
|
||||||
version = getAPIVer(api)
|
api, version, api_version = getAPIVersion(api)
|
||||||
if api in [u'directory', u'reports', u'datatransfer']:
|
|
||||||
api = u'admin'
|
|
||||||
http = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL],
|
http = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL],
|
||||||
cache=GC_Values[GC_CACHE_DIR])
|
cache=GC_Values[GC_CACHE_DIR])
|
||||||
try:
|
try:
|
||||||
service = googleapiclient.discovery.build(api, version, http=http, cache_discovery=False)
|
service = googleapiclient.discovery.build(api, version, http=http, cache_discovery=False)
|
||||||
except googleapiclient.errors.UnknownApiNameOrVersion:
|
return (api_version, http, service)
|
||||||
service = getServiceFromDiscoveryDocument(api, version, http)
|
|
||||||
except httplib2.ServerNotFoundError as e:
|
except httplib2.ServerNotFoundError as e:
|
||||||
systemErrorExit(4, e)
|
systemErrorExit(4, e)
|
||||||
scopes = GM_Globals[GM_GAMSCOPES_BY_API].get(u'{0}-{1}'.format(api, version), [])
|
except googleapiclient.errors.UnknownApiNameOrVersion:
|
||||||
if not scopes:
|
pass
|
||||||
|
disc_filename = u'%s.json' % (api_version)
|
||||||
|
disc_file = os.path.join(GC_Values[GC_SITE_DIR], disc_filename)
|
||||||
|
if hasattr(sys, '_MEIPASS'):
|
||||||
|
pyinstaller_disc_file = os.path.join(sys._MEIPASS, disc_filename)
|
||||||
|
else:
|
||||||
|
pyinstaller_disc_file = None
|
||||||
|
if os.path.isfile(disc_file):
|
||||||
|
discovery = readFile(disc_file)
|
||||||
|
elif pyinstaller_disc_file:
|
||||||
|
discovery = readFile(pyinstaller_disc_file)
|
||||||
|
else:
|
||||||
|
systemErrorExit(11, MESSAGE_NO_DISCOVERY_INFORMATION.format(disc_file))
|
||||||
|
service = googleapiclient.discovery.build_from_document(discovery, http=http)
|
||||||
|
return (api_version, http, service)
|
||||||
|
|
||||||
|
def buildGAPIObject(api, act_as=None, soft_errors=False):
|
||||||
|
svcsub = act_as if act_as else GC_Values[GC_ADMIN]
|
||||||
|
api_version, http, service = getAPIversionHttpService(api)
|
||||||
|
GM_Globals[GM_CURRENT_API_SCOPES] = GM_Globals[GM_GAMSCOPES_BY_API].get(api_version, [])
|
||||||
|
if not GM_Globals[GM_CURRENT_API_SCOPES]:
|
||||||
systemErrorExit(15, MESSAGE_NO_SCOPES_FOR_API.format(service._rootDesc[u'title']))
|
systemErrorExit(15, MESSAGE_NO_SCOPES_FOR_API.format(service._rootDesc[u'title']))
|
||||||
credentials = oauth2client.client.SignedJwtAssertionCredentials(GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_EMAIL],
|
credentials = oauth2client.client.SignedJwtAssertionCredentials(GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_EMAIL],
|
||||||
GM_Globals[GM_OAUTH2SERVICE_KEY],
|
GM_Globals[GM_OAUTH2SERVICE_KEY],
|
||||||
scope=scopes, user_agent=GAM_INFO, sub=svcsub)
|
scope=GM_Globals[GM_CURRENT_API_SCOPES], user_agent=GAM_INFO, sub=svcsub)
|
||||||
try:
|
try:
|
||||||
service._http = credentials.authorize(http)
|
service._http = credentials.authorize(http)
|
||||||
except oauth2client.client.AccessTokenRefreshError, e:
|
except oauth2client.client.AccessTokenRefreshError, e:
|
||||||
if e.message in [u'access_denied',
|
return handleOAuthTokenError(e, soft_errors)
|
||||||
u'unauthorized_client: Unauthorized client or scope in request.',
|
|
||||||
u'access_denied: Requested client not authorized.']:
|
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, MESSAGE_CLIENT_API_ACCESS_DENIED.format(GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID], u','.join(scopes))))
|
|
||||||
systemErrorExit(5, MESSAGE_CLIENT_API_ACCESS_CONFIG)
|
|
||||||
if soft_errors:
|
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(ERROR_PREFIX, e))
|
|
||||||
return False
|
|
||||||
systemErrorExit(4, e)
|
|
||||||
return service
|
return service
|
||||||
|
|
||||||
GDATA_API_INFO = {
|
GDATA_API_INFO = {
|
||||||
@ -887,11 +897,10 @@ GDATA_API_INFO = {
|
|||||||
|
|
||||||
def commonAppsObjInit(appsObj, api):
|
def commonAppsObjInit(appsObj, api):
|
||||||
getOAuth2ServiceDetails()
|
getOAuth2ServiceDetails()
|
||||||
GM_Globals[GM_CURRENT_API_SCOPES] = GM_Globals[GM_GAMSCOPES_BY_API].get(u'{0}-{1}'.format(api, getAPIVer(api)), [])
|
api, _, api_version = getAPIVersion(api)
|
||||||
|
GM_Globals[GM_CURRENT_API_SCOPES] = GM_Globals[GM_GAMSCOPES_BY_API].get(api_version, [])
|
||||||
if not GM_Globals[GM_CURRENT_API_SCOPES]:
|
if not GM_Globals[GM_CURRENT_API_SCOPES]:
|
||||||
systemErrorExit(15, MESSAGE_NO_SCOPES_FOR_API.format(GDATA_API_INFO[api]))
|
systemErrorExit(15, MESSAGE_NO_SCOPES_FOR_API.format(GDATA_API_INFO[api]))
|
||||||
if not tryOAuth(appsObj):
|
|
||||||
doRequestOAuth()
|
|
||||||
tryOAuth(appsObj)
|
tryOAuth(appsObj)
|
||||||
#Identify GAM to Google's Servers
|
#Identify GAM to Google's Servers
|
||||||
appsObj.source = GAM_INFO
|
appsObj.source = GAM_INFO
|
||||||
@ -8720,35 +8729,14 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, return_uids=Fa
|
|||||||
full_users = new_full_users
|
full_users = new_full_users
|
||||||
return full_users
|
return full_users
|
||||||
|
|
||||||
def validateSetGAMScopes(json_data):
|
|
||||||
GM_Globals[GM_GAMSCOPES_BY_API] = {}
|
|
||||||
if not isinstance(json_data, dict):
|
|
||||||
return False
|
|
||||||
for api, value in json_data.items():
|
|
||||||
if not isinstance(value, list):
|
|
||||||
return False
|
|
||||||
GM_Globals[GM_GAMSCOPES_BY_API][api] = list(set(value))
|
|
||||||
return len(GM_Globals[GM_GAMSCOPES_BY_API]) > 0
|
|
||||||
|
|
||||||
def OAuthInfo():
|
def OAuthInfo():
|
||||||
configRequired = False
|
configRequired = False
|
||||||
getOAuth2ServiceDetails()
|
print u'API Access, Admin: {0}'.format(GC_Values[GC_ADMIN])
|
||||||
print u'API Access'
|
|
||||||
for api in sorted(API_VER_MAPPING.keys()):
|
for api in sorted(API_VER_MAPPING.keys()):
|
||||||
print u' API: {0}'.format(api)
|
print u' API: {0}'.format(api)
|
||||||
version = getAPIVer(api)
|
api_version, http, service = getAPIversionHttpService(api)
|
||||||
if api in [u'directory', u'reports', u'datatransfer']:
|
|
||||||
api = u'admin'
|
|
||||||
http = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL],
|
|
||||||
cache=GC_Values[GC_CACHE_DIR])
|
|
||||||
try:
|
|
||||||
service = googleapiclient.discovery.build(api, version, http=http, cache_discovery=False)
|
|
||||||
except googleapiclient.errors.UnknownApiNameOrVersion:
|
|
||||||
service = getServiceFromDiscoveryDocument(api, version, http)
|
|
||||||
except httplib2.ServerNotFoundError as e:
|
|
||||||
systemErrorExit(4, e)
|
|
||||||
api_scopes = service._rootDesc[u'auth'][u'oauth2'][u'scopes']
|
api_scopes = service._rootDesc[u'auth'][u'oauth2'][u'scopes']
|
||||||
requested_scopes = GM_Globals[GM_GAMSCOPES_BY_API].get(u'{0}-{1}'.format(api, version), [])
|
requested_scopes = GM_Globals[GM_GAMSCOPES_BY_API].get(api_version, [])
|
||||||
if requested_scopes:
|
if requested_scopes:
|
||||||
for scope in requested_scopes:
|
for scope in requested_scopes:
|
||||||
credentials = oauth2client.client.SignedJwtAssertionCredentials(GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_EMAIL],
|
credentials = oauth2client.client.SignedJwtAssertionCredentials(GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_EMAIL],
|
||||||
@ -8759,17 +8747,15 @@ def OAuthInfo():
|
|||||||
status = u'Authorized'
|
status = u'Authorized'
|
||||||
except oauth2client.client.AccessTokenRefreshError, e:
|
except oauth2client.client.AccessTokenRefreshError, e:
|
||||||
configRequired = True
|
configRequired = True
|
||||||
if e.message in [u'access_denied',
|
if e.message in OAUTH_TOKEN_ERRORS:
|
||||||
u'unauthorized_client: Unauthorized client or scope in request.',
|
|
||||||
u'access_denied: Requested client not authorized.']:
|
|
||||||
status = u'DENIED'
|
status = u'DENIED'
|
||||||
else:
|
else:
|
||||||
status = u'{0}{1}'.format(ERROR_PREFIX, e)
|
status = u'{0}Authentication Token Error - {1}'.format(ERROR_PREFIX, e)
|
||||||
print u' {0}\n {1}\n Access: {2}'.format(api_scopes[scope][u'description'], scope, status)
|
print u' {0}\n {1}\n Access: {2}'.format(api_scopes[scope][u'description'], scope, status)
|
||||||
else:
|
else:
|
||||||
print u' Access: Not requested'
|
print u' Access: Not requested'
|
||||||
if configRequired:
|
if configRequired:
|
||||||
print MESSAGE_CLIENT_API_ACCESS_CONFIG
|
print MESSAGE_API_ACCESS_CONFIG
|
||||||
|
|
||||||
def doDeleteOAuth():
|
def doDeleteOAuth():
|
||||||
sys.stdout.write(u'Scopes file: {0}, will be Deleted in 3...'.format(GC_Values[GC_GAMSCOPES_JSON]))
|
sys.stdout.write(u'Scopes file: {0}, will be Deleted in 3...'.format(GC_Values[GC_GAMSCOPES_JSON]))
|
||||||
@ -8789,17 +8775,43 @@ def doDeleteOAuth():
|
|||||||
except OSError as e:
|
except OSError as e:
|
||||||
sys.stderr.write(u'{0}{1}\n'.format(WARNING_PREFIX, e))
|
sys.stderr.write(u'{0}{1}\n'.format(WARNING_PREFIX, e))
|
||||||
|
|
||||||
UBER_SCOPES = {
|
EMAIL_PATTERN = re.compile(r'^(.+)@(.+\..+)$')
|
||||||
u'gmail-v1': [u'https://mail.google.com/'],
|
EMAIL_FORMAT_REQUIRED = u'<Name>@<Name>.<TLD>'
|
||||||
}
|
|
||||||
|
|
||||||
def select_default_scopes(apis):
|
UBER_SCOPES = {u'gmail-v1': [u'https://mail.google.com/'],}
|
||||||
|
|
||||||
|
def doRequestOAuth():
|
||||||
|
|
||||||
|
def _getAdminDomain():
|
||||||
|
srcFile = os.path.expanduser(os.environ.get(u'OAUTHFILE', u'oauth2.txt'))
|
||||||
|
if not os.path.isabs(srcFile):
|
||||||
|
srcFile = os.path.expanduser(os.path.join(GC_Values[GC_CONFIG_DIR], srcFile))
|
||||||
|
if os.path.isfile(srcFile):
|
||||||
|
json_string = readFile(srcFile, continueOnError=True, displayError=False)
|
||||||
|
if json_string:
|
||||||
|
json_data = json.loads(json_string)
|
||||||
|
GC_Values[GC_ADMIN] = json_data.get(u'id_token', {}).get(u'email', GC_Defaults[GC_ADMIN])
|
||||||
|
if not GC_Values[GC_DOMAIN]:
|
||||||
|
GC_Values[GC_DOMAIN] = json_data.get(u'id_token', {}).get(u'hd', GC_Defaults[GC_DOMAIN])
|
||||||
|
if GC_Values[GC_ADMIN]:
|
||||||
|
return
|
||||||
|
print u''
|
||||||
|
while True:
|
||||||
|
value = raw_input(u'Enter Admin email address: ').strip().lower()
|
||||||
|
ema = EMAIL_PATTERN.match(value)
|
||||||
|
if ema:
|
||||||
|
GC_Values[GC_ADMIN] = value
|
||||||
|
if not GC_Values[GC_DOMAIN]:
|
||||||
|
GC_Values[GC_DOMAIN] = ema.group(2)
|
||||||
|
return
|
||||||
|
print u'{0}Enter full email address: {1}'.format(ERROR_PREFIX, EMAIL_FORMAT_REQUIRED)
|
||||||
|
|
||||||
|
def _select_default_scopes(apis):
|
||||||
for api_name, api in apis.items():
|
for api_name, api in apis.items():
|
||||||
if api_name in UBER_SCOPES:
|
if api_name in UBER_SCOPES:
|
||||||
api[u'use_scopes'] = UBER_SCOPES[api_name]
|
api[u'use_scopes'] = UBER_SCOPES[api_name]
|
||||||
else:
|
else:
|
||||||
scopes = api[u'auth'][u'oauth2'][u'scopes'].keys()
|
scopes = sorted(api[u'auth'][u'oauth2'][u'scopes'].keys())
|
||||||
scopes.sort()
|
|
||||||
api[u'use_scopes'] = []
|
api[u'use_scopes'] = []
|
||||||
# reduce # of scopes by checking if a scope is a substring of another
|
# reduce # of scopes by checking if a scope is a substring of another
|
||||||
# which should mean it covers same API operations. Add a . at end
|
# which should mean it covers same API operations. Add a . at end
|
||||||
@ -8814,7 +8826,7 @@ def select_default_scopes(apis):
|
|||||||
while (i < count) and scopes[i].startswith(scope):
|
while (i < count) and scopes[i].startswith(scope):
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
def getSelection(limit):
|
def _getSelection(limit):
|
||||||
while True:
|
while True:
|
||||||
selection = raw_input(u'Your selection: ')
|
selection = raw_input(u'Your selection: ')
|
||||||
if selection:
|
if selection:
|
||||||
@ -8826,32 +8838,24 @@ def getSelection(limit):
|
|||||||
else:
|
else:
|
||||||
print u'ERROR: please enter numbers only'
|
print u'ERROR: please enter numbers only'
|
||||||
|
|
||||||
def doRequestOAuth():
|
|
||||||
apis = API_VER_MAPPING.keys()
|
apis = API_VER_MAPPING.keys()
|
||||||
all_apis = {}
|
all_apis = {}
|
||||||
api_titles = {}
|
api_titles = {}
|
||||||
for api in apis:
|
for api in apis:
|
||||||
version = getAPIVer(api)
|
api_version, _, service = getAPIversionHttpService(api)
|
||||||
if api in [u'directory', u'reports', u'datatransfer']:
|
all_apis[api_version] = service._rootDesc
|
||||||
api = u'admin'
|
api_titles[api_version] = api_version
|
||||||
http = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL],
|
|
||||||
cache=GC_Values[GC_CACHE_DIR])
|
|
||||||
try:
|
|
||||||
service = googleapiclient.discovery.build(api, version, http=http, cache_discovery=False)
|
|
||||||
except googleapiclient.errors.UnknownApiNameOrVersion:
|
|
||||||
service = getServiceFromDiscoveryDocument(api, version, http)
|
|
||||||
api_name = u'%s-%s' % (api, version)
|
|
||||||
all_apis[api_name] = service._rootDesc
|
|
||||||
api_titles[api_name] = api_name
|
|
||||||
api_index = []
|
api_index = []
|
||||||
for _, api_name in sorted(api_titles.items()):
|
for _, api_version in sorted(api_titles.items()):
|
||||||
api_index.append(api_name)
|
api_index.append(api_version)
|
||||||
i = len(api_index)
|
i = len(api_index)
|
||||||
if GM_Globals[GM_GAMSCOPES_BY_API]:
|
if GM_Globals[GM_GAMSCOPES_BY_API]:
|
||||||
for api in all_apis:
|
for api in all_apis:
|
||||||
all_apis[api][u'use_scopes'] = GM_Globals[GM_GAMSCOPES_BY_API][api]
|
all_apis[api][u'use_scopes'] = GM_Globals[GM_GAMSCOPES_BY_API][api]
|
||||||
else:
|
else:
|
||||||
select_default_scopes(all_apis)
|
_select_default_scopes(all_apis)
|
||||||
|
if not GC_Values[GC_ADMIN]:
|
||||||
|
_getAdminDomain()
|
||||||
while True:
|
while True:
|
||||||
#os.system([u'clear', u'cls'][GM_Globals[GM_WINDOWS]])
|
#os.system([u'clear', u'cls'][GM_Globals[GM_WINDOWS]])
|
||||||
print u'Select the APIs to use with GAM.'
|
print u'Select the APIs to use with GAM.'
|
||||||
@ -8872,9 +8876,9 @@ def doRequestOAuth():
|
|||||||
print u' %2d) Cancel' % (i+2)
|
print u' %2d) Cancel' % (i+2)
|
||||||
print u' %2d) Continue' % (i+3)
|
print u' %2d) Continue' % (i+3)
|
||||||
print
|
print
|
||||||
selection = getSelection(i+3)
|
selection = _getSelection(i+3)
|
||||||
if selection == i: # defaults
|
if selection == i: # defaults
|
||||||
select_default_scopes(all_apis)
|
_select_default_scopes(all_apis)
|
||||||
elif selection == i+1: # unselect all
|
elif selection == i+1: # unselect all
|
||||||
for api in all_apis.keys():
|
for api in all_apis.keys():
|
||||||
all_apis[api][u'use_scopes'] = []
|
all_apis[api][u'use_scopes'] = []
|
||||||
@ -8888,7 +8892,9 @@ def doRequestOAuth():
|
|||||||
if not selected_scopes:
|
if not selected_scopes:
|
||||||
print u'YOU MUST SELECT AT LEAST ONE SCOPE'
|
print u'YOU MUST SELECT AT LEAST ONE SCOPE'
|
||||||
continue
|
continue
|
||||||
writeFile(GC_Values[GC_GAMSCOPES_JSON], json.dumps(GM_Globals[GM_GAMSCOPES_BY_API]))
|
writeFile(GC_Values[GC_GAMSCOPES_JSON], json.dumps({u'scopes': GM_Globals[GM_GAMSCOPES_BY_API],
|
||||||
|
u'admin': GC_Values[GC_ADMIN],
|
||||||
|
u'domain': GC_Values[GC_DOMAIN]}))
|
||||||
print u'Scopes file: {0}, Created'.format(GC_Values[GC_GAMSCOPES_JSON])
|
print u'Scopes file: {0}, Created'.format(GC_Values[GC_GAMSCOPES_JSON])
|
||||||
print MESSAGE_PLEASE_AUTHORIZE_SERVICE_ACCOUNT.format(len(selected_scopes), u','.join(selected_scopes))
|
print MESSAGE_PLEASE_AUTHORIZE_SERVICE_ACCOUNT.format(len(selected_scopes), u','.join(selected_scopes))
|
||||||
return
|
return
|
||||||
@ -8921,7 +8927,7 @@ def doRequestOAuth():
|
|||||||
print u' %2d) Cancel' % (x+3)
|
print u' %2d) Cancel' % (x+3)
|
||||||
print u' %2d) Back to all APIs' % (x+4)
|
print u' %2d) Back to all APIs' % (x+4)
|
||||||
print
|
print
|
||||||
selection = getSelection(x+4)
|
selection = _getSelection(x+4)
|
||||||
if selection < x: # select
|
if selection < x: # select
|
||||||
if api_scopes[selection] in all_apis[api][u'use_scopes']:
|
if api_scopes[selection] in all_apis[api][u'use_scopes']:
|
||||||
all_apis[api][u'use_scopes'].remove(api_scopes[selection])
|
all_apis[api][u'use_scopes'].remove(api_scopes[selection])
|
||||||
@ -8929,7 +8935,7 @@ def doRequestOAuth():
|
|||||||
all_apis[api][u'use_scopes'].append(api_scopes[selection])
|
all_apis[api][u'use_scopes'].append(api_scopes[selection])
|
||||||
elif selection == x: # defaults
|
elif selection == x: # defaults
|
||||||
just_this_api = {api: all_apis[api]}
|
just_this_api = {api: all_apis[api]}
|
||||||
select_default_scopes(just_this_api)
|
_select_default_scopes(just_this_api)
|
||||||
all_apis[api][u'use_scopes'] = just_this_api[api][u'use_scopes']
|
all_apis[api][u'use_scopes'] = just_this_api[api][u'use_scopes']
|
||||||
elif selection == x+1: # read-only
|
elif selection == x+1: # read-only
|
||||||
all_apis[api][u'use_scopes'] = []
|
all_apis[api][u'use_scopes'] = []
|
||||||
|
Reference in New Issue
Block a user