From bf69c870cdd88d2da042fe68d49f1cfceababcb2 Mon Sep 17 00:00:00 2001 From: Andrey Kovalev Date: Wed, 17 Feb 2021 20:43:57 +0300 Subject: [PATCH 1/7] Add rights management backend --- access_controller/urls.py | 5 +++- main/templates/pages/work.html | 31 ++++++++++++++++++++ main/views.py | 53 +++++++++++++++++++++++++++++++++- 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 main/templates/pages/work.html diff --git a/access_controller/urls.py b/access_controller/urls.py index b2603b2..bf79ff6 100644 --- a/access_controller/urls.py +++ b/access_controller/urls.py @@ -20,7 +20,7 @@ from django.urls import path, include from access_controller import settings from access_controller.settings import DEBUG -from main.views import main_page, profile_page, CustomRegistrationView +from main.views import main_page, profile_page, CustomRegistrationView, work_page, work_hand_over, work_become_engineer urlpatterns = [ path('admin/', admin.site.urls, name='admin'), @@ -30,4 +30,7 @@ urlpatterns = [ path('accounts/login/', LoginView.as_view(extra_context={}), name='login'), # TODO add extra context path('accounts/', include('django.contrib.auth.urls')), path('accounts/', include('django_registration.backends.one_step.urls')), + path('work/', work_page, name="work"), + path('work/hand_over/', work_hand_over, name="work_hand_over"), + path('work/become_engineer/', work_become_engineer, name="work_become_engineer"), ] diff --git a/main/templates/pages/work.html b/main/templates/pages/work.html new file mode 100644 index 0000000..5f49290 --- /dev/null +++ b/main/templates/pages/work.html @@ -0,0 +1,31 @@ +{% extends 'base/base.html' %} + +{% load static %} + + +{% block title %}{{ pagename }}{% endblock %} + + +{% block heading %}Управление правами{% endblock %} + +{% block content %} +
+

Инженеры

+{%for engineer in engineers%} + {{ engineer.name }} +{% endfor %} + +

Агенты

+{%for agent in agents%} + {{ agent.name }} +{% endfor %} + +
+ +{% if role == "engineer" %} + Сдать смену +{% else %} + Запросить права инженера +{% endif %} + +{% endblock %} diff --git a/main/views.py b/main/views.py index d9f8fd9..ee22299 100644 --- a/main/views.py +++ b/main/views.py @@ -1,4 +1,5 @@ -from django.shortcuts import render +from django.shortcuts import render, redirect, reverse +from django.http import HttpResponseRedirect from django.urls import reverse_lazy from main.extra_func import set_and_get_name, set_and_get_email, load_and_get_image, set_and_get_role, check_user_exist, \ @@ -12,6 +13,11 @@ from django_registration.views import RegistrationView from django.contrib.auth.decorators import login_required from zenpy import Zenpy +from zenpy.lib.api_objects import User as ZenpyUser + +from .models import UserProfile + +import os class CustomRegistrationView(RegistrationView): @@ -75,6 +81,51 @@ def profile_page(request): } return render(request, 'pages/profile.html', context) +def auth_user(request): + admin_creds = { + 'email': os.environ.get('Admin_email'), + 'subdomain': 'ngenix1612197338', + 'token': os.environ.get('Oauth_token'), + } + admin = Zenpy(**admin_creds) + zenpy_user: ZenpyUser = admin.users.search(request.user.email).values[0] + return zenpy_user, admin + +@login_required() +def work_page(request, id): + if request.user.is_authenticated and request.user.id == id: + zenpy_user, _ = auth_user(request) + + context = { + 'engineers': UserProfile.objects.filter(role=1), + 'agents': UserProfile.objects.filter(role=0), + 'role': zenpy_user.role, + 'pagename': 'Управление правами' + } + return render(request, 'pages/work.html', context) + return redirect("login") + +@login_required() +def work_hand_over(request): + zenpy_user, admin = auth_user(request) + if request.user.is_authenticated and zenpy_user.role == "end-user": + zenpy_user.role = "agent" + admin.users.update(zenpy_user) + request.user.userprofile.role = 0 + request.user.userprofile.save() + return HttpResponseRedirect(reverse('work', args=(request.user.id, ))) + + +@login_required() +def work_become_engineer(request): + zenpy_user, admin = auth_user(request) + if request.user.is_authenticated and zenpy_user.role == "agent": + zenpy_user.role = "end-user" + admin.users.update(zenpy_user) + request.user.userprofile.role = 1 + request.user.userprofile.save() + return HttpResponseRedirect(reverse('work', args=(request.user.id, ))) + def main_page(request): return render(request, 'pages/index.html') From 8ba662c0caecb5265cf2b891c0365157ecaf24a4 Mon Sep 17 00:00:00 2001 From: Andrey Kovalev Date: Wed, 17 Feb 2021 21:38:41 +0300 Subject: [PATCH 2/7] Fix role end-user -> admin --- main/templates/pages/work.html | 2 +- main/views.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main/templates/pages/work.html b/main/templates/pages/work.html index 5f49290..4719492 100644 --- a/main/templates/pages/work.html +++ b/main/templates/pages/work.html @@ -22,7 +22,7 @@
-{% if role == "engineer" %} +{% if role == "admin" %} Сдать смену {% else %} Запросить права инженера diff --git a/main/views.py b/main/views.py index ee22299..21e5f97 100644 --- a/main/views.py +++ b/main/views.py @@ -108,7 +108,7 @@ def work_page(request, id): @login_required() def work_hand_over(request): zenpy_user, admin = auth_user(request) - if request.user.is_authenticated and zenpy_user.role == "end-user": + if request.user.is_authenticated and zenpy_user.role == "admin": zenpy_user.role = "agent" admin.users.update(zenpy_user) request.user.userprofile.role = 0 @@ -120,7 +120,7 @@ def work_hand_over(request): def work_become_engineer(request): zenpy_user, admin = auth_user(request) if request.user.is_authenticated and zenpy_user.role == "agent": - zenpy_user.role = "end-user" + zenpy_user.role = "admin" admin.users.update(zenpy_user) request.user.userprofile.role = 1 request.user.userprofile.save() From b1d42ece61614080af92453548788b5a0fc00822 Mon Sep 17 00:00:00 2001 From: Andrey Kovalev Date: Thu, 25 Feb 2021 19:01:09 +0300 Subject: [PATCH 3/7] Fixed name parametrs in env --- main/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/views.py b/main/views.py index 917d5fa..5d362d7 100644 --- a/main/views.py +++ b/main/views.py @@ -98,9 +98,9 @@ def profile_page(request): def auth_user(request): admin_creds = { - 'email': os.environ.get('Admin_email'), + 'email': os.environ.get('ACCESS_CONTROLLER_API_EMAIL'), 'subdomain': 'ngenix1612197338', - 'token': os.environ.get('Oauth_token'), + 'token': os.environ.get('ACCESS_CONTROLLER_API_TOKEN'), } admin = Zenpy(**admin_creds) zenpy_user: ZenpyUser = admin.users.search(request.user.email).values[0] From 33375967493c7f13b5d75ac1ef424b1f7ff39b76 Mon Sep 17 00:00:00 2001 From: Andrew Smirnov Date: Thu, 25 Feb 2021 20:05:04 +0300 Subject: [PATCH 4/7] Remove useless auth check --- main/views.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/main/views.py b/main/views.py index 5d362d7..c1e7794 100644 --- a/main/views.py +++ b/main/views.py @@ -1,4 +1,3 @@ - from django.shortcuts import render, redirect, reverse from django.http import HttpResponseRedirect @@ -17,7 +16,6 @@ from django_registration.views import RegistrationView from django.contrib.auth.decorators import login_required import logging - from zenpy import Zenpy from zenpy.lib.api_objects import User as ZenpyUser @@ -26,7 +24,6 @@ from .models import UserProfile import os - class CustomRegistrationView(RegistrationView): """ Отображение и логика работы страницы регистрации пользователя @@ -96,6 +93,7 @@ def profile_page(request): } return render(request, 'pages/profile.html', context) + def auth_user(request): admin_creds = { 'email': os.environ.get('ACCESS_CONTROLLER_API_EMAIL'), @@ -106,11 +104,12 @@ def auth_user(request): zenpy_user: ZenpyUser = admin.users.search(request.user.email).values[0] return zenpy_user, admin + @login_required() def work_page(request, id): if request.user.is_authenticated and request.user.id == id: zenpy_user, _ = auth_user(request) - + context = { 'engineers': UserProfile.objects.filter(role=1), 'agents': UserProfile.objects.filter(role=0), @@ -120,26 +119,27 @@ def work_page(request, id): return render(request, 'pages/work.html', context) return redirect("login") + @login_required() def work_hand_over(request): zenpy_user, admin = auth_user(request) - if request.user.is_authenticated and zenpy_user.role == "admin": + if zenpy_user.role == "admin": zenpy_user.role = "agent" admin.users.update(zenpy_user) request.user.userprofile.role = 0 request.user.userprofile.save() - return HttpResponseRedirect(reverse('work', args=(request.user.id, ))) + return HttpResponseRedirect(reverse('work', args=(request.user.id,))) @login_required() def work_become_engineer(request): zenpy_user, admin = auth_user(request) - if request.user.is_authenticated and zenpy_user.role == "agent": + if zenpy_user.role == "agent": zenpy_user.role = "admin" admin.users.update(zenpy_user) request.user.userprofile.role = 1 request.user.userprofile.save() - return HttpResponseRedirect(reverse('work', args=(request.user.id, ))) + return HttpResponseRedirect(reverse('work', args=(request.user.id,))) def main_page(request): From f7522c9c69db883e92b042f70cd8d84dffecd2f7 Mon Sep 17 00:00:00 2001 From: Andrey Kovalev Date: Sun, 28 Feb 2021 17:27:07 +0300 Subject: [PATCH 5/7] Fix page html code --- main/templates/pages/profile.html | 2 +- main/templates/pages/work.html | 60 ++++++------------------------- main/views.py | 18 +++++----- 3 files changed, 20 insertions(+), 60 deletions(-) diff --git a/main/templates/pages/profile.html b/main/templates/pages/profile.html index bfd8cd7..6c6ecd2 100644 --- a/main/templates/pages/profile.html +++ b/main/templates/pages/profile.html @@ -45,7 +45,7 @@
- + Запросить права доступа
{% endblock %} diff --git a/main/templates/pages/work.html b/main/templates/pages/work.html index c6f2e01..2fcb4cf 100644 --- a/main/templates/pages/work.html +++ b/main/templates/pages/work.html @@ -2,39 +2,10 @@ {% load static %} - - {% block title %}{{ pagename }}{% endblock %} - {% block heading %}Управление правами{% endblock %} -{% block content %} -
-

Инженеры

-{%for engineer in engineers%} - {{ engineer.name }} -{% endfor %} - -

Агенты

-{%for agent in agents%} - {{ agent.name }} -{% endfor %} - -
- -{% if role == "admin" %} - Сдать смену -{% else %} - Запросить права инженера -{% endif %} - -{% endblock %} - -{% block title %}{{ pagename }}{% endblock %} - -{% block heading %}Управление{% endblock %} - {% block extra_css %} {% endblock %} @@ -52,24 +23,15 @@ - - - + - - - - - - - - - - - - - + {% for engineer in engineers %} + + + + + {% endfor %}
IDemailExpiration DateName(link to profile)Name
1big_boss123@example.ru19:30 18.02.21Иван Иванов
2gachi_cool456@example.ru21:00 18.02.21Пётр Петров
{{ engineer.id }}{{ engineer.name }}
@@ -81,22 +43,22 @@
инженеров:
- 13 + {{ engineers|length }}
легких агентов:
- 22 + {{ agents|length }}
- - + Получить права инженера + Сдать права инженера
diff --git a/main/views.py b/main/views.py index c1e7794..70facff 100644 --- a/main/views.py +++ b/main/views.py @@ -88,6 +88,7 @@ def profile_page(request): 'email': user_profile.user.email, 'name': user_profile.name, 'role': user_profile.role, + 'id': user_profile.id, 'image_url': user_profile.image, 'pagename': 'Страница профиля' } @@ -107,13 +108,10 @@ def auth_user(request): @login_required() def work_page(request, id): - if request.user.is_authenticated and request.user.id == id: - zenpy_user, _ = auth_user(request) - + if request.user.id == id: context = { - 'engineers': UserProfile.objects.filter(role=1), - 'agents': UserProfile.objects.filter(role=0), - 'role': zenpy_user.role, + 'engineers': UserProfile.objects.filter(role="admin"), + 'agents': UserProfile.objects.filter(role="agent"), 'pagename': 'Управление правами' } return render(request, 'pages/work.html', context) @@ -123,10 +121,10 @@ def work_page(request, id): @login_required() def work_hand_over(request): zenpy_user, admin = auth_user(request) - if zenpy_user.role == "admin": + if zenpy_user.role == "admin" or zenpy_user.role == "end-user": zenpy_user.role = "agent" admin.users.update(zenpy_user) - request.user.userprofile.role = 0 + request.user.userprofile.role = "agent" request.user.userprofile.save() return HttpResponseRedirect(reverse('work', args=(request.user.id,))) @@ -134,10 +132,10 @@ def work_hand_over(request): @login_required() def work_become_engineer(request): zenpy_user, admin = auth_user(request) - if zenpy_user.role == "agent": + if zenpy_user.role == "agent" or zenpy_user.role == "end-user": zenpy_user.role = "admin" admin.users.update(zenpy_user) - request.user.userprofile.role = 1 + request.user.userprofile.role = "admin" request.user.userprofile.save() return HttpResponseRedirect(reverse('work', args=(request.user.id,))) From f50ce49d7766cd08432c7f97a6c37aec3f63242d Mon Sep 17 00:00:00 2001 From: Sokurov Idar Date: Tue, 2 Mar 2021 21:44:13 +0300 Subject: [PATCH 6/7] update login with email --- access_controller/auth.py | 19 +++++++++++++++++++ access_controller/settings.py | 6 ++++++ access_controller/urls.py | 5 +++-- main/extra_func.py | 15 +++++++++------ main/forms.py | 23 +++++++++++++++++++---- main/views.py | 18 ++++++++++++------ 6 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 access_controller/auth.py diff --git a/access_controller/auth.py b/access_controller/auth.py new file mode 100644 index 0000000..be707e1 --- /dev/null +++ b/access_controller/auth.py @@ -0,0 +1,19 @@ +from django.contrib.auth.backends import ModelBackend +from django.contrib.auth.models import User + + +class EmailAuthBackend(ModelBackend): + def authenticate(self, request, username=None, password=None, **kwargs): + try: + user = User.objects.get(email=username) + if user.check_password(password): + return user + return None + except User.DoesNotExist: + return None + + def get_user(self, user_id): + try: + return User.objects.get(pk=user_id) + except User.DoesNotExist: + return None diff --git a/access_controller/settings.py b/access_controller/settings.py index 480cfbd..8b4ec96 100644 --- a/access_controller/settings.py +++ b/access_controller/settings.py @@ -135,6 +135,12 @@ ACCOUNT_ACTIVATION_DAYS = 7 LOGIN_REDIRECT_URL = '/' LOGOUT_REDIRECT_URL = '/' + +# Название_приложения.Название_файла.Название_класса_обработчика +AUTHENTICATION_BACKENDS = [ + 'access_controller.auth.EmailAuthBackend', +] + # Logging system # https://docs.djangoproject.com/en/3.1/topics/logging/ LOGGING = { diff --git a/access_controller/urls.py b/access_controller/urls.py index 45781bf..b91e747 100644 --- a/access_controller/urls.py +++ b/access_controller/urls.py @@ -14,17 +14,18 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin +from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.views import LoginView from django.contrib.auth import views as auth_views from django.urls import path, include -from main.views import main_page, profile_page, CustomRegistrationView +from main.views import main_page, profile_page, CustomRegistrationView, CustomLoginView urlpatterns = [ path('admin/', admin.site.urls, name='admin'), path('', main_page, name='index'), path('accounts/profile/', profile_page, name='profile'), path('accounts/register/', CustomRegistrationView.as_view(), name='registration'), - path('accounts/login/', LoginView.as_view(extra_context={}), name='login'), # TODO add extra context + path('accounts/login/', CustomLoginView.as_view(extra_context={}), name='login',), # TODO add extra context path('accounts/', include('django.contrib.auth.urls')), path('accounts/', include('django_registration.backends.activation.urls')), path('accounts/login/', include('django.contrib.auth.urls')), diff --git a/main/extra_func.py b/main/extra_func.py index 9e0a564..3ce296a 100644 --- a/main/extra_func.py +++ b/main/extra_func.py @@ -26,6 +26,12 @@ class ZendeskAdmin: email = os.getenv('ACCESS_CONTROLLER_API_EMAIL') token = os.getenv('ACCESS_CONTROLLER_API_TOKEN') password = os.getenv('ACCESS_CONTROLLER_API_PASSWORD') + _instance=None + + def __new__(cls, *args, **kwargs): + if cls._instance is None: + cls._instance = super().__new__(cls) + return cls._instance def __init__(self): self.create_admin() @@ -73,10 +79,7 @@ class ZendeskAdmin: def get_user_image(self, email: str) -> str: """ - Функция **get_user_image** возвращает аватар пользователя - - :param user_image: Аватар пользователя - :type user_image: :class:`img` + Функция **get_user_image** возвращает url-ссылку на аватар пользователя """ user = self.admin.users.search(email).values[0] return user.photo['content_url'] if user.photo else None @@ -86,7 +89,7 @@ class ZendeskAdmin: def get_user_org(self, email: str) -> str: user = self.admin.users.search(email).values[0] - return user.organization.name + return user.organization.name def create_admin(self) -> None: """ @@ -140,7 +143,7 @@ def check_user_exist(email: str) -> bool: return ZendeskAdmin().check_user(email) -def get_user_organization(email: str) -> bool: +def get_user_organization(email: str) -> str: """ Функция возвращает организацию пользователя diff --git a/main/forms.py b/main/forms.py index 6a058bb..e33e1ae 100644 --- a/main/forms.py +++ b/main/forms.py @@ -1,4 +1,5 @@ from django import forms +from django.contrib.auth.forms import AuthenticationForm from django_registration.forms import RegistrationFormUniqueEmail @@ -6,11 +7,7 @@ class CustomRegistrationForm(RegistrationFormUniqueEmail): """ Форма для регистрации :class:`django_registration.forms.RegistrationFormUniqueEmail` с добавлением bootstrap-класса 'form-control' - - :param password_zen: Поле для ввода пароля от Zendesk - :type password_zen: :class:`django.forms.CharField` """ - def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for visible in self.visible_fields(): @@ -24,3 +21,21 @@ class CustomRegistrationForm(RegistrationFormUniqueEmail): class Meta(RegistrationFormUniqueEmail.Meta): fields = RegistrationFormUniqueEmail.Meta.fields + + +class CustomAuthenticationForm(AuthenticationForm): + """ + Форма для авторизации :class:`django.contrib.auth.forms.AuthenticationForm` + с изменением поля username на email + """ + username = forms.CharField( + label="Электронная почта", + widget=forms.EmailInput(), + ) + error_messages = { + 'invalid_login': + "Пожалуйста, введите правильные электронную почту и пароль. Оба поля " + "могут быть чувствительны к регистру." + , + 'inactive': "Аккаунт не активен.", + } diff --git a/main/views.py b/main/views.py index a73017f..8fb892a 100644 --- a/main/views.py +++ b/main/views.py @@ -1,18 +1,17 @@ +import logging + from django.contrib.auth.decorators import login_required from django.contrib.auth.forms import PasswordResetForm from django.contrib.auth.models import User from django.contrib.auth.tokens import default_token_generator +from django.contrib.auth.views import LoginView from django.shortcuts import render from django.urls import reverse_lazy -from django_registration.backends.one_step.views import RegistrationView +from django_registration.views import RegistrationView from access_controller.settings import EMAIL_HOST_USER from main.extra_func import check_user_exist, update_profile, get_user_organization -from main.forms import CustomRegistrationForm -from django_registration.views import RegistrationView -from django.contrib.auth.decorators import login_required -import logging - +from main.forms import CustomRegistrationForm, CustomAuthenticationForm class CustomRegistrationView(RegistrationView): @@ -89,3 +88,10 @@ def main_page(request): logger = logging.getLogger('main.index') logger.info('Index page opened') return render(request, 'pages/index.html') + + +class CustomLoginView(LoginView): + """ + Отображение страницы авторизации пользователя + """ + form_class = CustomAuthenticationForm From 6cdd787cc8fcf4f8240fc3efdeeeb631d678ac32 Mon Sep 17 00:00:00 2001 From: Artyom Kravchenko Date: Wed, 3 Mar 2021 13:46:44 +0300 Subject: [PATCH 7/7] add typehints to requirements --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b2e4c64..4161b6c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,4 @@ django_registration==3.1.1 # Documentation Sphinx==3.4.3 sphinx-rtd-theme==0.5.1 - +sphinx-autodoc-typehints==1.11.1