Compare commits

..

31 Commits
v3.31 ... v3.4

Author SHA1 Message Date
Jay Lee
f233f7505c update whatsnew.txt 2014-09-24 07:55:58 -04:00
Jay Lee
315367c80c fix cd.schemas() calls 2014-09-23 21:22:01 -04:00
Jay Lee
aba602ff2a GAM 3.4 "Oktoberfest" 2014-09-23 21:12:11 -04:00
Jay Lee
da8833bdc6 make "gam create ou" work 2014-09-23 21:10:45 -04:00
Jay Lee
ed932cce55 sort domain settings customerId results to make sure a real user is returned. 2014-09-23 21:09:55 -04:00
Jay Lee
37ade0059f handle missing dns.resolver library quietly 2014-09-23 21:08:58 -04:00
Jay Lee
7f6683e21d language shouldn't lowercase 2014-09-23 21:07:54 -04:00
Jay Lee
ca44a0dfb6 make sure downloaded drive file names are safe 2014-09-23 21:07:18 -04:00
Jay Lee
d9f85c600b handle IndexError on page messages 2014-09-23 21:06:30 -04:00
Jay Lee
c019a6d2e8 Merge branch 'master' of github.com:jay0lee/GAM
yes
2014-09-23 21:04:46 -04:00
Jay Lee
e8e25c352b add user schema api scopes 2014-09-23 20:09:05 -04:00
Jay Lee
1f75ac6112 Support for custom user schemas and non-admin user information 2014-09-23 19:53:08 -04:00
Jay Lee
0f111e6eaf update uritemplate 2014-09-12 07:20:11 -04:00
Jay Lee
8ee5b0ffdb Merge pull request #3 from daethnir/client_secrets_url
Adds link to docs for creating client secrets file.
2014-09-12 05:15:53 -04:00
Jay Lee
8813f6c046 Merge branch 'master' of github.com:jay0lee/GAM 2014-09-12 05:14:08 -04:00
Jay Lee
58fde587a8 git push origin masterMerge branch 'ksdtech-master' 2014-09-12 05:13:20 -04:00
Jay Lee
06573991f6 Merge branch 'master' of https://github.com/ksdtech/GAM into ksdtech-master 2014-09-12 05:09:30 -04:00
Jay Lee
597d3ac4b4 don't try to delete python source file directory in build.bat 2014-09-11 15:28:11 -04:00
Jay Lee
77a785049f 3.32 2014-09-11 15:07:19 -04:00
Jay Lee
44f62026a9 short license names for vfe, gau, gams 2014-09-11 15:01:12 -04:00
Jay Lee
6e4bb0abf9 Merge branch 'master' of github.com:jay0lee/GAM 2014-09-10 14:11:06 -04:00
Jay Lee
2b243edd3a various updates 2014-09-10 14:10:58 -04:00
Jay Lee
b56fe39bb2 Print only certain SKUs via "gam print licenses" 2014-09-08 10:17:23 -04:00
Jay Lee
7447e73a37 fix format 2014-08-29 13:04:22 -04:00
Peter Zingg
c62fa88d00 'members' items key for members().list() GAPI call
The default 'items' key is not found when calling the members().list() GAPI, so no existing group members are returned from callGAPIpages. The correct key is 'members'.
2014-07-28 09:50:43 -07:00
Peter Zingg
fffb847b5f allow name and description settings in doUpdateGroup
You can set 'name' and 'description' separately in doCreateGroup, but the corresponding code in doUpdateGroup is not available. Using the settings interface for these parameters seems to work.  Also, the prompt for illegal attributes was incorrect. Should say 'gam update group...', not 'gam create group...'
2014-07-27 14:42:00 -07:00
Bri Hatch
fe69068bba Adds link to docs for creating client secrets file. 2014-07-15 14:03:52 -07:00
Jay Lee
c7f9abde89 Merge branch 'master' of github.com:jay0lee/GAM 2014-07-13 15:05:52 -04:00
Jay Lee
3dc755eafa use base64.urlsafe_b64 commands 2014-07-13 15:04:54 -04:00
Jay Lee
3597594d3c Merge pull request #1 from daethnir/master
Fleshes out README.md.
2014-07-09 14:17:56 -04:00
Bri Hatch
f5a44c159b Fleshes out README.md. 2014-07-09 10:43:59 -07:00
7 changed files with 920 additions and 303 deletions

