Added variable gcp_org_id to gam.cfg #1891

This commit is contained in:
Ross Scroggs
2026-03-19 19:52:18 -07:00
parent e5562eb917
commit 327dd6d7e5
6 changed files with 81 additions and 41 deletions

View File

@@ -1429,6 +1429,8 @@ gam print addresses [todrive <ToDriveAttribute>*]
# Authorization # Authorization
gam info gcporgid
gam create gcpfolder <String> gam create gcpfolder <String>
gam create gcpfolder [admin <EmailAddress>] folder <String> gam create gcpfolder [admin <EmailAddress>] folder <String>
@@ -2990,6 +2992,8 @@ gam [<UserTypeEntity>] show classificationlabelpermissions <ClassificationLabelN
(combiningfunction <CAACombiningFunction>) | (combiningfunction <CAACombiningFunction>) |
(condition <CAAConditionAttribute>+ endcondition) (condition <CAAConditionAttribute>+ endcondition)
gam info gcporgid
gam create caalevel <String> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)|<JSONData> gam create caalevel <String> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)|<JSONData>
gam update caalevel <CAALevelName> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)|<JSONData> gam update caalevel <CAALevelName> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)|<JSONData>
gam delete caalevel <CAALevelName> gam delete caalevel <CAALevelName>
@@ -8912,6 +8916,8 @@ gam <UserTypeEntity> show sheetrange <DriveFileEntity>
gam <UserTypeEntity> delete tokens clientid <ClientID> gam <UserTypeEntity> delete tokens clientid <ClientID>
gam info gcporgid
gam <UserTypeEntity> print tokens|token [todrive <ToDriveAttribute>*] [clientid <ClientID>] gam <UserTypeEntity> print tokens|token [todrive <ToDriveAttribute>*] [clientid <ClientID>]
[usertokencounts|(aggregateusersby|orderby clientid|id|appname|displaytext)] [usertokencounts|(aggregateusersby|orderby clientid|id|appname|displaytext)]
[delimiter <Character>] [gcpdetails] [delimiter <Character>] [gcpdetails]

View File

@@ -1,3 +1,21 @@
7.38.00
Added variable `gcp_org_id` to `gam.cfg` that is used by the following commands;
by setting the value, additional API calls are eliminated.
```
gam create project
gam create gcpfolder
gam create|update|delete caalevel
gam print|show caalevels
gam print|show tokens gcpdetails
```
You can get and set the `gcp_org_id` value with these commands:
```
$ gam info gcporgid
organizations/906207637890
$ gam config gcp_org_id organizations/906207637890 save
```
7.37.00 7.37.00
Added new client access scopes used by `gam print tokens`. Added new client access scopes used by `gam print tokens`.

View File

