# Copyright (c) 2000 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

from narval.lib import *
from md5 import new
from string import split

MOD_XML='''<?xml version="1.0" encoding="ISO-8859-1"?>
<al:module xmlns:al="%s">''' % AL_NS

AccessControlNS = NO_NS

def compile_password(clear_password):
    # this function takes a password in clear text an returns its encrypted
    # version
    compiled_password = ''
    for char in new(clear_password).digest():
        compiled_password='%s%u '%(compiled_password,ord(char))
    return compiled_password[:-1]

# tests if a given login/password is in a given access control list
def Authenticate(args, doc):
    ret = {}
    login_password = args['log']
    login = login_password.getAttributeNS(AccessControlNS, 'login')
    password = login_password.getAttributeNS(AccessControlNS, 'password')
    compiled_password = compile_password(password)
    acl =  args['acl']
    for aci in select_many(acl,'access-control-item'):
        if login != aci.getAttributeNS(AccessControlNS, 'login'):
            continue
        if compiled_password == aci.getAttributeNS(AccessControlNS, 'password'):
            # authentication success, do not return anything
            return ret
        if password == '' and aci.getAttributeNS(AccessControlNS, 'password') == '':
            # authentication success, do not return anything
            return ret
        error_string="authentication failure : bad password"
        break
    else:
        error_string="authentication failure : unknown user"
    # authentication failure, return an error element
    error = doc.createElementNS(AL_NS,'al:error')
    error.appendChild(doc.createTextNode(error_string))
    ret['error'] = [error]
    return ret

MOD_XML=MOD_XML+'''
<al:action name='authenticate' func='Authenticate'>
  <al:description lang='fr'>Vérifie une combinaison login/mot de passe.</al:description>
  <al:description lang='en'>
Verifies a login/password combination. It takes a non-encrypted password as
entry and does not output anything in case of success. It outputs an error in
case of wrong password 
  </al:description>
  <al:input id='log' ><al:match>login-password</al:match></al:input>
  <al:input id='acl' ><al:match>access-control-list</al:match></al:input>
</al:action>'''

# adds a given login/password to a given access control list
def AddACI(args, doc):
    ret = {}
    login_password = args['log']
    login=login_password.getAttributeNS(AccessControlNS, 'login')
    password=login_password.getAttributeNS(AccessControlNS, 'password')
    acl = args['acl']
    for aci in select_many(acl, 'access-control-item'):
        if login==aci.getAttributeNS(AccessControlNS, 'login'):
            error=doc.createElementNS(AL_NS, 'al:error')
            error.appendChild(doc.createTextNode(AccessControlNS,
                                                 'duplicated login'))
            ret['error'] = [error]
            return ret
    aci=doc.createElementNS(AccessControlNS,'access-control-item')
    acl.appendChild(aci)
    aci.setAttributeNS(AccessControlNS, 'login',login)
    aci.setAttributeNS(AccessControlNS, 'password',compile_password(password))
    ret['acl'] = acl
    return ret

MOD_XML=MOD_XML+"""
<al:action name='add_aci' func='AddACI'>
  <al:description lang='fr'>
Ajoute un couple login/mot de passe à une liste de controle d'accès. Cette
action prend en charge le cryptage du mot de passe.
  </al:description>
  <al:description lang='en'>
Adds a login/password pair to an access control list. This action provides
encryption for the password.
  </al:description>
  <al:input id='log' ><al:match>login-password</al:match></al:input>
  <al:input id='acl'><al:match>access-control-list</al:match></al:input>
  <al:output id='acl'><al:match>access-control-list</al:match></al:output>
</al:action>"""

# removes a login/password from a given access control list
def RemoveACI(args, doc):
    ret = {}
    acl = args['acl']
    login = args['log'].getAttributeNS(AccessControlNS, 'login')
    for aci in select_many(acl, 'access-control-item'):
        if aci.getAttributeNS(AccessControlNS,'login') == login:
            acl.removeElement(aci)
            ret['acl'] = acl
            return ret
    error = doc.createElementNS(AL_NS,'al:error')
    error.appendChild(doc.createElementNS(AccessControlNS,'no such login'))
    ret['error'] = [error]
    return ret

MOD_XML=MOD_XML+"""
<al:action name='remove_aci' func='RemoveACI'>
  <al:description lang='fr'>Supprime un couple login/mot de passe d'une liste de controle d'accès.</al:description>
  <al:description lang='en'>Removes a login/password pair from an access control list.</al:description>
  <al:input id='input' ><al:match>login-password</al:match></al:input>
  <al:input id='input' ><al:match>access-control-list</al:match></al:input>
  <al:output id='output'><al:match>access-control-list</al:match></al:output>
</al:action>"""

MOD_XML=MOD_XML+'''</al:module>'''

if __name__ == "__main__":
    print MOD_XML
    from xml.dom.ext.reader import Sax2
    doc=Sax2.FromXml(MOD_XML)
    print doc.documentElement
