Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve the management of user permissions #28

Closed
GerardPaligot opened this issue Dec 19, 2013 · 31 comments
Closed

Improve the management of user permissions #28

GerardPaligot opened this issue Dec 19, 2013 · 31 comments

Comments

@GerardPaligot
Copy link
Member

J'ai l'impression que la gestion des permissions liées aux sanctions pourraient être améliorées.

Actuellement, c'est géré par 2 booléens et 2 dates associées à ces booléens. Cependant, j'ai cru comprendre qu'un système de permissions était implémenté automatiquement dans Django à travers les Meta.

Il faudrait penser à migrer le système actuel par ce système intégré au framework. Ca rendrait le code vraiment plus joli que de gérer des booléens.

Et, si j'ai bien compris le module du forum, on aurait plus qu'à utiliser des annotations sur les différentes méthodes de views.py pour exécuter ou non la méthode. Les instructions suivantes deviendront inutiles :

profile = Profile.objects.filter(user__pk=request.user.pk).all()
if len(profile)>0:
    if not profile[0].can_read_now():
        raise Http404
@firm1
Copy link
Contributor

firm1 commented Dec 20, 2013

Actuellement, c'est géré par 2 booléens et 2 dates associées à ces booléens. Cependant, j'ai cru comprendre qu'un système de permissions était implémenté automatiquement dans Django à travers les Meta

