mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-22 11:23:10 +00:00
Fix #261
This commit is contained in:
parent
e7795a775c
commit
f6b2225eb8
13 changed files with 380 additions and 36 deletions
|
@ -48,6 +48,7 @@ from .models import (
|
||||||
LdapServiceUser,
|
LdapServiceUser,
|
||||||
LdapServiceUserGroup,
|
LdapServiceUserGroup,
|
||||||
LdapUserGroup,
|
LdapUserGroup,
|
||||||
|
SSHKey,
|
||||||
)
|
)
|
||||||
from .forms import (
|
from .forms import (
|
||||||
UserChangeForm,
|
UserChangeForm,
|
||||||
|
@ -130,6 +131,12 @@ class WhitelistAdmin(VersionAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SSHKeyAdmin(VersionAdmin):
|
||||||
|
"""SSHKey model for admin."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class UserAdmin(VersionAdmin, BaseUserAdmin):
|
class UserAdmin(VersionAdmin, BaseUserAdmin):
|
||||||
"""Gestion d'un user : modification des champs perso, mot de passe, etc"""
|
"""Gestion d'un user : modification des champs perso, mot de passe, etc"""
|
||||||
|
|
||||||
|
@ -224,6 +231,7 @@ admin.site.register(Ban, BanAdmin)
|
||||||
admin.site.register(EMailAddress, EMailAddressAdmin)
|
admin.site.register(EMailAddress, EMailAddressAdmin)
|
||||||
admin.site.register(Whitelist, WhitelistAdmin)
|
admin.site.register(Whitelist, WhitelistAdmin)
|
||||||
admin.site.register(Request, RequestAdmin)
|
admin.site.register(Request, RequestAdmin)
|
||||||
|
admin.site.register(SSHKey, SSHKeyAdmin)
|
||||||
# Now register the new UserAdmin...
|
# Now register the new UserAdmin...
|
||||||
admin.site.unregister(User)
|
admin.site.unregister(User)
|
||||||
admin.site.unregister(ServiceUser)
|
admin.site.unregister(ServiceUser)
|
||||||
|
|
|
@ -24,6 +24,7 @@ from rest_framework import serializers
|
||||||
import users.models as users
|
import users.models as users
|
||||||
from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer
|
from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(NamespacedHMSerializer):
|
class UserSerializer(NamespacedHMSerializer):
|
||||||
"""Serialize `users.models.User` objects.
|
"""Serialize `users.models.User` objects.
|
||||||
"""
|
"""
|
||||||
|
@ -242,3 +243,11 @@ class MailingSerializer(ClubSerializer):
|
||||||
|
|
||||||
class Meta(ClubSerializer.Meta):
|
class Meta(ClubSerializer.Meta):
|
||||||
fields = ("name", "members", "admins")
|
fields = ("name", "members", "admins")
|
||||||
|
|
||||||
|
|
||||||
|
class SSHKeySerializer(NamespacedHMSerializer):
|
||||||
|
"""Serialize an SSHKey."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = users.SSHKey
|
||||||
|
fields = "__all__"
|
||||||
|
|
|
@ -34,14 +34,14 @@ urls_viewset = [
|
||||||
(r"users/shell", views.ShellViewSet, "shell"),
|
(r"users/shell", views.ShellViewSet, "shell"),
|
||||||
(r"users/ban", views.BanViewSet, None),
|
(r"users/ban", views.BanViewSet, None),
|
||||||
(r"users/whitelist", views.WhitelistViewSet, None),
|
(r"users/whitelist", views.WhitelistViewSet, None),
|
||||||
(r"users/emailaddress", views.EMailAddressViewSet, None)
|
(r"users/emailaddress", views.EMailAddressViewSet, None),
|
||||||
|
(r"users/sshkey", views.SSHKeyViewSet, None),
|
||||||
]
|
]
|
||||||
|
|
||||||
urls_view = [
|
urls_view = [
|
||||||
(r"users/localemail", views.LocalEmailUsersView),
|
(r"users/localemail", views.LocalEmailUsersView),
|
||||||
(r"users/mailing-standard", views.StandardMailingView),
|
(r"users/mailing-standard", views.StandardMailingView),
|
||||||
(r"users/mailing-club", views.ClubMailingView),
|
(r"users/mailing-club", views.ClubMailingView),
|
||||||
|
|
||||||
# Deprecated
|
# Deprecated
|
||||||
(r"localemail/users", views.LocalEmailUsersView),
|
(r"localemail/users", views.LocalEmailUsersView),
|
||||||
(r"mailing/standard", views.StandardMailingView),
|
(r"mailing/standard", views.StandardMailingView),
|
||||||
|
|
|
@ -190,3 +190,11 @@ class ClubMailingView(generics.ListAPIView):
|
||||||
|
|
||||||
queryset = users.Club.objects.all()
|
queryset = users.Club.objects.all()
|
||||||
serializer_class = serializers.MailingSerializer
|
serializer_class = serializers.MailingSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class SSHKeyViewSet(viewsets.ReadOnlyModelViewSet):
|
||||||
|
"""Exposes list and details of `users.models.SSHKey` objects.
|
||||||
|
"""
|
||||||
|
|
||||||
|
queryset = users.SSHKey.objects.all()
|
||||||
|
serializer_class = serializers.SSHKeySerializer
|
||||||
|
|
|
@ -38,7 +38,10 @@ from __future__ import unicode_literals
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.forms import ModelForm, Form
|
from django.forms import ModelForm, Form
|
||||||
from django.contrib.auth.forms import ReadOnlyPasswordHashField
|
from django.contrib.auth.forms import ReadOnlyPasswordHashField
|
||||||
from django.contrib.auth.password_validation import validate_password, password_validators_help_text_html
|
from django.contrib.auth.password_validation import (
|
||||||
|
validate_password,
|
||||||
|
password_validators_help_text_html,
|
||||||
|
)
|
||||||
from django.core.validators import MinLengthValidator
|
from django.core.validators import MinLengthValidator
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import lazy
|
from django.utils.functional import lazy
|
||||||
|
@ -69,6 +72,7 @@ from .models import (
|
||||||
Ban,
|
Ban,
|
||||||
Adherent,
|
Adherent,
|
||||||
Club,
|
Club,
|
||||||
|
SSHKey,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,7 +88,7 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm):
|
||||||
label=_("New password"),
|
label=_("New password"),
|
||||||
max_length=255,
|
max_length=255,
|
||||||
widget=forms.PasswordInput,
|
widget=forms.PasswordInput,
|
||||||
help_text=password_validators_help_text_html()
|
help_text=password_validators_help_text_html(),
|
||||||
)
|
)
|
||||||
passwd2 = forms.CharField(
|
passwd2 = forms.CharField(
|
||||||
label=_("New password confirmation"),
|
label=_("New password confirmation"),
|
||||||
|
@ -133,12 +137,10 @@ class UserCreationForm(FormRevMixin, forms.ModelForm):
|
||||||
label=_("Password"),
|
label=_("Password"),
|
||||||
widget=forms.PasswordInput,
|
widget=forms.PasswordInput,
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=password_validators_help_text_html()
|
help_text=password_validators_help_text_html(),
|
||||||
)
|
)
|
||||||
password2 = forms.CharField(
|
password2 = forms.CharField(
|
||||||
label=_("Password confirmation"),
|
label=_("Password confirmation"), widget=forms.PasswordInput, max_length=255,
|
||||||
widget=forms.PasswordInput,
|
|
||||||
max_length=255,
|
|
||||||
)
|
)
|
||||||
is_admin = forms.BooleanField(label=_("Is admin"))
|
is_admin = forms.BooleanField(label=_("Is admin"))
|
||||||
|
|
||||||
|
@ -287,9 +289,7 @@ class MassArchiveForm(forms.Form):
|
||||||
|
|
||||||
date = forms.DateTimeField(help_text="%d/%m/%y")
|
date = forms.DateTimeField(help_text="%d/%m/%y")
|
||||||
full_archive = forms.BooleanField(
|
full_archive = forms.BooleanField(
|
||||||
label=_(
|
label=_("Fully archive users? WARNING: CRITICAL OPERATION IF TRUE"),
|
||||||
"Fully archive users? WARNING: CRITICAL OPERATION IF TRUE"
|
|
||||||
),
|
|
||||||
initial=False,
|
initial=False,
|
||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
@ -380,6 +380,7 @@ class AdherentCreationForm(AdherentForm):
|
||||||
AdherentForm auquel on ajoute une checkbox afin d'éviter les
|
AdherentForm auquel on ajoute une checkbox afin d'éviter les
|
||||||
doublons d'utilisateurs et, optionnellement,
|
doublons d'utilisateurs et, optionnellement,
|
||||||
un champ mot de passe"""
|
un champ mot de passe"""
|
||||||
|
|
||||||
# Champ pour choisir si un lien est envoyé par mail pour le mot de passe
|
# Champ pour choisir si un lien est envoyé par mail pour le mot de passe
|
||||||
init_password_by_mail_info = _(
|
init_password_by_mail_info = _(
|
||||||
"If this options is set, you will receive a link to set"
|
"If this options is set, you will receive a link to set"
|
||||||
|
@ -392,9 +393,7 @@ class AdherentCreationForm(AdherentForm):
|
||||||
)
|
)
|
||||||
|
|
||||||
init_password_by_mail = forms.BooleanField(
|
init_password_by_mail = forms.BooleanField(
|
||||||
help_text=init_password_by_mail_info,
|
help_text=init_password_by_mail_info, required=False, initial=True
|
||||||
required=False,
|
|
||||||
initial=True
|
|
||||||
)
|
)
|
||||||
init_password_by_mail.label = _("Send password reset link by email.")
|
init_password_by_mail.label = _("Send password reset link by email.")
|
||||||
|
|
||||||
|
@ -405,7 +404,7 @@ class AdherentCreationForm(AdherentForm):
|
||||||
label=_("Password"),
|
label=_("Password"),
|
||||||
widget=forms.PasswordInput,
|
widget=forms.PasswordInput,
|
||||||
max_length=255,
|
max_length=255,
|
||||||
help_text=password_validators_help_text_html()
|
help_text=password_validators_help_text_html(),
|
||||||
)
|
)
|
||||||
password2 = forms.CharField(
|
password2 = forms.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
|
@ -482,8 +481,12 @@ class AdherentCreationForm(AdherentForm):
|
||||||
# Save the provided password in hashed format
|
# Save the provided password in hashed format
|
||||||
user = super(AdherentForm, self).save(commit=False)
|
user = super(AdherentForm, self).save(commit=False)
|
||||||
|
|
||||||
is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation")
|
is_set_password_allowed = OptionalUser.get_cached_value(
|
||||||
set_passwd = is_set_password_allowed and not self.cleaned_data.get("init_password_by_mail")
|
"allow_set_password_during_user_creation"
|
||||||
|
)
|
||||||
|
set_passwd = is_set_password_allowed and not self.cleaned_data.get(
|
||||||
|
"init_password_by_mail"
|
||||||
|
)
|
||||||
if set_passwd:
|
if set_passwd:
|
||||||
user.set_password(self.cleaned_data["password1"])
|
user.set_password(self.cleaned_data["password1"])
|
||||||
|
|
||||||
|
@ -886,3 +889,15 @@ class InitialRegisterForm(forms.Form):
|
||||||
if self.cleaned_data["register_machine"]:
|
if self.cleaned_data["register_machine"]:
|
||||||
if self.mac_address and self.nas_type:
|
if self.mac_address and self.nas_type:
|
||||||
self.user.autoregister_machine(self.mac_address, self.nas_type)
|
self.user.autoregister_machine(self.mac_address, self.nas_type)
|
||||||
|
|
||||||
|
|
||||||
|
class SSHKeyForm(FormRevMixin, ModelForm):
|
||||||
|
"""Create or edit an SSHKey"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
|
||||||
|
super(SSHKeyForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SSHKey
|
||||||
|
exclude = ["user"]
|
||||||
|
|
54
users/migrations/0092_auto_20200423_1804.py
Normal file
54
users/migrations/0092_auto_20200423_1804.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.28 on 2020-04-23 16:04
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import ldapdb.models.fields
|
||||||
|
import re2o.mixins
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("users", "0091_auto_20200423_1256"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="SSHKey",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("key", models.TextField(blank=True, verbose_name="Public ssh key")),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"verbose_name": "SSH key",
|
||||||
|
"verbose_name_plural": "SSH keys",
|
||||||
|
"permissions": (("view_sshkey", "Can view an SSHKey object"),),
|
||||||
|
},
|
||||||
|
bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="ldapuser",
|
||||||
|
name="ssh_keys",
|
||||||
|
field=ldapdb.models.fields.ListField(
|
||||||
|
blank=True, db_column="sshkeys", null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
18
users/migrations/0093_auto_20200423_2028.py
Normal file
18
users/migrations/0093_auto_20200423_2028.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.28 on 2020-04-23 18:28
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("users", "0092_auto_20200423_1804"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name="ldapuser", old_name="ssh_keys", new_name="sshkeys",
|
||||||
|
),
|
||||||
|
]
|
23
users/migrations/0094_auto_20200423_2030.py
Normal file
23
users/migrations/0094_auto_20200423_2030.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.28 on 2020-04-23 18:30
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import ldapdb.models.fields
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("users", "0093_auto_20200423_2028"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="ldapuser",
|
||||||
|
name="sshkeys",
|
||||||
|
field=ldapdb.models.fields.ListField(
|
||||||
|
blank=True, db_column="sshkeys", max_length=200, null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -105,7 +105,7 @@ def linux_user_validator(login):
|
||||||
pas les contraintes unix (maj, min, chiffres ou tiret)"""
|
pas les contraintes unix (maj, min, chiffres ou tiret)"""
|
||||||
if not linux_user_check(login):
|
if not linux_user_check(login):
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
_("The username \"%(label)s\" contains forbidden characters."),
|
_('The username "%(label)s" contains forbidden characters.'),
|
||||||
params={"label": login},
|
params={"label": login},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -405,7 +405,10 @@ class User(
|
||||||
@cached_property
|
@cached_property
|
||||||
def get_shadow_expire(self):
|
def get_shadow_expire(self):
|
||||||
"""Return the shadow_expire value for the user"""
|
"""Return the shadow_expire value for the user"""
|
||||||
if self.state == self.STATE_DISABLED or self.email_state == self.EMAIL_STATE_UNVERIFIED:
|
if (
|
||||||
|
self.state == self.STATE_DISABLED
|
||||||
|
or self.email_state == self.EMAIL_STATE_UNVERIFIED
|
||||||
|
):
|
||||||
return str(0)
|
return str(0)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
@ -670,7 +673,12 @@ class User(
|
||||||
self.full_archive()
|
self.full_archive()
|
||||||
|
|
||||||
def ldap_sync(
|
def ldap_sync(
|
||||||
self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False
|
self,
|
||||||
|
base=True,
|
||||||
|
access_refresh=True,
|
||||||
|
mac_refresh=True,
|
||||||
|
group_refresh=False,
|
||||||
|
sshkeys_refresh=False,
|
||||||
):
|
):
|
||||||
""" Synchronisation du ldap. Synchronise dans le ldap les attributs de
|
""" Synchronisation du ldap. Synchronise dans le ldap les attributs de
|
||||||
self
|
self
|
||||||
|
@ -734,6 +742,10 @@ class User(
|
||||||
for group in Group.objects.all():
|
for group in Group.objects.all():
|
||||||
if hasattr(group, "listright"):
|
if hasattr(group, "listright"):
|
||||||
group.listright.ldap_sync()
|
group.listright.ldap_sync()
|
||||||
|
if sshkeys_refresh:
|
||||||
|
user_ldap.sshkeys = [
|
||||||
|
str(key.key) for key in SSHKey.objects.filter(user=self)
|
||||||
|
]
|
||||||
user_ldap.save()
|
user_ldap.save()
|
||||||
|
|
||||||
def ldap_del(self):
|
def ldap_del(self):
|
||||||
|
@ -1051,7 +1063,7 @@ class User(
|
||||||
False,
|
False,
|
||||||
_(
|
_(
|
||||||
"Impossible to edit the organisation's"
|
"Impossible to edit the organisation's"
|
||||||
" user without the \"change_all_users\" right."
|
' user without the "change_all_users" right.'
|
||||||
),
|
),
|
||||||
("users.change_all_users",),
|
("users.change_all_users",),
|
||||||
)
|
)
|
||||||
|
@ -1120,7 +1132,8 @@ class User(
|
||||||
if not (
|
if not (
|
||||||
(
|
(
|
||||||
self.pk == user_request.pk
|
self.pk == user_request.pk
|
||||||
and OptionalUser.get_cached_value("self_room_policy") != OptionalUser.DISABLED
|
and OptionalUser.get_cached_value("self_room_policy")
|
||||||
|
!= OptionalUser.DISABLED
|
||||||
)
|
)
|
||||||
or user_request.has_perm("users.change_user")
|
or user_request.has_perm("users.change_user")
|
||||||
):
|
):
|
||||||
|
@ -1263,7 +1276,7 @@ class User(
|
||||||
can = user_request.is_superuser
|
can = user_request.is_superuser
|
||||||
return (
|
return (
|
||||||
can,
|
can,
|
||||||
_("\"superuser\" right required to edit the superuser flag.")
|
_('"superuser" right required to edit the superuser flag.')
|
||||||
if not can
|
if not can
|
||||||
else None,
|
else None,
|
||||||
[],
|
[],
|
||||||
|
@ -1357,9 +1370,7 @@ class User(
|
||||||
# Allow empty emails only if the user had an empty email before
|
# Allow empty emails only if the user had an empty email before
|
||||||
is_created = not self.pk
|
is_created = not self.pk
|
||||||
if not self.email and (self.__original_email or is_created):
|
if not self.email and (self.__original_email or is_created):
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(_("Email field cannot be empty."))
|
||||||
_("Email field cannot be empty.")
|
|
||||||
)
|
|
||||||
|
|
||||||
self.email = self.email.lower()
|
self.email = self.email.lower()
|
||||||
|
|
||||||
|
@ -1432,9 +1443,7 @@ class Adherent(User):
|
||||||
a user or if the `options.all_can_create` is set.
|
a user or if the `options.all_can_create` is set.
|
||||||
"""
|
"""
|
||||||
if not user_request.is_authenticated:
|
if not user_request.is_authenticated:
|
||||||
if not OptionalUser.get_cached_value(
|
if not OptionalUser.get_cached_value("self_adhesion"):
|
||||||
"self_adhesion"
|
|
||||||
):
|
|
||||||
return False, _("Self registration is disabled."), None
|
return False, _("Self registration is disabled."), None
|
||||||
else:
|
else:
|
||||||
return True, None, None
|
return True, None, None
|
||||||
|
@ -2000,6 +2009,9 @@ class LdapUser(ldapdb.models.Model):
|
||||||
shadowexpire = ldapdb.models.fields.CharField(
|
shadowexpire = ldapdb.models.fields.CharField(
|
||||||
db_column="shadowExpire", blank=True, null=True
|
db_column="shadowExpire", blank=True, null=True
|
||||||
)
|
)
|
||||||
|
sshkeys = ldapdb.models.fields.ListField(
|
||||||
|
db_column="sshkeys", max_length=200, blank=True, null=True
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -2248,3 +2260,53 @@ class EMailAddress(RevMixin, AclMixin, models.Model):
|
||||||
if result:
|
if result:
|
||||||
raise ValidationError(reason)
|
raise ValidationError(reason)
|
||||||
super(EMailAddress, self).clean(*args, **kwargs)
|
super(EMailAddress, self).clean(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class SSHKey(RevMixin, AclMixin, models.Model):
|
||||||
|
"""Represents an SSH public key belonging to a user."""
|
||||||
|
|
||||||
|
key = models.TextField(blank=True, verbose_name=_("Public ssh key"))
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
permissions = (("view_sshkey", _("Can view an SSHKey object")),)
|
||||||
|
verbose_name = _("SSH key")
|
||||||
|
verbose_name_plural = _("SSH keys")
|
||||||
|
|
||||||
|
def can_edit(self, user_request, *_args, **_kwargs):
|
||||||
|
"""Check if a user can edit the SSH key
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_request: The user who wants to edit the object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
a message and a boolean which is True if the user can edit
|
||||||
|
the local email account.
|
||||||
|
"""
|
||||||
|
if self.user == user_request or user_request.has_perm("users.edit_sshkey"):
|
||||||
|
return True, None, None
|
||||||
|
return (
|
||||||
|
False,
|
||||||
|
_("You don't have the right to edit another user's SSHKey."),
|
||||||
|
("users.edit_sshkey",),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=SSHKey)
|
||||||
|
def sshkey_post_save(**kwargs):
|
||||||
|
"""Sync LDAP record for user when SSHKey is saved."""
|
||||||
|
key = kwargs["instance"]
|
||||||
|
is_created = kwargs["created"]
|
||||||
|
user = key.user
|
||||||
|
user.ldap_sync(
|
||||||
|
base=False, access_refresh=False, mac_refresh=False, sshkeys_refresh=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_delete, sender=SSHKey)
|
||||||
|
def sshkey_post_delete(**kwargs):
|
||||||
|
"""Sync LDAP record for user when SSHKey is deleted"""
|
||||||
|
user = kwargs["instance"].user
|
||||||
|
user.ldap_sync(
|
||||||
|
base=False, access_refresh=False, mac_refresh=False, sshkeys_refresh=True
|
||||||
|
)
|
||||||
|
|
60
users/templates/users/aff_sshkeys.html
Normal file
60
users/templates/users/aff_sshkeys.html
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Lara Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% load acl %}
|
||||||
|
{% load logs_extra %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if sshkeys.paginator %}
|
||||||
|
{% include 'pagination.html' with list=sshkeys %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "SSH key" %}</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for sshkey in sshkeys %}
|
||||||
|
<td>{{ sshkey.key }}</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{% can_delete sshkey %}
|
||||||
|
{% include 'buttons/suppr.html' with href='users:del-sshkey' id=sshkey.id %}
|
||||||
|
{% acl_end %}
|
||||||
|
{% history_button sshkey %}
|
||||||
|
{% can_edit sshkey %}
|
||||||
|
{% include 'buttons/edit.html' with href='users:edit-sshkey' id=sshkey.id %}
|
||||||
|
{% acl_end %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% if sshkey.paginator %}
|
||||||
|
{% include 'pagination.html' with list=sshkey %}
|
||||||
|
{% endif %}
|
|
@ -562,6 +562,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading clearfix profil" data-parent="#accordion" data-toggle="collapse" data-target="#sshkeys">
|
||||||
|
<h3 class="panel-title pull-left">
|
||||||
|
<i class="fa fa-key"></i>
|
||||||
|
{% trans "SSH keys" %}
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div id="sshkeys" class="panel-collapse collapse">
|
||||||
|
<div class="panel-body">
|
||||||
|
{% can_edit users %}
|
||||||
|
{% can_create SSHKey %}
|
||||||
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'users:add-sshkey' users.id %}">
|
||||||
|
<i class="fa fa-key"></i>
|
||||||
|
{% trans "Add an SSH key" %}
|
||||||
|
</a>
|
||||||
|
{% acl_end %}
|
||||||
|
{% acl_end %}
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
{% if sshkeys %}
|
||||||
|
{% include 'users/aff_sshkeys.html' with sshkeys=sshkeys %}
|
||||||
|
{% else %}
|
||||||
|
<p>{% trans "No SSH key" %}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% for template in optionnal_templates_list %}
|
{% for template in optionnal_templates_list %}
|
||||||
{{ template }}
|
{{ template }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -44,7 +44,11 @@ urlpatterns = [
|
||||||
url(r"^state/(?P<userid>[0-9]+)$", views.state, name="state"),
|
url(r"^state/(?P<userid>[0-9]+)$", views.state, name="state"),
|
||||||
url(r"^groups/(?P<userid>[0-9]+)$", views.groups, name="groups"),
|
url(r"^groups/(?P<userid>[0-9]+)$", views.groups, name="groups"),
|
||||||
url(r"^password/(?P<userid>[0-9]+)$", views.password, name="password"),
|
url(r"^password/(?P<userid>[0-9]+)$", views.password, name="password"),
|
||||||
url(r"^confirm_email/(?P<userid>[0-9]+)$", views.resend_confirmation_email, name="resend-confirmation-email"),
|
url(
|
||||||
|
r"^confirm_email/(?P<userid>[0-9]+)$",
|
||||||
|
views.resend_confirmation_email,
|
||||||
|
name="resend-confirmation-email",
|
||||||
|
),
|
||||||
url(
|
url(
|
||||||
r"^del_group/(?P<userid>[0-9]+)/(?P<listrightid>[0-9]+)$",
|
r"^del_group/(?P<userid>[0-9]+)/(?P<listrightid>[0-9]+)$",
|
||||||
views.del_group,
|
views.del_group,
|
||||||
|
@ -127,4 +131,7 @@ urlpatterns = [
|
||||||
url(r"^$", views.index, name="index"),
|
url(r"^$", views.index, name="index"),
|
||||||
url(r"^index_clubs/$", views.index_clubs, name="index-clubs"),
|
url(r"^index_clubs/$", views.index_clubs, name="index-clubs"),
|
||||||
url(r"^initial_register/$", views.initial_register, name="initial-register"),
|
url(r"^initial_register/$", views.initial_register, name="initial-register"),
|
||||||
|
url(r"^add_sshkey/(?P<userid>[0-9]+)$", views.add_sshkey, name="add-sshkey",),
|
||||||
|
url(r"^edit_sshkey/(?P<sshkeyid>[0-9]+)$", views.edit_sshkey, name="edit-sshkey",),
|
||||||
|
url(r"^del_sshkey/(?P<sshkeyid>[0-9]+)$", views.del_sshkey, name="del-sshkey",),
|
||||||
]
|
]
|
||||||
|
|
|
@ -86,6 +86,7 @@ from .models import (
|
||||||
Club,
|
Club,
|
||||||
ListShell,
|
ListShell,
|
||||||
EMailAddress,
|
EMailAddress,
|
||||||
|
SSHKey,
|
||||||
)
|
)
|
||||||
from .forms import (
|
from .forms import (
|
||||||
BanForm,
|
BanForm,
|
||||||
|
@ -110,6 +111,7 @@ from .forms import (
|
||||||
ClubAdminandMembersForm,
|
ClubAdminandMembersForm,
|
||||||
GroupForm,
|
GroupForm,
|
||||||
InitialRegisterForm,
|
InitialRegisterForm,
|
||||||
|
SSHKeyForm,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -932,6 +934,7 @@ def profil(request, users, **_kwargs):
|
||||||
request.GET.get("order"),
|
request.GET.get("order"),
|
||||||
SortTable.USERS_INDEX_WHITE,
|
SortTable.USERS_INDEX_WHITE,
|
||||||
)
|
)
|
||||||
|
sshkeys = users.sshkey_set.all()
|
||||||
try:
|
try:
|
||||||
balance = find_payment_method(Paiement.objects.get(is_balance=True))
|
balance = find_payment_method(Paiement.objects.get(is_balance=True))
|
||||||
except Paiement.DoesNotExist:
|
except Paiement.DoesNotExist:
|
||||||
|
@ -956,6 +959,7 @@ def profil(request, users, **_kwargs):
|
||||||
"local_email_accounts_enabled": (
|
"local_email_accounts_enabled": (
|
||||||
OptionalUser.objects.first().local_email_accounts_enabled
|
OptionalUser.objects.first().local_email_accounts_enabled
|
||||||
),
|
),
|
||||||
|
"sshkeys": sshkeys,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1101,3 +1105,51 @@ def initial_register(request):
|
||||||
request,
|
request,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@can_create(SSHKey)
|
||||||
|
@can_edit(User)
|
||||||
|
def add_sshkey(request, user, userid):
|
||||||
|
"""Create an SSHKey for the given user."""
|
||||||
|
sshkey_instance = SSHKey(user=user)
|
||||||
|
sshkey = SSHKeyForm(request.POST or None, instance=sshkey_instance)
|
||||||
|
|
||||||
|
if sshkey.is_valid():
|
||||||
|
sshkey.save()
|
||||||
|
messages.success(request, _("The SSH key was added."))
|
||||||
|
return redirect(reverse("users:profil", kwargs={"userid": str(userid)}))
|
||||||
|
|
||||||
|
return form(
|
||||||
|
{"userform": sshkey, "action_name": _("Add")}, "users/user.html", request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@can_edit(SSHKey)
|
||||||
|
def edit_sshkey(request, sshkey_instance, **_kwargs):
|
||||||
|
"""Edit an SSHKey for the given user."""
|
||||||
|
sshkey = SSHKeyForm(request.POST or None, instance=sshkey_instance)
|
||||||
|
sshkey.request = request
|
||||||
|
|
||||||
|
if sshkey.is_valid():
|
||||||
|
if sshkey.changed_data:
|
||||||
|
sshkey.save()
|
||||||
|
messages.success(request, _("The SSH Key was edited."))
|
||||||
|
return redirect(reverse("users:profil", kwargs={"userid": str(userid)}))
|
||||||
|
|
||||||
|
return form(
|
||||||
|
{"userform": sshkey, "action_name": _("Edit")}, "users/user.html", request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@can_delete(SSHKey)
|
||||||
|
def del_sshkey(request, sshkey, **_kwargs):
|
||||||
|
"""Delete SSH key."""
|
||||||
|
if request.method == "POST":
|
||||||
|
sshkey.delete()
|
||||||
|
messages.success(request, _("The SSH key was deleted."))
|
||||||
|
return redirect(reverse("users:profil", kwargs={"userid": str(sshkey.user.id)}))
|
||||||
|
return form(
|
||||||
|
{"objet": sshkey, "objet_name": _("SSH key")}, "users/delete.html", request
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in a new issue