
# Install and configure ldap authentication
#
# === Global variables
#
#   $ldap_server:
#       Array containing LDAP server URI's.
#
#   $ldap_basedn:
#       LDAP base DN.
#
#   $ldap_login_umask:
#       Default umask for LDAP users in OpenBSD, defaults to 077.
#
class ldap::auth inherits ldap::client {

    include pam::common

    tag("bootstrap")

    $ldap_uri = inline_template('<%= ldap_server.join(" ") -%>')

    case $::operatingsystem {
        "centos","redhat": {
            case $::operatingsystemrelease {
                /^6/: {
                    package { "nss-pam-ldapd":
                        ensure => installed,
                    }
                    exec { "authconfig --enableldap --enableldapauth --ldapserver='${ldap_uri}' --ldapbasedn='${ldap_basedn}' --update":
                        path    => "/bin:/usr/bin:/sbin:/usr/sbin",
                        unless  => 'cat /etc/sysconfig/authconfig | egrep "^USELDAPAUTH=yes$|^USELDAP=yes$" | wc -l | egrep "^2$"',
                        before  => [ Augeas["nslcd-conf"],
                                     Augeas["pam-ldap-conf"],
                                     File["/etc/openldap/ldap.conf"], ],
                        require => Package["authconfig", "nss-pam-ldapd"],
                    }
                    augeas { "nslcd-conf":
                        changes => [ "set pagesize 500",
                                     "set ssl on",
                                     "set tls_reqcert never",
                                     "rm tls_cacertdir", ],
                        onlyif  => [ "get pagesize != 500",
                                     "get ssl != on",
                                     "get tls_reqcert != never", ],
                        incl    => "/etc/nslcd.conf",
                        lens    => "Spacevars.simple_lns",
                        notify  => Service["nslcd"],
                    }
                    augeas { "pam-ldap-conf":
                        changes => [ "set ssl on",
                                     "set pam_password exop",
                                     "rm tls_cacertdir", ],
                        onlyif  => [ "get ssl != on",
                                     "get pam_password != exop", ],
                        incl    => "/etc/pam_ldap.conf",
                        lens    => "Spacevars.simple_lns",
                    }
                    service { "nslcd":
                         ensure => running,
                         enable => true,
                         notify => Service["nscd"],
                    }
                }
                default: {
                    package { "nss_ldap":
                        ensure => installed,
                    }
                    exec { "authconfig --enableldap --enableldapauth --enableldapssl --ldapserver='${ldap_uri}' --ldapbasedn='${ldap_basedn}' --update":
                        path    => "/bin:/usr/bin:/sbin:/usr/sbin",
                        unless  => 'cat /etc/sysconfig/authconfig | egrep "^USELDAPAUTH=yes$|^USELDAP=yes$" | wc -l | egrep "^2$"',
                        before  => [ Augeas["pam-ldap-conf"],
                                     File["/etc/openldap/ldap.conf"], ],
                        require => Package["authconfig", "nss_ldap"],
                    }
                    augeas { "pam-ldap-conf":
                        context  => "/files/etc/ldap.conf",
                        changes  => [ "set nss_paged_results yes",
                                      "set pam_password exop",
                                      "set ssl on", ],
                        onlyif   => [ "get nss_paged_results != yes",
                                      "get pam_password != exop",
                                      "get ssl != on", ],
                        notify   => Service["nscd"],
                    }
                }
            }
            package { "nscd":
                ensure => installed,
            }
            service { "nscd":
                ensure  => running,
                enable  => true,
                require => Package["nscd"],
            }
        }
        Fedora: {
            package { [ "sssd", "pam_ldap", ]:
                ensure => installed,
            }
            exec { "authconfig --enableldap --enableldapauth --ldapserver='${ldap_uri}' --ldapbasedn='${ldap_basedn}' --enablesssd --krb5realm='' --update":
                path    => "/bin:/usr/bin:/sbin:/usr/sbin",
                unless  => 'cat /etc/sysconfig/authconfig | egrep "^USELDAPAUTH=yes$|^USELDAP=yes$" | wc -l | egrep "^2$"',
                before  => Augeas["sssd-conf"],
                require => Package["authconfig", "sssd", "pam_ldap"],
            }
            augeas { "sssd-conf":
                changes => [
                  "set target[1]/ldap_tls_reqcert never",
                  "set target[1]/enumerate true",
                  "set target[1]/ldap_schema rfc2307bis",
                  "set target[1]/ldap_group_member uniqueMember",
                ],
                incl    => "/etc/sssd/sssd.conf",
                lens    => "MySQL.lns",
                before  => Service["sssd"],
            }
            service { "sssd":
                ensure => running,
                enable => true,
            }
        }
        Ubuntu: {
            package { "ldap-auth-client":
                ensure => installed,
            }
            exec { "auth-client-config -t nss -p lac_ldap":
                path    => "/bin:/usr/bin:/sbin:/usr/sbin",
                unless  => "auth-client-config -t nss -p lac_ldap -s",
                require => Package["ldap-auth-client"],
                before  => Augeas["pam-ldap-conf"],
                notify  => Exec["nssldap-update-ignoreusers"],
            }
            exec { "nssldap-update-ignoreusers":
                path        => "/bin:/usr/bin:/sbin:/usr/sbin",
                refreshonly => true,
            }
            augeas { "pam-ldap-conf":
                context => "/files/etc/ldap.conf",
                changes => [ "set uri '${ldap_uri}'",
                             "set base ${ldap_basedn}",
                             "set nss_paged_results yes",
                             "set pam_password exop",
                             "rm rootbinddn",
                             "set ssl on", ],
                onlyif  => [ "get uri != '${ldap_uri}'",
                             "get base != ${ldap_basedn}",
                             "get nss_paged_results != yes",
                             "get pam_password != exop",
                             "get rootbinddn == 'cn=manager,dc=example,dc=net'",
                             "get ssl != on", ],
            }
        }
        Debian: {
            package {[ "libnss-ldap",
                       "libpam-ldap" ]:
                ensure => installed,
            }
## Debian lacks some lenses. nss-ldap-conf and pam_ldap-conf needs corresponding files
## to /usr/share/augeas/lenses/dist/spacevars.aug. More info at:
## https://github.com/jwm/augeas/commit/8f768f45779048cbd95b5b7d71682b808d41bfd3
## There isn't lens for nsswitch.conf either. nss-ldap-conf and pam_ldap-conf are tested, nsswitch isn't.
#            augeas { "nss-ldap-conf":
#                context => "/files/etc/libnss-ldap.conf",
#                changes => [ "set uri '${ldap_uri}'",
#                             "set base ${ldap_basedn}",
#                             "set nss_paged_results yes",
#                             "set pam_password exop",
#                             "rm rootbinddn",
#                             "set ssl on", ],
#                onlyif  => [ "get uri != '${ldap_uri}'",
#                             "get base != ${ldap_basedn}",
#                             "get nss_paged_results != yes",
#                             "get pam_password != exop",
#                             "get rootbinddn == 'cn=manager,dc=example,dc=net'",
#                             "get ssl != on", ],
#               require => Package["libnss-ldap"],
#            }
#            augeas { "pam_ldap-conf":
#                context => "/files/etc/pam_ldap.conf",
#                changes => [ "set uri '${ldap_uri}'",
#                             "set base ${ldap_basedn}",
#                             "set nss_paged_results yes",
#                             "set pam_password exop",
#                             "rm rootbinddn",
#                             "set ssl on", ],
#                onlyif  => [ "get uri != '${ldap_uri}'",
#                             "get base != ${ldap_basedn}",
#                             "get nss_paged_results != yes",
#                             "get pam_password != exop",
#                             "get rootbinddn == 'cn=manager,dc=example,dc=net'",
#                             "get ssl != on", ],
#               require => Package["libpam-ldap"],
#            }
#            augeas { "nsswitch-conf":
#                context => "/files/etc/nsswitch.conf",
#                changes => [ "set passwd: 'files ldap'",
#                             "set group: 'files ldap'",
#                             "set shadow: 'files ldap'", ],
#                onlyif  => [ "get passwd: != 'files ldap'",
#                             "get group: != 'files ldap'",
#                             "get shadow: != 'files ldap'", ],
#               require => [ Augeas["pam_ldap-conf"],
#                             Augeas["nss-ldap-conf"], ],
#            }
        }
        OpenBSD: {
            if ! $ldap_login_umask {
                $ldap_login_umask = "077"
            }
            package { "login_ldap":
                ensure => installed,
            }
            file { "/etc/login.conf":
                ensure  => present,
                content => template("ldap/login.conf.erb"),
                mode    => "0644",
                owner   => root,
                group   => wheel,
                require => [ File["/etc/openldap/ldap.conf"],
                             Package["login_ldap"], ]
            }
        }
        default: {
            fail("ldap::auth not supported on ${::operatingsystem}")
        }
    }

}