La notions de LS/Ban temporaire n'est pas une notions gérée par django. Si on veut gérer le fait qu'un utilisation soit sanctionné un moment et que cette sanction prenne fin toute seule (sans devoir passer manuellement ou encore sans qu'un bot serveur fasse les réactivation) on est obligé de gérer une date de fin de sanction. Et là je ne vois pas comment se passer autrement des deux dates et des methodes can_<read/write>_now().

Après c'est surement faisable en annotations. Mais je ne m'y suis pas encore penché.

@GerardPaligot
Copy link
Member Author

J'ai un peu testé, les permissions avec Django sont vraiment élégantes. Par exemple, si nous voulons implémenter la lecture seule et le bannissement, il suffirait de :

1/ Rajouter les permissions dans la classe Meta du Profile.

class Profile(models.Model):

    '''Represents an user profile'''
    class Meta:
        verbose_name = 'Profil'
        verbose_name_plural = 'Profils'
        permissions = (
                ("moderation", u"Modérer un membre"),
                ("show_ip", u"Afficher les IP d'un membre"),
                ("can_read", "Possibilité de lecture"),
                ("can_write", "Possibilité d'écriture"),
        )

2/ Ajouter ou supprimer ces permissions sur les utilisateurs (à faire sur la classe User et non Profile). Par défaut, ils ne possèdent pas la permission.

myuser.user_permissions = [permission_list]
myuser.user_permissions.add(permission, permission, ...)
myuser.user_permissions.remove(permission, permission, ...)
myuser.user_permissions.clear()

3/ Obliger l'utilisateur à avoir la permission pour accéder à la vue voulue :

from django.contrib.auth.decorators import permission_required

@permission_required('member.can_read')
def topic(request):
    # Do something

A savoir qu'il existe aussi des méthodes pour savoir si l'utilisateur à les permissions :

myuser.has_perm('member.can_read')

Voilà, c'est qu'une idée mais c'est quand même vachement plus élégant que des tests au début de toutes les méthodes concernées par la permission. Après, c'est vrai que ça nécessite de réfléchir à comment mettre fin à la sanction de l'utilisateur. Je pense qu'il serait possible de trouver facilement une solution. Genre gérer les permissions à la re-connexion de l'utilisateur.

Voilà, qu'en penses-tu ?

@SpaceFox
Copy link
Contributor

C'est classe.

Le 20 décembre 2013 10:20, Gerard notifications@github.com a écrit :

J'ai un peu testé, les permissions avec Django sont vraiment élégantes.
Par exemple, si nous voulons implémenter la lecture seule et le
bannissement, il suffirait de :

1/ Rajouter les permissions dans la classe Meta du Profile.

class Profile(models.Model):

'''Represents an user profile'''
class Meta:
    verbose_name = 'Profil'
    verbose_name_plural = 'Profils'
    permissions = (
            ("moderation", u"Modérer un membre"),
            ("show_ip", u"Afficher les IP d'un membre"),
            ("can_read", "Possibilité de lecture"),
            ("can_write", "Possibilité d'écriture"),
    )

2/ Ajouter ou supprimer ces permissions sur les utilisateurs (à faire sur
la classe User et non Profile). Par défaut, ils ne possèdent pas la
permission.

myuser.user_permissions = [permission_list]myuser.user_permissions.add(permission, permission, ...)myuser.user_permissions.remove(permission, permission, ...)myuser.user_permissions.clear()

3/ Obliger l'utilisateur à avoir la permission pour accéder à la vue
voulue :

from django.contrib.auth.decorators import permission_required
@permission_required('member.can_read')@permission_required('member.can_write')def topic(request):
# Do something

A savoir qu'il existe aussi des méthodes pour savoir si l'utilisateur à
les permissions :

myuser.has_perm('member.can_read')

Voilà, c'est qu'une idée mais c'est quand même vachement plus élégant que
des tests au début de toutes les méthodes concernées par la permission.
Après, c'est vrai que ça nécessite de réfléchir à comment mettre fin à la
sanction de l'utilisateur. Je pense qu'il serait possible de trouver
facilement une solution. Genre gérer les permissions à la re-connexion de
l'utilisateur.

Voilà, qu'en penses-tu ?


Reply to this email directly or view it on GitHubhttps://github.com//issues/28#issuecomment-30997539
.

@firm1
Copy link
Contributor

firm1 commented Dec 20, 2013

Voilà, qu'en penses-tu ?

Tu as bien saisi le comportement de Django, et c'est ainsi que je gère les permissions pour tout le reste.

Le seul problème posé ici c'est que si on veut faire de la sanction temporaire, ça devient tout de suite moins trivial.

L'idéal serait de surcharger la méthode d'une annotation particulière mais encore une fois, c'est un autre paire de manche. Et je n'ai pas envie de gérer les de-ban manuellement ni par tâches cron.

@SpaceFox
Copy link
Contributor

Ça pose vraiment un problème les tâches cron pour ce faire ?

Le 20 décembre 2013 10:29, dralliw notifications@github.com a écrit :

Tu as bien saisi le comportement de Django, et c'est ainsi que je gère les
permissions pour tout le reste.

Le seul problème posé ici c'est que si on veut faire du temporaire, ça
devient tout de suite moins trivial.

L'idéal serait de surcharger la méthode d'une annotation particulière mais
encore une fois, c'est un autre paire de manche. Et je n'ai pas envie de
gérer les de-ban manuellement ni par tâches cron.


Reply to this email directly or view it on GitHubhttps://github.com//issues/28#issuecomment-30997957
.

@firm1
Copy link
Contributor

firm1 commented Dec 20, 2013

Ça pose vraiment un problème les tâches cron pour ce faire ?

Disons, que tu es quand même obligé de stocké ta date de fin de sanction et que ta cron tache dois parcourir (tous les jours? heures ?) tes membres sanctionné. Je trouve ça un peu lourd à faire par le serveur, qu'une simple surcharge de méthode (dans la mesure du possible).

@Coy0te
Copy link
Contributor

Coy0te commented Dec 20, 2013

Ce genre de modifs, c'est du confort et ça rentre donc dans le scope d'une version ultérieure. Pour le moment on a un truc qui marche, c'est le principal.

En bref, vous gâchez du temps de cerveau disponible de Willard, laissez-le travailler sur le modules des tutos !! :D

@GerardPaligot
Copy link
Member Author

Je me suis un peu re-penché sur cette issue et je dois dire que c'est plutôt simple de créer ses propres annotations. Pour ceux qui ne savent pas comment faire et pour reprendre l'exemple de mon premier poste, voici le code qui permet de créer une annotation @can_read_now :

def can_read_now(func):
    '''Decorator to check that the user can read now'''
    def _can_read_now(request, *args, **kwargs):
        profile = Profile.objects.filter(user__pk=request.user.pk).all()
        if len(profile)>0:
            if not profile[0].can_read_now():
                raise Http404
        return func(request, *args, **kwargs)
    return _can_read_now

Donc pour une méthode décorée comme l'index :

@can_read_now
def index(request):
    pass

L'attribut func, dans le décorateur, correspond à la méthode index. J'appelle une méthode privée où je récupère le request de la méthode et ses arguments s'ils existent (c'est le cas pour d'autres méthodes comme details(request, cat_slug, forum_slug)).

Dans cette méthode privée, il suffit d'y mettre le comportement qu'on veut répéter pour toutes les méthodes annotées @can_read_now.

J'aurais aimé avoir des avis si cela vous semble une solution viable et avoir vos avis pour savoir où je dois placer ces décorateurs.

@cgabard
Copy link
Contributor

cgabard commented Jan 2, 2014

Le decorateur c'est propre et classe mais il reste le prob du cron pour le coté temporaire des sancontions, non ?

@GerardPaligot
Copy link
Member Author

Pour l'exemple ci-dessus, je ne mentionnais pas une application pour les sanctions temporaires.

Si tu veux parler des sanctions temporaires, à mon sens, il faudrait une table propre aux sanctions avec l'utilisateur, le type de bannissement et la date de fin. A partir de là, ça serait très simple de les gérer dans un décorateur. :)

@firm1
Copy link
Contributor

firm1 commented Jan 2, 2014

J'aurais aimé avoir des avis si cela vous semble une solution viable et avoir vos avis pour savoir où je dois placer ces décorateurs.

Oh mais elle est juste magnifique cette solution. Je savais bien qu'on pouvais gérer ça proprement à coup de décorateur, mais je ne m'y étais pas encore penché.

Cette solution règle bien le problème de la répétition du code dans les fonctions, et permet de gérer les sanctions temporaires avec le modèles existant (on ne peut pas faire de sanctions temporaires sans rajouter des dates de fin de validité dans le modèle).

Pour l'implémentation, il faudrait créer un fichier zds/member/decorator.py et y renseigner ton décorateur qui sera appelé ensuite sur les méthodes de lecture/écriture du forum/tuto/articles/etc. Tout ceci dans une PR, et c'est validé direct.

@GerardPaligot
Copy link
Member Author

J'intègre la gestion des sanctions dans le PR ou simplement la répétition sur la lecture/écriture ou non ?

Dans le second cas, je peux déjà soumettre. :)

