src/pyams_security/property.py
changeset 2 94e76f8e9828
child 42 07229ac2497b
equal deleted inserted replaced
1:5595823c66f1 2:94e76f8e9828
       
     1 #
       
     2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net>
       
     3 # All Rights Reserved.
       
     4 #
       
     5 # This software is subject to the provisions of the Zope Public License,
       
     6 # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
       
     7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
       
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
       
    10 # FOR A PARTICULAR PURPOSE.
       
    11 #
       
    12 
       
    13 __docformat__ = 'restructuredtext'
       
    14 
       
    15 
       
    16 # import standard library
       
    17 
       
    18 # import interfaces
       
    19 from pyams_security.interfaces import IRole, IProtectedObject, IPrincipalInfo, IRoleProtectedObject
       
    20 from pyams_security.schema import IRoleField
       
    21 from zope.schema.interfaces import IField
       
    22 
       
    23 # import packages
       
    24 
       
    25 
       
    26 class RolePrincipalsFieldProperty(object):
       
    27     """Custom field property used to handle role principals"""
       
    28 
       
    29     def __init__(self, field, role_id=None, name=None, **args):
       
    30         if not IField.providedBy(field):
       
    31             raise ValueError("Provided field must implement IField interface")
       
    32         if (role_id is None) and not IRoleField.providedBy(field):
       
    33             raise ValueError("Provided field must implement IRoleField interface "
       
    34                              "or you must provide a role ID")
       
    35         if role_id is None:
       
    36             role_id = field.role_id
       
    37         elif IRole.providedBy(role_id):
       
    38             role_id = role_id.id
       
    39         if role_id is None:
       
    40             raise ValueError("Can't get role ID")
       
    41         if name is None:
       
    42             name = field.__name__
       
    43         self.__field = field
       
    44         self.__name = name
       
    45         self.__role_id = role_id
       
    46 
       
    47     def __get__(self, instance, klass):
       
    48         if instance is None:
       
    49             return self
       
    50         protection = IProtectedObject(instance, None)
       
    51         if protection is None:
       
    52             return set()
       
    53         return protection.get_principals(self.__role_id)
       
    54 
       
    55     def __set__(self, instance, value):
       
    56         if value is None:
       
    57             value = set()
       
    58         elif isinstance(value, str):
       
    59             value = set(value.split(','))
       
    60         value = set(map(lambda x: x.id if IPrincipalInfo.providedBy(x) else x, value))
       
    61         field = self.__field.bind(instance)
       
    62         field.validate(value)
       
    63         if field.readonly:
       
    64             raise ValueError("Field {0} is readonly!".format(self.__name))
       
    65         protection = IProtectedObject(instance, None)
       
    66         if not IRoleProtectedObject.providedBy(protection):
       
    67             raise ValueError("Can't use role properties on object not providing "
       
    68                              "IRoleProtectedObject interface!")
       
    69         old_principals = protection.get_principals(self.__role_id)
       
    70         added = value - old_principals
       
    71         removed = old_principals - value
       
    72         for principal_id in added:
       
    73             protection.grant_role(self.__role_id, principal_id)
       
    74         for principal_id in removed:
       
    75             protection.revoke_role(self.__role_id, principal_id)