#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007, TUBITAK/UEKAE
#
# 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. Please read the COPYING file.
#

import comar
import time
import subprocess
import logging

import ajan.config
import ajan.ldaputil
import ajan.pam
import ajan.nsswitch

header = """#
# Auto generated by Ahenk
#

"""


class UserPolicy(ajan.ldaputil.LdapClass):
    entries = (
        ("mode", "comarUserSourceMode", str, None),
        ("ldap_scope", "comarUserLdapSearchScope", str, None),
        ("ldap_base", "comarUserLdapBase", str, None),
        ("ldap_filter", "comarUserLdapFilter", str, None),
        ("ldap_uri", "comarUserLdapURI", str, None),
    )


class Policy:
    def __init__(self):
        self.policy = UserPolicy()
        self.old_mode = None
        self.log = logging.getLogger("Mod.User")
    
    def override(self, attr, is_ou=False):
        temp = UserPolicy(attr)
        if temp.mode == "ldap":
            self.policy.mode = "ldap"
            self.policy.ldap_scope = temp.ldap_scope
            self.policy.ldap_filter = temp.ldap_filter
            self.policy.ldap_base = temp.ldap_base
            self.policy.ldap_uri = temp.ldap_uri
        else:
            if temp.mode and (not self.policy.mode or not is_ou):
                self.policy.mode = "local"
            if not is_ou:
                if temp.ldap_filter:
                    self.policy.ldap_filter = temp.ldap_filter
    
    def update(self, computer, units):
        self.log.debug("Updating user policy")
        self.policy = UserPolicy()
        for unit in units:
            self.override(unit, True)
        self.override(computer)
        self.log.debug("User policy is now:\n%s" % str(self.policy))
    
    def set_padl_config(self):
        conf = header
        
        ldap_uri = self.policy.ldap_uri
        if not ldap_uri:
            ldap_uri = ajan.config.ldap.uri
        conf += "uri %s\n" % ldap_uri
        
        ldap_base = self.policy.ldap_base
        if not ldap_base:
            ldap_base = ajan.config.ldap.base_dn
        conf += "base %s\n" % ldap_base
        scope = {
            "base": "base",
            "onelevel": "one",
            "subtree": "sub",
        }.get(self.policy.ldap_scope, "sub")
        conf += "scope %s\n" % scope
        
        if self.policy.ldap_filter:
            conf += "pam_filter %s\n" % self.policy.ldap_filter
        
        conf += "nss_schema rfc2307bis\n"
        conf += "pam_password exop\n"
        conf += "bind_policy soft\n"
        
        f = file("/etc/security/ldap.conf", "w")
        f.write(conf)
        f.close()
    
    def set_pam(self):
        p = ajan.pam.Pam()
        p.load()
        service = p.services["system-auth"]
        if self.policy.mode == "ldap":
            service.auth.set_module("pam_ldap.so", "sufficient", before="pam_unix.so")
            service.account.set_module("pam_ldap.so", "sufficient", before="pam_unix.so")
            service.password.set_module("pam_ldap.so", "required")
            service.session.set_module("pam_mkhomedir.so", "required", args="umask=0077")
            for rule in service.auth:
                if rule.module == "pam_unix.so":
                    if not "try_first_pass" in rule.args:
                        rule.args += " try_first_pass"
        else:
            service.remove_module("pam_ldap.so")
        service.save()
    
    def set_nss(self):
        nss = ajan.nsswitch.NameServiceSwitch()
        sources = ["files"]
        if self.policy.mode == "ldap":
            sources = ["files", "ldap"]
        nss["shadow"].sources = sources
        nss["passwd"].sources = sources
        nss["group"].sources = sources
        nss.save()
    
    def restart_services(self):
        # /etc/nsswitch.conf is changed and this services need to load it again
        link = comar.Link()
        link.System.Service["PolicyKit"].stop()
        link.read_cmd()
        link.System.Service["dbus"].stop()
        link.read_cmd()
        link.System.Service["dbus"].start()
        link.read_cmd()
        link.System.Service["PolicyKit"].start()
        link.read_cmd()
        # Comar needs special treatment
        subprocess.call(["/bin/service", "comar", "restart"])
        timeout = 5
        while timeout > 0:
            try:
                link = comar.Link()
                break
            except comar.CannotConnect:
                pass
            timeout -= 0.2
            time.sleep(0.2)
    
    def apply(self):
        self.log.debug("Applying %s user policy" % self.policy.mode)
        if self.policy.mode == "ldap":
            self.set_padl_config()
        self.set_nss()
        self.set_pam()
        if self.policy.mode != self.old_mode:
            self.restart_services()
            self.old_mode = self.policy.mode
