Added delete option to database storage.
This commit is contained in:
parent
308604a33c
commit
963b5bc68b
1868 changed files with 192402 additions and 13278 deletions
520
venv/Lib/site-packages/firebase_admin/_user_import.py
Normal file
520
venv/Lib/site-packages/firebase_admin/_user_import.py
Normal file
|
@ -0,0 +1,520 @@
|
|||
# Copyright 2018 Google Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Firebase user import sub module."""
|
||||
|
||||
import base64
|
||||
import json
|
||||
|
||||
from firebase_admin import _auth_utils
|
||||
|
||||
|
||||
def b64_encode(bytes_value):
|
||||
return base64.urlsafe_b64encode(bytes_value).decode()
|
||||
|
||||
|
||||
class UserProvider:
|
||||
"""Represents a user identity provider that can be associated with a Firebase user.
|
||||
|
||||
One or more providers can be specified in an ``ImportUserRecord`` when importing users via
|
||||
``auth.import_users()``.
|
||||
|
||||
Args:
|
||||
uid: User's unique ID assigned by the identity provider.
|
||||
provider_id: ID of the identity provider. This can be a short domain name or the identifier
|
||||
of an OpenID identity provider.
|
||||
email: User's email address (optional).
|
||||
display_name: User's display name (optional).
|
||||
photo_url: User's photo URL (optional).
|
||||
"""
|
||||
|
||||
def __init__(self, uid, provider_id, email=None, display_name=None, photo_url=None):
|
||||
self.uid = uid
|
||||
self.provider_id = provider_id
|
||||
self.email = email
|
||||
self.display_name = display_name
|
||||
self.photo_url = photo_url
|
||||
|
||||
@property
|
||||
def uid(self):
|
||||
return self._uid
|
||||
|
||||
@uid.setter
|
||||
def uid(self, uid):
|
||||
self._uid = _auth_utils.validate_uid(uid, required=True)
|
||||
|
||||
@property
|
||||
def provider_id(self):
|
||||
return self._provider_id
|
||||
|
||||
@provider_id.setter
|
||||
def provider_id(self, provider_id):
|
||||
self._provider_id = _auth_utils.validate_provider_id(provider_id, required=True)
|
||||
|
||||
@property
|
||||
def email(self):
|
||||
return self._email
|
||||
|
||||
@email.setter
|
||||
def email(self, email):
|
||||
self._email = _auth_utils.validate_email(email)
|
||||
|
||||
@property
|
||||
def display_name(self):
|
||||
return self._display_name
|
||||
|
||||
@display_name.setter
|
||||
def display_name(self, display_name):
|
||||
self._display_name = _auth_utils.validate_display_name(display_name)
|
||||
|
||||
@property
|
||||
def photo_url(self):
|
||||
return self._photo_url
|
||||
|
||||
@photo_url.setter
|
||||
def photo_url(self, photo_url):
|
||||
self._photo_url = _auth_utils.validate_photo_url(photo_url)
|
||||
|
||||
def to_dict(self):
|
||||
payload = {
|
||||
'rawId': self.uid,
|
||||
'providerId': self.provider_id,
|
||||
'displayName': self.display_name,
|
||||
'email': self.email,
|
||||
'photoUrl': self.photo_url,
|
||||
}
|
||||
return {k: v for k, v in payload.items() if v is not None}
|
||||
|
||||
|
||||
class ImportUserRecord:
|
||||
"""Represents a user account to be imported to Firebase Auth.
|
||||
|
||||
Must specify the ``uid`` field at a minimum. A sequence of ``ImportUserRecord`` objects can be
|
||||
passed to the ``auth.import_users()`` function, in order to import those users into Firebase
|
||||
Auth in bulk. If the ``password_hash`` is set on a user, a hash configuration must be
|
||||
specified when calling ``import_users()``.
|
||||
|
||||
Args:
|
||||
uid: User's unique ID. Must be a non-empty string not longer than 128 characters.
|
||||
email: User's email address (optional).
|
||||
email_verified: A boolean indicating whether the user's email has been verified (optional).
|
||||
display_name: User's display name (optional).
|
||||
phone_number: User's phone number (optional).
|
||||
photo_url: User's photo URL (optional).
|
||||
disabled: A boolean indicating whether this user account has been disabled (optional).
|
||||
user_metadata: An ``auth.UserMetadata`` instance with additional user metadata (optional).
|
||||
provider_data: A list of ``auth.UserProvider`` instances (optional).
|
||||
custom_claims: A ``dict`` of custom claims to be set on the user account (optional).
|
||||
password_hash: User's password hash as a ``bytes`` sequence (optional).
|
||||
password_salt: User's password salt as a ``bytes`` sequence (optional).
|
||||
|
||||
Raises:
|
||||
ValueError: If provided arguments are invalid.
|
||||
"""
|
||||
|
||||
def __init__(self, uid, email=None, email_verified=None, display_name=None, phone_number=None,
|
||||
photo_url=None, disabled=None, user_metadata=None, provider_data=None,
|
||||
custom_claims=None, password_hash=None, password_salt=None):
|
||||
self.uid = uid
|
||||
self.email = email
|
||||
self.display_name = display_name
|
||||
self.phone_number = phone_number
|
||||
self.photo_url = photo_url
|
||||
self.password_hash = password_hash
|
||||
self.password_salt = password_salt
|
||||
self.email_verified = email_verified
|
||||
self.disabled = disabled
|
||||
self.user_metadata = user_metadata
|
||||
self.provider_data = provider_data
|
||||
self.custom_claims = custom_claims
|
||||
|
||||
@property
|
||||
def uid(self):
|
||||
return self._uid
|
||||
|
||||
@uid.setter
|
||||
def uid(self, uid):
|
||||
self._uid = _auth_utils.validate_uid(uid, required=True)
|
||||
|
||||
@property
|
||||
def email(self):
|
||||
return self._email
|
||||
|
||||
@email.setter
|
||||
def email(self, email):
|
||||
self._email = _auth_utils.validate_email(email)
|
||||
|
||||
@property
|
||||
def display_name(self):
|
||||
return self._display_name
|
||||
|
||||
@display_name.setter
|
||||
def display_name(self, display_name):
|
||||
self._display_name = _auth_utils.validate_display_name(display_name)
|
||||
|
||||
@property
|
||||
def phone_number(self):
|
||||
return self._phone_number
|
||||
|
||||
@phone_number.setter
|
||||
def phone_number(self, phone_number):
|
||||
self._phone_number = _auth_utils.validate_phone(phone_number)
|
||||
|
||||
@property
|
||||
def photo_url(self):
|
||||
return self._photo_url
|
||||
|
||||
@photo_url.setter
|
||||
def photo_url(self, photo_url):
|
||||
self._photo_url = _auth_utils.validate_photo_url(photo_url)
|
||||
|
||||
@property
|
||||
def password_hash(self):
|
||||
return self._password_hash
|
||||
|
||||
@password_hash.setter
|
||||
def password_hash(self, password_hash):
|
||||
self._password_hash = _auth_utils.validate_bytes(password_hash, 'password_hash')
|
||||
|
||||
@property
|
||||
def password_salt(self):
|
||||
return self._password_salt
|
||||
|
||||
@password_salt.setter
|
||||
def password_salt(self, password_salt):
|
||||
self._password_salt = _auth_utils.validate_bytes(password_salt, 'password_salt')
|
||||
|
||||
@property
|
||||
def user_metadata(self):
|
||||
return self._user_metadata
|
||||
|
||||
@user_metadata.setter
|
||||
def user_metadata(self, user_metadata):
|
||||
created_at = user_metadata.creation_timestamp if user_metadata is not None else None
|
||||
last_login_at = user_metadata.last_sign_in_timestamp if user_metadata is not None else None
|
||||
self._created_at = _auth_utils.validate_timestamp(created_at, 'creation_timestamp')
|
||||
self._last_login_at = _auth_utils.validate_timestamp(
|
||||
last_login_at, 'last_sign_in_timestamp')
|
||||
self._user_metadata = user_metadata
|
||||
|
||||
@property
|
||||
def provider_data(self):
|
||||
return self._provider_data
|
||||
|
||||
@provider_data.setter
|
||||
def provider_data(self, provider_data):
|
||||
if provider_data is not None:
|
||||
try:
|
||||
if any([not isinstance(p, UserProvider) for p in provider_data]):
|
||||
raise ValueError('One or more provider data instances are invalid.')
|
||||
except TypeError:
|
||||
raise ValueError('provider_data must be iterable.')
|
||||
self._provider_data = provider_data
|
||||
|
||||
@property
|
||||
def custom_claims(self):
|
||||
return self._custom_claims
|
||||
|
||||
@custom_claims.setter
|
||||
def custom_claims(self, custom_claims):
|
||||
json_claims = json.dumps(custom_claims) if isinstance(
|
||||
custom_claims, dict) else custom_claims
|
||||
self._custom_claims_str = _auth_utils.validate_custom_claims(json_claims)
|
||||
self._custom_claims = custom_claims
|
||||
|
||||
def to_dict(self):
|
||||
"""Returns a dict representation of the user. For internal use only."""
|
||||
payload = {
|
||||
'localId': self.uid,
|
||||
'email': self.email,
|
||||
'displayName': self.display_name,
|
||||
'phoneNumber': self.phone_number,
|
||||
'photoUrl': self.photo_url,
|
||||
'emailVerified': (bool(self.email_verified)
|
||||
if self.email_verified is not None else None),
|
||||
'disabled': bool(self.disabled) if self.disabled is not None else None,
|
||||
'customAttributes': self._custom_claims_str,
|
||||
'createdAt': self._created_at,
|
||||
'lastLoginAt': self._last_login_at,
|
||||
'passwordHash': b64_encode(self.password_hash) if self.password_hash else None,
|
||||
'salt': b64_encode(self.password_salt) if self.password_salt else None,
|
||||
}
|
||||
if self.provider_data:
|
||||
payload['providerUserInfo'] = [p.to_dict() for p in self.provider_data]
|
||||
return {k: v for k, v in payload.items() if v is not None}
|
||||
|
||||
|
||||
class UserImportHash:
|
||||
"""Represents a hash algorithm used to hash user passwords.
|
||||
|
||||
An instance of this class must be specified when importing users with passwords via the
|
||||
``auth.import_users()`` API. Use one of the provided class methods to obtain new
|
||||
instances when required. Refer to `documentation`_ for more details.
|
||||
|
||||
.. _documentation: https://firebase.google.com/docs/auth/admin/import-users
|
||||
"""
|
||||
|
||||
def __init__(self, name, data=None):
|
||||
self._name = name
|
||||
self._data = data
|
||||
|
||||
def to_dict(self):
|
||||
payload = {'hashAlgorithm': self._name}
|
||||
if self._data:
|
||||
payload.update(self._data)
|
||||
return payload
|
||||
|
||||
@classmethod
|
||||
def _hmac(cls, name, key):
|
||||
data = {
|
||||
'signerKey': b64_encode(_auth_utils.validate_bytes(key, 'key', required=True))
|
||||
}
|
||||
return UserImportHash(name, data)
|
||||
|
||||
@classmethod
|
||||
def hmac_sha512(cls, key):
|
||||
"""Creates a new HMAC SHA512 algorithm instance.
|
||||
|
||||
Args:
|
||||
key: Signer key as a byte sequence.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
return cls._hmac('HMAC_SHA512', key)
|
||||
|
||||
@classmethod
|
||||
def hmac_sha256(cls, key):
|
||||
"""Creates a new HMAC SHA256 algorithm instance.
|
||||
|
||||
Args:
|
||||
key: Signer key as a byte sequence.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
return cls._hmac('HMAC_SHA256', key)
|
||||
|
||||
@classmethod
|
||||
def hmac_sha1(cls, key):
|
||||
"""Creates a new HMAC SHA1 algorithm instance.
|
||||
|
||||
Args:
|
||||
key: Signer key as a byte sequence.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
return cls._hmac('HMAC_SHA1', key)
|
||||
|
||||
@classmethod
|
||||
def hmac_md5(cls, key):
|
||||
"""Creates a new HMAC MD5 algorithm instance.
|
||||
|
||||
Args:
|
||||
key: Signer key as a byte sequence.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
return cls._hmac('HMAC_MD5', key)
|
||||
|
||||
@classmethod
|
||||
def md5(cls, rounds):
|
||||
"""Creates a new MD5 algorithm instance.
|
||||
|
||||
Args:
|
||||
rounds: Number of rounds. Must be an integer between 0 and 8192.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
return UserImportHash(
|
||||
'MD5',
|
||||
{'rounds': _auth_utils.validate_int(rounds, 'rounds', 0, 8192)})
|
||||
|
||||
@classmethod
|
||||
def sha1(cls, rounds):
|
||||
"""Creates a new SHA1 algorithm instance.
|
||||
|
||||
Args:
|
||||
rounds: Number of rounds. Must be an integer between 1 and 8192.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
return UserImportHash(
|
||||
'SHA1',
|
||||
{'rounds': _auth_utils.validate_int(rounds, 'rounds', 1, 8192)})
|
||||
|
||||
@classmethod
|
||||
def sha256(cls, rounds):
|
||||
"""Creates a new SHA256 algorithm instance.
|
||||
|
||||
Args:
|
||||
rounds: Number of rounds. Must be an integer between 1 and 8192.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
return UserImportHash(
|
||||
'SHA256',
|
||||
{'rounds': _auth_utils.validate_int(rounds, 'rounds', 1, 8192)})
|
||||
|
||||
@classmethod
|
||||
def sha512(cls, rounds):
|
||||
"""Creates a new SHA512 algorithm instance.
|
||||
|
||||
Args:
|
||||
rounds: Number of rounds. Must be an integer between 1 and 8192.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
return UserImportHash(
|
||||
'SHA512',
|
||||
{'rounds': _auth_utils.validate_int(rounds, 'rounds', 1, 8192)})
|
||||
|
||||
@classmethod
|
||||
def pbkdf_sha1(cls, rounds):
|
||||
"""Creates a new PBKDF SHA1 algorithm instance.
|
||||
|
||||
Args:
|
||||
rounds: Number of rounds. Must be an integer between 0 and 120000.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
return UserImportHash(
|
||||
'PBKDF_SHA1',
|
||||
{'rounds': _auth_utils.validate_int(rounds, 'rounds', 0, 120000)})
|
||||
|
||||
@classmethod
|
||||
def pbkdf2_sha256(cls, rounds):
|
||||
"""Creates a new PBKDF2 SHA256 algorithm instance.
|
||||
|
||||
Args:
|
||||
rounds: Number of rounds. Must be an integer between 0 and 120000.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
return UserImportHash(
|
||||
'PBKDF2_SHA256',
|
||||
{'rounds': _auth_utils.validate_int(rounds, 'rounds', 0, 120000)})
|
||||
|
||||
@classmethod
|
||||
def scrypt(cls, key, rounds, memory_cost, salt_separator=None):
|
||||
"""Creates a new Scrypt algorithm instance.
|
||||
|
||||
This is the modified Scrypt algorithm used by Firebase Auth. See ``standard_scrypt()``
|
||||
function for the standard Scrypt algorith,
|
||||
|
||||
Args:
|
||||
key: Signer key as a byte sequence.
|
||||
rounds: Number of rounds. Must be an integer between 1 and 8.
|
||||
memory_cost: Memory cost as an integer between 1 and 14.
|
||||
salt_separator: Salt separator as a byte sequence (optional).
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
data = {
|
||||
'signerKey': b64_encode(_auth_utils.validate_bytes(key, 'key', required=True)),
|
||||
'rounds': _auth_utils.validate_int(rounds, 'rounds', 1, 8),
|
||||
'memoryCost': _auth_utils.validate_int(memory_cost, 'memory_cost', 1, 14),
|
||||
}
|
||||
if salt_separator:
|
||||
data['saltSeparator'] = b64_encode(_auth_utils.validate_bytes(
|
||||
salt_separator, 'salt_separator'))
|
||||
return UserImportHash('SCRYPT', data)
|
||||
|
||||
@classmethod
|
||||
def bcrypt(cls):
|
||||
"""Creates a new Bcrypt algorithm instance.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
return UserImportHash('BCRYPT')
|
||||
|
||||
@classmethod
|
||||
def standard_scrypt(cls, memory_cost, parallelization, block_size, derived_key_length):
|
||||
"""Creates a new standard Scrypt algorithm instance.
|
||||
|
||||
Args:
|
||||
memory_cost: Memory cost as a non-negaive integer.
|
||||
parallelization: Parallelization as a non-negative integer.
|
||||
block_size: Block size as a non-negative integer.
|
||||
derived_key_length: Derived key length as a non-negative integer.
|
||||
|
||||
Returns:
|
||||
UserImportHash: A new ``UserImportHash``.
|
||||
"""
|
||||
data = {
|
||||
'memoryCost': _auth_utils.validate_int(memory_cost, 'memory_cost', low=0),
|
||||
'parallelization': _auth_utils.validate_int(parallelization, 'parallelization', low=0),
|
||||
'blockSize': _auth_utils.validate_int(block_size, 'block_size', low=0),
|
||||
'dkLen': _auth_utils.validate_int(derived_key_length, 'derived_key_length', low=0),
|
||||
}
|
||||
return UserImportHash('STANDARD_SCRYPT', data)
|
||||
|
||||
|
||||
class ErrorInfo:
|
||||
"""Represents an error encountered while performing a batch operation such
|
||||
as importing users or deleting multiple user accounts.
|
||||
"""
|
||||
# TODO(rsgowman): This class used to be specific to importing users (hence
|
||||
# it's home in _user_import.py). It's now also used by bulk deletion of
|
||||
# users. Move this to a more common location.
|
||||
|
||||
def __init__(self, error):
|
||||
self._index = error['index']
|
||||
self._reason = error['message']
|
||||
|
||||
@property
|
||||
def index(self):
|
||||
return self._index
|
||||
|
||||
@property
|
||||
def reason(self):
|
||||
return self._reason
|
||||
|
||||
|
||||
class UserImportResult:
|
||||
"""Represents the result of a bulk user import operation.
|
||||
|
||||
See ``auth.import_users()`` API for more details.
|
||||
"""
|
||||
|
||||
def __init__(self, result, total):
|
||||
errors = result.get('error', [])
|
||||
self._success_count = total - len(errors)
|
||||
self._failure_count = len(errors)
|
||||
self._errors = [ErrorInfo(err) for err in errors]
|
||||
|
||||
@property
|
||||
def success_count(self):
|
||||
"""Returns the number of users successfully imported."""
|
||||
return self._success_count
|
||||
|
||||
@property
|
||||
def failure_count(self):
|
||||
"""Returns the number of users that failed to be imported."""
|
||||
return self._failure_count
|
||||
|
||||
@property
|
||||
def errors(self):
|
||||
"""Returns a list of ``auth.ErrorInfo`` instances describing the errors encountered."""
|
||||
return self._errors
|
Loading…
Add table
Add a link
Reference in a new issue