Esempio n. 1
0
    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:
            UnexpectedResponseError: If the backend server responds with an unexpected message
            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))

        body, http_resp = self._make_request('post', '/accounts:sendOobCode', json=payload)
        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')
Esempio n. 2
0
    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
Esempio n. 3
0
 def delete_user(self, uid):
     """Deletes the user identified by the specified user ID."""
     _auth_utils.validate_uid(uid, required=True)
     body, http_resp = self._make_request('post', '/accounts:delete', json={'localId' : uid})
     if not body or not body.get('kind'):
         raise _auth_utils.UnexpectedResponseError(
             'Failed to delete user: {0}.'.format(uid), http_response=http_resp)
Esempio n. 4
0
    def delete_users(self, uids, force_delete=False):
        """Deletes the users identified by the specified user ids.

        Args:
            uids: A list of strings indicating the uids of the users to be deleted.
                Must have <= 1000 entries.
            force_delete: Optional parameter that indicates if users should be
                deleted, even if they're not disabled. Defaults to False.


        Returns:
            BatchDeleteAccountsResponse: Server's proto response, wrapped in a
            python object.

        Raises:
            ValueError: If any of the identifiers are invalid or if more than 1000
                identifiers are specified.
            UnexpectedResponseError: If the backend server responds with an
                unexpected message.
        """
        if not uids:
            return BatchDeleteAccountsResponse()

        if len(uids) > 1000:
            raise ValueError("`uids` paramter must have <= 1000 entries.")
        for uid in uids:
            _auth_utils.validate_uid(uid, required=True)

        body, http_resp = self._make_request('post', '/accounts:batchDelete',
                                             json={'localIds': uids, 'force': force_delete})
        if not isinstance(body, dict):
            raise _auth_utils.UnexpectedResponseError(
                'Unexpected response from server while attempting to delete users.',
                http_response=http_resp)
        return BatchDeleteAccountsResponse(body.get('errors', []))
Esempio n. 5
0
 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}
     body, http_resp = self._make_request('post', '/accounts', json=payload)
     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 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')
Esempio n. 7
0
 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)
Esempio n. 8
0
    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')
Esempio n. 9
0
    def get_users(self, identifiers):
        """Looks up multiple users by their identifiers (uid, email, etc.)

        Args:
            identifiers: UserIdentifier[]: The identifiers indicating the user
                to be looked up. Must have <= 100 entries.

        Returns:
            list[dict[string, string]]: List of dicts representing the JSON
            `UserInfo` responses from the server.

        Raises:
            ValueError: If any of the identifiers are invalid or if more than
                100 identifiers are specified.
            UnexpectedResponseError: If the backend server responds with an
                unexpected message.
        """
        if not identifiers:
            return []
        if len(identifiers) > 100:
            raise ValueError(
                '`identifiers` parameter must have <= 100 entries.')

        payload = defaultdict(list)
        for identifier in identifiers:
            if isinstance(identifier, _user_identifier.UidIdentifier):
                payload['localId'].append(identifier.uid)
            elif isinstance(identifier, _user_identifier.EmailIdentifier):
                payload['email'].append(identifier.email)
            elif isinstance(identifier, _user_identifier.PhoneIdentifier):
                payload['phoneNumber'].append(identifier.phone_number)
            elif isinstance(identifier, _user_identifier.ProviderIdentifier):
                payload['federatedUserId'].append({
                    'providerId':
                    identifier.provider_id,
                    'rawId':
                    identifier.provider_uid
                })
            else:
                raise ValueError(
                    'Invalid entry in "identifiers" list. Unsupported type: {}'
                    .format(type(identifier)))

        body, http_resp = self._make_request('post',
                                             '/accounts:lookup',
                                             json=payload)
        if not http_resp.ok:
            raise _auth_utils.UnexpectedResponseError('Failed to get users.',
                                                      http_response=http_resp)
        return body.get('users', [])