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

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

Implement PasswordValidator? global utility as suggested by Uli.

  • Property svn:keywords set to Id
File size: 6.7 KB
Line 
1"""Authentication for WAeUP portals.
2"""
3import grok
4from zope.component import getUtility, getUtilitiesFor
5from zope.pluggableauth.plugins.session import SessionCredentialsPlugin
6from zope.pluggableauth.interfaces import (
7        ICredentialsPlugin, IAuthenticatorPlugin, IPrincipalInfo)
8from zope.password.interfaces import IPasswordManager
9from zope.securitypolicy.interfaces import IPrincipalRoleManager
10from zope.securitypolicy.principalrole import principalRoleManager
11from waeup.sirp.interfaces import (
12    IUserAccount, IAuthPluginUtility, IPasswordValidator)
13
14def setup_authentication(pau):
15    """Set up plugguble authentication utility.
16
17    Sets up an IAuthenticatorPlugin and
18    ICredentialsPlugin (for the authentication mechanism)
19
20    Then looks for any external utilities that want to modify the PAU.
21    """
22    pau.credentialsPlugins = ('No Challenge if Authenticated', 'credentials')
23    pau.authenticatorPlugins = ('users',)
24
25    # Give any third-party code and subpackages a chance to modify the PAU
26    auth_plugin_utilities = getUtilitiesFor(IAuthPluginUtility)
27    for name, util in auth_plugin_utilities:
28        util.register(pau)
29
30def get_principal_role_manager():
31    """Get a role manager for principals.
32
33    If we are currently 'in a site', return the role manager for the
34    portal or the global rolemanager else.
35    """
36    portal = grok.getSite()
37    if portal is not None:
38        return IPrincipalRoleManager(portal)
39    return principalRoleManager
40
41class WAeUPSessionCredentialsPlugin(grok.GlobalUtility,
42                                    SessionCredentialsPlugin):
43    grok.provides(ICredentialsPlugin)
44    grok.name('credentials')
45
46    loginpagename = 'login'
47    loginfield = 'form.login'
48    passwordfield = 'form.password'
49
50class PrincipalInfo(object):
51    grok.implements(IPrincipalInfo)
52
53    def __init__(self, id, title, description):
54        self.id = id
55        self.title = title
56        self.description = description
57        self.credentialsPlugin = None
58        self.authenticatorPlugin = None
59
60class Account(grok.Model):
61    grok.implements(IUserAccount)
62
63    _local_roles = dict()
64
65    def __init__(self, name, password, title=None, description=None,
66                 roles = []):
67        self.name = name
68        if title is None:
69            title = name
70        if description is None:
71            description = title
72        self.title = title
73        self.description = description
74        self.setPassword(password)
75        self.setRoles(roles)
76        # We don't want to share this dict with other accounts
77        self._local_roles = dict()
78
79    def setPassword(self, password):
80        passwordmanager = getUtility(IPasswordManager, 'SHA1')
81        self.password = passwordmanager.encodePassword(password)
82
83    def checkPassword(self, password):
84        passwordmanager = getUtility(IPasswordManager, 'SHA1')
85        return passwordmanager.checkPassword(self.password, password)
86
87    def getRoles(self):
88        prm = get_principal_role_manager()
89        roles = [x[0] for x in prm.getRolesForPrincipal(self.name)
90                 if x[0].startswith('waeup.')]
91        return roles
92
93    def setRoles(self, roles):
94        prm = get_principal_role_manager()
95
96        old_roles = self.getRoles()
97        for role in old_roles:
98            # Remove old roles, not to be set now...
99            if role.startswith('waeup.') and role not in roles:
100                prm.unsetRoleForPrincipal(role, self.name)
101
102        for role in roles:
103            prm.assignRoleToPrincipal(role, self.name)
104
105    roles = property(getRoles, setRoles)
106
107    def getLocalRoles(self):
108        return self._local_roles
109
110    def notifyLocalRoleChanged(self, obj, role_id, granted=True):
111        objects = self._local_roles.get(role_id, [])
112        if granted and obj not in objects:
113            objects.append(obj)
114        if not granted and obj in objects:
115            objects.remove(obj)
116        self._local_roles[role_id] = objects
117        if len(objects) == 0:
118            del self._local_roles[role_id]
119        self._p_changed = True
120        return
121
122class UserAuthenticatorPlugin(grok.GlobalUtility):
123    grok.implements(IAuthenticatorPlugin)
124    grok.provides(IAuthenticatorPlugin)
125    grok.name('users')
126
127    def authenticateCredentials(self, credentials):
128        if not isinstance(credentials, dict):
129            return None
130        if not ('login' in credentials and 'password' in credentials):
131            return None
132        account = self.getAccount(credentials['login'])
133
134        if account is None:
135            return None
136        if not account.checkPassword(credentials['password']):
137            return None
138        return PrincipalInfo(id=account.name,
139                             title=account.title,
140                             description=account.description)
141
142    def principalInfo(self, id):
143        account = self.getAccount(id)
144        if account is None:
145            return None
146        return PrincipalInfo(id=account.name,
147                             title=account.title,
148                             description=account.description)
149
150    def getAccount(self, login):
151        # ... look up the account object and return it ...
152        usercontainer = self.getUserContainer()
153        if usercontainer is None:
154            return
155        return usercontainer.get(login, None)
156
157    def addAccount(self, account):
158        usercontainer = self.getUserContainer()
159        if usercontainer is None:
160            return
161        # XXX: complain if name already exists...
162        usercontainer.addAccount(account)
163
164    def addUser(self, name, password, title=None, description=None):
165        usercontainer = self.getUserContainer()
166        if usercontainer is None:
167            return
168        usercontainer.addUser(name, password, title, description)
169
170    def getUserContainer(self):
171        site = grok.getSite()
172        return site['users']
173
174class PasswordValidator(grok.GlobalUtility):
175
176  grok.implements(IPasswordValidator)
177
178  def validate_password(self, pw, pw_repeat):
179       errors = []
180       if len(pw) < 3:
181         errors.append('Password must have at least 3 chars.')
182       if pw != pw_repeat:
183         errors.append('Passwords do not match.')
184       return errors
185
186@grok.subscribe(IUserAccount, grok.IObjectRemovedEvent)
187def handle_account_removed(account, event):
188    """When an account is removed, local roles might have to be deleted.
189    """
190    local_roles = account.getLocalRoles()
191    principal = account.name
192    for role_id, object_list in local_roles.items():
193        for object in object_list:
194            try:
195                role_manager = IPrincipalRoleManager(object)
196            except TypeError:
197                # No role manager, no roles to remove
198                continue
199            role_manager.unsetRoleForPrincipal(role_id, principal)
200    return
Note: See TracBrowser for help on using the repository browser.