Added variable oauth2_txt_lock_mode to gam.cfg

This commit is contained in:
Ross Scroggs
2026-01-21 09:55:50 -08:00
parent cd34c3d1e2
commit 613cae987f
5 changed files with 38 additions and 8 deletions

View File

@@ -1,3 +1,9 @@
7.32.02
Added variable `oauth2_txt_lock_mode` to `gam.cfg`, the default is 644 and valid values are: 644, 664, 666.
This value is used to set the file permissions on the `oauth2.txt.lock` file. In very special cases where
daemon processes, e.g. Apache/httpd, are running GAM, the value 666 may have to be used.
7.32.01
Added option `(addcsvdata <FieldName> <String>)*` to `gam <CrOSTypeEntity> issuecommand command <CrOSCommand> csv`

View File

@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
"""
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
__version__ = '7.32.01'
__version__ = '7.32.02'
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
# pylint: disable=wrong-import-position
@@ -4748,7 +4748,7 @@ def getClientCredentials(forceRefresh=False, forceWrite=False, filename=None, ap
"""Gets OAuth2 credentials which are guaranteed to be fresh and valid.
Locks during read and possible write so that only one process will
attempt refresh/write when running in parallel. """
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK])
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], mode=GC.Values[GC.OAUTH2_TXT_LOCK_MODE])
with lock:
writeCreds, credentials = getOauth2TxtCredentials(api=api, noDASA=noDASA, refreshOnly=refreshOnly, noScopes=noScopes)
if not credentials:
@@ -10760,7 +10760,7 @@ Continue to authorization by entering a 'c'
menu = oauth2_menu % tuple(range(numScopes))
selectedScopes = ['*'] * numScopes
if currentScopes is None and clientAccess:
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK])
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], mode=GC.Values[GC.OAUTH2_TXT_LOCK_MODE])
with lock:
_, credentials = getOauth2TxtCredentials(exitOnError=False)
if credentials and credentials.scopes is not None:
@@ -11258,7 +11258,7 @@ def doOAuthRequest(currentScopes, login_hint, verifyScopes=False):
access_type='offline',
login_hint=login_hint,
open_browser=not GC.Values[GC.NO_BROWSER])
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK])
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], mode=GC.Values[GC.OAUTH2_TXT_LOCK_MODE])
with lock:
writeClientCredentials(credentials, GC.Values[GC.OAUTH2_TXT])
entityActionPerformed([Ent.OAUTH2_TXT_FILE, GC.Values[GC.OAUTH2_TXT]])
@@ -11308,7 +11308,7 @@ def exitIfNoOauth2Txt():
def doOAuthDelete():
checkForExtraneousArguments()
exitIfNoOauth2Txt()
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], timeout=10)
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], mode=GC.Values[GC.OAUTH2_TXT_LOCK_MODE], timeout=10)
with lock:
_, credentials = getOauth2TxtCredentials(noScopes=True)
if not credentials:
@@ -11392,7 +11392,7 @@ def doOAuthUpdate():
login_hint = getEmailAddress(noUid=True, optional=True)
checkForExtraneousArguments()
exitIfNoOauth2Txt()
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK])
lock = FileLock(GM.Globals[GM.OAUTH2_TXT_LOCK], mode=GC.Values[GC.OAUTH2_TXT_LOCK_MODE])
with lock:
jsonData = readFile(GC.Values[GC.OAUTH2_TXT], continueOnError=True, displayError=False)
if not jsonData:
@@ -45905,6 +45905,20 @@ def verifyUserPrimaryEmail(cd, user, createIfNotFound, i, count):
entityUnknownWarning(Ent.USER, user, i, count)
return False
# gam create guestuser <EmailAddress>
def doCreateGuestUser():
cd = buildGAPIObject(API.DIRECTORY)
body = {'primaryGuestEmail': getEmailAddress(noUid=True),
'customer': GC.Values[GC.CUSTOMER_ID]}
checkForExtraneousArguments()
try:
result = callGAPI(cd.users(), 'createGuest',
throwReasons=[GAPI.FAILED_PRECONDITION],
body=body)
entityActionPerformed([Ent.GUEST_USER, result['primaryGuestEmail']])
except (GAPI.failedPrecondition) as e:
entityActionFailedExit([Ent.GUEST_USER, body['primaryGuestEmail']], str(e))
# gam <UserTypeEntity> update user <UserAttribute>*
# [verifynotinvitable|alwaysevict] [noactionifalias]
# [updateprimaryemail <RESEarchPattern> <RESubstitution>]
@@ -79485,6 +79499,7 @@ MAIN_ADD_CREATE_FUNCTIONS = {
Cmd.ARG_GROUP: doCreateGroup,
Cmd.ARG_GUARDIAN: doInviteGuardian,
Cmd.ARG_GUARDIANINVITATION: doInviteGuardian,
Cmd.ARG_GUESTUSER: doCreateGuestUser,
Cmd.ARG_INBOUNDSSOASSIGNMENT: doCreateInboundSSOAssignment,
Cmd.ARG_INBOUNDSSOCREDENTIAL: doCreateInboundSSOCredential,
Cmd.ARG_INBOUNDSSOPROFILE: doCreateInboundSSOProfile,
@@ -81218,6 +81233,7 @@ USER_COMMANDS_OBJ_ALIASES = {
Cmd.ARG_GUARDIANINVITATIONS: Cmd.ARG_GUARDIANINVITATION,
Cmd.ARG_GUARDIANINVITE: Cmd.ARG_GUARDIANINVITATION,
Cmd.ARG_GUARDIANS: Cmd.ARG_GUARDIAN,
Cmd.ARG_GUESTUSERS: Cmd.ARG_GUESTUSER,
Cmd.ARG_HOLD: Cmd.ARG_VAULTHOLD,
Cmd.ARG_HOLDS: Cmd.ARG_VAULTHOLD,
Cmd.ARG_IMAP4: Cmd.ARG_IMAP,

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2025 Ross Scroggs All Rights Reserved.
# Copyright (C) 2026 Ross Scroggs All Rights Reserved.
#
# All Rights Reserved.
#
@@ -218,6 +218,8 @@ NUM_TBATCH_THREADS = 'num_tbatch_threads'
NUM_THREADS = 'num_threads'
# Path to oauth2.txt
OAUTH2_TXT = 'oauth2_txt'
# File permissions for oauth2.txt.lock
OAUTH2_TXT_LOCK_MODE = 'oauth2_txt_lock_mode'
# Path to oauth2service.json
OAUTH2SERVICE_JSON = 'oauth2service_json'
# Output date format, empty defalts to ISOFormat
@@ -416,6 +418,7 @@ Defaults = {
NUM_TBATCH_THREADS: '2',
NUM_THREADS: '5',
OAUTH2_TXT: FN_OAUTH2_TXT,
OAUTH2_TXT_LOCK_MODE: '644',
OAUTH2SERVICE_JSON: FN_OAUTH2SERVICE_JSON,
OUTPUT_DATEFORMAT: '',
OUTPUT_TIMEFORMAT: '',
@@ -587,6 +590,7 @@ VAR_INFO = {
NUM_TBATCH_THREADS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 1000)},
NUM_THREADS: {VAR_TYPE: TYPE_INTEGER, VAR_ENVVAR: 'GAM_THREADS', VAR_LIMITS: (1, 1000)},
OAUTH2_TXT: {VAR_TYPE: TYPE_FILE, VAR_ENVVAR: 'OAUTHFILE', VAR_ACCESS: os.R_OK | os.W_OK},
OAUTH2_TXT_LOCK_MODE: {VAR_TYPE: TYPE_CHOICE, VAR_CHOICES: {'644': 0o644, '664': 0o664, '666': 0o666}},
OAUTH2SERVICE_JSON: {VAR_TYPE: TYPE_FILE, VAR_ENVVAR: 'OAUTHSERVICEFILE', VAR_ACCESS: os.R_OK | os.W_OK},
OUTPUT_DATEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
OUTPUT_TIMEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},

View File

@@ -928,6 +928,8 @@ class GamCLArgs():
ARG_GUARDIANINVITE = 'guardianinvite'
ARG_GUARDIANINVITATION = 'guardianinvitation'
ARG_GUARDIANINVITATIONS = 'guardianinvitations'
ARG_GUESTUSER = 'guestuser'
ARG_GUESTUSERS = 'guestusers'
ARG_HOLD = 'hold'
ARG_HOLDS = 'holds'
ARG_IMAP = 'imap'

View File

@@ -253,6 +253,7 @@ class GamEntity():
GUARDIAN = 'guar'
GUARDIAN_INVITATION = 'gari'
GUARDIAN_AND_INVITATION = 'gani'
GUEST_USER = 'gstu'
IAM_POLICY = 'iamp'
IMAP_ENABLED = 'imap'
INBOUND_SSO_ASSIGNMENT = 'insa'
@@ -358,7 +359,7 @@ class GamEntity():
SNIPPETS_ENABLED = 'snip'
SSO_KEY = 'ssok'
SSO_SETTINGS = 'ssos'
SOURCE_USER = 'src'
SOURCE_USER = 'srcu'
SPREADSHEET = 'sprd'
SPREADSHEET_RANGE = 'ssrn'
START_TIME = 'strt'
@@ -620,6 +621,7 @@ class GamEntity():
GROUP_MEMBERSHIP_TREE: ['Group Membership Trees', 'Group Membership Tree'],
GROUP_SETTINGS: ['Group Settings', 'Group Settings'],
GROUP_TREE: ['Group Trees', 'Group Tree'],
GUEST_USER: ['Guest Users', 'Guest User'],
GUARDIAN: ['Guardians', 'Guardian'],
GUARDIAN_INVITATION: ['Guardian Invitations', 'Guardian Invitation'],
GUARDIAN_AND_INVITATION: ['Guardians and Invitations', 'Guardian and Invitation'],