mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-03 22:01:39 +00:00
Added enforce_expansive_access Boolean variable to gam.cfg
Some checks failed
Build and test GAM / build (build, 1, Build Intel Ubuntu Jammy, ubuntu-22.04) (push) Has been cancelled
Build and test GAM / build (build, 10, Build Intel Windows, windows-2022) (push) Has been cancelled
Build and test GAM / build (build, 11, Build Arm Windows, windows-11-arm) (push) Has been cancelled
Build and test GAM / build (build, 2, Build Intel Ubuntu Noble, ubuntu-24.04) (push) Has been cancelled
Build and test GAM / build (build, 3, Build Arm Ubuntu Noble, ubuntu-24.04-arm) (push) Has been cancelled
Build and test GAM / build (build, 4, Build Arm Ubuntu Jammy, ubuntu-22.04-arm) (push) Has been cancelled
Build and test GAM / build (build, 5, Build Intel StaticX Legacy, ubuntu-22.04, yes) (push) Has been cancelled
Build and test GAM / build (build, 6, Build Arm StaticX Legacy, ubuntu-22.04-arm, yes) (push) Has been cancelled
Build and test GAM / build (build, 7, Build Intel MacOS, macos-13) (push) Has been cancelled
Build and test GAM / build (build, 8, Build Arm MacOS 14, macos-14) (push) Has been cancelled
Build and test GAM / build (build, 9, Build Arm MacOS 15, macos-15) (push) Has been cancelled
Build and test GAM / build (test, 12, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (test, 13, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (test, 14, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (test, 15, Test Python 3.14-dev, ubuntu-24.04, 3.14-dev) (push) Has been cancelled
Build and test GAM / merge (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Check for Google Root CA Updates / check-apis (push) Has been cancelled
Push wiki / pushwiki (push) Has been cancelled
Some checks failed
Build and test GAM / build (build, 1, Build Intel Ubuntu Jammy, ubuntu-22.04) (push) Has been cancelled
Build and test GAM / build (build, 10, Build Intel Windows, windows-2022) (push) Has been cancelled
Build and test GAM / build (build, 11, Build Arm Windows, windows-11-arm) (push) Has been cancelled
Build and test GAM / build (build, 2, Build Intel Ubuntu Noble, ubuntu-24.04) (push) Has been cancelled
Build and test GAM / build (build, 3, Build Arm Ubuntu Noble, ubuntu-24.04-arm) (push) Has been cancelled
Build and test GAM / build (build, 4, Build Arm Ubuntu Jammy, ubuntu-22.04-arm) (push) Has been cancelled
Build and test GAM / build (build, 5, Build Intel StaticX Legacy, ubuntu-22.04, yes) (push) Has been cancelled
Build and test GAM / build (build, 6, Build Arm StaticX Legacy, ubuntu-22.04-arm, yes) (push) Has been cancelled
Build and test GAM / build (build, 7, Build Intel MacOS, macos-13) (push) Has been cancelled
Build and test GAM / build (build, 8, Build Arm MacOS 14, macos-14) (push) Has been cancelled
Build and test GAM / build (build, 9, Build Arm MacOS 15, macos-15) (push) Has been cancelled
Build and test GAM / build (test, 12, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (test, 13, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (test, 14, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (test, 15, Test Python 3.14-dev, ubuntu-24.04, 3.14-dev) (push) Has been cancelled
Build and test GAM / merge (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Check for Google Root CA Updates / check-apis (push) Has been cancelled
Push wiki / pushwiki (push) Has been cancelled
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
7.08.03
|
||||
7.09.00
|
||||
|
||||
Removed the overly broad service account `IAM and Access Management API` scope `https://www.googleapis.com/auth/cloud-platform`
|
||||
from DWD. The `gam <UserTypeEntity> check|Update serviceaccount` commands issue an error message if this scope
|
||||
@@ -6,6 +6,19 @@ is enabled promptig you to update your service account authorization so that the
|
||||
|
||||
GAM commands that need IAM access now use the more limited scope `https://www.googleapis.com/auth/iam` in a non-DWD manner.
|
||||
|
||||
Added `enforce_expansive_access` Boolean variable to `gam.cfg` that provides the default value
|
||||
for option `enforceexpansiveaccess` in all commands that delete or update drive file ACLs/permissions.
|
||||
```
|
||||
gam <UserTypeEntity> delete permissions
|
||||
gam <UserTypeEntity> delete drivefileacl
|
||||
gam <UserTypeEntity> update drivefileacl
|
||||
gam <UserTypeEntity> copy drivefile
|
||||
gam <UserTypeEntity> move drivefile
|
||||
gam <UserTypeEntity> transfer ownership
|
||||
gam <UserTypeEntity> claim ownership
|
||||
gam <UserTypeEntity> transfer drive
|
||||
```
|
||||
|
||||
Updated to Python 3.13.4
|
||||
|
||||
7.08.02
|
||||
|
||||
@@ -11,7 +11,7 @@ if __name__ == '__main__':
|
||||
# One time initialization
|
||||
if platform.system() != 'Linux':
|
||||
multiprocessing.freeze_support()
|
||||
multiprocessing.set_start_method('spawn')
|
||||
multiprocessing.set_start_method('spawn', force=True)
|
||||
initializeLogging()
|
||||
#
|
||||
CallGAMCommand(['gam', 'version'])
|
||||
|
||||
@@ -11,5 +11,5 @@ from gam.__main__ import main
|
||||
if __name__ == '__main__':
|
||||
if platform.system() != 'Linux':
|
||||
multiprocessing.freeze_support()
|
||||
multiprocessing.set_start_method('spawn')
|
||||
multiprocessing.set_start_method('spawn', force=True)
|
||||
main()
|
||||
|
||||
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
|
||||
"""
|
||||
|
||||
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
||||
__version__ = '7.08.03'
|
||||
__version__ = '7.09.00'
|
||||
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
#pylint: disable=wrong-import-position
|
||||
@@ -10632,7 +10632,6 @@ def getOAuthClientIDAndSecret():
|
||||
invalidClientSecretsJsonExit(str(e))
|
||||
|
||||
def getScopesFromUser(scopesList, clientAccess, currentScopes=None):
|
||||
forceOffScopes = []
|
||||
OAUTH2_CMDS = ['s', 'u', 'e', 'c']
|
||||
oauth2_menu = ''
|
||||
numScopes = len(scopesList)
|
||||
@@ -10683,9 +10682,6 @@ Continue to authorization by entering a 'c'
|
||||
break
|
||||
i += 1
|
||||
else:
|
||||
for api in currentScopes:
|
||||
if api in API.FORCE_OFF_SA_SCOPES:
|
||||
forceOffScopes.extend(currentScopes[api])
|
||||
i = 0
|
||||
for a_scope in scopesList:
|
||||
selectedScopes[i] = ' '
|
||||
@@ -10754,12 +10750,12 @@ Continue to authorization by entering a 'c'
|
||||
for i in range(numScopes):
|
||||
selectedScopes[i] = ' '
|
||||
elif selection == 'e':
|
||||
return (None, None)
|
||||
return None
|
||||
break
|
||||
sys.stdout.write(f'{ERROR_PREFIX}Invalid input "{choice}"\n')
|
||||
if selection == 'c':
|
||||
break
|
||||
return (selectedScopes, forceOffScopes)
|
||||
return selectedScopes
|
||||
|
||||
def _localhost_to_ip():
|
||||
'''returns IPv4 or IPv6 loopback address which localhost resolves to.
|
||||
@@ -11106,7 +11102,7 @@ def doOAuthRequest(currentScopes, login_hint, verifyScopes=False):
|
||||
client_id, client_secret = getOAuthClientIDAndSecret()
|
||||
scopesList = API.getClientScopesList(GC.Values[GC.TODRIVE_CLIENTACCESS])
|
||||
if not currentScopes or verifyScopes:
|
||||
selectedScopes, _ = getScopesFromUser(scopesList, True, currentScopes)
|
||||
selectedScopes = getScopesFromUser(scopesList, True, currentScopes)
|
||||
if selectedScopes is None:
|
||||
return False
|
||||
scopes = set(API.REQUIRED_SCOPES)
|
||||
@@ -12237,7 +12233,7 @@ def checkServiceAccount(users):
|
||||
|
||||
def authorizeScopes(message):
|
||||
long_url = ('https://admin.google.com/ac/owl/domainwidedelegation'
|
||||
f'?clientScopeToAdd={",".join(checkScopes)}'
|
||||
f'?clientScopeToAdd={",".join(sorted(checkScopesSet-API.FORCE_OFF_SA_SCOPES))}'
|
||||
f'&clientIdToAdd={service_account}&overwriteClientId=true')
|
||||
if GC.Values[GC.DOMAIN]:
|
||||
long_url += f'&dn={GC.Values[GC.DOMAIN]}'
|
||||
@@ -12249,11 +12245,12 @@ def checkServiceAccount(users):
|
||||
allScopes = API.getSvcAcctScopes(GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY], Act.Get() == Act.UPDATE)
|
||||
checkScopesSet = set()
|
||||
saScopes = {}
|
||||
addForceOffScopes = True
|
||||
useColor = False
|
||||
forceOffScopes = []
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if myarg in {'scope', 'scopes'}:
|
||||
addForceOffScopes = False
|
||||
for scope in getString(Cmd.OB_API_SCOPE_URL_LIST).lower().replace(',', ' ').split():
|
||||
api = API.getSvcAcctScopeAPI(scope)
|
||||
if api is not None:
|
||||
@@ -12270,22 +12267,22 @@ def checkServiceAccount(users):
|
||||
testPass = createGreenText('PASS')
|
||||
testFail = createRedText('FAIL')
|
||||
testWarn = createYellowText('WARN')
|
||||
testDisable = createRedText('DISABLE')
|
||||
testSkip = createGreenText('SKIP')
|
||||
else:
|
||||
testPass = 'PASS'
|
||||
testFail = 'FAIL'
|
||||
testWarn = 'WARN'
|
||||
testDisable = 'DISABLE'
|
||||
testSkip = 'SKIP'
|
||||
if Act.Get() == Act.CHECK:
|
||||
if not checkScopesSet:
|
||||
currentScopes = GM.Globals[GM.SVCACCT_SCOPES]
|
||||
for api in currentScopes:
|
||||
if api in API.FORCE_OFF_SA_SCOPES:
|
||||
forceOffScopes.extend(currentScopes[api])
|
||||
else:
|
||||
checkScopesSet.update(currentScopes[api])
|
||||
for scope in iter(GM.Globals[GM.SVCACCT_SCOPES].values()):
|
||||
checkScopesSet.update(scope)
|
||||
else:
|
||||
if not checkScopesSet:
|
||||
scopesList = API.getSvcAcctScopesList(GC.Values[GC.USER_SERVICE_ACCOUNT_ACCESS_ONLY], True)
|
||||
selectedScopes, forceOffScopes = getScopesFromUser(scopesList, False, GM.Globals[GM.SVCACCT_SCOPES] if GM.Globals[GM.SVCACCT_SCOPES_DEFINED] else None)
|
||||
selectedScopes = getScopesFromUser(scopesList, False, GM.Globals[GM.SVCACCT_SCOPES] if GM.Globals[GM.SVCACCT_SCOPES_DEFINED] else None)
|
||||
if selectedScopes is None:
|
||||
return False
|
||||
i = 0
|
||||
@@ -12312,6 +12309,8 @@ def checkServiceAccount(users):
|
||||
json.dumps(GM.Globals[GM.OAUTH2SERVICE_JSON_DATA], ensure_ascii=False, sort_keys=True, indent=2),
|
||||
continueOnError=False)
|
||||
checkScopes = sorted(checkScopesSet)
|
||||
if addForceOffScopes:
|
||||
checkScopes.extend(sorted(API.FORCE_OFF_SA_SCOPES))
|
||||
jcount = len(checkScopes)
|
||||
printMessage(Msg.SYSTEM_TIME_STATUS)
|
||||
offsetSeconds, offsetFormatted = getLocalGoogleTimeOffset()
|
||||
@@ -12400,24 +12399,23 @@ def checkServiceAccount(users):
|
||||
if credentials.token:
|
||||
token_info = callGAPI(oa2, 'tokeninfo', access_token=credentials.token)
|
||||
if scope in token_info.get('scope', '').split(' ') and user == token_info.get('email', user).lower():
|
||||
if scope not in API.FORCE_OFF_SA_SCOPES:
|
||||
scopeStatus = testPass
|
||||
else:
|
||||
scopeStatus = testDisable
|
||||
allScopesPass = False
|
||||
else:
|
||||
if scope not in API.FORCE_OFF_SA_SCOPES:
|
||||
scopeStatus = testFail
|
||||
allScopesPass = False
|
||||
else:
|
||||
scopeStatus = testSkip
|
||||
else:
|
||||
if scope not in API.FORCE_OFF_SA_SCOPES:
|
||||
scopeStatus = testFail
|
||||
allScopesPass = False
|
||||
printPassFail(scope, f'{scopeStatus}{currentCount(j, jcount)}')
|
||||
if forceOffScopes:
|
||||
allScopesPass = False
|
||||
if useColor:
|
||||
scopeStatus = createRedText('DISABLE')
|
||||
else:
|
||||
scopeStatus = 'DISABLE'
|
||||
jcount = len(forceOffScopes)
|
||||
j = 0
|
||||
for scope in forceOffScopes:
|
||||
j +=1
|
||||
scopeStatus = testSkip
|
||||
printPassFail(scope, f'{scopeStatus}{currentCount(j, jcount)}')
|
||||
Ind.Decrement()
|
||||
service_account = GM.Globals[GM.OAUTH2SERVICE_JSON_DATA]['client_id']
|
||||
@@ -58737,7 +58735,7 @@ def initCopyMoveOptions(copyCmd):
|
||||
'showPermissionMessages': False,
|
||||
'sendEmailIfRequired': False,
|
||||
'useDomainAdminAccess': False,
|
||||
'enforceExpansiveAccess': False,
|
||||
'enforceExpansiveAccess': GC.Values[GC.ENFORCE_EXPANSIVE_ACCESS],
|
||||
'copiedShortcutsPointToCopiedFiles': True,
|
||||
'createShortcutsForNonmovableFiles': False,
|
||||
'duplicateFiles': DUPLICATE_FILE_OVERWRITE_OLDER,
|
||||
@@ -62117,7 +62115,8 @@ def transferDrive(users):
|
||||
targetUserFolderPattern = '#user# old files'
|
||||
targetUserOrphansFolderPattern = '#user# orphaned files'
|
||||
targetIds = [None, None]
|
||||
createShortcutsForNonmovableFiles = enforceExpansiveAccess = False
|
||||
createShortcutsForNonmovableFiles = False
|
||||
enforceExpansiveAccess = GC.Values[GC.ENFORCE_EXPANSIVE_ACCESS]
|
||||
mergeWithTarget = False
|
||||
thirdPartyOwners = {}
|
||||
skipFileIdEntity = initDriveFileEntity()
|
||||
@@ -62423,7 +62422,8 @@ def transferOwnership(users):
|
||||
body = {}
|
||||
newOwner = getEmailAddress()
|
||||
OBY = OrderBy(DRIVEFILE_ORDERBY_CHOICE_MAP)
|
||||
changeParents = enforceExpansiveAccess = filepath = includeTrashed = noRecursion = False
|
||||
changeParents = filepath = includeTrashed = noRecursion = False
|
||||
enforceExpansiveAccess = GC.Values[GC.ENFORCE_EXPANSIVE_ACCESS]
|
||||
pathDelimiter = '/'
|
||||
csvPF = fileTree = None
|
||||
addParents = ''
|
||||
@@ -62749,7 +62749,8 @@ def claimOwnership(users):
|
||||
onlyOwners = set()
|
||||
skipOwners = set()
|
||||
subdomains = []
|
||||
enforceExpansiveAccess = filepath = includeTrashed = False
|
||||
filepath = includeTrashed = False
|
||||
enforceExpansiveAccess = GC.Values[GC.ENFORCE_EXPANSIVE_ACCESS]
|
||||
pathDelimiter = '/'
|
||||
addParents = ''
|
||||
parentBody = {}
|
||||
@@ -63524,7 +63525,7 @@ def doCreateDriveFileACL():
|
||||
def updateDriveFileACLs(users, useDomainAdminAccess=False):
|
||||
fileIdEntity = getDriveFileEntity()
|
||||
isEmail, permissionId = getPermissionId()
|
||||
enforceExpansiveAccess = None
|
||||
enforceExpansiveAccess = GC.Values[GC.ENFORCE_EXPANSIVE_ACCESS]
|
||||
removeExpiration = showTitles = updateSheetProtectedRanges = False
|
||||
showDetails = True
|
||||
csvPF = None
|
||||
@@ -63853,7 +63854,7 @@ def doCreatePermissions():
|
||||
def deleteDriveFileACLs(users, useDomainAdminAccess=False):
|
||||
fileIdEntity = getDriveFileEntity()
|
||||
isEmail, permissionId = getPermissionId()
|
||||
enforceExpansiveAccess = None
|
||||
enforceExpansiveAccess = GC.Values[GC.ENFORCE_EXPANSIVE_ACCESS]
|
||||
showTitles = updateSheetProtectedRanges = False
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
@@ -63982,7 +63983,7 @@ def deletePermissions(users, useDomainAdminAccess=False):
|
||||
jsonData = getJSON([])
|
||||
PM = PermissionMatch()
|
||||
PM.SetDefaultMatch(False, {'role': 'owner'})
|
||||
enforceExpansiveAccess = False
|
||||
enforceExpansiveAccess = GC.Values[GC.ENFORCE_EXPANSIVE_ACCESS]
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if myarg in ADMIN_ACCESS_OPTIONS:
|
||||
|
||||
@@ -132,7 +132,10 @@ APIS_NEEDING_ACCESS_TOKEN = {
|
||||
CBCM: ['https://www.googleapis.com/auth/admin.directory.device.chromebrowsers']
|
||||
}
|
||||
#
|
||||
FORCE_OFF_SA_SCOPES = {IAM}
|
||||
FORCE_OFF_SA_SCOPES = {
|
||||
'https://www.googleapis.com/auth/cloud-identity',
|
||||
'https://www.googleapis.com/auth/cloud-platform',
|
||||
}
|
||||
#
|
||||
REFRESH_PERM_ERRORS = [
|
||||
'invalid_grant: reauth related error (rapt_required)', # no way to reauth today
|
||||
@@ -652,7 +655,7 @@ _SVCACCT_SCOPES = [
|
||||
# 'api': IAM,
|
||||
# 'offByDefault': True,
|
||||
# 'subscopes': [],
|
||||
# 'scope': IAM_SCOPE},
|
||||
# 'scope': CLOUD_PLATFORM_SCOPE},
|
||||
{'name': 'Keep API',
|
||||
'api': KEEP,
|
||||
'subscopes': READONLY,
|
||||
|
||||
@@ -163,6 +163,8 @@ EMAIL_BATCH_SIZE = 'email_batch_size'
|
||||
ENABLE_DASA = 'enable_dasa'
|
||||
# Enable Cloud Session Reauthentication by borrowing a RAPT token from gcloud command
|
||||
ENABLE_GCLOUD_REAUTH = 'enable_gcloud_reauth'
|
||||
# Value for enforceExpansiveAccess for commands that delete or update drive file ACLs/permissions.
|
||||
ENFORCE_EXPANSIVE_ACCESS = 'enforce_expansive_access'
|
||||
# When retrieving lists of calendar events from API, how many should be retrieved in each chunk
|
||||
EVENT_MAX_RESULTS = 'event_max_results'
|
||||
# Path to extra_args.txt
|
||||
@@ -377,6 +379,7 @@ Defaults = {
|
||||
DEVICE_MAX_RESULTS: '200',
|
||||
DOMAIN: '',
|
||||
DRIVE_DIR: '',
|
||||
ENFORCE_EXPANSIVE_ACCESS: FALSE,
|
||||
DRIVE_MAX_RESULTS: '1000',
|
||||
DRIVE_V3_BETA: FALSE,
|
||||
DRIVE_V3_NATIVE_NAMES: TRUE,
|
||||
@@ -545,6 +548,7 @@ VAR_INFO = {
|
||||
DEVICE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 200)},
|
||||
DOMAIN: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'GA_DOMAIN', VAR_LIMITS: (0, None)},
|
||||
DRIVE_DIR: {VAR_TYPE: TYPE_DIRECTORY, VAR_ENVVAR: 'GAMDRIVEDIR'},
|
||||
ENFORCE_EXPANSIVE_ACCESS: {VAR_TYPE: TYPE_BOOLEAN},
|
||||
DRIVE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 1000)},
|
||||
DRIVE_V3_BETA: {VAR_TYPE: TYPE_BOOLEAN},
|
||||
DRIVE_V3_NATIVE_NAMES: {VAR_TYPE: TYPE_BOOLEAN},
|
||||
|
||||
Reference in New Issue
Block a user