Compare commits

...

11 Commits
v4.60 ... v4.61

Author SHA1 Message Date
Jay Lee
d8db37786c remove email settings from spec files 2018-08-27 18:59:10 -04:00
Jay Lee
79bc1065f3 GAM 4.61 2018-08-27 18:44:52 -04:00
Ross Scroggs
b107afc13c Add ability to specify suspended/not suspended users for groups and ous (#786)
Simplify specifying ChromeBooks by serial number
2018-08-27 18:26:06 -04:00
Jay Lee
fcfbf0a733 make create/delete delegate API calls soft error 2018-08-27 12:49:14 -04:00
Ross Scroggs
8460a7a87d Remove ability to promote/demote super admins; minor delegate cleanup (#785) 2018-08-27 12:47:58 -04:00
Ross Scroggs
2940dd71ab Fix query in update/rename labels, allow user to keep old label (#783)
* Fix query in update/rename labels, allow user to keep old label

* Drop keepoldlabel
2018-08-25 07:32:02 -04:00
Jay Lee
135ebaa251 handle non-Gmail users, counting 2018-08-24 08:37:30 -04:00
Jay Lee
f94b3eb383 remove email-settings JSON, fix e on scope selection 2018-08-24 08:28:23 -04:00
Jay Lee
a3db496f31 New Delegation API, yippee! 2018-08-24 08:21:27 -04:00
Ross Scroggs
dd78c05d59 Vault zip files were being extracted to cwd not toFolder (#781) 2018-08-20 21:01:09 -04:00
Ross Scroggs
d9c4326a6b Vault updates - Ignore until end of vacation (#777)
* Vault updates

Code cleanup
Add delete export/print export
Make start/startime end/endtime consistent
Add noverify noextract targetfolder to download export

* Avoid API error

* Fix error

* Fix timezone BNF
2018-08-13 17:38:32 -04:00
8 changed files with 328 additions and 406 deletions

View File

@@ -41,14 +41,14 @@ If an item contains spaces, it should be surrounded by ".
<FileFormat> ::=
csv|html|txt|tsv|jpeg|jpg|png|svg|pdf|rtf|pptx|xlsx|docx|odt|ods|openoffice|ms|microsoft|micro$oft
<LabelColorHex> ::=
#000000|#076239|#0b804b|#149e60|#16a766|#1a764d|#1c4587|#285bac|
#2a9c68|#3c78d8|#3dc789|#41236d|#434343|#43d692|#44b984|#4a86e8|
#653e9b|#666666|#68dfa9|#6d9eeb|#822111|#83334c|#89d3b2|#8e63ce|
#999999|#a0eac9|#a46a21|#a479e2|#a4c2f4|#aa8831|#ac2b16|#b65775|
#b694e8|#b9e4d0|#c6f3de|#c9daf8|#cc3a21|#cccccc|#cf8933|#d0bcf1|
#d5ae49|#e07798|#e4d7f5|#e66550|#eaa041|#efa093|#efefef|#f2c960|
#f3f3f3|#f691b3|#f6c5be|#f7a7c0|#fad165|#fb4c2f|#fbc8d9|#fcda83|
#fcdee8|#fce8b3|#fef1d1|#ffad47|#ffbc6b|#ffd6a2|#ffe6c7|#ffffff
#000000|#076239|#0b804b|#149e60|#16a766|#1a764d|#1c4587|#285bac|
#2a9c68|#3c78d8|#3dc789|#41236d|#434343|#43d692|#44b984|#4a86e8|
#653e9b|#666666|#68dfa9|#6d9eeb|#822111|#83334c|#89d3b2|#8e63ce|
#999999|#a0eac9|#a46a21|#a479e2|#a4c2f4|#aa8831|#ac2b16|#b65775|
#b694e8|#b9e4d0|#c6f3de|#c9daf8|#cc3a21|#cccccc|#cf8933|#d0bcf1|
#d5ae49|#e07798|#e4d7f5|#e66550|#eaa041|#efa093|#efefef|#f2c960|
#f3f3f3|#f691b3|#f6c5be|#f7a7c0|#fad165|#fb4c2f|#fbc8d9|#fcda83|
#fcdee8|#fce8b3|#fef1d1|#ffad47|#ffbc6b|#ffd6a2|#ffe6c7|#ffffff
<Language> ::=
ach|af|ag|ak|am|ar|az|be|bem|bg|bn|br|bs|ca|chr|ckb|co|crs|cs|cy|da|de|ee|el|en|en-gb|en-us|eo|es|es-419|et|eu|
fa|fi|fo|fr|fr-ca|fy|ga|gaa|gd|gl|gn|gu|ha|haw|he|hi|hr|ht|hu|hy|ia|id|ig|in|is|it|iw|ja|jw|
@@ -136,6 +136,7 @@ If an item contains spaces, it should be surrounded by ".
<CalendarACLRole> ::= editor|freebusy|freebusyreader|owner|reader|writer
<CalendarColorIndex> ::= <Number in range 1-24>
<CalendarItem> ::= <EmailAddress>|<String>
<ChatRoom> ::= <String>
<ClientID> ::= <String>
<ColorValue> ::= <ColorName>|<ColorHex>
<CollaboratorItem> ::= <EmailAddress>|<UniqueID>|<String>
@@ -144,7 +145,6 @@ If an item contains spaces, it should be surrounded by ".
<CourseParticipantType> ::= teacher|teachers|student|students
<CourseState> ::= active|archived|provisioned|declined
<CrOSID> ::= <String>
<CrOSItem> ::= <CrOSID>|(query:<QueryCrOS>)|(query:orgunitpath:<OrgUnitPath>)
<CustomerID> ::= <String>
<DomainAlias> ::= <String>
<DriveFileACLRole> ::= commenter|editor|organizer|owner|reader|writer
@@ -157,6 +157,7 @@ If an item contains spaces, it should be surrounded by ".
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
<EventColorIndex> ::= <Number in range 1-11>
<EventID> ::= <String>
<ExportItem> ::= <UniqueID>|<String>
<FeatureName> ::= <String>
<FieldName> ::= <String>
<FileName> ::= <String>
@@ -209,6 +210,7 @@ If an item contains spaces, it should be surrounded by ".
<RoleAssignmentID> ::= <String>
<SchemaName> ::= <String>
<Section> ::= <String>
<SerialNumber> ::= <String>
<S/MIMEID> ::= <String>
<StudentItem> ::= <EmailAddress>|<UniqueID>|<String>
<TeamDriveID> ::= <String>
@@ -512,12 +514,13 @@ Items, separated by spaces, with spaces, commas or single quotes in the items th
<ACLList> ::= "<ACLScope>(,<ACLScope>)*"
<ASPIDList> ::= "<ASPID>(,<ASPID>)*"
<CalendarList> ::= "<CalendarItem>(,<CalendarItem>)*"
<ChatRoomList> ::= "<ChatRoom>(,<ChatRoom>)*"
<CollaboratorItemList> ::= "<CollaboratorItem>(,<CollaboratorItem>)*"
<CourseAliasList> ::= "<CourseAlias>(,<CourseAlias>)*"
<CourseIDList> ::= "<CourseID>(,<CourseID>)*"
<CourseStateList> ::= "<CourseState>(,<CourseState>)*"
<CrOSFieldNameList> ::= "<CrOSFieldName>(,<CrOSFieldName>)*"
<CrOSList> ::= "<CrOSID>(,<CrOSID>)*"
<CrOSIDList> ::= "<CrOSID>(,<CrOSID>)*"
<DriveFileList> ::= "<DriveFileItem>(,<DriveFileItem>)*"
<EmailAddressList> ::= "<EmailAddress>(,<EmailAddress>)*"
<EmailItemList> ::= "<EmailItem>(,<EmailItem>)*"
@@ -546,6 +549,8 @@ Items, separated by spaces, with spaces, commas or single quotes in the items th
<ResourceIDList> ::= "<ResourceID>(,<ResourceID>)*"
<SKUIDList> ="<SKUID>(,<SKUID>)*"
<SchemaNameList> ::= "<SchemaName>(,<SchemaName>)*"
<SerialNumberList> ::= "<SerialNumber>(,<SerialNumber>)*"
<TeamDriveIDList> ::= "<TeamDriveID>(,<TeamDriveID>)*"
<UserFieldNameList> ::= "<UserFieldName>(,<UserFieldName>)*"
<UserList> ::= "<UserItem>(,<UserItem>)*"
@@ -553,9 +558,14 @@ Items, separated by spaces, with spaces, commas or single quotes in the items th
Specify a collection of ChromeOS devices by directly specifying them
<CrOSEntity> ::=
<CrOSIDList> | (cros_sn <SerialNumberList>) |
(query:<QueryCrOS>)|(query:orgunitpath:<OrgUnitPath>)|(query <QueryCrOS>)
<CrOSTypeEntity> ::=
(all cros)|
(cros <CrOSList>)|
(cros <CrOSIDList>)|
(cros_sn <SerialNumberList>)|
(crosfile <FileName>)|
(croscsvfile <FileName>:<FieldName>)|
(crosquery <QueryCrOS>)|
@@ -569,9 +579,13 @@ Specify a collection of Users by directly specifying them or by specifiying item
(all users)|
(user <UserItem>)|
(users <UserList>)|
(group <GroupItem)|
(group|group_ns|group_susp <GroupItem)|
(ou|org <OrgUnitPath)|
(ou_ns|org_ns <OrgUnitPath)|
(ou_susp|org_susp <OrgUnitPath)|
(ou_and_children|ou_and_child <OrgUnitPath>)|
(ou_and_children_ns|ou_and_child_ns <OrgUnitPath>)|
(ou_and_children_susp|ou_and_child_susp <OrgUnitPath>)|
(courseparticipants <CourseID>)|
(students <CourseID>)|
(teachers <CourseID>)|
@@ -597,7 +611,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
(notification clear|(email|sms eventcreation|eventchange|eventcancellation|eventresponse|agenda))
<CalendarSettings> ::=
(summary <String>)|(description <String>)|(location <String>)|(timezone <String>)
(summary <String>)|(description <String>)|(location <String>)|(timezone <TimeZone>)
<CourseAttributes> ::=
(description <String>)|
@@ -706,7 +720,6 @@ Specify a collection of Users by directly specifying them or by specifiying item
<UserAttributes> ::=
(address clear|(type work|home|other|(custom <String>) [unstructured|formatted <String>] [pobox <String>] [extendedaddress <String>] [streetaddress <String>]
[locality <String>] [region <String>] [postalcode <String>] [country <String>] [countrycode <String>] notprimary|primary))|
(admin <Boolean>)|
(agreed2terms|agreedtoterms <Boolean>)|
(changepassword|changepasswordatnextlogin <Boolean>)|
(crypt|sha|sha1|sha-1|md5|nohash)|
@@ -859,7 +872,7 @@ gam create org|ou <Name> [description <String>] [parent <OrgUnitPath>] [inherit|
gam update org|ou <OrgUnitPath> [name <Name>] [description <String>] [parent <OrgUnitPath>] [inherit|noinherit]
gam update org|ou <OrgUnitPath> add|move <CrOSTypeEntity>|<UserTypeEntity>
gam delete org|ou <OrgUnitPath>
gam info org|ou <OrgUnitPath> [nousers] [children|child]
gam info org|ou <OrgUnitPath> [nousers|notsuspended|suspended] [children|child]
gam print orgs|ous [todrive] [toplevelonly] [from_parent <OrgUnitPath>] [allfields|(fields <OrgUnitFieldNameList>)]
gam create alias|nickname <EmailAddress> user|group|target <UniqueID>|<EmailAddress>
@@ -881,12 +894,12 @@ gam calendar <CalendarItem> wipe
summary <String>|
description <String>|
location <String>|
timezone <String>
timezone <TimeZone>
gam calendar <CalendarItem> modify <CalendarSettings>+
gam update cros <CrOSItem> (<CrOSAttributes>+)|(action deprovision_same_model_replace|deprovision_different_model_replace|deprovision_retiring_device|disable|reenable [acknowledge_device_touch_requirement])
gam info cros <CrOSItem> [nolists] [listlimit <Number>] [start <Date>] [end <Date>]
gam update cros <CrOSEntity> (<CrOSAttributes>+)|(action deprovision_same_model_replace|deprovision_different_model_replace|deprovision_retiring_device|disable|reenable [acknowledge_device_touch_requirement])
gam info cros <CrOSEntity> [nolists] [listlimit <Number>] [start <Date>] [end <Date>]
[basic|full|allfields] <CrOSFieldName>* [fields <CrOSFieldNameList>] [downloadfile latest|<Time>] [targetfolder <FilePath>]
gam print cros [todrive] [(query <QueryCrOS>)|(queries <QueryCrOSList>)] [limittoou <OrgUnitItem>]
@@ -949,11 +962,11 @@ gam print mobile [todrive] [(query <QueryMobile>)|(queries <QueryMobileList>)] [
gam create group <EmailAddress> <GroupAttributes>*
gam update group <GroupItem> [admincreated <Boolean>] [email <EmailAddress>] <GroupAttributes>*
gam update group <GroupItem> add [owner|manager|member] [notsuspended] <UserTypeEntity>
gam update group <GroupItem> add [owner|manager|member] [notsuspended|suspended] <UserTypeEntity>
gam update group <GroupItem> delete|remove [owner|manager|member] <UserTypeEntity>
gam update group <GroupItem> sync [owner|manager|member] [notsuspended] <UserTypeEntity>
gam update group <GroupItem> sync [owner|manager|member] [notsuspended|suspended] <UserTypeEntity>
gam update group <GroupItem> update [owner|manager|member] <UserTypeEntity>
gam update group <GroupItem> clear [member] [manager] [owner] [suspended]
gam update group <GroupItem> clear [member] [manager] [owner] [notsuspended|suspended]
gam delete group <GroupItem>
gam info group <GroupItem> [nousers] [noaliases] [groups]
@@ -962,7 +975,8 @@ gam print groups [todrive] ([domain <DomainName>] ([member <UserItem>]|[query <Q
[members|memberscount] [managers|managerscount] [owners|ownerscount]
[delimiter <Character>] [sortheaders]
gam print group-members|groups-members [todrive] ([domain <DomainName>] ([member <UserItem>]|[query <QueryGroup>]))|[group <GroupItem>]
gam print group-members|groups-members [todrive]
([domain <DomainName>] ([member <UserItem>]|[query <QueryGroup>]))|[group|group_ns|group_susp <GroupItem>] [notsuspended|suspended]
[roles <GroupRoleList>] [membernames] [fields <MembersFieldNameList>]
gam print license|licenses|licence|licences [todrive] [(products|product <ProductIDList>)|(skus|sku <SKUIDList>)]
@@ -1065,12 +1079,23 @@ gam print printjobs [todrive] [printer|printerid <PrinterID>]
[owner|user <EmailAddress>]
[limit <Number>]
gam create vaultexport|export matter <MatterItem> [name <name>] corpus <drive|mail|groups|hangouts_chat>
(accounts <EmailAddressList>) | (orgunit|ou <OrgUnitPath>) | (teamdrives <TeamDriveList>) | (rooms <ChatRoomList>) | everyone
[scope <all_data|held_data|unprocessed_data>]
[terms <terms>] [start|starttime <Date>|<DateTime>] [end|endtime <Date>|<DateTime>] [timezone <TimeZone>] [format mbox|pst]
[excludedrafts <Boolean>] [driveversiondate <Date>|<DateTime>] [includeteamdrives] [includerooms]
[includeaccessinfo <Boolean>]
gam delete export <MatterItem> <ExportItem>
gam info export <MatterItem> <ExportItem>
gam print exports [todrive] [matters <MatterItemList>]
gam download export <MatterItem> <ExportItem> [noverify] [noextract] [targetfolder <FilePath>]
gam create vaulthold|hold corpus drive|groups|mail matter <MatterItem> [name <String>] [query <QueryVaultCorpus>]
[(accounts|groups|users <EmailItemList>) | (orgunit|ou <OrgUnit>)]
[starttime <Date>|<DateTime>] [endtime <Date>|<DateTime>]
[start|starttime <Date>|<DateTime>] [end|endtime <Date>|<DateTime>]
gam update vaulthold|hold <HoldItem> matter <MatterItem> [query <QueryVaultCorpus>]
[([addaccounts|addgroups|addusers <EmailItemList>] [removeaccounts|removegroups|removeusers <EmailItemList>]) | (orgunit|ou <OrgUnit>)]
[starttime <Date>|<DateTime>] [endtime <Date>|<DateTime>]
[start|starttime <Date>|<DateTime>] [end|endtime <Date>|<DateTime>]
gam delete vaulthold|hold <HoldItem> matter <MatterItem>
gam info vaulthold|hold <HoldItem> matter <MatterItem>
gam print vaultholds|holds [todrive] [matters <MatterItemList>]
@@ -1117,7 +1142,7 @@ gam <UserTypeEntity> show filetree [anyowner] (orderby <DriveOrderByFieldName> [
gam <UserTypeEntity> create|add drivefile [drivefilename <DriveFileName>] <DriveFileAddAttributes>* [csv] [todrive]
gam <UserTypeEntity> update drivefile (id <DriveFileID)|(drivefilename <DriveFileName>)|(query <QueryDriveFile) [copy] [newfilename <DriveFileName>] <DriveFileUpdateAttributes>*
gam <UserTypeEntity> get drivefile (id <DriveFileID>)|(drivefilename <DriveFileName>)|(query <QueryDriveFile>) [revision <Number>] [format <FileFormatList>]
targetfolder <FilePath>] [targetname <FileName>] [overwrite] [showprogress]
[targetfolder <FilePath>] [targetname <FileName>] [overwrite] [showprogress]
gam <UserTypeEntity> delete|del drivefile <DriveFileID>|<DriveFileURL>|(query:<QueryDriveFile>) [purge|untrash]
gam <UserTypeEntity> transfer drive <UserItem> [keepuser]
gam <UserTypeEntity> delete|del emptydrivefolders

View File

@@ -1,151 +0,0 @@
{
"kind": "discovery#restDescription",
"discoveryVersion": "v1",
"id": "email-settings:v2",
"name": "email-settings",
"version": "v2",
"revision": "20161013",
"title": "Email Settings API",
"description": "Lets you manage Google Apps Email Settings",
"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-settings",
"protocol": "rest",
"baseUrl": "https://apps-apis.google.com/",
"rootUrl": "https://apps-apis.google.com/",
"servicePath": "/a/feeds/emailsettings/2.0/",
"parameters": {
"v": {
"type": "string",
"description": "GData Version",
"default": "2.0",
"enum": [
"2.0"
],
"enumDescriptions": [
"GData 2.0"
],
"location": "query"
},
"alt": {
"type": "string",
"description": "Data format for the response.",
"default": "json",
"enum": [
"json"
],
"enumDescriptions": [
"Responses with Content-Type of application/json"
],
"location": "query"
},
"quotaUser": {
"type": "string",
"description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters. Overrides userIp if both are provided.",
"location": "query"
},
"prettyPrint": {
"type": "boolean",
"description": "Returns response with indentations and line breaks.",
"default": "true",
"location": "query"
}
},
"auth": {
"oauth2": {
"scopes": {
"https://apps-apis.google.com/a/feeds/emailsettings/2.0/": {
"description": "Manage email settings"
}
}
}
},
"schemas": {
"Delegate": {
"id": "Delegate",
"type": "object",
"description": "a delegate.",
"properties": {
"apps$property": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "property name"
},
"value": {
"type": "string",
"description": "organization name value"
}
}
}
}
},
"Delegates": {
"id": "feed",
"type": "object",
"description": "List of delegates.",
"properties": {
"entry": {
"type": "object",
"description": "list of delegates",
"items": {
"$ref": "Delegate"
}
}
}
}
},
"resources": {
"delegates": {
"methods": {
"get": {
"id": "email-settings.delegates.get",
"path": "{domainName}/{delegator}/delegation",
"httpMethod": "GET",
"parameters": {
"domainName": {
"type": "string",
"required": "true",
"location": "path"
},
"delegator": {
"type": "string",
"required": "true",
"location": "path"
}
},
"response": {
"$ref": "Delegates"
}
},
"delete": {
"id": "email-settings.delegates.delete",
"path": "{domainName}/{delegator}/delegation/{delegate}",
"httpMethod": "DELETE",
"parameters": {
"domainName": {
"type": "string",
"required": "true",
"location": "path"
},
"delegator": {
"type": "string",
"required": "true",
"location": "path"
},
"delegate": {
"type": "string",
"required": "true",
"location": "path"
}
}
}
}
}
}
}

View File

@@ -1502,56 +1502,26 @@ def addDelegates(users, i):
if sys.argv[i].lower() != u'to':
systemErrorExit(2, u'%s is not a valid argument for "gam <users> delegate", expected to' % sys.argv[i])
i += 1
delegate = sys.argv[i].lower()
atLoc = delegate.find(u'@')
if atLoc == -1:
delegate_domain = GC_Values[GC_DOMAIN].lower()
delegate_email = u'%s@%s' % (delegate, delegate_domain)
else:
delegate_domain = delegate[atLoc+1:].lower()
delegate_email = delegate
delegate = normalizeEmailAddressOrUID(sys.argv[i], noUid=True)
i = 0
count = len(users)
emailsettings = buildGAPIObject(u'email-settings')
for delegator in users:
i += 1
atLocd = delegator.find(u'@')
if atLocd == -1:
delegator_domain = GC_Values[GC_DOMAIN].lower()
delegator_email = u'%s@%s' % (delegator, delegator_domain)
else:
delegator_domain = delegator[atLocd+1:].lower()
delegator_email = delegator
delegator = delegator[:atLocd]
uri = u'https://apps-apis.google.com/a/feeds/emailsettings/2.0/%s/%s/delegation' % (delegator_domain, delegator)
body = u'''<?xml version="1.0" encoding="utf-8"?>
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" xmlns:apps="http://schemas.google.com/apps/2006">
<apps:property name="address" value="%s" />
</atom:entry>''' % delegate_email
headers = {u'GData-Version': u'2.0', u'Content-Type': u'application/atom+xml; charset=UTF-8'}
print u"Giving %s delegate access to %s (%s/%s)" % (delegate_email, delegator_email, i, count)
retries = 10
for n in range(1, retries+1):
status, result = emailsettings._http.request(uri=uri, method=u'POST', body=body, headers=headers)
httpStatus = int(status[u'status'])
if httpStatus == 201: # Success
time.sleep(10) # on success, sleep 10 seconds before exiting or moving on to next user to prevent ghost delegates
break
elif httpStatus > 499:
waitOnFailure(n, retries, str(httpStatus))
else:
systemErrorExit(3, u'Could not create delegation - %s - %s' % (httpStatus, result))
delegator, gmail = buildGmailGAPIObject(delegator)
if not gmail:
continue
print u"Giving %s delegate access to %s (%s/%s)" % (delegate, delegator, i, count)
callGAPI(gmail.users().settings().delegates(), u'create', soft_errors=True, userId=u'me', body={u'delegateEmail': delegate})
def gen_sha512_hash(password):
from passlib.handlers.sha2_crypt import sha512_crypt
return sha512_crypt.encrypt(password, rounds=5000)
def printShowDelegates(users, csvFormat):
emailsettings = buildGAPIObject(u'email-settings')
if csvFormat:
todrive = False
csvRows = []
titles = [u'User', u'delegateName', u'delegateAddress', u'delegationStatus']
titles = [u'User', u'delegateAddress', u'delegationStatus']
else:
csvStyle = False
i = 5
@@ -1565,64 +1535,41 @@ def printShowDelegates(users, csvFormat):
i += 1
else:
systemErrorExit(2, u'%s is not a valid argument for "gam <users> show delegates"' % sys.argv[i])
count = len(users)
i = 1
for user in users:
atLoc = user.find(u'@')
if atLoc == -1:
userName = user
domainName = GC_Values[GC_DOMAIN]
user = u'%s@%s' % (user, domainName)
else:
userName = user[:atLoc]
domainName = user[atLoc+1:]
sys.stderr.write(u"Getting delegates for %s...\n" % (user))
delegates = callGAPI(emailsettings.delegates(), u'get', soft_errors=True, v=u'2.0', domainName=domainName, delegator=userName)
if delegates and u'feed' in delegates and u'entry' in delegates[u'feed']:
for delegate in delegates[u'feed']['entry']:
status = u''
delegateAddress = u''
delegateName = u''
delegationId = u''
for item in delegate[u'apps$property']:
if item[u'name'] == u'status':
status = item[u'value']
elif item[u'name'] == u'address':
delegateAddress = item[u'value']
elif item[u'name'] == u'delegate':
delegateName = item[u'value']
elif item[u'name'] == u'delegationId':
delegationId = item[u'value']
user, gmail = buildGmailGAPIObject(user)
if not gmail:
continue
sys.stderr.write(u"Getting delegates for %s (%s/%s)...\n" % (user, i, count))
i += 1
delegates = callGAPI(gmail.users().settings().delegates(), u'list', soft_errors=True, userId=u'me')
if delegates and u'delegates' in delegates:
for delegate in delegates[u'delegates']:
delegateAddress = delegate[u'delegateEmail']
status = delegate[u'verificationStatus']
if csvFormat:
row = {u'User': user, u'delegateName': delegateName, u'delegateAddress': delegateAddress, u'delegationStatus': status}
row = {u'User': user, u'delegateAddress': delegateAddress, u'delegationStatus': status}
csvRows.append(row)
else:
if csvStyle:
print u'%s,%s,%s' % (user, delegateAddress, status)
else:
print utils.convertUTF8(u"Delegator: %s\n Delegate: %s\n Status: %s\n Delegate Email: %s\n Delegate ID: %s\n" % (user, delegateName, status, delegateAddress, delegationId))
print utils.convertUTF8(u"Delegator: %s\n Status: %s\n Delegate Email: %s\n" % (user, status, delegateAddress))
if csvFormat:
writeCSVfile(csvRows, titles, u'Delegates', todrive)
def deleteDelegate(users):
emailsettings = buildGAPIObject(u'email-settings')
delegate = sys.argv[5]
if not delegate.find(u'@') > 0:
if users[0].find(u'@') > 0:
delegatedomain = users[0][users[0].find(u'@')+1:]
else:
delegatedomain = GC_Values[GC_DOMAIN]
delegate = u'%s@%s' % (delegate, delegatedomain)
delegate = normalizeEmailAddressOrUID(sys.argv[5], noUid=True)
i = 0
count = len(users)
for user in users:
i += 1
atLoc = user.find(u'@')
if atLoc == -1:
domainName = GC_Values[GC_DOMAIN] #make sure it's back at default domain
else:
domainName = user[atLoc+1:]
user = user[:atLoc]
print u"Deleting %s delegate access to %s (%s/%s)" % (delegate, user+u'@'+domainName, i, count)
callGAPI(emailsettings.delegates(), u'delete', v=u'2.0', delegate=delegate, delegator=user, domainName=domainName)
user, gmail = buildGmailGAPIObject(user)
if not gmail:
continue
print u"Deleting %s delegate access to %s (%s/%s)" % (delegate, user, i, count)
callGAPI(gmail.users().settings().delegates(), u'delete', soft_errors=True, userId=u'me', delegateEmail=delegate)
def doAddCourseParticipant():
croom = buildGAPIObject(u'classroom')
@@ -1648,7 +1595,7 @@ def doAddCourseParticipant():
def doSyncCourseParticipants():
courseId = addCourseIdScope(sys.argv[2])
participant_type = sys.argv[4].lower()
diff_entity_type = sys.argv[5]
diff_entity_type = sys.argv[5].lower()
diff_entity = sys.argv[6]
current_course_users = getUsersToModify(entity_type=participant_type, entity=courseId)
print
@@ -5800,8 +5747,8 @@ def renameLabels(users):
except GAPI_aborted:
if merge:
print u' Merging %s label to existing %s label' % (label[u'name'], new_label_name)
q = u'label:"%s"' % label[u'name']
messages_to_relabel = callGAPIpages(gmail.users().messages(), u'list', u'messages', userId=user, q=q)
messages_to_relabel = callGAPIpages(gmail.users().messages(), u'list', u'messages',
userId=user, q=u'label:%s' % label[u'name'].lower().replace(u'/', u'-').replace(u' ', u'-'))
if len(messages_to_relabel) > 0:
for new_label in labels[u'labels']:
if new_label[u'name'].lower() == new_label_name.lower():
@@ -6545,7 +6492,7 @@ def doGetUserSchema():
schema = callGAPI(cd.schemas(), u'get', customerId=GC_Values[GC_CUSTOMER_ID], schemaKey=schemaKey)
_showSchema(schema)
def getUserAttributes(i, cd, updateCmd=False):
def getUserAttributes(i, cd, updateCmd):
def getEntryType(i, entry, entryTypes, setTypeCustom=True, customKeyword=u'custom', customTypeKeyword=u'customType'):
""" Get attribute entry type
entryTypes is list of pre-defined types, a|b|c
@@ -6614,7 +6561,6 @@ def getUserAttributes(i, cd, updateCmd=False):
i += 1
need_password = True
need_to_hash_password = True
admin_body = {}
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if myarg in [u'firstname', u'givenname']:
@@ -6638,7 +6584,9 @@ def getUserAttributes(i, cd, updateCmd=False):
need_password = True
i += 2
elif myarg == u'admin':
admin_body[u'status'] = getBoolean(sys.argv[i+1], myarg)
value = getBoolean(sys.argv[i+1], myarg)
if updateCmd or value:
systemErrorExit(2, '%s %s is not a valid argument for "gam %s user"' % (sys.argv[i], value, [u'create', u'update'][updateCmd]))
i += 2
elif myarg == u'suspended':
body[u'suspended'] = getBoolean(sys.argv[i+1], myarg)
@@ -7049,7 +6997,7 @@ def getUserAttributes(i, cd, updateCmd=False):
if u'password' in body and need_to_hash_password:
body[u'password'] = gen_sha512_hash(body[u'password'])
body[u'hashFunction'] = u'crypt'
return (body, admin_body)
return body
VALIDEMAIL_PATTERN = re.compile(r'^[^@]+@[^@]+\.[^@]+$')
@@ -7477,6 +7425,20 @@ def doCreateVaultMatter():
print u' adding collaborator %s' % collaborator[u'email']
callGAPI(v.matters(), u'addPermissions', matterId=matterId, body={u'matterPermission': {u'role': u'COLLABORATOR', u'accountId': collaborator[u'id']}})
VAULT_SEARCH_METHODS_MAP = {
u'account': u'ACCOUNT',
u'accounts': u'ACCOUNT',
u'entireorg': u'ENTIRE_ORG',
u'everyone': u'ENTIRE_ORG',
u'orgunit': u'ORG_UNIT',
u'ou': u'ORG_UNIT',
u'room': u'ROOM',
u'rooms': u'ROOM',
u'teamdrive': u'TEAM_DRIVE',
u'teamdrives': u'TEAM_DRIVE',
}
VAULT_SEARCH_METHODS_LIST = [u'accounts', u'orgunit', u'teamdrives', u'rooms', u'everyone']
def doCreateVaultExport():
v = buildGAPIObject(u'vault')
allowed_corpuses = v._rootDesc[u'schemas'][u'Query'][u'properties'][u'corpus'][u'enum']
@@ -7508,38 +7470,37 @@ def doCreateVaultExport():
if body[u'query'][u'corpus'] not in allowed_corpuses:
systemErrorExit(3, 'corpus must be one of %s. Got %s' % (u', '.join(allowed_corpuses), sys.argv[i+1]))
i += 2
elif myarg in VAULT_SEARCH_METHODS_MAP:
if body[u'query'].get(u'searchMethod'):
systemErrorExit(3, 'Multiple search methods ({0}) specified, only one is allowed'.format(u', '.join(VAULT_SEARCH_METHODS_LIST)))
searchMethod = VAULT_SEARCH_METHODS_MAP[myarg]
body[u'query'][u'searchMethod'] = searchMethod
if searchMethod == u'ACCOUNT':
body[u'query'][u'accountInfo'] = {u'emails': sys.argv[i+1].split(u',')}
i += 2
elif searchMethod == u'ORG_UNIT':
body[u'query'][u'orgUnitInfo'] = {u'orgUnitId': getOrgUnitId(sys.argv[i+1])[1]}
i += 2
elif searchMethod == u'TEAM_DRIVE':
body[u'query'][u'teamDriveInfo'] = {u'teamDriveIds': sys.argv[i+1].split(u',')}
i += 2
elif searchMethod == u'ROOM':
body[u'query'][u'hangoutsChatInfo'] = {u'roomId': sys.argv[i+1].split(u',')}
i += 2
else:
i += 1
elif myarg == u'scope':
body[u'query'][u'dataScope'] = sys.argv[i+1].upper()
if body[u'query']['dataScope'] not in allowed_scopes:
systemErrorExit(3, 'scope must be one of %s. Got %s' % (u', '.join(allowed_scopes), sys.argv[i+1]))
i += 2
elif myarg in [u'account', u'accounts']:
body[u'query'][u'searchMethod'] = u'ACCOUNT'
body[u'query'][u'accountInfo'] = {u'emails': sys.argv[i+1].split(u',')}
i += 2
elif myarg in [u'ou', u'orgunit']:
body[u'query'][u'searchMethod'] = u'ORG_UNIT'
orgUnitId = getOrgUnitId(sys.argv[i+1])[1]
body[u'query'][u'orgUnitInfo'] = {u'orgUnitId': orgUnitId}
i += 2
elif myarg in [u'teamdrive', u'teamdrives']:
body[u'query'][u'searchMethod'] = u'TEAM_DRIVE'
body[u'query'][u'teamDriveInfo'] = {u'teamDriveIds': sys.argv[i+1].split(u',')}
i += 2
elif myarg in [u'room', u'rooms']:
body[u'query'][u'searchMethod'] = u'ROOM'
body[u'query'][u'hangoutsChatInfo'] = {u'roomId': sys.argv[i+1].split(u',')}
i += 2
elif myarg in [u'everyone']:
body[u'query'][u'searchMethod'] = u'ENTIRE_ORG'
i += 2
elif myarg in [u'terms']:
body[u'query'][u'terms'] = sys.argv[i+1]
i += 2
elif myarg in [u'start']:
elif myarg in [u'start', u'starttime']:
body[u'query'][u'startTime'] = getDateZeroTimeOrFullTime(sys.argv[i+1])
i += 2
elif myarg in [u'end']:
elif myarg in [u'end', u'endtime']:
body[u'query'][u'endTime'] = getDateZeroTimeOrFullTime(sys.argv[i+1])
i += 2
elif myarg in [u'timezone']:
@@ -7567,14 +7528,13 @@ def doCreateVaultExport():
body[u'exportOptions'].setdefault(u'driveOptions', {})[u'includeAccessInfo'] = getBoolean(sys.argv[i+1], myarg)
i += 2
else:
print u'ERROR %s is not a valid argument for "gam create export".' % sys.argv[i]
sys.exit(3)
systemErrorExit(3, '%s is not a valid argument to "gam create export"' % sys.argv[i])
if not matterId:
print u'ERROR: you must specify a matterId.'
sys.exit(3)
systemErrorExit(3, 'you must specify a matter for the new export.')
if u'corpus' not in body[u'query']:
print u'ERROR: you must specify a corpus type. Choose one of %s' % u', '.join(allowed_corpuses)
sys.exit(3)
systemErrorExit(3, 'you must specify a corpus for the new export. Choose one of %s' % u', '.join(allowed_corpuses))
if u'searchMethod' not in body[u'query']:
systemErrorExit(3, 'you must specify a search method for the new export. Choose one of %s' % u', '.join(VAULT_SEARCH_METHODS_LIST))
if u'name' not in body:
body[u'name'] = u'GAM %s export - %s' % (body[u'query'][u'corpus'], datetime.datetime.now())
options_field = None
@@ -7585,11 +7545,19 @@ def doCreateVaultExport():
elif body[u'query'][u'corpus'] == u'HANGOUTS_CHAT':
options_field = u'hangoutsChatOptions'
if options_field:
body[u'exportOptions'].pop(u'driveOptions', None)
body[u'exportOptions'][options_field] = {u'exportFormat': export_format}
results = callGAPI(v.matters().exports(), u'create', matterId=matterId, body=body)
print_json(None, results)
def doGetVaultExport():
def doDeleteVaultExport():
v = buildGAPIObject(u'vault')
matterId = getMatterItem(v, sys.argv[3])
exportId = convertExportNameToID(v, sys.argv[4], matterId)
print u'Deleting export %s / %s' % (sys.argv[4], exportId)
callGAPI(v.matters().exports(), u'delete', matterId=matterId, exportId=exportId)
def doGetVaultExportInfo():
v = buildGAPIObject(u'vault')
matterId = getMatterItem(v, sys.argv[3])
exportId = convertExportNameToID(v, sys.argv[4], matterId)
@@ -7604,6 +7572,22 @@ def doDownloadVaultExport():
matterId = getMatterItem(v, sys.argv[3])
exportId = convertExportNameToID(v, sys.argv[4], matterId)
targetFolder = GC_Values[GC_DRIVE_DIR]
i = 5
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace(u'_', u'')
if myarg == u'targetfolder':
targetFolder = os.path.expanduser(sys.argv[i+1])
if not os.path.isdir(targetFolder):
os.makedirs(targetFolder)
i += 2
elif myarg == u'noverify':
verifyFiles = False
i += 1
elif myarg == u'noextract':
extractFiles = False
i += 1
else:
systemErrorExit(3, '%s is not a valid argument to "gam download export"' % sys.argv[i])
export = callGAPI(v.matters().exports(), u'get', matterId=matterId, exportId=exportId)
for s_file in export[u'cloudStorageSink']['files']:
bucket = s_file['bucketName']
@@ -7651,9 +7635,9 @@ def extract_nested_zip(zippedFile, toFolder, spacing=u' '):
inner_files = zfile.infolist()
for inner_file in inner_files:
print u'%s %s' % (spacing, inner_file.filename)
zfile.extract(inner_file)
inner_file_path = zfile.extract(inner_file, toFolder)
if re.search(r'\.zip$', inner_file.filename):
extract_nested_zip(inner_file.filename, toFolder, spacing=spacing+u' ')
extract_nested_zip(inner_file_path, toFolder, spacing=spacing+u' ')
os.remove(zippedFile)
def doCreateVaultHold():
@@ -7683,13 +7667,12 @@ def doCreateVaultHold():
accounts = sys.argv[i+1].split(u',')
i += 2
elif myarg in [u'orgunit', u'ou']:
_, orgUnitId = getOrgUnitId(sys.argv[i+1], None)
body[u'orgUnit'] = {u'orgUnitId': orgUnitId}
body[u'orgUnit'] = {u'orgUnitId': getOrgUnitId(sys.argv[i+1])[1]}
i += 2
elif myarg == u'starttime':
elif myarg in [u'start', u'starttime']:
start_time = getDateZeroTimeOrFullTime(sys.argv[i+1])
i += 2
elif myarg == u'endtime':
elif myarg in [u'end', u'endtime']:
end_time = getDateZeroTimeOrFullTime(sys.argv[i+1])
i += 2
elif myarg == u'matter':
@@ -7702,7 +7685,7 @@ def doCreateVaultHold():
if not body.get(u'name'):
systemErrorExit(3, 'you must specify a name for the new hold.')
if not body.get(u'corpus'):
systemErrorExit(3, 'you must specify corpus for the new hold. One of %s' % (u', '.join(allowed_corpuses)))
systemErrorExit(3, 'you must specify a corpus for the new hold. Choose one of %s' % (u', '.join(allowed_corpuses)))
query_type = u'%sQuery' % body[u'corpus'].lower()
body[u'query'][query_type] = {}
if body[u'corpus'] == u'DRIVE':
@@ -7828,13 +7811,12 @@ def doUpdateVaultHold():
query = sys.argv[i+1]
i += 2
elif myarg in [u'orgunit', u'ou']:
_, orgUnitId = getOrgUnitId(sys.argv[i+1], None)
body[u'orgUnit'] = {u'orgUnitId': orgUnitId}
body[u'orgUnit'] = {u'orgUnitId': getOrgUnitId(sys.argv[i+1])[1]}
i += 2
elif myarg == u'starttime':
elif myarg in [u'start', u'starttime']:
start_time = getDateZeroTimeOrFullTime(sys.argv[i+1])
i += 2
elif myarg == u'endtime':
elif myarg in [u'end', u'endtime']:
end_time = getDateZeroTimeOrFullTime(sys.argv[i+1])
i += 2
elif myarg in [u'addusers', u'addaccounts', u'addgroups']:
@@ -7950,12 +7932,9 @@ def doGetVaultMatterInfo():
def doCreateUser():
cd = buildGAPIObject(u'directory')
body, admin_body = getUserAttributes(3, cd, updateCmd=False)
body = getUserAttributes(3, cd, False)
print u"Creating account for %s" % body[u'primaryEmail']
callGAPI(cd.users(), u'insert', body=body, fields=u'primaryEmail')
if admin_body:
print u' Changing admin status for %s to %s' % (body[u'primaryEmail'], admin_body[u'status'])
callGAPI(cd.users(), u'makeAdmin', userKey=body[u'primaryEmail'], body=admin_body)
def GroupIsAbuseOrPostmaster(emailAddr):
return emailAddr.startswith(u'abuse@') or emailAddr.startswith(u'postmaster@')
@@ -8336,7 +8315,7 @@ def doUpdateUser(users, i):
cd = buildGAPIObject(u'directory')
if users is None:
users = [normalizeEmailAddressOrUID(sys.argv[3])]
body, admin_body = getUserAttributes(i, cd, updateCmd=True)
body = getUserAttributes(i, cd, True)
vfe = u'primaryEmail' in body and body[u'primaryEmail'][:4].lower() == u'vfe@'
for user in users:
userKey = user
@@ -8350,8 +8329,6 @@ def doUpdateUser(users, i):
sys.stdout.write(u'updating user %s...\n' % user)
if body:
callGAPI(cd.users(), u'update', userKey=userKey, body=body)
if admin_body:
callGAPI(cd.users(), u'makeAdmin', userKey=userKey, body=admin_body)
def doRemoveUsersAliases(users):
cd = buildGAPIObject(u'directory')
@@ -8411,17 +8388,17 @@ def doUpdateGroup():
return emailAddress
def _getRoleAndUsers():
checkNotSuspended = False
checkSuspended = None
role = ROLE_MEMBER
i = 5
if sys.argv[i].lower() in GROUP_ROLES_MAP:
role = GROUP_ROLES_MAP[sys.argv[i].lower()]
i += 1
if sys.argv[i].lower() == u'notsuspended':
checkNotSuspended = True
if sys.argv[i].lower() in [u'suspended', u'notsuspended']:
checkSuspended = sys.argv[i].lower() == u'suspended'
i += 1
if sys.argv[i].lower() in usergroup_types:
users_email = getUsersToModify(entity_type=sys.argv[i], entity=sys.argv[i+1], checkNotSuspended=checkNotSuspended, groupUserMembersOnly=False)
users_email = getUsersToModify(entity_type=sys.argv[i].lower(), entity=sys.argv[i+1], checkSuspended=checkSuspended, groupUserMembersOnly=False)
else:
users_email = [normalizeEmailAddressOrUID(sys.argv[i], checkForCustomerId=True)]
return (role, users_email)
@@ -8523,7 +8500,7 @@ def doUpdateGroup():
except (GAPI_memberNotFound, GAPI_invalidMember) as e:
print u' Group: {0}, {1} Update to {2} Failed: {3}'.format(group, users_email[0], role, str(e))
else: # clear
suspended = False
checkSuspended = None
fields = [u'email', u'id']
roles = []
i = 5
@@ -8532,8 +8509,8 @@ def doUpdateGroup():
if myarg.upper() in [ROLE_OWNER, ROLE_MANAGER, ROLE_MEMBER]:
roles.append(myarg.upper())
i += 1
elif myarg == u'suspended':
suspended = True
elif myarg in [u'suspended', u'notsuspended']:
checkSuspended = myarg == u'suspended'
fields.append(u'status')
i += 1
else:
@@ -8552,12 +8529,14 @@ def doUpdateGroup():
page_message=page_message,
throw_reasons=GAPI_MEMBERS_THROW_REASONS,
groupKey=group, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
if not suspended:
if checkSuspended is None:
users_email = [member.get(u'email', member[u'id']) for member in result if not validRoles or member.get(u'role', ROLE_MEMBER) in validRoles]
else:
elif checkSuspended:
users_email = [member.get(u'email', member[u'id']) for member in result if (not validRoles or member.get(u'role', ROLE_MEMBER) in validRoles) and member[u'status'] == u'SUSPENDED']
else: # elif not checkSuspended
users_email = [member.get(u'email', member[u'id']) for member in result if (not validRoles or member.get(u'role', ROLE_MEMBER) in validRoles) and member[u'status'] != u'SUSPENDED']
if len(users_email) > 1:
sys.stderr.write(u'Group: {0}, Will remove {1} {2}{3}s.\n'.format(group, len(users_email), [u'', u'suspended '][suspended], roles))
sys.stderr.write(u'Group: {0}, Will remove {1} {2}{3}s.\n'.format(group, len(users_email), u'' if checkSuspended is None else [u'Non-suspended ', u'Suspended '][checkSuspended], roles))
for user_email in users_email:
items.append(['gam', 'update', 'group', group, 'remove', user_email])
else:
@@ -8629,23 +8608,27 @@ def doUpdateAlias():
callGAPI(cd.groups().aliases(), u'insert', groupKey=target_email, body={u'alias': alias})
print u'updated alias %s' % alias
def doUpdateCros():
cd = buildGAPIObject(u'directory')
deviceId = sys.argv[3]
if deviceId[:6].lower() == u'query:':
query = deviceId[6:]
def getCrOSDeviceEntity(i, cd):
myarg = sys.argv[i].lower()
if myarg == u'cros_sn':
return i+2, getUsersToModify(u'cros_sn', sys.argv[i+1])
if myarg == u'query':
return i+2, getUsersToModify(u'crosquery', sys.argv[i+1])
if myarg[:6] == u'query:':
query = sys.argv[i][6:]
if query[:12].lower() == u'orgunitpath:':
kwargs = {u'orgUnitPath': query[12:]}
else:
kwargs = {u'query': query}
devices_result = callGAPIpages(cd.chromeosdevices(), u'list', u'chromeosdevices',
customerId=GC_Values[GC_CUSTOMER_ID], fields=u'chromeosdevices/deviceId,nextPageToken', **kwargs)
devices = list()
for a_device in devices_result:
devices.append(a_device[u'deviceId'])
else:
devices = [deviceId,]
i = 4
devices = callGAPIpages(cd.chromeosdevices(), u'list', u'chromeosdevices',
customerId=GC_Values[GC_CUSTOMER_ID],
fields=u'nextPageToken,chromeosdevices(deviceId)', **kwargs)
return i+1, [device[u'deviceId'] for device in devices]
return i+1, sys.argv[i].replace(u',', u' ').split()
def doUpdateCros():
cd = buildGAPIObject(u'directory')
i, devices = getCrOSDeviceEntity(3, cd)
update_body = {}
action_body = {}
orgUnitPath = None
@@ -8744,11 +8727,13 @@ def doUpdateOrg():
cd = buildGAPIObject(u'directory')
orgUnitPath = getOrgUnitItem(sys.argv[3])
if sys.argv[4].lower() in [u'move', u'add']:
if sys.argv[5].lower() in usergroup_types:
users = getUsersToModify(entity_type=sys.argv[5].lower(), entity=sys.argv[6])
entity_type = sys.argv[5].lower()
if entity_type in usergroup_types:
users = getUsersToModify(entity_type=entity_type, entity=sys.argv[6])
else:
users = getUsersToModify(entity_type=u'user', entity=sys.argv[5])
if (sys.argv[5].lower().startswith(u'cros')) or ((sys.argv[5].lower() == u'all') and (sys.argv[6].lower() == u'cros')):
entity_type = u'users'
users = getUsersToModify(entity_type=entity_type, entity=sys.argv[5])
if (entity_type.startswith(u'cros')) or ((entity_type == u'all') and (sys.argv[6].lower() == u'cros')):
for l in range(0, len(users), 50):
move_body = {u'deviceIds': users[l:l+50]}
print u' moving %s devices to %s' % (len(move_body[u'deviceIds']), orgUnitPath)
@@ -9391,20 +9376,7 @@ def _getFilterDate(dateStr):
def doGetCrosInfo():
cd = buildGAPIObject(u'directory')
deviceId = sys.argv[3]
if deviceId[:6].lower() == u'query:':
query = deviceId[6:]
if query[:12].lower() == u'orgunitpath:':
kwargs = {u'orgUnitPath': query[12:]}
else:
kwargs = {u'query': query}
devices_result = callGAPIpages(cd.chromeosdevices(), u'list', u'chromeosdevices',
customerId=GC_Values[GC_CUSTOMER_ID], fields=u'chromeosdevices/deviceId,nextPageToken', **kwargs)
devices = list()
for a_device in devices_result:
devices.append(a_device[u'deviceId'])
else:
devices = [deviceId,]
i, devices = getCrOSDeviceEntity(3, cd)
downloadfile = None
targetFolder = GC_Values[GC_DRIVE_DIR]
projection = None
@@ -9412,7 +9384,6 @@ def doGetCrosInfo():
noLists = False
startDate = endDate = None
listLimit = 0
i = 4
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace(u'_', u'')
if myarg == u'nolists':
@@ -9759,10 +9730,11 @@ def doGetNotifications():
print u'--------------'
print u''
def orgUnitPathQuery(path):
if path != u'/':
return u"orgUnitPath='{0}'".format(path.replace(u"'", u"\\'"))
return None
def orgUnitPathQuery(path, checkSuspended):
query = u"orgUnitPath='{0}'".format(path.replace(u"'", u"\\'")) if path != u'/' else u''
if checkSuspended is not None:
query += u' isSuspended={0}'.format(checkSuspended)
return query
def makeOrgUnitPathAbsolute(path):
if path == u'/':
@@ -9830,6 +9802,7 @@ def getTopLevelOrgId(cd, orgUnitPath):
def doGetOrgInfo(name=None, return_attrib=None):
cd = buildGAPIObject(u'directory')
checkSuspended = None
if not name:
name = getOrgUnitItem(sys.argv[3])
get_users = True
@@ -9843,6 +9816,9 @@ def doGetOrgInfo(name=None, return_attrib=None):
elif myarg in [u'children', u'child']:
show_children = True
i += 1
elif myarg in [u'suspended', u'notsuspended']:
checkSuspended = myarg == u'suspended'
i += 1
else:
systemErrorExit(2, '%s is not a valid argument for "gam info org"' % sys.argv[i])
if name == u'/':
@@ -9863,11 +9839,16 @@ def doGetOrgInfo(name=None, return_attrib=None):
print_json(None, result)
if get_users:
name = result[u'orgUnitPath']
print u'Users: '
page_message = u'Got %%total_items%% Users: %%first_item%% - %%last_item%%\n'
users = callGAPIpages(cd.users(), u'list', u'users', page_message=page_message,
message_attribute=u'primaryEmail', customer=GC_Values[GC_CUSTOMER_ID], query=orgUnitPathQuery(name),
message_attribute=u'primaryEmail', customer=GC_Values[GC_CUSTOMER_ID], query=orgUnitPathQuery(name, checkSuspended),
fields=u'users(primaryEmail,orgUnitPath),nextPageToken', maxResults=GC_Values[GC_USER_MAX_RESULTS])
if checkSuspended is None:
print u'Users:'
elif not checkSuspended:
print u'Users (Not suspended):'
else:
print u'Users (Suspended):'
for user in users:
if show_children or (name.lower() == user[u'orgUnitPath'].lower()):
sys.stdout.write(u' %s' % user[u'primaryEmail'])
@@ -10919,7 +10900,7 @@ def doPrintGroupMembers():
todrive = False
membernames = False
customer = GC_Values[GC_CUSTOMER_ID]
usedomain = usemember = usequery = None
checkSuspended = usedomain = usemember = usequery = None
roles = []
fields = u'nextPageToken,members(email,id,role,status,type)'
titles = [u'group']
@@ -10927,7 +10908,7 @@ def doPrintGroupMembers():
groups_to_get = []
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower()
myarg = sys.argv[i].lower().replace(u'_', u'')
if myarg == u'todrive':
todrive = True
i += 1
@@ -10958,10 +10939,17 @@ def doPrintGroupMembers():
else:
systemErrorExit(2, '%s is not a valid role for "gam print group-members %s"' % (role, myarg))
i += 2
elif myarg == u'group':
elif myarg in [u'group', u'groupns', u'groupsusp']:
group_email = normalizeEmailAddressOrUID(sys.argv[i+1])
groups_to_get = [{u'email': group_email}]
if myarg == u'groupns':
checkSuspended = False
elif myarg == u'groupsusp':
checkSuspended = True
i += 2
elif myarg in [u'suspended', u'notsuspended']:
checkSuspended = myarg == u'suspended'
i += 1
else:
systemErrorExit(2, '%s is not a valid argument for "gam print group-members"' % sys.argv[i])
if not groups_to_get:
@@ -10979,10 +10967,8 @@ def doPrintGroupMembers():
soft_errors=True,
groupKey=group_email, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
for member in group_members:
for unwanted_item in [u'kind', u'etag']:
if unwanted_item in member:
del member[unwanted_item]
if validRoles and member.get(u'role', ROLE_MEMBER) not in validRoles:
if ((validRoles and member.get(u'role', ROLE_MEMBER) not in validRoles) or
(checkSuspended is not None and ((not checkSuspended and member[u'status'] == u'SUSPENDED') or (checkSuspended and member[u'status'] != u'SUSPENDED')))):
continue
for title in member:
if title not in titles:
@@ -11045,6 +11031,43 @@ def doPrintVaultMatters():
sortCSVTitles(initialTitles, titles)
writeCSVfile(csvRows, titles, u'Vault Matters', todrive)
def doPrintVaultExports():
v = buildGAPIObject(u'vault')
todrive = False
csvRows = []
initialTitles = [u'matterId', u'id', u'name', u'createTime', u'status']
titles = initialTitles[:]
matters = []
matterIds = []
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace(u'_', u'')
if myarg == u'todrive':
todrive = True
i += 1
elif myarg in [u'matter', u'matters']:
matters = sys.argv[i+1].split(u',')
i += 2
else:
systemErrorExit(3, '%s is not a valid a valid argument to "gam print exports"' % myarg)
if not matters:
matters_results = callGAPIpages(v.matters(), u'list', u'matters', view=u'BASIC', fields=u'matters(matterId,state),nextPageToken')
for matter in matters_results:
if matter[u'state'] != u'OPEN':
print u'ignoring matter %s in state %s' % (matter[u'matterId'], matter[u'state'])
continue
matterIds.append(matter[u'matterId'])
else:
for matter in matters:
matterIds.append(getMatterItem(v, matter))
for matterId in matterIds:
sys.stderr.write(u'Retrieving exports for matter %s\n' % matterId)
exports = callGAPIpages(v.matters().exports(), u'list', u'exports', matterId=matterId)
for export in exports:
addRowTitlesToCSVfile(flatten_json(export, flattened={u'matterId': matterId}), csvRows, titles)
sortCSVTitles(initialTitles, titles)
writeCSVfile(csvRows, titles, u'Vault Exports', todrive)
def doPrintVaultHolds():
v = buildGAPIObject(u'vault')
todrive = False
@@ -11071,8 +11094,9 @@ def doPrintVaultHolds():
print u'ignoring matter %s in state %s' % (matter[u'matterId'], matter[u'state'])
continue
matterIds.append(matter[u'matterId'])
for matter in matters:
matterIds.append(convertMatterNameToID(v, matter))
else:
for matter in matters:
matterIds.append(getMatterItem(v, matter))
for matterId in matterIds:
sys.stderr.write(u'Retrieving holds for matter %s\n' % matterId)
holds = callGAPIpages(v.matters().holds(), u'list', u'holds', matterId=matterId)
@@ -11728,7 +11752,7 @@ def _getRoleVerification(memberRoles, fields):
else:
return (set(), memberRoles, fields)
def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=None, checkNotSuspended=False, groupUserMembersOnly=True):
def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=None, checkSuspended=None, groupUserMembersOnly=True):
got_uids = False
if entity_type is None:
entity_type = sys.argv[1].lower()
@@ -11739,7 +11763,11 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
users = [entity,]
elif entity_type == u'users':
users = entity.replace(u',', u' ').split()
elif entity_type == u'group':
elif entity_type in [u'group', u'group_ns', u'group_susp']:
if entity_type == u'group_ns':
checkSuspended = False
elif entity_type == u'group_susp':
checkSuspended = True
got_uids = True
group = entity
if member_type is None:
@@ -11758,44 +11786,51 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
for member in members:
if (((not groupUserMembersOnly) or (member[u'type'] == u'USER')) and
(not validRoles or member.get(u'role', ROLE_MEMBER) in validRoles) and
not (checkNotSuspended and (member[u'status'] == u'SUSPENDED'))):
(checkSuspended is None or (not checkSuspended and member[u'status'] != u'SUSPENDED') or (checkSuspended and member[u'status'] == u'SUSPENDED'))):
users.append(member.get(u'email', member[u'id']))
elif entity_type in [u'ou', u'org']:
elif entity_type in [u'ou', u'org', u'ou_ns', u'org_ns', u'ou_susp', u'org_susp',]:
if entity_type in [u'ou_ns', u'org_ns']:
checkSuspended = False
elif entity_type in [u'ou_susp', u'org_susp']:
checkSuspended = True
got_uids = True
ou = makeOrgUnitPathAbsolute(entity)
users = []
if ou.startswith(u'id:'):
ou = callGAPI(cd.orgunits(), u'get',
customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=ou, fields=u'orgUnitPath')[u'orgUnitPath']
query = orgUnitPathQuery(ou)
query = orgUnitPathQuery(ou, checkSuspended)
page_message = None
if not silent:
printGettingAllItems(u'Users', query)
page_message = u'Got %%total_items%% Users...'
members = callGAPIpages(cd.users(), u'list', u'users', page_message=page_message,
customer=GC_Values[GC_CUSTOMER_ID], fields=u'nextPageToken,users(primaryEmail,suspended,orgUnitPath)',
customer=GC_Values[GC_CUSTOMER_ID], fields=u'nextPageToken,users(primaryEmail,orgUnitPath)',
query=query, maxResults=GC_Values[GC_USER_MAX_RESULTS])
ou = ou.lower()
for member in members:
if (ou == member.get(u'orgUnitPath', u'').lower()) and not (checkNotSuspended and member[u'suspended']):
if ou == member.get(u'orgUnitPath', u'').lower():
users.append(member[u'primaryEmail'])
if not silent:
sys.stderr.write(u"%s Users are directly in the OU.\n" % len(users))
elif entity_type in [u'ou_and_children', u'ou_and_child']:
elif entity_type in [u'ou_and_children', u'ou_and_child', u'ou_and_children_ns', u'ou_and_child_ns', u'ou_and_children_susp', u'ou_and_child_susp']:
if entity_type in [u'ou_and_children_ns', u'ou_and_child_ns']:
checkSuspended = False
elif entity_type in [u'ou_and_children_susp', u'ou_and_child_susp']:
checkSuspended = True
got_uids = True
ou = makeOrgUnitPathAbsolute(entity)
users = []
query = orgUnitPathQuery(ou)
query = orgUnitPathQuery(ou, checkSuspended)
page_message = None
if not silent:
printGettingAllItems(u'Users', query)
page_message = u'Got %%total_items%% Users...'
members = callGAPIpages(cd.users(), u'list', u'users', page_message=page_message,
customer=GC_Values[GC_CUSTOMER_ID], fields=u'nextPageToken,users(primaryEmail,suspended)',
customer=GC_Values[GC_CUSTOMER_ID], fields=u'nextPageToken,users(primaryEmail)',
query=query, maxResults=GC_Values[GC_USER_MAX_RESULTS])
for member in members:
if not (checkNotSuspended and member[u'suspended']):
users.append(member[u'primaryEmail'])
users.append(member[u'primaryEmail'])
if not silent:
sys.stderr.write(u"done.\r\n")
elif entity_type in [u'query', u'queries']:
@@ -11814,11 +11849,10 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
customer=GC_Values[GC_CUSTOMER_ID], fields=u'nextPageToken,users(primaryEmail,suspended)',
query=query, maxResults=GC_Values[GC_USER_MAX_RESULTS])
for member in members:
if not checkNotSuspended or not member[u'suspended']:
email = member[u'primaryEmail']
if email not in usersSet:
usersSet.add(email)
users.append(email)
email = member[u'primaryEmail']
if (checkSuspended is None or checkSuspended == member[u'suspended']) and email not in usersSet:
usersSet.add(email)
users.append(email)
if not silent:
sys.stderr.write(u"done.\r\n")
elif entity_type in [u'license', u'licenses', u'licence', u'licences']:
@@ -11873,15 +11907,15 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
users = []
entity = entity.lower()
if entity == u'users':
query = u'isSuspended=False'
if not silent:
printGettingAllItems(u'Users', None)
page_message = u'Got %%total_items%% Users...'
all_users = callGAPIpages(cd.users(), u'list', u'users', page_message=page_message,
customer=GC_Values[GC_CUSTOMER_ID],
fields=u'nextPageToken,users(primaryEmail,suspended)', maxResults=GC_Values[GC_USER_MAX_RESULTS])
customer=GC_Values[GC_CUSTOMER_ID], query=query,
fields=u'nextPageToken,users(primaryEmail)', maxResults=GC_Values[GC_USER_MAX_RESULTS])
for member in all_users:
if not member[u'suspended']:
users.append(member[u'primaryEmail'])
users.append(member[u'primaryEmail'])
if not silent:
sys.stderr.write(u"done getting %s Users.\r\n" % len(users))
elif entity == u'cros':
@@ -11900,11 +11934,13 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
elif entity_type == u'cros':
users = entity.replace(u',', u' ').split()
entity = u'cros'
elif entity_type in [u'crosquery', u'crosqueries']:
if entity_type == u'crosquery':
queries = [entity]
else:
elif entity_type in [u'crosquery', u'crosqueries', u'cros_sn']:
if entity_type == u'cros_sn':
queries = [u'id:{0}'.format(sn) for sn in shlexSplitList(entity)]
elif entity_type == u'crosqueries':
queries = shlexSplitList(entity)
else:
queries = [entity]
users = []
usersSet = set()
for query in queries:
@@ -11989,7 +12025,7 @@ def doRequestOAuth(login_hint=None):
flags = cmd_flags(noLocalWebserver=GC_Values[GC_NO_BROWSER])
scopes = getScopesFromUser()
if scopes is None:
return
systemErrorExit(0, u'')
client_id, client_secret = getOAuthClientIDAndSecret()
login_hint = getValidateLoginHint(login_hint)
flow = oauth2client.client.OAuth2WebServerFlow(client_id=client_id,
@@ -12081,9 +12117,6 @@ OAUTH2_SCOPES = [
{u'name': u'Directory API - Users',
u'subscopes': [u'readonly'],
u'scopes': u'https://www.googleapis.com/auth/admin.directory.user'},
{u'name': u'Email Settings API - Delegation',
u'subscopes': [],
u'scopes': u'https://apps-apis.google.com/a/feeds/emailsettings/2.0/'},
{u'name': u'Group Settings API',
u'subscopes': [],
u'scopes': u'https://www.googleapis.com/auth/apps.groups.settings'},
@@ -12551,7 +12584,7 @@ def ProcessGAMCommand(args):
elif argument in [u'hold', u'vaulthold']:
doGetVaultHoldInfo()
elif argument in [u'export', u'vaultexport']:
doGetVaultExport()
doGetVaultExportInfo()
elif argument in [u'building']:
doGetBuildingInfo()
else:
@@ -12606,6 +12639,8 @@ def ProcessGAMCommand(args):
doUpdateVaultMatter(action=command)
elif argument in [u'hold', u'vaulthold']:
doDeleteVaultHold()
elif argument in [u'export', u'vaultexport']:
doDeleteVaultExport()
elif argument in [u'building']:
doDeleteBuilding()
elif argument in [u'feature']:
@@ -12682,6 +12717,8 @@ def ProcessGAMCommand(args):
doPrintVaultMatters()
elif argument in [u'holds', u'vaultholds']:
doPrintVaultHolds()
elif argument in [u'exports', u'vaultexports']:
doPrintVaultExports()
elif argument in [u'building', u'buildings']:
doPrintBuildings()
elif argument in [u'feature', u'features']:

View File

@@ -10,7 +10,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-settings-v2.json', 'email-settings-v2.json', 'DATA')]
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,

View File

@@ -10,7 +10,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-settings-v2.json', 'email-settings-v2.json', 'DATA')]
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,

View File

@@ -4,7 +4,7 @@ import platform
import re
gam_author = u'Jay Lee <jay0lee@gmail.com>'
gam_version = u'4.60'
gam_version = u'4.61'
gam_license = u'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
GAM_URL = u'https://git.io/gam'
@@ -24,10 +24,12 @@ TRUE = u'true'
FALSE = u'false'
true_values = [u'on', u'yes', u'enabled', u'true', u'1']
false_values = [u'off', u'no', u'disabled', u'false', u'0']
usergroup_types = [u'user', u'users', u'group', u'ou', u'org',
u'ou_and_children', u'ou_and_child', u'query', u'queries',
u'license', u'licenses', u'licence', u'licences', u'file', u'csv', u'csvfile', u'all',
u'cros', u'crosquery', u'crosqueries', u'crosfile', u'croscsv', u'croscsvfile']
usergroup_types = [u'user', u'users',
u'group', u'group_ns', u'grooup_susp',
u'ou', u'org', u'ou_ns', u'org_ns', u'ou_susp', u'org_susp',
u'ou_and_children', u'ou_and_child', u'ou_and_children_ns', u'ou_and_child_ns', u'ou_and_children_susp', u'ou_and_child_susp',
u'query', u'queries', u'license', u'licenses', u'licence', u'licences', u'file', u'csv', u'csvfile', u'all',
u'cros', u'cros_sn', u'crosquery', u'crosqueries', u'crosfile', u'croscsv', u'croscsvfile']
ERROR = u'ERROR'
ERROR_PREFIX = ERROR+u': '
WARNING = u'WARNING'
@@ -100,7 +102,6 @@ API_VER_MAPPING = {
u'directory': u'directory_v1',
u'drive': u'v2',
u'drive3': u'v3',
u'email-settings': u'v2',
u'gmail': u'v1',
u'groupssettings': u'v1',
u'licensing': u'v1',

View File

@@ -1,3 +1,16 @@
GAM 4.61
- New Gmail delegation API
- Remove "admin" command from user create/update to avoid accidental super admins. Still possible to give super admin rights via "gam create admin" command. (Ross)
- Vault export fixes by Ross
- minor fixes and improvements by Ross and Jay
GAM 4.60
- Google Vault Export API gam support
- Ross - add textcolor and backgroundcolor options for Gmail labels
- Ross - add owneremail option to print courses
- GAM 4.50 had no bugs at all, nothing to fix.
- Just kidding, lots of bug fixes and code cleanup. Thanks Ross.
GAM 4.50
- many cleanups, bugfixes and improvements by Ross and ejochman
- multiple queries and more options for Chrome OS by Ross

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-settings-v2.json', 'email-settings-v2.json', 'DATA')]
pyz = PYZ(a.pure)
exe = EXE(pyz,
a.scripts,