Exemple #1
0
    def validate_oid_token(self, token: str, client_id: str, tenant_id: str,
                           issuer: str, provider: int) -> dict:
        """
        This method takes a token issued by an OID provider, the relevant information about the OID provider
        and validates that the token was generated by such source, is valid and extracts the information
        in the token for its use during the login process

        :param str token: the received token
        :param str client_id: the identifier from the client
        :param str tenant_id: the identifier for the tenant
        :param str issuer: the identifier for the issuer of the token
        :param int provider: the identifier for the provider of the token
        :return: the decoded token as a dictionary
        :rtype: dict
        """
        public_key = self._get_public_key(token, tenant_id, provider)
        try:
            decoded = jwt.decode(
                token,
                public_key,
                verify=True,
                algorithms=["RS256"],
                audience=[client_id],
                issuer=issuer,
            )
            return decoded
        except jwt.ExpiredSignatureError:
            raise InvalidCredentials(
                "The token has expired, please login again")
        except jwt.InvalidTokenError:
            raise InvalidCredentials(
                "Invalid token, please try again with a new token")
Exemple #2
0
    def _get_key_id(token: str) -> str:
        """
        Function to get the Key ID from the token

        :param str token: the given token
        :return: the key identifier
        :rtype: str
        """
        headers = jwt.get_unverified_header(token)
        if not headers:
            raise InvalidCredentials("Token is missing the headers")
        try:
            return headers["kid"]
        except KeyError:
            raise InvalidCredentials("Token is missing the key identifier")
Exemple #3
0
    def __init__(self, data):
        super().__init__()
        self.first_name = data.get("first_name")
        self.last_name = data.get("last_name")
        self.username = data.get("username")
        # TODO: handle better None passwords that can be found when using ldap
        check_pass, msg = check_password_pattern(data.get("password"))
        if check_pass:
            self.password = self.__generate_hash(data.get("password"))
        else:
            raise InvalidCredentials(msg)

        check_email, msg = check_email_pattern(data.get("email"))
        if check_email:
            self.email = data.get("email")
        else:
            raise InvalidCredentials(msg)
Exemple #4
0
    def sign_up(self, **kwargs):
        """
        The method in charge of performing the sign up of users

        :param kwargs: the keyword arguments needed to perform the sign up
        :return: a dictionary with the newly issued token and the user id, and a status code
        """
        auth_type = current_app.config["AUTH_TYPE"]
        if auth_type == AUTH_LDAP:
            raise EndpointNotImplemented(
                "The user has to sign up on the active directory")
        elif auth_type == AUTH_OID:
            raise EndpointNotImplemented(
                "The user has to sign up with the OpenID protocol")

        user = self.data_model(kwargs)

        if user.check_username_in_use():
            raise InvalidCredentials(
                error="Username already in use, please supply another username"
            )

        if user.check_email_in_use():
            raise InvalidCredentials(
                error=
                "Email already in use, please supply another email address")

        user.save()

        user_role = self.user_role_association({
            "user_id":
            user.id,
            "role_id":
            current_app.config["DEFAULT_ROLE"]
        })

        user_role.save()

        try:
            token = self.auth_class.generate_token(user.id)
        except Exception as e:
            raise InvalidUsage(error="Error in generating user token: " +
                               str(e),
                               status_code=400)

        return {"token": token, "id": user.id}, 201
Exemple #5
0
    def auth_db_authenticate(self, username, password):
        """
        Method in charge of performing the authentication against the database

        :param str username: the username of the user to log in
        :param str password:  the password of the user to log in
        :return: the user object or it raises an error if it has not been possible to log in
        :rtype: :class:`UserBaseModel`
        """
        user = self.data_model.get_one_object(username=username)

        if not user:
            raise InvalidCredentials()

        if not user.check_hash(password):
            raise InvalidCredentials()

        return user
Exemple #6
0
        def wrapper(*args, **kwargs):
            """
            The wrapper to the function that performs the authentication.

            :param args: the original args sent to the decorated function
            :param kwargs: the original kwargs sent to the decorated function
            :return: the result of the call to the function
            """
            if auth_class.authenticate():
                return func(*args, **kwargs)
            else:
                raise InvalidCredentials("Unable to authenticate the user")
