RESTful API

Table of Contents

Purpose

Applications need to programmatically access CAS. Generally, proxying works for this. However, there are cases where an application needs to access a resource as itself, in which case proxying doesn't make any sense.

At Rutgers, we've implemented a relatively "heavyweight" SOAP based service via Axis. We're now looking at complementing that with a lightweight resource-driven architecture. This page details that proposed work.

This API works to expose a way to RESTfully obtain a Ticket Granting Ticket resource and then use that to obtain a Service Ticket.

Protocol

The RESTful API follows the same basic protocol as the original CAS2 protocol, augmented with some additional well-defined resource urls (though the protocol doesn't change so it should be just as secure).

Ticket Granting Ticket

The Ticket Granting Ticket is an exposed resource. It has a unique URI.

Request for a Ticket Granting Ticket Resource

POST /cas/tickets HTTP/1.0

username=battags&password=password&additionalParam1=paramvalue

Response for a Ticket Granting Ticket Resource

Successful Response

201 Created
Location: http://www.whatever.com/cas/tickets/{TGT id}

Unsuccessful Responses

If incorrect credentials are sent, CAS will respond with a 400 Bad Request error (will also respond for missing parameters, etc.). If you send a media type it does not understand, it will send the 415 Unsupported Media Type

Request for a Service Ticket

POST /cas/tickets/{TGT id} HTTP/1.0

service={form encoded parameter for the service url}

Response for Service Ticket

Successful Response

200 OK

ST-1-FFDFHDSJKHSDFJKSDHFJKRUEYREWUIFSD2132

Unsuccessful Responses

If parameters are missing, etc. CAS will send a 400 Bad Request. If you send a media type it does not understand, it will send the 415 Unsupported Media Type.

Logout of the Service

To log out, you merely need to delete the ticket.

DELETE /cas/tickets/TGT-fdsjfsdfjkalfewrihfdhfaie HTTP/1.0

Configuration

By default the CAS RESTful API is configured in the restlet-servlet.xml, which contains the routing for the tickets. It also defines the resources that will resolve the URLs. The TicketResource defined by default (which can be extended) accepts username/password.

To turn on the RESTful API, add the following to the web.xml:

<servlet>
	<servlet-name>restlet</servlet-name>
	<servlet-class>com.noelios.restlet.ext.spring.RestletFrameworkServlet</servlet-class>
	<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>restlet</servlet-name>
	<url-pattern>/v1/*</url-pattern>
</servlet-mapping>

Note, that in the above configuration example, we are explicitly versioning the RESTful API, so things would be accessed via /cas/v1/tickets/*, etc.

In the pom.xml file include the following:

<dependency>
	<groupId>org.jasig.cas</groupId>
	<artifactId>cas-server-integration-restlet</artifactId>
	<version>3.3-RC3</version>
	<type>jar</type>
</dependency>

where 3.3-RC3 is the version of CAS you are currently using (3.3 or higher).

Python REST Client Example

#!/usr/bin/python
import os.path
import httplib, urllib, urllib2, cookielib

# 1. Grab the Ticket Granting Ticket (TGT)

params = urllib.urlencode({'username': 'battags', 'password': 'password'})
headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
conn = httplib.HTTPSConnection("cas.acme.com")
conn.request("POST", "/cas/v1/tickets/", params, headers)
response = conn.getresponse()
print response.status, response.reason
data = response.read()
location = response.getheader('location')
#  Pull off the TGT from the end of the location, this works for CAS 3.3-FINAL
tgt = location[location.rfind('/') + 1:]
conn.close()

print location
print tgt
print "***"

# 2. Grab a service ticket (ST) for a CAS protected service

document = '/secure/blah.txt'
service  = 'http://docs.acme.com%s' % (document)

params = urllib.urlencode({'service': service })
conn = httplib.HTTPSConnection("cas.acme.com")
conn.request("POST", "/cas/v1/tickets/%s" % ( tgt ), params, headers)
response = conn.getresponse()
print response.status, response.reason
st = response.read()
conn.close()

print "service: %s" % (service)
print "st     : %s" % (st)
print "***"

# 3. Grab the protected document

#httplib.HTTPConnection.debuglevel = 1

url  = "%s?ticket=%s" % ( service, st )  # Use &ticket if service already has query parameters
print "url    : %s" % (url)

cj = cookielib.CookieJar()

# no proxies please
no_proxy_support = urllib2.ProxyHandler({})
# we need to handle session cookies AND redirects
cookie_handler = urllib2.HTTPCookieProcessor(cj)

opener = urllib2.build_opener(no_proxy_support, cookie_handler)
urllib2.install_opener(opener)
protected_data = urllib2.urlopen(url).read()
print protected_data

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.