import logging import os from datetime import datetime from django.contrib.auth.decorators import login_required from django.contrib.auth.forms import PasswordResetForm from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.contrib.auth.models import User, Permission from django.contrib.auth.tokens import default_token_generator from django.contrib.auth.views import LoginView from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied from django.core.handlers.wsgi import WSGIRequest from django.http import HttpResponseRedirect, HttpResponse from django.shortcuts import render, get_list_or_404, redirect from django.urls import reverse_lazy, reverse from django.views.generic import FormView from django_registration.views import RegistrationView from zenpy import Zenpy from zenpy.lib.api_objects import User as ZenpyUser from access_controller.settings import EMAIL_HOST_USER, ZENDESK_ROLES from main.extra_func import check_user_exist, update_profile, get_user_organization, make_engineer, make_light_agent, \ get_users_list, StatisticData from main.forms import AdminPageUsers, CustomRegistrationForm, CustomAuthenticationForm, StatisticForm from .models import UserProfile class CustomRegistrationView(RegistrationView): """ Класс отображения и логики работы страницы регистрации пользователя :param form_class: Форма, которую необходимо заполнить для регистрации :type form_class: :class:`forms.CustomRegistrationForm` :param template_name: Указание пути к html-странице django регистрации :type template_name: :class:`str` :param success_url: Указание пути к html-странице завершения регистрации :type success_url: :class:`django.utils.functional.lazy..__proxy__` :param is_allowed: Определение зарегистрирован ли пользователь с введенным email на Zendesk и принадлежит ли он к организации SYSTEM :type is_allowed: :class:`bool` """ form_class = CustomRegistrationForm template_name = 'django_registration/registration_form.html' success_url = reverse_lazy('django_registration_complete') is_allowed = True def register(self, form: CustomRegistrationForm) -> User: """ Функция регистрации пользователя. 1. Ввод email пользователя, указанный на Zendesk 2. В случае если пользователь с данным паролем зарегистрирован на Zendesk и относится к организации SYSTEM, происходит сброс ссылки с установлением пароля на указанный email 3. Создается пользователь class User, а также его профиль. :param form: Email пользователя на Zendesk :return: user """ self.is_allowed = True if check_user_exist(form.data['email']) and get_user_organization(form.data['email']) == 'SYSTEM': forms = PasswordResetForm(self.request.POST) if forms.is_valid(): opts = { 'use_https': self.request.is_secure(), 'token_generator': default_token_generator, 'from_email': EMAIL_HOST_USER, 'email_template_name': 'registration/password_reset_email.html', 'subject_template_name': 'registration/password_reset_subject.txt', 'request': self.request, 'html_email_template_name': None, 'extra_email_context': None, } user = User.objects.create_user( username=form.data['email'], email=form.data['email'], password=User.objects.make_random_password(length=50) ) forms.save(**opts) update_profile(user.userprofile) self.set_permission(user) return user else: raise ValueError('Непредвиденная ошибка') else: self.is_allowed = False @staticmethod def set_permission(user: User) -> None: """ Функция дает разрешение на просмотр страница администратора, если пользователь имеет роль admin. :param user: авторизованный пользователь (получает разрешение, имея роль "admin") """ if user.userprofile.role == 'admin': content_type = ContentType.objects.get_for_model(UserProfile) permission = Permission.objects.get( codename='has_control_access', content_type=content_type, ) user.user_permissions.add(permission) def get_success_url(self, user: User = None) -> success_url: """ Функция возвращает url-адрес страницы, куда нужно перейти после успешной/неуспешной регистрации. Используется самой django-registration. :param user: пользователь, пытающийся зарегистроваться :return: адресация на страницу успешной регистрации """ if self.is_allowed: return reverse_lazy('password_reset_done') else: return reverse_lazy('django_registration_disallowed') @login_required() def profile_page(request: WSGIRequest) -> HttpResponse: """ Функция отображения страницы профиля. :param request: данные пользователя из БД :return: адресация на страницу пользователя """ user_profile: UserProfile = request.user.userprofile update_profile(user_profile) context = { 'profile': user_profile, 'pagename': 'Страница профиля' } return render(request, 'pages/profile.html', context) def auth_user(request: WSGIRequest) -> ZenpyUser: """ Функция возвращает профиль пользователя на Zendesk. :param request: email, subdomain и token пользователя :return: объект пользователя Zendesk """ admin_creds = { 'email': os.environ.get('ACCESS_CONTROLLER_API_EMAIL'), 'subdomain': 'ngenix1612197338', 'token': os.environ.get('ACCESS_CONTROLLER_API_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: WSGIRequest, id: int) -> HttpResponse: """ Функция отображения страницы "Управления правами" для текущего пользователя (login_required). :param request: объект пользователя :param id: id пользователя, используется для динамической адресации :return: адресация на страницу "Управления правами" (либо на страницу "Авторизации", если id и user.id не совпадают """ users = get_users_list() if request.user.id == id: engineers = [] light_agents = [] for user in users: if user.custom_role_id == ZENDESK_ROLES['engineer']: engineers.append(user) elif user.custom_role_id == ZENDESK_ROLES['light_agent']: light_agents.append(user) context = { 'engineers': engineers, 'agents': light_agents, 'pagename': 'Управление правами' } return render(request, 'pages/work.html', context) return redirect("login") @login_required() def work_hand_over(request: WSGIRequest) -> HttpResponseRedirect: """ Функция позволяет текущему пользователю (login_required) сдать права, а именно сменить в Zendesk роль с "engineer" на "light agent" и установить роль "agent" в БД. Действия выполняются, если исходная роль пользователя "engineer". :param request: данные текущего пользователя (login_required) :return: перезагрузка текущей страницы после выполнения смены роли """ zenpy_user, admin = auth_user(request) if zenpy_user.custom_role_id == ZENDESK_ROLES['engineer']: zenpy_user.custom_role_id = ZENDESK_ROLES['light_agent'] admin.users.update(zenpy_user) request.user.userprofile.role = "agent" request.user.userprofile.save() return HttpResponseRedirect(reverse('work', args=(request.user.id,))) @login_required() def work_become_engineer(request: WSGIRequest) -> HttpResponseRedirect: """ Функция меняет роль пользователя в Zendesk на "engineer" и присваевает роль "agent" в БД (в случае, если исходная роль пользователя была "light_agent"). :param request: данные текущего пользователя (login_required) :return: перезагрузка текущей страницы после выполнения смены роли """ zenpy_user, admin = auth_user(request) if zenpy_user.custom_role_id == ZENDESK_ROLES['light_agent']: zenpy_user.custom_role_id = ZENDESK_ROLES['engineer'] admin.users.update(zenpy_user) request.user.userprofile.role = "agent" request.user.userprofile.save() return HttpResponseRedirect(reverse('work', args=(request.user.id,))) def main_page(request: WSGIRequest) -> HttpResponse: """ Функция отображения логгирования на главной странице. .. todo:: Дописать параметры в документацию: :param request: :return: """ logger = logging.getLogger('main.index') logger.info('Index page opened') return render(request, 'pages/index.html') class AdminPageView(LoginRequiredMixin, PermissionRequiredMixin, FormView): """ Класс отображения страницы администратора. :param permission_required: Права доступа к странице администратора :type permission_required: :class:`str` :param template_name: HTML-шаблон страницы администратора :type template_name: :class:`str` :param form_class: Форма страницы администратора :type form_class: :class:`forms.AdminPageUsersForm` :param success_url: Адрес страницы администратора :type success_url: :class:`HttpResponseRedirect` """ permission_required = 'main.has_control_access' template_name = 'pages/adm_ruleset.html' form_class = AdminPageUsers success_url = '/control/' def form_valid(self, form: AdminPageUsers) -> AdminPageUsers: """ Функция обновления страницы AdminPageUsers. :param form: Форма страницы администратора :return: Обновленная страница (пользователям проставлены новые статусы) """ if 'engineer' in self.request.POST: self.make_engineers(form.cleaned_data['users']) elif 'light_agent' in self.request.POST: self.make_light_agents(form.cleaned_data['users']) return super().form_valid(form) @staticmethod def make_engineers(users): """ Функция проходит по списку пользователей, проставляя статус "engineer". :param users: Список пользователей :return: Обновленный список пользователей """ [make_engineer(user) for user in users] @staticmethod def make_light_agents(users): """ Функция проходит по списку пользователей, проставляя статус "light_agent". :param users: Список пользователей :return: Обновленный список пользователей """ [make_light_agent(user) for user in users] @staticmethod def count_users(users) -> tuple: """ Функция подсчета количества сотрудников с ролями "engineer" и "light_agent". .. todo:: this func counts users from all zendesk instead of just from a model: """ engineers, light_agents = 0, 0 for user in users: if user.custom_role_id == ZENDESK_ROLES['engineer']: engineers += 1 elif user.custom_role_id == ZENDESK_ROLES['light_agent']: light_agents += 1 return engineers, light_agents def get_context_data(self, **kwargs) -> dict: """ Функция формирования контента страницы администратора (с проверкой прав доступа) """ if self.request.user.userprofile.role != 'admin': raise PermissionDenied context = super().get_context_data(**kwargs) context['users'] = get_list_or_404( UserProfile, role='agent') context['engineers'], context['light_agents'] = self.count_users(get_users_list()) return context # TODO: need to get profile page url class CustomLoginView(LoginView): """ Классс отображения страницы авторизации пользователя. """ form_class = CustomAuthenticationForm @login_required() def statistic_page(request: WSGIRequest) -> HttpResponse: """ Функция отображения страницы статистики (для "superuser"). :param request: данные о пользователе: email, время и интервал работы. Данные получаем через forms.StatisticForm :return: адресация на страницу статистики """ if not request.user.is_superuser: return redirect('index') context = { 'pagename': 'страница статистики', 'errors': list(), } if request.method == "POST": form = StatisticForm(request.POST) if form.is_valid(): start_date, end_date = form.cleaned_data['range_start'], form.cleaned_data['range_end'] interval, show = form.cleaned_data['interval'], form.cleaned_data['display_format'] Data = StatisticData(start_date, end_date, form.cleaned_data['email']) Data.set_display(show) Data.set_interval(interval) stats = Data.get_statistic() if Data.errors: context['errors'] = Data.errors if Data.warnings: context['warnings'] = Data.warnings context['log_stats'] = stats if not context['errors'] else None if request.method == 'GET': form = StatisticForm() context['form'] = form return render(request, 'pages/stat.html', context)