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 */
017 package org.apache.camel.component.shiro.security;
018
019 import java.io.ByteArrayInputStream;
020 import java.io.ObjectInputStream;
021 import java.util.ArrayList;
022 import java.util.List;
023
024 import org.apache.camel.AsyncCallback;
025 import org.apache.camel.AsyncProcessor;
026 import org.apache.camel.CamelAuthorizationException;
027 import org.apache.camel.Exchange;
028 import org.apache.camel.Processor;
029 import org.apache.camel.impl.converter.AsyncProcessorTypeConverter;
030 import org.apache.camel.model.ProcessorDefinition;
031 import org.apache.camel.spi.AuthorizationPolicy;
032 import org.apache.camel.spi.RouteContext;
033 import org.apache.camel.util.AsyncProcessorHelper;
034 import org.apache.commons.logging.Log;
035 import org.apache.commons.logging.LogFactory;
036 import org.apache.shiro.SecurityUtils;
037 import org.apache.shiro.authc.AuthenticationException;
038 import org.apache.shiro.authc.IncorrectCredentialsException;
039 import org.apache.shiro.authc.LockedAccountException;
040 import org.apache.shiro.authc.UnknownAccountException;
041 import org.apache.shiro.authc.UsernamePasswordToken;
042 import org.apache.shiro.authz.Permission;
043 import org.apache.shiro.config.Ini;
044 import org.apache.shiro.config.IniSecurityManagerFactory;
045 import org.apache.shiro.crypto.AesCipherService;
046 import org.apache.shiro.crypto.CipherService;
047 import org.apache.shiro.mgt.SecurityManager;
048 import org.apache.shiro.subject.Subject;
049 import org.apache.shiro.util.ByteSource;
050 import org.apache.shiro.util.Factory;
051
052 public class ShiroSecurityPolicy implements AuthorizationPolicy {
053 private static final transient Log LOG = LogFactory.getLog(ShiroSecurityPolicy.class);
054 private final byte[] bits128 = {
055 (byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B,
056 (byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F,
057 (byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13,
058 (byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17};
059 private CipherService cipherService;
060 private byte[] passPhrase;
061 private SecurityManager securityManager;
062 private List<Permission> permissionsList;
063 private boolean alwaysReauthenticate;
064
065 public ShiroSecurityPolicy() {
066 this.passPhrase = bits128;
067 // Set up AES encryption based cipher service, by default
068 cipherService = new AesCipherService();
069 permissionsList = new ArrayList<Permission>();
070 alwaysReauthenticate = true;
071 }
072
073 public ShiroSecurityPolicy(String iniResourcePath) {
074 this();
075 Factory<SecurityManager> factory = new IniSecurityManagerFactory(iniResourcePath);
076 securityManager = (SecurityManager) factory.getInstance();
077 SecurityUtils.setSecurityManager(securityManager);
078 }
079
080 public ShiroSecurityPolicy(Ini ini) {
081 this();
082 Factory<SecurityManager> factory = new IniSecurityManagerFactory(ini);
083 securityManager = (SecurityManager) factory.getInstance();
084 SecurityUtils.setSecurityManager(securityManager);
085 }
086
087 public ShiroSecurityPolicy(String iniResourcePath, byte[] passPhrase) {
088 this(iniResourcePath);
089 this.setPassPhrase(passPhrase);
090 }
091
092 public ShiroSecurityPolicy(Ini ini, byte[] passPhrase) {
093 this(ini);
094 this.setPassPhrase(passPhrase);
095 }
096
097 public ShiroSecurityPolicy(String iniResourcePath, byte[] passPhrase, boolean alwaysReauthenticate) {
098 this(iniResourcePath, passPhrase);
099 this.setAlwaysReauthenticate(alwaysReauthenticate);
100 }
101
102 public ShiroSecurityPolicy(Ini ini, byte[] passPhrase, boolean alwaysReauthenticate) {
103 this(ini, passPhrase);
104 this.setAlwaysReauthenticate(alwaysReauthenticate);
105 }
106
107 public ShiroSecurityPolicy(String iniResourcePath, byte[] passPhrase, boolean alwaysReauthenticate, List<Permission> permissionsList) {
108 this(iniResourcePath, passPhrase, alwaysReauthenticate);
109 this.setPermissionsList(permissionsList);
110 }
111
112 public ShiroSecurityPolicy(Ini ini, byte[] passPhrase, boolean alwaysReauthenticate, List<Permission> permissionsList) {
113 this(ini, passPhrase, alwaysReauthenticate);
114 this.setPermissionsList(permissionsList);
115 }
116
117 public void beforeWrap(RouteContext routeContext, ProcessorDefinition<?> definition) {
118 //Not implemented
119 }
120
121 public Processor wrap(RouteContext routeContext, final Processor processor) {
122 return new AsyncProcessor() {
123 public boolean process(Exchange exchange, final AsyncCallback callback) {
124 boolean sync;
125 try {
126 applySecurityPolicy(exchange);
127 } catch (Exception e) {
128 // exception occurred so break out
129 exchange.setException(e);
130 callback.done(true);
131 return true;
132 }
133
134 // If here, then user is authenticated and authorized
135 // Now let the original processor continue routing supporting the async routing engine
136 AsyncProcessor ap = AsyncProcessorTypeConverter.convert(processor);
137 sync = AsyncProcessorHelper.process(ap, exchange, new AsyncCallback() {
138 public void done(boolean doneSync) {
139 // we only have to handle async completion of this policy
140 if (doneSync) {
141 return;
142 }
143 callback.done(false);
144 }
145 });
146
147 if (!sync) {
148 // if async, continue routing async
149 return false;
150 }
151
152 // we are done synchronously, so do our after work and invoke the callback
153 callback.done(true);
154 return true;
155 }
156
157 public void process(Exchange exchange) throws Exception {
158 applySecurityPolicy(exchange);
159 processor.process(exchange);
160 }
161
162 private void applySecurityPolicy(Exchange exchange) throws Exception {
163 ByteSource encryptedToken = (ByteSource)exchange.getIn().getHeader("SHIRO_SECURITY_TOKEN");
164 ByteSource decryptedToken = getCipherService().decrypt(encryptedToken.getBytes(), getPassPhrase());
165
166 ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decryptedToken.getBytes());
167 ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
168 ShiroSecurityToken securityToken = (ShiroSecurityToken)objectInputStream.readObject();
169 objectInputStream.close();
170 byteArrayInputStream.close();
171
172 Subject currentUser = SecurityUtils.getSubject();
173
174 // Authenticate user if not authenticated
175 try {
176 authenticateUser(currentUser, securityToken);
177
178 // Test whether user's role is authorized to perform functions in the permissions list
179 authorizeUser(currentUser, exchange);
180 } finally {
181 if (alwaysReauthenticate) {
182 currentUser.logout();
183 currentUser = null;
184 }
185 }
186
187 }
188 };
189 }
190
191 private void authenticateUser(Subject currentUser, ShiroSecurityToken securityToken) {
192 if (!currentUser.isAuthenticated()) {
193 UsernamePasswordToken token = new UsernamePasswordToken(securityToken.getUsername(), securityToken.getPassword());
194 if (alwaysReauthenticate) {
195 token.setRememberMe(false);
196 } else {
197 token.setRememberMe(true);
198 }
199
200 try {
201 currentUser.login(token);
202 if (LOG.isDebugEnabled()) {
203 LOG.debug("Current User " + currentUser.getPrincipal() + " successfully authenticated");
204 }
205 } catch (UnknownAccountException uae) {
206 throw new UnknownAccountException("Authentication Failed. There is no user with username of " + token.getPrincipal(), uae.getCause());
207 } catch (IncorrectCredentialsException ice) {
208 throw new IncorrectCredentialsException("Authentication Failed. Password for account " + token.getPrincipal() + " was incorrect!", ice.getCause());
209 } catch (LockedAccountException lae) {
210 throw new LockedAccountException("Authentication Failed. The account for username " + token.getPrincipal() + " is locked."
211 + "Please contact your administrator to unlock it.", lae.getCause());
212 } catch (AuthenticationException ae) {
213 throw new AuthenticationException("Authentication Failed.", ae.getCause());
214 }
215 }
216 }
217
218 private void authorizeUser(Subject currentUser, Exchange exchange) throws CamelAuthorizationException {
219 boolean authorized = false;
220 if (!permissionsList.isEmpty()) {
221 for (Permission permission : permissionsList) {
222 if (currentUser.isPermitted(permission)) {
223 authorized = true;
224 break;
225 }
226 }
227 } else {
228 if (LOG.isDebugEnabled()) {
229 LOG.debug("Valid Permissions List not specified for ShiroSecurityPolicy. No authorization checks will be performed for current user");
230 }
231 authorized = true;
232 }
233
234 if (!authorized) {
235 throw new CamelAuthorizationException("Authorization Failed. Subject's role set does not have the necessary permissions to perform further processing", exchange);
236 }
237
238 if (LOG.isDebugEnabled()) {
239 LOG.debug("Current User " + currentUser.getPrincipal() + " is successfully authorized. The exchange will be allowed to proceed");
240 }
241 }
242
243 public CipherService getCipherService() {
244 return cipherService;
245 }
246
247 public void setCipherService(CipherService cipherService) {
248 this.cipherService = cipherService;
249 }
250
251 public SecurityManager getSecurityManager() {
252 return securityManager;
253 }
254
255 public void setSecurityManager(SecurityManager securityManager) {
256 this.securityManager = securityManager;
257 }
258
259 public byte[] getPassPhrase() {
260 return passPhrase;
261 }
262
263 public void setPassPhrase(byte[] passPhrase) {
264 this.passPhrase = passPhrase;
265 }
266
267 public List<Permission> getPermissionsList() {
268 return permissionsList;
269 }
270
271 public void setPermissionsList(List<Permission> permissionsList) {
272 this.permissionsList = permissionsList;
273 }
274
275 public boolean isAlwaysReauthenticate() {
276 return alwaysReauthenticate;
277 }
278
279 public void setAlwaysReauthenticate(boolean alwaysReauthenticate) {
280 this.alwaysReauthenticate = alwaysReauthenticate;
281 }
282
283 }