# Install and configure ldap client
#
# === Global variables
#
#   $ldap_server:
#       Array containing LDAP server URI's.
#
#   $ldap_basedn:
#       LDAP base DN.
#
class ldap::client {

    package { "openldap-client":
        name   => $::operatingsystem ? {
            "debian"  => "ldap-utils",
            "ubuntu"  => "ldap-utils",
            "openbsd" => "openldap-client",
            default   => "openldap-clients",
        },
        ensure => $::operatingsystem ? {
            darwin  => absent,
            default => installed,
        },
    }

    file { "/etc/openldap/ldap.conf":
        ensure  => present,
        content => template("ldap/ldap.conf.erb"),
        path    => $::operatingsystem ? {
            "debian" => "/etc/ldap/ldap.conf",
            "ubuntu" => "/etc/ldap/ldap.conf",
            default  => "/etc/openldap/ldap.conf",
        },
        mode    => "0644",
        owner   => root,
        group   => $::operatingsystem ? {
            "darwin"  => wheel,
            "openbsd" => wheel,
            default   => root,
        },
        require => Package["openldap-client"],
    }

}


# Install python ldap bindings.
#
class ldap::client::python {

    package { "python-ldap":
        name   => $::operatingsystem ? {
            openbsd => "py-ldap",
            default => "python-ldap",
        },
        ensure => installed,
    }

