001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.shiro;
018
019import org.apache.activemq.ConfigurationException;
020import org.apache.activemq.broker.Broker;
021import org.apache.activemq.broker.BrokerPluginSupport;
022import org.apache.activemq.shiro.authc.AuthenticationFilter;
023import org.apache.activemq.shiro.authc.AuthenticationPolicy;
024import org.apache.activemq.shiro.authc.DefaultAuthenticationPolicy;
025import org.apache.activemq.shiro.authz.AuthorizationFilter;
026import org.apache.activemq.shiro.env.IniEnvironment;
027import org.apache.activemq.shiro.subject.ConnectionSubjectFactory;
028import org.apache.activemq.shiro.subject.DefaultConnectionSubjectFactory;
029import org.apache.activemq.shiro.subject.SubjectFilter;
030import org.apache.shiro.config.Ini;
031import org.apache.shiro.env.Environment;
032import org.apache.shiro.mgt.SecurityManager;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036/**
037 * @since 5.10.0
038 */
039public class ShiroPlugin extends BrokerPluginSupport {
040
041    private static final Logger LOG = LoggerFactory.getLogger(ShiroPlugin.class);
042
043    private volatile boolean enabled = true;
044
045    private Broker broker; //the downstream broker after any/all Shiro-specific broker filters
046
047    private SecurityManager securityManager;
048    private Environment environment;
049    private IniEnvironment iniEnvironment; //only used if the above environment instance is not explicitly configured
050
051    private SubjectFilter subjectFilter;
052
053    private AuthenticationFilter authenticationFilter;
054
055    private AuthorizationFilter authorizationFilter;
056
057    public ShiroPlugin() {
058
059        //Default if this.environment is not configured. See the ensureEnvironment() method below.
060        iniEnvironment = new IniEnvironment();
061
062        authorizationFilter = new AuthorizationFilter();
063
064        // we want to share one AuthenticationPolicy instance across both the AuthenticationFilter and the
065        // ConnectionSubjectFactory:
066        AuthenticationPolicy authcPolicy = new DefaultAuthenticationPolicy();
067
068        authenticationFilter = new AuthenticationFilter();
069        authenticationFilter.setAuthenticationPolicy(authcPolicy);
070        authenticationFilter.setNext(authorizationFilter);
071
072        subjectFilter = new SubjectFilter();
073        DefaultConnectionSubjectFactory subjectFactory = new DefaultConnectionSubjectFactory();
074        subjectFactory.setAuthenticationPolicy(authcPolicy);
075        subjectFilter.setConnectionSubjectFactory(subjectFactory);
076        subjectFilter.setNext(authenticationFilter);
077    }
078
079    public SubjectFilter getSubjectFilter() {
080        return subjectFilter;
081    }
082
083    public void setSubjectFilter(SubjectFilter subjectFilter) {
084        this.subjectFilter = subjectFilter;
085        this.subjectFilter.setNext(this.authenticationFilter);
086    }
087
088    public AuthenticationFilter getAuthenticationFilter() {
089        return authenticationFilter;
090    }
091
092    public void setAuthenticationFilter(AuthenticationFilter authenticationFilter) {
093        this.authenticationFilter = authenticationFilter;
094        this.authenticationFilter.setNext(this.authorizationFilter);
095        this.subjectFilter.setNext(authenticationFilter);
096    }
097
098    public AuthorizationFilter getAuthorizationFilter() {
099        return authorizationFilter;
100    }
101
102    public void setAuthorizationFilter(AuthorizationFilter authorizationFilter) {
103        this.authorizationFilter = authorizationFilter;
104        this.authorizationFilter.setNext(this.broker);
105        this.authenticationFilter.setNext(authorizationFilter);
106    }
107
108    public void setEnabled(boolean enabled) {
109        this.enabled = enabled;
110        if (isInstalled()) {
111            //we're running, so apply the changes now:
112            applyEnabled(enabled);
113        }
114    }
115
116    public boolean isEnabled() {
117        if (isInstalled()) {
118            return getNext() == this.subjectFilter;
119        }
120        return enabled;
121    }
122
123    private void applyEnabled(boolean enabled) {
124        if (enabled) {
125            //ensure the SubjectFilter and downstream filters are used:
126            super.setNext(this.subjectFilter);
127        } else {
128            //Shiro is not enabled, restore the original downstream broker:
129            super.setNext(this.broker);
130        }
131    }
132
133    public Environment getEnvironment() {
134        return environment;
135    }
136
137    public void setEnvironment(Environment environment) {
138        this.environment = environment;
139    }
140
141    public SecurityManager getSecurityManager() {
142        return securityManager;
143    }
144
145    public void setSecurityManager(SecurityManager securityManager) {
146        this.securityManager = securityManager;
147    }
148
149    public void setIni(Ini ini) {
150        this.iniEnvironment.setIni(ini);
151    }
152
153    public void setIniConfig(String iniConfig) {
154        this.iniEnvironment.setIniConfig(iniConfig);
155    }
156
157    public void setIniResourcePath(String resourcePath) {
158        this.iniEnvironment.setIniResourcePath(resourcePath);
159    }
160
161    // ===============================================================
162    // Authentication Configuration
163    // ===============================================================
164    public void setAuthenticationEnabled(boolean authenticationEnabled) {
165        this.authenticationFilter.setEnabled(authenticationEnabled);
166    }
167
168    public boolean isAuthenticationEnabled() {
169        return this.authenticationFilter.isEnabled();
170    }
171
172    public AuthenticationPolicy getAuthenticationPolicy() {
173        return authenticationFilter.getAuthenticationPolicy();
174    }
175
176    public void setAuthenticationPolicy(AuthenticationPolicy authenticationPolicy) {
177        authenticationFilter.setAuthenticationPolicy(authenticationPolicy);
178        //also set it on the ConnectionSubjectFactory:
179        ConnectionSubjectFactory factory = subjectFilter.getConnectionSubjectFactory();
180        if (factory instanceof DefaultConnectionSubjectFactory) {
181            ((DefaultConnectionSubjectFactory) factory).setAuthenticationPolicy(authenticationPolicy);
182        }
183    }
184
185    // ===============================================================
186    // Authorization Configuration
187    // ===============================================================
188    public void setAuthorizationEnabled(boolean authorizationEnabled) {
189        this.authorizationFilter.setEnabled(authorizationEnabled);
190    }
191
192    public boolean isAuthorizationEnabled() {
193        return this.authorizationFilter.isEnabled();
194    }
195
196    private Environment ensureEnvironment() throws ConfigurationException {
197        if (this.environment != null) {
198            return this.environment;
199        }
200
201        //this.environment is null - set it:
202        if (this.securityManager != null) {
203            this.environment = new Environment() {
204                @Override
205                public SecurityManager getSecurityManager() {
206                    return ShiroPlugin.this.securityManager;
207                }
208            };
209            return this.environment;
210        }
211
212        this.iniEnvironment.init(); //will automatically catch any config errors and throw.
213
214        this.environment = iniEnvironment;
215
216        return this.iniEnvironment;
217    }
218
219    @Override
220    public Broker installPlugin(Broker broker) throws Exception {
221
222        Environment environment = ensureEnvironment();
223
224        this.authorizationFilter.setEnvironment(environment);
225        this.authenticationFilter.setEnvironment(environment);
226        this.subjectFilter.setEnvironment(environment);
227
228        this.broker = broker;
229        this.authorizationFilter.setNext(broker);
230        this.authenticationFilter.setNext(this.authorizationFilter);
231        this.subjectFilter.setNext(this.authenticationFilter);
232
233        Broker next = this.subjectFilter;
234        if (!this.enabled) {
235            //not enabled at startup - default to the original broker:
236            next = broker;
237        }
238
239        setNext(next);
240        return this;
241    }
242
243    private boolean isInstalled() {
244        return getNext() != null;
245    }
246}