"""Authentication for WAeUP portals. """ import grok from zope.component import getUtility, getUtilitiesFor from zope.pluggableauth.plugins.session import SessionCredentialsPlugin from zope.pluggableauth.interfaces import ( ICredentialsPlugin, IAuthenticatorPlugin, IPrincipalInfo) from zope.password.interfaces import IPasswordManager from zope.securitypolicy.interfaces import IPrincipalRoleManager from zope.securitypolicy.principalrole import principalRoleManager from waeup.sirp.interfaces import ( IUserAccount, IAuthPluginUtility, IPasswordValidator) def setup_authentication(pau): """Set up plugguble authentication utility. Sets up an IAuthenticatorPlugin and ICredentialsPlugin (for the authentication mechanism) Then looks for any external utilities that want to modify the PAU. """ pau.credentialsPlugins = ('No Challenge if Authenticated', 'credentials') pau.authenticatorPlugins = ('users',) # Give any third-party code and subpackages a chance to modify the PAU auth_plugin_utilities = getUtilitiesFor(IAuthPluginUtility) for name, util in auth_plugin_utilities: util.register(pau) def get_principal_role_manager(): """Get a role manager for principals. If we are currently 'in a site', return the role manager for the portal or the global rolemanager else. """ portal = grok.getSite() if portal is not None: return IPrincipalRoleManager(portal) return principalRoleManager class WAeUPSessionCredentialsPlugin(grok.GlobalUtility, SessionCredentialsPlugin): grok.provides(ICredentialsPlugin) grok.name('credentials') loginpagename = 'login' loginfield = 'form.login' passwordfield = 'form.password' class PrincipalInfo(object): grok.implements(IPrincipalInfo) def __init__(self, id, title, description): self.id = id self.title = title self.description = description self.credentialsPlugin = None self.authenticatorPlugin = None class Account(grok.Model): grok.implements(IUserAccount) _local_roles = dict() def __init__(self, name, password, title=None, description=None, roles = []): self.name = name if title is None: title = name if description is None: description = title self.title = title self.description = description self.setPassword(password) self.setRoles(roles) # We don't want to share this dict with other accounts self._local_roles = dict() def setPassword(self, password): passwordmanager = getUtility(IPasswordManager, 'SHA1') self.password = passwordmanager.encodePassword(password) def checkPassword(self, password): passwordmanager = getUtility(IPasswordManager, 'SHA1') return passwordmanager.checkPassword(self.password, password) def getRoles(self): prm = get_principal_role_manager() roles = [x[0] for x in prm.getRolesForPrincipal(self.name) if x[0].startswith('waeup.')] return roles def setRoles(self, roles): prm = get_principal_role_manager() old_roles = self.getRoles() for role in old_roles: # Remove old roles, not to be set now... if role.startswith('waeup.') and role not in roles: prm.unsetRoleForPrincipal(role, self.name) for role in roles: prm.assignRoleToPrincipal(role, self.name) roles = property(getRoles, setRoles) def getLocalRoles(self): return self._local_roles def notifyLocalRoleChanged(self, obj, role_id, granted=True): objects = self._local_roles.get(role_id, []) if granted and obj not in objects: objects.append(obj) if not granted and obj in objects: objects.remove(obj) self._local_roles[role_id] = objects if len(objects) == 0: del self._local_roles[role_id] self._p_changed = True return class UserAuthenticatorPlugin(grok.GlobalUtility): grok.implements(IAuthenticatorPlugin) grok.provides(IAuthenticatorPlugin) grok.name('users') def authenticateCredentials(self, credentials): if not isinstance(credentials, dict): return None if not ('login' in credentials and 'password' in credentials): return None account = self.getAccount(credentials['login']) if account is None: return None if not account.checkPassword(credentials['password']): return None return PrincipalInfo(id=account.name, title=account.title, description=account.description) def principalInfo(self, id): account = self.getAccount(id) if account is None: return None return PrincipalInfo(id=account.name, title=account.title, description=account.description) def getAccount(self, login): # ... look up the account object and return it ... usercontainer = self.getUserContainer() if usercontainer is None: return return usercontainer.get(login, None) def addAccount(self, account): usercontainer = self.getUserContainer() if usercontainer is None: return # XXX: complain if name already exists... usercontainer.addAccount(account) def addUser(self, name, password, title=None, description=None): usercontainer = self.getUserContainer() if usercontainer is None: return usercontainer.addUser(name, password, title, description) def getUserContainer(self): site = grok.getSite() return site['users'] class PasswordValidator(grok.GlobalUtility): grok.implements(IPasswordValidator) def validate_password(self, pw, pw_repeat): errors = [] if len(pw) < 3: errors.append('Password must have at least 3 chars.') if pw != pw_repeat: errors.append('Passwords do not match.') return errors @grok.subscribe(IUserAccount, grok.IObjectRemovedEvent) def handle_account_removed(account, event): """When an account is removed, local roles might have to be deleted. """ local_roles = account.getLocalRoles() principal = account.name for role_id, object_list in local_roles.items(): for object in object_list: try: role_manager = IPrincipalRoleManager(object) except TypeError: # No role manager, no roles to remove continue role_manager.unsetRoleForPrincipal(role_id, principal) return