Support Questions

Find answers, ask questions, and share your expertise
Announcements
Celebrating as our community reaches 100,000 members! Thank you!

Authentication with the Zeppelin REST API

avatar
Expert Contributor

I'm trying to use the Sandbox to perform queries against Zeppelin's Notebook REST API. Previously Zeppelin did not require any credentials to access notebooks. Now that they use authentication I can't seem to find a way to access it. I haven't found any documentation either.

I made the following cURL request:

curl -i -X GET http://sandbox.hortonworks.com:9995/api/notebook

This gives me a redirect with the following response:

HTTP/1.1 302 Found
Date: Wed, 17 Aug 2016 08:24:39 GMT
Access-Control-Allow-Origin:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: authorization,Content-Type
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, HEAD, DELETE
Date: Wednesday, August 17, 2016 8:24:39 AM UTC
Set-Cookie: JSESSIONID=6b055152-eb81-4c86-8976-61e85d0541cf; Path=/; HttpOnly
Location: http://sandbox.hortonworks.com:9995/api/login;JSESSIONID=6b055152-eb81-4c86-8976-61e85d0541cf
Content-Length: 0
Server: Jetty(9.2.15.v20160210)

Looks like it's a redirect to /api/login

So let's try /api/login and see what happens

curl -i -X GET http://sandbox.hortonworks.com:9995/api/login

This gives the following response:

HTTP/1.1 500 Server Error
Date: Wed, 17 Aug 2016 08:25:53 GMT
Access-Control-Allow-Origin:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: authorization,Content-Type
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, HEAD, DELETE
Date: Wednesday, August 17, 2016 8:25:53 AM UTC
Content-Type: text/html; charset=ISO-8859-1
Cache-Control: must-revalidate,no-cache,no-store
Content-Length: 9546
Connection: close
Server: Jetty(9.2.15.v20160210)


<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Error 500 Server Error</title>
</head>
<body><h2>HTTP ERROR 500</h2>
<p>Problem accessing /api/login. Reason:
<pre>    Server Error</pre></p><h3>Caused by:</h3><pre>javax.servlet.ServletException: Filtered request failed.
	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:384)
	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
	at org.apache.zeppelin.server.CorsFilter.doFilter(CorsFilter.java:72)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
	at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:215)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
	at org.eclipse.jetty.server.Server.handle(Server.java:499)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)
	at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.AbstractMethodError: javax.ws.rs.core.Response.getStatusInfo()Ljavax/ws/rs/core/Response$StatusType;
	at javax.ws.rs.WebApplicationException.validate(WebApplicationException.java:186)
	at javax.ws.rs.ClientErrorException.<init>(ClientErrorException.java:88)
	at org.apache.cxf.jaxrs.utils.JAXRSUtils.findTargetMethod(JAXRSUtils.java:503)
	at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:207)
	at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:103)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272)
	at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
	at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:239)
	at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:248)
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:222)
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:153)
	at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:167)
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:286)
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:211)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:575)
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:262)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:812)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1669)
	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)
	at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)
	at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)
	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
	at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)
	at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
	at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
	at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
	at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383)
	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
	... 22 more
</pre>
<h3>Caused by:</h3><pre>java.lang.AbstractMethodError: javax.ws.rs.core.Response.getStatusInfo()Ljavax/ws/rs/core/Response$StatusType;
	at javax.ws.rs.WebApplicationException.validate(WebApplicationException.java:186)
	at javax.ws.rs.ClientErrorException.<init>(ClientErrorException.java:88)
	at org.apache.cxf.jaxrs.utils.JAXRSUtils.findTargetMethod(JAXRSUtils.java:503)
	at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.processRequest(JAXRSInInterceptor.java:207)
	at org.apache.cxf.jaxrs.interceptor.JAXRSInInterceptor.handleMessage(JAXRSInInterceptor.java:103)
	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272)
	at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
	at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:239)
	at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:248)
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:222)
	at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:153)
	at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:167)
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:286)
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:211)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:575)
	at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:262)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:812)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1669)
	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:61)
	at org.apache.shiro.web.servlet.AdviceFilter.executeChain(AdviceFilter.java:108)
	at org.apache.shiro.web.servlet.AdviceFilter.doFilterInternal(AdviceFilter.java:137)
	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
	at org.apache.shiro.web.servlet.ProxiedFilterChain.doFilter(ProxiedFilterChain.java:66)
	at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449)
	at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365)
	at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90)
	at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83)
	at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:383)
	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362)
	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
	at org.apache.zeppelin.server.CorsFilter.doFilter(CorsFilter.java:72)
	at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1652)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
	at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)
	at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1127)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
	at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1061)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
	at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:215)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
	at org.eclipse.jetty.server.Server.handle(Server.java:499)
	at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311)
	at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:257)
	at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
	at java.lang.Thread.run(Thread.java:745)
