package com.owera.xaps.base.http;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.owera.common.log.Context;
import com.owera.common.util.Cache;
import com.owera.common.util.CacheValue;
import com.owera.xaps.base.Log;
import com.owera.xaps.dbi.Certificate;
import com.owera.xaps.dbi.Certificates;
import com.owera.xaps.tr069.HTTPReqResData;
import com.owera.xaps.tr069.Properties;
import com.owera.xaps.tr069.SessionData;
import com.owera.xaps.tr069.exception.TR069AuthenticationException;
public class Authenticator {
private static Set<String> hostSet = new HashSet<String>();
private static Cache blockedClients = new Cache();
private static int blockedClientsCount = 0;
private static String computeBlockedClientKey(HttpServletRequest req) {
String remoteHost = req.getRemoteHost();
String authorization = req.getHeader("authorization");
if (authorization == null)
return null; // probably a challenge - no blocking at that stage
String username = authorization; // use raw header if basic auth, since the
// header is always the same
int startPos = authorization.indexOf("username=") + 9;
if (startPos > 8) { // digest auth, must extract username, since header
// varies in every request
int endPos = authorization.indexOf("=", startPos);
if (endPos > -1)
username = authorization.substring(startPos, endPos);
else
username = authorization.substring(startPos);
}
return remoteHost + username;
}
public static int getAndResetBlockedClientsCount() {
int tmp = blockedClientsCount;
blockedClientsCount = 0;
return tmp;
}
private static boolean block(HTTPReqResData reqRes, String bcKey) {
if (bcKey != null) {
CacheValue cv = blockedClients.get(bcKey);
if (cv != null) {
int count = (Integer) cv.getObject();
if (count >= 5) {
try {
reqRes.getRes().sendError(HttpServletResponse.SC_UNAUTHORIZED);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
blockedClientsCount++;
return true;
}
}
}
return false;
}
private static void incBlockCounter(String bcKey) {
if (bcKey != null) {
CacheValue cv = blockedClients.get(bcKey);
if (cv != null) {
cv.setObject(((Integer) cv.getObject()) + 1);
} else {
blockedClients.put(bcKey, new CacheValue(1, Cache.SESSION, 5 * 60 * 1000));
}
}
}
public static boolean authenticate(HTTPReqResData reqRes) throws TR069AuthenticationException {
SessionData sessionData = reqRes.getSessionData();
if (sessionData.isAuthenticated()) {
return true;
}
// Code for early return of a non-authorized device with no logging or fuss
// - minimize impact of
// devices which constantly tries to log on to Fusion (without success).
// If a device performs 5 non-authorized login attempts with less than 5
// minutes
// between each attempt, the device will be blocked by this code. The only
// way for the
// device to be allowed into verification is to be silent for more than 5
// minutes.
String bcKey = computeBlockedClientKey(reqRes.getReq());
if (block(reqRes, bcKey))
return false;
// Start of normal authentication procedure
Context.remove(Context.X);
boolean authenticated = true; // default
String auth_method = Properties.getAuthMethod();
try {
if (Properties.isDiscoveryMode()) {
authenticated = BasicAuthenticator.authenticate(reqRes);
} else {
if (auth_method.equalsIgnoreCase("basic"))
authenticated = BasicAuthenticator.authenticate(reqRes);
else if (auth_method.equalsIgnoreCase("digest"))
authenticated = DigestAuthenticator.authenticate(reqRes);
else if (auth_method.equalsIgnoreCase("none")) {
authenticated = true;
Log.debug(Authenticator.class, "No authentication method was required");
} else {
throw new TR069AuthenticationException("The authentication method is " + auth_method + ", but no impl. exist for this method (CPE IP address: " + reqRes.getReq().getRemoteHost() + ")",
null, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
} catch (TR069AuthenticationException ex) {
incBlockCounter(bcKey);
// Something failed in the verification step - the device is not allowed
// or something like that
throw ex;
}
if (authenticated) {
sessionData.setAuthenticated(true);
}
/*
* Morten - jan 2014 - no longer check certificates, since going open source
* if (authenticated) { // Can only happen if verification process is
* completed authenticated = checkCertificate(reqRes); } else { // Challenge
* is sent - not authenticated }
*/
if (authenticated) { // all checks are passed - cleanup
blockedClients.remove(bcKey);
}
return authenticated;
}
@SuppressWarnings("unused")
private static boolean checkCertificate(HTTPReqResData reqRes) {
SessionData sessionData = reqRes.getSessionData();
Certificates certs = reqRes.getSessionData().getDbAccess().getXaps().getCertificates();
Certificate cert = certs.getCertificate(Certificate.CERT_TYPE_PROVISIONING);
if (cert == null || !cert.isDecrypted()) {
Log.error(Authenticator.class, "The authentication was ok, but no Fusion provisioning certificate exists");
sessionData.setAuthenticated(false);
} else if (cert.isTrial()) {
if (cert.getMaxCount() != null) {
if (hostSet.size() <= cert.getMaxCount()) {
hostSet.add(reqRes.getReq().getRemoteHost());
sessionData.setAuthenticated(true);
} else {
Log.error(Authenticator.class, "The authentication was ok, but the Fusion provisioning certificate does not allow more than " + cert.getMaxCount() + " provisioned units");
sessionData.setAuthenticated(false);
}
} else if (cert.getDateLimit() != null) {
// will always add 1 day extra
if (System.currentTimeMillis() <= cert.getDateLimit().getTime() + 1440 * 60 * 1000) {
sessionData.setAuthenticated(true);
} else {
Log.error(Authenticator.class, "The authentication was ok, but the Fusion provisioning certificate expired at " + cert.getDateLimit());
sessionData.setAuthenticated(false);
}
}
} else if (cert.isProductionAndValid()) {
sessionData.setAuthenticated(true);
} else {
Log.error(Authenticator.class, "The authentication was ok, but the Fusion provisioning certificate was not ok (the error is unknown)");
sessionData.setAuthenticated(false);
}
return sessionData.isAuthenticated();
}
}