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

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

Automatically set global PortalUser? role when user account is created. Thus all users get view permissions for the academic section.

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