</pre>
<hr><i><small>Powered by Jetty://</small></i><hr/>


</body>
</html>

It's just an HTML page about the server error. I couldn't find anything in the docs on authentication.

Does anyone know how to use the REST API now that it's locked down?

1 ACCEPTED SOLUTION

avatar
Expert Contributor

So after some more investigation and toying around with the API I've found the solution.

The first step is to authentic through the API with a POST to /api/login

curl -i --data 'userName=admin&password=password1' -X POST http://127.0.0.1:9995/api/login

This should return a response like the following:

HTTP/1.1 200 OK
Date: Wed, 17 Aug 2016 10:07:22 GMT
Access-Control-Allow-Origin:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: authorization,Content-Type
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, HEAD, DELETE
Date: Wednesday, August 17, 2016 10:07:22 AM UTC
Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Tue, 16-Aug-2016 10:07:22 GMT
Set-Cookie: JSESSIONID=b1f15e00-4571-4079-a699-338bf619b0c4; Path=/; HttpOnly
Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Tue, 16-Aug-2016 10:07:22 GMT
Content-Type: application/json
Date: Wed, 17 Aug 2016 10:07:22 GMT
Content-Length: 118
Server: Jetty(9.2.15.v20160210)


{"status":"OK","message":"","body":{"principal":"admin","ticket":"47a1fe3a-593d-47ce-85bb-f6e7238c6dcb","roles":"[]"}}

The important thing to see here is the Set-Cookie in the response headers.

Look for the following:

Set-Cookie: JSESSIONID=b1f15e00-4571-4079-a699-338bf619b0c4; Path=/; HttpOnly

Using this cookie we can make authorized request to the API by simply setting this as a cookie in the cURL request.

curl -i -b 'JSESSIONID=b1f15e00-4571-4079-a699-338bf619b0c4; Path=/; HttpOnly' http://sandbox.hortonworks.com:9995/api/notebook

This request should now return a 200 OK response after adding the JSESSIONID cookie.

HTTP/1.1 200 OK
Date: Wed, 17 Aug 2016 10:10:44 GMT
Access-Control-Allow-Origin:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: authorization,Content-Type
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, HEAD, DELETE
Date: Wednesday, August 17, 2016 10:10:44 AM UTC
Content-Type: application/json
Date: Wed, 17 Aug 2016 10:10:44 GMT
Content-Length: 1012
Server: Jetty(9.2.15.v20160210)


{"status":"OK","message":"","body":[{"id":"2BSVACJ42","name":"/demo/note1"},{"id":"2BVBPU1VY","name":"/demo/note2"},{"id":"2APFTN3NY","name":"AON Demo"},{"id":"2ANT56EHN","name":"Australian Dataset (Hive example)"},{"id":"2ANTDG878","name":"Australian Dataset (SparkSQL example)"},{"id":"2B48PF7SN","name":"Hello World Tutorial"},{"id":"2AS5TY6AQ","name":"IoT  Data Analysis (Keynote Demo)"},{"id":"2BFGYS3YT","name":"Lab 101: Intro to Spark with Python"},{"id":"2BJVW65WS","name":"Lab 102: Intro to Spark with Scala"},{"id":"2BNDT63TY","name":"Lab 201: Intro to Machine Learning with Spark"},{"id":"2B21B3AYC","name":"Phoenix demo"},{"id":"2BB5CUPUW","name":"Predicting airline delays"},{"id":"2BAVUZ7NA","name":"Sensors \u0026 Machines Predictive Analysis"},{"id":"2BBBW75VS","name":"Single view demo"},{"id":"2BEQE47HR","name":"Tutorial - Hands-on Tour of Apache Spark in 5 Minutes"},{"id":"2A94M5J1Z","name":"Zeppelin Tutorial"},{"id":"2B4TWGC8M","name":"magellan-blog"},{"id":"2B522V3X8","name":"twitter"}]}

View solution in original post

5 REPLIES 5

avatar
Expert Contributor

After doing a bit of investigating I found that I can make a successful POST to the /api/login which results in a JSON response with a ticket.

curl -i --data 'userName=admin&password=password1' -X POST http://sandbox.hortonworks.com:9995/api/login
HTTP/1.1 200 OK
Date: Wed, 17 Aug 2016 09:44:57 GMT
Access-Control-Allow-Origin:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: authorization,Content-Type
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, HEAD, DELETE
Date: Wednesday, August 17, 2016 9:44:57 AM UTC
Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Tue, 16-Aug-2016 09:44:57 GMT
Set-Cookie: JSESSIONID=84983c45-ceca-4db3-a366-9eab27399a68; Path=/; HttpOnly
Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Tue, 16-Aug-2016 09:44:57 GMT
Content-Type: application/json
Date: Wed, 17 Aug 2016 09:44:57 GMT
Content-Length: 118
Server: Jetty(9.2.15.v20160210)