View File

@@ -1,4 +1,85 @@
gam
===
GAM, the Google Apps Manager
============================
gam
Dito GAM is a free, open source command line tool for
Google Apps Administrators to efficiently manage
domain and user settings quickly and easily. GAM has support
for many features, such as
* creating, deleting, and updating users, aliases, groups,
organizations, and resource calendars
* modifying user email settings such as IMAP, signatures,
vacation messages, profile sharing, email forwarding,
send as address, labels, and features.
* delegating mailboxes and calendars to other users
* modifying calendar access rights for users and resource calendars.
* auditing user accounts and mailboes
* monitoring incoming and outgoing email
* generating detailed reports for users, groups, resources,
account activity, email clients, and quotas.
Resources
========
There are a number of GAM resources available via several different
websites.
Source Repository
-----------------
The official GAM source repository is on [Github][github].
Downloads
---------
You can download current and pre-released versions of GAM from
the [Github Releases][github releases] page. Final releases
are also available on [Google Drive][google drive]
Wiki Documentation
----
The GAM documentation is currently hosted on Google Code
and can be found at the [Gam Getting Started Wiki][gam wiki]
Mailing List / Discussion group
-------------------------------
The GAM mailing list / discussion group is hosted
on [Google Groups]. You can join the list and interact
via email, or just post from the web itself.
Author
======
GAM is maintained by <a href="mailto:jay0lee@gmail.com">Jay Lee</a>.
THANKS TO
=========
GAM is made possible and maintained by the work of Dito.
Who is Dito?
Dito is solely focused on moving organizations to Google's
cloud. After hundreds of successful deployments over the
last 5 years, we have gained notoriety for our complete
understanding of the platform, our change management &
training ability, and our rock-star deployment engineers.
We are known worldwide as the Google Apps Experts.
Need a Google Apps Expert?
[Contact Dito](http://ditoweb.com/contact), which offers
[free premium GAM support](http://www.ditoweb.com/dito-gam)
for domains that sign up through Dito.
[github releases]: https://github.com/jay0lee/GAM/releases
[github]: https://github.com/jay0lee/GAM/
[google code downloads]: https://code.google.com/p/google-apps-manager/wiki/Downloads
[google drive]: https://googledrive.com/host/0B0YvUuHHn3MnbFl6N0k1UXcwdVk/
[gam wiki]: https://code.google.com/p/google-apps-manager/wiki/GAM3GettingStarted
[google groups]: http://groups.google.com/group/google-apps-manager

View File

@@ -1,9 +1,7 @@
rmdir /q /s gam
rmdir /q /s gam-64
rmdir /q /s python-src-%1
rmdir /q /s build
rmdir /q /s dist
del /q /f gam-%1-python-src.zip
del /q /f gam-%1-windows.zip
del /q /f gam-%1-windows-x64.zip

729
gam.py

File diff suppressed because it is too large Load Diff

View File

@@ -1269,7 +1269,7 @@ class OAuth2WebServerFlow(Flow):
'client_secret': self.client_secret,
'code': code,
'redirect_uri': self.redirect_uri,
'scope': self.scope,
# 'scope': self.scope,
})
headers = {
'content-type': 'application/x-www-form-urlencoded',

View File

@@ -10,7 +10,7 @@ setup(
options = {'py2exe':
{'optimize': 2,
'bundle_files': 1,
'includes': ['passlib.handlers.sha2_crypt']'
'includes': ['passlib.handlers.sha2_crypt'],
'dist_dir' : 'gam'}
}
)

View File

@@ -1,147 +1,265 @@
# Early, and incomplete implementation of -04.
#
#!/usr/bin/env python
"""
URI Template (RFC6570) Processor
"""
__copyright__ = """\
Copyright 2011-2013 Joe Gregorio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import re
import urllib
try:
from urllib.parse import quote
except ImportError:
from urllib import quote
__version__ = "0.6"
RESERVED = ":/?#[]@!$&'()*+,;="
OPERATOR = "+./;?|!@"
EXPLODE = "*+"
OPERATOR = "+#./;?&|!@"
MODIFIER = ":^"
TEMPLATE = re.compile(r"{(?P<operator>[\+\./;\?|!@])?(?P<varlist>[^}]+)}", re.UNICODE)
VAR = re.compile(r"^(?P<varname>[^=\+\*:\^]+)((?P<explode>[\+\*])|(?P<partial>[:\^]-?[0-9]+))?(=(?P<default>.*))?$", re.UNICODE)
def _tostring(varname, value, explode, operator, safe=""):
if type(value) == type([]):
if explode == "+":
return ",".join([varname + "." + urllib.quote(x, safe) for x in value])
else:
return ",".join([urllib.quote(x, safe) for x in value])
if type(value) == type({}):
keys = value.keys()
keys.sort()
if explode == "+":
return ",".join([varname + "." + urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
else:
return ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
else:
return urllib.quote(value, safe)
TEMPLATE = re.compile("{([^\}]+)}")
def _tostring_path(varname, value, explode, operator, safe=""):
joiner = operator
if type(value) == type([]):
if explode == "+":
return joiner.join([varname + "." + urllib.quote(x, safe) for x in value])
elif explode == "*":
return joiner.join([urllib.quote(x, safe) for x in value])
else:
return ",".join([urllib.quote(x, safe) for x in value])
elif type(value) == type({}):
keys = value.keys()
keys.sort()
if explode == "+":
return joiner.join([varname + "." + urllib.quote(key, safe) + joiner + urllib.quote(value[key], safe) for key in keys])
elif explode == "*":
return joiner.join([urllib.quote(key, safe) + joiner + urllib.quote(value[key], safe) for key in keys])
else:
return ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
else:
if value:
return urllib.quote(value, safe)
else:
return ""
def variables(template):
'''Returns the set of keywords in a uri template'''
vars = set()
for varlist in TEMPLATE.findall(template):
if varlist[0] in OPERATOR:
varlist = varlist[1:]
varspecs = varlist.split(',')
for var in varspecs:
# handle prefix values
var = var.split(':')[0]
# handle composite values
if var.endswith('*'):
var = var[:-1]
vars.add(var)
return vars
def _tostring_query(varname, value, explode, operator, safe=""):
joiner = operator
varprefix = ""
if operator == "?":
joiner = "&"
varprefix = varname + "="
if type(value) == type([]):
if 0 == len(value):
return ""
if explode == "+":
return joiner.join([varname + "=" + urllib.quote(x, safe) for x in value])
elif explode == "*":
return joiner.join([urllib.quote(x, safe) for x in value])
def _quote(value, safe, prefix=None):
if prefix is not None:
return quote(str(value)[:prefix], safe)
return quote(str(value), safe)
def _tostring(varname, value, explode, prefix, operator, safe=""):
if isinstance(value, list):
return ",".join([_quote(x, safe) for x in value])
if isinstance(value, dict):
keys = sorted(value.keys())
if explode:
return ",".join([_quote(key, safe) + "=" + \
_quote(value[key], safe) for key in keys])
else:
return ",".join([_quote(key, safe) + "," + \
_quote(value[key], safe) for key in keys])
elif value is None:
return
else:
return varprefix + ",".join([urllib.quote(x, safe) for x in value])
elif type(value) == type({}):
if 0 == len(value):
return ""
keys = value.keys()
keys.sort()
if explode == "+":
return joiner.join([varname + "." + urllib.quote(key, safe) + "=" + urllib.quote(value[key], safe) for key in keys])
elif explode == "*":
return joiner.join([urllib.quote(key, safe) + "=" + urllib.quote(value[key], safe) for key in keys])
return _quote(value, safe, prefix)
def _tostring_path(varname, value, explode, prefix, operator, safe=""):
joiner = operator
if isinstance(value, list):
if explode:
out = [_quote(x, safe) for x in value if value is not None]
else:
joiner = ","
out = [_quote(x, safe) for x in value if value is not None]
if out:
return joiner.join(out)
else:
return
elif isinstance(value, dict):
keys = sorted(value.keys())
if explode:
out = [_quote(key, safe) + "=" + \
_quote(value[key], safe) for key in keys \
if value[key] is not None]
else:
joiner = ","
out = [_quote(key, safe) + "," + \
_quote(value[key], safe) \
for key in keys if value[key] is not None]
if out:
return joiner.join(out)
else:
return
elif value is None:
return
else:
return varprefix + ",".join([urllib.quote(key, safe) + "," + urllib.quote(value[key], safe) for key in keys])
else:
if value:
return varname + "=" + urllib.quote(value, safe)
return _quote(value, safe, prefix)
def _tostring_semi(varname, value, explode, prefix, operator, safe=""):
joiner = operator
if operator == "?":
joiner = "&"
if isinstance(value, list):
if explode:
out = [varname + "=" + _quote(x, safe) \
for x in value if x is not None]
if out:
return joiner.join(out)
else:
return
else:
return varname + "=" + ",".join([_quote(x, safe) \
for x in value])
elif isinstance(value, dict):
keys = sorted(value.keys())
if explode:
return joiner.join([_quote(key, safe) + "=" + \
_quote(value[key], safe) \
for key in keys if key is not None])
else:
return varname + "=" + ",".join([_quote(key, safe) + "," + \
_quote(value[key], safe) for key in keys \
if key is not None])
else:
return varname
if value is None:
return
elif value:
return (varname + "=" + _quote(value, safe, prefix))
else:
return varname
def _tostring_query(varname, value, explode, prefix, operator, safe=""):
joiner = operator
if operator in ["?", "&"]:
joiner = "&"
if isinstance(value, list):
if 0 == len(value):
return None
if explode:
return joiner.join([varname + "=" + _quote(x, safe) \
for x in value])
else:
return (varname + "=" + ",".join([_quote(x, safe) \
for x in value]))
elif isinstance(value, dict):
if 0 == len(value):
return None
keys = sorted(value.keys())
if explode:
return joiner.join([_quote(key, safe) + "=" + \
_quote(value[key], safe) \
for key in keys])
else:
return varname + "=" + \
",".join([_quote(key, safe) + "," + \
_quote(value[key], safe) for key in keys])
else:
if value is None:
return
elif value:
return (varname + "=" + _quote(value, safe, prefix))
else:
return (varname + "=")
TOSTRING = {
"" : _tostring,
"+": _tostring,
";": _tostring_query,
"#": _tostring,
";": _tostring_semi,
"?": _tostring_query,
"&": _tostring_query,
"/": _tostring_path,
".": _tostring_path,
}
def expand(template, vars):
def _sub(match):
groupdict = match.groupdict()
operator = groupdict.get('operator')
if operator is None:
operator = ''
varlist = groupdict.get('varlist')
def expand(template, variables):
"""
Expand template as a URI Template using variables.
"""
def _sub(match):
expression = match.group(1)
operator = ""
if expression[0] in OPERATOR:
operator = expression[0]
varlist = expression[1:]
else:
varlist = expression
safe = "@"
if operator == '+':
safe = RESERVED
varspecs = varlist.split(",")
varnames = []
defaults = {}
for varspec in varspecs:
m = VAR.search(varspec)
groupdict = m.groupdict()
varname = groupdict.get('varname')
explode = groupdict.get('explode')
partial = groupdict.get('partial')
default = groupdict.get('default')
if default:
defaults[varname] = default
varnames.append((varname, explode, partial))
safe = ""
if operator in ["+", "#"]:
safe = RESERVED
varspecs = varlist.split(",")
varnames = []
defaults = {}
for varspec in varspecs:
default = None
explode = False
prefix = None
if "=" in varspec:
varname, default = tuple(varspec.split("=", 1))
else:
varname = varspec
if varname[-1] == "*":
explode = True
varname = varname[:-1]
elif ":" in varname:
try:
prefix = int(varname[varname.index(":")+1:])
except ValueError:
raise ValueError("non-integer prefix '{0}'".format(
varname[varname.index(":")+1:]))
varname = varname[:varname.index(":")]
if default:
defaults[varname] = default
varnames.append((varname, explode, prefix))
retval = []
joiner = operator
prefix = operator
if operator == "+":
prefix = ""
joiner = ","
if operator == "?":
joiner = "&"
if operator == "":
joiner = ","
for varname, explode, partial in varnames:
if varname in vars:
value = vars[varname]
#if not value and (type(value) == type({}) or type(value) == type([])) and varname in defaults:
if not value and value != "" and varname in defaults:
value = defaults[varname]
elif varname in defaults:
value = defaults[varname]
else:
continue
retval.append(TOSTRING[operator](varname, value, explode, operator, safe=safe))
if "".join(retval):
return prefix + joiner.join(retval)
else:
return ""
retval = []
joiner = operator
start = operator
if operator == "+":
start = ""
joiner = ","
if operator == "#":
joiner = ","
if operator == "?":
joiner = "&"
if operator == "&":
start = "&"
if operator == "":
joiner = ","
for varname, explode, prefix in varnames:
if varname in variables:
value = variables[varname]
if not value and value != "" and varname in defaults:
value = defaults[varname]
elif varname in defaults:
value = defaults[varname]
else:
continue
expanded = TOSTRING[operator](
varname, value, explode, prefix, operator, safe=safe)
if expanded is not None:
retval.append(expanded)
if len(retval) > 0:
return start + joiner.join(retval)
else:
return ""
return TEMPLATE.sub(_sub, template)
return TEMPLATE.sub(_sub, template)

View File

@@ -1,3 +1,36 @@
GAM 3.4 "Oktoberfest"
-Support for creating and setting custom user schemas http://goo.gl/M9rQrI
-End user view of print users and user info commands.
-fix updating name/description for groups
-fix groups sync commands
-gam print groups members no longer fails on zero member groups
-make sure downloaded Drive file names are safe for OS filenames.
-gam info domain should no longer crash on getting customer ID
-other minor bug fixes
GAM 3.32
-fix service account json files downloaded from new cloud console don't work with GAM
-use the new Gmail API for label command. Offers ability to rename labels and show/hide labels from the label and message list.
-copy Google drive files with commands like gam user <email> update drivefile id <fileid> copy
-print only one SKU for licenses with commands like gam print licenses sku vault
-short license names for Google Apps Message Security (gams), Google Apps Unlimtied (gau) and Google Vault Former Employee (vfe)
GAM 3.31
-New command "gam user delete aliases" clears all aliases for user.
-"gam update user email vfe" will rename user to vfe.oldname.XXXXX@olddomain.com
-fix delete photo command
-fix password update/create for Windows builds
GAM 3.3
-Major rewrites of "gam report" and "gam print users". Note that CSV headers have changed. Better performance and print users now supports "rich" fields like organization, phone, relations, etc.
-"gam report drive" now works for Google Apps Unlimited domains.
-"gam info user" now shows the user's licenses. Use "nolicenses" to prevent showing licenses.
-fix "gam print licenses" fails in large domains by reducing page size from 1000 to 100
-GAM now sends a sha-512 salted hashed password on user create and update for better security.
-cleanup unused features of old GData library.
-fix for Drive file downloading default format.
-upgrade to httplib v0.9 which may help with httplib.BadStatus errors.
GAM 3.21
-Fix crash when attempting to perform Drive operations for users with Drive service disabled.
-Fix "gam info org" only prints first 100 users in org.
@@ -222,4 +255,4 @@ GAM 1.8
-The settings filter command http://code.google.com/p/google-apps-manager/wiki/ExamplesEmailSettings#Create_a_Filter now has more actions including forward, star, trash and never send to spam.
-Downloading Audit Exports now has partial resume support. GAM will not re-download files that already exist on the local drive. If a large export download fails you should delete the last file GAM was working on since it's incomplete and then restart the process, GAM will pick up with the last file.
-Downloading Audit Exports now has partial resume support. GAM will not re-download files that already exist on the local drive. If a large export download fails you should delete the last file GAM was working on since it's incomplete and then restart the process, GAM will pick up with the last file.