diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c70849e..c47fa8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,7 +132,6 @@ jobs: LEGACY_USER_CLIENT_ID: ${{ secrets.LEGACY_USER_CLIENT_ID }} LEGACY_USER_CLIENT_SECRET: ${{ secrets.LEGACY_USER_CLIENT_SECRET }} LEGACY_USER_REFRESH_TOKEN: ${{ secrets.LEGACY_USER_REFRESH_TOKEN }} - SCOPED_USER_DROPBOX_TOKEN: ${{ secrets.SCOPED_USER_DROPBOX_TOKEN }} SCOPED_USER_CLIENT_ID: ${{ secrets.SCOPED_USER_CLIENT_ID }} SCOPED_USER_CLIENT_SECRET: ${{ secrets.SCOPED_USER_CLIENT_SECRET }} SCOPED_USER_REFRESH_TOKEN: ${{ secrets.SCOPED_USER_REFRESH_TOKEN }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 19d8371..338de00 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -64,7 +64,6 @@ jobs: LEGACY_USER_CLIENT_ID: ${{ secrets.LEGACY_USER_CLIENT_ID }} LEGACY_USER_CLIENT_SECRET: ${{ secrets.LEGACY_USER_CLIENT_SECRET }} LEGACY_USER_REFRESH_TOKEN: ${{ secrets.LEGACY_USER_REFRESH_TOKEN }} - SCOPED_USER_DROPBOX_TOKEN: ${{ secrets.SCOPED_USER_DROPBOX_TOKEN }} SCOPED_USER_CLIENT_ID: ${{ secrets.SCOPED_USER_CLIENT_ID }} SCOPED_USER_CLIENT_SECRET: ${{ secrets.SCOPED_USER_CLIENT_SECRET }} SCOPED_USER_REFRESH_TOKEN: ${{ secrets.SCOPED_USER_REFRESH_TOKEN }} diff --git a/dropbox/dropbox_client.py b/dropbox/dropbox_client.py index e751842..e818390 100644 --- a/dropbox/dropbox_client.py +++ b/dropbox/dropbox_client.py @@ -667,7 +667,6 @@ def raise_dropbox_error_for_resp(self, res): elif res.status_code == 400: try: if res.json().get('error') == 'invalid_grant': - request_id = res.headers.get('x-dropbox-request-id') err = stone_serializers.json_compat_obj_decode( AuthError_validator, 'invalid_access_token') raise AuthError(request_id, err) diff --git a/test/integration/test_dropbox.py b/test/integration/test_dropbox.py index a29a3e0..5715eeb 100644 --- a/test/integration/test_dropbox.py +++ b/test/integration/test_dropbox.py @@ -29,7 +29,7 @@ from dropbox.exceptions import ( ApiError, AuthError, - # BadInputError, + BadInputError, PathRootError, ) from dropbox.files import ( @@ -78,19 +78,37 @@ def dbx_session(request): return create_session(ca_certs=request.param) +def _refresh_dbx(dbx_session=None): + # Build a user client from the refresh token instead of a stored access + # token; the SDK mints a short-lived access token on demand. Access tokens + # were the recurring cause of CI auth failures, so this removes them from + # the required credentials. + refresh_token = _value_from_env_or_die(format_env_name(SCOPED_KEY, USER_KEY, REFRESH_TOKEN_KEY)) + app_key = _value_from_env_or_die(format_env_name(SCOPED_KEY, USER_KEY, CLIENT_ID_KEY)) + app_secret = _value_from_env_or_die(format_env_name(SCOPED_KEY, USER_KEY, CLIENT_SECRET_KEY)) + return Dropbox(oauth2_refresh_token=refresh_token, + app_key=app_key, app_secret=app_secret, + session=dbx_session) + + @pytest.fixture() def dbx_from_env(dbx_session): - oauth2_token = _value_from_env_or_die(format_env_name()) - return Dropbox(oauth2_token, session=dbx_session) + return _refresh_dbx(dbx_session) @pytest.fixture() def refresh_dbx_from_env(dbx_session): + return _refresh_dbx(dbx_session) + + +@pytest.fixture() +def bad_secret_dbx_from_env(dbx_session): + # Valid refresh token and app key, but a wrong app secret, so the token + # endpoint rejects the refresh. refresh_token = _value_from_env_or_die(format_env_name(SCOPED_KEY, USER_KEY, REFRESH_TOKEN_KEY)) app_key = _value_from_env_or_die(format_env_name(SCOPED_KEY, USER_KEY, CLIENT_ID_KEY)) - app_secret = _value_from_env_or_die(format_env_name(SCOPED_KEY, USER_KEY, CLIENT_SECRET_KEY)) return Dropbox(oauth2_refresh_token=refresh_token, - app_key=app_key, app_secret=app_secret, + app_key=app_key, app_secret='invalid_app_secret', session=dbx_session) @@ -120,13 +138,13 @@ def dbx_share_url_from_env(): DUMMY_PAYLOAD = string.ascii_letters.encode('ascii') RANDOM_FOLDER = random.sample(string.ascii_letters, 15) -TIMESTAMP = str(datetime.datetime.utcnow()) +TIMESTAMP = str(datetime.datetime.now(datetime.timezone.utc)) STATIC_FILE = "/test.txt" @pytest.fixture(scope='module') def pytest_setup(): print("Setup") - dbx = Dropbox(_value_from_env_or_die(format_env_name())) + dbx = _refresh_dbx() try: dbx.files_delete(STATIC_FILE) @@ -162,6 +180,11 @@ def test_multi_auth(self, dbx_from_env, dbx_app_auth_from_env, dbx_share_url_fro def test_refresh(self, refresh_dbx_from_env): refresh_dbx_from_env.users_get_current_account() + def test_refresh_failure_error_detail(self, bad_secret_dbx_from_env): + with pytest.raises(BadInputError) as cm: + bad_secret_dbx_from_env.users_get_current_account() + assert 'invalid_client' in cm.value.message + def test_app_auth(self, dbx_app_auth_from_env): dbx_app_auth_from_env.check_app(query="hello world")