Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit41e4b00

Browse files
authored
Stop providing a hardcoded CA bundle (dropbox#489)
The SDK no longer provides a CA bundle to verify SSL connections. This also allows us to remove the runtime dependency on `pkg_resources` and thus `setuptools`. The `ca_certs` parameter is still supported, so users can pin with their own CA bundle if they so choose. Otherwise, the default verification mechanism in the `requests` library now applies (this uses `certifi` and/or system certificates, depending on the configuration).Improves integration tests to cover both scenarios (i.e. when a bundle is provided, and when one isn't).
1 parent75596da commit41e4b00

File tree

7 files changed

+413
-1456
lines changed

7 files changed

+413
-1456
lines changed

‎dropbox/dropbox_client.py‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ def __init__(self,
181181
Not required if PKCE was used to authorize the token
182182
:param list scope: list of scopes to request on refresh. If left blank,
183183
refresh will request all available scopes for application
184-
:param str ca_certs: path toCA certificate. If left blank, default certificate location\
185-
will be used
184+
:param str ca_certs:apath toa file of concatenated CA certificates in PEM format.
185+
Has the same meaning as when using :func:`ssl.wrap_socket`.
186186
"""
187187

188188
ifnot (oauth2_access_tokenoroauth2_refresh_tokenor (app_keyandapp_secret)):
@@ -590,7 +590,6 @@ def request_json_string(self,
590590
headers=headers,
591591
data=body,
592592
stream=stream,
593-
verify=True,
594593
timeout=timeout,
595594
)
596595
self.raise_dropbox_error_for_resp(r)

‎dropbox/session.py‎

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
importpkg_resources
21
importos
32
importssl
43

@@ -32,21 +31,14 @@
3231
# This is the default longest time we'll block on receiving data from the server
3332
DEFAULT_TIMEOUT=100
3433

35-
try:
36-
_TRUSTED_CERT_FILE=pkg_resources.resource_filename(__name__,'trusted-certs.crt')
37-
exceptNotImplementedError:# Package is used inside python archive
38-
_TRUSTED_CERT_FILE=None
39-
4034

4135
# TODO(kelkabany): We probably only want to instantiate this once so that even
4236
# if multiple Dropbox objects are instantiated, they all share the same pool.
4337
class_SSLAdapter(HTTPAdapter):
4438
_ca_certs=None
4539

4640
def__init__(self,*args,**kwargs):
47-
self._ca_certs=kwargs.pop("ca_certs",None)or_TRUSTED_CERT_FILE
48-
ifnotself._ca_certs:
49-
raiseAttributeError("CA certificate not set")
41+
self._ca_certs=kwargs.pop("ca_certs",None)
5042
super(_SSLAdapter,self).__init__(*args,**kwargs)
5143

5244
definit_poolmanager(self,connections,maxsize,block=False,**_):
@@ -59,8 +51,19 @@ def init_poolmanager(self, connections, maxsize, block=False, **_):
5951
)
6052

6153
defpinned_session(pool_maxsize=8,ca_certs=None):
62-
http_adapter=_SSLAdapter(pool_connections=4,pool_maxsize=pool_maxsize,ca_certs=ca_certs)
54+
# always verify, use cert bundle if provided
55+
6356
_session=requests.session()
64-
_session.mount('https://',http_adapter)
6557

58+
# requests
59+
ifca_certsisnotNone:
60+
_session.verify=ca_certs
61+
else:
62+
_session.verify=True
63+
64+
# urllib3 within requests
65+
http_adapter=_SSLAdapter(pool_connections=4,pool_maxsize=pool_maxsize,ca_certs=ca_certs)
66+
_session.mount('https://',http_adapter)
6667
return_session
68+
69+
SSLError=requests.exceptions.SSLError# raised on verification errors

‎dropbox/trusted-certs.crt‎

Lines changed: 0 additions & 1396 deletions
This file was deleted.

‎setup.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
setup_requires=setup_requires,
5555
tests_require=test_reqs,
5656
packages=['dropbox'],
57-
package_data={'dropbox': ['trusted-certs.crt']},
5857
zip_safe=False,
5958
author_email='dev-platform@dropbox.com',
6059
author='Dropbox',

‎test/integration/expired-certs.crt‎

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# GeoTrust Global CA.pem
2+
# Certificate:
3+
# Data:
4+
# Version: 3 (0x2)
5+
# Serial Number: 144470 (0x23456)
6+
# Signature Algorithm: sha1WithRSAEncryption
7+
# Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
8+
# Validity
9+
# Not Before: May 21 04:00:00 2002 GMT
10+
# Not After : May 21 04:00:00 2022 GMT
11+
# Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA
12+
# Subject Public Key Info:
13+
# Public Key Algorithm: rsaEncryption
14+
# Public-Key: (2048 bit)
15+
# Modulus:
16+
# 00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df:
17+
# 3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8:
18+
# 43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29:
19+
# bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4:
20+
# 60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3:
21+
# ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92:
22+
# 2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d:
23+
# 80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14:
24+
# 15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd:
25+
# d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6:
26+
# d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5:
27+
# 5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39:
28+
# 19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05:
29+
# 9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2:
30+
# fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32:
31+
# eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07:
32+
# 36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b:
33+
# e4:f9
34+
# Exponent: 65537 (0x10001)
35+
# X509v3 extensions:
36+
# X509v3 Basic Constraints: critical
37+
# CA:TRUE
38+
# X509v3 Subject Key Identifier:
39+
# C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
40+
# X509v3 Authority Key Identifier:
41+
# keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E
42+
#
43+
# Signature Algorithm: sha1WithRSAEncryption
44+
# 35:e3:29:6a:e5:2f:5d:54:8e:29:50:94:9f:99:1a:14:e4:8f:
45+
# 78:2a:62:94:a2:27:67:9e:d0:cf:1a:5e:47:e9:c1:b2:a4:cf:
46+
# dd:41:1a:05:4e:9b:4b:ee:4a:6f:55:52:b3:24:a1:37:0a:eb:
47+
# 64:76:2a:2e:2c:f3:fd:3b:75:90:bf:fa:71:d8:c7:3d:37:d2:
48+
# b5:05:95:62:b9:a6:de:89:3d:36:7b:38:77:48:97:ac:a6:20:
49+
# 8f:2e:a6:c9:0c:c2:b2:99:45:00:c7:ce:11:51:22:22:e0:a5:
50+
# ea:b6:15:48:09:64:ea:5e:4f:74:f7:05:3e:c7:8a:52:0c:db:
51+
# 15:b4:bd:6d:9b:e5:c6:b1:54:68:a9:e3:69:90:b6:9a:a5:0f:
52+
# b8:b9:3f:20:7d:ae:4a:b5:b8:9c:e4:1d:b6:ab:e6:94:a5:c1:
53+
# c7:83:ad:db:f5:27:87:0e:04:6c:d5:ff:dd:a0:5d:ed:87:52:
54+
# b7:2b:15:02:ae:39:a6:6a:74:e9:da:c4:e7:bc:4d:34:1e:a9:
55+
# 5c:4d:33:5f:92:09:2f:88:66:5d:77:97:c7:1d:76:13:a9:d5:
56+
# e5:f1:16:09:11:35:d5:ac:db:24:71:70:2c:98:56:0b:d9:17:
57+
# b4:d1:e3:51:2b:5e:75:e8:d5:d0:dc:4f:34:ed:c2:05:66:80:
58+
# a1:cb:e6:33
59+
-----BEGIN CERTIFICATE-----
60+
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
61+
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
62+
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
63+
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
64+
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
65+
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
66+
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
67+
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
68+
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
69+
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
70+
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
71+
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
72+
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
73+
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
74+
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
75+
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
76+
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
77+
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
78+
-----END CERTIFICATE-----

‎test/integration/test_dropbox.py‎

Lines changed: 71 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
fromStringIOimportStringIOasBytesIO
1717

1818
fromdropboximport (
19+
create_session,
1920
Dropbox,
2021
DropboxOAuth2Flow,
2122
DropboxTeam,
@@ -39,6 +40,7 @@
3940
PathRoot,
4041
PathRoot_validator,
4142
)
43+
fromdropbox.sessionimportSSLError
4244

4345
# Key Types
4446
REFRESH_TOKEN_KEY="REFRESH_TOKEN"
@@ -65,34 +67,43 @@ def _value_from_env_or_die(env_name):
6567
sys.exit(1)
6668
returnvalue
6769

70+
_TRUSTED_CERTS_FILE=os.path.join(os.path.dirname(__file__),"trusted-certs.crt")
71+
_EXPIRED_CERTS_FILE=os.path.join(os.path.dirname(__file__),"expired-certs.crt")
72+
73+
# enables testing both with and without a manually-provided CA bundle
74+
@pytest.fixture(params=[None,_TRUSTED_CERTS_FILE],ids=["no-pinning","pinning"])
75+
defdbx_session(request):
76+
returncreate_session(ca_certs=request.param)
77+
6878

6979
@pytest.fixture()
70-
defdbx_from_env():
80+
defdbx_from_env(dbx_session):
7181
oauth2_token=_value_from_env_or_die(format_env_name())
72-
returnDropbox(oauth2_token)
82+
returnDropbox(oauth2_token,session=dbx_session)
7383

7484

7585
@pytest.fixture()
76-
defrefresh_dbx_from_env():
86+
defrefresh_dbx_from_env(dbx_session):
7787
refresh_token=_value_from_env_or_die(format_env_name(SCOPED_KEY,USER_KEY,REFRESH_TOKEN_KEY))
7888
app_key=_value_from_env_or_die(format_env_name(SCOPED_KEY,USER_KEY,CLIENT_ID_KEY))
7989
app_secret=_value_from_env_or_die(format_env_name(SCOPED_KEY,USER_KEY,CLIENT_SECRET_KEY))
8090
returnDropbox(oauth2_refresh_token=refresh_token,
81-
app_key=app_key,app_secret=app_secret)
91+
app_key=app_key,app_secret=app_secret,
92+
session=dbx_session)
8293

8394

8495
@pytest.fixture()
85-
defdbx_team_from_env():
96+
defdbx_team_from_env(dbx_session):
8697
team_oauth2_token=_value_from_env_or_die(
8798
format_env_name(SCOPED_KEY,TEAM_KEY,ACCESS_TOKEN_KEY))
88-
returnDropboxTeam(team_oauth2_token)
99+
returnDropboxTeam(team_oauth2_token,session=dbx_session)
89100

90101

91102
@pytest.fixture()
92-
defdbx_app_auth_from_env():
103+
defdbx_app_auth_from_env(dbx_session):
93104
app_key=_value_from_env_or_die(format_env_name(SCOPED_KEY,USER_KEY,CLIENT_ID_KEY))
94105
app_secret=_value_from_env_or_die(format_env_name(SCOPED_KEY,USER_KEY,CLIENT_SECRET_KEY))
95-
returnDropbox(app_key=app_key,app_secret=app_secret)
106+
returnDropbox(app_key=app_key,app_secret=app_secret,session=dbx_session)
96107

97108

98109
@pytest.fixture()
@@ -110,7 +121,7 @@ def dbx_share_url_from_env():
110121
TIMESTAMP=str(datetime.datetime.utcnow())
111122
STATIC_FILE="/test.txt"
112123

113-
@pytest.fixture(scope='module',autouse=True)
124+
@pytest.fixture(scope='module')
114125
defpytest_setup():
115126
print("Setup")
116127
dbx=Dropbox(_value_from_env_or_die(format_env_name()))
@@ -125,47 +136,14 @@ def pytest_setup():
125136
exceptException:
126137
print("File not found")
127138

128-
129139
@pytest.mark.usefixtures(
140+
"pytest_setup",
130141
"dbx_from_env",
131142
"refresh_dbx_from_env",
132143
"dbx_app_auth_from_env",
133-
"dbx_share_url_from_env"
144+
"dbx_share_url_from_env",
134145
)
135146
classTestDropbox:
136-
deftest_default_oauth2_urls(self):
137-
flow_obj=DropboxOAuth2Flow('dummy_app_key','dummy_app_secret',
138-
'http://localhost/dummy','dummy_session','dbx-auth-csrf-token')
139-
140-
assertre.match(
141-
r'^https://{}/oauth2/authorize\?'.format(re.escape(session.WEB_HOST)),
142-
flow_obj._get_authorize_url('http://localhost/redirect','state','legacy'),
143-
)
144-
145-
assertflow_obj.build_url(
146-
'/oauth2/authorize'
147-
)=='https://{}/oauth2/authorize'.format(session.API_HOST)
148-
149-
assertflow_obj.build_url(
150-
'/oauth2/authorize',host=session.WEB_HOST
151-
)=='https://{}/oauth2/authorize'.format(session.WEB_HOST)
152-
153-
deftest_bad_auth(self):
154-
# Test malformed token
155-
malformed_token_dbx=Dropbox(MALFORMED_TOKEN)
156-
# TODO: backend is no longer returning `BadInputError`
157-
# with pytest.raises(BadInputError,) as cm:
158-
# malformed_token_dbx.files_list_folder('')
159-
# assert 'token is malformed' in cm.value.message
160-
withpytest.raises(AuthError,):
161-
malformed_token_dbx.files_list_folder('')
162-
163-
# Test reasonable-looking invalid token
164-
invalid_token_dbx=Dropbox(INVALID_TOKEN)
165-
withpytest.raises(AuthError)ascm:
166-
invalid_token_dbx.files_list_folder('')
167-
assertcm.value.error.is_invalid_access_token()
168-
169147
deftest_multi_auth(self,dbx_from_env,dbx_app_auth_from_env,dbx_share_url_from_env):
170148
# Test for user (with oauth token)
171149
preview_result,resp=dbx_from_env.files_get_thumbnail_v2(
@@ -280,7 +258,10 @@ def test_versioned_route(self, dbx_from_env):
280258
# Verify response type is of v2 route
281259
assertisinstance(resp,DeleteResult)
282260

283-
@pytest.mark.usefixtures("dbx_team_from_env")
261+
@pytest.mark.usefixtures(
262+
"pytest_setup",
263+
"dbx_team_from_env",
264+
)
284265
classTestDropboxTeam:
285266
deftest_team(self,dbx_team_from_env):
286267
dbx_team_from_env.team_groups_list()
@@ -310,3 +291,48 @@ def test_clone_when_team_linked(self, dbx_team_from_env):
310291
new_dbxt=dbx_team_from_env.clone()
311292
assertdbx_team_from_envisnotnew_dbxt
312293
assertisinstance(new_dbxt,dbx_team_from_env.__class__)
294+
295+
deftest_default_oauth2_urls():
296+
flow_obj=DropboxOAuth2Flow('dummy_app_key','dummy_app_secret',
297+
'http://localhost/dummy','dummy_session','dbx-auth-csrf-token')
298+
299+
assertre.match(
300+
r'^https://{}/oauth2/authorize\?'.format(re.escape(session.WEB_HOST)),
301+
flow_obj._get_authorize_url('http://localhost/redirect','state','legacy'),
302+
)
303+
304+
assertflow_obj.build_url(
305+
'/oauth2/authorize'
306+
)=='https://{}/oauth2/authorize'.format(session.API_HOST)
307+
308+
assertflow_obj.build_url(
309+
'/oauth2/authorize',host=session.WEB_HOST
310+
)=='https://{}/oauth2/authorize'.format(session.WEB_HOST)
311+
312+
deftest_bad_auth(dbx_session):
313+
# Test malformed token
314+
malformed_token_dbx=Dropbox(MALFORMED_TOKEN,session=dbx_session)
315+
# TODO: backend is no longer returning `BadInputError`
316+
# with pytest.raises(BadInputError,) as cm:
317+
# malformed_token_dbx.files_list_folder('')
318+
# assert 'token is malformed' in cm.value.message
319+
withpytest.raises(AuthError):
320+
malformed_token_dbx.files_list_folder('')
321+
322+
# Test reasonable-looking invalid token
323+
invalid_token_dbx=Dropbox(INVALID_TOKEN,session=dbx_session)
324+
withpytest.raises(AuthError)ascm:
325+
invalid_token_dbx.files_list_folder('')
326+
assertcm.value.error.is_invalid_access_token()
327+
328+
deftest_bad_pins():
329+
# sanity-check: if we're pinning using expired pins, we should fail w/ an SSL error
330+
_dbx=Dropbox("dummy_token",ca_certs=_EXPIRED_CERTS_FILE)
331+
withpytest.raises(SSLError,):
332+
_dbx.files_list_folder('')
333+
334+
deftest_bad_pins_session():
335+
_session=create_session(ca_certs=_EXPIRED_CERTS_FILE)
336+
_dbx=Dropbox("dummy_token2",session=_session)
337+
withpytest.raises(SSLError,):
338+
_dbx.files_list_folder('')

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp