Usuellement, les utilisateurs d'un site Zope sont différenciés (outre leur login) par l'attribution d'un rôle créé par le concepteur ("user defined role") : "staff", "technical", "teacher", "student", etc ... Si l'on souhaite que les utilisateurs disposent de privilèges particuliers sur une partie de l'arborescence, deux méthodes sont proposées :
  • les permissions affectées au profil sont modifiées par le Manager depuis le Folder constituant le point de départ de la hiérarchie à impacter ;
    cela a un effet sur tous les utilisateurs bénéficiant du même rôle ... cela peut augmenter les risques d'erreur de manipulation ...
  • tous les utilisateurs ne devant pas être impactés, le Manager crée localement des "acl_users" où il redéclare quelques utilisateurs en leur affectant un rôle supplémentaire ...
    mais cela va compliquer sa gestion des utilisateurs, et rendre délicat la réalisation de fonctions de personnalisation (changement de password par exemple ...).

La gestion de "local roles" permet de s'affranchir de ces problèmes :

  • on ne gère qu'un seul "acl_users", à la racine du site ;
  • on envisage 2 sortes de "user defined roles" :
    • les rôles fonctionnels locaux : "reviewer", "writer", etc.
    • les catégories d'utilisateurs : "GroupA", "GroupB", etc.

Les "catégories" sont affectées dans l'"acl_users" ; elles s'appliquent à tout le site ; les "rôles fonctionnels" sont affectés depuis le Folder à partir duquel l'utilisateur doit jouir de ses privilèges étendus.

La catégorie et les privilèges locaux de l'utilisateur sont toujours disponibles :

Si l'utilisateur s'est identifié :

"_.SecurityGetUser().getUserName() != 'Anonymous User'"

Si l'utilisateur appartient à la catégorie "GroupA" :

"'GroupA' in _.SecurityGetUser().getRoles()"

ou encore :

"_.SecurityGetUser().has_role('GroupA')"

Et l'on peut évaluer les privilèges locaux, en faisant explicitement référence à l'objet (Folder ou DTMLDocument, ou autre) :

"_.SecurityGetUser().has_role('reviewer', mon_objet)"

Il est donc aisé de proposer des contenus, des liens ... en fonction de la configuration utilisateur.

Mais allons plus loin dans la manipulation conjointe des "groupes" et des "privilèges" locaux : il facile d'imaginer qu'au sein d'un groupe donné une personne joue un rôle d'animateur ; il pourrait être interessant de lui permettre de modifier la composition de son groupe, et d'attribuer à quelques membres des privilèges intermédiaires (mettre à jour ... ).

Voic quelques indications, fortement teintée DTML/PythonScript (que les fans de ZPT me pardonnent ...) :

Pour lister les membres d'un groupe :

""""
listUserGroup(Group)
Script Python pour récupérer la liste des utilisateurs bénéficiants du rôle ("user defined") 'Group'
paramètre : 'Group' (string)
""""
groupusers = []
users = context.acl_users.getUserNames()
for user in users:
    if Group in context.acl_users.getUser(user).getRoles():
       groupusers.append(user)

return groupusers

Affecter ou retirer le rôle 'GroupA' à un nouvel utilisateur ; cette fonction est un "toggle" :
si le rôle est affecté, on aura appelé ce script pour retirer le rôle
sinon, on veut ajouter ce rôle

""""
Script Python pour affecter/retirer un rôle ("user defined") 'Group' à un utilisateur donné
paramètres : 'Group' (string), 'user'(string)
""""

def deleterole(roles, role):
    newroles = []
    for keep in roles:    
        if keep != role:
            newroles.append(keep)
            
    return newroles

newroles = []
roles = context.acl_users.getUser(user).getRoles():
if Group in roles
    newroles = deleterole(roles, Group)
else:
    newroles = roles.append(Group)

return newroles

Pour affecter le rôle local "writer" à l'un des utilisateurs :

"getattr(context, objet).manage_addLocalRoles(user, ('writer',))"

Pour retirer le privilège "writer", mais en conservant éventuellement d'autres rôles (en particulier 'Owner' ...) :

""""
Script Python pour retirer le "local role" 'Group' à un utilisateur donné sur un objet donné
paramètres : 'Group' (string), 'user'(string)
"""

def deleterole(roles, role):
    newroles = []
    for keep in roles:
        if keep != role:
           newroles.append(keep)
        
    return newroles

newroles = []
roles = list(getattr(context, objet).get_local_roles_for_userid(user))
if 'writer' in roles
    newroles = deleterole(roles, 'writer')

if len(newroles) == 0:
    getattr(context, objet).manage_delLocalRoles((user,))
else:
    getattr(context, objet).manage_setLocalRoles(user, newroles)

Ces deux derniers codes doivent être utilisés dans un script Python doté d'un "proxy role" 'Manager' ; seront déclarés en paramètres l'objet à impacter et le nom de l'utilisateur concerné.

Nota technique :

  • la fonction "manage_delLocalRoles" demande une liste d'utilisateurs !
  • l'utilisation de "getattr(obj, attr)" permet de s'assurer que l'objet impacté est bien transmis ...