/*******************************************************************************
* Copyright (c) 2011, 2016 Eurotech and/or its affiliates
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eurotech
*******************************************************************************/
package org.eclipse.kura.web;
import java.io.IOException;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.util.StringTokenizer;
import javax.security.auth.Subject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.eclipse.kura.crypto.CryptoService;
import org.eclipse.kura.web.server.util.ServiceLocator;
import org.eclipse.kura.web.shared.GwtKuraException;
import org.osgi.service.http.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* HttpContext that delegates calls to getMimeType() and getResource(), but provides
* HTTP Basic authentication by authenticating a user against an LDAP directory. For
* the request to be accepted, the user has to be a member of a privileged group.
*/
public class SecureBasicHttpContext implements HttpContext
{
// Logger
private static Logger s_logger = LoggerFactory.getLogger(SecureBasicHttpContext.class);
/**
* The HttpContext where we delegate calls to getResource() and getMimeType()
*/
private final HttpContext delegate;
private AuthenticationManager m_authMgr;
private String m_appRoot;
public SecureBasicHttpContext(HttpContext delegate,
AuthenticationManager authMgr) {
this.delegate = delegate;
m_authMgr = authMgr;
m_appRoot = Console.getApplicationRoot();
}
public String getMimeType(String name) {
return delegate.getMimeType(name);
}
public URL getResource(String name) {
return delegate.getResource(name);
}
/**
* Provides Basic authentication over HTTPS.
*/
public synchronized boolean handleSecurity(HttpServletRequest request,
HttpServletResponse response)
throws IOException
{
response.setHeader("X-FRAME-OPTIONS", "SAMEORIGIN");
response.setHeader("X-XSS-protection", "1; mode=block");
response.setHeader("X-Content-Type-Options", "nosniff");
response.setHeader("Cache-Control", "no-cache,no-store");
response.setHeader("Pragma", "no-cache");
// If a trailing "/" is used when accesssing the app, redirect
if (request.getRequestURI().equals(m_appRoot + "/")) {
response.sendRedirect(m_appRoot);
}
// If using root context, redirect
if (request.getRequestURI().equals("/")) {
response.sendRedirect(m_appRoot);
}
HttpSession session = request.getSession(false);
if (session != null){
String logout = (String)session.getAttribute("logout");
if (logout != null) {
session.removeAttribute("logout");
session.invalidate();
return failAuthorization(response);
}
}
String authHeader = request.getHeader("Authorization");
if (authHeader == null) {
s_logger.debug("Missing 'Authorization' HTTP header");
return failAuthorization(response);
}
StringTokenizer tokens = new StringTokenizer(authHeader);
String authScheme = tokens.nextToken();
if (!"Basic".equals(authScheme)) {
s_logger.error("The authentication scheme is not 'Basic'");
return failAuthorization(response);
}
String base64 = tokens.nextToken();
String credentials = null;
try {
CryptoService cryptoService = ServiceLocator.getInstance().getService(CryptoService.class);
credentials = cryptoService.decodeBase64(base64);
} catch (GwtKuraException e) {
throw new IOException(e.getMessage());
} catch (NoSuchAlgorithmException e) {
throw new IOException(e.getMessage());
}
int colon = credentials.indexOf(':');
String userid = credentials.substring(0, colon);
String password = credentials.substring(colon + 1);
Subject subject = login(request, userid, password);
if (subject == null) {
return failAuthorization(response);
}
request.setAttribute(HttpContext.REMOTE_USER, null);
request.setAttribute(HttpContext.AUTHENTICATION_TYPE, request.getAuthType());
request.setAttribute(HttpContext.AUTHORIZATION, null);
return true;
}
/**
* Sets the correct HTTP Header to indicate that a request has failed
* @param response
* @return always false
*/
private boolean failAuthorization(HttpServletResponse response) {
response.setHeader("WWW-Authenticate", "Basic realm=\"Secure Area\"");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
/**
* Authenticates a user against an LDAP directory if he does not have an active session.
* @param request
* @param userid
* @param password
* @return A Subject Object if the credentials are valid, null otherwise.
*/
private Subject login(HttpServletRequest request,
String userid,
String password)
{
Subject subject = null;
HttpSession session = request.getSession(true);
//subject = (Subject) session.getAttribute("subject");
// if (subject != null) {
// // The user has already been authenticated
// return subject;
// }
subject = authorize(userid, password);
session.setAttribute("subject", subject);
session.setAttribute("username", userid);
return subject;
}
/**
* Authenticates a user against an SQL DB. The credentials have to be
* valid and the user has to be a member of a privileged group in order for him
* to be authorized
*
* @param userid
* @param password
* @return An empty Subject if the user is authorized, null otherwise.
*/
private Subject authorize(String userid, String password)
{
s_logger.debug("Authenticating user [{}]", userid);
try {
if (m_authMgr.authenticate(userid, password)) {
// TODO : We are temporarily returning an empty Subject,
// but we should return something more significant
return new Subject();
}
}
catch (Exception e) {
s_logger.error("Error during authentication", e);
}
return null;
}
}