blob: 760f6ab9e0e200b8abafe984a7110851eb780880 [file] [log] [blame]
/**
* 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.
*/
package org.apache.camel.component.shiro.security;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.camel.AsyncCallback;
import org.apache.camel.AsyncProcessor;
import org.apache.camel.CamelAuthorizationException;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.model.ProcessorDefinition;
import org.apache.camel.spi.AuthorizationPolicy;
import org.apache.camel.spi.RouteContext;
import org.apache.camel.util.AsyncProcessorConverterHelper;
import org.apache.camel.util.AsyncProcessorHelper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.config.Ini;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.crypto.CipherService;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ShiroSecurityPolicy implements AuthorizationPolicy {
private static final transient Logger LOG = LoggerFactory.getLogger(ShiroSecurityPolicy.class);
private final byte[] bits128 = {
(byte) 0x08, (byte) 0x09, (byte) 0x0A, (byte) 0x0B,
(byte) 0x0C, (byte) 0x0D, (byte) 0x0E, (byte) 0x0F,
(byte) 0x10, (byte) 0x11, (byte) 0x12, (byte) 0x13,
(byte) 0x14, (byte) 0x15, (byte) 0x16, (byte) 0x17};
private CipherService cipherService;
private byte[] passPhrase;
private SecurityManager securityManager;
private List<Permission> permissionsList;
private boolean alwaysReauthenticate;
public ShiroSecurityPolicy() {
this.passPhrase = bits128;
// Set up AES encryption based cipher service, by default
cipherService = new AesCipherService();
permissionsList = new ArrayList<Permission>();
alwaysReauthenticate = true;
}
public ShiroSecurityPolicy(String iniResourcePath) {
this();
Factory<SecurityManager> factory = new IniSecurityManagerFactory(iniResourcePath);
securityManager = (SecurityManager) factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
}
public ShiroSecurityPolicy(Ini ini) {
this();
Factory<SecurityManager> factory = new IniSecurityManagerFactory(ini);
securityManager = (SecurityManager) factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
}
public ShiroSecurityPolicy(String iniResourcePath, byte[] passPhrase) {
this(iniResourcePath);
this.setPassPhrase(passPhrase);
}
public ShiroSecurityPolicy(Ini ini, byte[] passPhrase) {
this(ini);
this.setPassPhrase(passPhrase);
}
public ShiroSecurityPolicy(String iniResourcePath, byte[] passPhrase, boolean alwaysReauthenticate) {
this(iniResourcePath, passPhrase);
this.setAlwaysReauthenticate(alwaysReauthenticate);
}
public ShiroSecurityPolicy(Ini ini, byte[] passPhrase, boolean alwaysReauthenticate) {
this(ini, passPhrase);
this.setAlwaysReauthenticate(alwaysReauthenticate);
}
public ShiroSecurityPolicy(String iniResourcePath, byte[] passPhrase, boolean alwaysReauthenticate, List<Permission> permissionsList) {
this(iniResourcePath, passPhrase, alwaysReauthenticate);
this.setPermissionsList(permissionsList);
}
public ShiroSecurityPolicy(Ini ini, byte[] passPhrase, boolean alwaysReauthenticate, List<Permission> permissionsList) {
this(ini, passPhrase, alwaysReauthenticate);
this.setPermissionsList(permissionsList);
}
public void beforeWrap(RouteContext routeContext, ProcessorDefinition<?> definition) {
//Not implemented
}
public Processor wrap(RouteContext routeContext, final Processor processor) {
return new AsyncProcessor() {
public boolean process(Exchange exchange, final AsyncCallback callback) {
boolean sync;
try {
applySecurityPolicy(exchange);
} catch (Exception e) {
// exception occurred so break out
exchange.setException(e);
callback.done(true);
return true;
}
// If here, then user is authenticated and authorized
// Now let the original processor continue routing supporting the async routing engine
AsyncProcessor ap = AsyncProcessorConverterHelper.convert(processor);
sync = AsyncProcessorHelper.process(ap, exchange, new AsyncCallback() {
public void done(boolean doneSync) {
// we only have to handle async completion of this policy
if (doneSync) {
return;
}
callback.done(false);
}
});
if (!sync) {
// if async, continue routing async
return false;
}
// we are done synchronously, so do our after work and invoke the callback
callback.done(true);
return true;
}
public void process(Exchange exchange) throws Exception {
applySecurityPolicy(exchange);
processor.process(exchange);
}
private void applySecurityPolicy(Exchange exchange) throws Exception {
ByteSource encryptedToken = (ByteSource)exchange.getIn().getHeader("SHIRO_SECURITY_TOKEN");
ByteSource decryptedToken = getCipherService().decrypt(encryptedToken.getBytes(), getPassPhrase());
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(decryptedToken.getBytes());
ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
ShiroSecurityToken securityToken = (ShiroSecurityToken)objectInputStream.readObject();
objectInputStream.close();
byteArrayInputStream.close();
Subject currentUser = SecurityUtils.getSubject();
// Authenticate user if not authenticated
try {
authenticateUser(currentUser, securityToken);
// Test whether user's role is authorized to perform functions in the permissions list
authorizeUser(currentUser, exchange);
} finally {
if (alwaysReauthenticate) {
currentUser.logout();
currentUser = null;
}
}
}
};
}
private void authenticateUser(Subject currentUser, ShiroSecurityToken securityToken) {
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken(securityToken.getUsername(), securityToken.getPassword());
if (alwaysReauthenticate) {
token.setRememberMe(false);
} else {
token.setRememberMe(true);
}
try {
currentUser.login(token);
LOG.debug("Current User {} successfully authenticated", currentUser.getPrincipal());
} catch (UnknownAccountException uae) {
throw new UnknownAccountException("Authentication Failed. There is no user with username of " + token.getPrincipal(), uae.getCause());
} catch (IncorrectCredentialsException ice) {
throw new IncorrectCredentialsException("Authentication Failed. Password for account " + token.getPrincipal() + " was incorrect!", ice.getCause());
} catch (LockedAccountException lae) {
throw new LockedAccountException("Authentication Failed. The account for username " + token.getPrincipal() + " is locked."
+ "Please contact your administrator to unlock it.", lae.getCause());
} catch (AuthenticationException ae) {
throw new AuthenticationException("Authentication Failed.", ae.getCause());
}
}
}
private void authorizeUser(Subject currentUser, Exchange exchange) throws CamelAuthorizationException {
boolean authorized = false;
if (!permissionsList.isEmpty()) {
for (Permission permission : permissionsList) {
if (currentUser.isPermitted(permission)) {
authorized = true;
break;
}
}
} else {
LOG.debug("Valid Permissions List not specified for ShiroSecurityPolicy. No authorization checks will be performed for current user");
authorized = true;
}
if (!authorized) {
throw new CamelAuthorizationException("Authorization Failed. Subject's role set does not have the necessary permissions to perform further processing", exchange);
}
LOG.debug("Current User {} is successfully authorized. The exchange will be allowed to proceed", currentUser.getPrincipal());
}
public CipherService getCipherService() {
return cipherService;
}
public void setCipherService(CipherService cipherService) {
this.cipherService = cipherService;
}
public SecurityManager getSecurityManager() {
return securityManager;
}
public void setSecurityManager(SecurityManager securityManager) {
this.securityManager = securityManager;
}
public byte[] getPassPhrase() {
return passPhrase;
}
public void setPassPhrase(byte[] passPhrase) {
this.passPhrase = passPhrase;
}
public List<Permission> getPermissionsList() {
return permissionsList;
}
public void setPermissionsList(List<Permission> permissionsList) {
this.permissionsList = permissionsList;
}
public boolean isAlwaysReauthenticate() {
return alwaysReauthenticate;
}
public void setAlwaysReauthenticate(boolean alwaysReauthenticate) {
this.alwaysReauthenticate = alwaysReauthenticate;
}
}