"""Authentication for WAeUP portals.
"""
import grok
import waeup.sirp.permissions
from zope.component import getUtility
try:
    from zope.pluggableauth.plugins.session import SessionCredentialsPlugin
except ImportError:
    # BBB
    from zope.app.authentication.session import SessionCredentialsPlugin
try:
    from zope.pluggableauth.interfaces import (
        ICredentialsPlugin, IAuthenticatorPlugin, IPrincipalInfo)
    from zope.password.interfaces import IPasswordManager
except ImportError:
    # BBB
    from zope.app.authentication.interfaces import (
        ICredentialsPlugin, IAuthenticatorPlugin, IPrincipalInfo,
        IPasswordManager)
from zope.securitypolicy.interfaces import IPrincipalRoleManager
from zope.securitypolicy.principalrole import principalRoleManager
from waeup.sirp.interfaces import IUserAccount

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']
