Compare commits

..

61 Commits
v3.8 ... v4.02

Author SHA1 Message Date
Jay Lee
b6b6824ee1 whatsnew for 4.02 2016-11-04 14:25:59 -04:00
Jay Lee
2902fc8931 Merge branch 'master' of https://github.com/jay0lee/GAM 2016-11-04 14:14:27 -04:00
Jay Lee
1a6ec398b2 gam calendar <> deleteevent 2016-11-04 14:14:21 -04:00
Ross Scroggs
7d849e0cc0 Code fixes, cleanup (#315)
* Code fixes, cleanup

Fix doCheckServiceAccount to pass correct api to buildGAPIServiceObject
pylint cleanup
Simplify passing login_hint to doRequestOAuth in main
Give error if invalid argument after gam <users> check

* Fix error

* Use try accept
2016-11-04 14:13:39 -04:00
Jay Lee
2958bd9f86 GAM 4.02 2016-11-04 12:02:35 -04:00
Jay Lee
15d93c9e5d pause at end of gam-setup.bat 2016-11-04 11:48:10 -04:00
Jay Lee
082c34b453 prompt for regular user to test service account. 2016-11-04 11:39:20 -04:00
Jay Lee
32e7932050 fix potential IndexError 2016-11-03 14:42:07 -04:00
Jay Lee
6becd08f3c fix login_hint issues 2016-11-03 14:30:14 -04:00
Jay Lee
26fbf9c524 don't run setup on upgrades 2016-11-03 14:20:15 -04:00
Ross Scroggs
d9124f3ffa Fix doRequestOAuth to find proper client secrets JSON file (#313)
The environment variables GAMUSERCONFIGDIR and CLIENTSECRETS are
factored in to GC_Values[GC_CLIENT_SECRETS_JSON]. On a new
installation, it will have the value
os.path.join(GM_Globals[GM_GAM_PATH], FN_CLIENT_SECRETS_JSON) but on an
existing installation the environment variables may have it located
somewhere else.
2016-11-03 13:50:43 -04:00
Jay Lee
aa1db89bd3 include gam-setup.bat in MSI and zip. Call it from MSI at end of install. 2016-11-03 13:29:26 -04:00
Jay Lee
ff3a8644ec Merge branch 'master' of https://github.com/jay0lee/GAM 2016-11-03 12:49:32 -04:00
Jay Lee
4721469b1d prettify oauth2.txt 2016-11-03 12:49:19 -04:00
Jay Lee
c4a3d29964 Easier to copy scope list 2016-11-03 12:48:46 -04:00
Jay Lee
1454526e65 gam-setup.bat file to automate Windows setup
part of #309
2016-11-03 11:38:23 -04:00
Ross Scroggs
2c0026512d Fix doGamVersion (#311)
* Fix doGamVersion

doGamVersion is called by showUsage with checkForArgs=False to keep
doGamVersion from wandering through some other command’s arguments

* Update documentation

* Further doGamVersion cleanup

Allow simple and check, keep pylint happy

* gam version simple is script only option, not exposed to user
2016-11-02 17:30:54 -04:00
Jay Lee
3c85da292e GAM 4.01 2016-11-02 15:28:15 -04:00
Jay Lee
c7b5251b03 prompt for admin email and use as hint 2016-11-02 15:23:43 -04:00
Jay Lee
5307a560bd fix service account create call 2016-11-02 14:45:17 -04:00
Jay Lee
059e6a1813 fix gam version 2016-11-02 13:30:42 -04:00
Jay Lee
395a561b8c GAM v4.0 2016-11-02 13:17:44 -04:00
Jay Lee
6c3a744ed3 improve project create instructions. 2016-11-02 13:15:10 -04:00
Jay Lee
907126d642 gam version simple, save project files to GAM path 2016-11-02 12:59:38 -04:00
Jay Lee
9e4506141e add message to restart term 2016-11-02 12:43:40 -04:00
Jay Lee
5deac72484 authorize admin and check service account 2016-11-02 11:40:00 -04:00
Jay Lee
9def6e6d73 run alias command. 2016-11-02 11:02:11 -04:00
Jay Lee
fefe9de384 further improvements to scope check. 2016-11-02 10:20:27 -04:00
Jay Lee
f8341be9ea add statement about extract starting. 2016-11-02 09:47:12 -04:00
Jay Lee
3c50f464cc add -p argument to disable profile update 2016-11-02 09:33:46 -04:00
Jay Lee
6961a0e1b3 more details on check serviceaccount 2016-11-01 22:32:33 -04:00
Jay Lee
3fa6cde6b0 another change to improve missing scope errors 2016-11-01 22:23:34 -04:00
Jay Lee
4129e05f5e check serviceaccount command, better error on missing service scopes 2016-11-01 22:13:45 -04:00
Jay Lee
42137297a1 extra space 2016-11-01 12:17:46 -04:00
Jay Lee
bc64e9a67c fix color escaping on MacOS 2016-11-01 12:16:49 -04:00
Jay Lee
e1ec8b8649 fix color function calls 2016-11-01 12:07:51 -04:00
Jay Lee
b9ec06807b message color and more error checking. 2016-11-01 12:01:33 -04:00
Jay Lee
e3d826cdb3 catch tar errors and exit. 2016-11-01 11:53:30 -04:00
Jay Lee
4306dba9f1 Merge branch 'master' of https://github.com/jay0lee/GAM 2016-11-01 11:44:30 -04:00
Jay Lee
cf397c228c MacOS >= 10.10 please 2016-11-01 11:44:05 -04:00
Jay Lee
a2a6719333 Update macos-build.sh 2016-10-31 14:57:21 -04:00
Jay Lee
7cef626a6f Add GAM to Windows PATH 2016-10-31 14:45:31 -04:00
Jay Lee
be44ae4322 newlines and stuff 2016-10-31 13:33:33 -04:00
Jay Lee
4c00b54ad4 no trailing slash 2016-10-31 13:30:22 -04:00
Jay Lee
8508ee4afa force to lower 2016-10-31 13:15:39 -04:00
Jay Lee
17b2c4091d login_hint, not hint 2016-10-31 12:23:45 -04:00
Jay Lee
925f4532bc IndexError, not KeyError 2016-10-31 12:20:04 -04:00
Jay Lee
786bbe5609 handle ToS not accepted yet for project create 2016-10-31 12:17:43 -04:00
Jay Lee
6be52c8b3c MacOS doesn't seem to like ~ as /home/jay 2016-10-31 09:37:17 -04:00
Jay Lee
2c2046a784 arguments and further improvements to gam-install.sh 2016-10-31 09:29:50 -04:00
Jay Lee
20de452685 Linux and MacOS install script 2016-10-31 05:57:47 -04:00
Jay Lee
df603937ee fix service account url display 2016-10-29 14:56:33 -04:00
Jay Lee
315a1db144 Merge branch 'master' of https://github.com/jay0lee/GAM 2016-10-29 14:09:42 -04:00
Jay Lee
968c096a99 "gam create project" rough draft. Needs a lot of work but first steps towards solving #309 2016-10-29 14:09:08 -04:00
Jay Lee
6703519d36 remove email-audit include 2016-10-28 12:07:53 -04:00
Jay Lee
9dd8696c1e remove email-audit include 2016-10-28 12:07:28 -04:00
Jay Lee
df7c12b737 Remove email-audit include 2016-10-28 12:07:06 -04:00
Jay Lee
7cfba0ada1 remove email-audit-v1.json file 2016-10-28 12:06:16 -04:00
Jay Lee
2ee5109424 remove email-audit API mapping 2016-10-28 12:05:44 -04:00
Jay Lee
721f787f0f Update macos-gam.spec 2016-10-28 10:56:08 -04:00
Jay Lee
bc62f7a9f6 Update linux-gam.spec 2016-10-28 10:55:37 -04:00
12 changed files with 660 additions and 69 deletions

View File

@@ -1,34 +0,0 @@
{
"kind": "discovery#restDescription",
"discoveryVersion": "v1",
"id": "email-audit:v1",
"name": "email-audit",
"version": "v1",
"revision": "20130823",
"title": "Email Audit API",
"description": "Lets you perform Google Apps email audits",
"ownerDomain": "google.com",
"ownerName": "Google",
"icons": {
"x16": "http://www.google.com/images/icons/product/search-16.gif",
"x32": "http://www.google.com/images/icons/product/search-32.gif"
},
"documentationLink": "https://developers.google.com/admin-sdk/email-audit",
"protocol": "rest",
"baseUrl": "https://apps-apis.google.com/",
"rootUrl": "https://apps-apis.google.com/",
"servicePath": "/a/feeds/compliance/audit/",
"auth": {
"oauth2": {
"scopes": {
"https://apps-apis.google.com/a/feeds/compliance/audit/": {
"description": "Manage email audits"
}
}
}
},
"schemas": {
},
"resources": {
}
}

280
src/gam-install.sh Executable file
View File

@@ -0,0 +1,280 @@
#!/usr/bin/env bash
usage()
{
cat << EOF
GAM installation script.
OPTIONS:
-h show help.
-d Directory where gam folder will be installed. Default is \$HOME/bin/
-a Architecture to install (i386, x86_64, arm). Default is to detect your arch with "uname -m".
-o OS we are running (linux, macos). Default is to detect your OS with "uname -s".
-p Profile update (true, false). Should script add gam command to environment. Default is true.
-u Admin user email address to use with GAM. Default is to prompt.
-r Regular user email address. Used to test service account access to user data. Default is to prompt.
-v Version to install (latest, prerelease, draft, 3.8, etc). Default is latest.
EOF
}
target_dir="$HOME/bin"
gamarch=$(uname -m)
gamos=$(uname -s)
update_profile=true
gamversion="latest"
adminuser=""
regularuser=""
while getopts "hd:a:o:p:u:r:v:" OPTION
do
case $OPTION in
h) usage; exit;;
d) target_dir=$OPTARG;;
a) gamarch=$OPTARG;;
o) gamos=$OPTARG;;
p) update_profile=$OPTARG;;
u) adminuser=$OPTARG;;
r) regularuser=$OPTARG;;
v) gamversion=$OPTARG;;
?) usage; exit;;
esac
done
update_profile() {
[ -f "$1" ] || return 1
grep -F "$alias_line" "$1" > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo_yellow "Adding gam alias to profile file $1."
echo -e "\n$alias_line" >> "$1"
else
echo_yellow "gam alias already exists in profile file $1. Skipping add."
fi
}
echo_red()
{
echo -e "\x1B[1;31m$1"
echo -e '\x1B[0m'
}
echo_green()
{
echo -e "\x1B[1;32m$1"
echo -e '\x1B[0m'
}
echo_yellow()
{
echo -e "\x1B[1;33m$1"
echo -e '\x1B[0m'
}
case $gamos in
[lL]inux)
gamos="linux"
case $gamarch in
x86_64) gamfile="linux-x86_64.tar.xz";;
i?86) gamfile="linux-i686.tar.xz";;
arm*) gamfile="linux-armv7l.tar.xz";;
*)
echo_red "ERROR: this installer currently only supports i386, x86_64 and arm Linux. Looks like you're running on $gamarch. Exiting."
exit
esac
;;
[Mm]ac[Oo][sS]|[Dd]arwin)
osver=$(sw_vers -productVersion | awk -F'.' '{print $2}')
if (( $osver < 10 )); then
echo_red "ERROR: GAM currently requires MacOS 10.10 or newer. You are running MacOS 10.$osver. Please upgrade."
exit
else
echo_green "Good, you're running MacOS 10.$osver..."
fi
gamos="macos"
gamfile="macos.tar.xz"
;;
*)
echo_red "Sorry, this installer currently only supports Linux and MacOS. Looks like you're runnning on $gamos. Exiting."
exit
;;
esac
if [ "$gamversion" == "latest" -o "$gamversion" == "prerelease" -o "$gamversion" == "draft" ]; then
release_url="https://api.github.com/repos/jay0lee/GAM/releases"
else
release_url="https://api.github.com/repos/jay0lee/GAM/releases/tags/v$gamversion"
fi
echo_yellow "Checking GitHub URL $release_url for $gamversion GAM release..."
release_json=$(curl -s $release_url 2>&1 /dev/null)
echo_yellow "Getting file and download URL..."
# Python is sadly the nearest to universal way to safely handle JSON with Bash
# At least this code should be compatible with just about any Python version ever
# unlike GAM itself. If some users don't have Python we can try grep / sed / etc
# but that gets really ugly
pycode="import json
import sys
attrib = sys.argv[1]
gamversion = sys.argv[2]
release = json.load(sys.stdin)
if type(release) is list:
for a_release in release:
if a_release['prerelease'] and gamversion != 'prerelease':
continue
elif a_release['draft'] and gamversion != 'draft':
continue
release = a_release
break
for asset in release['assets']:
if asset[sys.argv[1]].endswith('$gamfile'):
print asset[sys.argv[1]]
break"
browser_download_url=$(echo "$release_json" | python -c "$pycode" browser_download_url $gamversion)
name=$(echo "$release_json" | python -c "$pycode" name $gamversion)
# Temp dir for archive
temp_archive_dir=$(mktemp -d)
echo_yellow "Downloading file $name from $browser_download_url to $temp_archive_dir."
# Save archive to temp w/o losing our path
(cd $temp_archive_dir && curl -O -L $browser_download_url)
mkdir -p $target_dir
echo_yellow "Extracting archive to $target_dir"
tar xf $temp_archive_dir/$name -C $target_dir
rc=$?
if (( $rc != 0 )); then
echo_red "ERROR: extracting the GAM archive with tar failed with error $rc. Exiting."
exit
else
echo_green "Finished extracting GAM archive."
fi
# Update profile to add gam command
if [ "$update_profile" = true ]; then
alias_line="alias gam=\"$target_dir/gam/gam\""
if [ "$gamos" == "linux" ]; then
update_profile "$HOME/.bashrc" || update_profile "$HOME/.bash_profile"
elif [ "$gamos" == "macos" ]; then
update_profile "$HOME/.profile" || update_profile "$HOME/.bash_profile"
fi
else
echo_yellow "skipping profile update."
fi
while true; do
read -p "Can you run a full browser on this machine? (usually Y for MacOS, N for Linux if you SSH into this machine) " yn
case $yn in
[Yy]*)
break
;;
[Nn]*)
touch $target_dir/gam/nobrowser.txt > /dev/null 2>&1
break
;;
*)
echo_red "Please answer yes or no."
;;
esac
done
echo
project_created=false
while true; do
read -p "GAM is now installed. Are you ready to set up a Google API project for GAM? (yes or no) " yn
case $yn in
[Yy]*)
if [ "$adminuser" == "" ]; then
read -p "Please enter your G Suite admin email address: " adminuser
fi
$target_dir/gam/gam create project $adminuser
rc=$?
if (( $rc == 0 )); then
echo_green "Project creation complete."
project_created=true
break
else
echo_red "Projection creation failed. Trying again. Say N to skip projection creation."
fi
;;
[Nn]*)
echo -e "\nYou can create an API project later by running:\n\ngam create project\n"
break
;;
*)
echo_red "Please answer yes or no."
;;
esac
done
admin_authorized=false
while $project_created; do
read -p "Are you ready to authorize GAM to perform G Suite management operations as your admin account? (yes or no) " yn
case $yn in
[Yy]*)
$target_dir/gam/gam oauth create $adminuser
rc=$?
if (( $rc == 0 )); then
echo_green "Admin authorization complete."
admin_authorized=true
break
else
echo_red "Admin authorization failed. Trying again. Say N to skip admin authorization."
fi
;;
[Nn]*)
echo -e "\nYou can authorize an admin later by running:\n\ngam oauth create\n"
break
;;
*)
echo_red "Please answer yes or no."
;;
esac
done
service_account_authorized=false
while $project_created; do
read -p "Are you ready to authorize GAM to manage G Suite user data and settings? (yes or no) " yn
case $yn in
[Yy]*)
if [ "$regularuser" == "" ]; then
read -p "Please enter the email address of a regular G Suite user: " regularuser
fi
echo_yellow "Great! Checking service account scopes.This will fail the first time. Follow the steps to authorize and retry. It can take a few minutes for scopes to PASS after they've been authorized in the admin console."
$target_dir/gam/gam user $adminuser check serviceaccount
rc=$?
if (( $rc == 0 )); then
echo_green "Service account authorization complete."
service_account_authorized=true
break
else
echo_red "Service account authorization failed. Confirm you entered the scopes correctly in the admin console. It can take a few minutes for scopes to PASS after they are entered in the admin console so if you're sure you entered them correctly, go grab a coffee and then hit Y to try again. Say N to skip admin authorization."
fi
;;
[Nn]*)
echo -e "\nYou can authorize a service account later by running:\n\ngam check serviceaccount\n"
break
;;
*)
echo_red "Please answer yes or no."
;;
esac
done
echo_green "Here's information about your new GAM installation:"
$target_dir/gam/gam version
rc=$?
if (( $rc != 0 )); then
echo_red "ERROR: Failed running GAM for the first time with $rc. Please report this error to GAM mailing list. Exiting."
exit
fi
echo_green "GAM installation and setup complete!"
if [ "$update_profile" = true ]; then
echo_green "Please restart your terminal shell or to get started right away run:\n\n$alias_line"
fi
# Clean up after ourselves even if we are killed with CTRL-C
trap "rm -rf $temp_archive_dir" EXIT