{"status":"OK","message":"","body":{"principal":"admin","ticket":"47a1fe3a-593d-47ce-85bb-f6e7238c6dcb","roles":"[]"}}

Is there anything I can do with the ticket to authenticate against a notebook request?

avatar
Expert Contributor

So after some more investigation and toying around with the API I've found the solution.

The first step is to authentic through the API with a POST to /api/login

curl -i --data 'userName=admin&password=password1' -X POST http://127.0.0.1:9995/api/login

This should return a response like the following:

HTTP/1.1 200 OK
Date: Wed, 17 Aug 2016 10:07:22 GMT
Access-Control-Allow-Origin:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: authorization,Content-Type
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, HEAD, DELETE
Date: Wednesday, August 17, 2016 10:07:22 AM UTC
Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Tue, 16-Aug-2016 10:07:22 GMT
Set-Cookie: JSESSIONID=b1f15e00-4571-4079-a699-338bf619b0c4; Path=/; HttpOnly
Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Tue, 16-Aug-2016 10:07:22 GMT
Content-Type: application/json
Date: Wed, 17 Aug 2016 10:07:22 GMT
Content-Length: 118
Server: Jetty(9.2.15.v20160210)


{"status":"OK","message":"","body":{"principal":"admin","ticket":"47a1fe3a-593d-47ce-85bb-f6e7238c6dcb","roles":"[]"}}

The important thing to see here is the Set-Cookie in the response headers.

Look for the following:

Set-Cookie: JSESSIONID=b1f15e00-4571-4079-a699-338bf619b0c4; Path=/; HttpOnly

Using this cookie we can make authorized request to the API by simply setting this as a cookie in the cURL request.

curl -i -b 'JSESSIONID=b1f15e00-4571-4079-a699-338bf619b0c4; Path=/; HttpOnly' http://sandbox.hortonworks.com:9995/api/notebook

This request should now return a 200 OK response after adding the JSESSIONID cookie.

HTTP/1.1 200 OK
Date: Wed, 17 Aug 2016 10:10:44 GMT
Access-Control-Allow-Origin:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: authorization,Content-Type
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, HEAD, DELETE
Date: Wednesday, August 17, 2016 10:10:44 AM UTC
Content-Type: application/json
Date: Wed, 17 Aug 2016 10:10:44 GMT
Content-Length: 1012
Server: Jetty(9.2.15.v20160210)


{"status":"OK","message":"","body":[{"id":"2BSVACJ42","name":"/demo/note1"},{"id":"2BVBPU1VY","name":"/demo/note2"},{"id":"2APFTN3NY","name":"AON Demo"},{"id":"2ANT56EHN","name":"Australian Dataset (Hive example)"},{"id":"2ANTDG878","name":"Australian Dataset (SparkSQL example)"},{"id":"2B48PF7SN","name":"Hello World Tutorial"},{"id":"2AS5TY6AQ","name":"IoT  Data Analysis (Keynote Demo)"},{"id":"2BFGYS3YT","name":"Lab 101: Intro to Spark with Python"},{"id":"2BJVW65WS","name":"Lab 102: Intro to Spark with Scala"},{"id":"2BNDT63TY","name":"Lab 201: Intro to Machine Learning with Spark"},{"id":"2B21B3AYC","name":"Phoenix demo"},{"id":"2BB5CUPUW","name":"Predicting airline delays"},{"id":"2BAVUZ7NA","name":"Sensors \u0026 Machines Predictive Analysis"},{"id":"2BBBW75VS","name":"Single view demo"},{"id":"2BEQE47HR","name":"Tutorial - Hands-on Tour of Apache Spark in 5 Minutes"},{"id":"2A94M5J1Z","name":"Zeppelin Tutorial"},{"id":"2B4TWGC8M","name":"magellan-blog"},{"id":"2B522V3X8","name":"twitter"}]}

avatar
New Contributor

Just using cookie like zblanco said

avatar
New Contributor

I was able to succesfully post to the api/login to get the cookie and then post a notebook to the user's account using Postman. I am also able to log into the web browser successfully. However, I noticed that the notebooks when I log into Zeppelin through the browser are a different set of notebooks than when I log into zeppelin using the cookie.

avatar
New Contributor

Can you please share how you did it? I´m also trying to replicate it in Postman but so far with no success....