Skip to content

Commit

Permalink
LIVY-252. HTTP auth support for Scala LivyClient. (apache#245)
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-the-man authored Dec 15, 2016
1 parent e4a52bf commit de816f5
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,18 @@
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.concurrent.TimeUnit;
import static java.nio.charset.StandardCharsets.UTF_8;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
Expand Down Expand Up @@ -90,24 +92,43 @@ public boolean isContentCompressionEnabled() {
}
};

Credentials dummyCredentials = new Credentials() {
@Override
public String getPassword() {
return null;
Credentials credentials;
// If user info is specified in the url, pass them to the CredentialsProvider.
if (uri.getUserInfo() != null) {
String[] userInfo = uri.getUserInfo().split(":");
if (userInfo.length < 1) {
throw new IllegalArgumentException("Malformed user info in the url.");
}

@Override
public Principal getUserPrincipal() {
return null;
try {
String username = URLDecoder.decode(userInfo[0], StandardCharsets.UTF_8.name());
String password = "";
if (userInfo.length > 1) {
password = URLDecoder.decode(userInfo[1], StandardCharsets.UTF_8.name());
}
credentials = new UsernamePasswordCredentials(username, password);
} catch (Exception e) {
throw new IllegalArgumentException("User info in the url contains bad characters.", e);
}
};
} else {
credentials = new Credentials() {
@Override
public String getPassword() {
return null;
}

@Override
public Principal getUserPrincipal() {
return null;
}
};
}

// This is needed to get Kerberos credentials from the environment, instead of
// requiring the application to manually obtain the credentials.
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");

CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, dummyCredentials);
credsProvider.setCredentials(AuthScope.ANY, credentials);

HttpClientBuilder builder = HttpClientBuilder.create()
.disableAutomaticRetries()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Licensed to Cloudera, Inc. under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Cloudera, Inc. 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 com.cloudera.livy.client.http

import java.net.URLEncoder
import java.nio.charset.StandardCharsets.UTF_8
import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}

import org.apache.http.client.utils.URIBuilder
import org.eclipse.jetty.security._
import org.eclipse.jetty.security.authentication.BasicAuthenticator
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.{ServletContextHandler, ServletHolder}
import org.eclipse.jetty.util.security._
import org.scalatest.{BeforeAndAfterAll, FunSpecLike}
import org.scalatest.Matchers._

import com.cloudera.livy.LivyBaseUnitTestSuite

class LivyConnectionSpec extends FunSpecLike with BeforeAndAfterAll with LivyBaseUnitTestSuite {
describe("LivyConnection") {
def basicAuth(username: String, password: String, realm: String): SecurityHandler = {
val roles = Array("user")

val l = new HashLoginService()
l.putUser(username, Credential.getCredential(password), roles)
l.setName(realm)

val constraint = new Constraint()
constraint.setName(Constraint.__BASIC_AUTH)
constraint.setRoles(roles)
constraint.setAuthenticate(true)

val cm = new ConstraintMapping()
cm.setConstraint(constraint)
cm.setPathSpec("/*")

val csh = new ConstraintSecurityHandler()
csh.setAuthenticator(new BasicAuthenticator())
csh.setRealmName(realm)
csh.addConstraintMapping(cm)
csh.setLoginService(l)

csh
}

def staticServlet(): HttpServlet = new HttpServlet {
override def doGet(req: HttpServletRequest, resp: HttpServletResponse): Unit = {
resp.getWriter.print("true")
}
}

def test(password: String): Unit = {
val username = "user name"

val server = new Server(0)
val context = new ServletContextHandler(ServletContextHandler.SESSIONS)
context.setSecurityHandler(basicAuth(username, password, "realm"))
context.setContextPath("/")
context.addServlet(new ServletHolder(staticServlet()), "/")
server.setHandler(context)
server.start()

val utf8Name = UTF_8.name()
val uri = new URIBuilder(server.getURI())
.setUserInfo(URLEncoder.encode(username, utf8Name), URLEncoder.encode(password, utf8Name))
.build()
info(uri.toString)
val conn = new LivyConnection(uri, new HttpConf(null))
try {
conn.get(classOf[Boolean], "/") shouldBe true
} finally {
conn.close()
}

server.stop()
server.join()
}

it("should support HTTP auth with password") {
test("pass:word")
}

it("should support HTTP auth with empty password") {
test("")
}
}
}

0 comments on commit de816f5

Please sign in to comment.