    file { "${::pythonsitedir}/dynldap.py":
        ensure  => present,
        source  => "puppet:///modules/ldap/dynldap.py",
        mode    => "0644",
        owner   => "root",
        group   => $::operatingsystem ? {
            "openbsd" => "wheel",
            default   => "root",
        },
        require => Package["python-ldap"],
    }
    python::compile { "${::pythonsitedir}/dynldap.py": }

}


# Install Ruby ldap bindings.
#
class ldap::client::ruby {

    case $::operatingsystem {
        "ubuntu","debian": {
            $pkgname = regsubst($rubyversion, '^([0-9]+\.[0-9]+)\..*', 'libldap-ruby\1')
        }
        default: {
            $pkgname = "ruby-ldap"
        }
    }

    package { "ruby-ldap":
        ensure => installed,
        name   => $pkgname,
    }

}


# Install OpenLDAP server.
#
# === Global variables
#
#   $ldap_datadir:
#       Directory for LDAP databases. Defaults to /srv/ldap.
#
#   $ldap_modules:
#       List of dynamic modules to load, syncprov and ppolicy modules
#       are always loaded.
#
#   $ldap_server_key:
#       Path to SSL private key. Defaults to puppet client key.
#
#   $ldap_server_cert:
#       Path to SSL certificate. Defaults to puppet client certificate.
#
class ldap::server {

    require ssl

    if !$ldap_server_key {
        $ldap_server_key = "${puppet_ssldir}/private_keys/${homename}.pem"
    }
    if !$ldap_server_cert {
        $ldap_server_cert = "${puppet_ssldir}/certs/${homename}.pem"
    }

    case $::operatingsystem {
        "debian","ubuntu": {
            $user = "openldap"
            $group = "openldap"
            $package_name = "slapd"
            $service_name = "slapd"
            $config = "/etc/ldap"
            $modulepath = "/usr/lib/ldap"
            $rundir = "/var/run/slapd"
            exec { "usermod-openldap":
                path    => "/bin:/usr/bin:/sbin:/usr/sbin",
                command => "usermod -a -G ssl-cert openldap",
                unless  => "id -n -G openldap | grep '\\bssl-cert\\b'",
                require => Package["openldap-server"],
                before  => Exec["slaptest"],
            }
        }
        "fedora": {
            $user = "ldap"
            $group = "ldap"
            $package_name = "openldap-servers"
            $service_name = "slapd"
            $config = "/etc/openldap"
            $modulepath = $architecture ? {
                "x86_64" => "/usr/lib64/openldap",
                default  => "/usr/lib/openldap",
            }
            $rundir = "/var/run/openldap"
        }
        "centos","redhat": {
            $user = "ldap"
            $group = "ldap"
            $package_name = $::operatingsystemrelease ? {
                /^5/ => [ "openldap-servers", "openldap-servers-overlays" ],
                /^6/ => "openldap-servers",
            }
            $service_name = $::operatingsystemrelease ? {
                /^5/ => "ldap",
                /^6/ => "slapd",
            }
            $config = "/etc/openldap"
            $modulepath = $architecture ? {
                "x86_64" => "/usr/lib64/openldap",
                default  => "/usr/lib/openldap",
            }
            $rundir = "/var/run/openldap"
        }
        "openbsd": {
            $user = "_openldap"
            $group = "_openldap"
            $package_name = "openldap-server"
            $service_name = "slapd"
            $config = "/etc/openldap"
            $modulepath = ""
            $rundir = "/var/run/openldap"
        }
    }

