From 098e300a8f6fa29e9da25f2cd812b995a6c9c186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gu=C3=A9na=C3=ABl=20Muller?= Date: Tue, 24 Jul 2018 15:31:13 +0200 Subject: [PATCH] [#680] better support for is_active params --- tracim/__init__.py | 2 + tracim/exceptions.py | 4 ++ tracim/lib/core/user.py | 14 +++-- tracim/lib/utils/authentification.py | 1 + tracim/lib/utils/request.py | 3 + tracim/tests/functional/test_session.py | 82 +++++++++++++++++++++++++ tracim/tests/library/test_user_api.py | 32 +++++++++- 7 files changed, 129 insertions(+), 9 deletions(-) diff --git a/tracim/__init__.py b/tracim/__init__.py index cba1358..d23f110 100644 --- a/tracim/__init__.py +++ b/tracim/__init__.py @@ -29,6 +29,7 @@ from tracim.views.contents_api.comment_controller import CommentController from tracim.views.errors import ErrorSchema from tracim.exceptions import NotAuthenticated +from tracim.exceptions import UserNotActive from tracim.exceptions import InvalidId from tracim.exceptions import InsufficientUserProfile from tracim.exceptions import InsufficientUserRoleInWorkspace @@ -94,6 +95,7 @@ def web(global_config, **local_settings): context.handle_exception(InvalidId, HTTPStatus.BAD_REQUEST) # Auth exception context.handle_exception(NotAuthenticated, HTTPStatus.UNAUTHORIZED) + context.handle_exception(UserNotActive, HTTPStatus.FORBIDDEN) context.handle_exception(AuthenticationFailed, HTTPStatus.FORBIDDEN) context.handle_exception(InsufficientUserRoleInWorkspace, HTTPStatus.FORBIDDEN) # nopep8 context.handle_exception(InsufficientUserProfile, HTTPStatus.FORBIDDEN) diff --git a/tracim/exceptions.py b/tracim/exceptions.py index 6fed45b..16de387 100644 --- a/tracim/exceptions.py +++ b/tracim/exceptions.py @@ -167,3 +167,7 @@ class EmptyLabelNotAllowed(EmptyValueNotAllowed): class EmptyCommentContentNotAllowed(EmptyValueNotAllowed): pass + + +class UserNotActive(TracimException): + pass diff --git a/tracim/lib/core/user.py b/tracim/lib/core/user.py index f0c2709..6107be8 100644 --- a/tracim/lib/core/user.py +++ b/tracim/lib/core/user.py @@ -1,21 +1,21 @@ # -*- coding: utf-8 -*- -import threading from smtplib import SMTPException import transaction import typing as typing - -from tracim.exceptions import NotificationNotSend -from tracim.lib.mail_notifier.notifier import get_email_manager from sqlalchemy.orm import Session +from sqlalchemy.orm.exc import NoResultFound from tracim import CFG from tracim.models.auth import User from tracim.models.auth import Group -from sqlalchemy.orm.exc import NoResultFound -from tracim.exceptions import WrongUserPassword, UserDoesNotExist +from tracim.exceptions import WrongUserPassword +from tracim.exceptions import UserDoesNotExist from tracim.exceptions import AuthenticationFailed +from tracim.exceptions import NotificationNotSend +from tracim.exceptions import UserNotActive from tracim.models.context_models import UserInContext +from tracim.lib.mail_notifier.notifier import get_email_manager class UserApi(object): @@ -103,6 +103,8 @@ def authenticate_user(self, email: str, password: str) -> User: """ try: user = self.get_one_by_email(email) + if not user.is_active: + raise UserNotActive('User "{}" is not active'.format(email)) if user.validate_password(password): return user else: diff --git a/tracim/lib/utils/authentification.py b/tracim/lib/utils/authentification.py index e0309ea..20d3dca 100644 --- a/tracim/lib/utils/authentification.py +++ b/tracim/lib/utils/authentification.py @@ -32,6 +32,7 @@ def basic_auth_check_credentials( user = _get_basic_auth_unsafe_user(request) if not user \ or user.email != login \ + or not user.is_active \ or not user.validate_password(cleartext_password): return None return [] diff --git a/tracim/lib/utils/request.py b/tracim/lib/utils/request.py index f8c8d40..d0cc41c 100644 --- a/tracim/lib/utils/request.py +++ b/tracim/lib/utils/request.py @@ -3,6 +3,7 @@ from sqlalchemy.orm.exc import NoResultFound from tracim.exceptions import NotAuthenticated +from tracim.exceptions import UserNotActive from tracim.exceptions import ContentNotFound from tracim.exceptions import InvalidUserId from tracim.exceptions import InvalidWorkspaceId @@ -321,6 +322,8 @@ def _get_auth_safe_user( if not login: raise UserNotFoundInTracimRequest('You request a current user but the context not permit to found one') # nopep8 user = uapi.get_one_by_email(login) + if not user.is_active: + raise UserNotActive('User {} is not active'.format(login)) except (UserDoesNotExist, UserNotFoundInTracimRequest) as exc: raise NotAuthenticated('User {} not found'.format(login)) from exc return user diff --git a/tracim/tests/functional/test_session.py b/tracim/tests/functional/test_session.py index 4acd072..e070e2b 100644 --- a/tracim/tests/functional/test_session.py +++ b/tracim/tests/functional/test_session.py @@ -1,8 +1,13 @@ # coding=utf-8 import datetime import pytest +import transaction from sqlalchemy.exc import OperationalError +from tracim import models +from tracim.lib.core.group import GroupApi +from tracim.lib.core.user import UserApi +from tracim.models import get_tm_session from tracim.tests import FunctionalTest from tracim.tests import FunctionalTestNoDB @@ -59,6 +64,45 @@ def test_api__try_login_enpoint__ok_200__nominal_case(self): assert res.json_body['caldav_url'] is None assert res.json_body['avatar_url'] is None + def test_api__try_login_enpoint__err_401__user_not_activated(self): + dbsession = get_tm_session(self.session_factory, transaction.manager) + admin = dbsession.query(models.User) \ + .filter(models.User.email == 'admin@admin.admin') \ + .one() + uapi = UserApi( + current_user=admin, + session=dbsession, + config=self.app_config, + ) + gapi = GroupApi( + current_user=admin, + session=dbsession, + config=self.app_config, + ) + groups = [gapi.get_one_with_name('users')] + test_user = uapi.create_user( + email='test@test.test', + password='pass', + name='bob', + groups=groups, + timezone='Europe/Paris', + do_save=True, + do_notify=False, + ) + uapi.save(test_user) + uapi.disable(test_user) + transaction.commit() + + params = { + 'email': 'test@test.test', + 'password': 'test@test.test', + } + res = self.testapp.post_json( + '/api/v2/sessions/login', + params=params, + status=403, + ) + def test_api__try_login_enpoint__err_403__bad_password(self): params = { 'email': 'admin@admin.admin', @@ -117,6 +161,44 @@ def test_api__try_whoami_enpoint__ok_200__nominal_case(self): assert res.json_body['caldav_url'] is None assert res.json_body['avatar_url'] is None + def test_api__try_whoami_enpoint__err_401__user_is_not_active(self): + dbsession = get_tm_session(self.session_factory, transaction.manager) + admin = dbsession.query(models.User) \ + .filter(models.User.email == 'admin@admin.admin') \ + .one() + uapi = UserApi( + current_user=admin, + session=dbsession, + config=self.app_config, + ) + gapi = GroupApi( + current_user=admin, + session=dbsession, + config=self.app_config, + ) + groups = [gapi.get_one_with_name('users')] + test_user = uapi.create_user( + email='test@test.test', + password='pass', + name='bob', + groups=groups, + timezone='Europe/Paris', + do_save=True, + do_notify=False, + ) + uapi.save(test_user) + uapi.disable(test_user) + transaction.commit() + self.testapp.authorization = ( + 'Basic', + ( + 'test@test.test', + 'pass' + ) + ) + + res = self.testapp.get('/api/v2/sessions/whoami', status=401) + def test_api__try_whoami_enpoint__err_401__unauthenticated(self): self.testapp.authorization = ( 'Basic', diff --git a/tracim/tests/library/test_user_api.py b/tracim/tests/library/test_user_api.py index bd08e15..69a4b37 100644 --- a/tracim/tests/library/test_user_api.py +++ b/tracim/tests/library/test_user_api.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- import pytest -from sqlalchemy.orm.exc import NoResultFound - import transaction -from tracim.exceptions import UserDoesNotExist, AuthenticationFailed +from tracim.exceptions import AuthenticationFailed +from tracim.exceptions import UserDoesNotExist +from tracim.exceptions import UserNotActive +from tracim.lib.core.group import GroupApi from tracim.lib.core.user import UserApi from tracim.models import User from tracim.models.context_models import UserInContext @@ -171,6 +172,31 @@ def test_unit__authenticate_user___ok__nominal_case(self): assert isinstance(user, User) assert user.email == 'admin@admin.admin' + def test_unit__authenticate_user___err__user_not_active(self): + api = UserApi( + current_user=None, + session=self.session, + config=self.config, + ) + gapi = GroupApi( + current_user=None, + session=self.session, + config=self.config, + ) + groups = [gapi.get_one_with_name('users')] + user = api.create_user( + email='test@test.test', + password='pass', + name='bob', + groups=groups, + timezone='Europe/Paris', + do_save=True, + do_notify=False, + ) + api.disable(user) + with pytest.raises(UserNotActive): + api.authenticate_user('test@test.test', 'test@test.test') + def test_unit__authenticate_user___err__wrong_password(self): api = UserApi( current_user=None,