OU with + in name causes Directory API to return information about a different OU
I am the author of Advanced GAM and have run into the following issue regarding OU names. I have two test OUs: /Test/Test+Plus /Test/Test Plus
When trying to retrieve information about /Test/Test+Plus, the URL quoting causes results to be returned for /Test/Test Plus If /Test/Test Plus doesn't exist, you get a 404 Not Found error.
Thanks,
Ross
$ gam config debug_level 1 info ous "'/Test/Test+Plus','/Test/Test Plus'" nousers
connect: (admin.googleapis.com, 443)
send: b'GET /$discovery/rest?version=directory_v1 HTTP/1.1\r\nHost: admin.googleapis.com\r\ncontent-length: 0\r\nuser-agent: GAMADV-XTD3 6.22.18 - https://github.com/taers232c/GAMADV-XTD3 / Ross Scroggs <[email protected]> / Python 3.10.4 final / macOS-10.13.6-x86_64-i386-64bit x86_64 /\r\nauthorization: Bearer xxx\r\naccept-encoding: gzip, deflate\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
...
/Test/Test+Plus returns information about /Test/Test Plus
because the + is not quoted as %2B and the + is interpreted as a space at the server
send: b'GET /admin/directory/v1/customer/my_customer/orgunits/Test/Test+Plus?prettyPrint=true&alt=json HTTP/1.1\r\nHost: admin.googleapis.com\r\naccept: application/json\r\naccept-encoding: gzip, deflate\r\nuser-agent: GAMADV-XTD3 6.22.18 - https://github.com/taers232c/GAMADV-XTD3 / Ross Scroggs <[email protected]> / Python 3.10.4 final / macOS-10.13.6-x86_64-i386-64bit x86_64 / (gzip)\r\nx-goog-api-client: gdcl/2.49.0 gl-python/3.10.4\r\ncontent-length: 0\r\nauthorization: Bearer xxx\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: ETag: "SsISqFfgRYY11XaGpPyQF5FTf1EAwqUmKLMPaD85FHw/KI28M3UI1BxB0wdXnhkjO_hi7js"
...
Organizational Unit: /Test/Test Plus (1/2)
orgUnitId: id:03ph8a2z41o9s36
name: Test Plus
description:
parentOrgUnitPath: /Test
parentOrgUnitId: id:040i61yy3fw7czd
/Test/Test Plus returns the correct information, the space is quoted as %20
send: b'GET /admin/directory/v1/customer/my_customer/orgunits/Test/Test%20Plus?prettyPrint=true&alt=json HTTP/1.1\r\nHost: admin.googleapis.com\r\naccept: application/json\r\naccept-encoding: gzip, deflate\r\nuser-agent: GAMADV-XTD3 6.22.18 - https://github.com/taers232c/GAMADV-XTD3 / Ross Scroggs <[email protected]> / Python 3.10.4 final / macOS-10.13.6-x86_64-i386-64bit x86_64 / (gzip)\r\nx-goog-api-client: gdcl/2.49.0 gl-python/3.10.4\r\ncontent-length: 0\r\nauthorization: Bearer xxx\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: ETag: "SsISqFfgRYY11XaGpPyQF5FTf1EAwqUmKLMPaD85FHw/KI28M3UI1BxB0wdXnhkjO_hi7js"
...
Organizational Unit: /Test/Test Plus (2/2)
orgUnitId: id:03ph8a2z41o9s36
name: Test Plus
description:
parentOrgUnitPath: /Test
parentOrgUnitId: id:040i61yy3fw7czd
Hi @taers232c Please provide more details on how this specific repository is used to make those failing calls. Looking at the logs it seems like they are all showing how gam is used not how the client form this repository is used to make an actual call underneath. Basically the bug is formulated as a bug in gam, not as a bug in google-api-python-client.
def callGAPI(service, function,
throwReasons=None, retryReasons=None, retries=10,
**kwargs):
method = getattr(service, function)
svcparms = dict(list(kwargs.items())
return method(**svcparms).execute()
cd = googleapiclient.discovery.build(API.DIRECTORY, version, http=httpObj, cache_discovery=False,
discoveryServiceUrl=DISCOVERY_URIS[v2discovery], static_discovery=False)
# First call Test/Test+Plus
callGAPI(cd.orgunits(), 'get',
throwReasons=GAPI.ORGUNIT_GET_THROW_REASONS,
customerId=GC.Values[GC.CUSTOMER_ID], orgUnitPath='Test/Test+Plus')
send: b'GET /admin/directory/v1/customer/my_customer/orgunits/Test/Test+Plus?prettyPrint=true&alt=json HTTP/1.1\r\nHost: admin.googleapis.com\r\naccept: application/json\r\naccept-encoding: gzip, deflate\r\nuser-agent: GAMADV-XTD3 6.22.18 - https\
://github.com/taers232c/GAMADV-XTD3 / Ross Scroggs <[email protected]> / Python 3.10.4 final / macOS-10.13.6-x86_64-i386-64bit x86_64 / (gzip)\r\nx-goog-api-client: gdcl/2.49.0 gl-python/3.10.4\r\ncontent-length: 0\r\nauthorization: Bearer x\
xx\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: ETag: "SsISqFfgRYY11XaGpPyQF5FTf1EAwqUmKLMPaD85FHw/KI28M3UI1BxB0wdXnhkjO_hi7js"
# Second call Test/Test PLus
callGAPI(cd.orgunits(), 'get',
throwReasons=GAPI.ORGUNIT_GET_THROW_REASONS,
customerId=GC.Values[GC.CUSTOMER_ID], orgUnitPath='Test/Test Plus')
send: b'GET /admin/directory/v1/customer/my_customer/orgunits/Test/Test%20Plus?prettyPrint=true&alt=json HTTP/1.1\r\nHost: admin.googleapis.com\r\naccept: application/json\r\naccept-encoding: gzip, deflate\r\nuser-agent: GAMADV-XTD3 6.22.18 - htt\
ps://github.com/taers232c/GAMADV-XTD3 / Ross Scroggs <[email protected]> / Python 3.10.4 final / macOS-10.13.6-x86_64-i386-64bit x86_64 / (gzip)\r\nx-goog-api-client: gdcl/2.49.0 gl-python/3.10.4\r\ncontent-length: 0\r\nauthorization: Bearer\
xxx\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: ETag: "SsISqFfgRYY11XaGpPyQF5FTf1EAwqUmKLMPaD85FHw/KI28M3UI1BxB0wdXnhkjO_hi7js"
Note that the two ETags are identical. Since the plus is not encoded as %2b by the googleapiclient,
the Google server interprets the + as a space and returns infomation for Test/Test Plus instead of Test/Test+Plus
Do you need any additional information?
Is this issue going to be addressed?
I think this issue is caused because uritemplate is not encoding +
https://github.com/python-hyper/uritemplate/blob/main/uritemplate/variable.py#L419
Here's the OU retrieved by ID
$ gam info ou id:03ph8a2z1fdhw7r
Organizational Unit: /Test/Test+Plus
orgUnitId: id:03ph8a2z1fdhw7r
name: Test+Plus
description:
parentOrgUnitPath: /Test
parentOrgUnitId: id:040i61yy3fw7czd
Users:
Total Users in Organizational Unit: 0
Try to retrieve the OU by name
$ gam config debug_level 1 info ou "/Test+Plus"
connect: (admin.googleapis.com, 443)
send: b'GET /$discovery/rest?version=directory_v1 HTTP/1.1\r\nHost: admin.googleapis.com\r\ncontent-length: 0\r\nuser-agent: GAMADV-XTD3 6.71.12 - https://github.com/taers232c/GAMADV-XTD3 / Ross Scroggs <ross.scrogg\
@.***> / Python 3.12.2 final / macOS-14.3.1-arm64-arm-64bit arm64 /\r\nx-goog-api-client: cred-type/u\r\nauthorization: Bearer ya29.xxx\r\naccept-encoding: gzip, deflate\r\n\r\n'
reply: 'HTTP/1.1 200 OK\r\n'
header: Content-Type: application/json; charset=UTF-8
header: Vary: Origin
header: Vary: X-Origin
header: Vary: Referer
header: Date: Sat, 09 Mar 2024 19:59:48 GMT
header: Server: ESF
header: Content-Length: 368422
header: X-XSS-Protection: 0
header: X-Frame-Options: SAMEORIGIN
header: X-Content-Type-Options: nosniff
header: Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
send: b'GET /admin/directory/v1/customer/C03kt1xxx/orgunits/Test+Plus?prettyPrint=true&alt=json HTTP/1.1\r\nHost: admin.googleapis.com\r\naccept: application/json\r\naccept-encoding: gzip, deflate\r\nuser-agent: GAM\
ADV-XTD3 6.71.12 - https://github.com/taers232c/GAMADV-XTD3 / Ross Scroggs < @.***> / Python 3.12.2 final / macOS-14.3.1-arm64-arm-64bit arm64 / (gzip)\r\nx-goog-api-client: gdcl/2.117.0 gl-python/3.\
12.2 cred-type/u\r\ncontent-length: 0\r\nauthorization: Bearer ya29.xxx\r\n\r\n'
reply: 'HTTP/1.1 404 Not Found\r\n'
header: Vary: Origin
header: Vary: X-Origin
header: Vary: Referer
header: Content-Type: application/json; charset=UTF-8
header: Content-Encoding: gzip
header: Date: Sat, 09 Mar 2024 19:59:49 GMT
header: Server: ESF
header: Cache-Control: private
header: X-XSS-Protection: 0
header: X-Frame-Options: SAMEORIGIN
header: X-Content-Type-Options: nosniff
header: Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
header: Transfer-Encoding: chunked
ERROR: JSON: {'error': {'code': 404, 'message': 'Org unit not found', 'errors': [{'message': 'Org unit not found', 'domain': 'global', 'reason': 'notFound'}]}}
Organizational Unit: Test+Plus, Show Info Failed: Does not exist
It fails because the +is not encoded' it should be:
send: b'GET /admin/directory/v1/customer/C03kt1xxx/orgunits/Test%2bPlus?prettyPrint=true&alt=json
Ross
On Fri, Jun 3, 2022 at 12:26 PM Vadym Matsishevskyi < @.***> wrote:
Hi @taers232c https://github.com/taers232c Please provide more details on how this specific repository is used to make those failing calls. Looking at the logs it seems like they are all showing how gam is used not how the client form this repository is used to make an actual call underneath. Basically the bug is formulated as a bug in gam, not as a bug in google-api-python-client.
— Reply to this email directly, view it on GitHub https://github.com/googleapis/google-api-python-client/issues/1812#issuecomment-1146291223, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACCTYL7C25ZFK7V3VDFKKB3VNJL7NANCNFSM5XG2TNYA . You are receiving this because you were mentioned.Message ID: @.***>
-- Ross Scroggs @.***