source: main/waeup.sirp/trunk/src/waeup/sirp/authentication.py @ 7184

Last change on this file since 7184 was 7177, checked in by Henrik Bettermann, 13 years ago

Role back some changes made earlier: global and local role strings with html tags should be better constructed in view not in permission module.

Rename some role related methods to avoid (my) confusion.

  • Property svn:keywords set to Id
File size: 8.8 KB
RevLine 
[4073]1"""Authentication for WAeUP portals.
2"""
3import grok
[7169]4from zope.event import notify
[5900]5from zope.component import getUtility, getUtilitiesFor
[7169]6from zope.interface import Interface
7from zope.securitypolicy.interfaces import (
8    IPrincipalRoleMap, IPrincipalRoleManager)
[6610]9from zope.pluggableauth.plugins.session import SessionCredentialsPlugin
10from zope.pluggableauth.interfaces import (
[5055]11        ICredentialsPlugin, IAuthenticatorPlugin, IPrincipalInfo)
[6610]12from zope.password.interfaces import IPasswordManager
[4129]13from zope.securitypolicy.principalrole import principalRoleManager
[7169]14from waeup.sirp.interfaces import (ILocalRoleSetEvent,
[7147]15    IUserAccount, IAuthPluginUtility, IPasswordValidator)
[4073]16
17def setup_authentication(pau):
18    """Set up plugguble authentication utility.
19
20    Sets up an IAuthenticatorPlugin and
21    ICredentialsPlugin (for the authentication mechanism)
[5900]22
23    Then looks for any external utilities that want to modify the PAU.
[4073]24    """
[6661]25    pau.credentialsPlugins = ('No Challenge if Authenticated', 'credentials')
[6672]26    pau.authenticatorPlugins = ('users',)
[4073]27
[5900]28    # Give any third-party code and subpackages a chance to modify the PAU
29    auth_plugin_utilities = getUtilitiesFor(IAuthPluginUtility)
[5901]30    for name, util in auth_plugin_utilities:
[5900]31        util.register(pau)
32
[6673]33def get_principal_role_manager():
34    """Get a role manager for principals.
35
36    If we are currently 'in a site', return the role manager for the
37    portal or the global rolemanager else.
38    """
39    portal = grok.getSite()
40    if portal is not None:
41        return IPrincipalRoleManager(portal)
42    return principalRoleManager
43
[4073]44class WAeUPSessionCredentialsPlugin(grok.GlobalUtility,
45                                    SessionCredentialsPlugin):
46    grok.provides(ICredentialsPlugin)
47    grok.name('credentials')
48
49    loginpagename = 'login'
50    loginfield = 'form.login'
51    passwordfield = 'form.password'
52
53class PrincipalInfo(object):
54    grok.implements(IPrincipalInfo)
55
56    def __init__(self, id, title, description):
57        self.id = id
58        self.title = title
59        self.description = description
60        self.credentialsPlugin = None
61        self.authenticatorPlugin = None
62
63class Account(grok.Model):
[4109]64    grok.implements(IUserAccount)
[4129]65
[6180]66    _local_roles = dict()
67
[4125]68    def __init__(self, name, password, title=None, description=None,
69                 roles = []):
[4073]70        self.name = name
[4087]71        if title is None:
72            title = name
73        if description is None:
74            description = title
75        self.title = title
76        self.description = description
[4073]77        self.setPassword(password)
[7177]78        #self.setSiteRolesForPrincipal(roles)
[6180]79        # We don't want to share this dict with other accounts
80        self._local_roles = dict()
[4073]81
82    def setPassword(self, password):
83        passwordmanager = getUtility(IPasswordManager, 'SHA1')
84        self.password = passwordmanager.encodePassword(password)
85
86    def checkPassword(self, password):
87        passwordmanager = getUtility(IPasswordManager, 'SHA1')
88        return passwordmanager.checkPassword(self.password, password)
89
[7177]90    def getSiteRolesForPrincipal(self):
[6673]91        prm = get_principal_role_manager()
[4129]92        roles = [x[0] for x in prm.getRolesForPrincipal(self.name)
93                 if x[0].startswith('waeup.')]
94        return roles
[5055]95
[7177]96    def setSiteRolesForPrincipal(self, roles):
[6673]97        prm = get_principal_role_manager()
[7177]98        old_roles = self.getSiteRolesForPrincipal()
[4129]99        for role in old_roles:
100            # Remove old roles, not to be set now...
101            if role.startswith('waeup.') and role not in roles:
102                prm.unsetRoleForPrincipal(role, self.name)
103        for role in roles:
104            prm.assignRoleToPrincipal(role, self.name)
105
[7177]106    roles = property(getSiteRolesForPrincipal, setSiteRolesForPrincipal)
[4129]107
[6180]108    def getLocalRoles(self):
109        return self._local_roles
110
111    def notifyLocalRoleChanged(self, obj, role_id, granted=True):
112        objects = self._local_roles.get(role_id, [])
113        if granted and obj not in objects:
114            objects.append(obj)
115        if not granted and obj in objects:
116            objects.remove(obj)
117        self._local_roles[role_id] = objects
118        if len(objects) == 0:
119            del self._local_roles[role_id]
[6182]120        self._p_changed = True
[6180]121        return
122
[4073]123class UserAuthenticatorPlugin(grok.GlobalUtility):
[6625]124    grok.implements(IAuthenticatorPlugin)
[4073]125    grok.provides(IAuthenticatorPlugin)
126    grok.name('users')
127
128    def authenticateCredentials(self, credentials):
129        if not isinstance(credentials, dict):
130            return None
131        if not ('login' in credentials and 'password' in credentials):
132            return None
133        account = self.getAccount(credentials['login'])
134
135        if account is None:
136            return None
137        if not account.checkPassword(credentials['password']):
138            return None
139        return PrincipalInfo(id=account.name,
[4612]140                             title=account.title,
141                             description=account.description)
[4073]142
143    def principalInfo(self, id):
144        account = self.getAccount(id)
145        if account is None:
146            return None
147        return PrincipalInfo(id=account.name,
[4612]148                             title=account.title,
149                             description=account.description)
[4073]150
151    def getAccount(self, login):
[4087]152        # ... look up the account object and return it ...
[7172]153        userscontainer = self.getUsersContainer()
154        if userscontainer is None:
[4087]155            return
[7172]156        return userscontainer.get(login, None)
[4087]157
158    def addAccount(self, account):
[7172]159        userscontainer = self.getUsersContainer()
160        if userscontainer is None:
[4087]161            return
162        # XXX: complain if name already exists...
[7172]163        userscontainer.addAccount(account)
[4087]164
[4091]165    def addUser(self, name, password, title=None, description=None):
[7172]166        userscontainer = self.getUsersContainer()
167        if userscontainer is None:
[4091]168            return
[7172]169        userscontainer.addUser(name, password, title, description)
[5055]170
[7172]171    def getUsersContainer(self):
[4087]172        site = grok.getSite()
[4743]173        return site['users']
[6203]174
[7147]175class PasswordValidator(grok.GlobalUtility):
176
177  grok.implements(IPasswordValidator)
178
179  def validate_password(self, pw, pw_repeat):
180       errors = []
181       if len(pw) < 3:
182         errors.append('Password must have at least 3 chars.')
183       if pw != pw_repeat:
184         errors.append('Passwords do not match.')
185       return errors
186
[7169]187class LocalRoleSetEvent(object):
188
189    grok.implements(ILocalRoleSetEvent)
190
191    def __init__(self, object, role_id, principal_id, granted=True):
192        self.object = object
193        self.role_id = role_id
194        self.principal_id = principal_id
195        self.granted = granted
196
[6203]197@grok.subscribe(IUserAccount, grok.IObjectRemovedEvent)
[6839]198def handle_account_removed(account, event):
[6203]199    """When an account is removed, local roles might have to be deleted.
200    """
201    local_roles = account.getLocalRoles()
202    principal = account.name
203    for role_id, object_list in local_roles.items():
204        for object in object_list:
205            try:
206                role_manager = IPrincipalRoleManager(object)
207            except TypeError:
208                # No role manager, no roles to remove
209                continue
210            role_manager.unsetRoleForPrincipal(role_id, principal)
211    return
[7169]212
213@grok.subscribe(IUserAccount, grok.IObjectAddedEvent)
214def handle_account_added(account, event):
215    """When an account is added, the local owner role and the global
[7173]216    PortalUser role must be set.
[7169]217    """
[7173]218    # We set the local Owner role
219    role_manager_account = IPrincipalRoleManager(account)
220    role_manager_account.assignRoleToPrincipal(
[7169]221        'waeup.local.Owner', account.name)
[7173]222    # We set the global PortalUser role
223    site = grok.getSite()
224    role_manager_site = IPrincipalRoleManager(site)
225    role_manager_site.assignRoleToPrincipal(
226        'waeup.PortalUser', account.name)
227    # Finally we have to notify the user account that the local role
[7169]228    # of the same object has changed
229    notify(LocalRoleSetEvent(
230        account, 'waeup.local.Owner', account.name, granted=True))
231    return
232
233@grok.subscribe(Interface, ILocalRoleSetEvent)
234def handle_local_role_changed(obj, event):
235    site = grok.getSite()
236    if site is None:
237        return
238    users = site.get('users', None)
239    if users is None:
240        return
241    role_id = event.role_id
242    if event.principal_id not in users.keys():
243        return
244    user = users[event.principal_id]
245    user.notifyLocalRoleChanged(event.object, event.role_id, event.granted)
246    return
247
248@grok.subscribe(Interface, grok.IObjectRemovedEvent)
249def handle_local_roles_on_obj_removed(obj, event):
250    try:
251        role_map = IPrincipalRoleMap(obj)
252    except TypeError:
253        # no map, no roles to remove
254        return
255    for local_role, user_name, setting in role_map.getPrincipalsAndRoles():
256        notify(LocalRoleSetEvent(
257                obj, local_role, user_name, granted=False))
258    return
Note: See TracBrowser for help on using the repository browser.