    package { "openldap-server":
        ensure => installed,
        name   => $package_name,
    }

    file { "${ssl::certs}/slapd.crt":
        ensure  => present,
        source  => $ldap_server_cert,
        mode    => "0644",
        owner   => "root",
        group   => $::operatingsystem ? {
            "openbsd" => "wheel",
            default   => "root",
        },
        require => Package["openldap-server"],
        notify  => Exec["slaptest"],
    }
    file { "${ssl::private}/slapd.key":
        ensure  => present,
        source  => $ldap_server_key,
        mode    => "0640",
        owner   => "root",
        group   => $group,
        require => Package["openldap-server"],
        notify  => Exec["slaptest"],
    }

    file { "slapd.conf":
        ensure  => present,
        path    => "${config}/slapd.conf",
        content => template("ldap/slapd.conf.erb"),
        mode    => "0640",
        owner   => "root",
        group   => $group,
        notify  => Exec["slaptest"],
        require => Package["openldap-server"],
    }
    file { "${config}/slapd.conf.d":
        ensure  => directory,
        source  => "puppet:///modules/custom/empty",
        mode    => "0640",
        owner   => "root",
        group   => $group,
        purge   => true,
        recurse => true,
        force   => true,
        require => Package["openldap-server"],
        notify  => Exec["generate-slapd-database-config"],
    }

    case $::operatingsystem {
        "centos","redhat": {
            if $::operatinsystemrelease !~ /^5\./ {
                file { "/etc/sysconfig/ldap":
                    ensure  => present,
                    source  => "puppet:///modules/ldap/ldap.sysconfig",
                    mode    => "0644",
                    owner   => "root",
                    group   => "root",
                    notify  => Exec["slaptest"],
                    require => Package["openldap-server"],
                }
            }
        }
        "debian","ubuntu": {
            file { "/etc/default/slapd":
                source  => "puppet:///modules/ldap/slapd.default",
                mode    => "0644",
                owner   => "root",
                group   => "root",
                notify  => Exec["slaptest"],
                require => Package["openldap-server"],
            }
        }
    }

    exec { "slaptest":
        command     => "slaptest -f ${config}/slapd.conf",
        path        => "/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin",
        refreshonly => true,
        require     => File["${config}/slapd.conf.d"],
        notify      => Service["slapd"],
    }

    service { "slapd":
        name    => $service_name,
        start   => $::operatingsystem ? {
            "openbsd" => "/usr/local/libexec/slapd -u _openldap -h ldap:///\\ ldaps:///\\ ldapi:///",
            default   => undef,
        },
        ensure  => running,
        enable  => true,
        require => Package["openldap-server"]
    }

    if $ldap_datadir {
        file { $ldap_datadir:
            ensure  => directory,
            mode    => "0700",
            owner   => $user,
            group   => $group,
            seltype => "slapd_db_t",
            require => Package["openldap-server"],
        }
        file { "/srv/ldap":
            ensure  => link,
            target  => $ldap_datadir,
            seltype => "slapd_db_t",
            require => File[$ldap_datadir],
        }
    } else {
        file { "/srv/ldap":
            ensure  => directory,
            mode    => "0700",
            owner   => $user,
            group   => $group,
            seltype => "slapd_db_t",
            require => Package["openldap-server"],
        }
    }

    if "${selinux}" == "true" {
        selinux::manage_fcontext { "/srv/ldap(/.*)?":
            type   => "slapd_db_t",
            before => File["/srv/ldap"],
        }
        if $ldap_datadir {
            selinux::manage_fcontext { "${ldap_datadir}(/.*)?":
                type   => "slapd_db_t",
                before => File[$ldap_datadir],
            }
        }
    }

    file { "${config}/schema":
        ensure  => directory,
        source  => "puppet:///modules/custom/empty",
        mode    => "0644",
        owner   => "root",
        group   => $::operatingsystem ? {
            "openbsd" => "wheel",
            default   => "root",
        },
        purge   => true,
        recurse => true,
        force   => true,
        require => Package["openldap-server"],
        notify  => Exec["generate-slapd-schema-config"],
    }
    file { "${config}/slapd.conf.d/schema.conf":
        ensure  => present,
        mode    => "0640",
        owner   => "root",
        group   => $group,
        require => Exec["generate-slapd-schema-config"],
    }
    exec { "generate-slapd-schema-config":
        command     => "find ${config}/schema/*.schema -exec echo 'include {}' \\; | sort -n > ${config}/slapd.conf.d/schema.conf",
        path        => "/bin:/usr/bin:/sbin:/usr/sbin",
        refreshonly => true,
        require     => File["${config}/slapd.conf.d"],
        notify      => Exec["slaptest"],
    }
    ldap::server::schema { [ "core", "cosine", "ppolicy", ]:
        idx => 10,
    }

