Better linter score on users

This commit is contained in:
chapeau 2025-07-08 09:05:03 +02:00
parent 2197aa941b
commit cd722041a4
11 changed files with 221 additions and 297 deletions

View file

@ -45,7 +45,6 @@ from .models import (
Request,
School,
ServiceUser,
User,
Whitelist,
)
@ -58,8 +57,6 @@ class SchoolAdmin(VersionAdmin):
"""
pass
class ListRightAdmin(VersionAdmin):
"""ListRight and groups Admin view and management.
@ -82,8 +79,6 @@ class ListShellAdmin(VersionAdmin):
"""
pass
class RequestAdmin(admin.ModelAdmin):
"""User Request Admin view and management, for
@ -106,8 +101,6 @@ class BanAdmin(VersionAdmin):
"""
pass
class EMailAddressAdmin(VersionAdmin):
"""EmailAddress Admin view and management, for
@ -118,8 +111,6 @@ class EMailAddressAdmin(VersionAdmin):
"""
pass
class WhitelistAdmin(VersionAdmin):
"""Whitelist Admin view and management, for
@ -130,8 +121,6 @@ class WhitelistAdmin(VersionAdmin):
"""
pass
class AdherentAdmin(VersionAdmin, BaseUserAdmin):
"""Adherent Admin view and management, for

View file

@ -22,7 +22,7 @@
from rest_framework import serializers
import users.models as users
from api.serializers import NamespacedHIField, NamespacedHMSerializer, NamespacedHRField
from api.serializers import NamespacedHMSerializer
class UserSerializer(NamespacedHMSerializer):

View file

