Esempio n. 1
0
    def change_password(self, user, password, new_password, password_confirm):

        if new_password != password_confirm:
            msg = "Your password doesn't match the confirmation"
            raise RestApiException(msg, status_code=hcodes.HTTP_BAD_CONFLICT)

        if self.auth.VERIFY_PASSWORD_STRENGTH:
            check = True
            if password is not None:
                check, msg = self.verify_password_strength(
                    new_password, old_pwd=password
                )
            else:
                check, msg = self.verify_password_strength(
                    new_password, old_hash=user.password
                )

            if not check:
                raise RestApiException(msg, status_code=hcodes.HTTP_BAD_CONFLICT)

        if new_password is not None and password_confirm is not None:
            now = datetime.now(pytz.utc)
            user.password = BaseAuthentication.get_password_hash(new_password)
            user.last_password_change = now
            self.auth.save_user(user)

            tokens = self.auth.get_tokens(user=user)
            for token in tokens:
                self.auth.invalidate_token(token=token["token"])
            # changes the user uuid invalidating all tokens
            self.auth.invalidate_all_tokens()

        return True
Esempio n. 2
0
    def verify_password_strength(self, pwd, old_pwd=None, old_hash=None):

        if old_pwd is not None and pwd == old_pwd:
            return False, "The new password cannot match the previous password"
        if old_hash is not None:
            new_hash = BaseAuthentication.get_password_hash(pwd)
            if old_hash == new_hash:
                return False, "The new password cannot match the previous password"

        # FIXME: min length should configurable?
        if len(pwd) < 8:
            return False, "Password is too short, use at least 8 characters"

        if not re.search("[a-z]", pwd):
            return False, "Password is too weak, missing lower case letters"
        if not re.search("[A-Z]", pwd):
            return False, "Password is too weak, missing upper case letters"
        if not re.search("[0-9]", pwd):
            return False, "Password is too weak, missing numbers"

        # special_characters = "['\s!#$%&\"(),*+,-./:;<=>?@[\\]^_`{|}~']"
        special_characters = "[^a-zA-Z0-9]"
        if not re.search(special_characters, pwd):
            return False, "Password is too weak, missing special characters"

        return True, None
Esempio n. 3
0
    def put(self, user_id: str, target_user: User, user: User,
            **kwargs: Any) -> Response:

        if "password" in kwargs:
            unhashed_password = kwargs["password"]
            kwargs["password"] = BaseAuthentication.get_password_hash(
                kwargs["password"])
        else:
            unhashed_password = None

        payload = kwargs.copy()
        roles: List[str] = kwargs.pop("roles", [])

        # The role is already refused by webards... This is an additional check
        # to improve the security, but can't be reached
        if not self.auth.is_admin(
                user) and Role.ADMIN in roles:  # pragma: no cover
            raise Forbidden("This role is not allowed")

        group_id = kwargs.pop("group", None)

        email_notification = kwargs.pop("email_notification", False)

        self.auth.link_roles(target_user, roles)

        userdata, extra_userdata = self.auth.custom_user_properties_pre(kwargs)

        prev_expiration = target_user.expiration

        # mypy correctly raises errors because update_properties is not defined
        # in generic Connector instances, but in this case this is an instance
        # of an auth db and their implementation always contains this method
        self.auth.db.update_properties(target_user, userdata)  # type: ignore

        self.auth.custom_user_properties_post(target_user, userdata,
                                              extra_userdata, self.auth.db)

        self.auth.save_user(target_user)

        if group_id is not None:
            group = self.auth.get_group(group_id=group_id)
            if not group:
                # Can't be reached because group_id is prefiltered by marshmallow
                raise NotFound(
                    "This group cannot be found")  # pragma: no cover

            self.auth.add_user_to_group(target_user, group)

        if email_notification and unhashed_password is not None:
            notify_update_credentials_to_user(target_user, unhashed_password)

        if target_user.expiration:
            # Set expiration on a previously non-expiring account
            # or update the expiration by reducing the validity period
            # In both cases tokens should be invalited to prevent to have tokens
            # with TTL > account validity

            # dt_lower (alias for date_lower_than) is a comparison fn that ignores tz
            if not prev_expiration or dt_lower(target_user.expiration,
                                               prev_expiration):
                for token in self.auth.get_tokens(user=target_user):
                    # Invalidate all tokens with expiration after the account expiration
                    if dt_lower(target_user.expiration, token["expiration"]):
                        self.auth.invalidate_token(token=token["token"])

        self.log_event(self.events.modify, target_user, payload)

        return self.empty_response()