    file { "${config}/slapd.conf.d/database.conf":
        ensure  => present,
        mode    => "0640",
        owner   => "root",
        group   => $group,
        require => Exec["generate-slapd-database-config"],
        notify  => Exec["slaptest"],
    }
    exec { "generate-slapd-database-config":
        command     => "find ${config}/slapd.conf.d/db.*.conf -exec echo 'include {}' \\; > ${config}/slapd.conf.d/database.conf",
        path        => "/bin:/usr/bin:/sbin:/usr/sbin",
        refreshonly => true,
        notify      => Exec["slaptest"],
    }

}


# Create new LDAP database.
#
# === Parameters
#
#   $name:
#       Database suffix (base DN).
#
#   $aclsource:
#       Source file for custom ACL's. Default is to use template.
#
#   $master:
#       Master LDAP server URI when creating slave database.
#
#   $syncpw:
#       Password for uid=replicator,cn=config,${name} user on master.
#       Only needed for slave databases.
#
#   $rid:
#       Replica ID. Must be unique per replica per database.
#
#   $moduleoptions:
#       Options for overlay modules.
#
# === Sample usage
#
# ldap::server::database { "dc=example,dc=com":
#     moduleoptions => [ "smbkrb5pwd-enable=samba", ]
# }
#
define ldap::server::database($aclsource = "", $master = "", $syncpw = "", $rid = "", $moduleoptions = []) {

    include ldap::server

    if $rid == "" {
        $rid_real = fqdn_rand(999)
    } else {
        $rid_real = $rid
    }

    file { "${ldap::server::config}/slapd.conf.d/db.${name}.conf":
        ensure  => present,
        content => template("ldap/slapd-database.conf.erb"),
        mode    => "0640",
        owner   => "root",
        group   => $ldap::server::group,
        notify  => Exec["generate-slapd-database-config"],
    }

    file { "${ldap::server::config}/slapd.conf.d/acl.${name}.conf":
        ensure  => present,
        source  => $aclsource ? {
            ""      => undef,
            default => $aclsource,
        },
        content => $aclsource ? {
            ""      => template("ldap/slapd-acl.conf.erb"),
            default => undef,
        },
        mode    => "0640",
        owner   => "root",
        group   => $ldap::server::group,
        notify  => Exec["slaptest"],
    }

    file { "${ldap::server::config}/slapd.conf.d/index.${name}.conf":
        ensure  => present,
        source  => [ "puppet:///files/ldap/slapd-index.conf.${name}",
                     "puppet:///files/ldap/slapd-index.conf",
                     "puppet:///modules/ldap/slapd-index.conf", ],
        mode    => "0640",
        owner   => "root",
        group   => $ldap::server::group,
        notify  => Exec["slaptest"],
    }

    file { "/srv/ldap/${name}":
        ensure  => directory,
        mode    => "0700",
        owner   => $ldap::server::user,
        group   => $ldap::server::group,
        seltype => "slapd_db_t",
        require => File["/srv/ldap"],
    }

    file { "/srv/ldap/${name}/DB_CONFIG":
        ensure  => present,
        source  => [ "puppet:///files/ldap/DB_CONFIG.${name}",
                     "puppet:///files/ldap/DB_CONFIG",
                     "puppet:///modules/ldap/DB_CONFIG", ],
        mode    => "0644",
        owner   => "root",
        group   => $::operatingsystem ? {
            "openbsd" => "wheel",
            default   => "root",
        },
        seltype => "slapd_db_t",
        require => File["/srv/ldap/${name}"],
        before  => Exec["slaptest"],
    }

}


# Install custom schema to OpenLDAP.
#
# === Parameters
#
#   $name:
#       Schema name.
#
#   $idx:
#       Schema load order. Defaults to 50.
#
# === Sample usage
#
# ldap::server::schema { "samba": }
#
define ldap::server::schema($idx = 50) {

    include ldap::server

    file { "${name}.schema":
        ensure  => present,
        path    => "${ldap::server::config}/schema/${idx}-${name}.schema",
        source  => [ "puppet:///files/ldap/${name}.schema",
                     "puppet:///modules/ldap/${name}.schema", ],
        mode    => "0644",
        owner   => "root",
        group   => $::operatingsystem ? {
            "openbsd" => "wheel",
            default   => "root",
        },
        require => Package["openldap-server"],
        notify  => Exec["generate-slapd-schema-config"],
    }
}

