mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-29 18:31:38 +00:00
Initial support for delegated admin service accounts (DASA)
Google now allows GCP service accounts to be granted delegated admin status for a G Suite domain. To use this, admins can grant the service account email address delegated admin rights in the admin console and then set some environment variables for GAM to use: OAUTHFILE=oauth2service.json GA_DOMAIN=example.com # your primary domain name in Google CUSTOMER_ID=1d80dfc # admin.google.com > Account > Account settings > Customer ID
This commit is contained in:
@@ -64,6 +64,7 @@ from gam.gapi.directory import mobiledevices as gapi_directory_mobiledevices
|
|||||||
from gam.gapi.directory import orgunits as gapi_directory_orgunits
|
from gam.gapi.directory import orgunits as gapi_directory_orgunits
|
||||||
from gam.gapi.directory import privileges as gapi_directory_privileges
|
from gam.gapi.directory import privileges as gapi_directory_privileges
|
||||||
from gam.gapi.directory import resource as gapi_directory_resource
|
from gam.gapi.directory import resource as gapi_directory_resource
|
||||||
|
from gam.gapi.directory import roles as gapi_directory_roles
|
||||||
from gam.gapi import siteverification as gapi_siteverification
|
from gam.gapi import siteverification as gapi_siteverification
|
||||||
from gam.gapi import errors as gapi_errors
|
from gam.gapi import errors as gapi_errors
|
||||||
from gam.gapi import reports as gapi_reports
|
from gam.gapi import reports as gapi_reports
|
||||||
@@ -843,14 +844,14 @@ def getOauth2TxtStorageCredentials():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def getValidOauth2TxtCredentials(force_refresh=False):
|
def getValidOauth2TxtCredentials(force_refresh=False, api=None):
|
||||||
"""Gets OAuth2 credentials which are guaranteed to be fresh and valid."""
|
"""Gets OAuth2 credentials which are guaranteed to be fresh and valid."""
|
||||||
try:
|
try:
|
||||||
credentials = auth.get_admin_credentials()
|
credentials = auth.get_admin_credentials(api)
|
||||||
except gam.auth.oauth.InvalidCredentialsFileError:
|
except gam.auth.oauth.InvalidCredentialsFileError:
|
||||||
doRequestOAuth() # Make a new request which should store new creds.
|
doRequestOAuth() # Make a new request which should store new creds.
|
||||||
return getValidOauth2TxtCredentials(force_refresh=force_refresh)
|
return getValidOauth2TxtCredentials(force_refresh=force_refresh,
|
||||||
|
api=api)
|
||||||
if credentials.expired or force_refresh:
|
if credentials.expired or force_refresh:
|
||||||
request = transport.create_request()
|
request = transport.create_request()
|
||||||
credentials.refresh(request)
|
credentials.refresh(request)
|
||||||
@@ -922,7 +923,7 @@ def getService(api, http):
|
|||||||
|
|
||||||
def buildGAPIObject(api):
|
def buildGAPIObject(api):
|
||||||
GM_Globals[GM_CURRENT_API_USER] = None
|
GM_Globals[GM_CURRENT_API_USER] = None
|
||||||
credentials = getValidOauth2TxtCredentials()
|
credentials = getValidOauth2TxtCredentials(api=getAPIVersion(api)[0])
|
||||||
credentials.user_agent = GAM_INFO
|
credentials.user_agent = GAM_INFO
|
||||||
http = transport.AuthorizedHttp(
|
http = transport.AuthorizedHttp(
|
||||||
credentials, transport.create_http(cache=GM_Globals[GM_CACHE_DIR]))
|
credentials, transport.create_http(cache=GM_Globals[GM_CACHE_DIR]))
|
||||||
@@ -1647,7 +1648,8 @@ def doCreateAdmin():
|
|||||||
', '.join(['customer', 'org_unit']),
|
', '.join(['customer', 'org_unit']),
|
||||||
body['scopeType'])
|
body['scopeType'])
|
||||||
if body['scopeType'] == 'ORG_UNIT':
|
if body['scopeType'] == 'ORG_UNIT':
|
||||||
orgUnit, orgUnitId = gapi_directory_orgunits.getOrgUnitId(sys.argv[6], cd)
|
orgUnit, orgUnitId = gapi_directory_orgunits.getOrgUnitId(
|
||||||
|
sys.argv[6], cd)
|
||||||
body['orgUnitId'] = orgUnitId[3:]
|
body['orgUnitId'] = orgUnitId[3:]
|
||||||
scope = f'ORG_UNIT {orgUnit}'
|
scope = f'ORG_UNIT {orgUnit}'
|
||||||
else:
|
else:
|
||||||
@@ -1659,37 +1661,6 @@ def doCreateAdmin():
|
|||||||
body=body)
|
body=body)
|
||||||
|
|
||||||
|
|
||||||
def doPrintAdminRoles():
|
|
||||||
cd = buildGAPIObject('directory')
|
|
||||||
todrive = False
|
|
||||||
titles = [
|
|
||||||
'roleId', 'roleName', 'roleDescription', 'isSuperAdminRole',
|
|
||||||
'isSystemRole'
|
|
||||||
]
|
|
||||||
fields = f'nextPageToken,items({",".join(titles)})'
|
|
||||||
csvRows = []
|
|
||||||
i = 3
|
|
||||||
while i < len(sys.argv):
|
|
||||||
myarg = sys.argv[i].lower()
|
|
||||||
if myarg == 'todrive':
|
|
||||||
todrive = True
|
|
||||||
i += 1
|
|
||||||
else:
|
|
||||||
controlflow.invalid_argument_exit(sys.argv[i],
|
|
||||||
'gam print adminroles')
|
|
||||||
roles = gapi.get_all_pages(cd.roles(),
|
|
||||||
'list',
|
|
||||||
'items',
|
|
||||||
customer=GC_Values[GC_CUSTOMER_ID],
|
|
||||||
fields=fields)
|
|
||||||
for role in roles:
|
|
||||||
role_attrib = {}
|
|
||||||
for key, value in list(role.items()):
|
|
||||||
role_attrib[key] = value
|
|
||||||
csvRows.append(role_attrib)
|
|
||||||
display.write_csv_file(csvRows, titles, 'Admin Roles', todrive)
|
|
||||||
|
|
||||||
|
|
||||||
def doPrintAdmins():
|
def doPrintAdmins():
|
||||||
cd = buildGAPIObject('directory')
|
cd = buildGAPIObject('directory')
|
||||||
roleId = None
|
roleId = None
|
||||||
@@ -1731,7 +1702,9 @@ def doPrintAdmins():
|
|||||||
admin_attrib['role'] = role_from_roleid(value)
|
admin_attrib['role'] = role_from_roleid(value)
|
||||||
elif key == 'orgUnitId':
|
elif key == 'orgUnitId':
|
||||||
value = f'id:{value}'
|
value = f'id:{value}'
|
||||||
admin_attrib['orgUnit'] = gapi_directory_orgunits.orgunit_from_orgunitid(value)
|
admin_attrib[
|
||||||
|
'orgUnit'] = gapi_directory_orgunits.orgunit_from_orgunitid(
|
||||||
|
value)
|
||||||
admin_attrib[key] = value
|
admin_attrib[key] = value
|
||||||
csvRows.append(admin_attrib)
|
csvRows.append(admin_attrib)
|
||||||
display.write_csv_file(csvRows, titles, 'Admins', todrive)
|
display.write_csv_file(csvRows, titles, 'Admins', todrive)
|
||||||
@@ -6517,7 +6490,8 @@ def getUserAttributes(i, cd, updateCmd):
|
|||||||
body['agreedToTerms'] = getBoolean(sys.argv[i + 1], myarg)
|
body['agreedToTerms'] = getBoolean(sys.argv[i + 1], myarg)
|
||||||
i += 2
|
i += 2
|
||||||
elif myarg in ['org', 'ou']:
|
elif myarg in ['org', 'ou']:
|
||||||
body['orgUnitPath'] = gapi_directory_orgunits.getOrgUnitItem(sys.argv[i + 1], pathOnly=True)
|
body['orgUnitPath'] = gapi_directory_orgunits.getOrgUnitItem(
|
||||||
|
sys.argv[i + 1], pathOnly=True)
|
||||||
i += 2
|
i += 2
|
||||||
elif myarg in ['language', 'languages']:
|
elif myarg in ['language', 'languages']:
|
||||||
i += 1
|
i += 1
|
||||||
@@ -8799,6 +8773,7 @@ def doGetUserInfo(user_email=None):
|
|||||||
'list',
|
'list',
|
||||||
'groups',
|
'groups',
|
||||||
userKey=user_email,
|
userKey=user_email,
|
||||||
|
customer=GC_Values[GC_CUSTOMER_ID],
|
||||||
fields='groups(name,email),nextPageToken',
|
fields='groups(name,email),nextPageToken',
|
||||||
throw_reasons=throw_reasons)
|
throw_reasons=throw_reasons)
|
||||||
if groups:
|
if groups:
|
||||||
@@ -9108,7 +9083,8 @@ def doUndeleteUser():
|
|||||||
while i < len(sys.argv):
|
while i < len(sys.argv):
|
||||||
myarg = sys.argv[i].lower()
|
myarg = sys.argv[i].lower()
|
||||||
if myarg in ['ou', 'org']:
|
if myarg in ['ou', 'org']:
|
||||||
orgUnit = gapi_directory_orgunits.makeOrgUnitPathAbsolute(sys.argv[i + 1])
|
orgUnit = gapi_directory_orgunits.makeOrgUnitPathAbsolute(
|
||||||
|
sys.argv[i + 1])
|
||||||
i += 2
|
i += 2
|
||||||
else:
|
else:
|
||||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam undelete user')
|
controlflow.invalid_argument_exit(sys.argv[i], 'gam undelete user')
|
||||||
@@ -9911,8 +9887,9 @@ def getUsersToModify(entity_type=None,
|
|||||||
users = []
|
users = []
|
||||||
for member in members:
|
for member in members:
|
||||||
if ((not groupUserMembersOnly and not includeDerivedMembership) or
|
if ((not groupUserMembersOnly and not includeDerivedMembership) or
|
||||||
(member['type'] == 'USER')) and gapi_directory_groups._checkMemberRoleIsSuspended(
|
(member['type'] == 'USER')
|
||||||
member, validRoles, checkSuspended):
|
) and gapi_directory_groups._checkMemberRoleIsSuspended(
|
||||||
|
member, validRoles, checkSuspended):
|
||||||
users.append(member.get('email', member['id']))
|
users.append(member.get('email', member['id']))
|
||||||
elif entity_type in ['cigroup']:
|
elif entity_type in ['cigroup']:
|
||||||
got_uids = False
|
got_uids = False
|
||||||
@@ -9934,15 +9911,15 @@ def getUsersToModify(entity_type=None,
|
|||||||
)
|
)
|
||||||
page_message = gapi.got_total_items_msg(f'{member_type_message}',
|
page_message = gapi.got_total_items_msg(f'{member_type_message}',
|
||||||
'...')
|
'...')
|
||||||
members = gapi.get_all_pages(
|
members = gapi.get_all_pages(ci.groups().memberships(),
|
||||||
ci.groups().memberships(),
|
'list',
|
||||||
'list',
|
'memberships',
|
||||||
'memberships',
|
page_message=page_message,
|
||||||
page_message=page_message,
|
parent=parent,
|
||||||
parent=parent,
|
fields=fields)
|
||||||
fields=fields)
|
|
||||||
if member_type:
|
if member_type:
|
||||||
members = gapi_cloudidentity_groups.filter_members_to_roles(members, [member_type])
|
members = gapi_cloudidentity_groups.filter_members_to_roles(
|
||||||
|
members, [member_type])
|
||||||
users = []
|
users = []
|
||||||
for member in members:
|
for member in members:
|
||||||
users.append(member['memberKey']['id'])
|
users.append(member['memberKey']['id'])
|
||||||
@@ -11225,6 +11202,8 @@ def ProcessGAMCommand(args):
|
|||||||
doCreateAlertFeedback()
|
doCreateAlertFeedback()
|
||||||
elif argument in ['gcpfolder']:
|
elif argument in ['gcpfolder']:
|
||||||
createGCPFolder()
|
createGCPFolder()
|
||||||
|
elif argument in ['adminrole']:
|
||||||
|
gapi_directory_roles.create()
|
||||||
else:
|
else:
|
||||||
controlflow.invalid_argument_exit(argument, 'gam create')
|
controlflow.invalid_argument_exit(argument, 'gam create')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
@@ -11458,7 +11437,7 @@ def ProcessGAMCommand(args):
|
|||||||
elif argument == 'admins':
|
elif argument == 'admins':
|
||||||
doPrintAdmins()
|
doPrintAdmins()
|
||||||
elif argument in ['roles', 'adminroles']:
|
elif argument in ['roles', 'adminroles']:
|
||||||
doPrintAdminRoles()
|
gapi_directory_roles.print_()
|
||||||
elif argument in ['guardian', 'guardians']:
|
elif argument in ['guardian', 'guardians']:
|
||||||
doPrintShowGuardians(True)
|
doPrintShowGuardians(True)
|
||||||
elif argument in ['matters', 'vaultmatters']:
|
elif argument in ['matters', 'vaultmatters']:
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
"""Authentication/Credentials general purpose and convenience methods."""
|
"""Authentication/Credentials general purpose and convenience methods."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
from google.auth.jwt import Credentials as JWTCredentials
|
||||||
|
|
||||||
from gam.auth import oauth
|
from gam.auth import oauth
|
||||||
from gam.var import _FN_OAUTH2_TXT
|
from gam.var import _FN_OAUTH2_TXT
|
||||||
from gam.var import GC_OAUTH2_TXT
|
from gam.var import GC_OAUTH2_TXT
|
||||||
@@ -20,7 +26,16 @@ def get_admin_credentials_filename():
|
|||||||
return DEFAULT_OAUTH_STORAGE_FILE
|
return DEFAULT_OAUTH_STORAGE_FILE
|
||||||
|
|
||||||
|
|
||||||
def get_admin_credentials():
|
def get_admin_credentials(api=None):
|
||||||
"""Gets oauth.Credentials that are authenticated as the domain's admin user."""
|
"""Gets oauth.Credentials that are authenticated as the domain's admin user."""
|
||||||
credential_file = get_admin_credentials_filename()
|
credential_file = get_admin_credentials_filename()
|
||||||
return oauth.Credentials.from_credentials_file(credential_file)
|
if not os.path.isfile(credential_file):
|
||||||
|
raise oauth.InvalidCredentialsFileError
|
||||||
|
with open(credential_file, 'r') as f:
|
||||||
|
creds_data = json.load(f)
|
||||||
|
if 'token' in creds_data:
|
||||||
|
return oauth.Credentials.from_credentials_file(credential_file)
|
||||||
|
elif 'private_key' in creds_data:
|
||||||
|
audience = f'https://{api}.googleapis.com/'
|
||||||
|
return JWTCredentials.from_service_account_info(creds_data,
|
||||||
|
audience=audience)
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ def getOrgUnitId(orgUnit, cd=None):
|
|||||||
|
|
||||||
|
|
||||||
def buildOrgUnitIdToNameMap():
|
def buildOrgUnitIdToNameMap():
|
||||||
cd = buildGAPIObject('directory')
|
cd = gapi_directory.build()
|
||||||
result = gapi.call(cd.orgunits(),
|
result = gapi.call(cd.orgunits(),
|
||||||
'list',
|
'list',
|
||||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||||
@@ -420,5 +420,3 @@ def orgunit_from_orgunitid(orgunitid):
|
|||||||
if not GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME]:
|
if not GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME]:
|
||||||
buildOrgUnitIdToNameMap()
|
buildOrgUnitIdToNameMap()
|
||||||
return GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME].get(orgunitid, orgunitid)
|
return GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME].get(orgunitid, orgunitid)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from gam import display
|
|||||||
from gam import gapi
|
from gam import gapi
|
||||||
from gam.gapi import directory as gapi_directory
|
from gam.gapi import directory as gapi_directory
|
||||||
|
|
||||||
|
|
||||||
def flatten_privilege_list(privs, parent=None):
|
def flatten_privilege_list(privs, parent=None):
|
||||||
flat_privs = []
|
flat_privs = []
|
||||||
for priv in privs:
|
for priv in privs:
|
||||||
@@ -10,18 +11,22 @@ def flatten_privilege_list(privs, parent=None):
|
|||||||
if parent:
|
if parent:
|
||||||
priv['parent'] = parent
|
priv['parent'] = parent
|
||||||
if priv.get('childPrivileges'):
|
if priv.get('childPrivileges'):
|
||||||
children = flatten_privilege_list(priv['childPrivileges'], parent=priv['privilegeName'])
|
children = flatten_privilege_list(priv['childPrivileges'],
|
||||||
priv['children'] = ' '.join([child['privilegeName'] for child in children])
|
parent=priv['privilegeName'])
|
||||||
del(priv['childPrivileges'])
|
priv['children'] = ' '.join(
|
||||||
|
[child['privilegeName'] for child in children])
|
||||||
|
del (priv['childPrivileges'])
|
||||||
flat_privs = flat_privs + children
|
flat_privs = flat_privs + children
|
||||||
flat_privs.append(priv)
|
flat_privs.append(priv)
|
||||||
return flat_privs
|
return flat_privs
|
||||||
|
|
||||||
|
|
||||||
|
def print_(return_only=False):
|
||||||
def print_():
|
cd = gapi_directory.build()
|
||||||
cd = gapi_directory.build()
|
privs = gapi.call(cd.privileges(),
|
||||||
privs = gapi.call(cd.privileges(), 'list',
|
'list',
|
||||||
customer=GC_Values[GC_CUSTOMER_ID])
|
customer=GC_Values[GC_CUSTOMER_ID])
|
||||||
privs = flatten_privilege_list(privs.get('items', []))
|
privs = flatten_privilege_list(privs.get('items', []))
|
||||||
display.print_json(privs)
|
if return_only:
|
||||||
|
return privs
|
||||||
|
display.print_json(privs)
|
||||||
|
|||||||
72
src/gam/gapi/directory/roles.py
Normal file
72
src/gam/gapi/directory/roles.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from gam.var import GC_Values, GC_CUSTOMER_ID
|
||||||
|
from gam import display
|
||||||
|
from gam import gapi
|
||||||
|
from gam.gapi import directory as gapi_directory
|
||||||
|
from gam.gapi.directory import privileges as gapi_directory_privileges
|
||||||
|
|
||||||
|
|
||||||
|
def create():
|
||||||
|
cd = gapi_directory.build()
|
||||||
|
body = {'privileges': []}
|
||||||
|
all_privileges = gapi_directory_privileges.print_(return_only=True)
|
||||||
|
i = 3
|
||||||
|
while i < len(sys.argv):
|
||||||
|
myarg = sys.argv[i].lower()
|
||||||
|
if myarg == 'privileges':
|
||||||
|
privs = sys.argv[i + 1]
|
||||||
|
if privs == 'all':
|
||||||
|
body['rolePrivileges'] = all_privileges
|
||||||
|
elif privs == 'all_ou':
|
||||||
|
body['rolePrivileges'] = [
|
||||||
|
p for p in all_privileges if p.get('isOuScopable')
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
# Known broken, need to get serviceName in here also...
|
||||||
|
body['rolePrivileges'] = [{
|
||||||
|
'privilegeName': p
|
||||||
|
} for p in sys.argv[i + 1].split(',')]
|
||||||
|
i += 2
|
||||||
|
elif myarg == 'name':
|
||||||
|
body['roleName'] = sys.argv[i + 1]
|
||||||
|
i += 2
|
||||||
|
else:
|
||||||
|
controlflow.invalid_argument_exit(sys.argv[i],
|
||||||
|
'gam create adminrole')
|
||||||
|
print(f'Creating role {body["roleName"]}')
|
||||||
|
gapi.call(cd.roles(),
|
||||||
|
'insert',
|
||||||
|
customer=GC_Values[GC_CUSTOMER_ID],
|
||||||
|
body=body)
|
||||||
|
|
||||||
|
|
||||||
|
def print_():
|
||||||
|
cd = gapi_directory.build()
|
||||||
|
todrive = False
|
||||||
|
titles = [
|
||||||
|
'roleId', 'roleName', 'roleDescription', 'isSuperAdminRole',
|
||||||
|
'isSystemRole'
|
||||||
|
]
|
||||||
|
fields = f'nextPageToken,items({",".join(titles)})'
|
||||||
|
csvRows = []
|
||||||
|
i = 3
|
||||||
|
while i < len(sys.argv):
|
||||||
|
myarg = sys.argv[i].lower()
|
||||||
|
if myarg == 'todrive':
|
||||||
|
todrive = True
|
||||||
|
i += 1
|
||||||
|
else:
|
||||||
|
controlflow.invalid_argument_exit(sys.argv[i],
|
||||||
|
'gam print adminroles')
|
||||||
|
roles = gapi.get_all_pages(cd.roles(),
|
||||||
|
'list',
|
||||||
|
'items',
|
||||||
|
customer=GC_Values[GC_CUSTOMER_ID],
|
||||||
|
fields=fields)
|
||||||
|
for role in roles:
|
||||||
|
role_attrib = {}
|
||||||
|
for key, value in list(role.items()):
|
||||||
|
role_attrib[key] = value
|
||||||
|
csvRows.append(role_attrib)
|
||||||
|
display.write_csv_file(csvRows, titles, 'Admin Roles', todrive)
|
||||||
Reference in New Issue
Block a user