RestSecurityInterceptor.java
package net.bryansaunders.jee6divelog.security.interceptor;
/*
* #%L
* BSNet-DiveLog
* $Id:$
* $HeadURL:$
* %%
* Copyright (C) 2012 Bryan Saunders
* %%
* This program 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.
*
* This program 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 this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import java.util.Date;
import java.util.List;
import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import net.bryansaunders.jee6divelog.model.UserAccount;
import net.bryansaunders.jee6divelog.security.Credentials;
import net.bryansaunders.jee6divelog.security.Identity;
import net.bryansaunders.jee6divelog.service.UserAccountService;
import net.bryansaunders.jee6divelog.service.rest.RestApi;
import net.bryansaunders.jee6divelog.util.SecurityUtils;
import org.jboss.resteasy.annotations.interception.Precedence;
import org.jboss.resteasy.annotations.interception.ServerInterceptor;
import org.jboss.resteasy.core.ResourceMethod;
import org.jboss.resteasy.core.ServerResponse;
import org.jboss.resteasy.spi.Failure;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.interception.PreProcessInterceptor;
import org.jboss.resteasy.util.HttpResponseCodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Security Intercepter for REST API Calls.
*
* @author Bryan Saunders <btsaunde@gmail.com>
*
*/
@ServerInterceptor
@Precedence("SECURITY")
public class RestSecurityInterceptor implements PreProcessInterceptor {
/**
* Logger.
*/
private final Logger logger = LoggerFactory.getLogger(RestSecurityInterceptor.class);
/**
* Identity for current Session.
*/
@Inject
private Identity identity;
/**
* User Account Service.
*/
@Inject
private UserAccountService userAccountService;
/**
* Called before any REST service calls are processed.
*
* @param httpRequest
* Initial HTTP Request
* @param resourceMethod
* Resource Method being Called
* @return Response for the Server, Null if the API call should proceed
* @throws Failure
* Thrown on Failure
* @throws WebApplicationException
* Thrown on Error
*/
@Override
public ServerResponse preProcess(final HttpRequest httpRequest, final ResourceMethod resourceMethod)
throws Failure, WebApplicationException {
final String requestUrl = httpRequest.getUri().getAbsolutePath().toString();
ServerResponse response = null;
String publicApiKey = null;
String requestSignature = null;
this.logger.info("REST API Called: " + requestUrl);
final boolean isSecured = SecurityUtils.isMethodSecure(resourceMethod);
if (isSecured) {
this.logger.debug("Request requires Authentication");
publicApiKey = this.getUserDefinedHeader(httpRequest, RestApi.PUBLIC_KEY_HEADER);
requestSignature = this.getUserDefinedHeader(httpRequest, RestApi.SIGNATURE_HEADER);
this.logger.debug("Using Headers: apiKey:" + publicApiKey + " | signature:" + requestSignature);
// Get UserAccount by Username
if (publicApiKey != null && requestSignature != null) {
this.logger.debug("REST Request Headers Valid");
final UserAccount example = new UserAccount();
example.setPublicApiKey(publicApiKey);
final List<UserAccount> userAccounts = this.userAccountService.findByExample(example);
if (userAccounts != null && userAccounts.size() == 1) {
this.logger.debug("User Found For Key: " + publicApiKey);
UserAccount userAccount = userAccounts.get(0);
// Check Expiration Date
final Date expirationDate = userAccount.getApiKeyExpiration();
if (expirationDate != null && System.currentTimeMillis() < expirationDate.getTime()) {
this.logger.debug("API Key Not Expired");
// Generate Token
final String verb = httpRequest.getHttpMethod();
final String contentType = this.getUserDefinedHeader(httpRequest, HttpHeaders.CONTENT_TYPE);
final String contentMd5 = this.getUserDefinedHeader(httpRequest, RestApi.CONTENT_MD5_HEADER);
final String date = this.getUserDefinedHeader(httpRequest, HttpHeaders.DATE);
final String privateApiKey = userAccount.getPrivateApiKey();
final String expectedSignature = SecurityUtils.generateRestSignature(verb, contentType,
contentMd5, date, requestUrl, privateApiKey);
// Check Tokens
if (expectedSignature.equals(requestSignature)) {
this.logger.debug("Request Signature Valid");
// Set Identity
this.identity.setPublicApiKey(publicApiKey);
this.identity.setPrivateApiKey(privateApiKey);
this.identity.setApiKeyExpiration(expirationDate);
this.identity.setPermissions(userAccount.getPermissions());
this.identity.setRoles(userAccount.getRoles());
this.identity.setStatus(Identity.LOGGED_IN);
final Credentials credentials = new Credentials();
credentials.setUsername(userAccount.getEmail());
credentials.setPassword(userAccount.getPassword());
this.identity.setCredentials(credentials);
// Return null
response = null;
} else {
response = this.buildResponse(HttpResponseCodes.SC_UNAUTHORIZED, "Invalid Signature");
}
} else {
response = this.buildResponse(HttpResponseCodes.SC_UNAUTHORIZED, "Key Expired");
}
} else {
response = this.buildResponse(HttpResponseCodes.SC_UNAUTHORIZED, "Invalid User");
}
} else {
response = this.buildResponse(HttpResponseCodes.SC_BAD_REQUEST, "Missing Headers");
}
}
return response;
}
/**
* Build a ServerResponse Message.
*
* @param statusCode
* Response Status
* @param message
* Response Message
* @return ServerResponse
*/
private ServerResponse buildResponse(final int statusCode, final String message) {
this.logger.info("Creating REST Response: " + statusCode + " - " + message);
final ServerResponse response = new ServerResponse();
response.setStatus(statusCode);
response.setEntity(message);
return response;
}
/**
* Gets a User Defined Header.
*
* @param httpRequest
* HTTP Request with Headers
* @param headerName
* Name of User Defined Header
* @return Header Value if Found, null if not found
*/
private String getUserDefinedHeader(final HttpRequest httpRequest, final String headerName) {
String headerValue = null;
final HttpHeaders headers = httpRequest.getHttpHeaders();
final List<String> headerList = headers.getRequestHeader(headerName);
if (headerList != null && !headerList.isEmpty()) {
headerValue = headerList.get(0);
}
return headerValue;
}
}