au.edu.uq.cmm.benny.Benny.java Source code

Java tutorial

Introduction

Here is the source code for au.edu.uq.cmm.benny.Benny.java

Source

/*
* Copyright 2012, CMM, University of Queensland.
*
* This file is part of Benny.
*
* Benny is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Benny is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Benny. If not, see <http://www.gnu.org/licenses/>.
*/

package au.edu.uq.cmm.benny;

import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import au.edu.uq.cmm.aclslib.authenticator.AclsAuthenticator;

/**
 * The Benny servlet provides a simple HTTP-based service for checking a
 * username and password against an ACLS service ... using the ACLS login
 * protocol and a dummy ACLS facility.
 * <p>
 * The credentials can be supplied as URL parameters (e.g. 
 * {@literal "...?user=fred&password=secret"} or as a Authorization
 * header using the Basic authentication scheme.  If neither forms is
 * used, the servlet responds with a WWW-Authenticate challenge.  The
 * response codes are:
 * <li>
 *   <li>200 OK - the credentials were accepted</li>
 *   <li>403 Forbidden - the credentials were rejected</li>
 *   <li>401 Unauthorized - no credentials were supplied</li>
 *   <li>500 Internal Service Error - something went wrong: check the logs.</li>
 * </li>
 * There may be a text or HTML response body, but a client should rely on the
 * response status code when determining the request's outcome.
 * 
 * @author scrawley
 */
@SuppressWarnings("serial")
public class Benny extends HttpServlet {
    private static final Logger LOG = LoggerFactory.getLogger(Benny.class);
    private static final String PROPS_RESOURCE = "/benny.properties";
    private static final Pattern BASIC_AUTH_PATTERN = Pattern.compile("Basic\\s+([a-z0-9+/=]+)\\s*",
            Pattern.CASE_INSENSITIVE);
    private static final Charset UTF_8 = Charset.forName("UTF-8");
    private AclsAuthenticator authenticator;
    private String realm;

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        Properties props = new Properties();
        InputStream is = getClass().getResourceAsStream(PROPS_RESOURCE);
        if (is == null) {
            throw new ServletException("Cannot find resource: " + PROPS_RESOURCE);
        }
        try {
            props.load(is);
        } catch (IOException ex) {
            throw new ServletException("Cannot load the configuration properties in resource " + PROPS_RESOURCE);
        }
        try {
            realm = props.getProperty("benny.realm");
            authenticator = new AclsAuthenticator(props.getProperty("benny.serverHost"),
                    Integer.parseInt(props.getProperty("benny.serverPort")),
                    Integer.parseInt(props.getProperty("benny.serverTimeout")),
                    props.getProperty("benny.dummyFacility"), props.getProperty("benny.localHostId"));
        } catch (Exception ex) {
            throw new ServletException("Cannot instantiate the authenticator");
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String user = req.getParameter("user");
        String password = req.getParameter("password");
        if (user == null && password == null) {
            String[] credentials = getBasicAuthCredentials(req);
            if (credentials == null) {
                resp.setHeader("WWW-Authenticate", "Basic realm=\"" + realm + "\"");
                respond(resp, HttpServletResponse.SC_UNAUTHORIZED, "No credentials provided");
                return;
            }
            user = credentials[0];
            password = credentials[1];
        }
        try {
            LOG.debug("checking user='" + user + "', password='XXXXXX'");
            boolean ok = authenticator.authenticate(user, password, null) != null;
            if (ok) {
                respond(resp, HttpServletResponse.SC_OK, "Credentials accepted");
            } else {
                respond(resp, HttpServletResponse.SC_FORBIDDEN, "Credentials rejected");
            }
        } catch (IOException ex) {
            throw ex;
        } catch (Exception ex) {
            LOG.error("Unexpected exception", ex);
            respond(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Service error");
        }
    }

    private String[] getBasicAuthCredentials(HttpServletRequest req) {
        String authorization = req.getHeader("Authorization");
        if (authorization == null) {
            // No header
            return null;
        }
        Matcher matcher = BASIC_AUTH_PATTERN.matcher(authorization);
        if (matcher.matches()) {
            String base64 = matcher.group(1);
            String userPass = new String(Base64.decodeBase64(base64), UTF_8);
            int colonPos = userPass.indexOf(":");
            if (colonPos <= 0 || colonPos == userPass.length() - 1) {
                // Malformed <user-pass>
                return null;
            } else {
                return new String[] { userPass.substring(0, colonPos), userPass.substring(colonPos + 1) };
            }
        } else {
            // Don't understand this authorization scheme
            return null;
        }
    }

    private void respond(HttpServletResponse resp, int status, String msg) throws IOException {
        resp.setContentType("text/plain");
        resp.setStatus(status);
        Writer w = resp.getWriter();
        try {
            w.write(msg + "\r\n");
        } finally {
            w.close();
        }
    }
}