Compare commits

...

7 Commits

Author SHA1 Message Date
Ross Scroggs
6b9ac2700e Updated gam <UserTypeEntity> empty drivetrash <SharedDriveEntity>
Some checks failed
Build and test GAM / build (Win64, build, 8, VC-WIN64A, windows-2022) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 2, linux-aarch64, [self-hosted linux arm64]) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 4, linux-aarch64, [self-hosted linux arm64], yes) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 6, darwin64-arm64, macos-14) (push) Has been cancelled
Build and test GAM / build (universal2, build, 7, darwin64-arm64 darwin64-x86_64, macos-14) (push) Has been cancelled
Build and test GAM / build (x86_64, build, 1, linux-x86_64, ubuntu-20.04) (push) Has been cancelled
Build and test GAM / build (x86_64, build, 3, linux-x86_64, ubuntu-20.04, yes) (push) Has been cancelled
Build and test GAM / build (x86_64, build, 5, darwin64-x86_64, macos-12) (push) Has been cancelled
Build and test GAM / build (x86_64, test, 10, ubuntu-22.04, 3.9) (push) Has been cancelled
Build and test GAM / build (x86_64, test, 11, ubuntu-22.04, 3.10) (push) Has been cancelled
Build and test GAM / build (x86_64, test, 12, ubuntu-22.04, 3.11) (push) Has been cancelled
Build and test GAM / build (x86_64, test, 9, ubuntu-22.04, 3.8) (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
2024-07-26 17:09:27 -07:00
Ross Scroggs
012616a285 Fix bug in check ou 2024-07-25 19:16:01 -07:00
Ross Scroggs
2669b1bff6 Added options filename <FileName> and movetoou <OrgUnitItem> to gam check ou <OrgUnitItem> 2024-07-25 18:28:28 -07:00
Ross Scroggs
2aeebd17a4 Added column|field assignedToUnknown to gam print|show admins 2024-07-24 08:49:09 -07:00
Ross Scroggs
e43802e197 Updated gam print admins to handle the following error
ERROR: 404: notFound - Requested entity was not found.
2024-07-23 16:00:36 -07:00
Ross Scroggs
16b3d2b006 Allow <RoleItem> in any case 2024-07-22 20:47:05 -07:00
Ross Scroggs
f777ec177c Updated code to work around a Cryptography library change 2024-07-22 08:37:33 -07:00
9 changed files with 199 additions and 50 deletions

View File

@@ -199,6 +199,16 @@ gam update chromepolicy convertcrnl chrome.devices.DisabledDeviceReturnInstructi
```
### Examples
Restrict use of Chromebooks in an OU to a specific list of users.
```
gam update chromepolicy chrome.devices.SignInRestriction deviceAllowNewUsers RESTRICTED_LIST userAllowlist "user1@domain.com,user2@domain.com" ou "<Path/To/Ou>"
```
Restrict use of Chromebooks in an OU to users in a specific domain.
```
gam update chromepolicy chrome.devices.SignInRestriction deviceAllowNewUsers RESTRICTED_LIST userAllowlist "*@domain.com" ou "<Path/To/Ou>"
```
Restrict student users from adding additional printers and set default printing to black and white.
```
gam update chromepolicy chrome.users.UserPrintersAllowed userPrintersAllowed false chrome.users.DefaultPrintColor printingColorDefault MONOCHROME orgunit "/Students"

View File

@@ -10,6 +10,43 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
See [Downloads-Installs](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads-Installs) for Windows or other options, including manual installation
### 6.79.05
Updated `gam <UserTypeEntity> empty drivetrash <SharedDriveEntity>` to handle this error that
occurs when the user is not a Manager of the Shared Drive.
```
ERROR: 403: insufficientFilePermissions - The user does not have sufficient permissions for this file.
```
### 6.79.04
Added options `filename <FileName>` and `movetoou <OrgUnitItem>` to `gam check ou <OrgUnitItem>`
that causes GAM to create a batch file of GAM commands that will move any remaining items
in `ou <OrgUnitItem>` to `movetoou <OrgUnitItem>`; executing the batch file will then allow
`ou <OrgUnitItem>` to be deleted if desired.
### 6.79.03
Added column|field `assignedToUnknown` to `gam print|show admins` that will be True when
the API `assignedTo` value can not be converted to an email address; it will be False when
the email address is determinable.
### 6.79.02
Updated `gam print admins` to handle the following error that occurs when a service account admin no longer exists.
```
ERROR: 404: notFound - Requested entity was not found.
```
### 6.79.01
Updated commands that take `<RoleItem>` as an argument to take the value in any case,
e.g., _SEED_ADMIN_ROLE or _seed_admin_role.
### 6.79.00
Updated code to work around a Cryptography library change that caused service account private key creation to fail.
### 6.78.00
Added command to check if an OU contains items; this is useful when tryng to delete an OU

View File

@@ -251,7 +251,7 @@ writes the credentials into the file oauth2.txt.
admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt
admin@server:/Users/admin$ gam version
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
GAMADV-XTD3 6.78.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.79.05 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -923,7 +923,7 @@ writes the credentials into the file oauth2.txt.
C:\>del C:\GAMConfig\oauth2.txt
C:\>gam version
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
GAMADV-XTD3 6.78.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.79.05 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
Windows-10-10.0.17134 AMD64

View File

@@ -295,6 +295,7 @@ Only items directly within the OU are counted, items in sub-OUs are not counted.
gam check org|ou <OrgUnitItem> [todrive <ToDriveAttribute>*]
[<OrgUnitCheckName>*|(fields <OrgUnitCheckNameList>)]
[filename <FileName>] [movetoou <OrgUnitItem>]
[formatjson [quotechar <Character>]]
```
By default, GAM checks each of the five items; you can select specfic fields
@@ -309,6 +310,17 @@ When using the `formatjson` option, double quotes are used extensively in the da
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
If `movetoou <OrgUnitItem>` is specified, GAM will create a batch file of GAM commands that will move any remaining items
in `ou <OrgUnitItem>` to `movetoou <OrgUnitItem>`.
By default, the batch file will be named `CleanOuBatch.txt` and will be created in `gam.cfg/drive_dir`.
This can be overridden with `filename <FileName>`.
You can inspect the file and execute it if desired; substitute actual filenames as desired.
```
gam redirect stdout CleanOuLog.txt multiproces redirect stderr stdout batch CleanOuBatch.txt
```
## Special case handling for large number of organizational units
By default, the `print orgs` and `show orgtree` commands issue a single API call to get the

View File

@@ -1,9 +1,9 @@
\# Version and Help
\\# Version and Help
Print the current version of Gam with details
```
gam version
GAMADV-XTD3 6.78.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.79.05 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -15,7 +15,7 @@ Time: 2023-06-02T21:10:00-07:00
Print the current version of Gam with details and time offset information
```
gam version timeoffset
GAMADV-XTD3 6.78.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.79.05 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -27,7 +27,7 @@ Your system time differs from www.googleapis.com by less than 1 second
Print the current version of Gam with extended details and SSL information
```
gam version extended
GAMADV-XTD3 6.78.00 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.79.05 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64
@@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64
Path: /Users/Admin/bin/gamadv-xtd3
Version Check:
Current: 5.35.08
Latest: 6.78.00
Latest: 6.79.05
echo $?
1
```
@@ -72,7 +72,7 @@ echo $?
Print the current version number without details
```
gam version simple
6.78.00
6.79.05
```
In Linux/MacOS you can do:
```
@@ -82,7 +82,7 @@ echo $VER
Print the current version of Gam and address of this Wiki
```
gam help
GAM 6.78.00 - https://github.com/taers232c/GAMADV-XTD3
GAM 6.79.05 - https://github.com/taers232c/GAMADV-XTD3
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.4 64-bit final
MacOS Sonoma 14.5 x86_64

View File

@@ -4225,8 +4225,9 @@ gam show orgtree [fromparent <OrgUnitItem>] [batchsuborgs [<Boolean>]]
users
<OrgUnitCheckNameList> ::= "<OrgUnitCheckName>(,<OrgUnitCheckName>)*"
gam check org|ou <OrgUnitItem> [todrive <ToDriveAttribute>*]
gam check ou|org <OrgUnitItem> [todrive <ToDriveAttribute>*]
[<OrgUnitCheckName>*|(fields <OrgUnitCheckNameList>)]
[filename <FileName>] [movetoou <OrgUnitItem>]
[formatjson [quotechar <Character>]]
# Printers

View File

@@ -2,6 +2,43 @@
Merged GAM-Team version
6.79.05
Updated `gam <UserTypeEntity> empty drivetrash <SharedDriveEntity>` to handle this error that
occurs when the user is not a Manager of the Shared Drive.
```
ERROR: 403: insufficientFilePermissions - The user does not have sufficient permissions for this file.
```
6.79.04
Added options `filename <FileName>` and `movetoou <OrgUnitItem>` to `gam check ou <OrgUnitItem>`
that causes GAM to create a batch file of GAM commands that will move any remaining items
in `ou <OrgUnitItem>` to `movetoou <OrgUnitItem>`; executing the batch file will then allow
`ou <OrgUnitItem>` to be deleted if desired.
6.79.03
Added column|field `assignedToUnknown` to `gam print|show admins` that will be True when
the API `assignedTo` value can not be converted to an email address; it will be False when
the email address is determinable.
6.79.02
Updated `gam print admins` to handle the following error that occurs when a service account admin no longer exists.
```
ERROR: 404: notFound - Requested entity was not found.
```
6.79.01
Updated commands that take `<RoleItem>` as an argument to take the value in any case,
e.g., _SEED_ADMIN_ROLE or _seed_admin_role.
6.79.00
Updated code to work around a Cryptography library change that caused service account private key creation to fail.
6.78.00
Added command to check if an OU contains items; this is useful when tryng to delete an OU

View File

@@ -2810,6 +2810,15 @@ def entityModifierNewValueKeyValueActionPerformed(entityValueList, modifier, new
def cleanFilename(filename):
return sanitize_filename(filename, '_')
def setFilePath(fileName):
if fileName.startswith('./') or fileName.startswith('.\\'):
fileName = os.path.join(os.getcwd(), fileName[2:])
else:
fileName = os.path.expanduser(fileName)
if not os.path.isabs(fileName):
fileName = os.path.join(GC.Values[GC.DRIVE_DIR], fileName)
return fileName
def uniqueFilename(targetFolder, filetitle, overwrite, extension=None):
filename = filetitle
y = 0
@@ -3773,12 +3782,7 @@ def SetGlobalVariables():
def _setCSVFile(fileName, mode, encoding, writeHeader, multi):
if fileName != '-':
if fileName.startswith('./') or fileName.startswith('.\\'):
fileName = os.path.join(os.getcwd(), fileName[2:])
else:
fileName = os.path.expanduser(fileName)
if not os.path.isabs(fileName):
fileName = os.path.join(GC.Values[GC.DRIVE_DIR], fileName)
fileName = setFilePath(fileName)
GM.Globals[GM.CSVFILE][GM.REDIRECT_NAME] = fileName
GM.Globals[GM.CSVFILE][GM.REDIRECT_MODE] = mode
GM.Globals[GM.CSVFILE][GM.REDIRECT_ENCODING] = encoding
@@ -3799,12 +3803,7 @@ def SetGlobalVariables():
else:
GM.Globals[stdtype][GM.REDIRECT_FD] = os.fdopen(os.dup(sys.stderr.fileno()), mode, encoding=GM.Globals[GM.SYS_ENCODING])
else:
if fileName.startswith('./') or fileName.startswith('.\\'):
fileName = os.path.join(os.getcwd(), fileName[2:])
else:
fileName = os.path.expanduser(fileName)
if not os.path.isabs(fileName):
fileName = os.path.join(GC.Values[GC.DRIVE_DIR], fileName)
fileName = setFilePath(fileName)
if multi and mode == DEFAULT_FILE_WRITE_MODE:
deleteFile(fileName)
mode = DEFAULT_FILE_APPEND_MODE
@@ -5628,9 +5627,9 @@ def getServiceAccountEmailFromID(account_id, sal=None):
sal = buildGAPIObject(API.SERVICEACCOUNTLOOKUP)
try:
certs = callGAPI(sal.serviceaccounts(), 'lookup',
throwReasons = [GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.INVALID_ARGUMENT],
throwReasons = [GAPI.BAD_REQUEST, GAPI.NOT_FOUND, GAPI.RESOURCE_NOT_FOUND, GAPI.INVALID_ARGUMENT],
account=account_id)
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.invalidArgument):
except (GAPI.badRequest, GAPI.notFound, GAPI.resourceNotFound, GAPI.invalidArgument):
return None
sa_cn_rx = r'CN=(.+)\.(.+)\.iam\.gservice.*'
sa_emails = []
@@ -12348,8 +12347,8 @@ def _generatePrivateKeyAndPublicCert(projectId, clientEmail, name, key_size, b64
writeStdout(Msg.EXTRACTING_PUBLIC_CERTIFICATE+'\n')
public_key = private_key.public_key()
builder = x509.CertificateBuilder()
builder = builder.subject_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, name)]))
builder = builder.issuer_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, name)]))
builder = builder.subject_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, name, _validate=False)]))
builder = builder.issuer_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, name, _validate=False)]))
# Gooogle seems to enforce the not before date strictly. Set the not before
# date to be UTC two minutes ago which should cover any clock skew.
now = datetime.datetime.utcnow()
@@ -16210,7 +16209,7 @@ def makeRoleIdNameMap():
accessErrorExit(cd)
for role in result:
GM.Globals[GM.MAP_ROLE_ID_TO_NAME][role['roleId']] = role['roleName']
GM.Globals[GM.MAP_ROLE_NAME_TO_ID][role['roleName']] = role['roleId']
GM.Globals[GM.MAP_ROLE_NAME_TO_ID][role['roleName'].lower()] = role['roleId']
def role_from_roleid(roleid):
if GM.Globals[GM.MAKE_ROLE_ID_NAME_MAP]:
@@ -16220,7 +16219,7 @@ def role_from_roleid(roleid):
def roleid_from_role(role):
if GM.Globals[GM.MAKE_ROLE_ID_NAME_MAP]:
makeRoleIdNameMap()
return GM.Globals[GM.MAP_ROLE_NAME_TO_ID].get(role, None)
return GM.Globals[GM.MAP_ROLE_NAME_TO_ID].get(role.lower(), None)
def getRoleId():
role = getString(Cmd.OB_ROLE_ITEM)
@@ -16344,7 +16343,7 @@ def _showAdminRole(role, i=0, count=0):
def doInfoAdminRole():
cd = buildGAPIObject(API.DIRECTORY)
fieldsList = PRINT_ADMIN_ROLES_FIELDS[:]
role, roleId = getRoleId()
_, roleId = getRoleId()
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if myarg == 'privileges':
@@ -16506,7 +16505,7 @@ ASSIGNEE_EMAILTYPE_TOFIELD_MAP = {
}
PRINT_ADMIN_FIELDS = ['roleAssignmentId', 'roleId', 'assignedTo', 'scopeType', 'orgUnitId']
PRINT_ADMIN_TITLES = ['roleAssignmentId', 'roleId', 'role',
'assignedTo', 'assignedToUser', 'assignedToGroup', 'assignedToServiceAccount',
'assignedTo', 'assignedToUser', 'assignedToGroup', 'assignedToServiceAccount', 'assignedToUnknown',
'scopeType', 'orgUnitId', 'orgUnit']
# gam print admins [todrive <ToDriveAttribute>*]
@@ -16536,6 +16535,7 @@ def doPrintShowAdmins():
def _setNamesFromIds(admin, privileges):
admin['role'] = role_from_roleid(admin['roleId'])
assignedTo = admin['assignedTo']
admin['assignedToUnknown'] = False
if assignedTo not in assignedToIdEmailMap:
assigneeType = admin.get('assigneeType')
assignedToField = ASSIGNEE_EMAILTYPE_TOFIELD_MAP.get(assigneeType, None)
@@ -16545,10 +16545,11 @@ def doPrintShowAdmins():
emailTypes=list(ASSIGNEE_EMAILTYPE_TOFIELD_MAP.keys()))
if not assignedToField and assigneeType in ASSIGNEE_EMAILTYPE_TOFIELD_MAP:
assignedToField = ASSIGNEE_EMAILTYPE_TOFIELD_MAP[assigneeType]
if assigneeType == 'unknown':
assignedToField = 'assignedToUnknown'
assigneeEmail = True
assignedToIdEmailMap[assignedTo] = {'assignedToField': assignedToField, 'assigneeEmail': assigneeEmail}
assignedToField = assignedToIdEmailMap[assignedTo]['assignedToField']
if assignedToField:
admin[assignedToField] = assignedToIdEmailMap[assignedTo]['assigneeEmail']
admin[assignedToIdEmailMap[assignedTo]['assignedToField']] = assignedToIdEmailMap[assignedTo]['assigneeEmail']
if privileges is not None:
admin.update(privileges)
if 'orgUnitId' in admin:
@@ -17771,20 +17772,32 @@ ORG_ITEMS_FIELD_MAP = {
'users': 'users',
}
# gam check org|ou <OrgUnitItem> [todrive <ToDriveAttribute>*]
# gam check ou|org <OrgUnitItem> [todrive <ToDriveAttribute>*]
# [<OrgUnitCheckName>*|(fields <OrgUnitCheckNameList>)]
# [filename <FileName>] [movetoou <OrgUnitItem>]
# [formatjson [quotechar <Character>]]
def doCheckOrgUnit():
def writeCommandInfo(field):
nonlocal commitBatch
if commitBatch:
f.write(f'{Cmd.COMMIT_BATCH_CMD}\n')
else:
commitBatch = True
f.write(f'{Cmd.PRINT_CMD} Move {field} from {orgUnitPath} to {moveToOrgUnitPath}\n')
cd = buildGAPIObject(API.DIRECTORY)
csvPF = CSVPrintFile(['orgUnitPath', 'orgUnitId', 'empty'])
FJQC = FormatJSONQuoteChar(csvPF)
orgUnitPath = None
f = orgUnitPath = None
fieldsList = []
titlesList = []
status, orgUnitPath, orgUnitId = checkOrgUnitPathExists(cd, getOrgUnitItem())
orgUnitPathLower = orgUnitPath.lower()
if not status:
entityDoesNotExistExit(Ent.ORGANIZATIONAL_UNIT, orgUnitPath)
orgUnitPathLower = orgUnitPath.lower()
fileName = 'CleanOuBatch.txt'
moveToOrgUnitPath = moveToOrgUnitPathLower = None
commitBatch = False
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if csvPF and myarg == 'todrive':
@@ -17797,12 +17810,26 @@ def doCheckOrgUnit():
fieldsList.append(field)
else:
invalidChoiceExit(field, list(ORG_ITEMS_FIELD_MAP), True)
elif myarg == 'filename':
fileName = setFilePath(getString(Cmd.OB_FILE_NAME))
elif myarg == 'movetoou':
movetoouLocation = Cmd.Location()
status, moveToOrgUnitPath, _ = checkOrgUnitPathExists(cd, getOrgUnitItem())
moveToOrgUnitPathLower = moveToOrgUnitPath.lower()
if not status:
entityDoesNotExistExit(Ent.ORGANIZATIONAL_UNIT, moveToOrgUnitPath)
else:
FJQC.GetFormatJSONQuoteChar(myarg, True)
if orgUnitPath is None:
missingArgumentExit('orgunit <OrgUnitItem>')
if not fieldsList:
fieldsList = ORG_ITEMS_FIELD_MAP.keys()
if moveToOrgUnitPath is not None:
Cmd.SetLocation(movetoouLocation)
if orgUnitPathLower == moveToOrgUnitPathLower:
usageErrorExit(Msg.OU_AND_MOVETOOU_CANNOT_BE_IDENTICAL.format(orgUnitPath, moveToOrgUnitPath))
if 'subous' in fieldsList and moveToOrgUnitPathLower.startswith(orgUnitPathLower):
usageErrorExit(Msg.OU_SUBOUS_CANNOT_BE_MOVED_TO_MOVETOOU.format(orgUnitPath, moveToOrgUnitPath))
fileName = setFilePath(fileName)
f = openFile(fileName, DEFAULT_FILE_WRITE_MODE)
orgUnitItemCounts = {}
for field in sorted(fieldsList):
title = ORG_ITEMS_FIELD_MAP[field]
@@ -17823,6 +17850,9 @@ def doCheckOrgUnit():
fields='nextPageToken,browsers(deviceId)')
for browsers in feed:
orgUnitItemCounts['browsers'] += len(browsers)
if f is not None and orgUnitItemCounts['browsers'] > 0:
writeCommandInfo('browsers')
f.write(f'gam move browsers ou {moveToOrgUnitPath} browserou {orgUnitPath}\n')
except (GAPI.invalidInput, GAPI.forbidden) as e:
entityActionFailedWarning([Ent.CHROME_BROWSER, None], str(e))
except GAPI.invalidOrgunit as e:
@@ -17851,6 +17881,9 @@ def doCheckOrgUnit():
if not pageToken:
_finalizeGAPIpagesResult(pageMessage)
printGotAccountEntities(totalItems)
if f is not None and orgUnitItemCounts['devices'] > 0:
writeCommandInfo('devices')
f.write(f'gam update ou {moveToOrgUnitPath} add cros_ou {orgUnitPath}\n')
break
except GAPI.invalidInput as e:
message = str(e)
@@ -17879,8 +17912,18 @@ def doCheckOrgUnit():
customer=_getCustomersCustomerIdWithC(),
filter="type == 'shared_drive'")
orgUnitItemCounts['sharedDrives'] = len(sds)
if f is not None and orgUnitItemCounts['sharedDrives'] > 0:
writeCommandInfo('Shared Drives')
for sd in sds:
name = sd['name'].split(';')[1]
f.write(f'gam update shareddrive {name} ou {moveToOrgUnitPath}\n')
if 'subous' in fieldsList:
orgUnitItemCounts['subOus'] = len(_getOrgUnits(cd, orgUnitPath, ['orgUnitPath'], 'children', False, False, None, None))
subOus = _getOrgUnits(cd, orgUnitPath, ['orgUnitPath'], 'children', False, False, None, None)
orgUnitItemCounts['subOus'] = len(subOus)
if f is not None and orgUnitItemCounts['subOus'] > 0:
writeCommandInfo('Sub OrgUnit')
for ou in subOus:
f.write(f'gam update ou {ou["orgUnitPath"]} parent {moveToOrgUnitPath}\n')
if 'users' in fieldsList:
printGettingAllEntityItemsForWhom(Ent.USER, orgUnitPath, entityType=Ent.ORGANIZATIONAL_UNIT)
pageMessage = getPageMessageForWhom()
@@ -17895,9 +17938,15 @@ def doCheckOrgUnit():
for user in users:
if orgUnitPathLower == user.get('orgUnitPath', '').lower():
orgUnitItemCounts['users'] += 1
if f is not None and orgUnitItemCounts['users'] > 0:
writeCommandInfo('users')
f.write(f'gam update ou {moveToOrgUnitPath} add ou {orgUnitPath}\n')
except (GAPI.invalidOrgunit, GAPI.orgunitNotFound, GAPI.invalidInput, GAPI.badRequest, GAPI.backendError,
GAPI.invalidCustomerId, GAPI.loginRequired, GAPI.resourceNotFound, GAPI.forbidden):
checkEntityDNEorAccessErrorExit(cd, Ent.ORGANIZATIONAL_UNIT, orgUnitPath)
if f is not None:
closeFile(f)
writeStderr(Msg.GAM_BATCH_FILE_WRITTEN.format(fileName))
empty = True
for count in orgUnitItemCounts.values():
if count > 0:
@@ -17913,7 +17962,7 @@ def doCheckOrgUnit():
csvPF.writeCSVfile(f'OrgUnit {orgUnitPath} Item Counts')
if not empty and GM.Globals[GM.SYSEXITRC] == 0:
setSysExitRC(ORGUNIT_NOT_EMPTY_RC)
ALIAS_TARGET_TYPES = ['user', 'group', 'target']
# gam create aliases|nicknames <EmailAddressEntity> user|group|target <UniqueID>|<EmailAddress>
@@ -61621,10 +61670,10 @@ def emptyDriveTrash(users):
kwargs['driveId'] = fileIdEntity['shareddrive']['driveId']
try:
callGAPI(drive.files(), 'emptyTrash',
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.INSUFFICIENT_FILE_PERMISSIONS],
**kwargs)
entityActionPerformed([Ent.USER, user, Ent.DRIVE_TRASH, kwargs['driveId']], i, count)
except GAPI.notFound as e:
except (GAPI.notFound, GAPI.insufficientFilePermissions) as e:
entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, kwargs['driveId']], str(e), i, count)
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userSvcNotApplicableOrDriveDisabled(user, str(e), i, count)
@@ -68382,8 +68431,8 @@ def forwardMessagesThreads(users, entityType):
if not gmail:
continue
service = gmail.users().messages() if entityType == Ent.MESSAGE else gmail.users().threads()
try:
if parameters['messageEntity'] is None:
if parameters['messageEntity'] is None:
try:
printGettingAllEntityItemsForWhom(entityType, user, i, count)
listResult = callGAPIpages(service, 'list', parameters['listType'],
pageMessage=getPageMessageForWhom(), maxItems=parameters['maxItems'],
@@ -68391,12 +68440,12 @@ def forwardMessagesThreads(users, entityType):
userId='me', q=parameters['query'], fields=parameters['fields'], includeSpamTrash=includeSpamTrash,
maxResults=GC.Values[GC.MESSAGE_MAX_RESULTS])
entityIds = [entity['id'] for entity in listResult]
except (GAPI.failedPrecondition, GAPI.permissionDenied, GAPI.invalid, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.USER, user], str(e), i, count)
continue
except (GAPI.serviceNotAvailable, GAPI.badRequest):
entityServiceNotApplicableWarning(Ent.USER, user, i, count)
continue
except (GAPI.failedPrecondition, GAPI.permissionDenied, GAPI.invalid, GAPI.invalidArgument) as e:
entityActionFailedWarning([Ent.USER, user], str(e), i, count)
continue
except (GAPI.serviceNotAvailable, GAPI.badRequest):
entityServiceNotApplicableWarning(Ent.USER, user, i, count)
continue
jcount = len(entityIds)
if jcount == 0:
entityNumEntitiesActionNotPerformedWarning([Ent.USER, user], entityType, jcount, Msg.NO_ENTITIES_MATCHED.format(Ent.Plural(entityType)), i, count)

View File

@@ -258,6 +258,7 @@ FORMAT_NOT_AVAILABLE = 'Format ({0}) not available'
FORMAT_NOT_DOWNLOADABLE = 'Format not downloadable'
FROM = 'From'
FULL_PATH_MUST_START_WITH_DRIVE = 'fullpath must start with {0} or {1}'
GAM_BATCH_FILE_WRITTEN = 'GAM batch file {0} written\n'
GAM_LATEST_VERSION_NOT_AVAILABLE = 'GAM Latest Version information not available'
GAM_OUT_OF_MEMORY = 'GAM has run out of memory. If this is a large Google Workspace instance, you should use a 64-bit version of GAM on Windows or a 64-bit version of Python on other systems.'
GENERATING_NEW_PRIVATE_KEY = 'Generating new private key'
@@ -420,6 +421,8 @@ ONLY_ONE_DEVICE_SELECTION_ALLOWED = 'Only one device selection allowed, filter =
ONLY_ONE_JSON_RANGE_ALLOWED = 'Only one range/json allowed'
ONLY_ONE_OWNER_ALLOWED = 'Only one owner allowed'
OR = 'or'
OU_AND_MOVETOOU_CANNOT_BE_IDENTICAL = 'ou {0} can not be be identical to movetoou {1}'
OU_SUBOUS_CANNOT_BE_MOVED_TO_MOVETOOU = 'ou {0} sub OUs can not be be moved to movetoou {1}'
PERMISSION_DENIED = 'The caller does not have permission'
PLEASE_CORRECT_YOUR_SYSTEM_TIME = 'Please correct your system time.'
PLEASE_ENTER_A_OR_M = 'Please enter a or m ...\n'