Esempio n. 4
0
    def put(self, user_id=None):

        if user_id is None:

            raise RestApiException("Please specify a user id",
                                   status_code=hcodes.HTTP_BAD_REQUEST)

        schema = self.get_endpoint_custom_definition()
        if self.neo4j_enabled:
            self.graph = self.get_service_instance('neo4j')

        is_admin = self.auth.verify_admin()
        is_local_admin = self.auth.verify_local_admin()
        if not is_admin and not is_local_admin:
            raise RestApiException(
                "You are not authorized: missing privileges",
                status_code=hcodes.HTTP_BAD_UNAUTHORIZED,
            )

        v = self.get_input()

        user = self.auth.get_users(user_id)

        if user is None:
            raise RestApiException(
                "This user cannot be found or you are not authorized")

        user = user[0]

        current_user = self.get_current_user()
        is_authorized = self.check_permissions(current_user, user, is_admin,
                                               is_local_admin)
        if not is_authorized:
            raise RestApiException(
                "This user cannot be found or you are not authorized")

        if "password" in v and v["password"] == "":
            del v["password"]

        if "password" in v:
            unhashed_password = v["password"]
            v["password"] = BaseAuthentication.get_password_hash(v["password"])
        else:
            unhashed_password = None

        if "email" in v:
            v["email"] = v["email"].lower()

        roles = self.parse_roles(v)
        if not is_admin:
            allowed_roles = get_project_configuration(
                "variables.backend.allowed_roles",
                default=[],
            )

            for r in roles:
                if r not in allowed_roles:
                    raise RestApiException(
                        "You are not allowed to assign users to this role")

        self.auth.link_roles(user, roles)
        # Cannot update email address (unique username used to login-in)
        v.pop('email', None)

        if self.neo4j_enabled:
            self.update_properties(user, schema, v)
        elif self.sql_enabled:
            self.update_sql_properties(user, schema, v)
        elif self.mongo_enabled:
            self.update_mongo_properties(user, schema, v)
        else:
            raise RestApiException(
                "Invalid auth backend, all known db are disabled")

        self.auth.save_user(user)

        # FIXME: groups management is only implemented for neo4j
        if 'group' in v:

            group = self.parse_group(v)

            if not is_admin and group.shortname != "default":
                if not group.coordinator.is_connected(current_user):
                    raise RestApiException(
                        "You are not allowed to assign users to this group")

            p = None
            for p in user.belongs_to.all():
                if p == group:
                    continue

            if p is not None:
                user.belongs_to.reconnect(p, group)
            else:
                user.belongs_to.connect(group)

        email_notification = v.get('email_notification', False)
        if email_notification and unhashed_password is not None:
            self.send_notification(user, unhashed_password, is_update=True)

        return self.empty_response()
Esempio n. 5
0
    def put(self, user_id: str, **kwargs: Any) -> Response:

        user = self.auth.get_user(user_id=user_id)

        if user is None:
            raise NotFound(
                "This user cannot be found or you are not authorized")

        if "password" in kwargs:
            unhashed_password = kwargs["password"]
            kwargs["password"] = BaseAuthentication.get_password_hash(
                kwargs["password"])
        else:
            unhashed_password = None

        payload = kwargs.copy()
        roles: List[str] = kwargs.pop("roles", [])

        group_id = kwargs.pop("group", None)

        email_notification = kwargs.pop("email_notification", False)

        self.auth.link_roles(user, roles)

        userdata, extra_userdata = self.auth.custom_user_properties_pre(kwargs)

        prev_expiration = user.expiration

        self.auth.db.update_properties(user, userdata)

        self.auth.custom_user_properties_post(user, userdata, extra_userdata,
                                              self.auth.db)

        self.auth.save_user(user)

        if group_id is not None:
            group = self.auth.get_group(group_id=group_id)
            if not group:
                # Can't be reached because grup_id is prefiltered by marshmallow
                raise NotFound(
                    "This group cannot be found")  # pragma: no cover

            self.auth.add_user_to_group(user, group)

        if email_notification and unhashed_password is not None:
            smtp_client = smtp.get_instance()
            send_notification(smtp_client,
                              user,
                              unhashed_password,
                              is_update=True)

        if user.expiration:
            # Set expiration on a previously non-expiring account
            # or update the expiration by reducing the validity period
            # In both cases tokens should be invalited to prevent to have tokens
            # with TTL > account validity

            # dt_lower (alias for date_lower_than) is a comparison fn that ignores tz
            if prev_expiration is None or dt_lower(user.expiration,
                                                   prev_expiration):
                for token in self.auth.get_tokens(user=user):
                    # Invalidate all tokens with expiration after the account expiration
                    if dt_lower(user.expiration, token["expiration"]):
                        self.auth.invalidate_token(token=token["token"])

        self.log_event(self.events.modify, user, payload)

        return self.empty_response()