/*
* Copyright 2015-Present Entando Inc. (http://www.entando.com) All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License for more
* details.
*/
package org.entando.entando.aps.system.services.oauth;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthConsumer;
import net.oauth.OAuthException;
import net.oauth.OAuthMessage;
import net.oauth.OAuthProblemException;
import net.oauth.OAuthValidator;
import net.oauth.SimpleOAuthValidator;
import net.oauth.server.OAuthServlet;
import org.apache.commons.codec.digest.DigestUtils;
import org.entando.entando.aps.system.services.oauth.model.ConsumerRecordVO;
import org.entando.entando.aps.system.services.oauth.model.EntandoOAuthAccessor;
import org.entando.entando.aps.system.services.oauth.model.TokenUpdaterThread;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.agiletec.aps.system.SystemConstants;
import com.agiletec.aps.system.common.AbstractService;
import com.agiletec.aps.system.common.FieldSearchFilter;
import com.agiletec.aps.system.exception.ApsSystemException;
import com.agiletec.aps.system.services.user.UserDetails;
import com.agiletec.aps.util.DateConverter;
/**
* Manager of consumers, access token (stored in database and in local cache)
* and request tokens (stored in local cache).
* Special thanks to Praveen Alavilli and OAuth examples.
* http://oauth.googlecode.com/svn/code/java/example/oauth-provider/src/net/oauth/example/provider/core/SampleOAuthProvider.java
* @author Praveen Alavilli - E.Santoboni
*/
public class OAuthConsumerManager extends AbstractService implements IOAuthConsumerManager {
private static final Logger _logger = LoggerFactory.getLogger(OAuthConsumerManager.class);
public void init() throws Exception {
_logger.debug("{} ready", this.getClass().getName());
}
protected void release() {
super.release();
this.getConsumers().clear();
this.getUnauthorizedTokensCache().clear();
this.getAuthorizedTokensCache().clear();
}
public OAuthConsumer getConsumer(OAuthMessage requestMessage) throws IOException, OAuthProblemException {
String consumerKey = requestMessage.getConsumerKey();
OAuthConsumer consumer = null;
try {
consumer = this.getConsumers().get(consumerKey);
if (consumer == null) {
consumer = this.getConsumerDAO().getConsumer(consumerKey);
if (null != consumer) {
this.getConsumers().put(consumerKey, consumer);
}
}
} catch (Throwable t) {
_logger.error("Error extracting consumer by key '{}'", consumerKey, t);
//ApsSystemUtils.logThrowable(t, this, "getConsumer", "Error extracting consumer by key '" + consumerKey + "'");
throw new RuntimeException("Error extracting consumer by key '" + consumerKey + "'");
}
if (consumer == null) {
OAuthProblemException problem = new OAuthProblemException("Invalid Consumer - key '" + consumerKey + "'");
throw problem;
}
return consumer;
}
public synchronized OAuthAccessor getAuthorizedAccessor(OAuthMessage requestMessage)
throws IOException, OAuthProblemException {
EntandoOAuthAccessor accessor = null;
String consumerToken = requestMessage.getToken();
try {
OAuthConsumer consumer = this.getConsumer(requestMessage);
accessor = this.getAuthorizedTokensCache().get(consumerToken);
if (null == accessor) {
accessor = this.getTokenDAO().getAccessor(consumerToken, consumer);
if (null != accessor) {
this.getAuthorizedTokensCache().put(consumerToken, accessor);
}
}
if (null != accessor) {
accessor.setLastAccess(new Date());
TokenUpdaterThread thread = new TokenUpdaterThread(consumerToken, this.getTokenTimeValidity(), this.getTokenDAO());
String threadName = "OauthTokenUpdater_" + DateConverter.getFormattedDate(new Date(), "yyyyMMddHHmmss");
thread.setName(threadName);
thread.start();
}
} catch (OAuthProblemException t) {
throw t;
} catch (IOException io) {
throw io;
} catch (Throwable t) {
_logger.error("Error extracting access token", t);
//ApsSystemUtils.logThrowable(t, this, "getAuthorizedAccessor", "Error extracting access token");
throw new RuntimeException("Error extracting access token");
}
if (accessor == null) {
throw new OAuthProblemException("token_expired");
}
return accessor;
}
public synchronized OAuthAccessor getAccessor(OAuthMessage requestMessage)
throws IOException, OAuthProblemException {
String consumerToken = requestMessage.getToken();
OAuthAccessor accessor = this.getUnauthorizedTokensCache().get(consumerToken);
if (accessor == null) {
throw new OAuthProblemException("token_expired");
}
return accessor;
}
/**
* Set the access token
*/
public synchronized void markAsAuthorized(OAuthAccessor accessor, String username) throws OAuthException {
try {
String requestToken = accessor.requestToken;
OAuthAccessor unauthorizedAccessor = this.getUnauthorizedTokensCache().get(requestToken);
if (null == unauthorizedAccessor) {
throw new OAuthException("Invalid token for user '" + username + "'");
}
unauthorizedAccessor.setProperty("user", username);
unauthorizedAccessor.setProperty("authorized", Boolean.TRUE);
} catch (OAuthException oe) {
throw oe;
} catch (Throwable t) {
_logger.error("Error while mark As Authorized request token", t);
//ApsSystemUtils.logThrowable(t, this, "markAsAuthorized", "Error while mark As Authorized request token");
}
}
public synchronized void generateRequestToken(OAuthAccessor accessor) throws OAuthException {
try {
String consumerKey = (String) accessor.consumer.getProperty("name");
String token_data = consumerKey + System.nanoTime();
String token = DigestUtils.md5Hex(token_data);
String secret_data = consumerKey + System.nanoTime() + token;
String secret = DigestUtils.md5Hex(secret_data);
accessor.requestToken = token;
accessor.tokenSecret = secret;
accessor.accessToken = null;
this.getUnauthorizedTokensCache().put(token, accessor);
} catch (Throwable t) {
_logger.error("Error generating request token", t);
//ApsSystemUtils.logThrowable(t, this, "generateRequestToken", "Error generating request token");
}
}
public synchronized void generateAccessToken(OAuthAccessor accessor) throws OAuthException {
try {
String requestToken = accessor.requestToken;
OAuthAccessor unauthorizedAccessor = this.getUnauthorizedTokensCache().get(requestToken);
if (null == unauthorizedAccessor) {
throw new OAuthException("Invalid token");
}
Object authorized = unauthorizedAccessor.getProperty("authorized");
if (null == authorized || !authorized.equals(Boolean.TRUE)) {
throw new OAuthException("Unauthorized token");
}
String username = (String) unauthorizedAccessor.getProperty("user");
String consumerKey = (String) accessor.consumer.getProperty("name");
String token_data = consumerKey + System.nanoTime();
String token = DigestUtils.md5Hex(token_data);
this.getUnauthorizedTokensCache().remove(requestToken);
accessor.requestToken = null;
accessor.accessToken = token;
accessor.setProperty("user", username);
accessor.setProperty("authorized", Boolean.TRUE);
this.getTokenDAO().addAccessToken(accessor);
} catch (Throwable t) {
_logger.error("Error generating access token", t);
//ApsSystemUtils.logThrowable(t, this, "generateAccessToken", "Error generating access token");
}
}
public void handleException(Exception e, HttpServletRequest request,
HttpServletResponse response, boolean sendBody)
throws IOException, ServletException {
String realm = (request.isSecure()) ? "https://" : "http://";
realm += request.getLocalName();
OAuthServlet.handleException(response, e, realm, sendBody);
}
public OAuthValidator getOAuthValidator() {
return new SimpleOAuthValidator();
}
public void deleteMyAccessToken(Properties properties) throws Throwable {
try {
UserDetails user = (UserDetails) properties.get(SystemConstants.API_USER_PARAMETER);
if (null == user) {
_logger.info("Unable to delete access token form null user");
}
String username = user.getUsername();
String accessToken = properties.getProperty("accessToken");
OAuthConsumer consumer = (OAuthConsumer) properties.get(SystemConstants.API_OAUTH_CONSUMER_PARAMETER);
String consumerKey = (null != consumer) ? consumer.consumerKey : null;
this.getTokenDAO().deleteAccessToken(username, accessToken, consumerKey);
} catch (Throwable t) {
_logger.error("Error deleting access token", t);
//ApsSystemUtils.logThrowable(t, this, "deleteMyAccessToken", "Error deleting access token");
}
}
public ConsumerRecordVO getConsumerRecord(String consumerKey) throws ApsSystemException {
ConsumerRecordVO consumer = null;
try {
consumer = this.getConsumerDAO().getConsumerRecord(consumerKey);
} catch (Throwable t) {
_logger.error("Error extracting consumer record by key {}", consumerKey, t);
//ApsSystemUtils.logThrowable(t, this, "getConsumerRecord", "Error extracting consumer record by key " + consumerKey);
throw new ApsSystemException("Error extracting consumer record by key " + consumerKey, t);
}
return consumer;
}
public void addConsumer(ConsumerRecordVO consumer) throws ApsSystemException {
try {
this.getConsumerDAO().addConsumer(consumer);
} catch (Throwable t) {
_logger.error("Error adding consumer", t);
//ApsSystemUtils.logThrowable(t, this, "addConsumer", "Error adding consumer");
throw new ApsSystemException("Error adding consumer", t);
}
}
public void updateConsumer(ConsumerRecordVO consumer) throws ApsSystemException {
try {
this.getConsumerDAO().updateConsumer(consumer);
} catch (Throwable t) {
_logger.error("Error updating consumer", t);
//ApsSystemUtils.logThrowable(t, this, "updateConsumer", "Error updating consumer");
throw new ApsSystemException("Error updating consumer", t);
}
}
public void deleteConsumer(String consumerKey) throws ApsSystemException {
try {
this.getConsumerDAO().deleteConsumer(consumerKey);
} catch (Throwable t) {
_logger.error("Error deleting consumer record by key {}", consumerKey, t);
//ApsSystemUtils.logThrowable(t, this, "getConsumerRecord", "Error deleting consumer record by key " + consumerKey);
throw new ApsSystemException("Error deleting consumer record by key " + consumerKey, t);
}
}
public List<String> getConsumerKeys(FieldSearchFilter[] filters) throws ApsSystemException {
List<String> consumerKeys = null;
try {
consumerKeys = this.getConsumerDAO().getConsumerKeys(filters);
} catch (Throwable t) {
_logger.error("Error extracting consumer keys", t);
//ApsSystemUtils.logThrowable(t, this, "getConsumerKeys", "Error extracting consumer keys");
throw new ApsSystemException("Error extracting consumer keys", t);
}
return consumerKeys;
}
public Map<String, Integer> getTokenOccurrencesByConsumer() throws ApsSystemException {
Map<String, Integer> occurrences = null;
try {
occurrences = this.getTokenDAO().getOccurrencesByConsumer();
} catch (Throwable t) {
_logger.error("Error extracting token occurrences", t);
//ApsSystemUtils.logThrowable(t, this, "getTokenOccurrencesByConsumer", "Error extracting token occurrences");
throw new ApsSystemException("Error extracting token occurrences", t);
}
return occurrences;
}
protected Map<String, OAuthConsumer> getConsumers() {
return _consumers;
}
protected void setConsumers(Map<String, OAuthConsumer> consumers) {
this._consumers = consumers;
}
protected Map<String, EntandoOAuthAccessor> getAuthorizedTokensCache() {
return _authorizedTokensCache;
}
protected Map<String, OAuthAccessor> getUnauthorizedTokensCache() {
return _unauthorizedTokensCache;
}
protected Integer getTokenTimeValidity() {
if (null == this._tokenTimeValidity) {
return 365;
}
return _tokenTimeValidity;
}
public void setTokenTimeValidity(Integer tokenTimeValidity) {
this._tokenTimeValidity = tokenTimeValidity;
}
protected IOAuthConsumerDAO getConsumerDAO() {
return _consumerDAO;
}
public void setConsumerDAO(IOAuthConsumerDAO consumerDAO) {
this._consumerDAO = consumerDAO;
}
protected IOAuthTokenDAO getTokenDAO() {
return _tokenDAO;
}
public void setTokenDAO(IOAuthTokenDAO tokenDAO) {
this._tokenDAO = tokenDAO;
}
private Map<String, OAuthConsumer> _consumers = new HashMap<String, OAuthConsumer>();
private Map<String, EntandoOAuthAccessor> _authorizedTokensCache = new HashMap<String, EntandoOAuthAccessor>();
private Map<String, OAuthAccessor> _unauthorizedTokensCache = new HashMap<String, OAuthAccessor>();
private Integer _tokenTimeValidity;
private IOAuthConsumerDAO _consumerDAO;
private IOAuthTokenDAO _tokenDAO;
}