diff --git a/requirements.txt b/requirements.txt index b789cd5e..d66cdd83 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,3 +16,6 @@ django-jinja>=0.23 jinja2==2.7.1 pygments>=1.6 django-sites==0.4 + +# Uncomment it if you are using python < 3.4 +#enum34==0.9.23 diff --git a/taiga/auth/api.py b/taiga/auth/api.py index b543346b..05fb85db 100644 --- a/taiga/auth/api.py +++ b/taiga/auth/api.py @@ -1,137 +1,109 @@ -# -*- coding: utf-8 -*- +from functools import partial +from enum import Enum -from django.db.models.loading import get_model -from django.db.models import Q -from django.contrib.auth import logout, login, authenticate -from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext_lazy as _ from rest_framework.response import Response from rest_framework.permissions import AllowAny -from rest_framework import status, viewsets +from rest_framework import status +from rest_framework import viewsets +from rest_framework import serializers + from taiga.base.decorators import list_route - -from taiga.domains.models import DomainMember -from taiga.domains import get_active_domain -from taiga.base.users.models import User, Role -from taiga.base.users.serializers import UserSerializer from taiga.base import exceptions as exc -from taiga.base import auth +from taiga.users.services import get_and_validate_user +from taiga.domains.services import is_public_register_enabled_for_domain -from .serializers import (PublicRegisterSerializer, - PrivateRegisterSerializer, - PrivateGenericRegisterSerializer, - PrivateRegisterExistingSerializer) +from .serializers import PublicRegisterSerializer +from .serializers import PrivateRegisterForExistingUserSerializer +from .serializers import PrivateRegisterForNewUserSerializer + +from .services import private_register_for_existing_user +from .services import private_register_for_new_user +from .services import public_register +from .services import make_auth_response_data + + +def _parse_data(data:dict, *, cls): + """ + Generic function for parse user data using + specified serializer on `cls` keyword parameter. + + Raises: RequestValidationError exception if + some errors found when data is validated. + + Returns the parsed data. + """ + + serializer = cls(data=data) + if not serializer.is_valid(): + raise exc.RequestValidationError(serializer.errors) + return serializer.data + +# Parse public register data +parse_public_register_data = partial(_parse_data, cls=PublicRegisterSerializer) + +# Parse private register data for existing user +parse_private_register_for_existing_user_data = \ + partial(_parse_data, cls=PrivateRegisterForExistingUserSerializer) + +# Parse private register data for new user +parse_private_register_for_new_user_data = \ + partial(_parse_data, cls=PrivateRegisterForNewUserSerializer) + + +class RegisterTypeEnum(Enum): + new_user = 1 + existing_user = 2 + + +def parse_register_type(userdata:dict) -> str: + """ + Parses user data and detects that register type is. + It returns RegisterTypeEnum value. + """ + # Create adhoc inner serializer for avoid parse + # manually the user data. + class _serializer(serializers.Serializer): + existing = serializers.BooleanField() + + instance = _serializer(data=userdata) + if not instance.is_valid(): + raise exc.RequestValidationError(instance.errors) + + if instance.data["existing"]: + return RegisterTypeEnum.existing_user + return RegisterTypeEnum.new_user class AuthViewSet(viewsets.ViewSet): permission_classes = (AllowAny,) - def _create_response(self, user): - serializer = UserSerializer(user) - response_data = serializer.data - - domain = get_active_domain() - response_data['is_site_owner'] = domain.user_is_owner(user) - response_data['is_site_staff'] = domain.user_is_staff(user) - response_data["auth_token"] = auth.get_token_for_user(user) - return response_data - - def _create_domain_member(self, user): - domain = get_active_domain() - - if domain.members.filter(user=user).count() == 0: - domain_member = DomainMember(domain=domain, user=user, email=user.email, - is_owner=False, is_staff=False) - domain_member.save() - - def _send_public_register_email(self, user): - context = {"user": user} - - mbuilder = MagicMailBuilder() - email = mbuilder.public_register_user(user.email, context) - email.send() - def _public_register(self, request): - if not request.domain.public_register: + if not is_public_register_enabled_for_domain(request.domain): raise exc.BadRequest(_("Public register is disabled for this domain.")) - serializer = PublicRegisterSerializer(data=request.DATA) - if not serializer.is_valid(): - raise exc.BadRequest(serializer.errors) + try: + data = parse_public_register_data(request.DATA) + user = public_register(request.domain, **data) + except exc.IntegrityError as e: + raise exc.BadRequest(e.detail) - data = serializer.data - - if User.objects.filter(Q(username=data["username"]) | Q(email=data["email"])).exists(): - raise exc.BadRequest(_("This username or email is already in use.")) - - user = User(username=data["username"], - first_name=data["first_name"], - last_name=data["last_name"], - email=data["email"]) - user.set_password(data["password"]) - user.save() - - self._create_domain_member(user) - #self._send_public_register_email(user) - - response_data = self._create_response(user) - return Response(response_data, status=status.HTTP_201_CREATED) - - def _send_private_register_email(self, user, **kwargs): - context = {"user": user} - context.update(kwargs) - - mbuilder = MagicMailBuilder() - email = mbuilder.private_register_user(user.email, context) - email.send() + data = make_auth_response_data(request.domain, user) + return Response(data, status=status.HTTP_201_CREATED) def _private_register(self, request): - base_serializer = PrivateGenericRegisterSerializer(data=request.DATA) - if not base_serializer.is_valid(): - raise exc.BadRequest(base_serializer.errors) - - membership_model = get_model("projects", "Membership") - try: - membership = membership_model.objects.get(token=base_serializer.data["token"]) - except membership_model.DoesNotExist as e: - raise exc.BadRequest(_("Invalid token")) from e - - if base_serializer.data["existing"]: - serializer = PrivateRegisterExistingSerializer(data=request.DATA) - if not serializer.is_valid(): - raise exc.BadRequest(serializer.errors) - - user = get_object_or_404(User, username=serializer.data["username"]) - if not user.check_password(serializer.data["password"]): - raise exc.BadRequest({"password": _("Incorrect password")}) + register_type = parse_register_type(request.DATA) + if register_type is RegisterTypeEnum.existing_user: + data = parse_private_register_for_existing_user_data(request.DATA) + user = private_register_for_existing_user(request.domain, **data) else: - serializer = PrivateRegisterSerializer(data=request.DATA) - if not serializer.is_valid(): - raise exc.BadRequest(serializer.errors) + data = parse_private_register_for_new_user_data(request.DATA) + user = private_register_for_new_user(request.domain, **data) - data = serializer.data - - if User.objects.filter(Q(username=data["username"]) | Q(email=data["email"])).exists(): - raise exc.BadRequest(_("This username or email is already in use.")) - - user = User(username=data["username"], - first_name=data["first_name"], - last_name=data["last_name"], - email=data["email"]) - user.set_password(data["password"]) - user.save() - - self._create_domain_member(user) - - membership.user = user - membership.save() - - #self._send_private_register_email(user, membership=membership) - - response_data = self._create_response(user) - return Response(response_data, status=status.HTTP_201_CREATED) + data = make_auth_response_data(request.domain, user) + return Response(data, status=status.HTTP_201_CREATED) @list_route(methods=["POST"], permission_classes=[AllowAny]) def register(self, request, **kwargs): @@ -140,7 +112,6 @@ class AuthViewSet(viewsets.ViewSet): return self._public_register(request) elif type == "private": return self._private_register(request) - raise exc.BadRequest(_("invalid register type")) # Login view: /api/v1/auth @@ -148,13 +119,6 @@ class AuthViewSet(viewsets.ViewSet): username = request.DATA.get('username', None) password = request.DATA.get('password', None) - try: - user = User.objects.get(username=username) - except User.DoesNotExist: - raise exc.BadRequest(_("Invalid username or password")) - - if not user.check_password(password): - raise exc.BadRequest(_("Invalid username or password")) - - response_data = self._create_response(user) - return Response(response_data, status=status.HTTP_200_OK) + user = get_and_validate_user(username=username, password=password) + data = make_auth_response_data(request.domain, user) + return Response(data, status=status.HTTP_200_OK) diff --git a/taiga/auth/serializers.py b/taiga/auth/serializers.py index 512873d8..bc020a43 100644 --- a/taiga/auth/serializers.py +++ b/taiga/auth/serializers.py @@ -14,16 +14,10 @@ class PublicRegisterSerializer(BaseRegisterSerializer): pass -class PrivateRegisterSerializer(BaseRegisterSerializer): - pass - - -class PrivateGenericRegisterSerializer(serializers.Serializer): +class PrivateRegisterForNewUserSerializer(BaseRegisterSerializer): token = serializers.CharField(max_length=255, required=True) - existing = serializers.BooleanField() - # existing = serializers.ChoiceField(choices=[("on", "on"), ("off", "off")]) - -class PrivateRegisterExistingSerializer(serializers.Serializer): +class PrivateRegisterForExistingUserSerializer(serializers.Serializer): username = serializers.CharField(max_length=200) password = serializers.CharField(min_length=4) + token = serializers.CharField(max_length=255, required=True) diff --git a/taiga/auth/services.py b/taiga/auth/services.py new file mode 100644 index 00000000..584b1aa3 --- /dev/null +++ b/taiga/auth/services.py @@ -0,0 +1,179 @@ +""" +This module contains a domain logic for authentication +process. It called services because in DDD says it. + +NOTE: Python doesn't have java limitations for "everytghing +should be contained in a class". Because of that, it +not uses clasess and uses simple functions. +""" + +from django.db.models.loading import get_model +from django.db.models import Q +from django.db import transaction as tx +from django.db import IntegrityError +from django.utils.translation import ugettext as _ + +from djmail.template_mail import MagicMailBuilder + +from taiga.base import exceptions as exc +from taiga.users.serializers import UserSerializer +from taiga.users.services import get_and_validate_user +from taiga.domains.services import (create_domain_member, + is_user_exists_on_domain) + +from .backends import get_token_for_user + + +def send_public_register_email(user) -> bool: + """ + Given a user, send public register welcome email + message to specified user. + """ + + context = {"user": user} + mbuilder = MagicMailBuilder() + email = mbuilder.public_register_user(user.email, context) + return bool(email.send()) + + +def send_private_register_email(user, **kwargs) -> bool: + """ + Given a user, send private register welcome + email message to specified user. + """ + context = {"user": user} + context.update(kwargs) + + mbuilder = MagicMailBuilder() + email = mbuilder.private_register_user(user.email, context) + return bool(email.send()) + + +def is_user_already_registred(*, username:str, email:str) -> bool: + """ + Checks if a specified user is already registred. + """ + + user_model = get_model("users", "User") + qs = user_model.objects.filter(Q(username=username) | + Q(email=email)) + return qs.exists() + + +def get_membership_by_token(token:str): + """ + Given a token, returns a membership instance + that matches with specified token. + + If not matches with any membership NotFound exception + is raised. + """ + membership_model = get_model("projects", "Membership") + qs = membership_model.objects.filter(token=token) + if len(qs) == 0: + raise exc.NotFound("Token not matches any member.") + return qs[0] + + +@tx.atomic +def public_register(domain, *, username:str, password:str, + email:str, first_name:str, last_name:str): + """ + Given a parsed parameters, try register a new user + knowing that it follows a public register flow. + + This can raise `exc.IntegrityError` exceptions in + case of conflics found. + + :returns: User + """ + + if is_user_already_registred(username=username, email=email): + raise exc.IntegrityError("User is already registred.") + + user_model = get_model("users", "User") + user = user_model(username=username, + email=email, + first_name=first_name, + last_name=last_name) + user.set_password(password) + user.save() + + if not is_user_exists_on_domain(domain, user): + create_domain_member(domain, user) + + # send_public_register_email(user) + return user + + +@tx.atomic +def private_register_for_existing_user(domain, *, token:str, username:str, password:str): + """ + Register works not only for register users, also serves for accept + inviatations for projects as existing user. + + Given a invitation token with parsed parameters, accept inviation + as existing user. + """ + + user = get_and_validate_user(username=username, password=password) + membership = get_membership_by_token(token) + + if not is_user_exists_on_domain(domain, user): + create_domain_member(domain, user) + + membership.user = user + membership.save(update_fields=["user"]) + + # send_private_register_email(user) + return user + + +@tx.atomic +def private_register_for_new_user(domain, *, token:str, username:str, email:str, + first_name:str, last_name:str, password:str): + """ + Given a inviation token, try register new user matching + the invitation token. + """ + + user_model = get_model("users", "User") + + if is_user_already_registred(username=username, email=email): + raise exc.WrongArguments(_("Username or Email is already in use.")) + + user = user_model(username=username, + email=email, + first_name=first_name, + last_name=last_name) + + user.set_password(password) + try: + user.save() + except IntegrityError: + raise exc.IntegrityError(_("Error on creating new user.")) + + if not is_user_exists_on_domain(domain, user): + create_domain_member(domain, user) + + membership = get_membership_by_token(token) + membership.user = user + membership.save(update_fields=["user"]) + + return user + + +def make_auth_response_data(domain, user) -> dict: + """ + Given a domain and user, creates data structure + using python dict containing a representation + of the logged user. + """ + serializer = UserSerializer(user) + data = dict(serializer.data) + + data['is_site_owner'] = domain.user_is_owner(user) + data['is_site_staff'] = domain.user_is_staff(user) + data["auth_token"] = get_token_for_user(user) + + return data diff --git a/taiga/auth/tests/tests_auth.py b/taiga/auth/tests/tests_auth.py index 4adb3f6b..31a751ac 100644 --- a/taiga/auth/tests/tests_auth.py +++ b/taiga/auth/tests/tests_auth.py @@ -14,16 +14,19 @@ from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from taiga import urls -from taiga.base import auth -from taiga.base.users.tests import create_user, create_domain +from taiga.users.tests import create_user, create_domain from taiga.projects.tests import create_project from taiga.domains.models import Domain, DomainMember from taiga.projects.models import Membership +from taiga.auth.backends import Token as TokenAuthBackend +from taiga.auth.backends import get_token_for_user + + class TestAuthView(viewsets.ViewSet): - authentication_classes = (auth.Token,) + authentication_classes = (TokenAuthBackend,) permission_classes = (IsAuthenticated,) def get(self, request, *args, **kwargs): @@ -37,6 +40,7 @@ urls.urlpatterns += patterns("", class TokenAuthTests(test.TestCase): fixtures = ["initial_domains.json",] + def setUp(self): self.user1 = create_user(1) @@ -45,9 +49,9 @@ class TokenAuthTests(test.TestCase): self.assertEqual(response.status_code, 401) def test_token_auth_02(self): - token = auth.get_token_for_user(self.user1) + token = get_token_for_user(self.user1) response = self.client.get(reverse("test-token-auth"), - HTTP_AUTHORIZATION="Bearer {}".format(token)) + HTTP_AUTHORIZATION="Bearer {}".format(token)) self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b'"ok"') diff --git a/taiga/base/exceptions.py b/taiga/base/exceptions.py index 290c87cd..34881713 100644 --- a/taiga/base/exceptions.py +++ b/taiga/base/exceptions.py @@ -14,7 +14,7 @@ from .utils.json import to_json class BaseException(exceptions.APIException): status_code = status.HTTP_400_BAD_REQUEST - default_detail = _('Unexpected error') + default_detail = _("Unexpected error") def __init__(self, detail=None): self.detail = detail or self.default_detail @@ -26,7 +26,7 @@ class NotFound(BaseException): """ status_code = status.HTTP_404_NOT_FOUND - default_detail = _('Not found.') + default_detail = _("Not found.") class NotSupported(BaseException): @@ -39,7 +39,7 @@ class BadRequest(BaseException): Exception used on bad arguments detected on api view. """ - default_detail = _('Wrong arguments.') + default_detail = _("Wrong arguments.") class WrongArguments(BaseException): @@ -47,7 +47,11 @@ class WrongArguments(BaseException): Exception used on bad arguments detected on service. This is same as `BadRequest`. """ - default_detail = _('Wrong arguments.') + default_detail = _("Wrong arguments.") + + +class RequestValidationError(BaseException): + default_detail = _("Data validation error") class PermissionDenied(exceptions.PermissionDenied): @@ -58,6 +62,11 @@ class PermissionDenied(exceptions.PermissionDenied): pass +class IntegrityError(BaseException): + status_code = status.HTTP_400_BAD_REQUEST + default_detail = _("Integrity Error for wrong or invalid arguments") + + class PreconditionError(BaseException): """ Error raised on precondition method on viewset. @@ -108,20 +117,20 @@ def exception_handler(exc): if isinstance(exc, exceptions.APIException): headers = {} - if getattr(exc, 'auth_header', None): - headers['WWW-Authenticate'] = exc.auth_header - if getattr(exc, 'wait', None): - headers['X-Throttle-Wait-Seconds'] = '%d' % exc.wait + if getattr(exc, "auth_header", None): + headers["WWW-Authenticate"] = exc.auth_header + if getattr(exc, "wait", None): + headers["X-Throttle-Wait-Seconds"] = "%d" % exc.wait detail = format_exception(exc) return Response(detail, status=exc.status_code, headers=headers) elif isinstance(exc, Http404): - return Response({'_error_message': _('Not found')}, + return Response({"_error_message": _("Not found")}, status=status.HTTP_404_NOT_FOUND) elif isinstance(exc, DjangoPermissionDenied): - return Response({'_error_message': _('Permission denied')}, + return Response({"_error_message": _("Permission denied")}, status=status.HTTP_403_FORBIDDEN) # Note: Unhandled exceptions will raise a 500 error. diff --git a/taiga/domains/serializers.py b/taiga/domains/serializers.py index 6c307630..1537aa04 100644 --- a/taiga/domains/serializers.py +++ b/taiga/domains/serializers.py @@ -13,7 +13,7 @@ # along with this program. If not, see . from rest_framework import serializers -from taiga.base.users.serializers import UserSerializer +from taiga.users.serializers import UserSerializer from .models import Domain, DomainMember diff --git a/taiga/domains/services.py b/taiga/domains/services.py new file mode 100644 index 00000000..96a65938 --- /dev/null +++ b/taiga/domains/services.py @@ -0,0 +1,49 @@ +""" +This module contains a domain logic for domains application. +""" + +from django.db.models.loading import get_model +from django.db import transaction as tx +from django.db import IntegrityError + +from taiga.base import exceptions as exc + + +def is_user_exists_on_domain(domain, user) -> bool: + """ + Checks if user is alredy exists on domain. + """ + return domain.members.filter(user=user).exists() + + +def is_public_register_enabled_for_domain(domain) -> bool: + """ + Checks if a specified domain have public register + activated. + + The implementation is very simple but it encapsulates + request attribute access into more semantic function + call. + """ + return domain.public_register + + +@tx.atomic +def create_domain_member(domain, user): + """ + Given a domain and user, add user as member to + specified domain. + + :returns: DomainMember + """ + domain_member_model = get_model("domains", "DomainMember") + + try: + domain_member = domain_member_model(domain=domain, user=user, + email=user.email, is_owner=False, + is_staff=False) + domain_member.save() + except IntegrityError: + raise exc.IntegrityError("User is already member in a site") + + return domain_member diff --git a/taiga/events/tests.py b/taiga/events/tests.py index e21f7eab..b8c02be1 100644 --- a/taiga/events/tests.py +++ b/taiga/events/tests.py @@ -18,7 +18,7 @@ from django.http import HttpResponse from taiga.projects.tests import create_project from taiga.projects.issues.tests import create_issue -from taiga.base.users.tests import create_user +from taiga.users.tests import create_user from . import middleware as mw from . import changes as ch diff --git a/taiga/projects/admin.py b/taiga/projects/admin.py index 358ce456..d7959c48 100644 --- a/taiga/projects/admin.py +++ b/taiga/projects/admin.py @@ -18,7 +18,7 @@ from django.contrib import admin from django.contrib.contenttypes import generic from taiga.projects.milestones.admin import MilestoneInline -from taiga.base.users.admin import RoleInline +from taiga.users.admin import RoleInline from . import models import reversion diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 51d35a38..0299c19e 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -32,8 +32,9 @@ from taiga.base import filters from taiga.base import exceptions as exc from taiga.base.decorators import list_route, detail_route from taiga.base.permissions import has_project_perm -from taiga.base.api import ModelCrudViewSet, RetrieveModelMixin -from taiga.base.users.models import Role +from taiga.base.api import ModelCrudViewSet, ModelListViewSet, RetrieveModelMixin +from taiga.users.models import Role +from taiga.projects.aggregates.tags import get_all_tags from . import serializers from . import models diff --git a/taiga/projects/issues/tests/tests_api.py b/taiga/projects/issues/tests/tests_api.py index 6127a958..b9b193f6 100644 --- a/taiga/projects/issues/tests/tests_api.py +++ b/taiga/projects/issues/tests/tests_api.py @@ -6,7 +6,7 @@ from django import test from django.core import mail from django.core.urlresolvers import reverse -from taiga.base.users.tests import create_user +from taiga.users.tests import create_user from taiga.projects.tests import create_project, add_membership from taiga.projects.milestones.tests import create_milestone from taiga.projects.issues.models import Issue diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 983272f1..08a9fe22 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -23,7 +23,7 @@ from django.contrib.contenttypes.models import ContentType from sampledatahelper.helper import SampleDataHelper -from taiga.base.users.models import * +from taiga.users.models import * from taiga.projects.models import * from taiga.projects.milestones.models import * from taiga.projects.userstories.models import * diff --git a/taiga/projects/milestones/tests/tests_api.py b/taiga/projects/milestones/tests/tests_api.py index 5655443d..db0a5c00 100644 --- a/taiga/projects/milestones/tests/tests_api.py +++ b/taiga/projects/milestones/tests/tests_api.py @@ -6,7 +6,7 @@ from django import test from django.core import mail from django.core.urlresolvers import reverse -from taiga.base.users.tests import create_user +from taiga.users.tests import create_user from taiga.projects.tests import create_project, add_membership from taiga.projects.milestones.models import Milestone diff --git a/taiga/projects/models.py b/taiga/projects/models.py index 59b59609..1ac45bd5 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -32,14 +32,19 @@ from django.utils import timezone from picklefield.fields import PickledObjectField +from taiga.users.models import Role from taiga.domains.models import DomainMember from taiga.projects.userstories.models import UserStory from taiga.base.utils.slug import slugify_uniquely from taiga.base.utils.dicts import dict_sum -from taiga.base.users.models import Role from . import choices +# FIXME: this should to be on choices module (?) +VIDEOCONFERENCES_CHOICES = ( + ('appear-in', 'AppearIn'), + ('talky', 'Talky'), +) class Membership(models.Model): # This model stores all project memberships. Also diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 70ed30c3..749b466d 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -14,15 +14,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from os import path from rest_framework import serializers from taiga.base.serializers import PickleField -from taiga.base.users.models import Role +from taiga.users.models import Role from . import models -from os import path - class AttachmentSerializer(serializers.ModelSerializer): name = serializers.SerializerMethodField("get_name") diff --git a/taiga/projects/tasks/tests/tests_api.py b/taiga/projects/tasks/tests/tests_api.py index da45a70b..f38f46e2 100644 --- a/taiga/projects/tasks/tests/tests_api.py +++ b/taiga/projects/tasks/tests/tests_api.py @@ -8,7 +8,7 @@ from django.core.urlresolvers import reverse import reversion -from taiga.base.users.tests import create_user +from taiga.users.tests import create_user from taiga.projects.tests import create_project, add_membership from taiga.projects.milestones.tests import create_milestone from taiga.projects.userstories.tests import create_userstory diff --git a/taiga/projects/tests/tests_api.py b/taiga/projects/tests/tests_api.py index f95e3c91..8ba82add 100644 --- a/taiga/projects/tests/tests_api.py +++ b/taiga/projects/tests/tests_api.py @@ -7,10 +7,12 @@ from django.core.urlresolvers import reverse from django.core import mail from django.db.models import get_model -from taiga.base.users.tests import create_user +from taiga.users.tests import create_user from taiga.projects.models import Project, Membership -from . import create_project, add_membership +from . import create_project +from . import add_membership + class ProfileTestCase(test.TestCase): fixtures = ["initial_domains.json"] diff --git a/taiga/projects/tests/tests_notifications.py b/taiga/projects/tests/tests_notifications.py index b889637a..81fe1ce3 100644 --- a/taiga/projects/tests/tests_notifications.py +++ b/taiga/projects/tests/tests_notifications.py @@ -7,12 +7,13 @@ from django.core.urlresolvers import reverse from django.core import mail from django.db.models import get_model -from taiga.base.users.tests import create_user +from taiga.users.tests import create_user from taiga.projects.models import Project, Membership from taiga.projects.issues.tests import create_issue from taiga.projects.tasks.tests import create_task -from . import create_project, add_membership +from . import create_project +from . import add_membership class AllProjectEventsNotificationsTestCase(test.TestCase): fixtures = ["initial_domains.json"] diff --git a/taiga/projects/userstories/tests/tests_api.py b/taiga/projects/userstories/tests/tests_api.py index dd933dc1..f0773019 100644 --- a/taiga/projects/userstories/tests/tests_api.py +++ b/taiga/projects/userstories/tests/tests_api.py @@ -4,7 +4,7 @@ from django import test from django.core import mail from django.core.urlresolvers import reverse -from taiga.base.users.tests import create_user +from taiga.users.tests import create_user from taiga.projects.tests import create_project, add_membership from taiga.projects.milestones.tests import create_milestone from taiga.projects.userstories.models import UserStory diff --git a/taiga/projects/userstories/tests/tests_services.py b/taiga/projects/userstories/tests/tests_services.py index 54dc416c..0d7f9a81 100644 --- a/taiga/projects/userstories/tests/tests_services.py +++ b/taiga/projects/userstories/tests/tests_services.py @@ -1,15 +1,12 @@ -# -*- coding: utf-8 -*- - import json - from django import test -from taiga.base.users.tests import create_user +from taiga.users.tests import create_user from taiga.projects.tests import create_project -from . import create_userstory from .. import services from .. import models +from . import create_userstory class UserStoriesServiceTestCase(test.TestCase): diff --git a/taiga/projects/wiki/tests/tests_api.py b/taiga/projects/wiki/tests/tests_api.py index 9e982fc8..8f39b636 100644 --- a/taiga/projects/wiki/tests/tests_api.py +++ b/taiga/projects/wiki/tests/tests_api.py @@ -6,7 +6,7 @@ from django import test from django.core import mail from django.core.urlresolvers import reverse -from taiga.base.users.tests import create_user +from taiga.users.tests import create_user from taiga.projects.tests import create_project, add_membership from taiga.projects.wiki.models import WikiPage diff --git a/taiga/routers.py b/taiga/routers.py index 3a180ae2..d9f50b27 100644 --- a/taiga/routers.py +++ b/taiga/routers.py @@ -16,32 +16,37 @@ from taiga.base import routers +from taiga.auth.api import AuthViewSet +from taiga.users.api import UsersViewSet, PermissionsViewSet +from taiga.base.searches.api import SearchViewSet +from taiga.base.resolver.api import ResolverViewSet +from taiga.projects.api import (ProjectViewSet, MembershipViewSet, InvitationViewSet, + UserStoryStatusViewSet, PointsViewSet, TaskStatusViewSet, + IssueStatusViewSet, IssueTypeViewSet, PriorityViewSet, + SeverityViewSet, ProjectAdminViewSet, RolesViewSet) #, QuestionStatusViewSet) +from taiga.domains.api import DomainViewSet, DomainMembersViewSet +from taiga.projects.milestones.api import MilestoneViewSet +from taiga.projects.userstories.api import UserStoryViewSet, UserStoryAttachmentViewSet +from taiga.projects.tasks.api import TaskViewSet, TaskAttachmentViewSet +from taiga.projects.issues.api import IssueViewSet, IssueAttachmentViewSet +#from taiga.projects.questions.api import QuestionViewSet, QuestionAttachmentViewSet +#from taiga.projects.documents.api import DocumentViewSet, DocumentAttachmentViewSet +from taiga.projects.wiki.api import WikiViewSet, WikiAttachmentViewSet + + router = routers.DefaultRouter(trailing_slash=False) - -# Users & Auth -from taiga.base.users.api import UsersViewSet -from taiga.base.users.api import PermissionsViewSet -from taiga.base.auth.api import AuthViewSet - +# taiga.users router.register(r"users", UsersViewSet, base_name="users") router.register(r"permissions", PermissionsViewSet, base_name="permissions") router.register(r"auth", AuthViewSet, base_name="auth") # Resolver & Search -from taiga.base.resolver.api import ResolverViewSet -from taiga.base.searches.api import SearchViewSet - router.register(r"resolver", ResolverViewSet, base_name="resolver") router.register(r"search", SearchViewSet, base_name="search") - # Domains -from taiga.domains.api import DomainViewSet -from taiga.domains.api import DomainMembersViewSet -from taiga.projects.api import ProjectAdminViewSet - router.register(r"sites", DomainViewSet, base_name="sites") router.register(r"site-members", DomainMembersViewSet, base_name="site-members") router.register(r"site-projects", ProjectAdminViewSet, base_name="site-projects") diff --git a/taiga/users/services.py b/taiga/users/services.py new file mode 100644 index 00000000..8c73a751 --- /dev/null +++ b/taiga/users/services.py @@ -0,0 +1,27 @@ +""" +This model contains a domain logic for users application. +""" + +from django.db.models.loading import get_model +from taiga.base import exceptions as exc + + +def get_and_validate_user(*, username:str, password:str) -> bool: + """ + Check if user with username exists and specified + password matchs well with existing user password. + + if user is valid, user is returned else, corresponding + exception is raised. + """ + + user_model = get_model("users", "User") + qs = user_model.objects.filter(username=username) + if len(qs) == 0: + raise exc.WrongArguments("Username or password does not matches user.") + + user = qs[0] + if not user.check_password(password): + raise exc.WrongArguments("Username or password does not matches user.") + + return user