mirror of
https://github.com/GAM-team/GAM.git
synced 2026-07-03 20:31:35 +00:00
Lookup service account emails by ID
This commit is contained in:
@@ -15,6 +15,7 @@ datas += [('cbcm-v1.1beta1.json', '.')]
|
|||||||
datas += [('chat-v1.json', '.')]
|
datas += [('chat-v1.json', '.')]
|
||||||
datas += [('contactdelegation-v1.json', '.')]
|
datas += [('contactdelegation-v1.json', '.')]
|
||||||
datas += [('datastudio-v1.json', '.')]
|
datas += [('datastudio-v1.json', '.')]
|
||||||
|
datas += [('serviceaccountlookup-v1.json', '.')]
|
||||||
datas += [('cacerts.pem', '.')]
|
datas += [('cacerts.pem', '.')]
|
||||||
hiddenimports = [
|
hiddenimports = [
|
||||||
'gam.auth.yubikey',
|
'gam.auth.yubikey',
|
||||||
|
|||||||
@@ -5485,7 +5485,8 @@ def buildGAPIObject(api, credentials=None):
|
|||||||
API_Scopes = set(API.VAULT_SCOPES) if api == API.VAULT else set()
|
API_Scopes = set(API.VAULT_SCOPES) if api == API.VAULT else set()
|
||||||
GM.Globals[GM.CURRENT_CLIENT_API] = api
|
GM.Globals[GM.CURRENT_CLIENT_API] = api
|
||||||
GM.Globals[GM.CURRENT_CLIENT_API_SCOPES] = API_Scopes.intersection(GM.Globals[GM.CREDENTIALS_SCOPES])
|
GM.Globals[GM.CURRENT_CLIENT_API_SCOPES] = API_Scopes.intersection(GM.Globals[GM.CREDENTIALS_SCOPES])
|
||||||
if api not in {API.OAUTH2, API.CHROMEVERSIONHISTORY} and not GM.Globals[GM.CURRENT_CLIENT_API_SCOPES]:
|
scopeless_apis = {API.OAUTH2, API.CHROMEVERSIONHISTORY, API.SERVICEACCOUNTLOOKUP}
|
||||||
|
if api not in scopeless_apis and not GM.Globals[GM.CURRENT_CLIENT_API_SCOPES]:
|
||||||
systemErrorExit(NO_SCOPES_FOR_API_RC, Msg.NO_SCOPES_FOR_API.format(API.getAPIName(api)))
|
systemErrorExit(NO_SCOPES_FOR_API_RC, Msg.NO_SCOPES_FOR_API.format(API.getAPIName(api)))
|
||||||
if not GC.Values[GC.DOMAIN]:
|
if not GC.Values[GC.DOMAIN]:
|
||||||
GC.Values[GC.DOMAIN] = GM.Globals[GM.DECODED_ID_TOKEN].get('hd', 'UNKNOWN').lower()
|
GC.Values[GC.DOMAIN] = GM.Globals[GM.DECODED_ID_TOKEN].get('hd', 'UNKNOWN').lower()
|
||||||
@@ -5624,7 +5625,7 @@ def getGroupEmailFromID(uid, cd):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
# Convert UID to email address and type
|
# Convert UID to email address and type
|
||||||
def convertUIDtoEmailAddressWithType(emailAddressOrUID, cd=None, emailTypes=None,
|
def convertUIDtoEmailAddressWithType(emailAddressOrUID, cd=None, sal=None, emailTypes=None,
|
||||||
checkForCustomerId=False, ciGroupsAPI=False, aliasAllowed=True):
|
checkForCustomerId=False, ciGroupsAPI=False, aliasAllowed=True):
|
||||||
if emailTypes is None:
|
if emailTypes is None:
|
||||||
emailTypes = ['user']
|
emailTypes = ['user']
|
||||||
@@ -5675,8 +5676,36 @@ def convertUIDtoEmailAddressWithType(emailAddressOrUID, cd=None, emailTypes=None
|
|||||||
return (result['resourceEmail'].lower(), 'resource')
|
return (result['resourceEmail'].lower(), 'resource')
|
||||||
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
|
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
|
||||||
pass
|
pass
|
||||||
|
if 'serviceaccount' in emailTypes:
|
||||||
|
if sal is None:
|
||||||
|
sal = buildGAPIObject(API.SERVICEACCOUNTLOOKUP)
|
||||||
|
uid = getServiceAccountEmailFromID(normalizedEmailAddressOrUID, sal)
|
||||||
|
if uid:
|
||||||
|
return (uid, 'serviceaccount')
|
||||||
return (normalizedEmailAddressOrUID, 'unknown')
|
return (normalizedEmailAddressOrUID, 'unknown')
|
||||||
|
|
||||||
|
def getServiceAccountEmailFromID(account_id, sal=None):
|
||||||
|
if sal is None:
|
||||||
|
sal = buildGAPIObject('serviceaccountlookup')
|
||||||
|
throwReasons = [GAPI.BAD_REQUEST,
|
||||||
|
GAPI.RESOURCE_NOT_FOUND,
|
||||||
|
GAPI.INVALID_ARGUMENT]
|
||||||
|
try:
|
||||||
|
certs = callGAPI(sal.serviceaccounts(),
|
||||||
|
'lookup',
|
||||||
|
account=account_id,
|
||||||
|
throwReasons=throwReasons)
|
||||||
|
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.invalidArgument):
|
||||||
|
return
|
||||||
|
sa_cn_rx = r'CN=.*\.gserviceaccount\.com$'
|
||||||
|
sa_emails = []
|
||||||
|
for kid, raw_cert in certs.items():
|
||||||
|
cert = x509.load_pem_x509_certificate(raw_cert.encode(), default_backend())
|
||||||
|
subject = cert.issuer.rfc4514_string()
|
||||||
|
if re.match(sa_cn_rx, subject):
|
||||||
|
sa_emails.append(subject[3:])
|
||||||
|
return ' or '.join(sa_emails)
|
||||||
|
|
||||||
# Convert UID to email address
|
# Convert UID to email address
|
||||||
def convertUIDtoEmailAddress(emailAddressOrUID, cd=None, emailTypes=None,
|
def convertUIDtoEmailAddress(emailAddressOrUID, cd=None, emailTypes=None,
|
||||||
checkForCustomerId=False, ciGroupsAPI=False, aliasAllowed=True):
|
checkForCustomerId=False, ciGroupsAPI=False, aliasAllowed=True):
|
||||||
@@ -16177,6 +16206,9 @@ def doCreateUpdateAdminRoles():
|
|||||||
body['rolePrivileges'].append({'privilegeName': p, 'serviceId': ouPrivileges[p]})
|
body['rolePrivileges'].append({'privilegeName': p, 'serviceId': ouPrivileges[p]})
|
||||||
elif p in childPrivileges:
|
elif p in childPrivileges:
|
||||||
body['rolePrivileges'].append({'privilegeName': p, 'serviceId': childPrivileges[p]})
|
body['rolePrivileges'].append({'privilegeName': p, 'serviceId': childPrivileges[p]})
|
||||||
|
elif ':' in p:
|
||||||
|
priv, serv = p.split(':')
|
||||||
|
body['rolePrivileges'].append({'privilegeName': priv, 'serviceId': serv.lower()})
|
||||||
else:
|
else:
|
||||||
invalidChoiceExit(p, list(allPrivileges.keys())+list(ouPrivileges.keys())+list(childPrivileges.keys()), True)
|
invalidChoiceExit(p, list(allPrivileges.keys())+list(ouPrivileges.keys())+list(childPrivileges.keys()), True)
|
||||||
elif myarg == 'description':
|
elif myarg == 'description':
|
||||||
@@ -16255,7 +16287,10 @@ def doInfoAdminRole():
|
|||||||
fields = ','.join(set(fieldsList))
|
fields = ','.join(set(fieldsList))
|
||||||
try:
|
try:
|
||||||
role = callGAPI(cd.roles(), 'get',
|
role = callGAPI(cd.roles(), 'get',
|
||||||
throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND, GAPI.FORBIDDEN]+[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION],
|
throwReasons=[GAPI.BAD_REQUEST,
|
||||||
|
GAPI.CUSTOMER_NOT_FOUND,
|
||||||
|
GAPI.FORBIDDEN]+[GAPI.NOT_FOUND,
|
||||||
|
GAPI.FAILED_PRECONDITION],
|
||||||
customer=GC.Values[GC.CUSTOMER_ID], roleId=roleId, fields=fields)
|
customer=GC.Values[GC.CUSTOMER_ID], roleId=roleId, fields=fields)
|
||||||
role.setdefault('isSuperAdminRole', False)
|
role.setdefault('isSuperAdminRole', False)
|
||||||
role.setdefault('isSystemRole', False)
|
role.setdefault('isSystemRole', False)
|
||||||
@@ -16414,8 +16449,13 @@ def doPrintShowAdmins():
|
|||||||
if roleId not in rolePrivileges:
|
if roleId not in rolePrivileges:
|
||||||
try:
|
try:
|
||||||
rolePrivileges[roleId] = callGAPI(cd.roles(), 'get',
|
rolePrivileges[roleId] = callGAPI(cd.roles(), 'get',
|
||||||
throwReasons=[GAPI.BAD_REQUEST, GAPI.CUSTOMER_NOT_FOUND, GAPI.FORBIDDEN]+[GAPI.NOT_FOUND, GAPI.FAILED_PRECONDITION],
|
throwReasons=[GAPI.BAD_REQUEST,
|
||||||
customer=GC.Values[GC.CUSTOMER_ID], roleId=roleId, fields='rolePrivileges')
|
GAPI.CUSTOMER_NOT_FOUND,
|
||||||
|
GAPI.FORBIDDEN]+[GAPI.NOT_FOUND,
|
||||||
|
GAPI.FAILED_PRECONDITION],
|
||||||
|
customer=GC.Values[GC.CUSTOMER_ID],
|
||||||
|
roleId=roleId,
|
||||||
|
fields='rolePrivileges')
|
||||||
except (GAPI.notFound, GAPI.forbidden, GAPI.failedPrecondition) as e:
|
except (GAPI.notFound, GAPI.forbidden, GAPI.failedPrecondition) as e:
|
||||||
entityActionFailedExit([Ent.USER, userKey, Ent.ADMIN_ROLE, admin['roleId']], str(e))
|
entityActionFailedExit([Ent.USER, userKey, Ent.ADMIN_ROLE, admin['roleId']], str(e))
|
||||||
rolePrivileges[roleId] = None
|
rolePrivileges[roleId] = None
|
||||||
@@ -16432,14 +16472,22 @@ def doPrintShowAdmins():
|
|||||||
assignedToField = 'assignedToUser'
|
assignedToField = 'assignedToUser'
|
||||||
elif assigneeType == 'group':
|
elif assigneeType == 'group':
|
||||||
assignedToField = 'assignedToGroup'
|
assignedToField = 'assignedToGroup'
|
||||||
|
elif assigneeType == 'serviceaccount':
|
||||||
|
assignedToField = 'assignedToServiceAccount'
|
||||||
else:
|
else:
|
||||||
assignedToField = None
|
assignedToField = None
|
||||||
assigneeEmail, assigneeType = convertUIDtoEmailAddressWithType(f'uid:{assignedTo}', cd, emailTypes=['user', 'group'])
|
emailTypes = ['user', 'group', 'serviceaccount']
|
||||||
if not assignedToField and assigneeType in ['user', 'group']:
|
assigneeEmail, assigneeType = convertUIDtoEmailAddressWithType(f'uid:{assignedTo}',
|
||||||
|
cd,
|
||||||
|
sal,
|
||||||
|
emailTypes=emailTypes)
|
||||||
|
if not assignedToField and assigneeType in ['user', 'group', 'serviceaccount']:
|
||||||
if assigneeType == 'user':
|
if assigneeType == 'user':
|
||||||
assignedToField = 'assignedToUser'
|
assignedToField = 'assignedToUser'
|
||||||
else:
|
elif assigneeType == 'group':
|
||||||
assignedToField = 'assignedToGroup'
|
assignedToField = 'assignedToGroup'
|
||||||
|
elif assigneeType == 'serviceaccount':
|
||||||
|
assignedToField = 'assignedToServiceAccount'
|
||||||
assignedToIdEmailMap[assignedTo] = {'assignedToField': assignedToField, 'assigneeEmail': assigneeEmail}
|
assignedToIdEmailMap[assignedTo] = {'assignedToField': assignedToField, 'assigneeEmail': assigneeEmail}
|
||||||
assignedToField = assignedToIdEmailMap[assignedTo]['assignedToField']
|
assignedToField = assignedToIdEmailMap[assignedTo]['assignedToField']
|
||||||
if assignedToField:
|
if assignedToField:
|
||||||
@@ -16455,6 +16503,7 @@ def doPrintShowAdmins():
|
|||||||
admin['condition'] = 'nonsecuritygroup'
|
admin['condition'] = 'nonsecuritygroup'
|
||||||
|
|
||||||
cd = buildGAPIObject(API.DIRECTORY)
|
cd = buildGAPIObject(API.DIRECTORY)
|
||||||
|
sal = buildGAPIObject(API.SERVICEACCOUNTLOOKUP)
|
||||||
csvPF = CSVPrintFile(PRINT_ADMIN_TITLES) if Act.csvFormat() else None
|
csvPF = CSVPrintFile(PRINT_ADMIN_TITLES) if Act.csvFormat() else None
|
||||||
roleId = None
|
roleId = None
|
||||||
userKey = None
|
userKey = None
|
||||||
@@ -25673,10 +25722,10 @@ def _getChatMemberEmail(cd, member):
|
|||||||
if 'member' in member:
|
if 'member' in member:
|
||||||
if member['member']['type'] == 'HUMAN':
|
if member['member']['type'] == 'HUMAN':
|
||||||
_, memberUid = member['member']['name'].split('/')
|
_, memberUid = member['member']['name'].split('/')
|
||||||
member['member']['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, emailTypes=['user'])
|
member['member']['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, None, emailTypes=['user'])
|
||||||
elif 'groupMember' in member:
|
elif 'groupMember' in member:
|
||||||
_, memberUid = member['groupMember']['name'].split('/')
|
_, memberUid = member['groupMember']['name'].split('/')
|
||||||
member['groupMember']['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, emailTypes=['group'])
|
member['groupMember']['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, None, emailTypes=['group'])
|
||||||
|
|
||||||
# gam <UserTypeEntity> create chatmember <ChatSpace>
|
# gam <UserTypeEntity> create chatmember <ChatSpace>
|
||||||
# [type human|bot] [role member|manager]
|
# [type human|bot] [role member|manager]
|
||||||
@@ -26300,7 +26349,7 @@ def doPrintShowChatMembers():
|
|||||||
def _getChatSenderEmail(cd, sender):
|
def _getChatSenderEmail(cd, sender):
|
||||||
if sender['type'] == 'HUMAN':
|
if sender['type'] == 'HUMAN':
|
||||||
_, senderUid = sender['name'].split('/')
|
_, senderUid = sender['name'].split('/')
|
||||||
sender['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{senderUid}', cd, emailTypes=['user'])
|
sender['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{senderUid}', cd, None, emailTypes=['user'])
|
||||||
|
|
||||||
def trimChatMessageIfRequired(body):
|
def trimChatMessageIfRequired(body):
|
||||||
msgLen = len(body['text'])
|
msgLen = len(body['text'])
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ PRINTERS = 'printers'
|
|||||||
PUBSUB = 'pubsub'
|
PUBSUB = 'pubsub'
|
||||||
REPORTS = 'reports'
|
REPORTS = 'reports'
|
||||||
RESELLER = 'reseller'
|
RESELLER = 'reseller'
|
||||||
|
SERVICEACCOUNTLOOKUP = 'serviceaccountlookup'
|
||||||
SERVICEMANAGEMENT = 'servicemanagement'
|
SERVICEMANAGEMENT = 'servicemanagement'
|
||||||
SERVICEUSAGE = 'serviceusage'
|
SERVICEUSAGE = 'serviceusage'
|
||||||
SHEETS = 'sheets'
|
SHEETS = 'sheets'
|
||||||
@@ -252,6 +253,7 @@ _INFO = {
|
|||||||
PUBSUB: {'name': 'Pub / Sub API', 'version': 'v1', 'v2discovery': True},
|
PUBSUB: {'name': 'Pub / Sub API', 'version': 'v1', 'v2discovery': True},
|
||||||
REPORTS: {'name': 'Reports API', 'version': 'reports_v1', 'v2discovery': True, 'mappedAPI': 'admin'},
|
REPORTS: {'name': 'Reports API', 'version': 'reports_v1', 'v2discovery': True, 'mappedAPI': 'admin'},
|
||||||
RESELLER: {'name': 'Reseller API', 'version': 'v1', 'v2discovery': True},
|
RESELLER: {'name': 'Reseller API', 'version': 'v1', 'v2discovery': True},
|
||||||
|
SERVICEACCOUNTLOOKUP: {'name': 'Service Account Lookup psuedo-API', 'version': 'v1', 'v2discovery': True, 'localjson': True},
|
||||||
SERVICEMANAGEMENT: {'name': 'Service Management API', 'version': 'v1', 'v2discovery': True},
|
SERVICEMANAGEMENT: {'name': 'Service Management API', 'version': 'v1', 'v2discovery': True},
|
||||||
SERVICEUSAGE: {'name': 'Service Usage API', 'version': 'v1', 'v2discovery': True},
|
SERVICEUSAGE: {'name': 'Service Usage API', 'version': 'v1', 'v2discovery': True},
|
||||||
SHEETS: {'name': 'Sheets API', 'version': 'v4', 'v2discovery': True},
|
SHEETS: {'name': 'Sheets API', 'version': 'v4', 'v2discovery': True},
|
||||||
@@ -467,6 +469,10 @@ _CLIENT_SCOPES = [
|
|||||||
'subscopes': [],
|
'subscopes': [],
|
||||||
'offByDefault': True,
|
'offByDefault': True,
|
||||||
'scope': 'https://www.googleapis.com/auth/apps.order'},
|
'scope': 'https://www.googleapis.com/auth/apps.order'},
|
||||||
|
{'name': 'Service Account Lookup psuedo-API',
|
||||||
|
'api': SERVICEACCOUNTLOOKUP,
|
||||||
|
'subscopes': [],
|
||||||
|
'scope': ''},
|
||||||
{'name': 'Site Verification API',
|
{'name': 'Site Verification API',
|
||||||
'api': SITEVERIFICATION,
|
'api': SITEVERIFICATION,
|
||||||
'subscopes': [],
|
'subscopes': [],
|
||||||
|
|||||||
141
src/gam/serviceaccountlookup-v1.json
Normal file
141
src/gam/serviceaccountlookup-v1.json
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
{
|
||||||
|
"basePath": "",
|
||||||
|
"baseUrl": "https://www.googleapis.com/service_accounts/v1",
|
||||||
|
"canonicalName": "serviceaccountlookup",
|
||||||
|
"description": "Psuedo-API to lookup public certificates for a service account anonymously",
|
||||||
|
"discoveryVersion": "v1",
|
||||||
|
"documentationLink": "https://example.com/",
|
||||||
|
"fullyEncodeReservedExpansion": true,
|
||||||
|
"icons": {
|
||||||
|
"x16": "http://www.google.com/images/icons/product/search-16.gif",
|
||||||
|
"x32": "http://www.google.com/images/icons/product/search-32.gif"
|
||||||
|
},
|
||||||
|
"id": "serviceaccountlookup:v1",
|
||||||
|
"kind": "discovery#restDescription",
|
||||||
|
"name": "serviceaccountlookup",
|
||||||
|
"ownerDomain": "google.com",
|
||||||
|
"ownerName": "Google",
|
||||||
|
"packagePath": "admin",
|
||||||
|
"parameters": {
|
||||||
|
"$.xgafv": {
|
||||||
|
"description": "V1 error format.",
|
||||||
|
"enum": [
|
||||||
|
"1",
|
||||||
|
"2"
|
||||||
|
],
|
||||||
|
"enumDescriptions": [
|
||||||
|
"v1 error format",
|
||||||
|
"v2 error format"
|
||||||
|
],
|
||||||
|
"location": "query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"access_token": {
|
||||||
|
"description": "OAuth access token.",
|
||||||
|
"location": "query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"alt": {
|
||||||
|
"default": "json",
|
||||||
|
"description": "Data format for response.",
|
||||||
|
"enum": [
|
||||||
|
"json",
|
||||||
|
"media",
|
||||||
|
"proto"
|
||||||
|
],
|
||||||
|
"enumDescriptions": [
|
||||||
|
"Responses with Content-Type of application/json",
|
||||||
|
"Media download with context-dependent Content-Type",
|
||||||
|
"Responses with Content-Type of application/x-protobuf"
|
||||||
|
],
|
||||||
|
"location": "query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"callback": {
|
||||||
|
"description": "JSONP",
|
||||||
|
"location": "query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"fields": {
|
||||||
|
"description": "Selector specifying which fields to include in a partial response.",
|
||||||
|
"location": "query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.",
|
||||||
|
"location": "query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"oauth_token": {
|
||||||
|
"description": "OAuth 2.0 token for the current user.",
|
||||||
|
"location": "query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"prettyPrint": {
|
||||||
|
"default": "true",
|
||||||
|
"description": "Returns response with indentations and line breaks.",
|
||||||
|
"location": "query",
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"quotaUser": {
|
||||||
|
"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.",
|
||||||
|
"location": "query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uploadType": {
|
||||||
|
"description": "Legacy upload protocol for media (e.g. \"media\", \"multipart\").",
|
||||||
|
"location": "query",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"upload_protocol": {
|
||||||
|
"description": "Upload protocol for media (e.g. \"raw\", \"multipart\").",
|
||||||
|
"location": "query",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"protocol": "rest",
|
||||||
|
"resources": {
|
||||||
|
"serviceaccounts": {
|
||||||
|
"methods": {
|
||||||
|
"lookup": {
|
||||||
|
"description": "Lookup",
|
||||||
|
"flatPath": "metadata/x509/{account}",
|
||||||
|
"httpMethod": "GET",
|
||||||
|
"id": "serviceaccountslookup.lookup",
|
||||||
|
"parameterOrder": [
|
||||||
|
"account"
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"account": {
|
||||||
|
"description": "Email or ID of the service account.",
|
||||||
|
"location": "path",
|
||||||
|
"required": true,
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": "metadata/x509/{account}",
|
||||||
|
"response": {
|
||||||
|
"$ref": "Certificates"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rootUrl": "https://www.googleapis.com/service_accounts/v1",
|
||||||
|
"schemas": {
|
||||||
|
"Certificates": {
|
||||||
|
"description": "JSON template for certificates.",
|
||||||
|
"id": "Certificates",
|
||||||
|
"properties": {
|
||||||
|
"email": { "description": "Email of the delegate.",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"servicePath": "",
|
||||||
|
"title": "Service Account Lookup Psuedo-API",
|
||||||
|
"version": "v1",
|
||||||
|
"version_module": true
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user