#
# Enable the login forms and authn system.
#

# Defaults to use on platforms without a defines conf.
<IfDefine !HTDOCS_PATH>
    Define HTDOCS_PATH /usr/share/apache2/htdocs
</IfDefine>
<IfDefine !HTCONF_PATH>
    Define HTCONF_PATH /etc/apache2
</IfDefine>
<IfDefine !OPENSSL_PATH>
    Define OPENSSL_PATH openssl
</IfDefine>
<IfDefine !MODULES_PATH>
    Define MODULES_PATH lib/apache2/modules
</IfDefine>
<IfDefine !AUTHN_CACHE_PROVIDER>
    Define AUTHN_CACHE_PROVIDER shmcb
</IfDefine>
<IfDefine !SERVER_CONFIGURED>
    # Default to the file provider if we aren't using a defines conf.
    Define AUTHN_FILE_ENABLED
</IfDefine>

<IfModule !socache_${AUTHN_CACHE_PROVIDER}_module>
    LoadModule socache_${AUTHN_CACHE_PROVIDER}_module ${MODULES_PATH}/mod_socache_${AUTHN_CACHE_PROVIDER}.so
</IfModule>

# Determine which providers to use. The order is significant.
<IfDefine AUTHN_FILE_ENABLED>
    LoadModule authn_file_module ${MODULES_PATH}/mod_authn_file.so
    <IfDefine AUTHN_PROVIDER>
        Define AUTHN_PROVIDER "${AUTHN_PROVIDER} file"
    </IfDefine>
    <IfDefine !AUTHN_PROVIDER>
        Define AUTHN_PROVIDER file
    </IfDefine>
</IfDefine>
<IfDefine AUTHN_WINDOWS_ENABLED>
    LoadModule authnz_niwindows_module ${MODULES_PATH}/mod_authnz_niwindows.so
    <IfDefine AUTHN_PROVIDER>
        Define AUTHN_PROVIDER "${AUTHN_PROVIDER} windows"
    </IfDefine>
    <IfDefine !AUTHN_PROVIDER>
        Define AUTHN_PROVIDER windows
    </IfDefine>

    # Set up authentication caching.
    <IfDefine AUTHN_CACHE_WINDOWS>
        Define AUTHN_CACHE_CONTEXT windows
        AuthNIWindowsSOCache ${AUTHN_CACHE_PROVIDER}
    </IfDefine>
</IfDefine>
<IfDefine AUTHN_LDAP_ENABLED>
    LoadModule authnz_ldap_module ${MODULES_PATH}/mod_authnz_ldap.so
    <IfDefine AUTHN_PROVIDER>
        Define AUTHN_PROVIDER "${AUTHN_PROVIDER} ldap"
    </IfDefine>
    <IfDefine !AUTHN_PROVIDER>
        Define AUTHN_PROVIDER ldap
    </IfDefine>

    <IfDefine AUTHN_WINDOWS_ENABLED>
        # Pass failed Windows authn requests to LDAP.
        AuthNIWindowsAuthoritative Off
    </IfDefine>
</IfDefine>

# Load our privilege module for authorization.
LoadModule authz_niprivilege_module ${MODULES_PATH}/mod_authz_niprivilege.so

# Load our API key support backed by DBD.
LoadModule auth_niapikey_module ${MODULES_PATH}/mod_auth_niapikey.so

<IfModule !authn_dbd_module>
    LoadModule authn_dbd_module ${MODULES_PATH}/mod_authn_dbd.so
</IfModule>

# Load our provider configuration module.
LoadModule niprovider_config_module ${MODULES_PATH}/mod_niprovider_config.so
NIProvidersAuthType ApiKey Enabled
NIProvidersAuthType Basic
NIProvidersAuthType Form Logout
NIProvidersDefaultLoginRedirect /login/current.shtml
NIProvidersLogoutLocation /login/logout

<IfDefine AUTHN_PROVIDER>
    LoadModule auth_basic_module modules/mod_auth_basic.so
    NIProvidersAuthType Basic +Enabled
    NIProvidersAuthType Form +Enabled
</IfDefine>
<IfDefine !AUTHN_PROVIDER>
    LoadModule auth_disabled_module ${MODULES_PATH}/mod_auth_disabled.so
</IfDefine>

# Set up session crypto.
SessionCryptoDriver openssl

