Add SecurityProvider implementation basd on Apache Shiro

master
David BRASSELY 2014-02-28 23:49:35 +01:00
parent 78b1b36e71
commit 3bd6c597fd
16 changed files with 373 additions and 44 deletions

View File

@ -47,7 +47,9 @@ import javax.management.ObjectName;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import net.openesb.standalone.security.auth.login.CustomJMXAuthenticator;
import javax.naming.Context;
import net.openesb.standalone.security.SecurityProviderImpl;
import net.openesb.standalone.security.auth.login.JMXauthenticator;
//import net.openesb.standalone.node.Node;
//import net.openesb.standalone.node.NodeBuilder;
import net.openesb.standalone.settings.ImmutableSettings;
@ -114,16 +116,19 @@ public class JSEJBIFramework
mLog.log(Level.FINE, "Trying to load configuration from {0}", configFile);
Map configurations = null;
try {
Yaml yaml = new Yaml(new Constructor(), new Representer(), new DumperOptions(),
new Resolver() {
@Override
protected void addImplicitResolvers() {
//super.addImplicitResolvers(); //To change body of generated methods, choose Tools | Templates.
}
});
InputStream input = new FileInputStream(new File(configFile));
settings = new ImmutableSettings((Map) yaml.load(input));
configurations = (Map) yaml.load(input);
settings = new ImmutableSettings(configurations);
mLog.log(Level.FINE, "Configuration loaded from {0}", configFile);
} catch (FileNotFoundException fnfe) {
mLog.log(Level.WARNING, "Unable to load configuration file {0}. Default configuration will be used.", configFile);
@ -135,8 +140,9 @@ public class JSEJBIFramework
settings.get(INSTANCE_NAME, DEFAULT_INSTANCE_NAME),
settings.getAsInt(CONNECTOR_PORT, DEFAULT_CONNECTOR_PORT));
// Do it in the main thread, not during an RMI connection
// SecurityProvider.getSecurityProvider();
mPlatformContext.setSecurityProvider(
new SecurityProviderImpl(
(Map<String,Map<String,String>>) configurations.get("realm")));
}
/**
@ -270,9 +276,9 @@ public class JSEJBIFramework
// Create an RMI registry instance to hold the JMX connector server
mRegistry = LocateRegistry.createRegistry(port);
map.put(JMXConnectorServer.AUTHENTICATOR, new CustomJMXAuthenticator(
map.put(JMXConnectorServer.AUTHENTICATOR, new JMXauthenticator(
mPlatformContext.getSecurityProvider()));
map.put("java.naming.factory.initial", RegistryContextFactory.class.getName());
map.put(Context.INITIAL_CONTEXT_FACTORY, RegistryContextFactory.class.getName());
map.put("com.sun.management.jmxremote.authenticate", Boolean.TRUE.toString());
// Create and start the connector server

View File

@ -423,8 +423,14 @@ public class JSEPlatformContext implements com.sun.jbi.platform.PlatformContext
Logger.getLogger(JBI_LOGGER_NAME).setLevel(level);
}
private SecurityProvider securityProvider;
public void setSecurityProvider(SecurityProvider securityProvider) {
this.securityProvider = securityProvider;
}
@Override
public SecurityProvider getSecurityProvider() {
return new SecurityProviderImpl();
return securityProvider;
}
}

View File

@ -57,6 +57,11 @@
<artifactId>openesb-standalone-naming</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>net.open-esb.runtime.standalone</groupId>
<artifactId>openesb-standalone-security</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Atomikos -->
<dependency>
@ -101,6 +106,13 @@
<version>5.0.3</version>
</dependency>
<!-- Apache Shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- OpenESB REST API & Web Console -->
<dependency>
<groupId>net.open-esb</groupId>

View File

@ -96,6 +96,7 @@
<include>org.apache.tomcat:tomcat-juli</include>
<include>org.apache.tomcat:tomcat-catalina</include>
<include>org.yaml:snakeyaml</include>
<include>org.apache.shiro:shiro-core</include>
</includes>
<outputDirectory>lib/ext</outputDirectory>
<useProjectArtifact>false</useProjectArtifact>
@ -108,6 +109,7 @@
<include>net.open-esb.runtime.standalone:openesb-standalone-framework</include>
<include>net.open-esb.runtime.standalone:openesb-standalone-bootstrap</include>
<include>net.open-esb.runtime.standalone:openesb-standalone-naming</include>
<include>net.open-esb.runtime.standalone:openesb-standalone-security</include>
</includes>
<binaries>
<includeDependencies>false</includeDependencies>

View File

@ -10,9 +10,9 @@ import javax.security.auth.Subject;
import net.openesb.security.AuthenticationException;
import net.openesb.security.AuthenticationToken;
import net.openesb.security.SecurityProvider;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.PropertiesRealm;
import net.openesb.standalone.security.realm.Realm;
import net.openesb.standalone.security.realm.RealmBuilder;
import net.openesb.standalone.security.realm.shiro.ShiroAuthenticator;
/**
*
@ -21,59 +21,67 @@ import org.apache.shiro.realm.text.PropertiesRealm;
*/
public class SecurityProviderImpl implements SecurityProvider {
private Logger mLog =
private final Logger mLog =
Logger.getLogger(this.getClass().getPackage().getName());
private final Map <String, org.apache.shiro.mgt.SecurityManager> securityManagers =
new HashMap<String, org.apache.shiro.mgt.SecurityManager>();
private final Map<String, Realm> realms = new HashMap<String, Realm>();
private final ShiroAuthenticator authenticator = new ShiroAuthenticator();
private String adminRealmName = null;
public SecurityProviderImpl() {
this.init();
public SecurityProviderImpl(Map<String, Map<String, String>> realmsConfiguration) {
this.init(realmsConfiguration);
this.validate();
}
private void init() {
mLog.log(Level.INFO, "Loading Realms from configuration.");
private void init(Map<String, Map<String, String>> realmsConfiguration) {
if (realmsConfiguration != null) {
mLog.log(Level.INFO, "Loading realms from configuration file.");
PropertiesRealm propertiesRealm = new PropertiesRealm();
propertiesRealm.setResourcePath("/Users/david/test.properties");
propertiesRealm.init();
securityManagers.put("admin-realm", new DefaultSecurityManager(propertiesRealm));
for(Map.Entry<String, Map<String, String>> realmConfig : realmsConfiguration.entrySet()) {
Realm realm = RealmBuilder.
realmBuilder().
build(realmConfig.getKey(), realmConfig.getValue());
realms.put(realmConfig.getKey(), realm);
}
} else {
mLog.log(Level.WARNING, "No realm defined !");
}
}
private void validate() {
for(Realm realm : realms.values()) {
authenticator.loadRealm(realm);
if (realm.isAdmin()) {
if (adminRealmName == null) {
adminRealmName = realm.getName();
} else {
throw new IllegalStateException(
"Admin realm already defined: " + adminRealmName);
}
}
}
}
@Override
public Collection<String> getRealms() {
return Collections.unmodifiableSet(
securityManagers.keySet());
realms.keySet());
}
@Override
public String getAdminRealm() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
return adminRealmName;
}
@Override
public boolean isAvailable(String realmName) {
return securityManagers.containsKey(realmName);
return realms.containsKey(realmName);
}
@Override
public Subject login(String realmName, AuthenticationToken authenticationToken) throws AuthenticationException {
org.apache.shiro.mgt.SecurityManager securityManager = securityManagers.get(realmName);
org.apache.shiro.subject.Subject currentUser =
new org.apache.shiro.subject.Subject.Builder(securityManager).buildSubject();
UsernamePasswordToken token = new UsernamePasswordToken(
(String) authenticationToken.getPrincipal(),
(char []) authenticationToken.getCredentials());
try {
currentUser.login(token);
Subject subject = new Subject();
return subject;
} catch (org.apache.shiro.authc.AuthenticationException ae) {
throw new AuthenticationException(ae.getMessage());
}
return authenticator.authenticate(realmName, authenticationToken);
}
}

