<!--
        Licensed to the Apache Software Foundation (ASF) under one or more contributor
        license agreements. See the NOTICE file distributed with this work for additional
        information regarding copyright ownership. The ASF licenses this file to You under
        the Apache License, Version 2.0 (the "License"); you may not use this file except in
        compliance with the License. You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or
        agreed to in writing, software distributed under the License is distributed on an
        "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
        implied. See the License for the specific language governing permissions and
        limitations under the License.
    -->
    <!--
        An embedded servlet engine for serving up the Admin consoles, REST and Ajax APIs and
        some demos Include this file in your configuration to enable ActiveMQ web components
        e.g. <import resource="jetty.xml"/>
    -->
<beans xmlns="http://www.springframework.org/schema/beans" 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.xsd">

    <bean id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
        <property name="sendServerVersion" value="false"/>
        <property name="sendXPoweredBy" value="false"/>
        <property name="sendDateHeader" value="false"/>
        <!--
            When the web console sits behind a reverse proxy or load balancer,
            uncomment the customizer below so Jetty honors X-Forwarded-* (or
            RFC 7239 Forwarded) headers when populating request.getRemoteAddr(),
            request.getScheme(), etc. This is what gets the real client IP into
            access logs and audit trails.

            WARNING: ForwardedRequestCustomizer trusts these headers from any
            client. Only enable it when the upstream proxy strips inbound
            X-Forwarded-* headers, otherwise a remote client can spoof its
            source IP and apparent scheme.

            Note: this does NOT change InetAccessHandler's filtering — that
            handler always evaluates the socket peer (the proxy itself) and
            ignores forwarded headers. In a proxied deployment either keep the
            allow list scoped to the proxy address and rely on the proxy for
            client-IP filtering, or remove InetAccessHandler entirely.
        -->
        <!--
        <property name="customizers">
            <list>
                <bean class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/>
            </list>
        </property>
        -->
    </bean>

    <bean id="jaasLoginService" class="org.eclipse.jetty.jaas.JAASLoginService">
        <property name="name" value="ActiveMQRealm"/>
        <property name="loginModuleName" value="activemq"/>
        <property name="roleClassNames">
            <list>
                <value>org.apache.activemq.jaas.GroupPrincipal</value>
            </list>
        </property>
    </bean>

    <bean id="identityService" class="org.eclipse.jetty.security.DefaultIdentityService"/>

    <bean id="securityConstraint" class="org.eclipse.jetty.util.security.Constraint">
        <property name="name" value="BASIC" />
        <property name="roles" value="users,admins" />
        <!-- set authenticate=false to disable login -->
        <property name="authenticate" value="true" />
    </bean>
    <bean id="adminSecurityConstraint" class="org.eclipse.jetty.util.security.Constraint">
        <property name="name" value="BASIC" />
        <property name="roles" value="admins" />
         <!-- set authenticate=false to disable login -->
        <property name="authenticate" value="true" />
    </bean>
    <!--
        Catch-all mapping: any request not matched by a more specific
        constraint below requires authentication as a user or admin.
    -->
    <bean id="securityConstraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
        <property name="constraint" ref="securityConstraint" />
        <property name="pathSpec" value="/" />
    </bean>
    <!--
        Web console (/admin/*) is restricted to the admins role. This covers
        the full console UI, not just the *.action endpoints, so read-only
        pages (queue listings, message browsing, etc.) also require admin.
    -->
    <bean id="adminSecurityConstraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
        <property name="constraint" ref="adminSecurityConstraint" />
        <property name="pathSpec" value="/admin/*" />
    </bean>
    <!--
        Jolokia JMX bridge exposes broker management operations over HTTP;
        restrict to the admins role to prevent privilege escalation via JMX.
    -->
    <bean id="jolokiaSecurityConstraintMapping" class="org.eclipse.jetty.security.ConstraintMapping">
        <property name="constraint" ref="adminSecurityConstraint" />
        <property name="pathSpec" value="/api/jolokia/*" />
    </bean>
    
    <bean id="rewriteHandler" class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
        <property name="rules">
            <list>
                <bean id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
                  <property name="pattern" value="*"/>
                  <property name="name" value="X-FRAME-OPTIONS"/>
                  <property name="value" value="SAMEORIGIN"/>
                </bean>
                <bean id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
                  <property name="pattern" value="*"/>
                  <property name="name" value="X-XSS-Protection"/>
                  <property name="value" value="1; mode=block"/>
                </bean>
                <bean id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
                  <property name="pattern" value="*"/>
                  <property name="name" value="X-Content-Type-Options"/>
                  <property name="value" value="nosniff"/>
                </bean>
                <bean id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
                    <property name="pattern" value="*"/>
                    <property name="name" value="Cache-Control"/>
                    <property name="value" value="no-store"/>
                </bean>
                <bean id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
                    <property name="pattern" value="*"/>
                    <property name="name" value="Content-Security-Policy"/>
                    <property name="value" value="style-src-elem 'self'; style-src 'self'; img-src 'self'; script-src-elem 'self'; default-src 'none'; object-src 'none'; frame-ancestors 'none'; base-uri 'none';" />
                </bean>
                <bean id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
                    <property name="pattern" value="*"/>
                    <property name="name" value="Referrer-Policy"/>
                    <property name="value" value="no-referrer"/>
                </bean>
                <bean id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
                    <property name="pattern" value="*"/>
                    <property name="name" value="Permissions-Policy"/>
                    <property name="value" value="accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"/>
                </bean>
                <bean id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
                    <property name="pattern" value="*"/>
                    <property name="name" value="Cross-Origin-Opener-Policy"/>
                    <property name="value" value="same-origin"/>
                </bean>
                <bean id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
                    <property name="pattern" value="*"/>
                    <property name="name" value="Cross-Origin-Resource-Policy"/>
                    <property name="value" value="same-origin"/>
                </bean>
                <!-- More relaxed rules to allow browsers to properly render XML -->
                <bean id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
                    <property name="pattern" value="/admin/xml/*"/>
                    <property name="name" value="Content-Security-Policy"/>
                    <property name="value" value="style-src-elem 'self' 'unsafe-inline'; style-src 'self'; img-src 'self' data:; script-src-elem 'self'; default-src 'none'; object-src 'none'; frame-ancestors 'none'; base-uri 'none';" />
                </bean>
                <!-- Uncomment when serving the console over HTTPS only -->
                <!--
                <bean id="header" class="org.eclipse.jetty.rewrite.handler.HeaderPatternRule">
                    <property name="pattern" value="*"/>
                    <property name="name" value="Strict-Transport-Security"/>
                    <property name="value" value="max-age=31536000; includeSubDomains"/>
                </bean>
                -->
            </list>
        </property>
    </bean>
    
    <bean id="secHandlerCollection" class="org.eclipse.jetty.server.handler.HandlerCollection">
        <property name="handlers">
            <list>
                <ref bean="rewriteHandler"/>
                <bean class="org.eclipse.jetty.webapp.WebAppContext">
                    <property name="contextPath" value="/admin" />
                    <property name="resourceBase" value="${activemq.home}/webapps/admin" />
                    <property name="logUrlOnStart" value="true" />
                </bean>
                <bean class="org.eclipse.jetty.webapp.WebAppContext">
                    <property name="contextPath" value="/api" />
                    <property name="resourceBase" value="${activemq.home}/webapps/api" />
                    <property name="logUrlOnStart" value="true" />
                </bean>
                <bean class="org.eclipse.jetty.server.handler.ResourceHandler">
                    <property name="directoriesListed" value="false" />
                    <property name="welcomeFiles">
                        <list>
                            <value>index.html</value>
                        </list>
                    </property>
                    <property name="resourceBase" value="${activemq.home}/webapps/" />
                </bean>
                <bean id="defaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler">
                    <property name="serveIcon" value="false" />
                </bean>
            </list>
        </property>
    </bean>    
    <bean id="securityHandler" class="org.eclipse.jetty.security.ConstraintSecurityHandler">
        <property name="identityService" ref="identityService" />
        <property name="loginService" ref="jaasLoginService" />
        <property name="authenticator">
            <bean class="org.eclipse.jetty.security.authentication.BasicAuthenticator" />
        </property>
        <property name="constraintMappings">
            <list>
                <ref bean="adminSecurityConstraintMapping" />
                <ref bean="jolokiaSecurityConstraintMapping" />
                <ref bean="securityConstraintMapping" />
            </list>
        </property>
        <property name="handler" ref="secHandlerCollection" />
    </bean>

    <bean id="contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection">
    </bean>

    <bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
             <!-- the default port number for the web console -->
        <property name="host" value="127.0.0.1"/>
        <property name="port" value="8161"/>
    </bean>

    <bean id="handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
        <property name="handlers">
            <list>
                <ref bean="contexts" />
                <ref bean="securityHandler" />
            </list>
        </property>
    </bean>

    <!--
        IP-based access control for the web console.
        Patterns accept: exact IP (192.168.1.1), CIDR (10.0.0.0/8),
        range (10.0.0.0-10.255.255.255), or qualified with path/connector
        using '|' (e.g. 192.168.1.0/24|/api).
        Exclude rules take precedence over include rules.
        With the default host binding of 127.0.0.1, only localhost is reachable;
        if the bind address is broadened (e.g. 0.0.0.0), add explicit include
        entries below for the networks that should reach the console.
    -->
    <bean id="inetAccessHandler" class="org.eclipse.jetty.server.handler.InetAccessHandler">
        <property name="handler" ref="handlers"/>
    </bean>

    <!-- Allowed clients (include rules) -->
    <bean id="inetAccessIncludeLoopbackV4" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject" ref="inetAccessHandler"/>
        <property name="targetMethod" value="include"/>
        <property name="arguments">
            <list><value>127.0.0.1</value></list>
        </property>
    </bean>
    <bean id="inetAccessIncludeLoopbackV6" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
          depends-on="inetAccessIncludeLoopbackV4">
        <property name="targetObject" ref="inetAccessHandler"/>
        <property name="targetMethod" value="include"/>
        <property name="arguments">
            <list><value>::1</value></list>
        </property>
    </bean>
    <!--
        Example: allow standard private network ranges. Copy a block and
        chain depends-on to enforce ordering on container startup.

    <bean id="inetAccessIncludeRfc1918Class10" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
          depends-on="inetAccessIncludeLoopbackV6">
        <property name="targetObject" ref="inetAccessHandler"/>
        <property name="targetMethod" value="include"/>
        <property name="arguments">
            <list><value>10.0.0.0/8</value></list>
        </property>
    </bean>
    <bean id="inetAccessIncludeRfc1918Class172" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
          depends-on="inetAccessIncludeRfc1918Class10">
        <property name="targetObject" ref="inetAccessHandler"/>
        <property name="targetMethod" value="include"/>
        <property name="arguments">
            <list><value>172.16.0.0/12</value></list>
        </property>
    </bean>
    <bean id="inetAccessIncludeRfc1918Class192" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
          depends-on="inetAccessIncludeRfc1918Class172">
        <property name="targetObject" ref="inetAccessHandler"/>
        <property name="targetMethod" value="include"/>
        <property name="arguments">
            <list><value>192.168.0.0/16</value></list>
        </property>
    </bean>
    -->

    <!--
        Denied clients (exclude rules). Exclude rules take precedence over
        include rules. Empty by default; add entries below to block specific
        addresses or networks.

    <bean id="inetAccessExcludeExample" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
          depends-on="inetAccessIncludeLoopbackV6">
        <property name="targetObject" ref="inetAccessHandler"/>
        <property name="targetMethod" value="exclude"/>
        <property name="arguments">
            <list><value>1.2.3.4</value></list>
        </property>
    </bean>
    -->

    <bean id="Server" depends-on="jettyPort" class="org.eclipse.jetty.server.Server"
        destroy-method="stop">

        <property name="handler" ref="inetAccessHandler"/>

    </bean>

    <bean id="invokeConnectors" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject" ref="Server" />
        <property name="targetMethod" value="setConnectors" />
        <property name="arguments">
        <list>
               <bean id="Connector" class="org.eclipse.jetty.server.ServerConnector">
                   <constructor-arg ref="Server"/>
                   <constructor-arg>
                       <list>
                           <bean id="httpConnectionFactory" class="org.eclipse.jetty.server.HttpConnectionFactory">
                               <constructor-arg ref="httpConfig"/>
                           </bean>
                       </list>
                   </constructor-arg>
                   <!-- see the jettyPort bean -->
                   <property name="host" value="#{systemProperties['jetty.host']}" />
                   <property name="port" value="#{systemProperties['jetty.port']}" />
               </bean>
                <!--
                    Enable this connector if you wish to use https with web console
                -->
                <!-- bean id="SecureConnector" class="org.eclipse.jetty.server.ServerConnector">
                    <constructor-arg ref="Server" />
                    <constructor-arg>
                        <bean id="handlers" class="org.eclipse.jetty.util.ssl.SslContextFactory$Server">
                            <property name="keyStorePath" value="${activemq.conf}/broker.ks" />
                            <property name="keyStorePassword" value="password" />
                        </bean>
                    </constructor-arg>
                    <property name="port" value="8162" />
                </bean -->
            </list>
        </property>
    </bean>

    <bean id="configureJetty" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="org.apache.activemq.web.config.JspConfigurer.configureJetty" />
        <property name="arguments">
            <list>
                <ref bean="Server" />
                <ref bean="secHandlerCollection" />
            </list>
        </property>
    </bean>
    
    <bean id="invokeStart" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"
        depends-on="broker, configureJetty, invokeConnectors, inetAccessIncludeLoopbackV6">
        <property name="targetObject" ref="Server" />
        <property name="targetMethod" value="start" />
    </bean>
    
    
</beans>

