| /* |
| * 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.atlas.web.filters; |
| |
| import org.apache.atlas.RequestContextV1; |
| import org.apache.atlas.web.security.BaseSecurityTest; |
| import org.apache.atlas.web.service.EmbeddedServer; |
| import org.apache.commons.configuration.PropertiesConfiguration; |
| import org.apache.commons.io.FileUtils; |
| import org.apache.hadoop.hdfs.web.URLConnectionFactory; |
| import org.eclipse.jetty.server.Server; |
| import org.eclipse.jetty.webapp.WebAppContext; |
| import org.testng.annotations.Test; |
| |
| 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.callback.PasswordCallback; |
| import javax.security.auth.callback.UnsupportedCallbackException; |
| import javax.security.auth.login.LoginContext; |
| import javax.security.auth.login.LoginException; |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.HttpURLConnection; |
| import java.net.URL; |
| import java.security.PrivilegedExceptionAction; |
| |
| import static org.testng.Assert.assertEquals; |
| |
| /** |
| * |
| */ |
| public class AtlasAuthenticationKerberosFilterTest extends BaseSecurityTest { |
| public static final String TEST_USER_JAAS_SECTION = "TestUser"; |
| public static final String TESTUSER = "testuser"; |
| public static final String TESTPASS = "testpass"; |
| |
| private File userKeytabFile; |
| private File httpKeytabFile; |
| |
| class TestEmbeddedServer extends EmbeddedServer { |
| public TestEmbeddedServer(int port, String path) throws IOException { |
| super(ATLAS_DEFAULT_BIND_ADDRESS, port, path); |
| } |
| |
| Server getServer() { |
| return server; |
| } |
| |
| @Override |
| protected WebAppContext getWebAppContext(String path) { |
| WebAppContext application = new WebAppContext(path, "/"); |
| application.setDescriptor(System.getProperty("projectBaseDir") + "/webapp/src/test/webapp/WEB-INF/web.xml"); |
| application.setClassLoader(Thread.currentThread().getContextClassLoader()); |
| return application; |
| } |
| } |
| |
| @Test(enabled = false) |
| public void testKerberosBasedLogin() throws Exception { |
| String originalConf = System.getProperty("atlas.conf"); |
| |
| setupKDCAndPrincipals(); |
| TestEmbeddedServer server = null; |
| |
| try { |
| // setup the atlas-application.properties file |
| String confDirectory = generateKerberosTestProperties(); |
| System.setProperty("atlas.conf", confDirectory); |
| |
| // need to create the web application programmatically in order to control the injection of the test |
| // application properties |
| server = new TestEmbeddedServer(23000, "webapp/target/apache-atlas"); |
| |
| startEmbeddedServer(server.getServer()); |
| |
| final URLConnectionFactory connectionFactory = URLConnectionFactory.DEFAULT_SYSTEM_CONNECTION_FACTORY; |
| // attempt to hit server and get rejected |
| URL url = new URL("http://localhost:23000/"); |
| HttpURLConnection connection = (HttpURLConnection) connectionFactory.openConnection(url, false); |
| connection.setRequestMethod("GET"); |
| connection.connect(); |
| |
| assertEquals(connection.getResponseCode(), 401); |
| |
| // need to populate the ticket cache with a local user, so logging in... |
| Subject subject = loginTestUser(); |
| |
| Subject.doAs(subject, new PrivilegedExceptionAction<Object>() { |
| @Override |
| public Object run() throws Exception { |
| // attempt to hit server and get rejected |
| URL url = new URL("http://localhost:23000/"); |
| HttpURLConnection connection = (HttpURLConnection) connectionFactory.openConnection(url, true); |
| connection.setRequestMethod("GET"); |
| connection.connect(); |
| |
| assertEquals(connection.getResponseCode(), 200); |
| assertEquals(RequestContextV1.get().getUser(), TESTUSER); |
| return null; |
| } |
| }); |
| } finally { |
| server.getServer().stop(); |
| kdc.stop(); |
| |
| if (originalConf != null) { |
| System.setProperty("atlas.conf", originalConf); |
| } else { |
| System.clearProperty("atlas.conf"); |
| } |
| |
| } |
| } |
| |
| protected Subject loginTestUser() throws LoginException, IOException { |
| LoginContext lc = new LoginContext(TEST_USER_JAAS_SECTION, new CallbackHandler() { |
| |
| @Override |
| public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { |
| for (Callback callback : callbacks) { |
| if (callback instanceof PasswordCallback) { |
| PasswordCallback passwordCallback = (PasswordCallback) callback; |
| passwordCallback.setPassword(TESTPASS.toCharArray()); |
| } |
| if (callback instanceof NameCallback) { |
| NameCallback nameCallback = (NameCallback) callback; |
| nameCallback.setName(TESTUSER); |
| } |
| } |
| } |
| }); |
| // attempt authentication |
| lc.login(); |
| return lc.getSubject(); |
| } |
| |
| protected String generateKerberosTestProperties() throws Exception { |
| PropertiesConfiguration props = new PropertiesConfiguration(); |
| props.setProperty("atlas.http.authentication.enabled", "true"); |
| props.setProperty("atlas.http.authentication.type", "kerberos"); |
| props.setProperty("atlas.http.authentication.kerberos.principal", "HTTP/localhost@" + kdc.getRealm()); |
| props.setProperty("atlas.http.authentication.kerberos.keytab", httpKeytabFile.getAbsolutePath()); |
| props.setProperty("atlas.http.authentication.kerberos.name.rules", |
| "RULE:[1:$1@$0](.*@EXAMPLE.COM)s/@.*//\nDEFAULT"); |
| |
| return writeConfiguration(props); |
| } |
| |
| public void setupKDCAndPrincipals() throws Exception { |
| // set up the KDC |
| File kdcWorkDir = startKDC(); |
| |
| userKeytabFile = createKeytab(kdc, kdcWorkDir, "dgi", "dgi.keytab"); |
| httpKeytabFile = createKeytab(kdc, kdcWorkDir, "HTTP", "spnego.service.keytab"); |
| |
| // create a test user principal |
| kdc.createPrincipal(TESTUSER, TESTPASS); |
| |
| StringBuilder jaas = new StringBuilder(1024); |
| jaas.append("TestUser {\n" + |
| " com.sun.security.auth.module.Krb5LoginModule required\nuseTicketCache=true;\n" + |
| "};\n"); |
| jaas.append(createJAASEntry("Client", "dgi", userKeytabFile)); |
| jaas.append(createJAASEntry("Server", "HTTP", httpKeytabFile)); |
| |
| File jaasFile = new File(kdcWorkDir, "jaas.txt"); |
| FileUtils.write(jaasFile, jaas.toString()); |
| bindJVMtoJAASFile(jaasFile); |
| } |
| |
| } |