Exemple #7
0
    def decode_token(token: str = None) -> dict:
        """
        Decodes a given JSON Web token and extracts the sub from it to give it back.

        :param str token: the given JSON Web Token
        :return: the sub field of the token as the user_id
        :rtype: dict
        """
        if token is None:
            raise InvalidUsage("The provided token is not valid")
        try:
            payload = decode(token,
                             current_app.config["SECRET_KEY"],
                             algorithms="HS256")
            return {"user_id": payload["sub"]}
        except ExpiredSignatureError:
            raise InvalidCredentials(
                "The token has expired, please login again")
        except InvalidTokenError:
            raise InvalidCredentials(
                "Invalid token, please try again with a new token")
Exemple #8
0
    def get_token_from_header(headers: Headers = None) -> str:
        """
        Extracts the token given on the request from the Authorization headers.

        :param headers: the request headers
        :type headers: `Headers`
        :return: the extracted token
        :rtype: str
        """
        if headers is None:
            raise InvalidUsage

        if "Authorization" not in headers:
            raise InvalidCredentials("Auth token is not available")
        auth_header = headers.get("Authorization")
        if not auth_header:
            return ""
        try:
            return auth_header.split(" ")[1]
        except Exception as e:
            raise InvalidCredentials(
                f"The authorization header has a bad syntax: {e}")
Exemple #9
0
    def put(self, user_id, **data):
        """
        API method to edit an existing user.
        It requires authentication to be passed in the form of a token that has to be linked to
        an existing session (login) made by a user. Only admin and service user can edit other users.

        :param int user_id: id of the user
        :return: A dictionary with a message (error if authentication failed, or the execution does not exist or
          a message) and an integer with the HTTP status code.
        :rtype: Tuple(dict, integer)
        """
        if self.get_user_id() != user_id and not self.is_admin():
            raise NoPermission()
        user_obj = UserModel.get_one_user(user_id)
        if user_obj is None:
            raise ObjectDoesNotExist()
        # working with a ldap service users cannot be edited.
        if (current_app.config["AUTH_TYPE"] == AUTH_LDAP
                and user_obj.comes_from_external_provider()):
            raise EndpointNotImplemented("To edit a user, go to LDAP server")
        # working with an OID provider users can not be edited
        if (current_app.config["AUTH_TYPE"] == AUTH_OID
                and user_obj.comes_from_external_provider()):
            raise EndpointNotImplemented(
                "To edit a user, go to the OID provider")

        if data.get("password"):
            check, msg = check_password_pattern(data.get("password"))
            if not check:
                raise InvalidCredentials(msg)

        if data.get("email"):
            check, msg = check_email_pattern(data.get("email"))
            if not check:
                raise InvalidCredentials(msg)

        log.info(f"User {user_id} was edited by user {self.get_user()}")
        return self.put_detail(data=data, idx=user_id, track_user=False)
Exemple #10
0
    def _get_jwk(self, kid: str, tenant_id: str, provider: int) -> dict:
        """
        Function to get the JSON Web Key from the key identifier, the tenant id and the provider information

        :param str kid: the key identifier
        :param str tenant_id: the tenant information
        :param int provider: the provider information
        :return: the JSON Web Key
        :rtype: dict
        """
        for jwk in self._get_json_web_keys(tenant_id, provider).get("keys"):
            if jwk.get("kid") == kid:
                return jwk
        raise InvalidCredentials("Token has an unknown key identifier")
Exemple #11
0
    def auth_ldap_authenticate(self, username, password):
        """
        Method in charge of performing the authentication against the ldap server

        :param str username: the username of the user to log in
        :param str password:  the password of the user to log in
        :return: the user object or it raises an error if it has not been possible to log in
        :rtype: :class:`UserBaseModel`
        """
        ldap_obj = self.ldap_class(current_app.config)
        if not ldap_obj.authenticate(username, password):
            raise InvalidCredentials()
        user = self.data_model.get_one_object(username=username)
        if not user:
            log.info(f"LDAP user {username} does not exist and is created")
            email = ldap_obj.get_user_email(username)
            if not email:
                email = ""
            data = {"username": username, "email": email}
            user = self.data_model(data=data)
            user.save()

        roles = ldap_obj.get_user_roles(username)

        try:
            self.user_role_association.del_one_user(user.id)
            for role in roles:
                user_role = self.user_role_association(data={
                    "user_id": user.id,
                    "role_id": role
                })
                user_role.save()

        except IntegrityError as e:
            db.session.rollback()
            log.error(
                f"Integrity error on user role assignment on log in: {e}")
        except DBAPIError as e:
            db.session.rollback()
            log.error(f"Unknown error on user role assignment on log in: {e}")

        return user