Create, update and delete Cloud Identity policies

This commit is contained in:
Ross Scroggs
2026-06-10 15:48:00 -07:00
parent c122a55ad7
commit d745aa65f5
5 changed files with 55 additions and 42 deletions

View File

@@ -4378,6 +4378,14 @@ gam show policies
[group <REMatchPattern>] [ou|org|orgunit <REMatchPattern>] [group <REMatchPattern>] [ou|org|orgunit <REMatchPattern>]
[formatjson] [formatjson]
gam create policy
json <JSONData>
[(ou|orgunit <OrgUnitItem>)|(group <GroupItem>)|(query <String>)]
gam update policy
json <JSONData>
[(ou|orgunit <OrgUnitItem>)|(group <GroupItem>)|(query <String>)]
gam delete policies <CIPolicyNameEntity>
# Inbound SSO # Inbound SSO
<SSOProfileDisplayName> ::= <String> <SSOProfileDisplayName> ::= <String>

View File

@@ -1,3 +1,10 @@
7.46.00
Added commands to create, update and delete Cloud Identity policies for data loss prevention (DLP) rules and detectors.
* See: https://github.com/GAM-team/GAM/wiki/Cloud-Identity-Policies
* See: https://workspaceupdates.googleblog.com/2026/06/introducing-workspace-policy-api-mutate-endpoints-for-DLP.html
7.45.00 7.45.00
Added options `isdisabled [<Boolean>]`, `disabledafter <DateTime>` and `disabledbefore <DateTime>` Added options `isdisabled [<Boolean>]`, `disabledafter <DateTime>` and `disabledbefore <DateTime>`

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.45.00' __version__ = '7.46.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
@@ -38526,37 +38526,25 @@ def _checkPoliciesWithDASA():
systemErrorExit(USAGE_ERROR_RC, systemErrorExit(USAGE_ERROR_RC,
Msg.COMMAND_NOT_COMPATIBLE_WITH_ENABLE_DASA.format(Act.ToPerform().lower(), Cmd.ARG_CIPOLICIES)) Msg.COMMAND_NOT_COMPATIBLE_WITH_ENABLE_DASA.format(Act.ToPerform().lower(), Cmd.ARG_CIPOLICIES))
def _getCIPolicyOrgUnitTarget(cd, myarg, groupEmail):
if groupEmail:
Cmd.Backup()
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'group'))
targetName, targetResource = _getOrgunitsOrgUnitIdPath(cd, getString(Cmd.OB_ORGUNIT_PATH))
return (targetName, targetResource)
def _getCIPolicyGroupTarget(cd, myarg, orgUnit):
if orgUnit:
Cmd.Backup()
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'ou|org|orgunit'))
targetName = getEmailAddress(returnUIDprefix='uid:')
targetResource = f"groups/{convertEmailAddressToUID(targetName, cd, emailType='group')}"
return (targetName, targetResource)
# gam create policy # gam create policy
# json <JSONData> # json <JSONData>
# [(ou|orgunit <OrgUnitItem>)|(group <GroupItem>)] # [(ou|orgunit <OrgUnitItem>)|(group <GroupItem>)|(query <String>)]
# gam update policy # gam update policy
# json <JSONData> # json <JSONData>
# [(ou|orgunit <OrgUnitItem>)|(group <GroupItem>)] # [(ou|orgunit <OrgUnitItem>)|(group <GroupItem>)|(query <String>)]
def doCreateUpdateCIPolicy(): def doCreateUpdateCIPolicy():
_checkPoliciesWithDASA() _checkPoliciesWithDASA()
ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY_BETA) ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY)
cd = buildGAPIObject(API.DIRECTORY) cd = buildGAPIObject(API.DIRECTORY)
updateCmd = Act.Get() == Act.UPDATE updateCmd = Act.Get() == Act.UPDATE
groupEmail = orgUnit = None groupEmail = orgUnit = query = None
checkArgumentPresent('json', True) checkArgumentPresent('json', True)
policy = getJSON(['customer', 'type']) policy = getJSON(['customer', 'type'])
if updateCmd: if updateCmd:
pname = policy.pop('name', None) pname = policy.pop('name', None)
if not pname:
Cmd.Backup()
usageErrorExit(Msg.POLICY_NAME_NOT_FOUND)
else: else:
policy.pop('name', None) policy.pop('name', None)
pname = 'New Policy' pname = 'New Policy'
@@ -38564,6 +38552,8 @@ def doCreateUpdateCIPolicy():
policy['policyQuery'].pop('orgUnitPath', None) policy['policyQuery'].pop('orgUnitPath', None)
policy['policyQuery'].pop('groupEmail', None) policy['policyQuery'].pop('groupEmail', None)
policy['policyQuery'].pop('sortOrder', None) policy['policyQuery'].pop('sortOrder', None)
if 'orgUnit' in policy['policyQuery'] or 'group' in policy['policyQuery']:
policy['policyQuery'].pop('query', None)
if 'setting' in policy: if 'setting' in policy:
if 'value' in policy['setting']: if 'value' in policy['setting']:
policy['setting']['value'].pop('createTime', None) policy['setting']['value'].pop('createTime', None)
@@ -38576,19 +38566,37 @@ def doCreateUpdateCIPolicy():
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if myarg in {'ou', 'org', 'orgunit'}: if myarg in {'ou', 'org', 'orgunit'}:
orgUnit, targetResource = _getCIPolicyOrgUnitTarget(cd, myarg, groupEmail) if groupEmail:
policy.setdefault('policyQuery', {}) Cmd.Backup()
policy['policyQuery'].pop('group', None) usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'group'))
policy['policyQuery']['orgUnit'] = f"orgUnits/{targetResource}" if query:
policy['policyQuery']['query'] = f"entity.org_units.exists(org_unit, org_unit.org_unit_id == orgUnitId('{targetResource}'))" Cmd.Backup()
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'query'))
orgUnit, targetResource = _getOrgunitsOrgUnitIdPath(cd, getString(Cmd.OB_ORGUNIT_PATH))
policy['policyQuery'] = {'orgUnit': f"orgUnits/{targetResource}"}
elif myarg == 'group': elif myarg == 'group':
groupEmail, targetResource = _getCIPolicyGroupTarget(cd, myarg, orgUnit) if orgUnit:
policy.setdefault('policyQuery', {}) Cmd.Backup()
policy['policyQuery'].pop('orgUnit', None) usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'ou|org|orgunit'))
policy['policyQuery']['group'] = f"groups/{targetResource}" if query:
policy['policyQuery']['query'] = f"entity.groups.exists(group, group.group_id == groupId('{targetResource}'))" Cmd.Backup()
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'query'))
groupEmail = getEmailAddress(returnUIDprefix='uid:')
targetResource = f"groups/{convertEmailAddressToUID(groupEmail, cd, emailType='group')}"
policy['policyQuery'] = {'group': f"groups/{targetResource}"}
elif myarg == 'query':
if groupEmail:
Cmd.Backup()
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'group'))
if orgUnit:
Cmd.Backup()
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'ou|org|orgunit'))
query = getString(Cmd.OB_QUERY)
policy['policyQuery'] = {'query': query}
else: else:
unknownArgumentExit() unknownArgumentExit()
if 'policyQuery' not in policy:
missingArgumentExit('ou|org|orgunit|group|query')
policy['customer'] = _getCustomersCustomerIdWithC() policy['customer'] = _getCustomersCustomerIdWithC()
try: try:
if updateCmd: if updateCmd:
@@ -38616,11 +38624,10 @@ def doCreateUpdateCIPolicy():
GAPI.notFound, GAPI.permissionDenied, GAPI.internalError) as e: GAPI.notFound, GAPI.permissionDenied, GAPI.internalError) as e:
entityActionFailedWarning([Ent.POLICY, pname], str(e)) entityActionFailedWarning([Ent.POLICY, pname], str(e))
# gam delete policies <CIPolicyNameEntity> # gam delete policies <CIPolicyNameEntity>
def doDeleteCIPolicies(): def doDeleteCIPolicies():
_checkPoliciesWithDASA() _checkPoliciesWithDASA()
ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY_BETA) ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY)
entityList = getEntityList(Cmd.OB_CIPOLICY_NAME_ENTITY) entityList = getEntityList(Cmd.OB_CIPOLICY_NAME_ENTITY)
checkForExtraneousArguments() checkForExtraneousArguments()
i = 0 i = 0

