/**
* 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.openejb;
import org.apache.openejb.assembler.classic.Assembler;
import org.apache.openejb.client.JNDIContext;
import org.apache.openejb.client.RemoteInitialContextFactory;
import org.apache.openejb.config.ConfigurationFactory;
import org.apache.openejb.config.DeploymentsResolver;
import org.apache.openejb.core.ServerFederation;
import org.apache.openejb.jee.EjbJar;
import org.apache.openejb.jee.StatelessBean;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.server.ServiceDaemon;
import org.apache.openejb.server.ejbd.EjbServer;
import org.apache.openejb.spi.SecurityService;
import org.apache.openejb.testng.PropertiesBuilder;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.ejb.EJBException;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import java.util.Map;
import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
public class AuthentWithRequestTest {
@BeforeClass
public static void initJAAS() {
System.setProperty("java.security.auth.login.config", AuthentWithRequestTest.class.getResource("/login.config").getFile());
}
@AfterClass
public static void resetJAAS() {
System.clearProperty("java.security.auth.login.config");
}
@Test
public void invoke() throws Exception {
final EjbServer ejbServer = new EjbServer();
OpenEJB.init(new PropertiesBuilder().p(DeploymentsResolver.DEPLOYMENTS_CLASSPATH_PROPERTY, "false").build(), new ServerFederation());
ejbServer.init(new Properties());
final ServiceDaemon serviceDaemon = new ServiceDaemon(ejbServer, 0, "localhost");
serviceDaemon.start();
final int port = serviceDaemon.getPort();
final Assembler assembler = SystemInstance.get().getComponent(Assembler.class);
final ConfigurationFactory config = new ConfigurationFactory();
final EjbJar ejbJar = new EjbJar();
ejbJar.addEnterpriseBean(new StatelessBean(RemoteWithSecurity.class));
assembler.createApplication(config.configureApplication(ejbJar));
try {
{ // ok case
final Context context = new InitialContext(new PropertiesBuilder()
.p(Context.INITIAL_CONTEXT_FACTORY, RemoteInitialContextFactory.class.getName())
.p(Context.PROVIDER_URL, "ejbd://127.0.0.1:" + port)
.p(JNDIContext.AUTHENTICATE_WITH_THE_REQUEST, "true")
.p("java.naming.security.principal", "foo")
.p("java.naming.security.credentials", "bar")
.p("openejb.authentication.realmName", "LM")
.build());
final AnInterfaceRemote client = AnInterfaceRemote.class.cast(context.lookup("RemoteWithSecurityRemote"));
assertNotNull(client);
assertEquals("foo", client.call());
}
{// now the failing case
final Context context = new InitialContext(new PropertiesBuilder()
.p(Context.INITIAL_CONTEXT_FACTORY, RemoteInitialContextFactory.class.getName())
.p(Context.PROVIDER_URL, "ejbd://127.0.0.1:" + port)
.p(JNDIContext.AUTHENTICATE_WITH_THE_REQUEST, "true")
.p("java.naming.security.principal", "wrong")
.p("java.naming.security.credentials", "wrong")
.p("openejb.authentication.realmName", "LM")
.build());
final AnInterfaceRemote client = AnInterfaceRemote.class.cast(context.lookup("RemoteWithSecurityRemote"));
try {
client.call();
} catch (final EJBException e) {
if (!LoginException.class.isInstance(e.getCause())) {
e.printStackTrace();
}
assertTrue(LoginException.class.isInstance(e.getCause()));
}
}
} finally {
serviceDaemon.stop();
OpenEJB.destroy();
}
}
@Remote
public static interface AnInterfaceRemote {
String call();
}
@Stateless
public static class RemoteWithSecurity implements AnInterfaceRemote {
private static ThreadLocal<String> name = new ThreadLocal<String>();
@Override
public String call() {
return name.get();
}
}
public static class MyLoginModule implements LoginModule {
private CallbackHandler callbackHandler;
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
this.callbackHandler = callbackHandler;
}
@Override
public boolean login() throws LoginException {
assertNull(SystemInstance.get().getComponent(SecurityService.class).currentState()); // check the user was not logged at lookup()
final NameCallback nameCallback = new NameCallback("name?", "dummy");
try {
callbackHandler.handle(new Callback[]{nameCallback});
} catch (final Exception e) {
throw new LoginException(e.getMessage());
}
if (!"foo".equals(nameCallback.getName())) {
throw new IllegalArgumentException("Not an Error/assert cause in java 9 jaas doesnt capture it anymore");
}
RemoteWithSecurity.name.set(nameCallback.getName());
return true;
}
@Override
public boolean commit() throws LoginException {
return true;
}
@Override
public boolean abort() throws LoginException {
return true;
}
@Override
public boolean logout() throws LoginException {
return true;
}
}
}