@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
""" """
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>' __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
__version__ = '7.37.00' __version__ = '7.38.00'
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
# pylint: disable=wrong-import-position # pylint: disable=wrong-import-position
@@ -11991,20 +11991,39 @@ def _checkForExistingProjectFiles(projectFiles):
if os.path.exists(a_file): if os.path.exists(a_file):
systemErrorExit(JSON_ALREADY_EXISTS_RC, Msg.AUTHORIZATION_FILE_ALREADY_EXISTS.format(a_file, Act.ToPerform())) systemErrorExit(JSON_ALREADY_EXISTS_RC, Msg.AUTHORIZATION_FILE_ALREADY_EXISTS.format(a_file, Act.ToPerform()))
def getGCPOrg(crm, login_hint, login_domain): def getCRMOrgId(forceSearch=False):
try: if not GC.Values[GC.GCP_ORG_ID] or forceSearch:
getorg = callGAPI(crm.organizations(), 'search', setTrueCustomerId()
throwReasons=[GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED], _, crm = buildGAPIServiceObject(API.CLOUDRESOURCEMANAGER, None)
query=f'domain:{login_domain}', results = callGAPI(crm.organizations(), 'search',
pageSize=1, fields='organizations/name') query=f'directorycustomerid:{GC.Values[GC.CUSTOMER_ID]}',
except (GAPI.invalidArgument, GAPI.permissionDenied) as e: pageSize=1, fields='organizations/name')
entityActionFailedExit([Ent.USER, login_hint, Ent.DOMAIN, login_domain], str(e)) orgs = results.get('organizations')
try: if not orgs:
organization = getorg['organizations'][0]['name'] # return nothing and let calling API deal with it
# sys.stdout.write(Msg.YOUR_ORGANIZATION_NAME_IS.format(organization)) # since caller knows what GCP role would serve best
return organization return None
except (KeyError, IndexError): return orgs[0].get('name')
systemErrorExit(3, Msg.YOU_HAVE_NO_RIGHTS_TO_CREATE_PROJECTS_AND_YOU_ARE_NOT_A_SUPER_ADMIN) return GC.Values[GC.GCP_ORG_ID]
# gam info gcporgid
def doInfoGCPOrgId():
checkForExtraneousArguments()
writeStdout(f'{getCRMOrgId(forceSearch=True)}\n')
def getGCPOrgId(crm, login_hint, login_domain):
if not GC.Values[GC.GCP_ORG_ID]:
try:
results = callGAPI(crm.organizations(), 'search',
throwReasons=[GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
query=f'domain:{login_domain}',
pageSize=1, fields='organizations/name')
return results['organizations'][0]['name']
except (GAPI.invalidArgument, GAPI.permissionDenied) as e:
entityActionFailedExit([Ent.USER, login_hint, Ent.DOMAIN, login_domain], str(e))
except (KeyError, IndexError):
systemErrorExit(3, Msg.YOU_HAVE_NO_RIGHTS_TO_CREATE_PROJECTS_AND_YOU_ARE_NOT_A_SUPER_ADMIN)
return GC.Values[GC.GCP_ORG_ID]
# gam create gcpfolder <String> # gam create gcpfolder <String>
# gam create gcpfolder [admin <EmailAddress] folder <String> # gam create gcpfolder [admin <EmailAddress] folder <String>
@@ -12028,7 +12047,7 @@ def doCreateGCPFolder():
login_hint = _getValidateLoginHint(login_hint) login_hint = _getValidateLoginHint(login_hint)
login_domain = getEmailAddressDomain(login_hint) login_domain = getEmailAddressDomain(login_hint)
_, crm = getCRMService(login_hint) _, crm = getCRMService(login_hint)
organization = getGCPOrg(crm, login_hint, login_domain) organization = getGCPOrgId(crm, login_hint, login_domain)
try: try:
result = callGAPI(crm.folders(), 'create', result = callGAPI(crm.folders(), 'create',
throwReasons=[GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED], throwReasons=[GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
@@ -12074,7 +12093,7 @@ def doCreateProject():
if 'error' in status: if 'error' in status:
if status['error'].get('message', '') == 'No permission to create project in organization': if status['error'].get('message', '') == 'No permission to create project in organization':
sys.stdout.write(Msg.NO_RIGHTS_GOOGLE_CLOUD_ORGANIZATION) sys.stdout.write(Msg.NO_RIGHTS_GOOGLE_CLOUD_ORGANIZATION)
organization = getGCPOrg(crm, login_hint, login_domain) organization = getGCPOrgId(crm, login_hint, login_domain)
org_policy = callGAPI(crm.organizations(), 'getIamPolicy', org_policy = callGAPI(crm.organizations(), 'getIamPolicy',
resource=organization) resource=organization)
if 'bindings' not in org_policy: if 'bindings' not in org_policy:
@@ -14205,6 +14224,7 @@ def doReport():
showNoActivities = False showNoActivities = False
if usageReports: if usageReports:
includeServices = set() includeServices = set()
kwargs = {}
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if myarg == 'todrive': if myarg == 'todrive':
@@ -14296,6 +14316,8 @@ def doReport():
gmailEventTypes = set(getNumberRangeList()) gmailEventTypes = set(getNumberRangeList())
elif activityReports and myarg == 'userisactor': elif activityReports and myarg == 'userisactor':
mapAdminUsersToFilter = False mapAdminUsersToFilter = False
elif activityReports and myarg == 'includesensitivedata':
kwargs['includeSensitiveData'] = True
elif myarg == 'addcsvdata': elif myarg == 'addcsvdata':
getAddCSVData(addCSVData) getAddCSVData(addCSVData)
elif activityReports and myarg == 'shownoactivities': elif activityReports and myarg == 'shownoactivities':
@@ -14617,7 +14639,7 @@ def doReport():
actorIpAddress=actorIpAddress, orgUnitID=orgUnitId, actorIpAddress=actorIpAddress, orgUnitID=orgUnitId,
startTime=startEndTime.startTime, endTime=startEndTime.endTime, startTime=startEndTime.startTime, endTime=startEndTime.endTime,
eventName=eventName, filters=pfilters, groupIdFilter=groupIdFilter, eventName=eventName, filters=pfilters, groupIdFilter=groupIdFilter,
resourceDetailsFilter=resourceDetailsFilter, maxResults=maxResults) resourceDetailsFilter=resourceDetailsFilter, maxResults=maxResults, **kwargs)
except GAPI.badRequest: except GAPI.badRequest:
if user != 'all': if user != 'all':
entityUnknownWarning(Ent.USER, user, i, count) entityUnknownWarning(Ent.USER, user, i, count)
@@ -72458,7 +72480,7 @@ def _printShowTokens(entityType, users):
throwReasons=[GAPI.PERMISSION_DENIED], throwReasons=[GAPI.PERMISSION_DENIED],
projectId=result['project']) projectId=result['project'])
for ancestor in results.get('ancestor', []): for ancestor in results.get('ancestor', []):
if ancestor.get('resourceId', {}).get('type') == 'organization' and ancestor.get('resourceId', {}).get('id') == GC.Values['GCP_ORG_ID']: if ancestor.get('resourceId', {}).get('type') == 'organization' and ancestor.get('resourceId', {}).get('id') == GC.Values[GC.GCP_ORG_ID]:
result['internal'] = True result['internal'] = True
internal_projects.add(result['project']) internal_projects.add(result['project'])
except GAPI.permissionDenied: except GAPI.permissionDenied:
@@ -72520,8 +72542,7 @@ def _printShowTokens(entityType, users):
crm1 = buildGAPIObject('cloudresourcemanagerv1') crm1 = buildGAPIObject('cloudresourcemanagerv1')
admin_email = _getAdminEmail() admin_email = _getAdminEmail()
admin_domain = getEmailAddressDomain(admin_email) admin_domain = getEmailAddressDomain(admin_email)
if 'GCP_ORG_ID' not in GC.Values: GC.Values[GC.GCP_ORG_ID] = getGCPOrgId(crm, admin_email, admin_domain).split('/')[1]
GC.Values['GCP_ORG_ID'] = getGCPOrg(crm, admin_email, admin_domain).split('/')[1]
fields = ','.join(TOKENS_FIELDS_TITLES) fields = ','.join(TOKENS_FIELDS_TITLES)
i, count, users = getEntityArgument(users) i, count, users = getEntityArgument(users)
for user in users: for user in users:
@@ -79625,30 +79646,11 @@ def printShowTagManagerTags(users):
def printShowTagManagerPermissions(users): def printShowTagManagerPermissions(users):
printShowTagManagerObjects(users, Ent.TAGMANAGER_PERMISSION) printShowTagManagerObjects(users, Ent.TAGMANAGER_PERMISSION)
def getCRMOrgId():
setTrueCustomerId()
_, crm = buildGAPIServiceObject(API.CLOUDRESOURCEMANAGER, None)
results = callGAPI(crm.organizations(), 'search',
query=f'directorycustomerid:{GC.Values[GC.CUSTOMER_ID]}',
pageSize=1, fields='organizations/name')
orgs = results.get('organizations')
if not orgs:
# return nothing and let calling API deal with it
# since caller knows what GCP role would serve best
return None
return orgs[0].get('name')
def CAARoleErrorExit(caa): def CAARoleErrorExit(caa):
sa_email = caa._http.credentials.signer_email sa_email = caa._http.credentials.signer_email
systemErrorExit(NO_SA_ACCESS_CONTEXT_MANAGER_EDITOR_ROLE_RC, systemErrorExit(NO_SA_ACCESS_CONTEXT_MANAGER_EDITOR_ROLE_RC,
f'Please grant service account {sa_email} the Access Context Manager Editor role in your GCP organization.') f'Please grant service account {sa_email} the Access Context Manager Editor role in your GCP organization.')
def normalizeCAALevelName(caa, name):
if name.startswith('accessPolicies/'):
return name
ap_name = getAccessPolicy(caa)
return f'{ap_name}/accessLevels/{name}'
def buildCAAServiceObject(): def buildCAAServiceObject():
_, caa = buildGAPIServiceObject(API.ACCESSCONTEXTMANAGER, None) _, caa = buildGAPIServiceObject(API.ACCESSCONTEXTMANAGER, None)
return caa return caa
@@ -79672,7 +79674,13 @@ def getAccessPolicy(caa=None):
for ap in aps: for ap in aps:
if ap.get('title') == 'Access policy created in Cloud Identity Console': if ap.get('title') == 'Access policy created in Cloud Identity Console':
return ap['name'] return ap['name']
systemErrorExit(ACCESS_POLICY_ERROR_RC, ' Could not find a org level access policy. That is odd.') systemErrorExit(ACCESS_POLICY_ERROR_RC, 'Could not find a org level access policy. That is odd.')
def normalizeCAALevelName(caa, name):
if name.startswith('accessPolicies/'):
return name
ap_name = getAccessPolicy(caa)
return f'{ap_name}/accessLevels/{name}'
CAA_OS_TYPE_MAP = { CAA_OS_TYPE_MAP = {
'desktopmac': 'DESKTOP_MAC', 'desktopmac': 'DESKTOP_MAC',
@@ -80189,6 +80197,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
Cmd.ARG_DRIVEFILEACL: doInfoDriveFileACLs, Cmd.ARG_DRIVEFILEACL: doInfoDriveFileACLs,
Cmd.ARG_DRIVELABEL: doInfoDriveLabels, Cmd.ARG_DRIVELABEL: doInfoDriveLabels,
Cmd.ARG_INSTANCE: doInfoInstance, Cmd.ARG_INSTANCE: doInfoInstance,
Cmd.ARG_GCPORGID: doInfoGCPOrgId,
Cmd.ARG_GROUP: doInfoGroups, Cmd.ARG_GROUP: doInfoGroups,
Cmd.ARG_GROUPMEMBERS: doInfoGroupMembers, Cmd.ARG_GROUPMEMBERS: doInfoGroupMembers,
Cmd.ARG_INBOUNDSSOASSIGNMENT: doInfoInboundSSOAssignment, Cmd.ARG_INBOUNDSSOASSIGNMENT: doInfoInboundSSOAssignment,

View File

@@ -177,6 +177,8 @@ ENFORCE_EXPANSIVE_ACCESS = 'enforce_expansive_access'
EVENT_MAX_RESULTS = 'event_max_results' EVENT_MAX_RESULTS = 'event_max_results'
# Path to extra_args.txt # Path to extra_args.txt
EXTRA_ARGS = 'extra_args' EXTRA_ARGS = 'extra_args'
# Google Cloud Project Organization ID
GCP_ORG_ID = 'gcp_org_id'
# Gmail CSE certificates directory # Gmail CSE certificates directory
GMAIL_CSE_INCERT_DIR = 'gmail_cse_incert_dir' GMAIL_CSE_INCERT_DIR = 'gmail_cse_incert_dir'
# Gmail CSE KACL wrapped key files # Gmail CSE KACL wrapped key files
@@ -403,6 +405,7 @@ Defaults = {
ENABLE_GCLOUD_REAUTH: FALSE, ENABLE_GCLOUD_REAUTH: FALSE,
EVENT_MAX_RESULTS: '250', EVENT_MAX_RESULTS: '250',
EXTRA_ARGS: '', EXTRA_ARGS: '',
GCP_ORG_ID: '',
GMAIL_CSE_INCERT_DIR: '', GMAIL_CSE_INCERT_DIR: '',
GMAIL_CSE_INKEY_DIR: '', GMAIL_CSE_INKEY_DIR: '',
INPUT_DIR: '.', INPUT_DIR: '.',
@@ -577,6 +580,7 @@ VAR_INFO = {
ENABLE_GCLOUD_REAUTH: {VAR_TYPE: TYPE_BOOLEAN}, ENABLE_GCLOUD_REAUTH: {VAR_TYPE: TYPE_BOOLEAN},
EVENT_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 2500)}, EVENT_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 2500)},
EXTRA_ARGS: {VAR_TYPE: TYPE_FILE, VAR_SIGFILE: FN_EXTRA_ARGS_TXT, VAR_SFFT: ('', FN_EXTRA_ARGS_TXT), VAR_ACCESS: os.R_OK}, EXTRA_ARGS: {VAR_TYPE: TYPE_FILE, VAR_SIGFILE: FN_EXTRA_ARGS_TXT, VAR_SFFT: ('', FN_EXTRA_ARGS_TXT), VAR_ACCESS: os.R_OK},
GCP_ORG_ID: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
GMAIL_CSE_INCERT_DIR: {VAR_TYPE: TYPE_DIRECTORY}, GMAIL_CSE_INCERT_DIR: {VAR_TYPE: TYPE_DIRECTORY},
GMAIL_CSE_INKEY_DIR: {VAR_TYPE: TYPE_DIRECTORY}, GMAIL_CSE_INKEY_DIR: {VAR_TYPE: TYPE_DIRECTORY},
INPUT_DIR: {VAR_TYPE: TYPE_DIRECTORY}, INPUT_DIR: {VAR_TYPE: TYPE_DIRECTORY},

