package edu.gatech.oad.rocket.findmythings.server.security;
import edu.gatech.oad.rocket.findmythings.server.db.DatabaseService;
import edu.gatech.oad.rocket.findmythings.server.model.MessageBean;
import edu.gatech.oad.rocket.findmythings.server.util.HTTP;
import edu.gatech.oad.rocket.findmythings.server.util.Messages;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import com.google.appengine.labs.repackaged.org.json.JSONObject;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.Locale;
import java.util.logging.Logger;
public final class BearerTokenAuthenticatingFilter extends AuthenticatingFilter {
private static final String AUTHORIZATION_HEADER = "X-Authorization";
private static final String AUTHORIZATION_PARAM = "fmthings_auth";
private static final String AUTHORIZATION_SCHEME = "FMTTOKEN";
private static final String AUTHORIZATION_SCHEME_ALT = "Basic";
@SuppressWarnings("unused")
private static final Logger LOGGER = Logger.getLogger(BearerTokenAuthenticatingFilter.class.getName());
private String usernameParam;
private String passwordParam;
public void setUsernameParam(String usernameParam) {
this.usernameParam = usernameParam;
}
public void setPasswordParam(String passwordParam) {
this.passwordParam = passwordParam;
}
String getUsernameParam() {
return usernameParam;
}
String getPasswordParam() {
return passwordParam;
}
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
if (isLoginRequest(request, response)) {
String JSON = request.getReader().readLine();
JSONObject contents = new JSONObject(JSON);
String username = (String) contents.get(getUsernameParam());
String password = (String) contents.get(getPasswordParam());
return createToken(username, password, request, response);
} else {
String authorizeHeader = getAuthorizationHeader(request);
String authorizeParameter = getAuthorizationParameter(request);
String[] principlesAndCredentials;
if (isHeaderLoginAttempt(authorizeHeader)) {
principlesAndCredentials = this.getHeaderPrincipalsAndCredentials(authorizeHeader);
} else if (isParameterLoginAttempt(authorizeParameter)) {
principlesAndCredentials = this.getParameterPrincipalsAndCredentials(authorizeParameter);
} else {
return null;
}
if (principlesAndCredentials == null || principlesAndCredentials.length != 2) {
return null;
}
String username = principlesAndCredentials[0];
String token = principlesAndCredentials[1];
return new BearerToken(username, token);
}
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
boolean authHasToken = hasAuthorizationToken(request);
boolean isLogin = isLoginRequest(request, response);
if (authHasToken || isLogin) {
return executeLogin(request, response);
} else {
HTTP.writeError(response, HTTP.Status.UNAUTHORIZED);
return false;
}
}
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
if (isLoginRequest(request, response)) {
String email = (String)subject.getPrincipal();
String newToken = DatabaseService.ofy().createAuthenticationToken(email);
HTTP.writeAsJSON(response,
MessageBean.STATUS, HTTP.Status.OK.toInt(),
MessageBean.MESSAGE, Messages.Status.OK.toString(),
MessageBean.TOKEN, newToken,
MessageBean.EMAIL, email);
return false;
} else {
return true;
}
}
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
if (isLoginRequest(request, response)) {
HTTP.writeAsJSON(response,
MessageBean.STATUS, HTTP.Status.UNAUTHORIZED.toInt(),
MessageBean.MESSAGE, Messages.Status.UNAUTHORIZED.toString(),
MessageBean.FAILURE_REASON, Messages.Login.getMessage(e));
} else {
HTTP.writeError(response, HTTP.Status.UNAUTHORIZED);
}
return false;
}
@Override
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return isLoginRequest(request, response) && hasAuthorizationToken(request) || super.onPreHandle(request, response, mappedValue);
}
boolean hasAuthorizationToken(ServletRequest request) {
String authorizeHeader = getAuthorizationHeader(request);
String authorizeParam = getAuthorizationParameter(request);
return isHeaderLoginAttempt(authorizeHeader) || isParameterLoginAttempt(authorizeParam);
}
String getAuthorizationHeader(ServletRequest request) {
HttpServletRequest httpRequest = WebUtils.toHttp(request);
return httpRequest.getHeader(AUTHORIZATION_HEADER);
}
String getAuthorizationParameter(ServletRequest request) {
HttpServletRequest httpRequest = WebUtils.toHttp(request);
return WebUtils.getCleanParam(httpRequest, AUTHORIZATION_PARAM);
}
boolean isHeaderLoginAttempt(String authorizeHeader) {
if (authorizeHeader == null) return false;
String authorizeScheme = AUTHORIZATION_SCHEME.toLowerCase(Locale.ENGLISH);
String authorizeSchemeAlt = AUTHORIZATION_SCHEME_ALT.toLowerCase(Locale.ENGLISH);
String test = authorizeHeader.toLowerCase(Locale.ENGLISH);
return test.startsWith(authorizeScheme) || test.startsWith(authorizeSchemeAlt);
}
boolean isParameterLoginAttempt(String authorizeParam) {
return (authorizeParam != null) && Base64.isBase64(authorizeParam.getBytes());
}
String[] getHeaderPrincipalsAndCredentials(String authorizeHeader) {
if (authorizeHeader == null) {
return null;
}
String[] authTokens = authorizeHeader.split(" ");
if (authTokens == null || authTokens.length < 2) {
return null;
}
return getPrincipalsAndCredentials(authTokens[1]);
}
String[] getParameterPrincipalsAndCredentials(String authorizeParam) {
if (authorizeParam == null) {
return null;
}
return getPrincipalsAndCredentials(authorizeParam);
}
String[] getPrincipalsAndCredentials(String encoded) {
String decoded = Base64.decodeToString(encoded);
return decoded.split(":", 2);
}
String getUsername(ServletRequest request) {
return WebUtils.getCleanParam(request, getUsernameParam());
}
String getPassword(ServletRequest request) {
return WebUtils.getCleanParam(request, getPasswordParam());
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
return !(!isLoginRequest(request, response) && isPermissive(mappedValue) && hasAuthorizationToken(request)) && (super.isAccessAllowed(request, response, mappedValue) || (!isLoginRequest(request, response) && isPermissive(mappedValue) && !hasAuthorizationToken(request)));
}
}