Enterprise Java Development@TOPIC@
JBoss Remoting and JNDI InitialContext
EJBClient and CallbackHandler
Changing Users
Access Violations
Access Granted
Figure 112.1. jndi.properties
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory java.naming.factory.url.pkgs=org.jboss.ejb.client.naming java.naming.provider.url=http-remoting://localhost:8080 jboss.naming.client.ejb.context=true
factory.initial set to JBoss Remoting implementation
provider.url set to address of JBoss server
ejb.context set to "true" to use this library to establish EJB contexts
url.pkgs option and used for alternate namespaces (e.g., "ejb:")
Figure 112.2. JBoss Remoting JNDI Name
securePingEAR/securePingEJB/SecurePingEJB!info.ejava.examples.secureping.ejb.SecurePingRemote
Using generic JBoss Remoting JNDI name
Figure 112.3. Client Provides Credentials to JNDI InitialContext
private Context runAs(String username, String password) throws NamingException {
Properties env = new Properties();
if (username != null) {
env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password);
}
env.put("jboss.naming.client.ejb.context", true); //override anything we put there for EJBClient
return new InitialContext(env);
}
Client provides credentials in JNDI prior to obtaining InitialContext
Must use current JNDI Context to lookup @Remote
Figure 112.4. Example Changing Users with JBoss Remoting
jndi=runAs(userUser, userPassword);
ejb=(SecurePing)jndi.lookup(jndiName);
assertFalse("user in admin role", ejb.isCallerInRole("admin"));
jndi=runAs(adminUser, adminPassword);
ejb=(SecurePing)jndi.lookup(jndiName);
assertTrue("admin not in admin role", ejb.isCallerInRole("admin"));
Client can switch credentials with a change in InitialContexts and @Remote reference
Figure 112.5. Fixed Credentials
java.naming.security.principal=known java.naming.security.credentials=password1!
Fixed credentials can be placed in jndi.properties when known and not changing
Figure 112.6. jndi.properties
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory java.naming.factory.url.pkgs=org.jboss.ejb.client.naming java.naming.provider.url=http-remoting://localhost:8080 jboss.naming.client.ejb.context=false
factory.initial not important, will delegate to naming extension
url.pkgs defines Java package with EJBClient extensions
provider.url ignored in this case
ejb.context not used and set to false in this case
Figure 112.7. jboss-ejb-client.properties
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false remote.connections=default remote.connection.default.host=localhost remote.connection.default.port=8080 remote.connection.default.connect.options.org.xnio.Options.SASL_DISALLOWED_MECHANISMS=JBOSS-LOCAL-USER remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false remote.connection.default.callback.handler.class=info.ejava.examples.secureping.ejbclient.BasicCallbackHandler
SSL_ENABLED=false - example setup does not yet cover SSL and would require trustStore
SASL_DISALLOWED_MECHANISMS=JBOSS-LOCAL-USER - disallowing default user to be set to $local
callback.handler.class=info.ejava...BasicCallbackHandler - provider credentials via callback
Figure 112.8. CallbackHandler used to Provide Credentials
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.RealmCallback;
public class BasicCallbackHandler implements CallbackHandler {
private String name;
private char[] password;
private String realm="ApplicationRealm";
public BasicCallbackHandler(String name, String password) {
this.name = name;
setPassword(password);
}
public void handle(Callback[] callbacks)
throws UnsupportedCallbackException, IOException {
for (Callback cb : callbacks) {
if (cb instanceof NameCallback) {
((NameCallback)cb).setName(name);
}
else if (cb instanceof PasswordCallback) {
((PasswordCallback)cb).setPassword(password);
}
else if (cb instanceof RealmCallback) {
((RealmCallback)cb).setText(realm);
}
else {
throw new UnsupportedCallbackException(cb);
}
}
}
}
Class was registered in jboss-ejb-client.properties
Class responds to handle() callbacks
Figure 112.9. CallbackHandler Accessed at Class Scope
public class BasicCallbackHandler implements CallbackHandler {
...
private static CallbackHandler login;
public void handle(Callback[] callbacks)
throws UnsupportedCallbackException, IOException {
if (login!=null && login!=this) {
login.handle(callbacks);
return;
}
for (Callback cb : callbacks) {
...
public static void setLogin(CallbackHandler login) {
BasicCallbackHandler.login=login;
}
public static CallbackHandler getLogin() {
return login;
}
}
CallbackHandler accessed at class-scope
Must register credentials at class level
Credentials tied to connection. Cannot change identities without breaking and re-establishing connection (with proprietary API)
Figure 112.10. Proprietary API used to Change Identity
private void runAs(CallbackHandler login) throws NamingException, IOException {
InputStream is = getClass().getResourceAsStream("/jboss-ejb-client.properties");
try {
Properties props = new Properties();
props.load(is);
BasicCallbackHandler.setLogin(login);
EJBClientConfiguration cfg = new PropertiesBasedEJBClientConfiguration(props);
ContextSelector<EJBClientContext> contextSelector = new ConfigBasedEJBClientContextSelector(cfg);
EJBClientContext.setSelector(contextSelector);
} finally {
is.close();
}
}
EJBClient stores credentials with connection
Must establish new connection to have CallbackHandler called again
Figure 112.11. Example Changing Users with EJBClient
runAs(userLogin);
assertFalse("user in admin role", securePing.isCallerInRole("admin"));
runAs(adminLogin);
assertTrue("admin not in admin role", securePing.isCallerInRole("admin"));
No need to get new InitialContext or new lookup of @Remote
Figure 112.12. Fixed Credentials
remote.connection.default.username=known remote.connection.default.password=password1!
Fixed credentials can be placed in jboss-ejb-client.properties when known and not changing
Useful to implement sanity check to assure authentication and authorizations in place
Figure 112.13. EJB Security Query Methods
@Stateless
public class SecurePingEJB implements SecurePingRemote, SecurePingLocal {
@Resource
SessionContext ctx;
@PermitAll
public boolean isCallerInRole(String role) {
boolean result = ctx.isCallerInRole(role);
logger.debug("user=" + ctx.getCallerPrincipal().getName() +
", isCallerInRole(" + role + ")=" + result);
return result;
}
@PermitAll
public String getPrincipal() {
String name= ctx.getCallerPrincipal().getName();
logger.debug("getPrincipal(), name=" + name);
return name;
}
Implement security query methods in a EJB
Figure 112.14. Client Issues Security Query Calls
runAs(userLogin);
assertEquals("unexpected user", userUser, securePing.getPrincipal());
assertFalse("user in internalRole role", securePing.isCallerInRole("internalRole"));
runAs(adminLogin);
assertEquals("unexpected user", adminUser, securePing.getPrincipal());
assertTrue("admin not in internalRole role", securePing.isCallerInRole("internalRole"));
Client asserts security query results to verify setup correctly
Figure 112.15. Example Access Violation
Context jndi = new InitialContext();
logger.debug("looking up jndi.name={}", jndiName);
securePing = (SecurePingRemote)jndi.lookup(jndiName);
try {
runAs(userLogin);
logger.info(securePing.pingAdmin());
fail("didn't detect non-admin user");
}
catch (EJBAccessException ex) {
logger.info("expected exception thrown:" + ex);
}
-looking up jndi.name=ejb:securePingEAR/securePingEJB/SecurePingEJB!info.ejava.examples.secureping.ejb.SecurePingRemote -realm callback:ApplicationRealm -name callback:user1 -password callback:password1! -expected exception thrown:javax.ejb.EJBAccessException: JBAS014502: Invocation on method: public abstract java.lang.String info.ejava.examples.secureping.ejb.SecurePing.pingAdmin() of bean: SecurePingEJB is not allowed
EJBAccessException thrown when accessing method not allowed
Figure 112.16. Example Access Granted
runAs(adminLogin);
logger.info(securePing.pingAdmin());
-realm callback:ApplicationRealm -name callback:admin1 -password callback:password1! -called pingAdmin, principal=admin1, isUser=true, isAdmin=true, isInternalRole=true
Access to method protected by declarative security granted
Results of programmatic security checks returned in formatted text string