All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jetdrone.vertx.yoke.middleware.BasicAuth Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
/**
 * Copyright 2011-2014 the original author or authors.
 */
package com.jetdrone.vertx.yoke.middleware;

import com.jetdrone.vertx.yoke.Middleware;
import org.jetbrains.annotations.NotNull;
import org.vertx.java.core.Handler;
import org.vertx.java.core.json.JsonObject;

import javax.xml.bind.DatatypeConverter;

/**
 * # BasicAuth
 *
 * Enfore basic authentication by providing a AuthHandler.handler(user, pass), which must return true in order to gain
 * access. Populates request.user. The final alternative is simply passing username / password strings.
 */
public class BasicAuth extends Middleware {

    /**
     * Realm name for the application
     */
    private final String realm;

    /**
     * AuthHandler for validating this instance authentication requests.
     */
    private final AuthHandler authHandler;

    /**
     * Creates a new BasicAuth middleware with a master username / password and a given realm.
     * 
     *   Yoke yoke = new Yoke(...);
     *     yoke.use("/admin", new BasicAuth("admin", "s3cr37",
     *         "MyApp Auth Required"));
     * 
* * @param username the security principal user name * @param password the security principal password * @param realm the security realm */ public BasicAuth(@NotNull final String username, @NotNull final String password, @NotNull String realm) { this.realm = realm; authHandler = new AuthHandler() { @Override public void handle(String _username, String _password, Handler result) { boolean success = username.equals(_username) && password.equals(_password); if (success) { result.handle(new JsonObject().putString("username", _username)); } else { result.handle(null); } } }; } /** * Creates a new BasicAuth middleware with a master username / password. By default the realm will be `Authentication required`. * *
     *       Yoke yoke = new Yoke(...);
     *       yoke.use("/admin", new BasicAuth("admin", "s3cr37"));
     * 
* * @param username the security principal user name * @param password the security principal password */ public BasicAuth(@NotNull String username, @NotNull String password) { this (username, password, "Authentication required"); } /** * Creates a new BasicAuth middleware with a AuthHandler and a given realm. * *
     *       Yoke yoke = new Yoke(...);
     *       yoke.use("/admin", new AuthHandler() {
     *         public void handle(String user, String password, Handler next) {
     *           // a better example would be fetching user from a DB
     *           if ("user".equals(user) && "pass".equals(password)) {
     *             next.handle(true);
     *           } else {
     *             next.handle(false);
     *           }
     *         }
     *       }, "My App Auth");
     * 
* * @param authHandler the authentication handler * @param realm the security realm */ public BasicAuth(@NotNull String realm, @NotNull AuthHandler authHandler) { this.realm = realm; this.authHandler = authHandler; } /** * Creates a new BasicAuth middleware with a AuthHandler. * *
     *       Yoke yoke = new Yoke(...);
     *       yoke.use("/admin", new AuthHandler() {
     *         public void handle(String user, String password, Handler next) {
     *           // a better example would be fetching user from a DB
     *           if ("user".equals(user) && "pass".equals(password)) {
     *             next.handle(true);
     *           } else {
     *             next.handle(false);
     *           }
     *         }
     *       });
     * 
* @param authHandler the authentication handler */ public BasicAuth(@NotNull AuthHandler authHandler) { this("Authentication required", authHandler); } /** * Handle all forbidden errors, in this case we need to add a special header to the response * * @param request yoke request * @param next middleware to be called next */ private void handle401(final YokeRequest request, final Handler next) { YokeResponse response = request.response(); response.putHeader("WWW-Authenticate", "Basic realm=\"" + getRealm(request) + "\""); response.setStatusCode(401); next.handle("No authorization token"); } @Override public void handle(@NotNull final YokeRequest request, @NotNull final Handler next) { String authorization = request.getHeader("authorization"); if (authorization == null) { handle401(request, next); } else { final String user; final String pass; final String scheme; try { String[] parts = authorization.split(" "); scheme = parts[0]; String[] credentials = new String(DatatypeConverter.parseBase64Binary(parts[1])).split(":"); user = credentials[0]; // when the header is: "user:" pass = credentials.length > 1 ? credentials[1] : null; } catch (ArrayIndexOutOfBoundsException e) { handle401(request, next); return; } catch (IllegalArgumentException | NullPointerException e) { // IllegalArgumentException includes PatternSyntaxException next.handle(e); return; } if (!"Basic".equals(scheme)) { next.handle(400); } else { authHandler.handle(user, pass, new Handler() { @Override public void handle(JsonObject json) { if (json != null) { request.put("user", user); next.handle(null); } else { handle401(request, next); } } }); } } } /** * Get the realm for this instance * * The usecase is a multitenant app where I want different realms for paths like /foo/homepage and /bar/homepage. * * @param request http yoke request * @return realm name */ public String getRealm(@NotNull YokeRequest request) { return realm; } }