This commit is contained in:
Ross Scroggs
2025-08-14 17:32:27 -07:00
parent f0d984c693
commit 65b23113a9
3 changed files with 253 additions and 102 deletions

View File

@@ -4282,19 +4282,19 @@ gam show policies
<SSOProfileItem> ::= <SSOProfileDisplayName>|<SSOProfileName> <SSOProfileItem> ::= <SSOProfileDisplayName>|<SSOProfileName>
<SSOProfileItemList> ::= "<SSOProfileItem>(,<SSOProfileItem>)*" <SSOProfileItemList> ::= "<SSOProfileItem>(,<SSOProfileItem>)*"
gam create inboundssoprofile [name <SSOProfileDisplayName>] gam create inboundssoprofile [saml|oidc] [name <SSOProfileDisplayName>]
[entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>] [entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
[returnnameonly] [returnnameonly]
gam update inboundssoprofile <SSOProfileItem> gam update inboundssoprofile [saml|oidc] <SSOProfileItem>
[entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>] [entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
[returnnameonly] [returnnameonly]
gam delete inboundssoprofile <SSOProfileItem> gam delete inboundssoprofile [saml|oidc] <SSOProfileItem>
gam info inboundssoprofile <SSOProfileItem> gam info inboundssoprofile [all|saml|oidc] <SSOProfileItem>
[formatjson] [formatjson]
gam show inboundssoprofiles gam show inboundssoprofiles [all|saml|oidc]
[formatjson] [formatjson]
gam print inboundssoprofiles [todrive <ToDriveAttribute>*] gam print inboundssoprofiles [all|saml|oidc] [todrive <ToDriveAttribute>*]
[[formatjson [quotechar <Character>]] [[formatjson [quotechar <Character>]]
<SSOCredentialsName> ::= [id:]inboundSamlSsoProfiles/<String>/idpCredentials/<String> <SSOCredentialsName> ::= [id:]inboundSamlSsoProfiles/<String>/idpCredentials/<String>
@@ -4318,10 +4318,14 @@ gam print inboundssocredentials [profile|profiles <SSOProfileItemList>]
orgunits/<String> | orgunits/<String> |
orgunit:<OrgUnitPath> orgunit:<OrgUnitPath>
gam create inboundssoassignment (group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>) gam create inboundssoassignment
(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled) [neverredirect] (group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)
gam update inboundssoassignment [(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)] (mode sso_off)|(mode saml_sso profile <SSOProfileItem>)|(mode oidc_sso profile <SSOProfileName>}|(mode domain_wide_saml_if_enabled)
[(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled)] [neverredirect] [neverredirect]
gam update inboundssoassignment <SSOAssignmentName>
[(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)]
(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)|(mode oidc_sso profile <SSOProfileName>}|(mode domain_wide_saml_if_enabled)
[neverredirect]
gam delete inboundssoassignment <SSOAssignmentSelector> gam delete inboundssoassignment <SSOAssignmentSelector>
gam info inboundssoassignment <SSOAssignmentSelector> gam info inboundssoassignment <SSOAssignmentSelector>

View File

@@ -1,3 +1,11 @@
7.10.05
Added support for Inbound SSO OIDC profiles.
Currently, if you enter `gam select <SectionName>` and nothing else on the command line,
GAM performs no action. Now, it will be treated as if you entered:
`gam select <SectionName> save`
7.18.04 7.18.04
Added commands to display/manage Alert Center Pub/Sub notifications. Added commands to display/manage Alert Center Pub/Sub notifications.

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.18.04' __version__ = '7.18.05'
__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
@@ -3952,6 +3952,12 @@ def SetGlobalVariables():
if checkArgumentPresent(Cmd.SELECT_CMD): if checkArgumentPresent(Cmd.SELECT_CMD):
sectionName = _selectSection() sectionName = _selectSection()
GM.Globals[GM.SECTION] = sectionName # Save section for inner gams GM.Globals[GM.SECTION] = sectionName # Save section for inner gams
# If command line is simply: gam select <SectionName>
# assume save
if not Cmd.ArgumentsRemaining():
GM.Globals[GM.PARSER].set(configparser.DEFAULTSECT, GC.SECTION, sectionName)
_writeGamCfgFile(GM.Globals[GM.PARSER], GM.Globals[GM.GAM_CFG_FILE], Act.SAVE)
else:
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
if checkArgumentPresent('save'): if checkArgumentPresent('save'):
GM.Globals[GM.PARSER].set(configparser.DEFAULTSECT, GC.SECTION, sectionName) GM.Globals[GM.PARSER].set(configparser.DEFAULTSECT, GC.SECTION, sectionName)
@@ -11482,13 +11488,14 @@ def _createOauth2serviceJSON(httpObj, projectInfo, svcAcctInfo, create_key=True)
iam = getAPIService(API.IAM, httpObj) iam = getAPIService(API.IAM, httpObj)
try: try:
service_account = callGAPI(iam.projects().serviceAccounts(), 'create', service_account = callGAPI(iam.projects().serviceAccounts(), 'create',
throwReasons=[GAPI.NOT_FOUND, GAPI.PERMISSION_DENIED, GAPI.ALREADY_EXISTS], throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.NOT_FOUND,
GAPI.PERMISSION_DENIED, GAPI.ALREADY_EXISTS],
name=f'projects/{projectInfo["projectId"]}', name=f'projects/{projectInfo["projectId"]}',
body={'accountId': svcAcctInfo['name'], body={'accountId': svcAcctInfo['name'],
'serviceAccount': {'displayName': svcAcctInfo['displayName'], 'serviceAccount': {'displayName': svcAcctInfo['displayName'],
'description': svcAcctInfo['description']}}) 'description': svcAcctInfo['description']}})
entityActionPerformed([Ent.PROJECT, projectInfo['projectId'], Ent.SVCACCT, service_account['name'].rsplit('/', 1)[-1]]) entityActionPerformed([Ent.PROJECT, projectInfo['projectId'], Ent.SVCACCT, service_account['name'].rsplit('/', 1)[-1]])
except (GAPI.notFound, GAPI.permissionDenied) as e: except (GAPI.failedPrecondition, GAPI.notFound, GAPI.permissionDenied) as e:
entityActionFailedWarning([Ent.PROJECT, projectInfo['projectId']], str(e)) entityActionFailedWarning([Ent.PROJECT, projectInfo['projectId']], str(e))
return False return False
except GAPI.alreadyExists as e: except GAPI.alreadyExists as e:
@@ -46393,9 +46400,30 @@ def checkCIUserIsInvitable(users):
return return
csvPF.writeCSVfile('Invitable Users') csvPF.writeCSVfile('Invitable Users')
INBOUNDSSO_INPUT_MODE_CHOICE_MAP = {
'saml': 'saml',
'samlsso': 'saml',
'oidc': 'oidc',
'oidcsso': 'oidc',
}
INBOUNDSSO_OUTPUT_MODE_CHOICE_MAP = {
'all': 'all',
'saml': 'saml',
'samlsso': 'saml',
'oidc': 'oidc',
'oidcsso': 'oidc',
}
INBOUNDSSO_ALL_SAML = {'all', 'saml'}
INBOUNDSSO_ALL_OIDC = {'all', 'oidc'}
INBOUNDSSO_MODE_CHOICE_MAP = { INBOUNDSSO_MODE_CHOICE_MAP = {
'ssooff': 'SSO_OFF', 'ssooff': 'SSO_OFF',
'saml': 'SAML_SSO',
'samlsso': 'SAML_SSO', 'samlsso': 'SAML_SSO',
'oidc': 'OIDC_SSO',
'oidcsso': 'OIDC_SSO',
'domainwidesamlifenabled': 'DOMAIN_WIDE_SAML_IF_ENABLED' 'domainwidesamlifenabled': 'DOMAIN_WIDE_SAML_IF_ENABLED'
} }
@@ -46405,29 +46433,49 @@ def getCIOrgunitID(cd, orgunit):
ou_id = ou_id[3:] ou_id = ou_id[3:]
return f'orgUnits/{ou_id}' return f'orgUnits/{ou_id}'
def _getInboundSSOProfiles(ci): def _getInboundSSOProfiles(ci, mode):
customer = normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID]) customer = normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID])
profiles = []
if mode in INBOUNDSSO_ALL_SAML:
try: try:
return callGAPIpages(ci.inboundSamlSsoProfiles(), 'list', 'inboundSamlSsoProfiles', profiles.extend(callGAPIpages(ci.inboundSamlSsoProfiles(), 'list', 'inboundSamlSsoProfiles',
throwReasons=GAPI.CISSO_LIST_THROW_REASONS, throwReasons=GAPI.CISSO_LIST_THROW_REASONS,
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
bailOnInternalError=True, bailOnInternalError=True,
filter=f'customer=="{customer}"') filter=f'customer=="{customer}"'))
except (GAPI.notFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, except (GAPI.notFound, GAPI.domainNotFound, GAPI.domainCannotUseApis,
GAPI.forbidden, GAPI.badRequest, GAPI.invalid, GAPI.forbidden, GAPI.badRequest, GAPI.invalid,
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e: GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning([Ent.INBOUND_SSO_PROFILE, customer], str(e)) entityActionFailedWarning([Ent.INBOUND_SSO_PROFILE, customer], str(e))
return [] if mode in INBOUNDSSO_ALL_OIDC:
try:
profiles.extend(callGAPIpages(ci.inboundOidcSsoProfiles(), 'list', 'inboundOidcSsoProfiles',
throwReasons=GAPI.CISSO_LIST_THROW_REASONS,
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
bailOnInternalError=True,
filter=f'customer=="{customer}"'))
except (GAPI.notFound, GAPI.domainNotFound, GAPI.domainCannotUseApis,
GAPI.forbidden, GAPI.badRequest, GAPI.invalid,
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning([Ent.INBOUND_SSO_PROFILE, customer], str(e))
return profiles
def _convertInboundSSOProfileDisplaynameToName(ci=None, displayName=''): def _convertInboundSSOProfileDisplaynameToName(ci, mode, displayName='',
entityType=Ent.INBOUND_SSO_PROFILE):
if displayName.lower().startswith('id:') or displayName.lower().startswith('uid:'): if displayName.lower().startswith('id:') or displayName.lower().startswith('uid:'):
displayName = displayName.split(':', 1)[1] displayName = displayName.split(':', 1)[1]
if mode == 'all':
if not (displayName.startswith('inboundSamlSsoProfiles/') and
displayName.startswith('inboundOidcSsoProfiles/')):
displayName = f'inboundSamlSsoProfiles/{displayName}'
elif mode == 'saml':
if not displayName.startswith('inboundSamlSsoProfiles/'): if not displayName.startswith('inboundSamlSsoProfiles/'):
displayName = f'inboundSamlSsoProfiles/{displayName}' displayName = f'inboundSamlSsoProfiles/{displayName}'
else:
if not displayName.startswith('inboundOidcSsoProfiles/'):
displayName = f'inboundOidcSsoProfiles/{displayName}'
return displayName return displayName
if not ci: profiles = _getInboundSSOProfiles(ci, mode)
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
profiles = _getInboundSSOProfiles(ci)
matches = [] matches = []
for profile in profiles: for profile in profiles:
if displayName.lower() == profile.get('displayName', '').lower(): if displayName.lower() == profile.get('displayName', '').lower():
@@ -46435,14 +46483,17 @@ def _convertInboundSSOProfileDisplaynameToName(ci=None, displayName=''):
if len(matches) == 1: if len(matches) == 1:
return matches[0]['name'] return matches[0]['name']
if len(matches) == 0: if len(matches) == 0:
usageErrorExit(Msg.NO_SSO_PROFILE_MATCHES.format(displayName)) errMsg = Msg.NO_SSO_PROFILE_MATCHES.format(displayName)
else:
errMsg = Msg.MULTIPLE_SSO_PROFILES_MATCH.format(displayName) errMsg = Msg.MULTIPLE_SSO_PROFILES_MATCH.format(displayName)
for m in matches: for m in matches:
errMsg += f' {m["name"]} {m["displayName"]}\n' errMsg += f' {m["name"]} {m["displayName"]}\n'
usageErrorExit(errMsg) entityActionFailedWarning([entityType, None], errMsg)
return None
def _getInboundSSOProfileArguments(body): def _getInboundSSOProfileArguments(body, mode):
returnNameOnly = False returnNameOnly = False
if mode == 'saml':
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if myarg == 'name': if myarg == 'name':
@@ -46459,6 +46510,23 @@ def _getInboundSSOProfileArguments(body):
returnNameOnly = True returnNameOnly = True
else: else:
unknownArgumentExit() unknownArgumentExit()
else:
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if myarg == 'name':
body['displayName'] = getString(Cmd.OB_STRING)
elif myarg == 'issueruri':
body.setdefault('idpConfig', {})['issuerUri'] = getString(Cmd.OB_STRING)
elif myarg == 'changepasswordurl':
body.setdefault('idpConfig', {})['changePasswordUri'] = getString(Cmd.OB_STRING)
elif myarg == 'clientid':
body.setdefault('rpConfig', {})['clientId'] = getString(Cmd.OB_STRING)
elif myarg == 'clientsecret':
body.setdefault('rpConfig', {})['clientSecret'] = getString(Cmd.OB_STRING)
elif myarg == 'returnnameonly':
returnNameOnly = True
else:
unknownArgumentExit()
return (returnNameOnly, body) return (returnNameOnly, body)
def _showInboundSSOProfile(profile, FJQC, i=0, count=0): def _showInboundSSOProfile(profile, FJQC, i=0, count=0):
@@ -46489,18 +46557,24 @@ def _processInboundSSOProfileResult(result, returnNameOnly, kvlist, function):
else: else:
writeStdout('inProgress\n') writeStdout('inProgress\n')
# gam create inboundssoprofile [name <SSOProfileName>] def _getInboundSSOModeService(ci):
mode = getChoice(INBOUNDSSO_INPUT_MODE_CHOICE_MAP, defaultChoice='saml', mapChoice=True)
service = ci.inboundSamlSsoProfiles() if mode == 'saml' else ci.inboundOidcSsoProfiles()
return (mode, service)
# gam create inboundssoprofile [saml|oidc] [name <SSOProfileName>]
# [entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>] # [entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
# [returnnameonly] # [returnnameonly]
def doCreateInboundSSOProfile(): def doCreateInboundSSOProfile():
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO) ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
mode, service = _getInboundSSOModeService(ci)
body = {'customer': normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID]), body = {'customer': normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID]),
'displayName': 'SSO Profile' 'displayName': 'SSO Profile'
} }
returnNameOnly, body = _getInboundSSOProfileArguments(body) returnNameOnly, body = _getInboundSSOProfileArguments(body, mode)
kvlist = [Ent.INBOUND_SSO_PROFILE, body['displayName']] kvlist = [Ent.INBOUND_SSO_PROFILE, body['displayName']]
try: try:
result = callGAPI(ci.inboundSamlSsoProfiles(), 'create', result = callGAPI(service, 'create',
throwReasons=GAPI.CISSO_CREATE_THROW_REASONS, throwReasons=GAPI.CISSO_CREATE_THROW_REASONS,
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
bailOnInternalError=True, bailOnInternalError=True,
@@ -46511,16 +46585,19 @@ def doCreateInboundSSOProfile():
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e: GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning(kvlist, str(e)) entityActionFailedWarning(kvlist, str(e))
# gam update inboundssoprofile <SSOProfileItem> # gam update inboundssoprofile [saml|oidc] <SSOProfileItem>
# [entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>] # [entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
# [returnnameonly] # [returnnameonly]
def doUpdateInboundSSOProfile(): def doUpdateInboundSSOProfile():
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO) ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
name = _convertInboundSSOProfileDisplaynameToName(ci, getString(Cmd.OB_STRING)) mode, service = _getInboundSSOModeService(ci)
returnNameOnly, body = _getInboundSSOProfileArguments({}) name = _convertInboundSSOProfileDisplaynameToName(ci, mode, getString(Cmd.OB_STRING))
if not name:
return
returnNameOnly, body = _getInboundSSOProfileArguments({}, mode)
kvlist = [Ent.INBOUND_SSO_PROFILE, name] kvlist = [Ent.INBOUND_SSO_PROFILE, name]
try: try:
result = callGAPI(ci.inboundSamlSsoProfiles(), 'patch', result = callGAPI(service, 'patch',
throwReasons=GAPI.CISSO_UPDATE_THROW_REASONS, throwReasons=GAPI.CISSO_UPDATE_THROW_REASONS,
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
bailOnInternalError=True, bailOnInternalError=True,
@@ -46533,14 +46610,17 @@ def doUpdateInboundSSOProfile():
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e: GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning(kvlist, str(e)) entityActionFailedWarning(kvlist, str(e))
# gam delete inboundssoprofile <SSOProfileItem> # gam delete inboundssoprofile [saml|oidc] <SSOProfileItem>
def doDeleteInboundSSOProfile(): def doDeleteInboundSSOProfile():
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO) ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
name = _convertInboundSSOProfileDisplaynameToName(ci, getString(Cmd.OB_STRING)) mode, service = _getInboundSSOModeService(ci)
name = _convertInboundSSOProfileDisplaynameToName(ci, mode, getString(Cmd.OB_STRING))
if not name:
return
checkForExtraneousArguments() checkForExtraneousArguments()
kvlist = [Ent.INBOUND_SSO_PROFILE, name] kvlist = [Ent.INBOUND_SSO_PROFILE, name]
try: try:
result = callGAPI(ci.inboundSamlSsoProfiles(), 'delete', result = callGAPI(service, 'delete',
throwReasons=GAPI.CISSO_UPDATE_THROW_REASONS, throwReasons=GAPI.CISSO_UPDATE_THROW_REASONS,
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
bailOnInternalError=True, bailOnInternalError=True,
@@ -46553,8 +46633,10 @@ def doDeleteInboundSSOProfile():
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e: GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning(kvlist, str(e)) entityActionFailedWarning(kvlist, str(e))
def _getInboundSSOProfile(ci, name): def _getInboundSSOProfileByName(ci, mode, name):
notFound = False
kvlist = [Ent.INBOUND_SSO_PROFILE, name] kvlist = [Ent.INBOUND_SSO_PROFILE, name]
if mode in INBOUNDSSO_ALL_SAML:
try: try:
return callGAPI(ci.inboundSamlSsoProfiles(), 'get', return callGAPI(ci.inboundSamlSsoProfiles(), 'get',
throwReasons=GAPI.CISSO_GET_THROW_REASONS, throwReasons=GAPI.CISSO_GET_THROW_REASONS,
@@ -46562,24 +46644,43 @@ def _getInboundSSOProfile(ci, name):
bailOnInternalError=True, bailOnInternalError=True,
name=name) name=name)
except GAPI.notFound: except GAPI.notFound:
entityActionFailedWarning(kvlist, Msg.DOES_NOT_EXIST) notFound = True
except (GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden, except (GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden,
GAPI.badRequest, GAPI.invalid, GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e: GAPI.badRequest, GAPI.invalid, GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning(kvlist, str(e)) entityActionFailedWarning(kvlist, str(e))
if mode in INBOUNDSSO_ALL_OIDC:
try:
return callGAPI(ci.inboundOidcSsoProfiles(), 'get',
throwReasons=GAPI.CISSO_GET_THROW_REASONS,
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
bailOnInternalError=True,
name=name)
except GAPI.notFound:
notFound = True
except (GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden,
GAPI.badRequest, GAPI.invalid, GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning(kvlist, str(e))
if notFound:
entityActionFailedWarning(kvlist, Msg.DOES_NOT_EXIST)
return None return None
# gam info inboundssoprofile <SSOProfileItem> [formatjson] # gam info inboundssoprofile [all|saml|oidc] <SSOProfileItem> [formatjson]
def doInfoInboundSSOProfile(): def doInfoInboundSSOProfile():
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO) ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
name = _convertInboundSSOProfileDisplaynameToName(ci, getString(Cmd.OB_STRING)) mode = getChoice(INBOUNDSSO_OUTPUT_MODE_CHOICE_MAP, defaultChoice='all', mapChoice=True)
name = getString(Cmd.OB_STRING)
FJQC = FormatJSONQuoteChar(formatJSONOnly=True) FJQC = FormatJSONQuoteChar(formatJSONOnly=True)
profile = _getInboundSSOProfile(ci, name) name = _convertInboundSSOProfileDisplaynameToName(ci, mode, name)
if not name:
return
mode = 'saml' if name.startswith('inboundSamlSsoProfiles/') else 'oidc'
profile = _getInboundSSOProfileByName(ci, mode, name)
if profile: if profile:
_showInboundSSOProfile(profile, FJQC) _showInboundSSOProfile(profile, FJQC)
# gam show inboundssoprofile # gam show inboundssoprofile [all|saml|oidc]
# [formatjson] # [formatjson]
# gam print inboundssoprofile [todrive <ToDriveAttribute>*] # gam print inboundssoprofile [all|saml|oidc] [todrive <ToDriveAttribute>*]
# [[formatjson [quotechar <Character>]] # [[formatjson [quotechar <Character>]]
def doPrintShowInboundSSOProfiles(): def doPrintShowInboundSSOProfiles():
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO) ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
@@ -46587,6 +46688,7 @@ def doPrintShowInboundSSOProfiles():
csvPF = CSVPrintFile(['name']) if Act.csvFormat() else None csvPF = CSVPrintFile(['name']) if Act.csvFormat() else None
FJQC = FormatJSONQuoteChar(csvPF) FJQC = FormatJSONQuoteChar(csvPF)
cfilter = f'customer=="{customer}"' cfilter = f'customer=="{customer}"'
mode = getChoice(INBOUNDSSO_OUTPUT_MODE_CHOICE_MAP, defaultChoice='all', mapChoice=True)
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if csvPF and myarg == 'todrive': if csvPF and myarg == 'todrive':
@@ -46595,7 +46697,7 @@ def doPrintShowInboundSSOProfiles():
FJQC.GetFormatJSONQuoteChar(myarg, True) FJQC.GetFormatJSONQuoteChar(myarg, True)
if csvPF: if csvPF:
printGettingAllAccountEntities(Ent.INBOUND_SSO_PROFILE, cfilter) printGettingAllAccountEntities(Ent.INBOUND_SSO_PROFILE, cfilter)
profiles = _getInboundSSOProfiles(ci) profiles = _getInboundSSOProfiles(ci, mode)
if not csvPF: if not csvPF:
count = len(profiles) count = len(profiles)
if not FJQC.formatJSON: if not FJQC.formatJSON:
@@ -46665,6 +46767,7 @@ def _processInboundSSOCredentialsResult(result, kvlist, function):
# (pemfile <FileName>)|(generatekey [keysize 1024|2048|4096]) [replaceolddest] # (pemfile <FileName>)|(generatekey [keysize 1024|2048|4096]) [replaceolddest]
def doCreateInboundSSOCredential(): def doCreateInboundSSOCredential():
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO) ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
mode = 'saml'
profile = None profile = None
generateKey = replaceOldest = False generateKey = replaceOldest = False
keySize = 2048 keySize = 2048
@@ -46672,7 +46775,11 @@ def doCreateInboundSSOCredential():
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if myarg == 'profile': if myarg == 'profile':
profile = _convertInboundSSOProfileDisplaynameToName(ci, getString(Cmd.OB_STRING)) profile = _convertInboundSSOProfileDisplaynameToName(ci, mode,
getString(Cmd.OB_STRING),
Ent.INBOUND_SSO_CREDENTIALS)
if not profile:
return
elif myarg == 'pemfile': elif myarg == 'pemfile':
pemData = readFile(getString(Cmd.OB_FILE_NAME)) pemData = readFile(getString(Cmd.OB_FILE_NAME))
elif myarg == 'generatekey': elif myarg == 'generatekey':
@@ -46776,15 +46883,24 @@ def doPrintShowInboundSSOCredentials():
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO) ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
csvPF = CSVPrintFile(['name']) if Act.csvFormat() else None csvPF = CSVPrintFile(['name']) if Act.csvFormat() else None
FJQC = FormatJSONQuoteChar(csvPF) FJQC = FormatJSONQuoteChar(csvPF)
mode = 'saml'
profiles = [] profiles = []
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if myarg in {'profile', 'profiles'}: if myarg in {'profile', 'profiles'}:
profiles = [_convertInboundSSOProfileDisplaynameToName(ci, profile) for profile in getString(Cmd.OB_STRING_LIST).split(',')] errors = 0
for profile in getEntityList(Cmd.OB_STRING_LIST, shlexSplit=True):
name = _convertInboundSSOProfileDisplaynameToName(ci, mode, profile, Ent.INBOUND_SSO_CREDENTIALS)
if name:
profiles.append(name)
else:
errors += 1
if errors:
return
else: else:
FJQC.GetFormatJSONQuoteChar(myarg, True) FJQC.GetFormatJSONQuoteChar(myarg, True)
if not profiles: if not profiles:
profiles = [p['name'] for p in _getInboundSSOProfiles(ci)] profiles = [p['name'] for p in _getInboundSSOProfiles(ci, mode)]
count = len(profiles) count = len(profiles)
i = 0 i = 0
for profile in profiles: for profile in profiles:
@@ -46877,6 +46993,7 @@ def _getInboundSSOAssignmentByTarget(ci, cd, target):
usageErrorExit(Msg.NO_SSO_PROFILE_ASSIGNED.format(targetType, target)) usageErrorExit(Msg.NO_SSO_PROFILE_ASSIGNED.format(targetType, target))
def _getInboundSSOAssignmentArguments(ci, cd, body): def _getInboundSSOAssignmentArguments(ci, cd, body):
mode = None
rank = 0 rank = 0
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
@@ -46884,9 +47001,19 @@ def _getInboundSSOAssignmentArguments(ci, cd, body):
rank = getInteger(minVal=1) rank = getInteger(minVal=1)
elif myarg == 'mode': elif myarg == 'mode':
body['ssoMode'] = getChoice(INBOUNDSSO_MODE_CHOICE_MAP, mapChoice=True) body['ssoMode'] = getChoice(INBOUNDSSO_MODE_CHOICE_MAP, mapChoice=True)
elif myarg == 'profile': if body['ssoMode'] == 'SAML_SSO':
body['samlSsoInfo'] = {'inboundSamlSsoProfile': mode = 'saml'
_convertInboundSSOProfileDisplaynameToName(ci, getString(Cmd.OB_STRING))} profile = 'inboundSamlSsoProfile'
elif body['ssoMode'] == 'OIDC_SSO':
mode = 'oidc'
profile = 'inboundOidcSsoProfile'
elif mode and myarg == 'profile':
name = _convertInboundSSOProfileDisplaynameToName(ci, mode,
getString(Cmd.OB_STRING),
Ent.INBOUND_SSO_ASSIGNMENT)
if not name:
return None
body['samlSsoInfo'] = {profile: name}
elif myarg == 'neverredirect': elif myarg == 'neverredirect':
body['signInBehavior'] = {'redirectCondition': 'NEVER'} body['signInBehavior'] = {'redirectCondition': 'NEVER'}
elif myarg == 'group': elif myarg == 'group':
@@ -46897,7 +47024,7 @@ def _getInboundSSOAssignmentArguments(ci, cd, body):
unknownArgumentExit() unknownArgumentExit()
if 'ssoMode' not in body: if 'ssoMode' not in body:
missingArgumentExit('mode') missingArgumentExit('mode')
if body['ssoMode'] == 'SAML_SSO' and 'samlSsoInfo' not in body: if mode and 'samlSsoInfo' not in body:
missingArgumentExit('profile') missingArgumentExit('profile')
if 'targetGroup' in body: if 'targetGroup' in body:
if 'targetOrgUnit' in body: if 'targetOrgUnit' in body:
@@ -46935,13 +47062,17 @@ def _processInboundSSOAssignmentResult(result, kvlist, ci, cd, function):
else: else:
entityActionPerformedMessage(kvlist, Msg.ACTION_IN_PROGRESS.format(f'{function} inboundssoassignment')) entityActionPerformedMessage(kvlist, Msg.ACTION_IN_PROGRESS.format(f'{function} inboundssoassignment'))
# gam create inboundssoassignment (group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>) # gam create inboundssoassignment
# (mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled) [neverredirect] # (group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)
# (mode sso_off)|(mode saml_sso profile <SSOProfileItem>)|(mode oidc_sso profile <SSOProfileName>}|(mode domain_wide_saml_if_enabled)
# [neverredirect]
def doCreateInboundSSOAssignment(): def doCreateInboundSSOAssignment():
cd = buildGAPIObject(API.DIRECTORY) cd = buildGAPIObject(API.DIRECTORY)
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO) ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
body = {'customer': normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID])} body = {'customer': normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID])}
body = _getInboundSSOAssignmentArguments(ci, cd, body) body = _getInboundSSOAssignmentArguments(ci, cd, body)
if not body:
return
kvlist = [Ent.INBOUND_SSO_ASSIGNMENT, body['customer']] kvlist = [Ent.INBOUND_SSO_ASSIGNMENT, body['customer']]
try: try:
result = callGAPI(ci.inboundSsoAssignments(), 'create', result = callGAPI(ci.inboundSsoAssignments(), 'create',
@@ -46955,8 +47086,10 @@ def doCreateInboundSSOAssignment():
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e: GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning(kvlist, str(e)) entityActionFailedWarning(kvlist, str(e))
# gam update inboundssoassignment [(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)] # gam update inboundssoassignment <SSOAssignmentName>
# [(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled)] [neverredirect] # [(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)]
# (mode sso_off)|(mode saml_sso profile <SSOProfileItem>)|(mode oidc_sso profile <SSOProfileName>}|(mode domain_wide_saml_if_enabled)
# [neverredirect]
def doUpdateInboundSSOAssignment(): def doUpdateInboundSSOAssignment():
cd = buildGAPIObject(API.DIRECTORY) cd = buildGAPIObject(API.DIRECTORY)
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO) ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
@@ -47013,14 +47146,20 @@ def doInfoInboundSSOAssignment():
return return
name = assignment.get('samlSsoInfo', {}).get('inboundSamlSsoProfile') name = assignment.get('samlSsoInfo', {}).get('inboundSamlSsoProfile')
if name: if name:
profile = _getInboundSSOProfile(ci, name) profile = _getInboundSSOProfileByName(ci, 'saml', name)
if profile: if profile:
assignment['samlSsoInfo']['inboundSamlSsoProfile'] = profile assignment['samlSsoInfo']['inboundSamlSsoProfile'] = profile
else:
name = assignment.get('oidcSsoInfo', {}).get('inboundOidcSsoProfile')
if name:
profile = _getInboundSSOProfileByName(ci, 'oidc', name)
if profile:
assignment['oidcSsoInfo']['inboundOidcSsoProfile'] = profile
_showInboundSSOAssignment(assignment, FJQC, ci, cd) _showInboundSSOAssignment(assignment, FJQC, ci, cd)
# gam show inboundssoassignment # gam show inboundssoassignments
# [formatjson] # [formatjson]
# gam print inboundssoassignment [todrive <ToDriveAttribute>*] # gam print inboundssoassignments [todrive <ToDriveAttribute>*]
# [[formatjson [quotechar <Character>]] # [[formatjson [quotechar <Character>]]
def doPrintShowInboundSSOAssignments(): def doPrintShowInboundSSOAssignments():
cd = buildGAPIObject(API.DIRECTORY) cd = buildGAPIObject(API.DIRECTORY)