Compare commits

...

26 Commits
v6.54 ... v6.57

Author SHA1 Message Date
Jay Lee
85dd32e0ce Update build.yml 2023-04-17 08:17:43 -04:00
Jay Lee
28e418ff23 Update build.yml 2023-04-16 09:05:31 -04:00
Jay Lee
4eb89b187f Update build.yml 2023-04-14 19:43:41 -04:00
Jay Lee
c5734beef6 Update var.py 2023-04-14 18:53:52 -04:00
Jay Lee
f4735ebd80 Update build.yml 2023-04-14 16:07:45 -04:00
Jay Lee
43ae6a4a37 Update build.yml 2023-04-14 15:55:36 -04:00
Jay Lee
f362f58f95 fi 2023-04-14 18:42:59 +00:00
Jay Lee
6d211264fc Support delegated admin role assignments to groups 2023-04-14 18:35:44 +00:00
Jay Lee
3d919f5df6 fix legacy linux package name 2023-04-14 18:09:04 +00:00
Jay Lee
f9d5f9852a Update gam.spec 2023-04-12 16:49:14 -04:00
Jay Lee
0e79035765 Update build.yml 2023-04-12 16:38:59 -04:00
Jay Lee
d5cf38eaca single static list of libs 2023-04-12 14:56:31 +00:00
Jay Lee
1cfa14d8d2 only copy metadata for imports that exist 2023-04-12 14:38:24 +00:00
Jay Lee
bf5a50eb2a copy_metadata for all reqs. Fixes "gam version extended" dep outputs 2023-04-12 14:23:31 +00:00
Jay Lee
f296579aad Update transport.py 2023-04-11 10:43:21 -04:00
Jay Lee
16bb53d0e4 Update var.py 2023-04-11 09:54:43 -04:00
Jay Lee
b6e2549436 Update build.yml 2023-04-11 09:11:53 -04:00
Jay Lee
0814173210 [no ci] use default Vault export format 2023-04-11 08:18:33 -04:00
Jay Lee
375ffada5c Update build.yml 2023-04-11 08:02:52 -04:00
Jay Lee
ae37de0dd2 Update build.yml 2023-04-10 16:22:59 -04:00
Jay Lee
ce4b4771db Update build.yml 2023-04-10 15:10:20 -04:00
Jay Lee
56c61ac723 Update build.yml 2023-04-10 13:32:21 -04:00
Jay Lee
9900dd64b8 prepare 6.55 2023-04-10 17:14:36 +00:00
Jay Lee
53400b6322 Update __init__.py 2023-04-10 12:20:22 -04:00
Jay Lee
47537ab30a Update signjwt.py 2023-04-10 12:18:56 -04:00
Jay Lee
6a3692d7f4 catch signjwt refresh error on create 2023-04-10 10:21:59 -04:00
11 changed files with 152 additions and 74 deletions

View File

