#!/usr/bin/python
# -*- coding: utf-8 -*-

from __future__ import with_statement
from collections import defaultdict
import ConfigParser
import crypt
import random
import os

# Defaults settings
LOAD_FILE = SAVE_FILE = DEFAULT_FILE = os.path.join(os.path.dirname(__file__), 'auth.cfg')

TRACDB_PATH = '/home/tracmaster/sites/hg_trac/src/hg_trac/db/trac.db'


class groups_set(set):
    def __init__(self, arg):
        """
        Converts automatically a CSV row into a set of groups.
        """
        
        if isinstance(arg, (str, unicode)):
            arg = map(lambda x: x.strip(), arg.split())
        super(groups_set, self).__init__(arg)
    
    def remove(self, element):
        try:
            super(groups_set, self).remove(element)
        except KeyError:
            pass
    
    def __str__(self):
        return ' '.join(self)
    

def crypt_password(password):
    letters = 'abcdefghijklmnopqrstuvwxyz' \
              'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \
              '0123456789/.'
    
    salt = random.choice(letters) + random.choice(letters)
    return crypt.crypt(password, salt)
    

def update_trac(users):
    import sqlite3 as sqlite
    
    connector = sqlite.connect(TRACDB_PATH)
    cursor = connector.cursor()
    
    for login, user in users.iteritems():
        cursor.executemany("replace into session_attribute values (?, 1, ?, ?)", 
            [(login, 'email', user.get('email', '')), (login, 'name', user.fullname)])
        connector.commit()
    cursor.close()
    

class User(dict):
    
    def __init__(self):
        """Default attributes"""
        
        self['groups'] = groups_set([])
    
    def __setitem__(self, name, value):
        if name == 'groups':
            # forces groups to be a groups_set
            value = groups_set(value)
        elif name == 'password':
            # crypts automatically password
            value = crypt_password(value)
            name = 'crypted_password'
        
        super(User, self).__setitem__(name, value)
    
    @property
    def fullname(self):
        fullname = "%s %s" % (self.get('firstname', ''), self.get('lastname', ''))
        return fullname.strip()
    

class UsersDict(dict):
    """
    Proxifies the configuration file."""
    
    save_pipelines = [update_trac]
    
    def __init__(self, load_file=LOAD_FILE, save_file=SAVE_FILE):
        self.load_file = load_file or DEFAULT_FILE
        self.save_file = save_file or self.load_file
    
    @property
    def _users(self):
        if not getattr(self, '_loaded', False):
            parser = ConfigParser.ConfigParser()
            parser.read(self.load_file)
            
            users = defaultdict(User)
            
            for name, value in parser.items('Users'):
                login, attr = name.rsplit('.', 1)
                users[login][attr] = value
                users[login].login = login
            
            self._loaded = users
        return self._loaded
    
    def __iter__(self):
        return self._users.__iter__()
    
    def __contains__(self, key):
        return key in self._users
    
    def __getitem__(self, name):
        return self._users[name]
    
    def __setitem__(self, name, value):
        if not isinstance(value, User): raise ValueError('Must be a User')
        
        self._users[name] = value
    
    def __delitem__(self, name):
        del self._users[name]
    
    def save(self):
        parser = ConfigParser.RawConfigParser()
        parser.add_section('Users')
        
        for login, infos in self._users.iteritems():
            for attr, value in infos.iteritems():
                if value is None: continue
                if isinstance(value, (list, tuple, set)):
                    v = ', '.join(value)
                else:
                    v = value
                parser.set('Users', '%s.%s' % (login, attr), value)
        
        with open(self.save_file, 'wb') as configfile:
            parser.write(configfile)
        
        for pipe in self.save_pipelines:
            pipe(self._users)
    

users = UsersDict()

def check_password(environ, user, password):
    if user not in users:
        return None
    return True
    
    if 'crypted_password' not in users[user]:
        # enforce password
        return False
    
    crypted_password = users[user]['crypted_password']
    return crypt.crypt(password, crypted_password) == crypted_password
    

def groups_for_user(environ, user):
    if user in users and 'groups' in users[user]:
        return list(users[user]['groups'])
    return []
    

if __name__ == '__main__':
    # des helpers
    # createuser [login] [password] [email] [firstname] [lastname]
    # set [login] [attr] [value]
    # set [login] password [new_password]
    # addgroup [login] [group]
    # delgroup [login] [group]
    
    from optparse import OptionParser
    parser = OptionParser(usage='%prog options [options [...]]',
        description='Allow you to manage authentification. '
                'Main options are : createuser, set, addgroup and delgroup.'
        )
    parser.add_option("-l", "--load-file", dest="loadfile", default=LOAD_FILE, help="set the cfg file to be read")
    parser.add_option("-s", "--save-file", dest="savefile", default=SAVE_FILE, help="set the cfg file to be written")
    parser.add_option("-t", "--tracdb-path", dest="tracdb_path", default=TRACDB_PATH, help="set the trac.db to be updated")
    
    (options, args) = parser.parse_args()
    
    LOAD_FILE = options.loadfile
    SAVE_FILE = options.savefile
    TRACDB_PATH = options.tracdb_path
    
    if not args: parser.print_help()
    
    if args[0] == 'set':
        try:
            login, attr, value = args[1], args[2], args[3:]
            users[login][attr] = ' '.join(value)
            users.save()
            print '%s.%s = %s' % (login, attr, ' '.join(value))
            exit(0)
        except IndexError:
            parser.set_usage('%prog set [login] [attr] [value]')
            parser.set_description('Exemple: set user1 password new_password')
            parser.print_help()
    elif args[0] == 'createuser':
        try:
            _, login, password, email, firstname, lastname = args
            users[login]['password'] = password
            users[login]['email'] = email
            users[login]['firstname'] = firstname
            users[login]['lastname'] = lastname
            users.save()
            print '%s.password =  %s' % (login, password)
            print '%s.email =     %s' % (login, email)
            print '%s.firstname = %s' % (login, firstname)
            print '%s.lastname =  %s' % (login, lastname)
            exit(0)
        except IndexError:
            parser.set_usage('%prog createuser [login] [password] [email] [firstname] [lastname]')
            parser.set_description('Exemple: createuser user1 new_password email@domain FirstName LastName')
            parser.print_help()
    elif args[0] == 'addgroup':
        try:
            login, groups = args[1], args[2:]
            for group in groups:
                users[login]['groups'].add(group)
            users.save()
            print '%s.group = %s' % (login, users[login]['groups'])
            exit(0)
        except IndexError:
            parser.set_usage('%prog addgroup [login] [group]')
            parser.set_description('Exemple: addgroup xba group1 group2 group3')
            parser.print_help()
    elif args[0] == 'delgroup':
        try:
            login, groups = args[1], args[2:]
            for group in groups:
                users[login]['groups'].remove(group)
            users.save()
            print '%s.group = %s' % (login, users[login]['groups'])
            exit(0)
        except IndexError:
            parser.set_usage('%prog delgroup [login] [group]')
            parser.set_description('Exemple: delgroup xba group1 group2 group3')
            parser.print_help()
    else:
        print 'Usage:   set [login] [attr] [value]'
        print 'Exemple: set user1 password new_password'
        print 'Usage:   createuser [login] [password] [email] [firstname] [lastname]'
        print 'Exemple: createuser user1 new_password email@domain FirstName LastName'
        print 'Usage:   addgroup [login] [group]'
        print 'Exemple: addgroup user1 group1 group2 group3'
        print 'Usage:   delgroup [login] [group]'
        print 'Exemple: delgroup user1 group1 group2 group3'
        exit(1)