View File

@@ -913,6 +913,7 @@ class GamCLArgs():
ARG_FORWARDINGADDRESS = 'forwardingaddress' ARG_FORWARDINGADDRESS = 'forwardingaddress'
ARG_FORWARDINGADDRESSES = 'forwardingaddresses' ARG_FORWARDINGADDRESSES = 'forwardingaddresses'
ARG_GCPFOLDER = 'gcpfolder' ARG_GCPFOLDER = 'gcpfolder'
ARG_GCPORGID = 'gcporgid'
ARG_GCPSERVICEACCOUNT = 'gcpserviceaccount' ARG_GCPSERVICEACCOUNT = 'gcpserviceaccount'
ARG_GMAIL = 'gmail' ARG_GMAIL = 'gmail'
ARG_GMAILPROFILE = 'gmailprofile' ARG_GMAILPROFILE = 'gmailprofile'

View File

@@ -242,6 +242,7 @@ class GamEntity():
FORWARDING_ADDRESS = 'fwda' FORWARDING_ADDRESS = 'fwda'
GCP_FOLDER = 'gcpf' GCP_FOLDER = 'gcpf'
GCP_FOLDER_NAME = 'gcpn' GCP_FOLDER_NAME = 'gcpn'
GCP_ORG_ID = 'gcpo'
GMAIL_PROFILE = 'gmpr' GMAIL_PROFILE = 'gmpr'
GROUP = 'grou' GROUP = 'grou'
GROUP_ALIAS = 'gali' GROUP_ALIAS = 'gali'
@@ -613,6 +614,7 @@ class GamEntity():
FORWARDING_ADDRESS: ['Forwarding Addresses', 'Forwarding Address'], FORWARDING_ADDRESS: ['Forwarding Addresses', 'Forwarding Address'],
GCP_FOLDER: ['GCP Folders', 'GCP Folder'], GCP_FOLDER: ['GCP Folders', 'GCP Folder'],
GCP_FOLDER_NAME: ['GCP Folder Names', 'GCP Folder Name'], GCP_FOLDER_NAME: ['GCP Folder Names', 'GCP Folder Name'],
GCP_ORG_ID: ['GCP Organization ID', 'GCP Organization ID'],
GMAIL_PROFILE: ['Gmail Profile', 'Gmail Profile'], GMAIL_PROFILE: ['Gmail Profile', 'Gmail Profile'],
GROUP: ['Groups', 'Group'], GROUP: ['Groups', 'Group'],
GROUP_ALIAS: ['Group Aliases', 'Group Alias'], GROUP_ALIAS: ['Group Aliases', 'Group Alias'],