def send_email_action_link(self, action_type, email, action_code_settings=None): """Sends an email of the given action type Args: action_type: String. Valid values ['VERIFY_EMAIL', 'EMAIL_SIGNIN', 'PASSWORD_RESET'] email: Email of the user to whom the email will be sent action_code_settings: ``ActionCodeSettings`` object or dict (optional). Defines whether the link is to be handled by a mobile app and the additional state information to be passed in the deep link, etc. Raises: FirebaseError: If an error occurs while generating the link ValueError: If the provided arguments are invalid """ payload = { 'requestType': _auth_utils.validate_action_type(action_type), 'email': _auth_utils.validate_email(email) } if action_code_settings: payload.update(encode_action_code_settings(action_code_settings)) try: self._client.body_and_response( 'post', '/accounts:sendOobCode', json=payload) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error)
def import_users(self, users, hash_alg=None): """Imports the given list of users to Firebase Auth.""" try: if not users or len(users) > MAX_IMPORT_USERS_SIZE: raise ValueError( 'Users must be a non-empty list with no more than {0} elements.' .format(MAX_IMPORT_USERS_SIZE)) if any([ not isinstance(u, _user_import.ImportUserRecord) for u in users ]): raise ValueError('One or more user objects are invalid.') except TypeError: raise ValueError('users must be iterable') payload = {'users': [u.to_dict() for u in users]} if any(['passwordHash' in u for u in payload['users']]): if not isinstance(hash_alg, _user_import.UserImportHash): raise ValueError( 'A UserImportHash is required to import users with passwords.' ) payload.update(hash_alg.to_dict()) try: body, http_resp = self._client.body_and_response( 'post', '/accounts:batchCreate', json=payload) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error) else: if not isinstance(body, dict): raise _auth_utils.UnexpectedResponseError( 'Failed to import users.', http_response=http_resp) return body
def get_user(self, **kwargs): """Gets the user data corresponding to the provided key.""" if 'uid' in kwargs: key, key_type = kwargs.pop('uid'), 'user ID' payload = { 'localId': [_auth_utils.validate_uid(key, required=True)] } elif 'email' in kwargs: key, key_type = kwargs.pop('email'), 'email' payload = { 'email': [_auth_utils.validate_email(key, required=True)] } elif 'phone_number' in kwargs: key, key_type = kwargs.pop('phone_number'), 'phone number' payload = { 'phoneNumber': [_auth_utils.validate_phone(key, required=True)] } else: raise TypeError( 'Unsupported keyword arguments: {0}.'.format(kwargs)) try: body, http_resp = self._client.body_and_response( 'post', '/accounts:lookup', json=payload) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error) else: if not body or not body.get('users'): raise _auth_utils.UserNotFoundError( 'No user record found for the provided {0}: {1}.'.format( key_type, key), http_response=http_resp) return body['users'][0]
def update_tenant(self, tenant_id, display_name=None, allow_password_sign_up=None, enable_email_link_sign_in=None): """Updates the specified tenant with the given parameters.""" if not isinstance(tenant_id, str) or not tenant_id: raise ValueError('Tenant ID must be a non-empty string.') payload = {} if display_name is not None: payload['displayName'] = _validate_display_name(display_name) if allow_password_sign_up is not None: payload['allowPasswordSignup'] = _auth_utils.validate_boolean( allow_password_sign_up, 'allowPasswordSignup') if enable_email_link_sign_in is not None: payload['enableEmailLinkSignin'] = _auth_utils.validate_boolean( enable_email_link_sign_in, 'enableEmailLinkSignin') if not payload: raise ValueError( 'At least one parameter must be specified for update.') url = '/tenants/{0}'.format(tenant_id) update_mask = ','.join(_auth_utils.build_update_mask(payload)) params = 'updateMask={0}'.format(update_mask) try: body = self.client.body('patch', url, json=payload, params=params) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error) else: return Tenant(body)
def generate_email_action_link(self, action_type, email, action_code_settings=None): """Fetches the email action links for types Args: action_type: String. Valid values ['VERIFY_EMAIL', 'EMAIL_SIGNIN', 'PASSWORD_RESET'] email: Email of the user for which the action is performed action_code_settings: ``ActionCodeSettings`` object or dict (optional). Defines whether the link is to be handled by a mobile app and the additional state information to be passed in the deep link, etc. Returns: link_url: action url to be emailed to the user Raises: FirebaseError: If an error occurs while generating the link ValueError: If the provided arguments are invalid """ payload = { 'requestType': _auth_utils.validate_action_type(action_type), 'email': _auth_utils.validate_email(email), 'returnOobLink': True } if action_code_settings: payload.update(encode_action_code_settings(action_code_settings)) try: body, http_resp = self._client.body_and_response( 'post', '/accounts:sendOobCode', json=payload) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error) else: if not body or not body.get('oobLink'): raise _auth_utils.UnexpectedResponseError( 'Failed to generate email action link.', http_response=http_resp) return body.get('oobLink')
def create_session_cookie(self, id_token, expires_in): """Creates a session cookie from the provided ID token.""" id_token = id_token.decode('utf-8') if isinstance(id_token, bytes) else id_token if not isinstance(id_token, str) or not id_token: raise ValueError( 'Illegal ID token provided: {0}. ID token must be a non-empty ' 'string.'.format(id_token)) if isinstance(expires_in, datetime.timedelta): expires_in = int(expires_in.total_seconds()) if isinstance(expires_in, bool) or not isinstance(expires_in, int): raise ValueError('Illegal expiry duration: {0}.'.format(expires_in)) if expires_in < MIN_SESSION_COOKIE_DURATION_SECONDS: raise ValueError('Illegal expiry duration: {0}. Duration must be at least {1} ' 'seconds.'.format(expires_in, MIN_SESSION_COOKIE_DURATION_SECONDS)) if expires_in > MAX_SESSION_COOKIE_DURATION_SECONDS: raise ValueError('Illegal expiry duration: {0}. Duration must be at most {1} ' 'seconds.'.format(expires_in, MAX_SESSION_COOKIE_DURATION_SECONDS)) url = '{0}:createSessionCookie'.format(self.base_url) payload = { 'idToken': id_token, 'validDuration': expires_in, } try: body, http_resp = self.http_client.body_and_response('post', url, json=payload) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error) else: if not body or not body.get('sessionCookie'): raise _auth_utils.UnexpectedResponseError( 'Failed to create session cookie.', http_response=http_resp) return body.get('sessionCookie')
def delete_tenant(self, tenant_id): """Deletes the tenant corresponding to the given ``tenant_id``.""" if not isinstance(tenant_id, str) or not tenant_id: raise ValueError( 'Invalid tenant ID: {0}. Tenant ID must be a non-empty string.' .format(tenant_id)) try: self.client.request('delete', '/tenants/{0}'.format(tenant_id)) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error)
def delete_user(self, uid): """Deletes the user identified by the specified user ID.""" _auth_utils.validate_uid(uid, required=True) try: body, http_resp = self._client.body_and_response( 'post', '/accounts:delete', json={'localId' : uid}) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error) else: if not body or not body.get('kind'): raise _auth_utils.UnexpectedResponseError( 'Failed to delete user: {0}.'.format(uid), http_response=http_resp)
def get_tenant(self, tenant_id): """Gets the tenant corresponding to the given ``tenant_id``.""" if not isinstance(tenant_id, str) or not tenant_id: raise ValueError( 'Invalid tenant ID: {0}. Tenant ID must be a non-empty string.' .format(tenant_id)) try: body = self.client.body('get', '/tenants/{0}'.format(tenant_id)) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error) else: return Tenant(body)
def update_user(self, uid, display_name=None, email=None, phone_number=None, photo_url=None, password=None, disabled=None, email_verified=None, valid_since=None, custom_claims=None): """Updates an existing user account with the specified properties""" payload = { 'localId': _auth_utils.validate_uid(uid, required=True), 'email': _auth_utils.validate_email(email), 'password': _auth_utils.validate_password(password), 'validSince': _auth_utils.validate_timestamp(valid_since, 'valid_since'), 'emailVerified': bool(email_verified) if email_verified is not None else None, 'disableUser': bool(disabled) if disabled is not None else None, } remove = [] if display_name is not None: if display_name is DELETE_ATTRIBUTE: remove.append('DISPLAY_NAME') else: payload['displayName'] = _auth_utils.validate_display_name(display_name) if photo_url is not None: if photo_url is DELETE_ATTRIBUTE: remove.append('PHOTO_URL') else: payload['photoUrl'] = _auth_utils.validate_photo_url(photo_url) if remove: payload['deleteAttribute'] = remove if phone_number is not None: if phone_number is DELETE_ATTRIBUTE: payload['deleteProvider'] = ['phone'] else: payload['phoneNumber'] = _auth_utils.validate_phone(phone_number) if custom_claims is not None: if custom_claims is DELETE_ATTRIBUTE: custom_claims = {} json_claims = json.dumps(custom_claims) if isinstance( custom_claims, dict) else custom_claims payload['customAttributes'] = _auth_utils.validate_custom_claims(json_claims) payload = {k: v for k, v in payload.items() if v is not None} try: body, http_resp = self._client.body_and_response( 'post', '/accounts:update', json=payload) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error) else: if not body or not body.get('localId'): raise _auth_utils.UnexpectedResponseError( 'Failed to update user: {0}.'.format(uid), http_response=http_resp) return body.get('localId')
def list_users(self, page_token=None, max_results=MAX_LIST_USERS_RESULTS): """Retrieves a batch of users.""" if page_token is not None: if not isinstance(page_token, six.string_types) or not page_token: raise ValueError('Page token must be a non-empty string.') if not isinstance(max_results, int): raise ValueError('Max results must be an integer.') elif max_results < 1 or max_results > MAX_LIST_USERS_RESULTS: raise ValueError( 'Max results must be a positive integer less than ' '{0}.'.format(MAX_LIST_USERS_RESULTS)) payload = {'maxResults': max_results} if page_token: payload['nextPageToken'] = page_token try: return self._client.body('get', '/accounts:batchGet', params=payload) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error)
def create_tenant(self, display_name, allow_password_sign_up=None, enable_email_link_sign_in=None): """Creates a new tenant from the given parameters.""" payload = {'displayName': _validate_display_name(display_name)} if allow_password_sign_up is not None: payload['allowPasswordSignup'] = _auth_utils.validate_boolean( allow_password_sign_up, 'allowPasswordSignup') if enable_email_link_sign_in is not None: payload['enableEmailLinkSignin'] = _auth_utils.validate_boolean( enable_email_link_sign_in, 'enableEmailLinkSignin') try: body = self.client.body('post', '/tenants', json=payload) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error) else: return Tenant(body)
def list_tenants(self, page_token=None, max_results=_MAX_LIST_TENANTS_RESULTS): """Retrieves a batch of tenants.""" if page_token is not None: if not isinstance(page_token, str) or not page_token: raise ValueError('Page token must be a non-empty string.') if not isinstance(max_results, int): raise ValueError('Max results must be an integer.') if max_results < 1 or max_results > _MAX_LIST_TENANTS_RESULTS: raise ValueError( 'Max results must be a positive integer less than or equal to ' '{0}.'.format(_MAX_LIST_TENANTS_RESULTS)) payload = {'pageSize': max_results} if page_token: payload['pageToken'] = page_token try: return self.client.body('get', '/tenants', params=payload) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error)
def create_user(self, uid=None, display_name=None, email=None, phone_number=None, photo_url=None, password=None, disabled=None, email_verified=None): """Creates a new user account with the specified properties.""" payload = { 'localId': _auth_utils.validate_uid(uid), 'displayName': _auth_utils.validate_display_name(display_name), 'email': _auth_utils.validate_email(email), 'phoneNumber': _auth_utils.validate_phone(phone_number), 'photoUrl': _auth_utils.validate_photo_url(photo_url), 'password': _auth_utils.validate_password(password), 'emailVerified': bool(email_verified) if email_verified is not None else None, 'disabled': bool(disabled) if disabled is not None else None, } payload = {k: v for k, v in payload.items() if v is not None} try: body, http_resp = self._client.body_and_response('post', '/accounts', json=payload) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error) else: if not body or not body.get('localId'): raise _auth_utils.UnexpectedResponseError( 'Failed to create new user.', http_response=http_resp) return body.get('localId')
def _make_request(self, method, path, **kwargs): url = '{0}{1}'.format(self.base_url, path) try: return self.http_client.body(method, url, **kwargs) except requests.exceptions.RequestException as error: raise _auth_utils.handle_auth_backend_error(error)