@@ -109,7 +109,7 @@ jobs:
path: |
bin.tar.xz
src/cpython
key: gam-${{ matrix.jid }}-20230405
key: gam-${{ matrix.jid }}-20230417
- name: Untar Cache archive
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
@@ -467,6 +467,8 @@ jobs:
git clone https://github.com/pyinstaller/pyinstaller.git
cd pyinstaller
export latest_release=$(git tag --list | grep -v dev | grep -v rc | sort -Vr | head -n1)
git checkout "${latest_release}"
# remove pre-compiled bootloaders so we fail if bootloader compile fails
rm -rvf PyInstaller/bootloader/*-*/*
@@ -544,8 +546,11 @@ jobs:
esac
echo "ldlib=${ldlib}"
$PYTHON -m staticx -l "${ldlib}" "${gam}" "${gam}-staticx"
rm -v "${gam}"
mv -v "${gam}-staticx" "${gam}"
- name: Basic Tests all jobs
id: basictests
run: |
$PYTHON -m unittest discover --start-directory ./ --pattern "*_test.py" --buffer
$gam version extended
@@ -559,8 +564,12 @@ jobs:
if [[ "${RUNNER_OS}" == "macOS" ]]; then
GAM_ARCHIVE="gam-${GAMVERSION}-macos-universal2.tar.xz"
elif [[ "${RUNNER_OS}" == "Linux" ]]; then
this_glibc_ver=$(ldd --version | awk '/ldd/{print $NF}')
GAM_ARCHIVE="gam-${GAMVERSION}-linux-$(arch)-glibc${this_glibc_ver}.tar.xz"
if [[ "${staticx}" == "yes" ]]; then
libver="legacy"
else
libver="glibc$(ldd --version | awk '/ldd/{print $NF}')"
fi
GAM_ARCHIVE="gam-${GAMVERSION}-linux-$(arch)-$libver}.tar.xz"
fi
tar -C dist/ --create --verbose --exclude-from "${GITHUB_WORKSPACE}/.github/actions/package_exclusions.txt" --file $GAM_ARCHIVE --xz gam
@@ -665,7 +674,9 @@ jobs:
$gam update group $newgroup add member $newuser
rm "${gampath}/enabledasa.txt"
$gam create admin $newuser _GROUPS_EDITOR_ROLE CUSTOMER # condition nonsecuritygroup
$gam create admin $newgroup _HELP_DESK_ADMIN_ROLE org_unit "${newou}"
GAM_CSV_ROW_FILTER="assignedToUser:regex:${newuser}" $gam print admins | $gam csv - gam delete admin "~roleAssignmentId"
GAM_CSV_ROW_FILTER="assignedToGroup:regex:${newgroup}" $gam print admins | $gam csv - gam delete admin "~roleAssignmentId"
touch "${gampath}/enabledasa.txt"
$gam csv sample.csv gam create user ~~email~~ firstname "GHA Bulk" lastname ~~email~~ gha.jid $JID ou "${newou}"
$gam csv sample.csv gam update user ~~email~~ recoveryphone 12125121110 recoveryemail jay0lee@gmail.com password random displayname "GitHub Actions Bulk ${JID}"
@@ -722,7 +733,7 @@ jobs:
$gam print vaultmatters matterstate open
$gam print vaultholds matter $matterid
$gam print vaultcount matter $matterid corpus mail everyone todrive
$gam create vaultexport matter $matterid name "GHA export $newbase" corpus mail accounts $newuser use_new_export false
$gam create vaultexport matter $matterid name "GHA export $newbase" corpus mail accounts $newuser
$gam print exports matter $matterid | $gam csv - gam info export $matterid id:~~id~~
touch "${gampath}/enabledasa.txt"
$gam csv sample.csv gam user ~email add calendar id:$newresource
@@ -806,9 +817,13 @@ jobs:
tar cJvvf bin.tar.xz bin/
publish:
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
if: github.event_name == 'push'
runs-on: ubuntu-latest
needs: build
permissions:
contents: write
packages: write
pull-requests: read
steps:
- uses: actions/checkout@v3
@@ -826,11 +841,18 @@ jobs:
files: |
gam-binaries/*
- name: Set datetime version string
id: dateversion
run: |
export dateversion="$(date +'%Y%m%d.%H%M%S')"
echo "Date version: ${dateversion}"
echo "dateversion=${dateversion}" >> $GITHUB_OUTPUT
- uses: "marvinpinto/action-automatic-releases@latest"
name: Publish draft release
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: latest
automatic_release_tag: "${{ steps.dateversion.outputs.dateversion }}"
prerelease: false
draft: true
files: |

View File

@@ -1,12 +1,15 @@
# -*- mode: python ; coding: utf-8 -*-
from os import getenv
from re import search
from sys import platform
from PyInstaller.utils.hooks import copy_metadata
from gam.var import GAM_VER_LIBS
extra_files = []
extra_files += copy_metadata('google-api-python-client')
for pkg in GAM_VER_LIBS:
extra_files += copy_metadata(pkg, recursive=True)
extra_files += [('cbcm-v1.1beta1.json', '.')]
extra_files += [('contactdelegation-v1.json', '.')]
extra_files += [('admin-directory_v1.1beta1.json', '.')]

View File

@@ -6,10 +6,7 @@ import configparser
import csv
import datetime
from email import message_from_string
try:
from importlib.metadata import version as lib_version
except ImportError:
from importlib_metadata import version as lib_version
from importlib.metadata import version as lib_version
import io
import json
import mimetypes
@@ -887,17 +884,7 @@ def doGAMVersion(checkForArgs=True):
doGAMCheckForUpdates(forceCheck)
if extended:
print(ssl.OPENSSL_VERSION)
libs = ['cryptography',
'filelock',
'google-auth-httplib2',
'google-auth-oauthlib',
'google-auth',
'httplib2',
'passlib',
'python-dateutil',
'yubikey-manager',
]
for lib in libs:
for lib in GAM_VER_LIBS:
try:
print(f'{lib} {lib_version(lib)}')
except:
@@ -1145,39 +1132,66 @@ def buildGAPIObjectNoAuthentication(api):
service = getService(api, httpObj)
return service
# Convert UID to email address
def get_user_email_from_id(uid, cd):
try:
result = gapi.call(
cd.users(),
'get',
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND],
userKey=uid,
fields='primaryEmail')
return result.get('primaryEmail')
except gapi_errors.GapiUserNotFoundError:
return
def get_group_email_from_id(uid, cd):
try:
result = gapi.call(
cd.groups(),
'get',
throw_reasons=[gapi_errors.ErrorReason.GROUP_NOT_FOUND],
groupKey=uid,
fields='email')
return result.get('email')
except gapi_errors.GapiGroupNotFoundError:
return
def convertUIDtoEmailAddress(emailAddressOrUID, cd=None, email_types=['user']):
'''convert UID to email address
returns email address and object type'''
if isinstance(email_types, str):
email_types = email_types.split(',')
normalizedEmailAddressOrUID = normalizeEmailAddressOrUID(emailAddressOrUID)
if normalizedEmailAddressOrUID.find('@') > 0:
return normalizedEmailAddressOrUID
return normalizedEmailAddressOrUID, 'email'
if not cd:
cd = buildGAPIObject('directory')
if 'user' in email_types:
try:
result = gapi.call(
cd.users(),
'get',
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND],
userKey=normalizedEmailAddressOrUID,
fields='primaryEmail')
if 'primaryEmail' in result:
return result['primaryEmail'].lower()
except gapi_errors.GapiUserNotFoundError:
pass
if 'group' in email_types:
try:
result = gapi.call(
cd.groups(),
'get',
throw_reasons=[gapi_errors.ErrorReason.GROUP_NOT_FOUND],
groupKey=normalizedEmailAddressOrUID,
fields='email')
if 'email' in result:
return result['email'].lower()
except gapi_errors.GapiGroupNotFoundError:
pass
if 'user' in email_types and 'group' in email_types:
# Google User IDs *TEND* to be integers while groups tend to have letters
# thus we can optimize which check we try first. We'll still check
# both since there is no guarantee this will always be true.
if normalizedEmailAddressOrUID.isdigit():
uid = get_user_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'user'
uid = get_group_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'group'
else:
uid = get_group_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'group'
uid = get_user_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'user'
elif 'user' in email_types:
uid = get_user_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'user'
elif 'group' in email_types:
uid = get_group_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'group'
if 'resource' in email_types:
try:
result = gapi.call(
@@ -1188,10 +1202,10 @@ def convertUIDtoEmailAddress(emailAddressOrUID, cd=None, email_types=['user']):
customer=GC_Values[GC_CUSTOMER_ID],
fields='resourceEmail')
if 'resourceEmail' in result:
return result['resourceEmail'].lower()
return result['resourceEmail'].lower(), 'resource'
except gapi_errors.GapiResourceNotFoundError:
pass
return normalizedEmailAddressOrUID
return normalizedEmailAddressOrUID, 'unknown'
# Convert email address to UID
@@ -1205,12 +1219,13 @@ def convertEmailAddressToUID(emailAddressOrUID, cd=None, email_type='user'):
result = gapi.call(
cd.users(),
'get',
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND],
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND,
gapi_errors.ErrorReason.BAD_REQUEST],
userKey=normalizedEmailAddressOrUID,
fields='id')
if 'id' in result:
return result['id']
except gapi_errors.GapiUserNotFoundError:
except (gapi_errors.GapiUserNotFoundError, gam.gapi.errors.GapiBadRequestError):
pass
try:
result = gapi.call(
@@ -1262,27 +1277,27 @@ def buildGAPIServiceObject(api, act_as, showAuthError=True, scopes=None):
def buildAlertCenterGAPIObject(user):
userEmail = convertUIDtoEmailAddress(user)
userEmail, _ = convertUIDtoEmailAddress(user)
return (userEmail, buildGAPIServiceObject('alertcenter', userEmail))
def buildActivityGAPIObject(user):
userEmail = convertUIDtoEmailAddress(user)
userEmail, _ = convertUIDtoEmailAddress(user)
return (userEmail, buildGAPIServiceObject('driveactivity', userEmail))
def buildDriveGAPIObject(user):
userEmail = convertUIDtoEmailAddress(user)
userEmail, _ = convertUIDtoEmailAddress(user)
return (userEmail, buildGAPIServiceObject('drive', userEmail))
def buildDrive3GAPIObject(user):
userEmail = convertUIDtoEmailAddress(user)
userEmail, _ = convertUIDtoEmailAddress(user)
return (userEmail, buildGAPIServiceObject('drive3', userEmail))
def buildGmailGAPIObject(user):
userEmail = convertUIDtoEmailAddress(user)
userEmail, _ = convertUIDtoEmailAddress(user)
return (userEmail, buildGAPIServiceObject('gmail', userEmail))
@@ -2306,7 +2321,7 @@ def doGetCourseInfo():
croom = buildGAPIObject('classroom')
courseId = addCourseIdScope(sys.argv[3])
info = gapi.call(croom.courses(), 'get', id=courseId)
info['ownerEmail'] = convertUIDtoEmailAddress(f'uid:{info["ownerId"]}')
info['ownerEmail'], _ = convertUIDtoEmailAddress(f'uid:{info["ownerId"]}')
display.print_json(info)
teachers = gapi.get_all_pages(croom.courses().teachers(),
'list',
@@ -2491,7 +2506,7 @@ def doPrintCourses():
if ownerEmails is not None:
ownerId = course['ownerId']
if ownerId not in ownerEmails:
ownerEmails[ownerId] = convertUIDtoEmailAddress(f'uid:{ownerId}',
ownerEmails[ownerId], _ = convertUIDtoEmailAddress(f'uid:{ownerId}',
cd=cd)
course['ownerEmail'] = ownerEmails[ownerId]
for field in skipFieldsList:
@@ -7199,7 +7214,8 @@ def enable_apis():
try:
_, projectId = google.auth.default(scopes=signjwt._IAM_SCOPES,
request=request)
except google.auth.exceptions.DefaultCredentialsError as e:
except (google.auth.exceptions.DefaultCredentialsError,
google.auth.exceptions.RefreshError) as e:
projectId = input('Please enter your project ID: ')
while a_or_m not in ['a', 'm']:
a_or_m = input('Do you want to enable projects [a]utomatically or [m]anually? (a/m): ').strip().lower()
@@ -7968,7 +7984,8 @@ def create_signjwt_serviceaccount():
try:
creds, sa_info['project_id'] = google.auth.default(scopes=signjwt._IAM_SCOPES,
request=request)
except google.auth.exceptions.DefaultCredentialsError as e:
except (google.auth.exceptions.DefaultCredentialsError,
google.auth.exceptions.RefreshError) as e:
controlflow.system_error_exit(2, e)
creds.refresh(request)
sa_info['client_email'] = creds.service_account_email

View File

@@ -84,7 +84,8 @@ class SignJwt(google.auth.crypt.Signer):
try:
credentials, _ = google.auth.default(scopes=_IAM_SCOPES,
request=request)
except google.auth.exceptions.DefaultCredentialsError as e:
except (google.auth.exceptions.DefaultCredentialsError,
google.auth.exceptions.RefreshError) as e:
controlflow.system_error_exit(2, e)
httpObj = transport.AuthorizedHttp(
credentials,

View File

@@ -18,9 +18,9 @@ def normalizeCalendarId(calname, checkPrimary=False):
return calname
if not GC_Values[GC_DOMAIN]:
GC_Values[GC_DOMAIN] = gam._getValueFromOAuth('hd')
return gam.convertUIDtoEmailAddress(calname,
email, _ = gam.convertUIDtoEmailAddress(calname,
email_types=['user', 'resource'])
return email
def buildCalendarGAPIObject(calname):
calendarId = normalizeCalendarId(calname)

View File

@@ -230,7 +230,7 @@ def print_():
todrive = True
i += 1
elif myarg == 'enterprisemember':
member = gam.convertUIDtoEmailAddress(sys.argv[i + 1], email_types=['user', 'group'])
member, _ = gam.convertUIDtoEmailAddress(sys.argv[i + 1], email_types=['user', 'group'])
usemember = f"member_key_id == '{member}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels"
i += 2
elif myarg == 'delimiter':
@@ -501,7 +501,7 @@ def print_members():
)
i += 2
elif myarg == 'enterprisemember':
member = gam.convertUIDtoEmailAddress(sys.argv[i + 1], email_types=['user', 'group'])
member, _ = gam.convertUIDtoEmailAddress(sys.argv[i + 1], email_types=['user', 'group'])
usemember = f"member_key_id == '{member}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels"
i += 2
elif myarg in ['cigroup', 'cigroups']:
@@ -876,6 +876,13 @@ def update():
'cloudidentity.googleapis.com/groups.discussion_forum': ''
}
i += 1
elif myarg == 'locked':
body['labels'] = {
'cloudidentity.googleapis.com/groups.locked': '',
'cloudidentity.googleapis.com/groups.security': '',
'cloudidentity.googleapis.com/groups.discussion_forum': ''
}
i += 1
elif myarg == 'dynamicsecurity':
body['labels'] = {
'cloudidentity.googleapis.com/groups.dynamic': '',

View File

@@ -16,7 +16,9 @@ NONSECURITY_GROUP_CONDITION = f'!{SECURITY_GROUP_CONDITION}'
def create():
cd = gapi_directory.build()
user = gam.normalizeEmailAddressOrUID(sys.argv[3])
body = {'assignedTo': gam.convertEmailAddressToUID(user, cd)}
body = {'assignedTo': gam.convertEmailAddressToUID(sys.argv[3],
cd=cd,
email_type='any')}
role = sys.argv[4]
body['roleId'] = gapi_directory_roles.getRoleId(role)
body['scopeType'] = sys.argv[5].upper()
@@ -70,7 +72,7 @@ def print_():
item_fields = ['roleAssignmentId', 'roleId', 'assignedTo', 'scopeType', 'orgUnitId']
titles = [
'roleAssignmentId', 'roleId', 'role', 'assignedTo', 'assignedToUser',
'scopeType', 'orgUnitId', 'orgUnit'
'assignedToGroup', 'scopeType', 'orgUnitId', 'orgUnit'
]
csvRows = []
i = 3
@@ -107,7 +109,21 @@ def print_():
admin_attrib = {}
for key, value in list(admin.items()):
if key == 'assignedTo':
admin_attrib['assignedToUser'] = gam.user_from_userid(value)
email_types = admin_attrib.get('assigneeType')
if email_types == 'user':
email_field = 'assignedToUser'
elif email_types == 'group':
email_field = 'assignedToGroup'
else:
email_field = None
assignment_email, assignment_type = gam.convertUIDtoEmailAddress(f'uid:{value}', cd, email_types=['user', 'group'])
if not email_field and assignment_type in ['user', 'group']:
if assignment_type == 'user':
email_field = 'assignedToUser'
else:
email_field = 'assignedToGroup'
if email_field:
admin_attrib[email_field] = assignment_email
elif key == 'roleId':
admin_attrib['role'] = gapi_directory_roles.role_from_roleid(value)
elif key == 'orgUnitId':

View File

@@ -4,5 +4,5 @@ import gam
def build(user=None):
if not user:
user = gam._get_admin_email()
userEmail = gam.convertUIDtoEmailAddress(user)
userEmail, _ = gam.convertUIDtoEmailAddress(user)
return (userEmail, gam.buildGAPIServiceObject('drive3', userEmail))

View File

@@ -511,7 +511,7 @@ def getHoldInfo():
account_type = 'group' if results['corpus'] == 'GROUPS' else 'user'
for i in range(0, len(results['accounts'])):
uid = f'uid:{results["accounts"][i]["accountId"]}'
acct_email = gam.convertUIDtoEmailAddress(uid, cd, [account_type])
acct_email, _ = gam.convertUIDtoEmailAddress(uid, cd, [account_type])
results['accounts'][i]['email'] = acct_email
if 'orgUnit' in results:
results['orgUnit']['orgUnitPath'] = gapi_directory_orgunits.info(
@@ -792,7 +792,7 @@ def getMatterInfo():
cd = gam.buildGAPIObject('directory')
for i in range(0, len(result['matterPermissions'])):
uid = f'uid:{result["matterPermissions"][i]["accountId"]}'
user_email = gam.convertUIDtoEmailAddress(uid, cd)
user_email, _ = gam.convertUIDtoEmailAddress(uid, cd)
result['matterPermissions'][i]['email'] = user_email
display.print_json(result)

View File

@@ -10,7 +10,7 @@ from gam.var import GC_TLS_MIN_VERSION
from gam.var import GC_Values
# Bump default retries
#httplib2.RETRIES = 5
httplib2.RETRIES = 5
def create_http(cache=None,
timeout=None,

View File

@@ -8,7 +8,7 @@ import platform
import re
GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>'
GAM_VERSION = '6.54'
GAM_VERSION = '6.57'
GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
GAM_URL = 'https://jaylee.us/gam'
@@ -17,6 +17,18 @@ GAM_INFO = (
f'Python {platform.python_version()} {sys.version_info.releaselevel} / '
f'{platform.platform()} {platform.machine()}')
# Packages we want version info available
# for "gam version extended"
GAM_VER_LIBS = ['cryptography',
'filelock',
'google-auth-httplib2',
'google-auth-oauthlib',
'google-auth',
'httplib2',
'passlib',
'python-dateutil',
'yubikey-manager',
]
GAM_RELEASES = 'https://github.com/GAM-team/GAM/releases'
GAM_WIKI = 'https://github.com/GAM-team/GAM/wiki'
GAM_ALL_RELEASES = 'https://api.github.com/repos/GAM-team/GAM/releases'