View File

@ -11,11 +11,11 @@ import net.openesb.security.UsernamePasswordToken;
* @author David BRASSELY (brasseld at gmail.com)
* @author OpenESB Community
*/
public class CustomJMXAuthenticator implements JMXAuthenticator {
public class JMXauthenticator implements JMXAuthenticator {
private final SecurityProvider securityProvider;
public CustomJMXAuthenticator(final SecurityProvider securityProvider) {
public JMXauthenticator(final SecurityProvider securityProvider) {
this.securityProvider = securityProvider;
}

View File

@ -0,0 +1,35 @@
package net.openesb.standalone.security.realm;
/**
*
* @author David BRASSELY (brasseld at gmail.com)
* @author OpenESB Community
*/
public abstract class AbstractRealm implements Realm {
private String realmName;
private boolean admin = false;
protected AbstractRealm() {
}
protected AbstractRealm(String realmName) {
this.realmName = realmName;
}
public boolean isAdmin() {
return admin;
}
public void setAdmin(boolean admin) {
this.admin = admin;
}
public String getName() {
return realmName;
}
public void setName(String realmName) {
this.realmName = realmName;
}
}

View File

@ -0,0 +1,17 @@
package net.openesb.standalone.security.realm;
/**
*
* @author David BRASSELY (brasseld at gmail.com)
* @author OpenESB Community
*/
public interface Realm {
void setName(String name);
String getName();
boolean isAdmin();
void setAdmin(boolean isAdmin);
}

View File

@ -0,0 +1,31 @@
package net.openesb.standalone.security.realm;
import java.util.Map;
import java.util.ServiceLoader;
/**
*
* @author David BRASSELY (brasseld at gmail.com)
* @author OpenESB Community
*/
public final class RealmBuilder {
public static RealmBuilder realmBuilder() {
return new RealmBuilder();
}
public Realm build(String realmName, Map<String, String> properties) {
ServiceLoader<RealmHandler> handlers = ServiceLoader.load(RealmHandler.class);
for(RealmHandler handler : handlers) {
if (handler.canHandle(realmName)) {
Realm realm = handler.create(properties);
realm.setName(realmName);
return realm;
}
}
throw new IllegalStateException("Unable to create realm " + realmName +
" : no handler found !");
}
}

View File

@ -0,0 +1,15 @@
package net.openesb.standalone.security.realm;
import java.util.Map;
/**
*
* @author David BRASSELY (brasseld at gmail.com)
* @author OpenESB Community
*/
public interface RealmHandler<T extends Realm> {
boolean canHandle(String type);
T create(Map<String, String> properties);
}

View File

@ -0,0 +1,44 @@
package net.openesb.standalone.security.realm.impl;
import net.openesb.standalone.security.realm.AbstractRealm;
/**
*
* @author David BRASSELY (brasseld at gmail.com)
* @author OpenESB Community
*/
public class PropertiesRealm extends AbstractRealm {
private String path;
private boolean reload = false;
/**
* Unit: seconds
*/
private int reloadInterval;
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public boolean isReload() {
return reload;
}
public void setReload(boolean reload) {
this.reload = reload;
}
public int getReloadInterval() {
return reloadInterval;
}
public void setReloadInterval(int reloadInterval) {
this.reloadInterval = reloadInterval;
}
}

View File

@ -0,0 +1,59 @@
package net.openesb.standalone.security.realm.impl;
import java.io.File;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.openesb.standalone.security.realm.RealmHandler;
/**
*
* @author David BRASSELY (brasseld at gmail.com)
* @author OpenESB Community
*/
public class PropertiesRealmHandler implements RealmHandler<PropertiesRealm> {
private final Logger mLog =
Logger.getLogger(this.getClass().getPackage().getName());
private final static String PROPERTIES_REALM = "properties";
private final static String PROPERTY_PATH = "file";
private final static String PROPERTY_RELOAD_ENABLE = "reload";
private final static String PROPERTY_RELOAD_INTERVAL = "interval";
@Override
public boolean canHandle(String type) {
return PROPERTIES_REALM.equalsIgnoreCase(type);
}
@Override
public PropertiesRealm create(Map<String, String> properties) {
String file = properties.get(PROPERTY_PATH);
mLog.log(Level.INFO, "Creating properties realm using file: {0}", file);
File propertyFile = new File(file);
if (! propertyFile.exists()) {
mLog.log(Level.SEVERE, "Properties realm, invalid path: {0}",
propertyFile.getAbsolutePath());
throw new IllegalStateException("Properties realm, invalid path: " +
propertyFile.getAbsolutePath());
}
boolean reload = Boolean.parseBoolean(properties.get(PROPERTY_RELOAD_ENABLE));
PropertiesRealm propertiesRealm = new PropertiesRealm();
propertiesRealm.setPath(propertyFile.getAbsolutePath());
if (reload) {
String sInterval = properties.get(PROPERTY_RELOAD_INTERVAL);
try {
int interval = Integer.parseInt(sInterval);
propertiesRealm.setReloadInterval(interval);
} catch (NumberFormatException nfe) {
}
}
return propertiesRealm;
}
}

View File

@ -0,0 +1,26 @@
package net.openesb.standalone.security.realm.shiro;
import org.apache.shiro.realm.text.PropertiesRealm;
/**
*
* @author David BRASSELY (brasseld at gmail.com)
* @author OpenESB Community
*/
public class PropertiesRealmConverter implements
RealmConverter<net.openesb.standalone.security.realm.impl.PropertiesRealm, PropertiesRealm> {
@Override
public PropertiesRealm convert(net.openesb.standalone.security.realm.impl.PropertiesRealm realm) {
PropertiesRealm cRealm = new PropertiesRealm();
cRealm.setResourcePath(realm.getPath());
if (realm.isReload()) {
cRealm.setReloadIntervalSeconds(realm.getReloadInterval());
}
return cRealm;
}
}

View File

@ -0,0 +1,13 @@
package net.openesb.standalone.security.realm.shiro;
import net.openesb.standalone.security.realm.Realm;
/**
*
* @author David BRASSELY (brasseld at gmail.com)
* @author OpenESB Community
*/
public interface RealmConverter<T extends Realm, S extends org.apache.shiro.realm.Realm> {
S convert(T realm);
}

View File

@ -0,0 +1,54 @@
package net.openesb.standalone.security.realm.shiro;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import javax.security.auth.Subject;
import net.openesb.security.AuthenticationException;
import net.openesb.security.AuthenticationToken;
import net.openesb.standalone.security.realm.Realm;
import net.openesb.standalone.security.realm.impl.PropertiesRealm;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
/**
*
* @author David BRASSELY (brasseld at gmail.com)
* @author OpenESB Community
*/
public class ShiroAuthenticator {
private final Logger mLog =
Logger.getLogger(this.getClass().getPackage().getName());
private final Map <String, org.apache.shiro.mgt.SecurityManager> securityManagers =
new HashMap<String, org.apache.shiro.mgt.SecurityManager>();
public void loadRealm(Realm realm) {
//TODO: find a way to automate the convertion
org.apache.shiro.realm.Realm sRealm = new PropertiesRealmConverter().convert((PropertiesRealm)realm);
DefaultSecurityManager manager = new DefaultSecurityManager(sRealm);
securityManagers.put(realm.getName(), manager);
}
public Subject authenticate(String realmName, AuthenticationToken authenticationToken)
throws AuthenticationException {
org.apache.shiro.mgt.SecurityManager securityManager = securityManagers.get(realmName);
org.apache.shiro.subject.Subject currentUser =
new org.apache.shiro.subject.Subject.Builder(securityManager).buildSubject();
UsernamePasswordToken token = new UsernamePasswordToken(
(String) authenticationToken.getPrincipal(),
(char []) authenticationToken.getCredentials());
try {
currentUser.login(token);
Subject subject = new Subject();
return subject;
} catch (org.apache.shiro.authc.AuthenticationException ae) {
throw new AuthenticationException(ae.getMessage());
}
}
}

View File

@ -0,0 +1 @@
net.openesb.standalone.security.realm.shiro.PropertiesRealmHandler