75
src/gam-setup.bat Normal file
View File

@@ -0,0 +1,75 @@
@echo(
@set /p adminemail= "Please enter your G Suite admin email address: "
:createproject
@echo(
@set /p yn= "Are you ready to set up a Google API project for GAM? [y or n] "
@if /I "%yn%"=="n" (
@ echo(
@ echo You can create an API project later by running:
@ echo(
@ echo gam create project
@ goto alldone
)
@if /I not "%yn%"=="y" (
@ echo(
@ echo Please answer y or n.
@ goto createproject
)
@gam create project %adminemail%
@if not ERRORLEVEL 1 goto projectdone
@echo(
@echo Projection creation failed. Trying again. Say n to skip projection creation.
@goto createproject
:projectdone
:adminauth
@echo(
@set /p yn= "Are you ready to authorize GAM to perform G Suite management operations as your admin account? [y or n] "
@if /I "%yn%"=="n" (
@ echo(
@ echo You can authorize an admin later by running:
@ echo(
@ echo gam oauth create %adminemail%
@ goto admindone
)
@if /I not "%yn%"=="y" (
@ echo(
@ echo Please answer y or n.
@ goto adminauth
)
@gam oauth create %adminemail%
@if not ERRORLEVEL 1 goto admindone
@echo(
@echo Admin authorization failed. Trying again. Say n to skip admin authorization.
@goto adminauth
:admindone
:saauth
@echo(
@set /p yn= "Are you ready to authorize GAM to manage G Suite user data and settings? [y or n] "
@if /I "%yn%"=="n" (
@ echo(
@ echo You can authorize a service account later by running:
@ echo(
@ echo gam user %adminemail% check serviceaccount
@ goto sadone
)
@if /I not "%yn%"=="y" (
@ echo(
@ echo Please answer y or n.
@ goto saauth
)
@echo(
@set /p regularuser= "Please enter the email address of a regular G Suite user: "
@echo Great! Checking service account scopes. This will fail the first time. Follow the steps to authorize and retry. It can take a few minutes for scopes to PASS after they've been authorized in the admin console.
@gam user %regularuser% check serviceaccount
@if not ERRORLEVEL 1 goto sadone
@echo(
@echo Service account authorization failed. Confirm you entered the scopes correctly in the admin console. It can take a few minutes for scopes to PASS after they are entered in the admin console so if you're sure you entered them correctly, go grab a coffee and then hit Y to try again. Say N to skip admin authorization.
@goto saauth
:sadone
@echo GAM installation and setup complete!
:alldone
@pause

View File

@@ -23,7 +23,7 @@ For more information, see http://git.io/gam
"""
__author__ = u'Jay Lee <jay0lee@gmail.com>'
__version__ = u'3.8'
__version__ = u'4.02'
__license__ = u'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
import sys
@@ -428,7 +428,7 @@ def indentMultiLineText(message, n=0):
return message.replace(u'\n', u'\n{0}'.format(u' '*n)).rstrip()
def showUsage():
doGAMVersion(checkForCheck=False)
doGAMVersion(checkForArgs=False)
print u'''
Usage: gam [OPTIONS]...
@@ -818,25 +818,33 @@ def doGAMCheckForUpdates(forceCheck=False):
except (urllib2.HTTPError, urllib2.URLError):
return
def doGAMVersion(checkForCheck=True):
import struct
print u'GAM {0} - {1}\n{2}\nPython {3}.{4}.{5} {6}-bit {7}\ngoogle-api-python-client {8}\n{9} {10}\nPath: {11}'.format(__version__, GAM_URL,
__author__,
sys.version_info[0], sys.version_info[1], sys.version_info[2],
struct.calcsize(u'P')*8, sys.version_info[3],
googleapiclient.__version__,
platform.platform(), platform.machine(),
GM_Globals[GM_GAM_PATH])
if checkForCheck:
def doGAMVersion(checkForArgs=True):
force_check = False
simple = False
if checkForArgs:
i = 2
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace(u'_', u'')
if myarg == u'check':
doGAMCheckForUpdates(forceCheck=True)
force_check = True
i += 1
elif myarg == u'simple':
simple = True
i += 1
else:
print u'ERROR: %s is not a valid argument for "gam version"' % sys.argv[i]
sys.exit(2)
if simple:
sys.stdout.write(__version__)
return
import struct
version_data = u'GAM {0} - {1}\n{2}\nPython {3}.{4}.{5} {6}-bit {7}\ngoogle-api-python-client {8}\n{9} {10}\nPath: {11}'
print version_data.format(__version__, GAM_URL, __author__, sys.version_info[0],
sys.version_info[1], sys.version_info[2], struct.calcsize(u'P')*8,
sys.version_info[3], googleapiclient.__version__, platform.platform(),
platform.machine(), GM_Globals[GM_GAM_PATH])
if force_check:
doGAMCheckForUpdates(forceCheck=True)
def handleOAuthTokenError(e, soft_errors):
if e.message in OAUTH2_TOKEN_ERRORS:
@@ -960,7 +968,8 @@ def callGAPI(service, function,
handleOAuthTokenError(e, soft_errors or GAPI_SERVICE_NOT_AVAILABLE in throw_reasons)
if GAPI_SERVICE_NOT_AVAILABLE in throw_reasons:
raise GAPI_serviceNotAvailable(e.message)
entityUnknownWarning(u'User', GM_Globals[GM_CURRENT_API_USER], 0, 0)
print u'ERROR: user %s: %s' % (GM_Globals[GM_CURRENT_API_USER], e)
#entityUnknownWarning(u'User', GM_Globals[GM_CURRENT_API_USER], 0, 0)
return None
except httplib2.CertificateValidationUnsupported:
noPythonSSLExit()
@@ -1032,7 +1041,6 @@ API_VER_MAPPING = {
u'datatransfer': u'datatransfer_v1',
u'directory': u'directory_v1',
u'drive': u'v2',
u'email-audit': u'v1',
u'email-settings': u'v2',
u'gmail': u'v1',
u'groupssettings': u'v1',
@@ -1161,17 +1169,19 @@ def getSvcAcctAPIversionHttpService(api):
except (ValueError, KeyError):
invalidJSONExit(disc_file)
def buildGAPIServiceObject(api, act_as):
def buildGAPIServiceObject(api, act_as, use_scopes=None):
_, http, service = getSvcAcctAPIversionHttpService(api)
GM_Globals[GM_CURRENT_API_USER] = act_as
GM_Globals[GM_CURRENT_API_SCOPES] = API_SCOPE_MAPPING[api]
credentials = getSvcAcctCredentials(GM_Globals[GM_CURRENT_API_SCOPES], act_as)
if not use_scopes:
use_scopes = GM_Globals[GM_CURRENT_API_SCOPES]
credentials = getSvcAcctCredentials(use_scopes, act_as)
try:
service._http = credentials.authorize(http)
except httplib2.ServerNotFoundError as e:
systemErrorExit(4, e)
except oauth2client.client.AccessTokenRefreshError as e:
entityServiceNotApplicableWarning([u'Calendar', u'User'][api != u'calendar'], act_as, 0, 0)
print u'ERROR user %s: %s' % (act_as, e)
return handleOAuthTokenError(e, True)
return service
@@ -1195,6 +1205,45 @@ def buildGplusGAPIObject(user):
userEmail = convertUserUIDtoEmailAddress(user)
return (userEmail, buildGAPIServiceObject(u'plus', userEmail))
def doCheckServiceAccount(users):
for user in users:
all_scopes_pass = True
all_scopes = []
print u'User: %s' % (user)
for api, scopes in API_SCOPE_MAPPING.items():
for scope in scopes:
if scope in all_scopes:
continue # don't check same scope twice
all_scopes.append((api, scope))
all_scopes = sorted(all_scopes)
for scope in all_scopes:
try:
service = buildGAPIServiceObject(scope[0], act_as=user, use_scopes=scope[1])
service._http.request.credentials.refresh(httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL]))
result = u'PASS'
except oauth2client.client.HttpAccessTokenRefreshError:
result = u'FAIL'
all_scopes_pass = False
print u' Scope: {0:60} {1}'.format(scope[1], result)
service_account = service._http.request.credentials.serialization_data[u'client_id']
if all_scopes_pass:
print u'\nAll scopes passed!\nService account %s is fully authorized.' % service_account
else:
user_domain = user[user.find(u'@')+1:]
print u'''
ERROR: Some scopes failed! Please go to:
https://admin.google.com/%s/AdminHome?#OGX:ManageOauthClients
and grant Client name:
%s
Access to scopes:
%s\n''' % (user_domain, service_account, ',\n'.join([scope[1] for scope in all_scopes]))
sys.exit(int(not all_scopes_pass))
def showReport():
def _adjustDate(errMsg):
@@ -3269,6 +3318,42 @@ def doCalendarWipeData():
return
callGAPI(cal.calendars(), u'clear', calendarId=calendarId)
def doCalendarDeleteEvent():
calendarId, cal = buildCalendarGAPIObject(sys.argv[2])
if not cal:
return
events = []
sendNotifications = None
doit = False
i = 4
while (i < len(sys.argv)):
if sys.argv[i].lower() == u'notifyattendees':
sendNotifications = True
i += 1
elif sys.argv[i].lower() in [u'id', u'eventid']:
events.append(sys.argv[i+1])
i += 2
elif sys.argv[i].lower() in [u'query', u'eventquery']:
query = sys.argv[i+1]
result = callGAPIpages(cal.events(), u'list', items=u'items', calendarId=calendarId, q=query)
for event in result:
if u'id' in event:
events.append(event[u'id'])
i += 2
elif sys.argv[i].lower() == u'doit':
doit = True
i += 1
else:
print u'ERROR: %s is not a valid argument for gam calendar <email> delete event'
sys.exit(3)
if doit:
for eventId in events:
print u' deleting eventId %s' % eventId
callGAPI(cal.events(), u'delete', calendarId=calendarId, eventId=eventId)
else:
for eventId in events:
print u' would delete eventId %s. Add doit to command to actually delete event' % eventId
def doCalendarAddEvent():
calendarId, cal = buildCalendarGAPIObject(sys.argv[2])
if not cal:
@@ -6753,6 +6838,139 @@ def getUserAttributes(i, cd, updateCmd=False):
body[u'hashFunction'] = u'crypt'
return (body, admin_body)
def doCreateProject():
try:
login_hint = sys.argv[3]
except IndexError:
while True:
login_hint = raw_input(u'\nWhat is your G Suite admin email address? ')
if login_hint.find(u'@') == -1:
print u'Error: that is not a valid email address'
else:
break
from oauth2client.contrib.dictionary_storage import DictionaryStorage
project_id = u'gam-project'
for i in range(3):
project_id += u'-%s' % ''.join(random.choice(string.digits + string.ascii_lowercase) for i in range(3))
project_name = u'project:%s' % project_id
scope = u'https://www.googleapis.com/auth/cloud-platform'
client_id = u'297408095146-fug707qsjv4ikron0hugpevbrjhkmsk7.apps.googleusercontent.com'
client_secret = u'qM3dP8f_4qedwzWQE1VR4zzU'
flow = oauth2client.client.OAuth2WebServerFlow(client_id=client_id,
client_secret=client_secret, scope=scope, redirect_uri=oauth2client.client.OOB_CALLBACK_URN,
user_agent=GAM_INFO, access_type=u'online', response_type=u'code', login_hint=login_hint)
flags = cmd_flags(noLocalWebserver=GC_Values[GC_NO_BROWSER])
storage_dict = {}
storage = DictionaryStorage(storage_dict, u'credentials')
flags = cmd_flags(noLocalWebserver=GC_Values[GC_NO_BROWSER])
http = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL])
try:
credentials = oauth2client.tools.run_flow(flow=flow, storage=storage, flags=flags, http=http)
except httplib2.CertificateValidationUnsupported:
noPythonSSLExit()
credentials.user_agent = GAM_INFO
http = credentials.authorize(httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL],
cache=GC_Values[GC_CACHE_DIR]))
crm = googleapiclient.discovery.build(u'cloudresourcemanager', u'v1', http=http, cache_discovery=False)
body = {u'projectId': project_id, u'name': u'GAM Project'}
while True:
create_again = False
print u'Creating project "%s"...' % body[u'name']
create_operation = callGAPI(crm.projects(), u'create', body=body)
operation_name = create_operation[u'name']
time.sleep(5) # Google recommends always waiting at least 5 seconds
for i in range(1, 5):
print u'Checking project status...'
status = callGAPI(crm.operations(), u'get', name=operation_name)
if u'error' in status:
if u'message' in status[u'error'] and status[u'error'][u'message'].find(u'Callers must accept ToS') != -1:
print u'''Please go to:
https://console.developers.google.com
and accept the Terms of Service (ToS). As soon as you've accepted the ToS popup, you can return here and press enter.'''
raw_input()
create_again = True
break
else:
sys.exit(1)
if u'done' in status and status[u'done']:
break
sleep_time = i ** 2
print u'Project still being created. Sleeping %s seconds' % sleep_time
time.sleep(sleep_time)
if create_again:
continue
if not u'done' in status or not status[u'done']:
print u'Failed to create project: %s' % status
sys.exit(1)
elif u'error' in status:
print status[u'error']
sys.exit(2)
break
serveman = googleapiclient.discovery.build(u'servicemanagement', u'v1', http=http, cache_discovery=False)
apis = [u'admin-json.googleapis.com', u'appsactivity-json.googleapis.com', u'calendar-json.googleapis.com',
u'classroom.googleapis.com', u'drive', u'gmail-json.googleapis.com', u'groupssettings-json.googleapis.com',
u'licensing-json.googleapis.com', u'plus-json.googleapis.com', u'contacts-json.googleapis.com']
for api in apis:
print u' enabling API %s...' % api
enable_operation = callGAPI(serveman.services(), u'enable', serviceName=api, body={u'consumerId': project_name})
iam = googleapiclient.discovery.build(u'iam', u'v1', http=http, cache_discovery=False)
print u'Creating Service Account'
service_account = callGAPI(iam.projects().serviceAccounts(), u'create', name=u'projects/%s' % project_id,
body={u'accountId': project_id, u'serviceAccount': {u'displayName': u'GAM Project'}})
body = {u'privateKeyType': u'TYPE_GOOGLE_CREDENTIALS_FILE', u'keyAlgorithm': u'KEY_ALG_RSA_4096'}
key = callGAPI(iam.projects().serviceAccounts().keys(), u'create', name=service_account[u'name'], body=body)
oauth2service_data = base64.b64decode(key[u'privateKeyData'])
service_account_file = os.path.join(GM_Globals[GM_GAM_PATH], FN_OAUTH2SERVICE_JSON)
if os.path.isfile(service_account_file):
service_account_file = u'%s-%s' % (service_account_file, project_id)
writeFile(service_account_file, oauth2service_data, continueOnError=False)
console_credentials_url = u'https://console.developers.google.com/apis/credentials?project=%s' % project_id
print u'''Please go to:
%s
1. Click the blue "Create credentials" button. Choose "OAuth client ID".
2. Click the blue "Configure consent screen" button. Enter "GAM" for "Product name to show to users".
3. Leave other fields blank. Click "Save" button.
3. Choose "Other" and click the blue "Create" button.
4. Copy your "client ID" value.
''' % console_credentials_url
client_id = raw_input(u'Enter your Client ID: ')
print u'\nNow go back to your browser and copy your client secret.'
client_secret = raw_input(u'Enter your Client Secret: ')
cs_data = u'''{
"installed": {
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"client_id": "%s",
"client_secret": "%s",
"project_id": "%s",
"redirect_uris": [
"urn:ietf:wg:oauth:2.0:oob",
"http://localhost"
],
"token_uri": "https://accounts.google.com/o/oauth2/token"
}
}''' % (client_id, client_secret, project_id)
client_secrets_file = os.path.join(GM_Globals[GM_GAM_PATH], FN_CLIENT_SECRETS_JSON)
if os.path.isfile(client_secrets_file):
client_secrets_file = u'%s-%s' % (client_secrets_file, project_id)
writeFile(client_secrets_file, cs_data, continueOnError=False)
print u'''Almost there! Now please switch back to your browser and:
1. Click OK to close "OAuth client" popup if it's still open.
2. Click "Manage service accounts" on the right of the screen.
3. Click the 3 dots to the right of your service account.
4. Choose Edit.
5. Check the "Enable G Suite Domain-wide Delegation" box and click Save.
'''
raw_input(u'Press Enter when done...')
print u'That\'s it! Your GAM Project is created and ready to use.'
def doCreateUser():
cd = buildGAPIObject(u'directory')
body, admin_body = getUserAttributes(3, cd, updateCmd=False)
@@ -9760,7 +9978,7 @@ OAUTH2_MENU += '''
OAUTH2_CMDS = [u's', u'u', u'e', u'c']
MAXIMUM_SCOPES = 28
def doRequestOAuth():
def doRequestOAuth(login_hint=None):
def _checkMakeScopesList(scopes):
del scopes[:]
for i in range(num_scopes):
@@ -9783,7 +10001,9 @@ def doRequestOAuth():
MISSING_CLIENT_SECRETS_MESSAGE = u"""Please configure OAuth 2.0
To make GAM run you will need to populate the {0} file found at:
{1}
with information from the APIs Console <https://console.developers.google.com>.
See this site for instructions:
@@ -9791,6 +10011,24 @@ See this site for instructions:
""".format(FN_CLIENT_SECRETS_JSON, GC_Values[GC_CLIENT_SECRETS_JSON], GAM_WIKI_CREATE_CLIENT_SECRETS)
cs_data = readFile(GC_Values[GC_CLIENT_SECRETS_JSON], mode=u'rb', continueOnError=True, displayError=True, encoding=None)
if not cs_data:
systemErrorExit(14, MISSING_CLIENT_SECRETS_MESSAGE)
try:
cs_json = json.loads(cs_data)
client_id = cs_json[u'installed'][u'client_id']
client_secret = cs_json[u'installed'][u'client_secret']
except (ValueError, IndexError, KeyError):
print u'ERROR: the format of your client secrets file:\n\n%s\n\n is incorrect. Please recreate the file.'
sys.exit(3)
if not login_hint:
while True:
login_hint = raw_input(u'\nWhat is your G Suite admin email address? ')
if login_hint.find(u'@') == -1:
print u'Error: that is not a valid email address'
else:
break
num_scopes = len(OAUTH2_SCOPES)
menu = OAUTH2_MENU % tuple(range(num_scopes))
selected_scopes = []
@@ -9852,19 +10090,20 @@ See this site for instructions:
status, message = _checkMakeScopesList(scopes)
if status:
break
try:
FLOW = oauth2client.client.flow_from_clientsecrets(GC_Values[GC_CLIENT_SECRETS_JSON], scope=scopes)
except oauth2client.client.clientsecrets.InvalidClientSecretsError:
systemErrorExit(14, MISSING_CLIENT_SECRETS_MESSAGE)
flow = oauth2client.client.OAuth2WebServerFlow(client_id=client_id,
client_secret=client_secret, scope=scopes, redirect_uri=oauth2client.client.OOB_CALLBACK_URN,
user_agent=GAM_INFO, access_type=u'offline', response_type=u'code', login_hint=login_hint)
storage = oauth2client.file.Storage(GC_Values[GC_OAUTH2_TXT])
credentials = storage.get()
flags = cmd_flags(noLocalWebserver=GC_Values[GC_NO_BROWSER])
if credentials is None or credentials.invalid:
http = httplib2.Http(disable_ssl_certificate_validation=GC_Values[GC_NO_VERIFY_SSL])
try:
credentials = oauth2client.tools.run_flow(flow=FLOW, storage=storage, flags=flags, http=http)
credentials = oauth2client.tools.run_flow(flow=flow, storage=storage, flags=flags, http=http)
except httplib2.CertificateValidationUnsupported:
noPythonSSLExit()
else:
print u'It looks like you\'ve already authorized GAM. Refusing to overwrite existing file:\n\n%s' % GC_Values[GC_OAUTH2_TXT]
def batch_worker():
while True:
@@ -10043,6 +10282,8 @@ def ProcessGAMCommand(args):
doCreateAdmin()
elif argument in [u'guardianinvite', u'inviteguardian', u'guardian']:
doInviteGuardian()
elif argument in [u'project', u'apiproject']:
doCreateProject()
else:
print u'ERROR: %s is not a valid argument for "gam create"' % argument
sys.exit(2)
@@ -10234,7 +10475,11 @@ def ProcessGAMCommand(args):
elif command in [u'oauth', u'oauth2']:
argument = sys.argv[2].lower()
if argument in [u'request', u'create']:
doRequestOAuth()
try:
login_hint = sys.argv[3]
except IndexError:
login_hint = None
doRequestOAuth(login_hint)
elif argument in [u'info', u'verify']:
OAuthInfo()
elif argument in [u'delete', u'revoke']:
@@ -10257,6 +10502,8 @@ def ProcessGAMCommand(args):
doCalendarWipeData()
elif argument == u'addevent':
doCalendarAddEvent()
elif argument == u'deleteevent':
doCalendarDeleteEvent()
else:
print u'ERROR: %s is not a valid argument for "gam calendar"' % argument
sys.exit(2)
@@ -10554,6 +10801,13 @@ def ProcessGAMCommand(args):
else:
print u'ERROR: %s is not a valid argument for "gam <users> info"' % infoWhat
sys.exit(2)
elif command == u'check':
checkWhat = sys.argv[4].replace(u'_', '').lower()
if checkWhat == u'serviceaccount':
doCheckServiceAccount(users)
else:
print u'ERROR: %s is not a valid argument for "gam <users> check"' % checkWhat
sys.exit(2)
elif command == u'profile':
doProfile(users)
elif command == u'imap':

View File

@@ -44,6 +44,7 @@
Source="gam-64">
<Component Id="gam_exe" Guid="886abc07-73c5-4acc-9f71-58daf62aabc1">
<File Name="gam.exe" KeyPath="yes" />
<Environment Id="PATH" Name="PATH" Value="[INSTALLFOLDER]" Permanent="yes" Part="last" Action="set" System="yes" />
</Component>
<Component Id="license" Guid="7a15de2e-fb91-4d0a-b8bf-c8b19c68f569">
<File Name="LICENSE" KeyPath="yes" />
@@ -51,6 +52,12 @@
<Component Id="whatsnew_txt" Guid="6aa9863c-90d9-412f-9b73-fda82549a950">
<File Name="whatsnew.txt" KeyPath="yes" />
</Component>
<Component Id="gam_setup_bat" Guid="ef01f93a-4b50-488a-9c04-ec5e13e66218">
<File Name="gam-setup.bat" KeyPath="yes" />
</Component>
<Component Id="gamcommands_txt" Guid="58ff9c45-a7c9-4e22-8845-a9a92610c1f3">
<File Name="gamcommands.txt" KeyPath="yes" />
</Component>
</ComponentGroup>
</Fragment>
@@ -58,7 +65,10 @@
<InstallUISequence>
<ExecuteAction />
<Show Dialog="WelcomeDlg" Before="ProgressDlg" />
<!-- <Show Dialog="ProgressDlg" After="" /> -->
</InstallUISequence>
<CustomAction Id="setup_gam" ExeCommand="[INSTALLFOLDER]gam-setup.bat" Directory="INSTALLFOLDER" Execute="commit" Impersonate="yes" Return="asyncWait"/>
<InstallExecuteSequence>
<Custom Action="setup_gam" After="InstallFiles" >NOT Installed AND NOT UPGRADINGPRODUCTCODE AND NOT WIX_UPGRADE_DETECTED</Custom>
</InstallExecuteSequence>
</Fragment>
</Wix>

View File

@@ -8,9 +8,8 @@ for d in a.datas:
if 'pyconfig' in d[0]:
a.datas.remove(d)
break
a.datas += [('httplib2/cacerts.txt', 'httplib2\cacerts.txt', 'DATA')]
a.datas += [('httplib2/cacerts.txt', 'httplib2/cacerts.txt', 'DATA')]
a.datas += [('cloudprint-v2.json', 'cloudprint-v2.json', 'DATA')]
a.datas += [('email-audit-v1.json', 'email-audit-v1.json', 'DATA')]
a.datas += [('email-settings-v2.json', 'email-settings-v2.json', 'DATA')]
pyz = PYZ(a.pure)
exe = EXE(pyz,
@@ -21,5 +20,5 @@ exe = EXE(pyz,
name='gam',
debug=False,
strip=None,
upx=True,
upx=False,
console=True )

View File

@@ -3,7 +3,7 @@ rmdir /q /s build
rmdir /q /s dist
rm -rf gam-$1-macos.tar.xz
pyinstaller --clean -F --distpath=gam macos-gam.spec
/Library/Frameworks/Python.framework/Versions/2.7/bin/pyinstaller --clean -F --distpath=gam macos-gam.spec
cp LICENSE gam
cp whatsnew.txt gam

View File

@@ -8,9 +8,8 @@ for d in a.datas:
if 'pyconfig' in d[0]:
a.datas.remove(d)
break
a.datas += [('httplib2/cacerts.txt', 'httplib2\cacerts.txt', 'DATA')]
a.datas += [('httplib2/cacerts.txt', 'httplib2/cacerts.txt', 'DATA')]
a.datas += [('cloudprint-v2.json', 'cloudprint-v2.json', 'DATA')]
a.datas += [('email-audit-v1.json', 'email-audit-v1.json', 'DATA')]
a.datas += [('email-settings-v2.json', 'email-settings-v2.json', 'DATA')]
pyz = PYZ(a.pure)
exe = EXE(pyz,
@@ -21,5 +20,5 @@ exe = EXE(pyz,
name='gam',
debug=False,
strip=None,
upx=True,
upx=False,
console=True )

View File

@@ -273,7 +273,7 @@ class Credentials(object):
to_serialize[key] = val.decode('utf-8')
if isinstance(val, set):
to_serialize[key] = list(val)
return json.dumps(to_serialize)
return json.dumps(to_serialize, indent=4, sort_keys=True)
def to_json(self):
"""Creating a JSON representation of an instance of Credentials.

View File

@@ -1,3 +1,8 @@
GAM 4.02
- "gam create project" command simplifies creation of client_secrets.json and oauth2service.json project files.
- Automated wizard simplifies GAM setup on Linux and MacOS (coming soon to Windows MSI).
- "gam calendar <email> deleteevent" deletes events by ID or query
GAM 3.8
- Old GData APIs removed from GAM. Admin Settings and Email Audit commands are no longer included, keep a copy of GAM 3.72 around if you use them. All Email Settings commands now use new Gmail API.
- Updated httplib2, google-api-client, uritemplate libraries

View File

@@ -11,12 +11,16 @@ del /q /f gam.wixpdb
c:\python27-32\scripts\pyinstaller --clean -F --distpath=gam windows-gam.spec
xcopy LICENSE gam\
xcopy whatsnew.txt gam\
xcopy gam-setup.bat gam\
xcopy gamcommands.txt gam\
del gam\w9xpopen.exe
"%ProgramFiles%\7-Zip\7z.exe" a -tzip gam-%1-windows.zip gam\ -xr!.svn
c:\python27-64\scripts\pyinstaller --clean -F --distpath=gam-64 windows-gam.spec
xcopy LICENSE gam-64\
xcopy whatsnew.txt gam-64\
xcopy gam-setup.bat gam-64\
xcopy gamcommands.txt gam-64\
"%ProgramFiles%\7-Zip\7z.exe" a -tzip gam-%1-windows-x64.zip gam-64\ -xr!.svn
set GAMVERSION=%1

View File

@@ -11,7 +11,6 @@ for d in a.datas:
break
a.datas += [('httplib2/cacerts.txt', 'httplib2\cacerts.txt', 'DATA')]
a.datas += [('cloudprint-v2.json', 'cloudprint-v2.json', 'DATA')]
a.datas += [('email-audit-v1.json', 'email-audit-v1.json', 'DATA')]
a.datas += [('email-settings-v2.json', 'email-settings-v2.json', 'DATA')]
pyz = PYZ(a.pure)
exe = EXE(pyz,