Skip to content

Commit c54a453

Browse files
authored
Merge branch 'firebase:master' into master
2 parents 53d973d + 1a53b04 commit c54a453

34 files changed

+1146
-299
lines changed
Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
1+
---
2+
name: Bug report
3+
about: Bug reports related to any component in this repo
4+
title: ''
5+
labels: ''
6+
assignees: ''
7+
8+
---
9+
110
### [READ] Step 1: Are you in the right place?
211

3-
* For issues or feature requests related to __the code in this repository__
4-
file a Github issue.
5-
* If this is a __feature request__ make sure the issue title starts with "FR:".
12+
* For issues related to __the code in this repository__ file a GitHub issue.
13+
* If the issue pertains to __Cloud Firestore__, report directly in the
14+
[Python Firestore](https://github.com/googleapis/python-firestore) GitHub repo. Firestore
15+
bugs reported in this repo will be closed with a reference to the Python Firestore
16+
project.
617
* For general technical questions, post a question on [StackOverflow](http://stackoverflow.com/)
7-
with the firebase tag.
18+
with the `firebase` tag.
819
* For general Firebase discussion, use the [firebase-talk](https://groups.google.com/forum/#!forum/firebase-talk)
920
google group.
1021
* For help troubleshooting your application that does not fall under one
@@ -15,8 +26,9 @@
1526

1627
* Operating System version: _____
1728
* Firebase SDK version: _____
18-
* Library version: _____
1929
* Firebase Product: _____ (auth, database, storage, etc)
30+
* Python version: _____
31+
* Pip version: _____
2032

2133
### [REQUIRED] Step 3: Describe the problem
2234

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
name: Feature request
3+
about: Suggest an idea for this project
4+
title: "[FR]"
5+
labels: 'type: feature request'
6+
assignees: ''
7+
8+
---
9+
10+
**Is your feature request related to a problem? Please describe.**
11+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12+
13+
**Describe the solution you'd like**
14+
A clear and concise description of what you want to happen.
15+
16+
**Describe alternatives you've considered**
17+
A clear and concise description of any alternative solutions or features you've considered.
18+
19+
**Additional context**
20+
Add any other context, code samples or screenshots about the feature request here.

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ jobs:
88
strategy:
99
fail-fast: false
1010
matrix:
11-
python: [3.5, 3.6, 3.7, pypy3]
11+
python: [3.6, 3.7, 3.8, 3.9, pypy3]
1212

1313
steps:
1414
- uses: actions/checkout@v1

.github/workflows/nightly.yml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Copyright 2021 Google Inc.
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+
name: Nightly Builds
16+
17+
on:
18+
# Runs every day at 06:20 AM (PT) and 08:20 PM (PT) / 04:20 AM (UTC) and 02:20 PM (UTC)
19+
# or on 'firebase_nightly_build' repository dispatch event.
20+
schedule:
21+
- cron: "20 4,14 * * *"
22+
repository_dispatch:
23+
types: [firebase_nightly_build]
24+
25+
jobs:
26+
nightly:
27+
28+
runs-on: ubuntu-latest
29+
30+
steps:
31+
- name: Checkout source for staging
32+
uses: actions/checkout@v2
33+
with:
34+
ref: ${{ github.event.client_payload.ref || github.ref }}
35+
36+
- name: Set up Python
37+
uses: actions/setup-python@v1
38+
with:
39+
python-version: 3.6
40+
41+
- name: Install dependencies
42+
run: |
43+
python -m pip install --upgrade pip
44+
pip install -r requirements.txt
45+
pip install setuptools wheel
46+
pip install tensorflow
47+
pip install keras
48+
49+
- name: Run unit tests
50+
run: pytest
51+
52+
- name: Run integration tests
53+
run: ./.github/scripts/run_integration_tests.sh
54+
env:
55+
FIREBASE_SERVICE_ACCT_KEY: ${{ secrets.FIREBASE_SERVICE_ACCT_KEY }}
56+
FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }}
57+
58+
# Build the Python Wheel and the source distribution.
59+
- name: Package release artifacts
60+
run: python setup.py bdist_wheel sdist
61+
62+
# Attach the packaged artifacts to the workflow output. These can be manually
63+
# downloaded for later inspection if necessary.
64+
- name: Archive artifacts
65+
uses: actions/upload-artifact@v1
66+
with:
67+
name: dist
68+
path: dist
69+
70+
- name: Send email on failure
71+
if: failure()
72+
uses: firebase/firebase-admin-node/.github/actions/send-email@master
73+
with:
74+
api-key: ${{ secrets.OSS_BOT_MAILGUN_KEY }}
75+
domain: ${{ secrets.OSS_BOT_MAILGUN_DOMAIN }}
76+
from: 'GitHub <admin-github@${{ secrets.OSS_BOT_MAILGUN_DOMAIN }}>'
77+
to: ${{ secrets.FIREBASE_ADMIN_GITHUB_EMAIL }}
78+
subject: 'Nightly build ${{github.run_id}} of ${{github.repository}} failed!'
79+
html: >
80+
<b>Nightly workflow ${{github.run_id}} failed on: ${{github.repository}}</b>
81+
<br /><br />Navigate to the
82+
<a href="https://github.com/firebase/firebase-admin-python/actions/runs/${{github.run_id}}">failed workflow</a>.
83+
continue-on-error: true
84+
85+
- name: Send email on cancelled
86+
if: cancelled()
87+
uses: firebase/firebase-admin-node/.github/actions/send-email@master
88+
with:
89+
api-key: ${{ secrets.OSS_BOT_MAILGUN_KEY }}
90+
domain: ${{ secrets.OSS_BOT_MAILGUN_DOMAIN }}
91+
from: 'GitHub <admin-github@${{ secrets.OSS_BOT_MAILGUN_DOMAIN }}>'
92+
to: ${{ secrets.FIREBASE_ADMIN_GITHUB_EMAIL }}
93+
subject: 'Nightly build ${{github.run_id}} of ${{github.repository}} cancelled!'
94+
html: >
95+
<b>Nightly workflow ${{github.run_id}} cancelled on: ${{github.repository}}</b>
96+
<br /><br />Navigate to the
97+
<a href="https://github.com/firebase/firebase-admin-python/actions/runs/${{github.run_id}}">cancelled workflow</a>.
98+
continue-on-error: true

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ information on using pull requests.
8585

8686
### Initial Setup
8787

88-
You need Python 3.4+ to build and test the code in this repo.
88+
You need Python 3.6+ to build and test the code in this repo.
8989

9090
We recommend using [pip](https://pypi.python.org/pypi/pip) for installing the necessary tools and
9191
project dependencies. Most recent versions of Python ship with pip. If your development environment

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ requests, code review feedback, and also pull requests.
4343

4444
## Supported Python Versions
4545

46-
We currently support Python 3.5+. Firebase Admin Python SDK is also tested on
47-
PyPy and [Google App Engine](https://cloud.google.com/appengine/) environments.
46+
We currently support Python 3.6+. Firebase
47+
Admin Python SDK is also tested on PyPy and
48+
[Google App Engine](https://cloud.google.com/appengine/) environments.
4849

4950

5051
## Documentation

firebase_admin/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
"""About information (version, etc) for Firebase Admin SDK."""
1616

17-
__version__ = '4.3.0'
17+
__version__ = '5.0.3'
1818
__title__ = 'firebase_admin'
1919
__author__ = 'Firebase'
2020
__license__ = 'Apache License 2.0'

firebase_admin/_auth_client.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from firebase_admin import _user_identifier
2525
from firebase_admin import _user_import
2626
from firebase_admin import _user_mgt
27+
from firebase_admin import _utils
2728

2829

2930
class Client:
@@ -36,17 +37,37 @@ def __init__(self, app, tenant_id=None):
3637
2. set the project ID explicitly via Firebase App options, or
3738
3. set the project ID via the GOOGLE_CLOUD_PROJECT environment variable.""")
3839

39-
credential = app.credential.get_credential()
40+
credential = None
4041
version_header = 'Python/Admin/{0}'.format(firebase_admin.__version__)
42+
timeout = app.options.get('httpTimeout', _http_client.DEFAULT_TIMEOUT_SECONDS)
43+
# Non-default endpoint URLs for emulator support are set in this dict later.
44+
endpoint_urls = {}
45+
self.emulated = False
46+
47+
# If an emulator is present, check that the given value matches the expected format and set
48+
# endpoint URLs to use the emulator. Additionally, use a fake credential.
49+
emulator_host = _auth_utils.get_emulator_host()
50+
if emulator_host:
51+
base_url = 'http://{0}/identitytoolkit.googleapis.com'.format(emulator_host)
52+
endpoint_urls['v1'] = base_url + '/v1'
53+
endpoint_urls['v2beta1'] = base_url + '/v2beta1'
54+
credential = _utils.EmulatorAdminCredentials()
55+
self.emulated = True
56+
else:
57+
# Use credentials if provided
58+
credential = app.credential.get_credential()
59+
4160
http_client = _http_client.JsonHttpClient(
42-
credential=credential, headers={'X-Client-Version': version_header})
61+
credential=credential, headers={'X-Client-Version': version_header}, timeout=timeout)
4362

4463
self._tenant_id = tenant_id
45-
self._token_generator = _token_gen.TokenGenerator(app, http_client)
64+
self._token_generator = _token_gen.TokenGenerator(
65+
app, http_client, url_override=endpoint_urls.get('v1'))
4666
self._token_verifier = _token_gen.TokenVerifier(app)
47-
self._user_manager = _user_mgt.UserManager(http_client, app.project_id, tenant_id)
67+
self._user_manager = _user_mgt.UserManager(
68+
http_client, app.project_id, tenant_id, url_override=endpoint_urls.get('v1'))
4869
self._provider_manager = _auth_providers.ProviderConfigClient(
49-
http_client, app.project_id, tenant_id)
70+
http_client, app.project_id, tenant_id, url_override=endpoint_urls.get('v2beta1'))
5071

5172
@property
5273
def tenant_id(self):
@@ -79,7 +100,8 @@ def verify_id_token(self, id_token, check_revoked=False):
79100
80101
Args:
81102
id_token: A string of the encoded JWT.
82-
check_revoked: Boolean, If true, checks whether the token has been revoked (optional).
103+
check_revoked: Boolean, If true, checks whether the token has been revoked or
104+
the user disabled (optional).
83105
84106
Returns:
85107
dict: A dictionary of key-value pairs parsed from the decoded JWT.
@@ -94,6 +116,8 @@ def verify_id_token(self, id_token, check_revoked=False):
94116
this ``Client`` instance.
95117
CertificateFetchError: If an error occurs while fetching the public key certificates
96118
required to verify the ID token.
119+
UserDisabledError: If ``check_revoked`` is ``True`` and the corresponding user
120+
record is disabled.
97121
"""
98122
if not isinstance(check_revoked, bool):
99123
# guard against accidental wrong assignment.
@@ -108,7 +132,8 @@ def verify_id_token(self, id_token, check_revoked=False):
108132
'Invalid tenant ID: {0}'.format(token_tenant_id))
109133

110134
if check_revoked:
111-
self._check_jwt_revoked(verified_claims, _token_gen.RevokedIdTokenError, 'ID token')
135+
self._check_jwt_revoked_or_disabled(
136+
verified_claims, _token_gen.RevokedIdTokenError, 'ID token')
112137
return verified_claims
113138

114139
def revoke_refresh_tokens(self, uid):
@@ -160,7 +185,7 @@ def get_user_by_email(self, email):
160185
161186
Raises:
162187
ValueError: If the email is None, empty or malformed.
163-
UserNotFoundError: If no user exists by the specified email address.
188+
UserNotFoundError: If no user exists for the specified email address.
164189
FirebaseError: If an error occurs while retrieving the user.
165190
"""
166191
response = self._user_manager.get_user(email=email)
@@ -177,7 +202,7 @@ def get_user_by_phone_number(self, phone_number):
177202
178203
Raises:
179204
ValueError: If the phone number is ``None``, empty or malformed.
180-
UserNotFoundError: If no user exists by the specified phone number.
205+
UserNotFoundError: If no user exists for the specified phone number.
181206
FirebaseError: If an error occurs while retrieving the user.
182207
"""
183208
response = self._user_manager.get_user(phone_number=phone_number)
@@ -423,6 +448,7 @@ def generate_password_reset_link(self, email, action_code_settings=None):
423448
424449
Raises:
425450
ValueError: If the provided arguments are invalid
451+
EmailNotFoundError: If no user exists for the specified email address.
426452
FirebaseError: If an error occurs while generating the link
427453
"""
428454
return self._user_manager.generate_email_action_link(
@@ -443,6 +469,7 @@ def generate_email_verification_link(self, email, action_code_settings=None):
443469
444470
Raises:
445471
ValueError: If the provided arguments are invalid
472+
UserNotFoundError: If no user exists for the specified email address.
446473
FirebaseError: If an error occurs while generating the link
447474
"""
448475
return self._user_manager.generate_email_action_link(
@@ -697,7 +724,9 @@ def list_saml_provider_configs(
697724
"""
698725
return self._provider_manager.list_saml_provider_configs(page_token, max_results)
699726

700-
def _check_jwt_revoked(self, verified_claims, exc_type, label):
727+
def _check_jwt_revoked_or_disabled(self, verified_claims, exc_type, label):
701728
user = self.get_user(verified_claims.get('uid'))
729+
if user.disabled:
730+
raise _auth_utils.UserDisabledError('The user record is disabled.')
702731
if verified_claims.get('iat') * 1000 < user.tokens_valid_after_timestamp:
703732
raise exc_type('The Firebase {0} has been revoked.'.format(label))

firebase_admin/_auth_providers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,10 @@ class ProviderConfigClient:
166166

167167
PROVIDER_CONFIG_URL = 'https://identitytoolkit.googleapis.com/v2beta1'
168168

169-
def __init__(self, http_client, project_id, tenant_id=None):
169+
def __init__(self, http_client, project_id, tenant_id=None, url_override=None):
170170
self.http_client = http_client
171-
self.base_url = '{0}/projects/{1}'.format(self.PROVIDER_CONFIG_URL, project_id)
171+
url_prefix = url_override or self.PROVIDER_CONFIG_URL
172+
self.base_url = '{0}/projects/{1}'.format(url_prefix, project_id)
172173
if tenant_id:
173174
self.base_url += '/tenants/{0}'.format(tenant_id)
174175

0 commit comments

Comments
 (0)