# General SSO class for SOC. Based on cnaas::sso
#
# @param hostname FQDN of the host this is running on.
#
# @param email Support email used in error messages etc.
#
# @param service_endpoint Location of service to reverse proxy for.
#
# @param groups
#   List of user groups from sso_groups in global/overlay/etc/hiera/data/common.yaml. The
#   default is a non-existing placeholder group, added to make the Apache config valid.
#
# @param passthrough List of paths to disable SAML protection for, e.g. API paths.
#
# @param x_remote_user
#   If true, EPPN is put in the HTTP header X-Remote-User instead of REMOTE_USER.
#
# @param single_user
#   If true, EPPN is discarded and X-Remote-User is set to "soc-user". This is useful in
#   cases where the service we reverse proxy for can't create new accounts automatically.
#   We use this only for Graylog at the time of writing.
#
# @param satos
#   If we have a satosa proxy or not, default true.
#
# @param proxy
#   Hostname of the satosa proxy.
#
# @param entityID
#   EntityID of the satosa proxy, must not be the same as the proxy hostname.
#   Default set to value of proxy.

class soc::sso(
  String            $ssotype          = 'docker',
  String            $hostname         = $facts['networking']['fqdn'],
  String            $email            = 'cert@cert.sunet.se',
  Optional[String]  $service_endpoint = undef,
  Array             $groups           = ['PLACEHOLDER'],
  Array             $passthrough      = [],
  Boolean           $x_remote_user    = false,
  Boolean           $single_user      = false,
  Boolean           $satosa           = true,
  Boolean           $satosa_certbot   = false,
  String            $translog         = 'INFO',
  String            $proxy            = 'https://shared-sso-proxy1.cert.sunet.se/idp',
  String            $entityID         = $proxy,
) {

  if $ssotype == "docker" {
    file { '/opt/sso':
      ensure => directory,
    }

    #
    # Apache files
    #

    file { '/opt/sso/apache':
      ensure => directory,
    }

    file { '/opt/sso/apache/site.conf':
      ensure  => file,
      content => template('soc/sso/apache-site.conf.erb'),
    }

    # SSL defaults copied from certbot:
    # https://github.com/certbot/certbot/blob/master/certbot-apache/certbot_apache/_internal/tls_configs/current-options-ssl-apache.conf
    file { '/opt/sso/apache/ssl.conf':
      ensure  => file,
      content => file('soc/sso/apache-ssl.conf'),
    }
  }

  if $ssotype == 'docker' {
    $apache_groups = '/opt/sso/apache/groups.txt'
  } elsif $ssotype == 'apache' {
    $apache_groups = '/etc/apache2/groups.txt'
  } else {
    $apache_groups = '/tmp/groups.txt'
  }

  file { $apache_groups:
    ensure  => file,
    content => template('soc/sso/apache-groups.txt.erb')
  }

  #
  # Shibboleth files
  #

  if $ssotype == 'apache' {
    package { ['libapache2-mod-shib', 'shibboleth-sp-utils']:
      ensure => installed,
    }

    exec { 'Make sure mod_shib is loaded':
      command => 'a2enmod shib',
      creates => '/etc/apache2/mods-enabled/shib.load',
    }

    exec { 'Make sure authz_groupfile is loaded':
      command => 'a2enmod authz_groupfile',
      creates => '/etc/apache2/mods-enabled/authz_groupfile.load',
    }

    $shibbase = '/etc/shibboleth'

  } elsif $ssotype == 'docker' {
    $shibbase = '/opt/sso/shibboleth'

    file { $shibbase:
      ensure => directory,
    }
  }

  file { "${shibbase}/shibboleth2.xml":
    ensure  => file,
    content => template('soc/sso/shibboleth2.xml.erb'),
  }

  file { "${shibbase}/shibd.logger":
    ensure  => file,
    content => template('soc/sso/shibd.logger.erb'),
  }

  file { "${shibbase}/attribute-map.xml":
    ensure  => file,
    content => file('soc/sso/attribute-map.xml'),
  }

  file { "${shibbase}/md-signer2.crt":
    ensure  => file,
    content => file('soc/sso/md-signer2.crt'),
  }

  if $satosa {
    file { "${shibbase}/frontend.xml":
      ensure  => file,
      content => file('soc/sso/frontend.xml'),
    }

    file { "${shibbase}/attribute-policy.xml":
      ensure  => file,
      content => file('soc/sso/attribute-policy.xml'),
    }

    if lookup('sso_sp_key', undef, undef, undef) != undef {
      sunet::snippets::secret_file { "${shibbase}/sp-key.pem":
        hiera_key => 'sso_sp_key'
      }
    } else {
      sunet::snippets::keygen {'shib_cert':
        key_file  => "${shibbase}/sp-key.pem",
        cert_file => "${shibbase}/sp-cert.pem"
      }
    }
  } else {
    sunet::snippets::secret_file { "${shibbase}/sp-key.pem":
      hiera_key => 'sso_sp_key'
    }
  }

  #
  # Certbot
  #

  if $satosa_certbot {
    package { ['certbot', 'python3-requests']:
      ensure => 'latest',
    }

    file { '/etc/letsencrypt/acme-dns-auth.py':
      ensure  => file,
      content => file('soc/sso/acme-dns-auth.py'),
      mode    => '0744',
    }

    file { '/etc/letsencrypt/renewal-hooks/deploy/soc-sso-reload':
      ensure  => file,
      mode    => '0700',
      content => "#!/bin/sh -eu\ndocker exec sso service apache2 reload",
    }

    sunet::scriptherder::cronjob { 'le_renew':
      cmd     => '/bin/echo "Keeping this cronjob, but disabled to avoid scriptherder complainign about unknown check"',
      special => 'daily',
    }
  }

  #
  # Docker
  #

  if $ssotype == 'docker' {

    exec {"Create Docker network \"sso\" to talk to service":
      # We OR with true to ignore errors, since the network often already exists.
      # We specify a subnet so that services which have the option/requirement can
      # specify this subnet as source of trusted proxies. This is used in Graylog,
      # for example; see setting "trusted_proxies".
      command => 'docker network create sso --subnet 172.29.0.0/24 || true',
      unless  => 'docker network ls | grep -q sso 2>&1'
    }

    file { '/opt/sso/docker-compose.yml':
      ensure  => file,
      mode    => '0600',
      content => template('soc/sso/docker-compose.yml.erb'),
    }

    sunet::docker_compose_service { 'sso':
      description  =>  '',
      compose_file => '/opt/sso/docker-compose.yml',
    }

    #
    # NFT Rules
    #

    sunet::nftables::docker_expose { 'apache_sso_https' :
      allow_clients => ['0.0.0.0/0'],
      port          => 443,
      iif           => 'ens3',
    }
  }

}