- whoami
- Simple Handler Example
- Force SSL
- Ratpack Sessions
- Pac4j
- BasicAuth Example
- Twitter Auth Example
- Case Study CellarHQ
Jeff Beck
Engineer at SmartThings
-note Abstract
So you are all excited about this hot framework Ratpack now you are about ready to launch into production. But you need security, I will go over using pac4j with Ratpack to secure your application. Showing integrations with Twitter, Basic Auth, and others. I will also go over a case study of CellarHQ and their security in Ratpack.
This talk and slides are targeted at Ratpack v0.9.15
Specify a handler at the start of the chain that all request will go through.
handlers{
handler {
//All traffic hits this handler first
}
}
-note We can provide a simple handler at the start of the chain that intercepts all traffic and checks for security concerns.
--
handler {
if (request.headers['Authorization'] != "Token faketoken") {
response.status(401)
//We must send some response or the request will hang.
response.send()
} else {
//We can choose to do nothing but allow the next handler in the chain to deal with the request.
next()
}
}
-note Here we are actually checking a token now its a simple hard coded value but you could at this point easily do a check against a DB or other datastore.
--
- Identities
- Roles / Authorization
- Mixed Secure / Insecure Content
- Multiple Authentication Options
--
- Stateless MicroService
- Prototyping
handler {
if (!checkForSSL) {
redirect(301, request.rawUri)
}
next()
}
-note AWS Headers: http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/TerminologyandKeyConcepts.html#x-forwarded-headers
--
Using the ELB for SSL termination, we can easily detect if the request was made with https.
request.headers.get('X-Forwarded-Proto') != 'https'
--
The Session
module provides the basics of sessions as well as a session scoped data store.
compile ratpack.dependency('session')
--
The SessionModule will make sure every request is set up with a session. The MapSessionModule provides an in memory map of sessions and data.
bindings {
add SessionModule
add new MapSessionsModule(1000, 360)
}
-note MapSessionsModule(int maxEntries, int idleTimeoutMinutes)
--
def session = context.get(Session)
//Get Session ID
String sessionId = session.existingId
//Terminate Session
session.terminate()
--
//Put Data
def sessionStorage = context.request.get(SessionStorage)
sessionStorage.put("example", "Galaxy")
//Get Data
sessionStorage.get("example")
--
The Session
module will most likely be changing before the 1.0 release. Moving to promises, something like:
public interface SessionStorage {
Promise<Optional<String>> get(String key);
Promise<Boolean> set(String key, String value);
Promise<Integer> remove(String key);
Promise<Integer> clear();
}
-note We are doing this so session stores can be persistent/expensive stores EX DB, Cassandra, Memcached See: ratpack/ratpack#447
Provides an easy framework to work across many Java security libraries and authentication systems. Such as authenticating with Twitter.
--
There is a pac4j module that ties pac4j into Ratpack well providing some basics you can extend.
--
Authorizer The interface that is used to determine if authentication required and deals with authorization.
You can extend AbstractAuthorizer to just check if authentication is required and not do authorization.
build.gradle
compile ratpack.dependency('pac4j')
compile "org.pac4j:pac4j-http:1.6.0"
--
bindings {
add SessionModule
add new MapSessionsModule(1000, 360)
add new Pac4jModule(new BasicAuthClient(new DumbUsernamePasswordAuthenticator()), new SecureAllAuthorizer())
}
handlers {
handler {
render "Hello World"
}
}
--
build.gradle
compile ratpack.dependency('pac4j')
compile "org.pac4j:pac4j-oauth:1.6.0"
--
ratpack {
bindings {
add SessionModule
add new MapSessionsModule(1000, 360)
add new Pac4jModule(new TwitterClient("key", "secret"), new SecureAllAuthorizer())
}
handlers {
handler {
render "Hello Twitter"
}
}
}
--
--
A online list of your beer cellar.
--
Written is Ratpack
--
- Dual Twitter and Web Form Authentication
- Complex Authorizer
- Handler Decoration
- Uses Roles
Supply their own AuthenticationModule instead of the Pac4j Module.
--
Based around a white list for anonymous action
final static List<String> ANONYMOUS_WHITELIST = [
'',
'about',
/styles\/.*/,
/images\/.*/,
/scripts\/.*/,
/pac4j.*/,
'health-checks'
]
--
boolean isAuthenticationRequired(Context context) {
return !matchesAnyPath(context.request.path, ANONYMOUS_WHITELIST)
}
private boolean matchesAnyPath(String subject, List<String> patterns) {
return matchesAny(subject, patterns.collect { String pattern -> (String) "^${pattern}/?\$"})
}
private boolean matchesAny(String subject, List<String> patterns) {
return patterns.any { String pattern -> subject.matches(pattern) }
}
--
void handleAuthorization(Context context, UserProfile userProfile) throws Exception {
if (matchesAnyPath(context.request.path, ADMIN_ROLE_REQUIRED) && !userHasRole(userProfile, Role.ADMIN)) {
context.redirect('/login?error=' + Messages.UNAUTHORIZED_ERROR)
return
}
context.next()
}
private boolean userHasRole(UserProfile userProfile, Role role) {
return userProfile.roles.contains(role.toString())
}
--
CellarHQ uses HandlerDecorator to apply their login handlers to the chain. The same method used by the Pac4j Module.
-note This is why we don't have to put a login handler directly in the chain it is done for us.
--
CellarHQ uses the AuthSuccessCallback to tie into some of their functions such as setting the default "cellar" to display.