@dreherch
Copy link

dreherch commented Jan 2, 2014

Si tu peux ne pas l'intégrer, c'est que c'est deux choses différentes, du
coup, il vaut mieux le faire en 2 PR séparées, non ?

Le 2 janvier 2014 14:48, Gerard notifications@github.com a écrit :

J'intègre la gestion des sanctions dans les PR ou simplement la répétition
sur la lecture/écriture ou non ?

Dans le second cas, je peux déjà soumettre. :)


Reply to this email directly or view it on GitHubhttps://github.com//issues/28#issuecomment-31453269
.

@GerardPaligot
Copy link
Member Author

Ce n'est pas si différent que ça. C'est juste que j'ai pas trop de temps en ce moment. Du coup, soit je soumets mon PR maintenant avec les annotations @can_read_now et @can_read_and_write_now ou plus tard (je ne sais pas quand), avec une gestion plus poussée des sanctions.

@firm1
Copy link
Contributor

firm1 commented Jan 2, 2014

Si tu veux parler des sanctions temporaires, à mon sens, il faudrait une table propre aux sanctions avec l'utilisateur, le type de bannissement et la date de fin. A partir de là, ça serait très simple de les gérer dans un décorateur. :)

Il y'a plusieurs chose à gérer :

  • Le fait qu'un utilisateur ait la capacité de lire/écrire , qui pour moi, devrait être visible directement dans la table profil utilisateur. On aura besoin de ces informations à chaque fois qu'il faudra lire/écrire sur le forum. Actuellement les attributs can_read/can_write et end_ban_read/end_ban_write jouent très bien ce rôle et évite de faire une jointure avec je ne sais quelle autre table à chaque demande d'un utilisateur.
  • L'historique des sanctions, qui doit être dans une table à part, et n'est consulté, que lorsqu'on a besoin de l'avoir (c'est à dire, sur la page profil du membre). La table membre.Ban joue donc très bien ce rôle.

Donc, en principe, je ne vois pas de modification particulière à faire pour la gestion "poussée" des sanctions, et donc, une PR pour décorer un peu les fonctions adéquates serait suffisante selon moi.

@GerardPaligot
Copy link
Member Author

Oui, j'ai tendance à vouloir bien tout diviser mais j'oubliais que les jointures étaient couteuses pour un système comme celui-ci. Du coup, j'étais partie là pour terminer la possibilité de lire ou d'écrire pour un utilisateur quand il est sanctionné et je me suis posé plusieurs questions :

  1. Dans la méthode du décorateur, pourquoi nous tentons de récupérer l'utilisateur courant via une liste alors que nous pourrions directement le récupérer par la méthode get ?
  2. Je ne parviens pas à trouver où nous mettons fin à une sanction. Soit c'est implémenté et j'aimerais bien savoir où, soit ça ne l'est pas et ça serait bien d'en discuter.
  3. Il faudrait aussi discuter de ce que peut faire un membre en lecture seule et banni pour annoter les autres modules (tutoriels, articles, mp).
  4. Tu as une idée des fonctions adéquates ? J'ai un peu regardé, j'en trouve pas des masses (sauf si nous en rajoutons lorsque nous discuterons du point 3).

@firm1
Copy link
Contributor

firm1 commented Jan 3, 2014

Dans la méthode du décorateur, pourquoi nous tentons de récupérer l'utilisateur courant via une liste alors que nous pourrions directement le récupérer par la méthode get ?

ça c'est ce que j'appelle une grosse connerie de ma part. En effet, la méthode get est plus adaptée ici.

Je ne parviens pas à trouver où nous mettons fin à une sanction. Soit c'est implémenté et j'aimerais bien savoir où, soit ça ne l'est pas et ça serait bien d'en discuter.

  • Les sanctions temporaires prennent fin toutes seules grâce à la date
  • La LS est défaite ici
  • Le Ban est dafait ici

Il faudrait aussi discuter de ce que peut faire un membre en lecture seule et banni pour annoter les autres modules (tutoriels, articles, mp).

Pour moi

  • un membre en lecture seule est comme un visiteur à la seule différence qu'il peut envoyer envoyer recevoir des MP
  • un membre banni ne peut même pas se connecter avec son compte

Tu as une idée des fonctions adéquates ? J'ai un peu regardé, j'en trouve pas des masses (sauf si nous en rajoutons lorsque nous discuterons du point 3).

Pour les LS, déjà je vois toutes les fonctions qui sont annotées en tant que @login_required dans la views.py du forum. Pour le Ban, j'ai envie de dire, il faut déjà annoter la méthode de connexion (interdiction de se connecter si on est ban).

@cgabard
Copy link
Contributor

cgabard commented Jan 3, 2014

Pour les LS, déjà je vois toutes les fonctions qui sont annotées en tant que @login_required dans la views.py du forum. Pour le Ban, j'ai envie de dire, il faut déjà annoter la méthode de connexion (interdiction de se connecter si on est ban).

Pour le ban Ok tu peux l'empecher de se connecter a condition de le deconnecter au moment de l'application de la sanction

@firm1
Copy link
Contributor

firm1 commented Jan 3, 2014

Pour le ban Ok tu peux l'empecher de se connecter a condition de le deconnecter au moment de l'application de la sanction

Toutafè

@GerardPaligot
Copy link
Member Author

J'ai intégré les dernières modifications. Juste 2 questions :

  • Comment je fais pour déconnecter un autre utilisateur ?
  • Si nous interdisons à un membre banni de se connecter, est-ce que le décorateur can_read_now() a vraiment du sens ? Actuellement, je le mentionne partout dans les méthodes du fichier forum/views.py mais, normalement, faudrait annoter absolument toutes les méthodes de tous les modules avec cette annotation.

Après ces réponses, je fais un nouveau PR de la branche permission.

@cgabard
Copy link
Contributor

cgabard commented Jan 4, 2014

En effet si tu deconnecte pour le ban, pas besoin d'annoter ainsi. En fait cela aurait une certaine logique si tu aitorisais par exemple a un banni de consulter ces mp. La, en effet, ne t'embete pas.

@firm1
Copy link
Contributor

firm1 commented Jan 5, 2014

Comment je fais pour déconnecter un autre utilisateur ?

from django.contrib.auth import logout

def logout_view(request):
    '''Log out user'''
    logout(request)
    request.session.clear()

Si nous interdisons à un membre banni de se connecter, est-ce que le décorateur can_read_now() a vraiment du sens ? Actuellement, je le mentionne partout dans les méthodes du fichier forum/views.py mais, normalement, faudrait annoter absolument toutes les méthodes de tous les modules avec cette annotation.

J'ai dis "empêcher de se connecter", c'est une proposition, pas forcément décidée. On peut aussi dire que le membre ban doit pouvoir lire ses MP. Quelque soit la décision prise au final, le can_read_now() sera toujours utile à court terme.

@GerardPaligot
Copy link
Member Author

La méthode logout_view(request) déconnecte l'utilisateur qui fait la requête. Comme c'est un membre du staff qui va lancer la requête en sanctionnant un membre, appeller logout_view(request) ne déconnectera pas l'utilisateur concerné mais le membre du staff.

can_read_now() sera peut-être utile à l'avenir mais si, dans un premier temps, nous permettons à l'utilisateur sanctionné de ne rien pouvoir faire, dans quel cas ce décorateur sera-t-il utile ?

@firm1
Copy link
Contributor

firm1 commented Jan 6, 2014

Ah si tu comptes déconnecter un utilisateur a distance, ça ne marchera pas. Faudra le déconnecter s'il essaye d'accéder a une vue quelconque (et dans ce cas c'est lui qui invoque le logout(request)). Et donc décorer toutes les autres vues que tu souhaites interdire (ce qui résoudre le dilemme de l'utilité de can_read_now ... Je te l'avais dis que tôt ou tard on en aurais besoin).

En somme, tu trouveras du code pas utilisé (mais dont je sais qu'on en aura besoin, a moyen/long terme) si tu regarde bien, mais l'avantage de ce genre de pratique est que le jour ou j'en ai besoin, je n'ai plus a me replacer dans le contexte du problème en plus d'avoir déjà un squelette de la façon de faire.

@GerardPaligot
Copy link
Member Author

Wow, c'est un peu chaud comme solution ça. Je vais devoir annoter absolument toutes les méthodes des vues des différents modules. C'est bizarre qu'il n'existe aucune solution pour déconnecter un utilisateur mais si nous n'avons pas le choix ...

@Coy0te
Copy link
Contributor

Coy0te commented Jan 8, 2014

Si jamais vous ne l'avez pas lu, y'a quelques pistes ici :
=> http://stackoverflow.com/questions/953879/how-to-force-user-logout-in-django

@GerardPaligot
Copy link
Member Author

J'avais vu cette solution mais ça me semblait vraiment couteux de lister toutes les sessions pour récupérer la bonne et la supprimer.

Puis, je ne suis pas un as du systèmes des sessions, mais que se passe-t-il lorsqu'un utilisateur est connecté avec plusieurs appareils ?

Dans tous les cas, cette solution me semble un peu du bricolage.

@Coy0te
Copy link
Contributor

Coy0te commented Jan 8, 2014

La solution avec la classe ActiveUserMiddleware ne conviendrait pas ?

@GerardPaligot
Copy link
Member Author

Cette solution semble pas mal mais elle permet encore une action de l'utilisateur avant de le déconnecter, je ne comprends pas trop comment l'intégrer dans les settings et il semble y avoir des problèmes si l'utilisateur est connecté depuis plusieurs endroits.

@Coy0te
Copy link
Contributor

Coy0te commented Jan 8, 2014

S'il est connecté depuis plusieurs endroits, alors il aura le droit à autant de dernières actions.

Si jamais on ne trouve pas mieux sans aller lister les sessions ouvertes, ça me paraît acceptable comme compromis.

@ghost ghost assigned GerardPaligot Jan 9, 2014
@firm1
Copy link
Contributor

firm1 commented Jan 20, 2014

Cette issue a déjà été corrigée, je ferme donc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants