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

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

Put all user component related stuff into the authentication module to avoid errors due to circular imports. The users module now only contains the usercontainer components.

  • Property svn:keywords set to Id
File size: 8.5 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        usercontainer = self.getUserContainer()
156        if usercontainer is None:
157            return
158        return usercontainer.get(login, None)
159
160    def addAccount(self, account):
161        usercontainer = self.getUserContainer()
162        if usercontainer is None:
163            return
164        # XXX: complain if name already exists...
165        usercontainer.addAccount(account)
166
167    def addUser(self, name, password, title=None, description=None):
168        usercontainer = self.getUserContainer()
169        if usercontainer is None:
170            return
171        usercontainer.addUser(name, password, title, description)
172
173    def getUserContainer(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 rolemust be set.
219    """
220    # First we have to set the local owner role of the account object
221    role_manager = IPrincipalRoleManager(account)
222    role_manager.assignRoleToPrincipal(
223        'waeup.local.Owner', account.name)
224    # Then we have to notify the user account that the local role
225    # of the same object has changed
226    notify(LocalRoleSetEvent(
227        account, 'waeup.local.Owner', account.name, granted=True))
228    return
229
230@grok.subscribe(Interface, ILocalRoleSetEvent)
231def handle_local_role_changed(obj, event):
232    site = grok.getSite()
233    if site is None:
234        return
235    users = site.get('users', None)
236    if users is None:
237        return
238    role_id = event.role_id
239    if event.principal_id not in users.keys():
240        return
241    user = users[event.principal_id]
242    user.notifyLocalRoleChanged(event.object, event.role_id, event.granted)
243    return
244
245@grok.subscribe(Interface, grok.IObjectRemovedEvent)
246def handle_local_roles_on_obj_removed(obj, event):
247    try:
248        role_map = IPrincipalRoleMap(obj)
249    except TypeError:
250        # no map, no roles to remove
251        return
252    for local_role, user_name, setting in role_map.getPrincipalsAndRoles():
253        notify(LocalRoleSetEvent(
254                obj, local_role, user_name, granted=False))
255    return
Note: See TracBrowser for help on using the repository browser.