# XXX: iconv is crashing on Linux RT when we enable AuthLDAPCharsetConfig.
<IfDefine AUTHN_LDAP_USE_CHARSET>
    <IfDefine AUTHN_LDAP_ENABLED>
        AuthLDAPCharsetConfig ${HTCONF_PATH}/charset.conv
    </IfDefine>
</IfDefine>

# Sets the ni_auth_type envvar to the selected auth type.
Include ${HTCONF_PATH}/conf.d/select-auth-type.d/*.conf

# Provide a macro to enable browser authentication in a directory or location.
<Macro NIAuthnEnableBrowsers>
    <If "reqenv('ni_auth_type') == 'Form'">
        Session On
    </If>

    <If "reqenv('ni_auth_browser') == 1">
        <IfDefine TLS_ENABLED>
            Header always add Set-Cookie "AUTH_REDIR_URI_SECURE=%{AUTH_REQUEST_URI}e;HttpOnly;Secure;path=/;SameSite=Lax;" "expr=-z %{REMOTE_USER}"
        </IfDefine>
        <IfDefine !TLS_ENABLED>
            Header always add Set-Cookie "AUTH_REDIR_URI=%{AUTH_REQUEST_URI}e;HttpOnly;path=/;SameSite=Lax;" "expr=-z %{REMOTE_USER}"
        </IfDefine>
    </If>
</Macro>

# Provide a macro to require a privilege for a given application. This should
# not be used more than once in a single directory or location.
<Macro NIAuthnRequirePrivilege $app $priv>
    AuthNIPrivilegeApplication $app
    <LimitExcept OPTIONS>
        Require privilege $priv
    </LimitExcept>
</Macro>

<Location />
    Include ${HTCONF_PATH}/conf.d/apply-auth-type.d/*.conf
    <If "reqenv('ni_auth_browser') == 1">
        # Set up the redirection URI for Header to read if needed.
        SetEnvIf Request_URI "^(.*)$" "AUTH_REQUEST_URI=$1"
    </If>

    AuthName "protected area"

    # Authentication module settings
    <IfDefine AUTHN_FILE_ENABLED>
        AuthUserFile ${HTCONF_PATH}/.htpasswd
    </IfDefine>
    <IfDefine AUTHN_WINDOWS_ENABLED>
        AuthNIWindowsRemoteUserIsSID On
        <IfDefine AUTHN_WINDOWS_UPN>
            AuthNIWindowsAcceptUPN On
            Define AUTHN_CACHE_CONTEXT windows-UPN
        </IfDefine>
        <IfDefine !AUTHN_WINDOWS_UPN>
            <IfDefine AUTHN_WINDOWS_DOMAIN>
                AuthNIWindowsDomain ${AUTHN_WINDOWS_DOMAIN}
                Define AUTHN_CACHE_CONTEXT "windows-domain-${AUTHN_WINDOWS_DOMAIN}"
            </IfDefine>
        </IfDefine>
        <IfDefine AUTHN_CACHE_WINDOWS>
            AuthNIWindowsCache On
            AuthNIWindowsCacheContext ${AUTHN_CACHE_CONTEXT}
            <IfDefine AUTHN_CACHE_TIMEOUT>
                AuthNIWindowsCacheTimeout ${AUTHN_CACHE_TIMEOUT}
            </IfDefine>
        </IfDefine>
    </IfDefine>
    <IfDefine AUTHN_LDAP_ENABLED>
        AuthLDAPBindDN ${AUTHN_LDAP_BIND_DN}
        AuthLDAPBindPassword ${AUTHN_LDAP_BIND_PASSWORD}
        AuthLDAPUrl ${AUTHN_LDAP_URL}
        AuthLDAPRemoteUserIsDN On
        AuthLDAPSubGroupClass group groupOfNames groupOfUniqueNames
        LDAPReferrals Off
    </IfDefine>

    # Authorization module settings
    AuthNIPrivilegeDeviceVariable isDevice
    AuthNIPrivilegeDirectory ${HTCONF_PATH}/htpriv.d
    AuthNIPrivilegeRoleMappings ${HTCONF_PATH}/.htroles

    <IfDefine AUTHN_DBROLES_ENABLED>
        AuthNIApiKeyRolesVariable roles
    </IfDefine>

    # Expire sessions after 15 minutes, but only update the expiry time every 10 seconds.
    # Note that SessionExpiryUpdateInterval support comes from trunk mod_session.
    SessionMaxAge 900
    # XXX: Disabling SessionExpiryUpdateInterval because it does not play well
    # Apache bug 58171, where restarting the web server prevents the session
    # from being reset.
    #SessionExpiryUpdateInterval 10
    <IfDefine TLS_ENABLED>
        SessionDBDCookieName session_secure path=/;HttpOnly;Secure;SameSite=Lax
    </IfDefine>
    <IfDefine !TLS_ENABLED>
        SessionDBDCookieName session path=/;HttpOnly;SameSite=Lax
    </IfDefine>
    SessionCryptoPassphrase "exec:${OPENSSL_PATH} rand -base64 32"
</Location>

# Contains the static files for the form auth login page.
<Location /login>
    <If "reqenv('ni_auth_browser') == 1">
        # Get AUTH_REDIR_URI from cookie --> env, so that mod_include can insert it into the log-in page.
        # We first default to /
        # TODO: default to a login successful page?
        SetEnvIf Request_URI .* "AUTH_REDIR_URI=/"
        <IfDefine TLS_ENABLED>
            SetEnvIf Cookie "(^|;\s*)AUTH_REDIR_URI_SECURE=(/[^;]*)($|;)" "AUTH_REDIR_URI=$2"
        </IfDefine>
        <IfDefine !TLS_ENABLED>
            SetEnvIf Cookie "(^|;\s*)AUTH_REDIR_URI=(/[^;]*)($|;)" "AUTH_REDIR_URI=$2"
        </IfDefine>
    </If>

    <If "reqenv('ni_auth_type') == 'Form'">
        <IfDefine !AUTHN_PROVIDER>
            SetEnv FORM_AUTH_DISABLED 1
        </IfDefine>
        <IfDefine LOGIN_REDIRECT_INCLUDE>
            SetEnv LOGIN_REDIRECT_INCLUDE "${LOGIN_REDIRECT_INCLUDE}"
        </IfDefine>
    </If>
    <Else>
        # Since we are in an If, this will override later Location and Directory
        # scopes that enable sessions and set cookies.
        Session Off
    </Else>

    # Enable server-side includes.
    AddType text/html .shtml
    AddOutputFilter INCLUDES .shtml
    Options +Includes
    DirectoryIndex login.shtml

    #
    # Tell clients to re-validate cached assets every hour rather than
    # rely on default freshness calculations based on Last-Modified.
    #
    <IfModule headers_module>
        Header setifempty Cache-Control "max-age=3600"
    </IfModule>
</Location>

<Location /login/config>
    Use NIAuthnEnableBrowsers
    SetHandler niprovider-config-handler
</Location>

<Location /login/login-redirect>
    <If "reqenv('ni_auth_type') != 'Form'">
        Redirect 404
    </If>

    # If authentication fails, issue a 401 instead of a 200 OK. This will lead
    # to an internal redirect to the ErrorDocument (login.shtml).
    Redirect 401
</Location>

# Form auth processor route that handles the posted auth form.
<Location /login/login.do>
    <If "reqenv('ni_auth_type') != 'Form' && -T NIAuthTypeHasLogout(reqenv('ni_auth_type'))">
        Session On
        AuthFormLocation __disabled__
        AuthFormLoginSuccessLocation /login/logout?logoutLocation=/login/current.shtml
    </If>
    <ElseIf "reqenv('ni_auth_type') != 'Form'">
        Redirect 404
    </ElseIf>
    <IfDefine AUTHN_PROVIDER>
        Session On
        SetHandler form-login-handler
        # Will redirect back to the posted `httpd_location`
    </IfDefine>
    <IfDefine !AUTHN_PROVIDER>
        Redirect 403
        ErrorDocument 403 "Username and password authentication is disabled in the web server configuration."
    </IfDefine>
</Location>

# Form auth processor route that logs out users
<Location /login/logout>
    SetEnvIf ni_auth_logout_location "^$" ni_auth_logout_location=/login/
    Include ${HTCONF_PATH}/conf.d/logout.d/*.conf
</Location>

<Directory ${HTDOCS_PATH}/login>
    <Files login.shtml>
        <If "reqenv('ni_auth_type') != 'Form'">
            Redirect /login/current.shtml
        </If>

        # Disable caching of pages that rely on user information.
        <IfModule headers_module>
            Header always set Cache-Control no-store
        </IfModule>
    </Files>
    <Files current.s*>
        Use NIAuthnEnableBrowsers

        # Authn is not performed unless Authz calls for it.
        Require valid-user

        # Disable caching of pages that rely on user information.
        <IfModule headers_module>
            Header always set Cache-Control no-store
        </IfModule>
    </Files>
</Directory>
