@@ -19,15 +19,18 @@ package org.apache.spark
1919
2020import java .io .File
2121
22+ import org .eclipse .jetty .util .security .{Constraint , Password }
23+ import org .eclipse .jetty .security .authentication .DigestAuthenticator
24+ import org .eclipse .jetty .security .{ConstraintMapping , ConstraintSecurityHandler , HashLoginService , SecurityHandler }
25+
2226import org .eclipse .jetty .server .Server
2327import org .eclipse .jetty .server .bio .SocketConnector
24- import org .eclipse .jetty .server .handler .DefaultHandler
25- import org .eclipse .jetty .server .handler .HandlerList
26- import org .eclipse .jetty .server .handler .ResourceHandler
28+ import org .eclipse .jetty .server .handler .{DefaultHandler , HandlerList , ResourceHandler }
2729import org .eclipse .jetty .util .thread .QueuedThreadPool
2830
2931import org .apache .spark .util .Utils
3032
33+
3134/**
3235 * Exception type thrown by HttpServer when it is in the wrong state for an operation.
3336 */
@@ -38,7 +41,8 @@ private[spark] class ServerStateException(message: String) extends Exception(mes
3841 * as well as classes created by the interpreter when the user types in code. This is just a wrapper
3942 * around a Jetty server.
4043 */
41- private [spark] class HttpServer (resourceBase : File ) extends Logging {
44+ private [spark] class HttpServer (resourceBase : File , securityManager : SecurityManager )
45+ extends Logging {
4246 private var server : Server = null
4347 private var port : Int = - 1
4448
@@ -59,14 +63,60 @@ private[spark] class HttpServer(resourceBase: File) extends Logging {
5963 server.setThreadPool(threadPool)
6064 val resHandler = new ResourceHandler
6165 resHandler.setResourceBase(resourceBase.getAbsolutePath)
66+
6267 val handlerList = new HandlerList
6368 handlerList.setHandlers(Array (resHandler, new DefaultHandler ))
64- server.setHandler(handlerList)
69+
70+ if (securityManager.isAuthenticationEnabled()) {
71+ logDebug(" HttpServer is using security" )
72+ val sh = setupSecurityHandler(securityManager)
73+ // make sure we go through security handler to get resources
74+ sh.setHandler(handlerList)
75+ server.setHandler(sh)
76+ } else {
77+ logDebug(" HttpServer is not using security" )
78+ server.setHandler(handlerList)
79+ }
80+
6581 server.start()
6682 port = server.getConnectors()(0 ).getLocalPort()
6783 }
6884 }
6985
86+ /**
87+ * Setup Jetty to the HashLoginService using a single user with our
88+ * shared secret. Configure it to use DIGEST-MD5 authentication so that the password
89+ * isn't passed in plaintext.
90+ */
91+ private def setupSecurityHandler (securityMgr : SecurityManager ): ConstraintSecurityHandler = {
92+ val constraint = new Constraint ()
93+ // use DIGEST-MD5 as the authentication mechanism
94+ constraint.setName(Constraint .__DIGEST_AUTH)
95+ constraint.setRoles(Array (" user" ))
96+ constraint.setAuthenticate(true )
97+ constraint.setDataConstraint(Constraint .DC_NONE )
98+
99+ val cm = new ConstraintMapping ()
100+ cm.setConstraint(constraint)
101+ cm.setPathSpec(" /*" )
102+ val sh = new ConstraintSecurityHandler ()
103+
104+ // the hashLoginService lets us do a single user and
105+ // secret right now. This could be changed to use the
106+ // JAASLoginService for other options.
107+ val hashLogin = new HashLoginService ()
108+
109+ val userCred = new Password (securityMgr.getSecretKey())
110+ if (userCred == null ) {
111+ throw new Exception (" Error: secret key is null with authentication on" )
112+ }
113+ hashLogin.putUser(securityMgr.getHttpUser(), userCred, Array (" user" ))
114+ sh.setLoginService(hashLogin)
115+ sh.setAuthenticator(new DigestAuthenticator ());
116+ sh.setConstraintMappings(Array (cm))
117+ sh
118+ }
119+
70120 def stop () {
71121 if (server == null ) {
72122 throw new ServerStateException (" Server is already stopped" )
0 commit comments