@ -42,6 +42,7 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet):
class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes infos of `users.models.Users` objects to create homes."""
# pylint: disable=unsupported-binary-operation
queryset = users.User.objects.exclude(
Q(state=users.User.STATE_DISABLED)
| Q(state=users.User.STATE_NOT_YET_ACTIVE)
@ -137,7 +138,6 @@ class EMailAddressViewSet(viewsets.ReadOnlyModelViewSet):
def get_queryset(self):
if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"):
return users.EMailAddress.objects.filter(user__local_email_enabled=True)
else:
return users.EMailAddress.objects.none()
@ -149,7 +149,6 @@ class LocalEmailUsersView(generics.ListAPIView):
def get_queryset(self):
if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"):
return users.User.objects.filter(local_email_enabled=True)
else:
return users.User.objects.none()

View file

@ -45,7 +45,6 @@ from os import path, walk
from django import forms
from django.conf import settings
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth.models import Group, Permission
from django.contrib.auth.password_validation import (
password_validators_help_text_html,
@ -54,13 +53,11 @@ from django.contrib.auth.password_validation import (
from django.core.validators import MinLengthValidator
from django.forms import Form, ModelForm
from django.utils import timezone
from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from machines.models import Interface, Machine, Nas
from machines.models import Interface, Nas
from preferences.models import GeneralOption, OptionalUser
from re2o.base import get_input_formats_help_text
from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin
from re2o.utils import remove_user_room
@ -108,7 +105,7 @@ class UserAdminForm(FormRevMixin, forms.ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(UserAdminForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
self.fields["email"].required = True
class Meta:
@ -140,7 +137,7 @@ class UserAdminForm(FormRevMixin, forms.ModelForm):
commit : If False, don't make the real save in database
"""
# Save the provided password in hashed format
user = super(UserAdminForm, self).save(commit=False)
user = super().save(commit=False)
if self.cleaned_data["password1"]:
user.set_password(self.cleaned_data["password1"])
user.save()
@ -164,7 +161,7 @@ class ServiceUserAdminForm(FormRevMixin, forms.ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(ServiceUserAdminForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
class Meta:
model = ServiceUser
@ -193,7 +190,7 @@ class ServiceUserAdminForm(FormRevMixin, forms.ModelForm):
self : Apply on a django Form ServiceUserAdminForm instance
commit : If False, don't make the real save in database
"""
user = super(ServiceUserAdminForm, self).save(commit=False)
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.save()
return user
@ -255,7 +252,6 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm):
"""
if not self.instance.check_password(self.cleaned_data.get("selfpasswd")):
raise forms.ValidationError(_("The current password is incorrect."))
return
def save(self, commit=True):
"""Save function. Call standard "set_password" django function,
@ -265,7 +261,7 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm):
self : Apply on a django Form PassForm instance
commit : If False, don't make the real save in database
"""
user = super(PassForm, self).save(commit=False)
user = super().save(commit=False)
user.set_password(self.cleaned_data.get("passwd1"))
user.save()
@ -297,7 +293,7 @@ class MassArchiveForm(forms.Form):
)
def clean(self):
cleaned_data = super(MassArchiveForm, self).clean()
cleaned_data = super().clean()
date = cleaned_data.get("date")
if date:
if date > timezone.now():
@ -320,7 +316,7 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(AdherentForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
self.fields["name"].label = _("First name")
self.fields["surname"].label = _("Surname")
self.fields["email"].label = _("Email address")
@ -348,7 +344,8 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"room": AutocompleteModelWidget(
url="/topologie/room-autocomplete",
attrs={
"data-minimum-input-length": 3 # Only trigger autocompletion after 3 characters have been typed
# Only trigger autocompletion after 3 characters have been typed
"data-minimum-input-length": 3
},
),
"shell": AutocompleteModelWidget(url="/users/shell-autocomplete"),
@ -382,7 +379,6 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
room = self.cleaned_data.get("room")
if self.cleaned_data.get("force", False) and room:
remove_user_room(room)
return
def clean_room(self):
"""Clean room, based on room policy provided by preferences.
@ -478,16 +474,13 @@ class AdherentCreationForm(AdherentForm):
]
def __init__(self, *args, **kwargs):
super(AdherentCreationForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
gtu_file = GeneralOption.get_cached_value("GTU")
gtu_url = gtu_file.url if gtu_file else "#"
self.fields["email"].required = True
self.fields["gtu_check"].label = mark_safe(
"%s <a href='%s' download='CGU'>%s</a>."
% (
_("I commit to accept the"),
gtu_file.url if gtu_file else "#",
_("General Terms of Use"),
)
f"""{_("I commit to accept the")} <a href='{gtu_url}' """
f"""download='CGU'>{_("General Terms of Use")}</a>."""
)
# Remove password fields if option is disabled
@ -553,7 +546,7 @@ class AdherentEditForm(AdherentForm):
"""
def __init__(self, *args, **kwargs):
super(AdherentEditForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.fields["gpg_fingerprint"].widget.attrs["placeholder"] = _(
"Leave empty if you don't have any GPG key."
)
@ -590,7 +583,7 @@ class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(ClubForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
self.fields["surname"].label = _("Name")
self.fields["school"].label = _("School")
self.fields["comment"].label = _("Comment")
@ -659,7 +652,7 @@ class ClubAdminandMembersForm(FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(ClubAdminandMembersForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
class PasswordForm(FormRevMixin, ModelForm):
@ -675,7 +668,7 @@ class PasswordForm(FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(PasswordForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
class ServiceUserForm(FormRevMixin, ModelForm):
@ -700,7 +693,7 @@ class ServiceUserForm(FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(ServiceUserForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
def save(self, commit=True):
"""Save function. If password has been changed and provided,
@ -711,7 +704,7 @@ class ServiceUserForm(FormRevMixin, ModelForm):
self : Apply on a django Form ServiceUserForm instance
commit : If False, don't make the real save in database
"""
user = super(ServiceUserForm, self).save(commit=False)
user = super().save(commit=False)
if self.cleaned_data["password"]:
user.set_password(self.cleaned_data.get("password"))
user.save()
@ -751,7 +744,7 @@ class StateForm(FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(StateForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
self.fields["state"].label = _("State")
self.fields["email_state"].label = _("Email state")
@ -773,7 +766,7 @@ class GroupForm(FieldPermissionFormMixin, FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(GroupForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
if "is_superuser" in self.fields:
self.fields["is_superuser"].label = _("Superuser")
@ -791,7 +784,7 @@ class SchoolForm(FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(SchoolForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
self.fields["name"].label = _("School")
@ -808,7 +801,7 @@ class ShellForm(FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(ShellForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
self.fields["shell"].label = _("Shell name")
@ -833,7 +826,7 @@ class ListRightForm(FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(ListRightForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
self.fields["unix_name"].label = _("Name of the group of rights")
@ -849,7 +842,7 @@ class NewListRightForm(ListRightForm):
fields = ("name", "unix_name", "gid", "critical", "permissions", "details")
def __init__(self, *args, **kwargs):
super(NewListRightForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.fields["gid"].label = _(
"GID. Warning: this field must not be edited after creation."
)
@ -871,7 +864,7 @@ class DelListRightForm(Form):
def __init__(self, *args, **kwargs):
instances = kwargs.pop("instances", None)
super(DelListRightForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if instances:
self.fields["listrights"].queryset = instances
else:
@ -894,7 +887,7 @@ class DelSchoolForm(Form):
def __init__(self, *args, **kwargs):
instances = kwargs.pop("instances", None)
super(DelSchoolForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if instances:
self.fields["schools"].queryset = instances
else:
@ -910,7 +903,7 @@ class BanForm(FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(BanForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
self.fields["date_end"].label = _("End date")
self.fields["date_end"].localize = False
@ -929,7 +922,7 @@ class WhitelistForm(FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(WhitelistForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
self.fields["date_end"].label = _("End date")
self.fields["date_end"].localize = False
@ -949,7 +942,7 @@ class EMailAddressForm(FormRevMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(EMailAddressForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
self.fields["local_part"].label = _("Local part of the email address")
self.fields["local_part"].help_text = _("Can't contain @.")
@ -970,7 +963,7 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
super(EmailSettingsForm, self).__init__(*args, prefix=prefix, **kwargs)
super().__init__(*args, prefix=prefix, **kwargs)
self.user = kwargs["instance"]
self.fields["email"].label = _("Main email address")
self.fields["email"].required = bool(self.user.email)
@ -1022,7 +1015,7 @@ class InitialRegisterForm(forms.Form):
self.nas_type = Nas.objects.filter(
nas_type__interface__ipv4__ipv4=switch_ip
).first()
super(InitialRegisterForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
if hasattr(self, "new_room"):
self.fields["register_room"].label = _("This room is my room")
else:
@ -1074,5 +1067,5 @@ class ThemeForm(FormRevMixin, forms.Form):
_, _, themes = next(walk(path.join(settings.STATIC_ROOT, "css/themes")))
if not themes:
themes = ["default.css"]
super(ThemeForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.fields["theme"].choices = [(theme, theme) for theme in themes]

View file

@ -46,11 +46,9 @@ from __future__ import unicode_literals
import datetime
import re
import sys
import traceback
import uuid
from datetime import timedelta
from io import BytesIO
from django import forms
from django.contrib.auth.models import (
@ -59,7 +57,6 @@ from django.contrib.auth.models import (
Group,
PermissionsMixin,
)
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.core.validators import RegexValidator
from django.db import models, transaction
from django.db.models import Q
@ -71,7 +68,6 @@ from django.urls import reverse
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from PIL import Image
from reversion import revisions as reversion
from cotisations.models import Cotisation, Facture, Paiement, Vente
@ -80,7 +76,6 @@ from preferences.models import (
AssoOption,
GeneralOption,
MailMessageOption,
OptionalMachine,
OptionalUser,
)
from re2o.base import smtp_check
@ -102,8 +97,8 @@ def linux_user_check(login):
Returns:
boolean: True if login comply with policy
"""
UNIX_LOGIN_PATTERN = re.compile("^[a-z][a-z0-9-]*[$]?$")
return UNIX_LOGIN_PATTERN.match(login)
unix_login_pattern = re.compile("^[a-z][a-z0-9-]*[$]?$")
return unix_login_pattern.match(login)
def linux_user_validator(login):
@ -332,7 +327,6 @@ class User(
"""
if self.is_class_adherent:
return self.adherent.name
else:
return ""
@cached_property
@ -348,9 +342,8 @@ class User(
"""
if self.is_class_adherent:
return self.adherent.room
elif self.is_class_club:
if self.is_class_club:
return self.club.room
else:
raise NotImplementedError(_("Unknown type."))
@cached_property
@ -370,8 +363,9 @@ class User(
@cached_property
def get_mail(self):
"""Shortcuts, returns the email address to use to contact the instance user self.
Depends on if local_email account has been activated, otherwise returns self.email.
"""Shortcuts, returns the email address to use to contact the instance user
self. Depends on if local_email account has been activated, otherwise returns
self.email.
Parameters:
self (user instance): user to return infos
@ -385,7 +379,6 @@ class User(
or self.local_email_redirect
):
return str(self.email)
else:
return str(self.emailaddress_set.get(local_part=self.pseudo.lower()))
@cached_property
@ -401,9 +394,8 @@ class User(
"""
if hasattr(self, "adherent"):
return "Adherent"
elif hasattr(self, "club"):
if hasattr(self, "club"):
return "Club"
else:
raise NotImplementedError(_("Unknown type."))
@cached_property
@ -419,9 +411,8 @@ class User(
"""
if hasattr(self, "adherent"):
return _("Member")
elif hasattr(self, "club"):
if hasattr(self, "club"):
return _("Club")
else:
raise NotImplementedError(_("Unknown type."))
@cached_property
@ -537,8 +528,7 @@ class User(
"""
name = self.name
if name:
return "%s %s" % (name, self.surname)
else:
return f"{name} {self.surname}"
return self.surname
def get_short_name(self):
@ -595,7 +585,6 @@ class User(
or self.email_state == self.EMAIL_STATE_UNVERIFIED
):
return str(0)
else:
return None
@cached_property
@ -709,9 +698,8 @@ class User(
end = self.end_adhesion()
if not end:
return False
elif end < timezone.now():
if end < timezone.now():
return False
else:
return True
# it looks wrong, we should check if there is a cotisation where
# were date_start_memb < timezone.now() < date_end_memb,
@ -727,14 +715,14 @@ class User(
self (user instance): user to return infos
Returns:
is_connected (boolean) : True is user has a valid membership and a valid connexion.
is_connected (boolean) : True is user has a valid membership and a valid
connexion.
"""
end = self.end_connexion()
if not end:
return False
elif end < timezone.now():
if end < timezone.now():
return False
else:
return self.is_adherent()
# it looks wrong, we should check if there is a cotisation where
# were date_start_con < timezone.now() < date_end_con,
@ -784,9 +772,8 @@ class User(
end = self.end_ban()
if not end:
return False
elif end < timezone.now():
if end < timezone.now():
return False
else:
return True
def is_whitelisted(self):
@ -802,9 +789,8 @@ class User(
end = self.end_whitelist()
if not end:
return False
elif end < timezone.now():
if end < timezone.now():
return False
else:
return True
def has_access(self):
@ -838,12 +824,9 @@ class User(
if not self.end_connexion():
if not self.end_whitelist():
return None
else:
return self.end_whitelist()
else:
if not self.end_whitelist():
return self.end_connexion()
else:
return max(self.end_connexion(), self.end_whitelist())
@classmethod
@ -865,7 +848,6 @@ class User(
return Interface.objects.filter(
machine__in=Machine.objects.filter(user__in=users)
).select_related("domain__extension")
else:
return Interface.objects.filter(
machine__in=Machine.objects.filter(user__in=users, active=active)
).select_related("domain__extension")
@ -918,26 +900,25 @@ class User(
):
self.state = self.STATE_ACTIVE
self.save()
if self.state == self.STATE_ARCHIVE or self.state == self.STATE_FULL_ARCHIVE:
if self.state in (self.STATE_ARCHIVE, self.STATE_FULL_ARCHIVE):
self.unarchive()
self.state = self.STATE_ACTIVE
self.save()
def set_password(self, password):
def set_password(self, raw_password):
"""Method, overload the basic set_password inherited from django BaseUser.
Called when setting a new password, to set the classic django password
hashed, and also the NTLM hashed pwd_ntlm password.
Parameters:
self (user instance): user to set password
password (string): new password (cleatext) to set.
raw_password (string): new password (cleatext) to set.
"""
from re2o.login import hash_nt
super().set_password(password)
self.pwd_ntlm = hash_nt(password)
return
super().set_password(raw_password)
self.pwd_ntlm = hash_nt(raw_password)
def confirm_mail(self):
"""Method, set the email_state to VERIFIED when the email has been verified.
@ -1156,8 +1137,8 @@ class User(
send_mail(
request,
"Bienvenue au %(name)s / Welcome to %(name)s"
% {"name": AssoOption.get_cached_value("name")},
f'Bienvenue au {AssoOption.get_cached_value("name")} / '
f'Welcome to {AssoOption.get_cached_value("name")}',
"",
GeneralOption.get_cached_value("email_from"),
[self.email],
@ -1194,8 +1175,8 @@ class User(
send_mail(
request,
"Changement de mot de passe de %(name)s / Password change for "
"%(name)s" % {"name": AssoOption.get_cached_value("name")},
f'Changement de mot de passe de {AssoOption.get_cached_value("name")} / '
f'Password change for {AssoOption.get_cached_value("name")}',
template.render(context),
GeneralOption.get_cached_value("email_from"),
[req.user.email],
@ -1290,8 +1271,8 @@ class User(
email: An email with a link to confirm the new email address
"""
# Delete all older requests for this user, that aren't for this email
filter = Q(user=self) & Q(type=Request.EMAIL) & ~Q(email=self.email)
Request.objects.filter(filter).delete()
mail_filter = Q(user=self) & Q(type=Request.EMAIL) & ~Q(email=self.email)
Request.objects.filter(mail_filter).delete()
# Create the request and send the email
req = Request()
@ -1316,14 +1297,13 @@ class User(
send_mail(
request,
"Confirmation du mail de %(name)s / Email confirmation for "
"%(name)s" % {"name": AssoOption.get_cached_value("name")},
f'Confirmation du mail de {AssoOption.get_cached_value("name")} / '
f'Email confirmation for {AssoOption.get_cached_value("name")}',
template.render(context),
GeneralOption.get_cached_value("email_from"),
[req.user.email],
fail_silently=False,
)
return
def autoregister_machine(self, mac_address, nas_type, request=None):
"""Function, register a new interface on the user instance account.
@ -1364,7 +1344,7 @@ class User(
domain.clean()
domain.save()
self.notif_auto_newmachine(interface_cible)
except Exception as error:
except Exception:
return False, traceback.format_exc()
return interface_cible, _("OK")
@ -1397,7 +1377,6 @@ class User(
[self.email],
html_message=template.render(context),
)
return
def notif_disable(self, request=None):
"""Function/method, send an email to notify that the account is disabled
@ -1426,7 +1405,6 @@ class User(
[self.email],
fail_silently=False,
)
return
def get_next_domain_name(self):
"""Function/method, provide a unique name for a new interface.
@ -1472,25 +1450,23 @@ class User(
or user_request.adherent in self.club.administrators.all()
):
return True, warning_message, None
else:
return (
False,
_("You don't have the right to edit this club."),
("users.change_user",),
)
else:
if self == user_request:
return True, warning_message, None
elif user_request.has_perm("users.change_all_users"):
if user_request.has_perm("users.change_all_users"):
return True, warning_message, None
elif user_request.has_perm("users.change_user"):
if user_request.has_perm("users.change_user"):
if self.groups.filter(listright__critical=True):
return (
False,
_("User with critical rights, can't be edited."),
("users.change_all_users",),
)
elif self == AssoOption.get_cached_value("utilisateur_asso"):
if self == AssoOption.get_cached_value("utilisateur_asso"):
return (
False,
_(
@ -1499,11 +1475,9 @@ class User(
),
("users.change_all_users",),
)
else:
return True, warning_message, None
elif user_request.has_perm("users.change_all_users"):
if user_request.has_perm("users.change_all_users"):
return True, warning_message, None
else:
return (
False,
_("You don't have the right to edit another user."),
@ -1526,22 +1500,17 @@ class User(
or user_request.adherent in self.club.administrators.all()
):
return True, None, None
else:
return (
False,
_("You don't have the right to edit this club."),
("users.change_user_password",),
)
else:
if self == user_request or user_request.has_perm(
"users.change_user_groups"
):
if self == user_request or user_request.has_perm("users.change_user_groups"):
# Peut éditer les groupes d'un user,
# c'est un privilège élevé, True
return True, None, None
elif user_request.has_perm("users.change_user") and not self.groups.all():
if user_request.has_perm("users.change_user") and not self.groups.all():
return True, None, None
else:
return (
False,
_("You don't have the right to edit another user."),
@ -1574,7 +1543,6 @@ class User(
_("You don't have the right to change the room."),
("users.change_user",),
)
else:
return True, None, None
@staticmethod
@ -1611,7 +1579,6 @@ class User(
_("You don't have the right to change the shell."),
("users.change_user_shell",),
)
else:
return True, None, None
def can_change_pseudo(self, user_request, *_args, **_kwargs):
@ -1634,7 +1601,6 @@ class User(
_("You don't have the right to change the pseudo."),
("users.change_user_pseudo",),
)
else:
return True, None, None
@staticmethod
@ -1737,16 +1703,13 @@ class User(
or user_request.adherent in self.club.members.all()
):
return True, None, None
else:
return (
False,
_("You don't have the right to view this club."),
("users.view_user",),
)
else:
if self == user_request or user_request.has_perm("users.view_user"):
return True, None, None
else:
return (
False,
_("You don't have the right to view another user."),
@ -1789,7 +1752,7 @@ class User(
)
def __init__(self, *args, **kwargs):
super(User, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
self.field_permissions = {
"shell": self.can_change_shell,
"pseudo": self.can_change_pseudo,
@ -1854,7 +1817,7 @@ class User(
self (user instance): user to clean.
"""
super(User, self).clean(*args, **kwargs)
super().clean(*args, **kwargs)
self.clean_pseudo(*args, **kwargs)
self.clean_email(*args, **kwargs)
@ -1868,7 +1831,7 @@ class User(
Returns:
str: name of theme
"""
return self.theme.split(".")[0]
return self.theme.split(".", maxsplit=1)[0]
class Adherent(User):
@ -1946,20 +1909,13 @@ class Adherent(User):
if not user_request.is_authenticated:
if not OptionalUser.get_cached_value("self_adhesion"):
return False, _("Self registration is disabled."), None
else:
return True, None, None
else:
if OptionalUser.get_cached_value("all_can_create_adherent"):
return True, None, None
else:
can = user_request.has_perm("users.add_user")
return (
can,
(
_("You don't have the right to create a user.")
if not can
else None
),
(_("You don't have the right to create a user.") if not can else None),
("users.add_user",),
)
@ -1977,7 +1933,6 @@ class Adherent(User):
can, _message, _group = Club.can_view_all(user_request)
if user_request.has_perm("users.view_user") or can:
return (True, None, None, cls.objects.all())
else:
return (
True,
_("You don't have the right to list all adherents."),
@ -1992,7 +1947,7 @@ class Adherent(User):
self (user instance): user to perform clean.
"""
super(Adherent, self).clean(*args, **kwargs)
super().clean(*args, **kwargs)
if self.gpg_fingerprint:
self.validate_gpgfp()
self.format_gpgfp()
@ -2035,18 +1990,12 @@ class Club(User):
"""
if not user_request.is_authenticated:
return False, _("You must be authenticated."), None
else:
if OptionalUser.get_cached_value("all_can_create_club"):
return True, None, None
else:
can = user_request.has_perm("users.add_user")
return (
can,
(
_("You don't have the right to create a club.")
if not can
else None
),
(_("You don't have the right to create a club.") if not can else None),
("users.add_user",),
)
@ -2148,10 +2097,11 @@ def user_post_delete(**kwargs):
class ServiceUser(RevMixin, AclMixin, AbstractBaseUser):
"""A class representing a serviceuser (it is considered as a user
with special informations).
The serviceuser is a special user used with special access to authentication tree. It is
its only usefullness, and service user can't connect to re2o.
Each service connected to authentication for auth (ex dokuwiki, owncloud, etc) should
have a different service user with special acl (readonly, auth) and password.
The serviceuser is a special user used with special access to authentication tree.
It is its only usefullness, and service user can't connect to re2o.
Each service connected to authentication for auth (ex dokuwiki, owncloud, etc)
should have a different service user with special acl (readonly, auth) and
password.
Attributes:
pseudo: login of the serviceuser
@ -2332,7 +2282,7 @@ class ListShell(RevMixin, AclMixin, models.Model):
pretty_name (string): Return a pretty name string for this shell.
"""
return self.shell.split("/")[-1]
return self.shell.split("/", maxsplit=1)[-1]
@classmethod
def can_list(cls, user_request, *_args, **_kwargs):
@ -2409,7 +2359,6 @@ class Ban(RevMixin, AclMixin, models.Model):
[self.user.email],
fail_silently=False,
)
return
def is_active(self):
"""Method, return if the ban is active now or not.
@ -2437,7 +2386,6 @@ class Ban(RevMixin, AclMixin, models.Model):
_("You don't have the right to view other bans than yours."),
("users.view_ban",),
)
else:
return True, None, None
def __str__(self):
@ -2531,7 +2479,6 @@ class Whitelist(RevMixin, AclMixin, models.Model):
_("You don't have the right to view other whitelists than yours."),
("users.view_whitelist",),
)
else:
return True, None, None
def __str__(self):
@ -2604,7 +2551,7 @@ class Request(models.Model):
)
if not self.token:
self.token = str(uuid.uuid4()).replace("-", "") # remove hyphens
super(Request, self).save()
super().save()
class EMailAddress(RevMixin, AclMixin, models.Model):
@ -2673,7 +2620,7 @@ class EMailAddress(RevMixin, AclMixin, models.Model):
),
("users.add_emailaddress",),
)
elif user_request.email_address.count() >= OptionalUser.get_cached_value(
if user_request.email_address.count() >= OptionalUser.get_cached_value(
"max_email_address"
):
return (
@ -2796,4 +2743,4 @@ class EMailAddress(RevMixin, AclMixin, models.Model):
result, reason = smtp_check(self.local_part)
if result:
raise ValidationError(reason)
super(EMailAddress, self).clean(*args, **kwargs)
super().clean(*args, **kwargs)

View file

@ -1,24 +1,28 @@
"""
A set of signals used by users. Various classes in users emit these signals to signal the need to sync or
remove an object from optionnal authentication backends, e.g. LDAP.
A set of signals used by users. Various classes in users emit these signals to signal
the need to sync or remove an object from optionnal authentication backends, e.g. LDAP.
* `users.signals.synchronise`:
Expresses the need for an instance of a users class to be synchronised. `sender` and `instance` are
always set. It is up to the receiver to ensure the others are set correctly if they make sense.
Expresses the need for an instance of a users class to be synchronised. `sender`
and `instance` are always set. It is up to the receiver to ensure the others are
set correctly if they make sense.
Arguments:
* `sender` : The model class.
* `instance` : The actual instance being synchronised.
* `base` : Default `True`. When `True`, synchronise basic attributes.
* `access_refresh` : Default `True`. When `True`, synchronise the access time.
* `mac_refresh` : Default `True`. When True, synchronise the list of mac addresses.
* `group_refresh`: Default `False`. When `True` synchronise the groups of the instance.
* `mac_refresh` : Default `True`. When True, synchronise the list of mac
addresses.
* `group_refresh`: Default `False`. When `True` synchronise the groups of
the instance.
* `users.signals.remove`:
Expresses the need for an instance of a users class to be removed.
Arguments:
* `sender` : The model class.
* `instance` : The actual instance being removed.
* `users.signals.remove_mass`:
Same as `users.signals.remove` except it removes a queryset. For now it is only used by `users.models.User`.
Same as `users.signals.remove` except it removes a queryset. For now it is only used
by `users.models.User`.
Arguments:
* `sender` : The model class.
* `queryset` : The actual instances being removed.

View file

@ -19,7 +19,7 @@ class UserModelTests(TestCase):
paiement = Paiement.objects.create(moyen="test payment")
invoice = Facture.objects.create(user=self.user, paiement=paiement, valid=True)
date = timezone.now()
purchase1 = Vente.objects.create(
Vente.objects.create(
facture=invoice,
number=1,
name="Test purchase",
@ -29,7 +29,7 @@ class UserModelTests(TestCase):
duration_connection=0,
prix=0,
)
purchase2 = Vente.objects.create(
Vente.objects.create(
facture=invoice,
number=1,
name="Test purchase",

View file

@ -23,9 +23,6 @@
The tests for the Users module.
"""
import os.path
from django.conf import settings
from django.test import TestCase
from . import models

View file

@ -44,38 +44,33 @@ Also add extra views for :
* Ask for new email for email confirmation
* Register room and interface on user account with switch web redirection.
All the view must be as simple as possible, with returning the correct form to user during
get, and during post, performing change in database with simple ".save()" function.
All the view must be as simple as possible, with returning the correct form to user
during get, and during post, performing change in database with simple ".save()"
function.
The aim is to put all "intelligent" functions in both forms and models functions. In fact, this
will allow to user other frontend (like REST api) to perform editions, creations, etc on database,
without code duplication.
The aim is to put all "intelligent" functions in both forms and models functions. In
fact, this will allow to user other frontend (like REST api) to perform editions,
creations, etc on database, without code duplication.
"""
from __future__ import unicode_literals
import os
from importlib import import_module
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.db.models import Count, Max, ProtectedError
from django.http import HttpResponse, HttpResponseRedirect
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render
from django.template import loader
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import ugettext as _
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from reversion import revisions as reversion
from cotisations.models import Facture, Paiement
from cotisations.models import Paiement
from cotisations.utils import find_payment_method
from machines.models import Machine
from preferences.models import AssoOption, GeneralOption, OptionalUser
from re2o.acl import (
can_change,
@ -150,8 +145,8 @@ def new_user(request):
)
user.request = request
GTU_sum_up = GeneralOption.get_cached_value("GTU_sum_up")
GTU = GeneralOption.get_cached_value("GTU")
gtu_sum_up = GeneralOption.get_cached_value("GTU_sum_up")
gtu = GeneralOption.get_cached_value("GTU")
is_set_password_allowed = OptionalUser.get_cached_value(
"allow_set_password_during_user_creation"
)
@ -180,8 +175,8 @@ def new_user(request):
# but they should be treated differently
params = {
"userform": user,
"GTU_sum_up": GTU_sum_up,
"GTU": GTU,
"GTU_sum_up": gtu_sum_up,
"GTU": gtu,
"showCGU": True,
"action_name": _("Commit"),
}
@ -1487,9 +1482,8 @@ def process(request, token):
if req.type == Request.PASSWD:
return process_passwd(request, req)
elif req.type == Request.EMAIL:
if req.type == Request.EMAIL:
return process_email(request, req)
else:
messages.error(request, _("Error: please contact an admin."))
redirect(reverse("index"))
@ -1545,7 +1539,7 @@ def process_email(request, req):
# Delete all remaining requests
Request.objects.filter(user=user, type=Request.EMAIL).delete()
messages.success(request, _("The %s address was confirmed." % user.email))
messages.success(request, _("The %s address was confirmed.") % user.email)
return redirect(reverse("index"))
return form(
@ -1557,7 +1551,7 @@ def process_email(request, req):
@login_required
@can_edit(User)
def resend_confirmation_email(request, logged_user, userid):
def resend_confirmation_email(request, _logged_user, userid):
"""View to resend confirm email, for adding a new email.
Check if User has the correct acl.
@ -1590,8 +1584,8 @@ def initial_register(request):
This view is used with switchs function of redirect web after AAA authentication
failed. Then, the users log-in, and the new mac-address and switch port, in order to
get the room, are included in HTTP Headers by the switch redirection functionnality.
This allow to add the new interface with the correct mac-address, and confirm if needed,
the new room of request.user.
This allow to add the new interface with the correct mac-address, and confirm if
needed, the new room of request.user.
Parameters:
request (django request): Standard django request.

View file

@ -31,7 +31,7 @@ Here are defined the autocomplete class based view.
"""
from __future__ import unicode_literals
from django.db.models import CharField, Q, Value
from django.db.models import Q, Value
from django.db.models.functions import Concat
from re2o.views import AutocompleteLoggedOutViewMixin, AutocompleteViewMixin
@ -46,7 +46,8 @@ class SchoolAutocomplete(AutocompleteLoggedOutViewMixin):
class UserAutocomplete(AutocompleteViewMixin):
obj_type = User
# Precision on search to add annotations so search behaves more like users expect it to
# Precision on search to add annotations so search behaves more like users
# expect it to
def filter_results(self):
# Comments explain what we try to match
self.query_set = self.query_set.annotate(
@ -59,6 +60,7 @@ class UserAutocomplete(AutocompleteViewMixin):
).all()
if self.q:
# pylint: disable=unsupported-binary-operation
self.query_set = self.query_set.filter(
Q(pseudo__icontains=self.q)
| Q(full_name__icontains=self.q)
@ -69,7 +71,8 @@ class UserAutocomplete(AutocompleteViewMixin):
class AdherentAutocomplete(AutocompleteViewMixin):
obj_type = Adherent
# Precision on search to add annotations so search behaves more like users expect it to
# Precision on search to add annotations so search behaves more like users
# expect it to
def filter_results(self):
# Comments explain what we try to match
self.query_set = self.query_set.annotate(
@ -82,6 +85,7 @@ class AdherentAutocomplete(AutocompleteViewMixin):
).all()
if self.q:
# pylint: disable=unsupported-binary-operation
self.query_set = self.query_set.filter(
Q(pseudo__icontains=self.q)
| Q(full_name__icontains=self.q)

View file

@ -1,13 +1,10 @@
from django.conf import settings
from django.forms.utils import flatatt
from django.forms.widgets import Input
from django.template import Template
from django.template.loader import get_template
from django.utils.dates import (
MONTHS,
MONTHS_3,
MONTHS_ALT,
MONTHS_AP,
WEEKDAYS,
WEEKDAYS_ABBR,
)