gam7 7.19.3__py3-none-any.whl → 7.28.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gam/__init__.py +1836 -700
- gam/__main__.py +6 -1
- gam/gamlib/glapi.py +35 -19
- gam/gamlib/glcfg.py +16 -0
- gam/gamlib/glclargs.py +294 -5
- gam/gamlib/glentity.py +15 -3
- gam/gamlib/glglobals.py +6 -0
- gam/gamlib/glmsgs.py +5 -2
- gam/gamlib/glskus.py +1 -1
- gam/gamlib/glverlibs.py +1 -1
- gam/gamlib/yubikey.py +13 -12
- {gam7-7.19.3.dist-info → gam7-7.28.2.dist-info}/METADATA +10 -4
- {gam7-7.19.3.dist-info → gam7-7.28.2.dist-info}/RECORD +16 -34
- gam/googleapiclient/__init__.py +0 -27
- gam/googleapiclient/_auth.py +0 -167
- gam/googleapiclient/_helpers.py +0 -207
- gam/googleapiclient/channel.py +0 -315
- gam/googleapiclient/discovery.py +0 -1662
- gam/googleapiclient/discovery_cache/__init__.py +0 -78
- gam/googleapiclient/discovery_cache/appengine_memcache.py +0 -55
- gam/googleapiclient/discovery_cache/base.py +0 -46
- gam/googleapiclient/discovery_cache/file_cache.py +0 -145
- gam/googleapiclient/errors.py +0 -197
- gam/googleapiclient/http.py +0 -1962
- gam/googleapiclient/mimeparse.py +0 -183
- gam/googleapiclient/model.py +0 -429
- gam/googleapiclient/schema.py +0 -317
- gam/googleapiclient/version.py +0 -15
- gam/iso8601/__init__.py +0 -28
- gam/iso8601/iso8601.py +0 -160
- gam/six.py +0 -982
- {gam7-7.19.3.dist-info → gam7-7.28.2.dist-info}/WHEEL +0 -0
- {gam7-7.19.3.dist-info → gam7-7.28.2.dist-info}/entry_points.txt +0 -0
- {gam7-7.19.3.dist-info → gam7-7.28.2.dist-info}/licenses/LICENSE +0 -0
gam/gamlib/yubikey.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
|
-
# Copyright (C)
|
|
3
|
+
# Copyright (C) 2025 Ross Scroggs All Rights Reserved.
|
|
4
4
|
#
|
|
5
5
|
# All Rights Reserved.
|
|
6
6
|
#
|
|
@@ -19,11 +19,20 @@
|
|
|
19
19
|
"""YubiKey"""
|
|
20
20
|
|
|
21
21
|
import base64
|
|
22
|
-
import datetime
|
|
23
22
|
from secrets import SystemRandom
|
|
24
23
|
import string
|
|
25
24
|
import sys
|
|
26
25
|
|
|
26
|
+
import arrow
|
|
27
|
+
|
|
28
|
+
from gam import mplock
|
|
29
|
+
|
|
30
|
+
from gam import systemErrorExit
|
|
31
|
+
from gam import readStdin
|
|
32
|
+
from gam import writeStdout
|
|
33
|
+
|
|
34
|
+
from gam.gamlib import glmsgs as Msg
|
|
35
|
+
|
|
27
36
|
from cryptography.hazmat.primitives import hashes, serialization
|
|
28
37
|
from cryptography.hazmat.primitives.asymmetric import padding
|
|
29
38
|
from smartcard.Exceptions import CardConnectionException
|
|
@@ -49,14 +58,6 @@ YUBIKEY_VALUE_ERROR_RC = 85
|
|
|
49
58
|
YUBIKEY_MULTIPLE_CONNECTED_RC = 86
|
|
50
59
|
YUBIKEY_NOT_FOUND_RC = 87
|
|
51
60
|
|
|
52
|
-
from gam import mplock
|
|
53
|
-
|
|
54
|
-
from gam import systemErrorExit
|
|
55
|
-
from gam import readStdin
|
|
56
|
-
from gam import writeStdout
|
|
57
|
-
|
|
58
|
-
from gam.gamlib import glmsgs as Msg
|
|
59
|
-
|
|
60
61
|
PIN_PUK_CHARS = string.ascii_letters+string.digits+string.punctuation
|
|
61
62
|
|
|
62
63
|
class YubiKey():
|
|
@@ -155,8 +156,8 @@ class YubiKey():
|
|
|
155
156
|
KEY_TYPE.RSA2048,
|
|
156
157
|
PIN_POLICY.ALWAYS,
|
|
157
158
|
TOUCH_POLICY.NEVER)
|
|
158
|
-
now =
|
|
159
|
-
valid_to = now
|
|
159
|
+
now = arrow.utcnow()
|
|
160
|
+
valid_to = now.shift(days=36500)
|
|
160
161
|
subject = 'CN=GAM Created Key'
|
|
161
162
|
piv.authenticate(MANAGEMENT_KEY_TYPE.TDES, DEFAULT_MANAGEMENT_KEY)
|
|
162
163
|
piv.verify_pin(new_pin)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gam7
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.28.2
|
|
4
4
|
Summary: CLI tool to manage Google Workspace
|
|
5
5
|
Project-URL: Homepage, https://github.com/GAM-team/GAM
|
|
6
6
|
Project-URL: Issues, https://github.com/GAM-team/GAM/issues
|
|
@@ -17,7 +17,8 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
-
Requires-Python: >=3.
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: arrow>=1.3.0
|
|
21
22
|
Requires-Dist: chardet>=5.2.0
|
|
22
23
|
Requires-Dist: cryptography>=44.0.2
|
|
23
24
|
Requires-Dist: distro; sys_platform == 'linux'
|
|
@@ -26,11 +27,11 @@ Requires-Dist: google-api-python-client>=2.167.0
|
|
|
26
27
|
Requires-Dist: google-auth-httplib2>=0.2.0
|
|
27
28
|
Requires-Dist: google-auth-oauthlib>=1.2.2
|
|
28
29
|
Requires-Dist: google-auth>=2.39.0
|
|
29
|
-
Requires-Dist: httplib2>=0.
|
|
30
|
+
Requires-Dist: httplib2>=0.31.0
|
|
30
31
|
Requires-Dist: lxml>=5.4.0
|
|
31
32
|
Requires-Dist: passlib>=1.7.4
|
|
32
33
|
Requires-Dist: pathvalidate>=3.2.3
|
|
33
|
-
Requires-Dist:
|
|
34
|
+
Requires-Dist: pysocks>=1.7.1
|
|
34
35
|
Provides-Extra: yubikey
|
|
35
36
|
Requires-Dist: yubikey-manager>=5.6.1; extra == 'yubikey'
|
|
36
37
|
Description-Content-Type: text/markdown
|
|
@@ -55,6 +56,11 @@ this will download GAM, install it and start setup.
|
|
|
55
56
|
|
|
56
57
|
Download the MSI Installer from the [GitHub Releases] page. Install the MSI and you'll be prompted to setup GAM.
|
|
57
58
|
|
|
59
|
+
## Use your own Python
|
|
60
|
+
If you'd prefer to install GAM as a Python package you can install with pip:
|
|
61
|
+
```
|
|
62
|
+
pip install gam7
|
|
63
|
+
```
|
|
58
64
|
# Documentation
|
|
59
65
|
|
|
60
66
|
The GAM documentation is hosted in the [GitHub Wiki]
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
gam/__init__.py,sha256=
|
|
2
|
-
gam/__main__.py,sha256=
|
|
1
|
+
gam/__init__.py,sha256=7JwnsTQbnk6etqIKl33q4PI2MASrBI1AWoptjyvOU8E,3634088
|
|
2
|
+
gam/__main__.py,sha256=VwEYS7a9vYQPbT6iLduMOoVUJ6p4R-HZgerZQmM1NpE,1307
|
|
3
3
|
gam/cacerts.pem,sha256=DUsVo2XlFYwfkhe3gnxa-Km4Z4noz74hSApXwTT-nQE,44344
|
|
4
4
|
gam/cbcm-v1.1beta1.json,sha256=xO5XloCQQULmPbFBx5bckdqmbLFQ7sJ2TImhE1ysDIY,19439
|
|
5
5
|
gam/contactdelegation-v1.json,sha256=kCWykxSH7jLHglb-nWE_RFauxRXa_1eisWXXAqHu6Ws,7663
|
|
6
6
|
gam/datastudio-v1.json,sha256=L_mQBN8oYmfWODGvgrwTF9d2i8rEZJmzqIxw_Yr83lI,15673
|
|
7
7
|
gam/meet-v2beta.json,sha256=VUx2P9QMmmNK5tU4W9SYzmVhQzMoshP4VbvHSPoNdTI,50511
|
|
8
8
|
gam/serviceaccountlookup-v1.json,sha256=NDe8BbviJo8_vlEOA7iobT5GSaBg6h-DjnkPwiAYlvs,4121
|
|
9
|
-
gam/six.py,sha256=U4Z_yv534W5CNyjY9i8V1OXY2SjAny8y2L5vDLhhThM,34159
|
|
10
9
|
gam/atom/__init__.py,sha256=pnPkqTZcRh1LniagGNTBDO9COzniXUgRQv_DNE3KPBw,52245
|
|
11
10
|
gam/atom/auth.py,sha256=hvzDptCCSNRi1hUK89hFmYn5rct5glWJ6Ffzm0p43bw,1200
|
|
12
11
|
gam/atom/client.py,sha256=6AQbkndS2BSpw_17cd8C2y2zSBa4hXwZpOTKYZDFEjY,8086
|
|
@@ -23,19 +22,19 @@ gam/atom/token_store.py,sha256=7E6Ecvxa86WCvl1pJAhv78jg9OxQv8pMtIUcPhZCq04,3803
|
|
|
23
22
|
gam/atom/url.py,sha256=pxO1TlORxyKQTQ1bkBE1unFzjnv9c8LjJkm-UEORShY,4276
|
|
24
23
|
gam/gamlib/__init__.py,sha256=z5mF-y0j8pm-YNFBaiuxB4M_GAUPG-cXWwrhYwrVReM,679
|
|
25
24
|
gam/gamlib/glaction.py,sha256=1Il_HrChVnPkzZwiZs5au4mFQVtq4K1Z42uIuR6qdnI,9419
|
|
26
|
-
gam/gamlib/glapi.py,sha256=
|
|
27
|
-
gam/gamlib/glcfg.py,sha256=
|
|
28
|
-
gam/gamlib/glclargs.py,sha256=
|
|
29
|
-
gam/gamlib/glentity.py,sha256=
|
|
25
|
+
gam/gamlib/glapi.py,sha256=xV-Wj3Zz_LPV7PqufP3gdVyAuLDCUU4UbXGqefrOy04,36627
|
|
26
|
+
gam/gamlib/glcfg.py,sha256=do_FR9m9m7Bmh2SgjLPk2pysU-wPHt7PlqPPjX90tpw,28684
|
|
27
|
+
gam/gamlib/glclargs.py,sha256=C_mMXmW8rDfG6W5pn7kYR1vqluulkIm6i04npVPO0UU,53355
|
|
28
|
+
gam/gamlib/glentity.py,sha256=bIpmwanOU8XxsNCZsD42r2o2sMOkIPHmIJNCQ04140Q,35478
|
|
30
29
|
gam/gamlib/glgapi.py,sha256=pdBbwNtnCwFWxJGaP-_3hdTjSNoOCJF2yo76WdQOi1k,40426
|
|
31
30
|
gam/gamlib/glgdata.py,sha256=weRppttWm6uRyqtBoGPKoHiNZ2h28nhfUV4J_mbCszY,2707
|
|
32
|
-
gam/gamlib/glglobals.py,sha256=
|
|
31
|
+
gam/gamlib/glglobals.py,sha256=U1eCXHOkWAtwVXG8-0HL4ZzQP0YcbeFlhJxbOa_x1QI,9804
|
|
33
32
|
gam/gamlib/glindent.py,sha256=RfBa2LDfLIqPLL5vMfC689TCVmqn8xf-qulSzkiatrc,1228
|
|
34
|
-
gam/gamlib/glmsgs.py,sha256=
|
|
35
|
-
gam/gamlib/glskus.py,sha256=
|
|
33
|
+
gam/gamlib/glmsgs.py,sha256=TkLg6p8o9elVIbe_4Kq0hac6qSM-qoIMuQvXoEn4Q_c,34254
|
|
34
|
+
gam/gamlib/glskus.py,sha256=29vlBLBJCL4u9GawCt3eNeZq9HQG3hGFZk9-EainNng,15384
|
|
36
35
|
gam/gamlib/gluprop.py,sha256=IyPLCyvn7-NHTUenM71YPQPXRZXx6CB5q-GtJ-FYd1c,11461
|
|
37
|
-
gam/gamlib/glverlibs.py,sha256=
|
|
38
|
-
gam/gamlib/yubikey.py,sha256
|
|
36
|
+
gam/gamlib/glverlibs.py,sha256=mDphdXVN_dJlGUVGuLqOEA3yHBNBeqRV4DRoTX7Wl7A,982
|
|
37
|
+
gam/gamlib/yubikey.py,sha256=UNPaeSx0y6_v1RnShO1e9ZyVSHni6sGvOgcEwktlCeE,7896
|
|
39
38
|
gam/gdata/__init__.py,sha256=uvjmSza2EdL7lGaoJ04-uXHGeYa0i1dBQHIetBybcUQ,29637
|
|
40
39
|
gam/gdata/service.py,sha256=CuImJDRVcNMM1dfo7V6T0LrztzqTNrIraaLkHXpL0Tw,70045
|
|
41
40
|
gam/gdata/urlfetch.py,sha256=yAglxcDC3sCp763P9dXOVwKMjrHvwKRoVSMv_G3Rt8E,9308
|
|
@@ -48,25 +47,8 @@ gam/gdata/apps/audit/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrK
|
|
|
48
47
|
gam/gdata/apps/audit/service.py,sha256=Z1eueThcNeVUMWP1DRWc_DGHrJCiJI8W_xj6L-cqu-Q,9658
|
|
49
48
|
gam/gdata/apps/contacts/__init__.py,sha256=Um6zgIkiahZns7yAEuC3pxHSMD8iciZ_EoynSLoYPfU,30463
|
|
50
49
|
gam/gdata/apps/contacts/service.py,sha256=5lNb-Ii1Gyek6ePFji3kyoYtCBc8CuJTwagx2BL2o14,15684
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
gam/googleapiclient/errors.py,sha256=9h3uimcMcczBHZJFWAX_YDABzJeJugWB0jmj11rp-LI,5460
|
|
57
|
-
gam/googleapiclient/http.py,sha256=ITE51oqDBqN1-AA5D-Tnlj3egGc_5O0V5xSzBw3UTKI,68241
|
|
58
|
-
gam/googleapiclient/mimeparse.py,sha256=wwouQMCjppTocJtiQhkkTa27kocYwlFRALL2z11Xo1Y,6530
|
|
59
|
-
gam/googleapiclient/model.py,sha256=NQDO1GhOGNVCJlSSCLOecdA11yf8RDXfSLFxYb3R7EE,14085
|
|
60
|
-
gam/googleapiclient/schema.py,sha256=rR3u8WPQ_V8a7GCUsNuvtf6GxzwuMO0HaqsTBp3tnyM,10414
|
|
61
|
-
gam/googleapiclient/version.py,sha256=vY1VaLgft_SIONbdSFPvHzipKt1agXbOX-Z-ZFbo9s0,599
|
|
62
|
-
gam/googleapiclient/discovery_cache/__init__.py,sha256=ww_vl0vhVLuHSEdRTv3-gq6EDG--Ff7rILYHHFifnzc,2315
|
|
63
|
-
gam/googleapiclient/discovery_cache/appengine_memcache.py,sha256=6T1pQj-toAhDwfgLuiggFGhxKNGw5y-NnLUzLIF_M4s,1657
|
|
64
|
-
gam/googleapiclient/discovery_cache/base.py,sha256=yCDPtxnbNN-p5_9fzBacC6P3wcUPlaCQIy5v_dXTons,1389
|
|
65
|
-
gam/googleapiclient/discovery_cache/file_cache.py,sha256=sim3Mg4HgRYo3vX75jvcKy_aV568EvIrtBfvfbw-044,4774
|
|
66
|
-
gam/iso8601/__init__.py,sha256=Z2PsYbXgAH5a5xzUvgczCboPzqWpm65kRcIngCnhViU,1218
|
|
67
|
-
gam/iso8601/iso8601.py,sha256=Li2FHZ4sBTWuthuQhyCvmvj0j6At8JbGzkSv2fc2RHU,4384
|
|
68
|
-
gam7-7.19.3.dist-info/METADATA,sha256=TeUdQCPXnItSyqXwJbMSkU3f43C-2H1wKRyc2AF7YPM,2940
|
|
69
|
-
gam7-7.19.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
70
|
-
gam7-7.19.3.dist-info/entry_points.txt,sha256=HVUM5J7dA8YwvJfG30jiLefR19ExMs387TWugWd9sf4,42
|
|
71
|
-
gam7-7.19.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
72
|
-
gam7-7.19.3.dist-info/RECORD,,
|
|
50
|
+
gam7-7.28.2.dist-info/METADATA,sha256=9T2F_6RdE6qx4VtSEF83_Dqh399tSvoKDARS-AEXXQ4,3093
|
|
51
|
+
gam7-7.28.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
52
|
+
gam7-7.28.2.dist-info/entry_points.txt,sha256=HVUM5J7dA8YwvJfG30jiLefR19ExMs387TWugWd9sf4,42
|
|
53
|
+
gam7-7.28.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
54
|
+
gam7-7.28.2.dist-info/RECORD,,
|
gam/googleapiclient/__init__.py
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Copyright 2014 Google Inc. All Rights Reserved.
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
# Set default logging handler to avoid "No handler found" warnings.
|
|
16
|
-
import logging
|
|
17
|
-
|
|
18
|
-
try: # Python 2.7+
|
|
19
|
-
from logging import NullHandler
|
|
20
|
-
except ImportError:
|
|
21
|
-
|
|
22
|
-
class NullHandler(logging.Handler):
|
|
23
|
-
def emit(self, record):
|
|
24
|
-
pass
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
logging.getLogger(__name__).addHandler(NullHandler())
|
gam/googleapiclient/_auth.py
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# Copyright 2016 Google Inc. All Rights Reserved.
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
"""Helpers for authentication using oauth2client or google-auth."""
|
|
16
|
-
|
|
17
|
-
import httplib2
|
|
18
|
-
|
|
19
|
-
try:
|
|
20
|
-
import google.auth
|
|
21
|
-
import google.auth.credentials
|
|
22
|
-
|
|
23
|
-
HAS_GOOGLE_AUTH = True
|
|
24
|
-
except ImportError: # pragma: NO COVER
|
|
25
|
-
HAS_GOOGLE_AUTH = False
|
|
26
|
-
|
|
27
|
-
try:
|
|
28
|
-
import google_auth_httplib2
|
|
29
|
-
except ImportError: # pragma: NO COVER
|
|
30
|
-
google_auth_httplib2 = None
|
|
31
|
-
|
|
32
|
-
try:
|
|
33
|
-
import oauth2client
|
|
34
|
-
import oauth2client.client
|
|
35
|
-
|
|
36
|
-
HAS_OAUTH2CLIENT = True
|
|
37
|
-
except ImportError: # pragma: NO COVER
|
|
38
|
-
HAS_OAUTH2CLIENT = False
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def credentials_from_file(filename, scopes=None, quota_project_id=None):
|
|
42
|
-
"""Returns credentials loaded from a file."""
|
|
43
|
-
if HAS_GOOGLE_AUTH:
|
|
44
|
-
credentials, _ = google.auth.load_credentials_from_file(
|
|
45
|
-
filename, scopes=scopes, quota_project_id=quota_project_id
|
|
46
|
-
)
|
|
47
|
-
return credentials
|
|
48
|
-
else:
|
|
49
|
-
raise EnvironmentError(
|
|
50
|
-
"client_options.credentials_file is only supported in google-auth."
|
|
51
|
-
)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def default_credentials(scopes=None, quota_project_id=None):
|
|
55
|
-
"""Returns Application Default Credentials."""
|
|
56
|
-
if HAS_GOOGLE_AUTH:
|
|
57
|
-
credentials, _ = google.auth.default(
|
|
58
|
-
scopes=scopes, quota_project_id=quota_project_id
|
|
59
|
-
)
|
|
60
|
-
return credentials
|
|
61
|
-
elif HAS_OAUTH2CLIENT:
|
|
62
|
-
if scopes is not None or quota_project_id is not None:
|
|
63
|
-
raise EnvironmentError(
|
|
64
|
-
"client_options.scopes and client_options.quota_project_id are not supported in oauth2client."
|
|
65
|
-
"Please install google-auth."
|
|
66
|
-
)
|
|
67
|
-
return oauth2client.client.GoogleCredentials.get_application_default()
|
|
68
|
-
else:
|
|
69
|
-
raise EnvironmentError(
|
|
70
|
-
"No authentication library is available. Please install either "
|
|
71
|
-
"google-auth or oauth2client."
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def with_scopes(credentials, scopes):
|
|
76
|
-
"""Scopes the credentials if necessary.
|
|
77
|
-
|
|
78
|
-
Args:
|
|
79
|
-
credentials (Union[
|
|
80
|
-
google.auth.credentials.Credentials,
|
|
81
|
-
oauth2client.client.Credentials]): The credentials to scope.
|
|
82
|
-
scopes (Sequence[str]): The list of scopes.
|
|
83
|
-
|
|
84
|
-
Returns:
|
|
85
|
-
Union[google.auth.credentials.Credentials,
|
|
86
|
-
oauth2client.client.Credentials]: The scoped credentials.
|
|
87
|
-
"""
|
|
88
|
-
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
|
|
89
|
-
return google.auth.credentials.with_scopes_if_required(credentials, scopes)
|
|
90
|
-
else:
|
|
91
|
-
try:
|
|
92
|
-
if credentials.create_scoped_required():
|
|
93
|
-
return credentials.create_scoped(scopes)
|
|
94
|
-
else:
|
|
95
|
-
return credentials
|
|
96
|
-
except AttributeError:
|
|
97
|
-
return credentials
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
def authorized_http(credentials):
|
|
101
|
-
"""Returns an http client that is authorized with the given credentials.
|
|
102
|
-
|
|
103
|
-
Args:
|
|
104
|
-
credentials (Union[
|
|
105
|
-
google.auth.credentials.Credentials,
|
|
106
|
-
oauth2client.client.Credentials]): The credentials to use.
|
|
107
|
-
|
|
108
|
-
Returns:
|
|
109
|
-
Union[httplib2.Http, google_auth_httplib2.AuthorizedHttp]: An
|
|
110
|
-
authorized http client.
|
|
111
|
-
"""
|
|
112
|
-
from googleapiclient.http import build_http
|
|
113
|
-
|
|
114
|
-
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
|
|
115
|
-
if google_auth_httplib2 is None:
|
|
116
|
-
raise ValueError(
|
|
117
|
-
"Credentials from google.auth specified, but "
|
|
118
|
-
"google-api-python-client is unable to use these credentials "
|
|
119
|
-
"unless google-auth-httplib2 is installed. Please install "
|
|
120
|
-
"google-auth-httplib2."
|
|
121
|
-
)
|
|
122
|
-
return google_auth_httplib2.AuthorizedHttp(credentials, http=build_http())
|
|
123
|
-
else:
|
|
124
|
-
return credentials.authorize(build_http())
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
def refresh_credentials(credentials):
|
|
128
|
-
# Refresh must use a new http instance, as the one associated with the
|
|
129
|
-
# credentials could be a AuthorizedHttp or an oauth2client-decorated
|
|
130
|
-
# Http instance which would cause a weird recursive loop of refreshing
|
|
131
|
-
# and likely tear a hole in spacetime.
|
|
132
|
-
refresh_http = httplib2.Http()
|
|
133
|
-
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
|
|
134
|
-
request = google_auth_httplib2.Request(refresh_http)
|
|
135
|
-
return credentials.refresh(request)
|
|
136
|
-
else:
|
|
137
|
-
return credentials.refresh(refresh_http)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def apply_credentials(credentials, headers):
|
|
141
|
-
# oauth2client and google-auth have the same interface for this.
|
|
142
|
-
if not is_valid(credentials):
|
|
143
|
-
refresh_credentials(credentials)
|
|
144
|
-
return credentials.apply(headers)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def is_valid(credentials):
|
|
148
|
-
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
|
|
149
|
-
return credentials.valid
|
|
150
|
-
else:
|
|
151
|
-
return (
|
|
152
|
-
credentials.access_token is not None
|
|
153
|
-
and not credentials.access_token_expired
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def get_credentials_from_http(http):
|
|
158
|
-
if http is None:
|
|
159
|
-
return None
|
|
160
|
-
elif hasattr(http.request, "credentials"):
|
|
161
|
-
return http.request.credentials
|
|
162
|
-
elif hasattr(http, "credentials") and not isinstance(
|
|
163
|
-
http.credentials, httplib2.Credentials
|
|
164
|
-
):
|
|
165
|
-
return http.credentials
|
|
166
|
-
else:
|
|
167
|
-
return None
|
gam/googleapiclient/_helpers.py
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
# Copyright 2015 Google Inc. All rights reserved.
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
|
|
15
|
-
"""Helper functions for commonly used utilities."""
|
|
16
|
-
|
|
17
|
-
import functools
|
|
18
|
-
import inspect
|
|
19
|
-
import logging
|
|
20
|
-
import urllib
|
|
21
|
-
|
|
22
|
-
logger = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
POSITIONAL_WARNING = "WARNING"
|
|
25
|
-
POSITIONAL_EXCEPTION = "EXCEPTION"
|
|
26
|
-
POSITIONAL_IGNORE = "IGNORE"
|
|
27
|
-
POSITIONAL_SET = frozenset(
|
|
28
|
-
[POSITIONAL_WARNING, POSITIONAL_EXCEPTION, POSITIONAL_IGNORE]
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
positional_parameters_enforcement = POSITIONAL_WARNING
|
|
32
|
-
|
|
33
|
-
_SYM_LINK_MESSAGE = "File: {0}: Is a symbolic link."
|
|
34
|
-
_IS_DIR_MESSAGE = "{0}: Is a directory"
|
|
35
|
-
_MISSING_FILE_MESSAGE = "Cannot access {0}: No such file or directory"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def positional(max_positional_args):
|
|
39
|
-
"""A decorator to declare that only the first N arguments may be positional.
|
|
40
|
-
|
|
41
|
-
This decorator makes it easy to support Python 3 style keyword-only
|
|
42
|
-
parameters. For example, in Python 3 it is possible to write::
|
|
43
|
-
|
|
44
|
-
def fn(pos1, *, kwonly1=None, kwonly2=None):
|
|
45
|
-
...
|
|
46
|
-
|
|
47
|
-
All named parameters after ``*`` must be a keyword::
|
|
48
|
-
|
|
49
|
-
fn(10, 'kw1', 'kw2') # Raises exception.
|
|
50
|
-
fn(10, kwonly1='kw1') # Ok.
|
|
51
|
-
|
|
52
|
-
Example
|
|
53
|
-
^^^^^^^
|
|
54
|
-
|
|
55
|
-
To define a function like above, do::
|
|
56
|
-
|
|
57
|
-
@positional(1)
|
|
58
|
-
def fn(pos1, kwonly1=None, kwonly2=None):
|
|
59
|
-
...
|
|
60
|
-
|
|
61
|
-
If no default value is provided to a keyword argument, it becomes a
|
|
62
|
-
required keyword argument::
|
|
63
|
-
|
|
64
|
-
@positional(0)
|
|
65
|
-
def fn(required_kw):
|
|
66
|
-
...
|
|
67
|
-
|
|
68
|
-
This must be called with the keyword parameter::
|
|
69
|
-
|
|
70
|
-
fn() # Raises exception.
|
|
71
|
-
fn(10) # Raises exception.
|
|
72
|
-
fn(required_kw=10) # Ok.
|
|
73
|
-
|
|
74
|
-
When defining instance or class methods always remember to account for
|
|
75
|
-
``self`` and ``cls``::
|
|
76
|
-
|
|
77
|
-
class MyClass(object):
|
|
78
|
-
|
|
79
|
-
@positional(2)
|
|
80
|
-
def my_method(self, pos1, kwonly1=None):
|
|
81
|
-
...
|
|
82
|
-
|
|
83
|
-
@classmethod
|
|
84
|
-
@positional(2)
|
|
85
|
-
def my_method(cls, pos1, kwonly1=None):
|
|
86
|
-
...
|
|
87
|
-
|
|
88
|
-
The positional decorator behavior is controlled by
|
|
89
|
-
``_helpers.positional_parameters_enforcement``, which may be set to
|
|
90
|
-
``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or
|
|
91
|
-
``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do
|
|
92
|
-
nothing, respectively, if a declaration is violated.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
max_positional_arguments: Maximum number of positional arguments. All
|
|
96
|
-
parameters after this index must be
|
|
97
|
-
keyword only.
|
|
98
|
-
|
|
99
|
-
Returns:
|
|
100
|
-
A decorator that prevents using arguments after max_positional_args
|
|
101
|
-
from being used as positional parameters.
|
|
102
|
-
|
|
103
|
-
Raises:
|
|
104
|
-
TypeError: if a keyword-only argument is provided as a positional
|
|
105
|
-
parameter, but only if
|
|
106
|
-
_helpers.positional_parameters_enforcement is set to
|
|
107
|
-
POSITIONAL_EXCEPTION.
|
|
108
|
-
"""
|
|
109
|
-
|
|
110
|
-
def positional_decorator(wrapped):
|
|
111
|
-
@functools.wraps(wrapped)
|
|
112
|
-
def positional_wrapper(*args, **kwargs):
|
|
113
|
-
if len(args) > max_positional_args:
|
|
114
|
-
plural_s = ""
|
|
115
|
-
if max_positional_args != 1:
|
|
116
|
-
plural_s = "s"
|
|
117
|
-
message = (
|
|
118
|
-
"{function}() takes at most {args_max} positional "
|
|
119
|
-
"argument{plural} ({args_given} given)".format(
|
|
120
|
-
function=wrapped.__name__,
|
|
121
|
-
args_max=max_positional_args,
|
|
122
|
-
args_given=len(args),
|
|
123
|
-
plural=plural_s,
|
|
124
|
-
)
|
|
125
|
-
)
|
|
126
|
-
if positional_parameters_enforcement == POSITIONAL_EXCEPTION:
|
|
127
|
-
raise TypeError(message)
|
|
128
|
-
elif positional_parameters_enforcement == POSITIONAL_WARNING:
|
|
129
|
-
logger.warning(message)
|
|
130
|
-
return wrapped(*args, **kwargs)
|
|
131
|
-
|
|
132
|
-
return positional_wrapper
|
|
133
|
-
|
|
134
|
-
if isinstance(max_positional_args, int):
|
|
135
|
-
return positional_decorator
|
|
136
|
-
else:
|
|
137
|
-
args, _, _, defaults, _, _, _ = inspect.getfullargspec(max_positional_args)
|
|
138
|
-
return positional(len(args) - len(defaults))(max_positional_args)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def parse_unique_urlencoded(content):
|
|
142
|
-
"""Parses unique key-value parameters from urlencoded content.
|
|
143
|
-
|
|
144
|
-
Args:
|
|
145
|
-
content: string, URL-encoded key-value pairs.
|
|
146
|
-
|
|
147
|
-
Returns:
|
|
148
|
-
dict, The key-value pairs from ``content``.
|
|
149
|
-
|
|
150
|
-
Raises:
|
|
151
|
-
ValueError: if one of the keys is repeated.
|
|
152
|
-
"""
|
|
153
|
-
urlencoded_params = urllib.parse.parse_qs(content)
|
|
154
|
-
params = {}
|
|
155
|
-
for key, value in urlencoded_params.items():
|
|
156
|
-
if len(value) != 1:
|
|
157
|
-
msg = "URL-encoded content contains a repeated value:" "%s -> %s" % (
|
|
158
|
-
key,
|
|
159
|
-
", ".join(value),
|
|
160
|
-
)
|
|
161
|
-
raise ValueError(msg)
|
|
162
|
-
params[key] = value[0]
|
|
163
|
-
return params
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def update_query_params(uri, params):
|
|
167
|
-
"""Updates a URI with new query parameters.
|
|
168
|
-
|
|
169
|
-
If a given key from ``params`` is repeated in the ``uri``, then
|
|
170
|
-
the URI will be considered invalid and an error will occur.
|
|
171
|
-
|
|
172
|
-
If the URI is valid, then each value from ``params`` will
|
|
173
|
-
replace the corresponding value in the query parameters (if
|
|
174
|
-
it exists).
|
|
175
|
-
|
|
176
|
-
Args:
|
|
177
|
-
uri: string, A valid URI, with potential existing query parameters.
|
|
178
|
-
params: dict, A dictionary of query parameters.
|
|
179
|
-
|
|
180
|
-
Returns:
|
|
181
|
-
The same URI but with the new query parameters added.
|
|
182
|
-
"""
|
|
183
|
-
parts = urllib.parse.urlparse(uri)
|
|
184
|
-
query_params = parse_unique_urlencoded(parts.query)
|
|
185
|
-
query_params.update(params)
|
|
186
|
-
new_query = urllib.parse.urlencode(query_params)
|
|
187
|
-
new_parts = parts._replace(query=new_query)
|
|
188
|
-
return urllib.parse.urlunparse(new_parts)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
def _add_query_parameter(url, name, value):
|
|
192
|
-
"""Adds a query parameter to a url.
|
|
193
|
-
|
|
194
|
-
Replaces the current value if it already exists in the URL.
|
|
195
|
-
|
|
196
|
-
Args:
|
|
197
|
-
url: string, url to add the query parameter to.
|
|
198
|
-
name: string, query parameter name.
|
|
199
|
-
value: string, query parameter value.
|
|
200
|
-
|
|
201
|
-
Returns:
|
|
202
|
-
Updated query parameter. Does not update the url if value is None.
|
|
203
|
-
"""
|
|
204
|
-
if value is None:
|
|
205
|
-
return url
|
|
206
|
-
else:
|
|
207
|
-
return update_query_params(url, {name: value})
|