View File

@@ -51,7 +51,6 @@ CLOUDIDENTITY_INBOUND_SSO = 'cloudidentityinboundsso'
CLOUDIDENTITY_ORGUNITS = 'cloudidentityorgunits' CLOUDIDENTITY_ORGUNITS = 'cloudidentityorgunits'
CLOUDIDENTITY_ORGUNITS_BETA = 'cloudidentityorgunitsbeta' CLOUDIDENTITY_ORGUNITS_BETA = 'cloudidentityorgunitsbeta'
CLOUDIDENTITY_POLICY = 'cloudidentitypolicy' CLOUDIDENTITY_POLICY = 'cloudidentitypolicy'
CLOUDIDENTITY_POLICY_BETA = 'cloudidentitypolicybeta'
CLOUDIDENTITY_USERINVITATIONS = 'cloudidentityuserinvitations' CLOUDIDENTITY_USERINVITATIONS = 'cloudidentityuserinvitations'
CLOUDRESOURCEMANAGER = 'cloudresourcemanager' CLOUDRESOURCEMANAGER = 'cloudresourcemanager'
CLOUDRESOURCEMANAGERV1 = 'cloudresourcemanagerv1' CLOUDRESOURCEMANAGERV1 = 'cloudresourcemanagerv1'
@@ -261,7 +260,6 @@ _INFO = {
CLOUDIDENTITY_ORGUNITS: {'name': 'Cloud Identity API - OrgUnits', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'}, CLOUDIDENTITY_ORGUNITS: {'name': 'Cloud Identity API - OrgUnits', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
CLOUDIDENTITY_ORGUNITS_BETA: {'name': 'Cloud Identity API - OrgUnits Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'}, CLOUDIDENTITY_ORGUNITS_BETA: {'name': 'Cloud Identity API - OrgUnits Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
CLOUDIDENTITY_POLICY: {'name': 'Cloud Identity API - Policy', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'}, CLOUDIDENTITY_POLICY: {'name': 'Cloud Identity API - Policy', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
CLOUDIDENTITY_POLICY_BETA: {'name': 'Cloud Identity API - Policy Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
CLOUDIDENTITY_USERINVITATIONS: {'name': 'Cloud Identity API - User Invitations', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'}, CLOUDIDENTITY_USERINVITATIONS: {'name': 'Cloud Identity API - User Invitations', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
CLOUDRESOURCEMANAGER: {'name': 'Resource Manager API v3', 'version': 'v3', 'v2discovery': True}, CLOUDRESOURCEMANAGER: {'name': 'Resource Manager API v3', 'version': 'v3', 'v2discovery': True},
CLOUDRESOURCEMANAGERV1: {'name': 'Resource Manager API v1', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudresourcemanager'}, CLOUDRESOURCEMANAGERV1: {'name': 'Resource Manager API v1', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudresourcemanager'},
@@ -405,10 +403,6 @@ _CLIENT_SCOPES = [
'subscopes': READONLY, 'subscopes': READONLY,
'roByDefault': True, 'roByDefault': True,
'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'}, 'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
{'name': 'Cloud Identity API - Policy Beta',
'api': CLOUDIDENTITY_POLICY_BETA,
'offByDefault': True,
'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
{'name': 'Cloud Identity API - User Invitations', {'name': 'Cloud Identity API - User Invitations',
'api': CLOUDIDENTITY_USERINVITATIONS, 'api': CLOUDIDENTITY_USERINVITATIONS,
'subscopes': READONLY, 'subscopes': READONLY,
@@ -641,10 +635,6 @@ _SVCACCT_SCOPES = [
# 'subscopes': READONLY, # 'subscopes': READONLY,
# 'roByDefault': True, # 'roByDefault': True,
# 'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'}, # 'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
# {'name': 'Cloud Identity API - Policy Beta',
# 'api': CLOUDIDENTITY_POLICY_BETA,
# 'offByDefault': True,
# 'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
# {'name': 'Cloud Identity User Invitations API', # {'name': 'Cloud Identity User Invitations API',
# 'api': CLOUDIDENTITY_USERINVITATIONS, # 'api': CLOUDIDENTITY_USERINVITATIONS,
# 'subscopes': READONLY, # 'subscopes': READONLY,

View File

@@ -462,6 +462,7 @@ PLEASE_CORRECT_YOUR_SYSTEM_TIME = 'Please correct your system time.'
PLEASE_ENTER_A_OR_M = 'Please enter a or m ...\n' PLEASE_ENTER_A_OR_M = 'Please enter a or m ...\n'
PLEASE_SELECT_ENTITY_TO_PROCESS = '{0} {1} found, please select the correct one to {2} and specify with {3}' PLEASE_SELECT_ENTITY_TO_PROCESS = '{0} {1} found, please select the correct one to {2} and specify with {3}'
PLEASE_SPECIFY_BUILDING_EXACT_CASE_NAME_OR_ID = 'Please specify building by exact case name or ID.' PLEASE_SPECIFY_BUILDING_EXACT_CASE_NAME_OR_ID = 'Please specify building by exact case name or ID.'
POLICY_NAME_NOT_FOUND = 'JSON key "name" not found in JSON data'
PREVIEW_ONLY = 'Preview Only' PREVIEW_ONLY = 'Preview Only'
PRIMARY_EMAIL_DID_NOT_MATCH_PATTERN = 'primaryEmail address did not match pattern: {0}' PRIMARY_EMAIL_DID_NOT_MATCH_PATTERN = 'primaryEmail address did not match pattern: {0}'
PROCESS = 'process' PROCESS = 'process'