"""Authentication for WAeUP portals.
"""
import grok
from zope.app.authentication.session import SessionCredentialsPlugin
from zope.app.authentication.interfaces import (ICredentialsPlugin,
                                                IAuthenticatorPlugin,
                                                IPrincipalInfo,
                                                IPasswordManager)
from zope.component import getUtility
from zope.securitypolicy.interfaces import IPrincipalRoleManager
from zope.securitypolicy.principalrole import principalRoleManager
from waeup.sirp.interfaces import IUserAccount
import waeup.sirp.permissions

def setup_authentication(pau):
    """Set up plugguble authentication utility.

    Sets up an IAuthenticatorPlugin and
    ICredentialsPlugin (for the authentication mechanism)
    """
    pau.credentialsPlugins = ['No Challenge if Authenticated', 'credentials']
    pau.authenticatorPlugins = ['users']

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)


    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)

    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 = self._getPrincipalRoleManager()
        roles = [x[0] for x in prm.getRolesForPrincipal(self.name)
                 if x[0].startswith('waeup.')]
        return roles
    
    def setRoles(self, roles):
        prm = self._getPrincipalRoleManager()
        
        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 _getPrincipalRoleManager(self):
        portal = grok.getSite()
        if portal is not None:
            return IPrincipalRoleManager(portal)
        return principalRoleManager
        
    
class UserAuthenticatorPlugin(grok.GlobalUtility):
    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']
