<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:util="http://www.springframework.org/schema/util"
  xmlns:sec="http://www.springframework.org/schema/security"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.3.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-5.8.xsd">
  <!-- The Spring Schemas are the latest available schemas with pinned version numbers -->
  <!-- Configure security for web APIs. -->
  <util:list id="webApiPathList" value-type="java.lang.String">
    <value>/webapi/**</value>
    <value>/internal/webapi/**</value>
    <value>/plugins/servlet/stateless/**</value>
    <value>/deployment-management/**</value>
    <value>/connected-system-management/**</value>
    <value>/cloud-database-management/**</value>
  </util:list>
  <!-- Configure security for rest APIs. -->
  <!-- Switch from MultiPathRequestMatcher (Ant) to ContentRestServiceSpecializedMultiPathRegexRequestMatcher (Regex)-->
  <!-- What makes it specialized is if conf.content.download.login.prompt is enabled it makes document download links not match. -->
  <util:list id="restApiPathList" value-type="java.lang.String">
    <value>/api/.*</value>
    <value>/rest/.*</value>
    <value>/clientapi/.*</value>
    <value>/portals/clientapi/.*</value>
  </util:list>

  <bean id="webApiRequestMatcher" class="com.appiancorp.common.spring.MultiPathRequestMatcher">
    <constructor-arg ref="webApiPathList"/>
  </bean>
  <bean id="restApiRequestMatcher" class="com.appiancorp.common.spring.ContentRestServiceSpecializedMultiPathRegexRequestMatcher">
    <constructor-arg ref="restApiPathList"/>
  </bean>

<!--  Since custom filters cannot reference other custom filters when setting up the order-->
<!--  of filters in a filter chain, define the desired order in a composite filter-->
  <bean id="preBasicAuthFilter" class="org.springframework.web.filter.CompositeFilter">
    <property name="filters">
      <util:list value-type="javax.servlet.Filter">
        <ref bean="oAuthFilter" />
        <ref bean="apiKeyFilter" />
        <ref bean="rpaTokenFilter" />
      </util:list>
    </property>
  </bean>

  <sec:http create-session="never" use-expressions="false" request-matcher-ref="webApiRequestMatcher" disable-url-rewriting="false">
    <!-- This is needed for CSRF protection and must not be removed -->
    <sec:csrf disabled="true"/>
    <sec:custom-filter ref="csrfChannelProcessingFilter" before="LOGOUT_FILTER" />
    <sec:custom-filter position="CONCURRENT_SESSION_FILTER" ref="appianForcedLogoutSessionFilter" />
    <sec:custom-filter ref="logoutReasonFilter" before="CONCURRENT_SESSION_FILTER"/>

    <!-- This filter is placed early in the chain (just after the channel filter),
          because we want to validate the client before starting the authentication process -->
    <sec:custom-filter ref="mobileClientValidationFilter" after="CHANNEL_FILTER"/>

    <sec:request-cache ref="appianWebApiRequestCache"/>
    <sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY,IS_AUTHENTICATED_REMEMBERED"/>
    <sec:http-basic entry-point-ref="httpBasicAuthenticationEntryPoint"
      authentication-details-source-ref="webApiAuthenticationDetailsSource"/>
    <sec:anonymous enabled="false"/>
    <sec:session-management session-authentication-strategy-ref="webApiSessionAuthenticationStrategy"/>

    <sec:remember-me services-ref="appianRememberMeServices"/>
    <sec:custom-filter ref="userActivityFilter" after="FILTER_SECURITY_INTERCEPTOR" />
    <sec:custom-filter ref="cookieTheftRedirectFilter" after="REMEMBER_ME_FILTER" />
    <sec:custom-filter before="BASIC_AUTH_FILTER" ref="preBasicAuthFilter"/>
    <sec:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
    <sec:custom-filter ref="mtlsForWebApiX509AuthenticationFilter" before="SECURITY_CONTEXT_FILTER" />
    <sec:headers disabled="true"/>
  </sec:http>

  <!-- This is different than the webApi because we do not want SAIL rest api / AJAX calls to go through the
       SAML Filter, which should only be used when navigating to a URL through a browser -->
  <sec:http create-session="never" use-expressions="false" request-matcher-ref="restApiRequestMatcher" disable-url-rewriting="false">
    <!-- This is needed for CSRF protection and must not be removed -->
    <sec:csrf disabled="true"/>
    <sec:custom-filter ref="csrfChannelProcessingFilter" before="LOGOUT_FILTER" />
    <sec:custom-filter position="CONCURRENT_SESSION_FILTER" ref="appianForcedLogoutSessionFilter" />
    <sec:custom-filter ref="logoutReasonFilter" before="CONCURRENT_SESSION_FILTER"/>

    <!-- This filter is placed early in the chain (just after the channel filter),
          because we want to validate the client before starting the authentication process -->
    <sec:custom-filter ref="mobileClientValidationFilter" after="CHANNEL_FILTER"/>

    <sec:request-cache ref="appianWebApiRequestCache"/>
    <sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY,IS_AUTHENTICATED_REMEMBERED"/>
    <sec:http-basic entry-point-ref="httpBasicAuthenticationEntryPoint"
      authentication-details-source-ref="webApiAuthenticationDetailsSource"/>
    <sec:anonymous enabled="false"/>
    <sec:session-management session-authentication-strategy-ref="webApiSessionAuthenticationStrategy"/>

    <sec:remember-me services-ref="appianRememberMeServices"/>
    <sec:custom-filter ref="userActivityFilter" after="FILTER_SECURITY_INTERCEPTOR" />
    <sec:custom-filter ref="cookieTheftRedirectFilter" after="REMEMBER_ME_FILTER" />
    <sec:custom-filter before="BASIC_AUTH_FILTER" ref="preBasicAuthFilter"/>
    <sec:headers disabled="true"/>
  </sec:http>

  <sec:http pattern="/s/**" create-session="never" use-expressions="false" disable-url-rewriting="false">
    <!-- This filter is placed early in the chain (just after the channel filter),
          because we want to validate the client before starting the authentication process -->
    <sec:custom-filter ref="mobileClientValidationFilter" after="CHANNEL_FILTER"/>
    <sec:custom-filter position="CONCURRENT_SESSION_FILTER" ref="appianForcedLogoutSessionFilter" />
    <sec:custom-filter ref="logoutReasonFilter" before="CONCURRENT_SESSION_FILTER"/>
    <sec:csrf disabled="true"/>

    <sec:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY,IS_AUTHENTICATED_REMEMBERED"/>
    <sec:http-basic entry-point-ref="httpBasicAuthenticationEntryPoint"
      authentication-details-source-ref="webApiAuthenticationDetailsSource"/>
    <sec:anonymous enabled="false"/>
    <sec:session-management session-authentication-strategy-ref="webApiSessionAuthenticationStrategy"/>

    <sec:remember-me services-ref="appianRememberMeServices"/>

    <sec:custom-filter ref="userActivityFilter" after="FILTER_SECURITY_INTERCEPTOR" />
    <sec:custom-filter ref="cookieTheftRedirectFilter" after="REMEMBER_ME_FILTER" />
    <sec:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/>
    <sec:headers disabled="true"/>
  </sec:http>

  <!-- For this entrypoint we do not want cache REST request to forward the user after authentication -->
  <bean id="appianWebApiRequestCache" class="org.springframework.security.web.savedrequest.NullRequestCache" />

  <bean id="mobileClientValidationFilter" class="com.appiancorp.security.auth.MobileClientValidationFilter">
    <constructor-arg name="mobileConfig" ref="mobileConfiguration" />
    <constructor-arg name="authenticationEntryPoint" ref="httpBasicAuthenticationEntryPoint" />
  </bean>

  <bean id="httpBasicAuthenticationEntryPoint" class="com.appiancorp.security.auth.HttpBasicAuthenticationEntryPoint">
    <constructor-arg name="customBrandingConfiguration" ref="customBrandingConfiguration" />
    <constructor-arg name="inAppBrowserClientRequestMatcher" ref="inAppBrowserClientRequestMatcher" />
  </bean>
</beans>
