/*
* Copyright (c) 2013, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.wso2.carbon.identity.oauth2.dao;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
import org.wso2.carbon.identity.base.IdentityException;
import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil;
import org.wso2.carbon.identity.core.util.IdentityUtil;
import org.wso2.carbon.identity.oauth.common.OAuthConstants;
import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration;
import org.wso2.carbon.identity.oauth.tokenprocessor.PlainTextPersistenceProcessor;
import org.wso2.carbon.identity.oauth.tokenprocessor.TokenPersistenceProcessor;
import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
import org.wso2.carbon.identity.oauth2.model.AccessTokenDO;
import org.wso2.carbon.identity.oauth2.model.AuthzCodeDO;
import org.wso2.carbon.identity.oauth2.model.RefreshTokenValidationDataDO;
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import java.sql.Connection;
import java.sql.DataTruncation;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
/**
* Data Access Layer functionality for Token management in OAuth 2.0 implementation. This includes
* storing and retrieving access tokens, authorization codes and refresh tokens.
*/
public class TokenMgtDAO {
public static final String AUTHZ_USER = "AUTHZ_USER";
public static final String LOWER_AUTHZ_USER = "LOWER(AUTHZ_USER)";
private static final String UTC = "UTC";
private static TokenPersistenceProcessor persistenceProcessor;
private static int maxPoolSize = 100;
private boolean enablePersist = true;
private static BlockingDeque<AccessContextTokenDO> accessContextTokenQueue = new LinkedBlockingDeque<>();
private static BlockingDeque<AuthContextTokenDO> authContextTokenQueue = new LinkedBlockingDeque<>();
private static final Log log = LogFactory.getLog(TokenMgtDAO.class);
private static final String IDN_OAUTH2_ACCESS_TOKEN = "IDN_OAUTH2_ACCESS_TOKEN";
static {
final Log log = LogFactory.getLog(TokenMgtDAO.class);
try {
String maxPoolSizeConfigValue = IdentityUtil.getProperty("JDBCPersistenceManager.SessionDataPersist" +
".PoolSize");
if (StringUtils.isNotBlank(maxPoolSizeConfigValue)) {
maxPoolSize = Integer.parseInt(maxPoolSizeConfigValue);
}
} catch (NumberFormatException e) {
if(log.isDebugEnabled()){
log.debug("Error while parsing the JDBCPersistenceManager.SessionDataPersist.PoolSize.", e);
}
log.warn("Session data persistence pool size is not configured. Using default value.");
}
if (maxPoolSize > 0) {
log.info("Thread pool size for session persistent consumer : " + maxPoolSize);
ExecutorService threadPool = Executors.newFixedThreadPool(maxPoolSize);
for (int i = 0; i < maxPoolSize; i++) {
threadPool.execute(new TokenPersistenceTask(accessContextTokenQueue));
}
threadPool = Executors.newFixedThreadPool(maxPoolSize);
for (int i = 0; i < maxPoolSize; i++) {
threadPool.execute(new AuthPersistenceTask(authContextTokenQueue));
}
}
}
public TokenMgtDAO() {
try {
persistenceProcessor = OAuthServerConfiguration.getInstance().getPersistenceProcessor();
} catch (IdentityOAuth2Exception e) {
log.error("Error retrieving TokenPersistenceProcessor. Defaulting to PlainTextProcessor", e);
persistenceProcessor = new PlainTextPersistenceProcessor();
}
if (IdentityUtil.getProperty("JDBCPersistenceManager.TokenPersist.Enable") != null) {
enablePersist = Boolean.parseBoolean(IdentityUtil.getProperty("JDBCPersistenceManager.TokenPersist.Enable"));
}
}
public void storeAuthorizationCode(String authzCode, String consumerKey, String callbackUrl,
AuthzCodeDO authzCodeDO) throws IdentityOAuth2Exception {
if (!enablePersist) {
return;
}
if (maxPoolSize > 0) {
authContextTokenQueue.push(new AuthContextTokenDO(authzCode, consumerKey, callbackUrl, authzCodeDO));
} else {
persistAuthorizationCode(authzCode, consumerKey, callbackUrl, authzCodeDO);
}
}
public void persistAuthorizationCode(String authzCode, String consumerKey, String callbackUrl,
AuthzCodeDO authzCodeDO) throws IdentityOAuth2Exception {
if (!enablePersist) {
return;
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
try {
prepStmt = connection.prepareStatement(SQLQueries.STORE_AUTHORIZATION_CODE);
prepStmt.setString(1, authzCodeDO.getAuthzCodeId());
prepStmt.setString(2, persistenceProcessor.getProcessedAuthzCode(authzCode));
prepStmt.setString(3, callbackUrl);
prepStmt.setString(4, OAuth2Util.buildScopeString(authzCodeDO.getScope()));
prepStmt.setString(5, authzCodeDO.getAuthorizedUser().getUserName());
prepStmt.setString(6, authzCodeDO.getAuthorizedUser().getUserStoreDomain());
int tenantId = OAuth2Util.getTenantId(authzCodeDO.getAuthorizedUser().getTenantDomain());
prepStmt.setInt(7, tenantId);
prepStmt.setTimestamp(8, authzCodeDO.getIssuedTime(),
Calendar.getInstance(TimeZone.getTimeZone(UTC)));
prepStmt.setLong(9, authzCodeDO.getValidityPeriod());
prepStmt.setString(10, authzCodeDO.getAuthorizedUser().getAuthenticatedSubjectIdentifier());
prepStmt.setString(11, persistenceProcessor.getProcessedClientId(consumerKey));
prepStmt.execute();
connection.commit();
} catch (SQLException e) {
throw new IdentityOAuth2Exception("Error when storing the authorization code for consumer key : " +
consumerKey, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, prepStmt);
}
}
public void storeAccessToken(String accessToken, String consumerKey,
AccessTokenDO accessTokenDO, Connection connection,
String userStoreDomain) throws IdentityOAuth2Exception {
if (!enablePersist) {
return;
}
PreparedStatement prepStmt = null;
String accessTokenStoreTable = "IDN_OAUTH2_ACCESS_TOKEN";
if (StringUtils.isNotBlank(userStoreDomain)) {
accessTokenStoreTable = accessTokenStoreTable + "_" + userStoreDomain;
}
String sql = SQLQueries.INSERT_OAUTH2_ACCESS_TOKEN.replaceAll("\\$accessTokenStoreTable",
accessTokenStoreTable);
String sqlAddScopes = SQLQueries.INSERT_OAUTH2_TOKEN_SCOPE;
try {
prepStmt = connection.prepareStatement(sql);
prepStmt.setString(1, persistenceProcessor.getProcessedAccessTokenIdentifier(accessToken));
if (accessTokenDO.getRefreshToken() != null) {
prepStmt.setString(2, persistenceProcessor.getProcessedRefreshToken(accessTokenDO.getRefreshToken()));
} else {
prepStmt.setString(2, accessTokenDO.getRefreshToken());
}
prepStmt.setString(3, accessTokenDO.getAuthzUser().getUserName());
int tenantId = OAuth2Util.getTenantId(accessTokenDO.getAuthzUser().getTenantDomain());
prepStmt.setInt(4, tenantId);
prepStmt.setString(5, accessTokenDO.getAuthzUser().getUserStoreDomain());
prepStmt.setTimestamp(6, accessTokenDO.getIssuedTime(), Calendar.getInstance(TimeZone.getTimeZone(UTC)));
prepStmt.setTimestamp(7, accessTokenDO.getRefreshTokenIssuedTime(), Calendar.getInstance(TimeZone
.getTimeZone(UTC)));
prepStmt.setLong(8, accessTokenDO.getValidityPeriodInMillis());
prepStmt.setLong(9, accessTokenDO.getRefreshTokenValidityPeriodInMillis());
prepStmt.setString(10, OAuth2Util.hashScopes(accessTokenDO.getScope()));
prepStmt.setString(11, accessTokenDO.getTokenState());
prepStmt.setString(12, accessTokenDO.getTokenType());
prepStmt.setString(13, accessTokenDO.getTokenId());
prepStmt.setString(14, accessTokenDO.getGrantType());
prepStmt.setString(15, accessTokenDO.getAuthzUser().getAuthenticatedSubjectIdentifier());
prepStmt.setString(16, persistenceProcessor.getProcessedClientId(consumerKey));
prepStmt.execute();
String accessTokenId = accessTokenDO.getTokenId();
prepStmt = connection.prepareStatement(sqlAddScopes);
if (accessTokenDO.getScope() != null && accessTokenDO.getScope().length > 0) {
for (String scope : accessTokenDO.getScope()) {
prepStmt.setString(1, accessTokenId);
prepStmt.setString(2, scope);
prepStmt.setInt(3, tenantId);
prepStmt.execute();
}
}
} catch (SQLIntegrityConstraintViolationException e) {
String errorMsg = "Access Token for consumer key : " + consumerKey + ", user : " +
accessTokenDO.getAuthzUser() + " and scope : " +
OAuth2Util.buildScopeString(accessTokenDO.getScope()) + "already exists";
throw new IdentityOAuth2Exception(errorMsg, e);
} catch (DataTruncation e) {
throw new IdentityOAuth2Exception("Invalid request", e);
} catch (SQLException e) {
throw new IdentityOAuth2Exception(
"Error when storing the access token for consumer key : " + consumerKey, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(null, null, prepStmt);
}
}
public void storeAccessToken(String accessToken, String consumerKey, AccessTokenDO newAccessTokenDO,
AccessTokenDO existingAccessTokenDO, String userStoreDomain)
throws IdentityException {
if (!enablePersist) {
return;
}
if (maxPoolSize > 0) {
accessContextTokenQueue.push(new AccessContextTokenDO(accessToken, consumerKey, newAccessTokenDO
, existingAccessTokenDO, userStoreDomain));
} else {
persistAccessToken(accessToken, consumerKey, newAccessTokenDO, existingAccessTokenDO, userStoreDomain);
}
}
public boolean persistAccessToken(String accessToken, String consumerKey,
AccessTokenDO newAccessTokenDO, AccessTokenDO existingAccessTokenDO,
String userStoreDomain) throws IdentityOAuth2Exception {
if (!enablePersist) {
return false;
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
try {
if (existingAccessTokenDO != null) {
// Mark the existing access token as expired on database if a token exist for the user
setAccessTokenState(connection, existingAccessTokenDO.getTokenId(), OAuthConstants.TokenStates
.TOKEN_STATE_EXPIRED, UUID.randomUUID().toString(), userStoreDomain);
}
if (newAccessTokenDO.getAuthorizationCode() != null) {
storeAccessToken(accessToken, consumerKey, newAccessTokenDO, connection, userStoreDomain);
// expire authz code and insert issued access token against authz code
AuthzCodeDO authzCodeDO = new AuthzCodeDO();
authzCodeDO.setAuthorizationCode(newAccessTokenDO.getAuthorizationCode());
authzCodeDO.setOauthTokenId(newAccessTokenDO.getTokenId());
List<AuthzCodeDO> authzCodeDOList = new ArrayList<>(Arrays.asList(authzCodeDO));
deactivateAuthorizationCode(authzCodeDOList);
} else {
storeAccessToken(accessToken, consumerKey, newAccessTokenDO, connection, userStoreDomain);
}
connection.commit();
return true;
} catch (SQLException e) {
throw new IdentityOAuth2Exception("Error occurred while persisting access token", e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, null);
}
}
public AccessTokenDO retrieveLatestAccessToken(String consumerKey, AuthenticatedUser authzUser,
String userStoreDomain, String scope,
boolean includeExpiredTokens)
throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
boolean isUsernameCaseSensitive = IdentityUtil.isUserStoreInUsernameCaseSensitive(authzUser.toString());
String tenantDomain = authzUser.getTenantDomain();
int tenantId = OAuth2Util.getTenantId(tenantDomain);
String tenantAwareUsernameWithNoUserDomain = authzUser.getUserName();
String userDomain = authzUser.getUserStoreDomain();
if ((userDomain != null)){
userDomain.toUpperCase();
}
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
try {
String sql;
if (connection.getMetaData().getDriverName().contains("MySQL")
|| connection.getMetaData().getDriverName().contains("H2")) {
sql = SQLQueries.RETRIEVE_LATEST_ACCESS_TOKEN_BY_CLIENT_ID_USER_SCOPE_MYSQL;
} else if (connection.getMetaData().getDatabaseProductName().contains("DB2")) {
sql = SQLQueries.RETRIEVE_LATEST_ACCESS_TOKEN_BY_CLIENT_ID_USER_SCOPE_DB2SQL;
}
else if (connection.getMetaData().getDriverName().contains("MS SQL")) {
sql = SQLQueries.RETRIEVE_LATEST_ACCESS_TOKEN_BY_CLIENT_ID_USER_SCOPE_MSSQL;
} else if (connection.getMetaData().getDriverName().contains("Microsoft")) {
sql = SQLQueries.RETRIEVE_LATEST_ACCESS_TOKEN_BY_CLIENT_ID_USER_SCOPE_MSSQL;
} else if (connection.getMetaData().getDriverName().contains("PostgreSQL")) {
sql = SQLQueries.RETRIEVE_LATEST_ACCESS_TOKEN_BY_CLIENT_ID_USER_SCOPE_POSTGRESQL;
} else if (connection.getMetaData().getDriverName().contains("Informix")){
// Driver name = "IBM Informix JDBC Driver for IBM Informix Dynamic Server"
sql = SQLQueries.RETRIEVE_LATEST_ACCESS_TOKEN_BY_CLIENT_ID_USER_SCOPE_INFORMIX;
} else {
sql = SQLQueries.RETRIEVE_LATEST_ACCESS_TOKEN_BY_CLIENT_ID_USER_SCOPE_ORACLE;
}
if (StringUtils.isNotEmpty(userStoreDomain)) {
//logic to store access token into different tables when multiple user stores are configured.
sql = sql.replace(IDN_OAUTH2_ACCESS_TOKEN, IDN_OAUTH2_ACCESS_TOKEN + "_" + userStoreDomain);
}
if (!isUsernameCaseSensitive) {
sql = sql.replace(AUTHZ_USER, LOWER_AUTHZ_USER);
}
String hashedScope = OAuth2Util.hashScopes(scope);
if (hashedScope == null) {
sql = sql.replace("TOKEN_SCOPE_HASH=?", "TOKEN_SCOPE_HASH IS NULL");
}
prepStmt = connection.prepareStatement(sql);
prepStmt.setString(1, persistenceProcessor.getProcessedClientId(consumerKey));
if (isUsernameCaseSensitive) {
prepStmt.setString(2, tenantAwareUsernameWithNoUserDomain);
} else {
prepStmt.setString(2, tenantAwareUsernameWithNoUserDomain.toLowerCase());
}
prepStmt.setInt(3, tenantId);
prepStmt.setString(4, userDomain);
if (hashedScope != null) {
prepStmt.setString(5, hashedScope);
}
resultSet = prepStmt.executeQuery();
connection.commit();
if (resultSet.next()) {
boolean returnToken = false;
String tokenState = resultSet.getString(7);
if (includeExpiredTokens) {
if (OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE.equals(tokenState) ||
OAuthConstants.TokenStates.TOKEN_STATE_EXPIRED.equals(tokenState)) {
returnToken = true;
}
} else {
if (OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE.equals(tokenState)) {
returnToken = true;
}
}
if (returnToken) {
String accessToken = persistenceProcessor.getPreprocessedAccessTokenIdentifier(
resultSet.getString(1));
String refreshToken = null;
if (resultSet.getString(2) != null) {
refreshToken = persistenceProcessor.getPreprocessedRefreshToken(resultSet.getString(2));
}
long issuedTime = resultSet.getTimestamp(3, Calendar.getInstance(TimeZone.getTimeZone(UTC)))
.getTime();
long refreshTokenIssuedTime = resultSet.getTimestamp(4, Calendar.getInstance(TimeZone.getTimeZone
(UTC))).getTime();
long validityPeriodInMillis = resultSet.getLong(5);
long refreshTokenValidityPeriodInMillis = resultSet.getLong(6);
String userType = resultSet.getString(8);
String tokenId = resultSet.getString(9);
String subjectIdentifier = resultSet.getString(10);
// data loss at dividing the validity period but can be neglected
AuthenticatedUser user = new AuthenticatedUser();
user.setUserName(tenantAwareUsernameWithNoUserDomain);
user.setTenantDomain(tenantDomain);
user.setUserStoreDomain(userDomain);
user.setAuthenticatedSubjectIdentifier(subjectIdentifier);
AccessTokenDO accessTokenDO = new AccessTokenDO(consumerKey, user, OAuth2Util.buildScopeArray
(scope), new Timestamp(issuedTime), new Timestamp(refreshTokenIssuedTime)
, validityPeriodInMillis, refreshTokenValidityPeriodInMillis, userType);
accessTokenDO.setAccessToken(accessToken);
accessTokenDO.setRefreshToken(refreshToken);
accessTokenDO.setTokenState(tokenState);
accessTokenDO.setTokenId(tokenId);
return accessTokenDO;
}
}
return null;
} catch (SQLException e) {
String errorMsg = "Error occurred while trying to retrieve latest 'ACTIVE' " +
"access token for Client ID : " + consumerKey + ", User ID : " + authzUser +
" and Scope : " + scope;
if (includeExpiredTokens) {
errorMsg = errorMsg.replace("ACTIVE", "ACTIVE or EXPIRED");
}
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
}
}
public Set<AccessTokenDO> retrieveAccessTokens(String consumerKey, AuthenticatedUser userName,
String userStoreDomain, boolean includeExpired)
throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
boolean isUsernameCaseSensitive = IdentityUtil.isUserStoreInUsernameCaseSensitive(userName.toString());
String tenantDomain = userName.getTenantDomain();
String tenantAwareUsernameWithNoUserDomain = userName.getUserName();
String userDomain = userName.getUserStoreDomain();
if ((userDomain != null)){
userDomain.toUpperCase();
}
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
Map<String, AccessTokenDO> accessTokenDOMap = new HashMap<>();
try {
int tenantId = OAuth2Util.getTenantId(tenantDomain);
String sql = SQLQueries.RETRIEVE_ACTIVE_ACCESS_TOKEN_BY_CLIENT_ID_USER;
if (includeExpired) {
sql = SQLQueries.RETRIEVE_ACTIVE_EXPIRED_ACCESS_TOKEN_BY_CLIENT_ID_USER;
}
if (StringUtils.isNotEmpty(userStoreDomain)) {
sql = sql.replace(IDN_OAUTH2_ACCESS_TOKEN, IDN_OAUTH2_ACCESS_TOKEN + "_" + userStoreDomain);
}
if (!isUsernameCaseSensitive) {
sql = sql.replace(AUTHZ_USER, LOWER_AUTHZ_USER);
}
prepStmt = connection.prepareStatement(sql);
prepStmt.setString(1, persistenceProcessor.getProcessedClientId(consumerKey));
if (isUsernameCaseSensitive) {
prepStmt.setString(2, tenantAwareUsernameWithNoUserDomain);
} else {
prepStmt.setString(2, tenantAwareUsernameWithNoUserDomain.toLowerCase());
}
prepStmt.setInt(3, tenantId);
prepStmt.setString(4, userDomain);
resultSet = prepStmt.executeQuery();
while (resultSet.next()) {
String accessToken = persistenceProcessor.
getPreprocessedAccessTokenIdentifier(resultSet.getString(1));
if(accessTokenDOMap.get(accessToken) == null) {
String refreshToken = persistenceProcessor.
getPreprocessedRefreshToken(resultSet.getString(2));
Timestamp issuedTime = resultSet.getTimestamp(3, Calendar.getInstance(TimeZone.getTimeZone(UTC)));
Timestamp refreshTokenIssuedTime = resultSet.getTimestamp(4, Calendar.getInstance(TimeZone
.getTimeZone(UTC)));
long validityPeriodInMillis = resultSet.getLong(5);
long refreshTokenValidityPeriodMillis = resultSet.getLong(6);
String tokenType = resultSet.getString(7);
String[] scope = OAuth2Util.buildScopeArray(resultSet.getString(8));
String tokenId = resultSet.getString(9);
String subjectIdentifier = resultSet.getString(10);
AuthenticatedUser user = new AuthenticatedUser();
user.setUserName(tenantAwareUsernameWithNoUserDomain);
user.setTenantDomain(tenantDomain);
user.setUserStoreDomain(userDomain);
user.setAuthenticatedSubjectIdentifier(subjectIdentifier);
AccessTokenDO dataDO = new AccessTokenDO(consumerKey, user, scope, issuedTime,
refreshTokenIssuedTime, validityPeriodInMillis,
refreshTokenValidityPeriodMillis, tokenType);
dataDO.setAccessToken(accessToken);
dataDO.setRefreshToken(refreshToken);
dataDO.setTokenId(tokenId);
accessTokenDOMap.put(accessToken, dataDO);
} else {
String scope = resultSet.getString(8).trim();
AccessTokenDO accessTokenDO = accessTokenDOMap.get(accessToken);
accessTokenDO.setScope((String[]) ArrayUtils.add(accessTokenDO.getScope(), scope));
}
}
connection.commit();
} catch (SQLException e) {
String errorMsg = "Error occurred while retrieving 'ACTIVE' access tokens for " +
"Client ID : " + consumerKey + " and User ID : " + userName;
if (includeExpired) {
errorMsg = errorMsg.replace("ACTIVE", "ACTIVE or EXPIRED");
}
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
}
return new HashSet<>(accessTokenDOMap.values());
}
public AuthzCodeDO validateAuthorizationCode(String consumerKey, String authorizationKey)
throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
try {
prepStmt = connection.prepareStatement(SQLQueries.VALIDATE_AUTHZ_CODE);
prepStmt.setString(1, persistenceProcessor.getProcessedClientId(consumerKey));
prepStmt.setString(2, persistenceProcessor.getProcessedAuthzCode(authorizationKey));
resultSet = prepStmt.executeQuery();
if (resultSet.next()) {
if (resultSet.getString(8).equals(OAuthConstants.AuthorizationCodeState.ACTIVE)) {
String authorizedUser = resultSet.getString(1);
String userstoreDomain = resultSet.getString(2);
int tenantId = resultSet.getInt(3);
String tenantDomain = OAuth2Util.getTenantDomain(tenantId);
String scopeString = resultSet.getString(4);
String callbackUrl = resultSet.getString(5);
Timestamp issuedTime = resultSet.getTimestamp(6, Calendar.getInstance(TimeZone.getTimeZone(UTC)));
long validityPeriod = resultSet.getLong(7);
String codeId = resultSet.getString(11);
String subjectIdentifier = resultSet.getString(12);
AuthenticatedUser user = new AuthenticatedUser();
user.setUserName(authorizedUser);
user.setTenantDomain(tenantDomain);
user.setUserStoreDomain(userstoreDomain);
user.setAuthenticatedSubjectIdentifier(subjectIdentifier);
return new AuthzCodeDO(user, OAuth2Util.buildScopeArray(scopeString), issuedTime, validityPeriod,
callbackUrl, consumerKey, authorizationKey, codeId);
} else {
String authorizedUser = resultSet.getString(1);
String userStoreDomain = resultSet.getString(2);
int tenantId = resultSet.getInt(3);
String tenantDomain = OAuth2Util.getTenantDomain(tenantId);
authorizedUser = UserCoreUtil.addDomainToName(authorizedUser, userStoreDomain);
authorizedUser = UserCoreUtil.addTenantDomainToEntry(authorizedUser, tenantDomain);
String tokenId = resultSet.getString(9);
revokeToken(tokenId, authorizedUser);
}
}
connection.commit();
} catch (SQLException e) {
throw new IdentityOAuth2Exception("Error when validating an authorization code", e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
}
return null;
}
public void expireAuthzCode(String authzCode) throws IdentityOAuth2Exception {
if (maxPoolSize > 0) {
authContextTokenQueue.push(new AuthContextTokenDO(authzCode));
} else {
doExpireAuthzCode(authzCode);
}
}
public void doExpireAuthzCode(String authzCode) throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
try {
prepStmt = connection.prepareStatement(SQLQueries.EXPIRE_AUTHZ_CODE);
prepStmt.setString(1, persistenceProcessor.getPreprocessedAuthzCode(authzCode));
prepStmt.execute();
connection.commit();
} catch (SQLException e) {
throw new IdentityOAuth2Exception("Error when cleaning up an authorization code", e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, prepStmt);
}
}
public void deactivateAuthorizationCode(List<AuthzCodeDO> authzCodeDOs) throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
try {
prepStmt = connection.prepareStatement(SQLQueries.DEACTIVATE_AUTHZ_CODE_AND_INSERT_CURRENT_TOKEN);
for (AuthzCodeDO authzCodeDO : authzCodeDOs){
prepStmt.setString(1, authzCodeDO.getOauthTokenId());
prepStmt.setString(2, persistenceProcessor.getPreprocessedAuthzCode(authzCodeDO.getAuthorizationCode()));
prepStmt.addBatch();
}
prepStmt.executeBatch();
connection.commit();
} catch (SQLException e) {
throw new IdentityOAuth2Exception("Error when deactivating authorization code", e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, prepStmt);
}
}
public RefreshTokenValidationDataDO validateRefreshToken(String consumerKey, String refreshToken)
throws IdentityOAuth2Exception {
RefreshTokenValidationDataDO validationDataDO = new RefreshTokenValidationDataDO();
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
String userStoreDomain = null;
String sql = null;
String mySqlQuery;
String db2Query;
String oracleQuery;
String msSqlQuery;
String postgreSqlQuery;
String informixQuery;
try {
if (OAuth2Util.checkAccessTokenPartitioningEnabled() &&
OAuth2Util.checkUserNameAssertionEnabled()) {
userStoreDomain = OAuth2Util.getUserStoreDomainFromAccessToken(refreshToken);
}
String accessTokenStoreTable = "IDN_OAUTH2_ACCESS_TOKEN";
if (StringUtils.isNotBlank(userStoreDomain)) {
accessTokenStoreTable = accessTokenStoreTable + "_" + userStoreDomain;
}
mySqlQuery = SQLQueries.RETRIEVE_ACCESS_TOKEN_VALIDATION_DATA_MYSQL.replaceAll("\\$accessTokenStoreTable",
accessTokenStoreTable);
db2Query = SQLQueries.RETRIEVE_ACCESS_TOKEN_VALIDATION_DATA_DB2SQL.replaceAll("\\$accessTokenStoreTable",
accessTokenStoreTable);
oracleQuery = SQLQueries.RETRIEVE_ACCESS_TOKEN_VALIDATION_DATA_ORACLE.replaceAll("\\$accessTokenStoreTable",
accessTokenStoreTable);
msSqlQuery = SQLQueries.RETRIEVE_ACCESS_TOKEN_VALIDATION_DATA_MSSQL.replaceAll("\\$accessTokenStoreTable",
accessTokenStoreTable);
informixQuery = SQLQueries.RETRIEVE_ACCESS_TOKEN_VALIDATION_DATA_INFORMIX.replaceAll
("\\$accessTokenStoreTable", accessTokenStoreTable);
postgreSqlQuery = SQLQueries.RETRIEVE_ACCESS_TOKEN_VALIDATION_DATA_POSTGRESQL.replaceAll
("\\$accessTokenStoreTable", accessTokenStoreTable);
if (connection.getMetaData().getDriverName().contains("MySQL")
|| connection.getMetaData().getDriverName().contains("H2")) {
sql = mySqlQuery;
} else if(connection.getMetaData().getDatabaseProductName().contains("DB2")){
sql = db2Query;
} else if (connection.getMetaData().getDriverName().contains("MS SQL")) {
sql = msSqlQuery;
} else if (connection.getMetaData().getDriverName().contains("Microsoft")) {
sql = msSqlQuery;
} else if (connection.getMetaData().getDriverName().contains("PostgreSQL")) {
sql = postgreSqlQuery;
} else if (connection.getMetaData().getDriverName().contains("INFORMIX")) {
sql = informixQuery;
} else {
sql = oracleQuery;
}
if (refreshToken == null) {
sql = sql.replace("REFRESH_TOKEN = ?", "REFRESH_TOKEN IS NULL");
}
prepStmt = connection.prepareStatement(sql);
prepStmt.setString(1, persistenceProcessor.getProcessedClientId(consumerKey));
if (refreshToken != null) {
prepStmt.setString(2, persistenceProcessor.getProcessedRefreshToken(refreshToken));
}
resultSet = prepStmt.executeQuery();
int iterateId = 0;
List<String> scopes = new ArrayList<>();
while (resultSet.next()) {
if (iterateId == 0) {
validationDataDO.setAccessToken(persistenceProcessor.getPreprocessedAccessTokenIdentifier(
resultSet.getString(1)));
String userName = resultSet.getString(2);
int tenantId = resultSet.getInt(3);
String userDomain = resultSet.getString(4);
String tenantDomain = OAuth2Util.getTenantDomain(tenantId);
validationDataDO.setScope(OAuth2Util.buildScopeArray(resultSet.getString(5)));
validationDataDO.setRefreshTokenState(resultSet.getString(6));
validationDataDO.setIssuedTime(
resultSet.getTimestamp(7, Calendar.getInstance(TimeZone.getTimeZone(UTC))));
validationDataDO.setValidityPeriodInMillis(resultSet.getLong(8));
validationDataDO.setTokenId(resultSet.getString(9));
validationDataDO.setGrantType(resultSet.getString(10));
String subjectIdentifier = resultSet.getString(11);
AuthenticatedUser user = new AuthenticatedUser();
user.setUserName(userName);
user.setUserStoreDomain(userDomain);
user.setTenantDomain(tenantDomain);
user.setAuthenticatedSubjectIdentifier(subjectIdentifier);
validationDataDO.setAuthorizedUser(user);
} else {
scopes.add(resultSet.getString(5));
}
iterateId++;
}
if (scopes.size() > 0 && validationDataDO != null) {
validationDataDO.setScope((String[]) ArrayUtils.addAll(validationDataDO.getScope(),
scopes.toArray(new String[scopes.size()])));
}
connection.commit();
} catch (SQLException e) {
throw new IdentityOAuth2Exception("Error when validating a refresh token", e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
}
return validationDataDO;
}
public AccessTokenDO retrieveAccessToken(String accessTokenIdentifier, boolean includeExpired)
throws IdentityOAuth2Exception {
AccessTokenDO dataDO = null;
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
String userStoreDomain = null;
try {
//select the user store domain when multiple user stores are configured.
if (OAuth2Util.checkAccessTokenPartitioningEnabled() &&
OAuth2Util.checkUserNameAssertionEnabled()) {
userStoreDomain = OAuth2Util.getUserStoreDomainFromAccessToken(accessTokenIdentifier);
}
String sql;
if (includeExpired) {
sql = SQLQueries.RETRIEVE_ACTIVE_EXPIRED_ACCESS_TOKEN;
} else {
sql = SQLQueries.RETRIEVE_ACTIVE_ACCESS_TOKEN;
}
if (StringUtils.isNotBlank(userStoreDomain)) {
sql = sql.replace(IDN_OAUTH2_ACCESS_TOKEN, IDN_OAUTH2_ACCESS_TOKEN + "_" + userStoreDomain);
}
prepStmt = connection.prepareStatement(sql);
prepStmt.setString(1, persistenceProcessor.getProcessedAccessTokenIdentifier(accessTokenIdentifier));
resultSet = prepStmt.executeQuery();
int iterateId = 0;
List<String> scopes = new ArrayList<>();
while (resultSet.next()) {
if (iterateId == 0) {
String consumerKey = persistenceProcessor.getPreprocessedClientId(resultSet.getString(1));
String authorizedUser = resultSet.getString(2);
int tenantId = resultSet.getInt(3);
String tenantDomain = OAuth2Util.getTenantDomain(tenantId);
String userDomain = resultSet.getString(4);
String[] scope = OAuth2Util.buildScopeArray(resultSet.getString(5));
Timestamp issuedTime = resultSet.getTimestamp(6, Calendar.getInstance(TimeZone.getTimeZone(UTC)));
Timestamp refreshTokenIssuedTime = resultSet.getTimestamp(7,
Calendar.getInstance(TimeZone.getTimeZone(UTC)));
long validityPeriodInMillis = resultSet.getLong(8);
long refreshTokenValidityPeriodMillis = resultSet.getLong(9);
String tokenType = resultSet.getString(10);
String refreshToken = resultSet.getString(11);
String tokenId = resultSet.getString(12);
String grantType = resultSet.getString(13);
String subjectIdentifier = resultSet.getString(14);
AuthenticatedUser user = new AuthenticatedUser();
user.setUserName(authorizedUser);
user.setUserStoreDomain(userDomain);
user.setTenantDomain(tenantDomain);
user.setAuthenticatedSubjectIdentifier(subjectIdentifier);
dataDO = new AccessTokenDO(consumerKey, user, scope, issuedTime, refreshTokenIssuedTime,
validityPeriodInMillis, refreshTokenValidityPeriodMillis, tokenType);
dataDO.setAccessToken(accessTokenIdentifier);
dataDO.setRefreshToken(refreshToken);
dataDO.setTokenId(tokenId);
dataDO.setGrantType(grantType);
dataDO.setTenantID(tenantId);
} else {
scopes.add(resultSet.getString(5));
}
iterateId++;
}
if (scopes.size() > 0 && dataDO != null) {
dataDO.setScope((String[]) ArrayUtils.addAll(dataDO.getScope(),
scopes.toArray(new String[scopes.size()])));
}
connection.commit();
} catch (SQLException e) {
throw new IdentityOAuth2Exception("Error when retrieving Access Token" + e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
}
return dataDO;
}
/**
*
* @param connection database connection
* @param tokenId accesstoken
* @param tokenState state of the token need to be updated.
* @param tokenStateId token state id.
* @param userStoreDomain user store domain.
* @throws IdentityOAuth2Exception
*/
public void setAccessTokenState(Connection connection, String tokenId, String tokenState,
String tokenStateId, String userStoreDomain)
throws IdentityOAuth2Exception {
PreparedStatement prepStmt = null;
try {
String sql = SQLQueries.UPDATE_TOKE_STATE;
if (StringUtils.isNotBlank(userStoreDomain)) {
sql = sql.replace(IDN_OAUTH2_ACCESS_TOKEN, IDN_OAUTH2_ACCESS_TOKEN + "_" + userStoreDomain);
}
prepStmt = connection.prepareStatement(sql);
prepStmt.setString(1, tokenState);
prepStmt.setString(2, tokenStateId);
prepStmt.setString(3, tokenId);
prepStmt.executeUpdate();
} catch (SQLException e) {
throw new IdentityOAuth2Exception("Error while updating Access Token with ID : " +
tokenId + " to Token State : " + tokenState, e);
} finally {
IdentityDatabaseUtil.closeStatement(prepStmt);
}
}
/**
* This method is to revoke specific tokens
*
* @param tokens tokens that needs to be revoked
* @throws IdentityOAuth2Exception if failed to revoke the access token
*/
public void revokeTokens(String[] tokens) throws IdentityOAuth2Exception {
if (OAuth2Util.checkAccessTokenPartitioningEnabled() && OAuth2Util.checkUserNameAssertionEnabled()) {
revokeTokensIndividual(tokens);
} else {
revokeTokensBatch(tokens);
}
}
public void revokeTokensBatch(String[] tokens) throws IdentityOAuth2Exception {
String accessTokenStoreTable = OAuthConstants.ACCESS_TOKEN_STORE_TABLE;
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement ps = null;
try {
String sqlQuery = SQLQueries.REVOKE_ACCESS_TOKEN.replace(IDN_OAUTH2_ACCESS_TOKEN, accessTokenStoreTable);
ps = connection.prepareStatement(sqlQuery);
for (String token : tokens) {
ps.setString(1, OAuthConstants.TokenStates.TOKEN_STATE_REVOKED);
ps.setString(2, UUID.randomUUID().toString());
ps.setString(3, persistenceProcessor.getProcessedAccessTokenIdentifier(token));
ps.addBatch();
}
ps.executeBatch();
connection.commit();
} catch (SQLException e) {
IdentityDatabaseUtil.rollBack(connection);
throw new IdentityOAuth2Exception("Error occurred while revoking Access Tokens : " + tokens.toString(), e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, ps);
}
}
public void revokeTokensIndividual(String[] tokens) throws IdentityOAuth2Exception {
String accessTokenStoreTable = OAuthConstants.ACCESS_TOKEN_STORE_TABLE;
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement ps = null;
try {
for (String token: tokens){
if (OAuth2Util.checkAccessTokenPartitioningEnabled() &&
OAuth2Util.checkUserNameAssertionEnabled()) {
accessTokenStoreTable = OAuth2Util.getAccessTokenStoreTableFromAccessToken(token);
}
String sqlQuery = SQLQueries.REVOKE_ACCESS_TOKEN.replace(
IDN_OAUTH2_ACCESS_TOKEN, accessTokenStoreTable);
ps = connection.prepareStatement(sqlQuery);
ps.setString(1, OAuthConstants.TokenStates.TOKEN_STATE_REVOKED);
ps.setString(2, UUID.randomUUID().toString());
ps.setString(3, persistenceProcessor.getProcessedAccessTokenIdentifier(token));
int count = ps.executeUpdate();
if (log.isDebugEnabled()) {
log.debug("Number of rows being updated : " + count);
}
}
connection.commit();
} catch (SQLException e) {
IdentityDatabaseUtil.rollBack(connection);
throw new IdentityOAuth2Exception("Error occurred while revoking Access Token : " + tokens.toString(), e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, ps);
}
}
/**
* Ths method is to revoke specific tokens
*
* @param tokenId token that needs to be revoked
* @throws IdentityOAuth2Exception if failed to revoke the access token
*/
public void revokeToken(String tokenId, String userId) throws IdentityOAuth2Exception {
String accessTokenStoreTable = OAuthConstants.ACCESS_TOKEN_STORE_TABLE;
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement ps = null;
try {
if (OAuth2Util.checkAccessTokenPartitioningEnabled() &&
OAuth2Util.checkUserNameAssertionEnabled()) {
accessTokenStoreTable = OAuth2Util.getAccessTokenStoreTableFromUserId(userId);
}
String sqlQuery = SQLQueries.REVOKE_ACCESS_TOKEN_BY_TOKEN_ID.replace(
IDN_OAUTH2_ACCESS_TOKEN, accessTokenStoreTable);
ps = connection.prepareStatement(sqlQuery);
ps.setString(1, OAuthConstants.TokenStates.TOKEN_STATE_REVOKED);
ps.setString(2, UUID.randomUUID().toString());
ps.setString(3, tokenId);
int count = ps.executeUpdate();
if (log.isDebugEnabled()) {
log.debug("Number of rows being updated : " + count);
}
connection.commit();
} catch (SQLException e) {
IdentityDatabaseUtil.rollBack(connection);
throw new IdentityOAuth2Exception("Error occurred while revoking Access Token with ID : " + tokenId, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, ps);
}
}
/**
* @param authenticatedUser
* @return
* @throws IdentityOAuth2Exception
*/
public Set<String> getAccessTokensForUser(AuthenticatedUser authenticatedUser) throws
IdentityOAuth2Exception {
String accessTokenStoreTable = OAuthConstants.ACCESS_TOKEN_STORE_TABLE;
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement ps = null;
ResultSet rs = null;
Set<String> accessTokens = new HashSet<>();
boolean isUsernameCaseSensitive = IdentityUtil.isUserStoreInUsernameCaseSensitive(authenticatedUser.toString());
try {
if (OAuth2Util.checkAccessTokenPartitioningEnabled() &&
OAuth2Util.checkUserNameAssertionEnabled()) {
accessTokenStoreTable = OAuth2Util.getAccessTokenStoreTableFromUserId(authenticatedUser.toString());
}
String sqlQuery = SQLQueries.GET_ACCESS_TOKEN_BY_AUTHZUSER.replace(
IDN_OAUTH2_ACCESS_TOKEN, accessTokenStoreTable);
if (!isUsernameCaseSensitive){
sqlQuery = sqlQuery.replace(AUTHZ_USER, LOWER_AUTHZ_USER);
}
ps = connection.prepareStatement(sqlQuery);
if (isUsernameCaseSensitive) {
ps.setString(1, authenticatedUser.getUserName());
} else {
ps.setString(1, authenticatedUser.getUserName().toLowerCase());
}
ps.setString(2, Integer.toString(OAuth2Util.getTenantId(authenticatedUser.getTenantDomain())));
ps.setString(3, OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE);
ps.setString(4, authenticatedUser.getUserStoreDomain());
rs = ps.executeQuery();
while (rs.next()){
accessTokens.add(rs.getString(1));
}
connection.commit();
} catch (SQLException e) {
IdentityDatabaseUtil.rollBack(connection);
throw new IdentityOAuth2Exception("Error occurred while revoking Access Token with user Name : " +
authenticatedUser.getUserName() + " tenant ID : " + OAuth2Util.getTenantId(authenticatedUser
.getTenantDomain()), e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, ps);
}
return accessTokens;
}
/**
*
* @param authenticatedUser
* @return
* @throws IdentityOAuth2Exception
*/
public Set<String> getAuthorizationCodesForUser(AuthenticatedUser authenticatedUser) throws
IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement ps = null;
ResultSet rs = null;
Set<String> authorizationCodes = new HashSet<>();
boolean isUsernameCaseSensitive = IdentityUtil.isUserStoreInUsernameCaseSensitive(authenticatedUser.toString());
try {
String sqlQuery = SQLQueries.GET_AUTHORIZATION_CODES_BY_AUTHZUSER;
if (!isUsernameCaseSensitive) {
sqlQuery = sqlQuery.replace(AUTHZ_USER, LOWER_AUTHZ_USER);
}
ps = connection.prepareStatement(sqlQuery);
if (isUsernameCaseSensitive) {
ps.setString(1, authenticatedUser.getUserName());
} else {
ps.setString(1, authenticatedUser.getUserName().toLowerCase());
}
ps.setString(2,Integer.toString(OAuth2Util.getTenantId(authenticatedUser.getTenantDomain())));
ps.setString(3, authenticatedUser.getUserStoreDomain());
rs = ps.executeQuery();
while (rs.next()){
authorizationCodes.add(rs.getString(1));
}
connection.commit();
} catch (SQLException e) {
IdentityDatabaseUtil.rollBack(connection);
throw new IdentityOAuth2Exception("Error occurred while revoking Access Token with user Name : " +
authenticatedUser.getUserName() + " tenant ID : " + OAuth2Util.getTenantId(authenticatedUser
.getTenantDomain()), e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, ps);
}
return authorizationCodes;
}
public Set<String> getActiveTokensForConsumerKey(String consumerKey) throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement ps = null;
ResultSet rs = null;
Set<String> accessTokens = new HashSet<>();
try {
String sqlQuery = SQLQueries.GET_ACCESS_TOKENS_FOR_CONSUMER_KEY;
ps = connection.prepareStatement(sqlQuery);
ps.setString(1, consumerKey);
ps.setString(2, OAuthConstants.TokenStates.TOKEN_STATE_ACTIVE);
rs = ps.executeQuery();
while (rs.next()) {
accessTokens.add(rs.getString(1));
}
connection.commit();
} catch (SQLException e) {
IdentityDatabaseUtil.rollBack(connection);
throw new IdentityOAuth2Exception("Error occurred while getting access tokens from acces token table for " +
"the application with consumer key : " + consumerKey, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, ps);
}
return accessTokens;
}
public Set<String> getAuthorizationCodesForConsumerKey(String consumerKey) throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement ps = null;
ResultSet rs = null;
Set<String> authorizationCodes = new HashSet<>();
try {
String sqlQuery = SQLQueries.GET_AUTHORIZATION_CODES_FOR_CONSUMER_KEY;
ps = connection.prepareStatement(sqlQuery);
ps.setString(1, consumerKey);
rs = ps.executeQuery();
while (rs.next()) {
authorizationCodes.add(rs.getString(1));
}
connection.commit();
} catch (SQLException e) {
IdentityDatabaseUtil.rollBack(connection);
throw new IdentityOAuth2Exception("Error occurred while getting authorization codes from authorization code table for the application with consumer key : " + consumerKey, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, ps);
}
return authorizationCodes;
}
/**
* This method is to list the application authorized by OAuth resource owners
*
* @param authzUser username of the resource owner
* @return set of distinct client IDs authorized by user until now
* @throws IdentityOAuth2Exception if failed to update the access token
*/
public Set<String> getAllTimeAuthorizedClientIds(AuthenticatedUser authzUser) throws IdentityOAuth2Exception {
String accessTokenStoreTable = OAuthConstants.ACCESS_TOKEN_STORE_TABLE;
PreparedStatement ps = null;
Connection connection = IdentityDatabaseUtil.getDBConnection();;
ResultSet rs = null;
Set<String> distinctConsumerKeys = new HashSet<>();
boolean isUsernameCaseSensitive = IdentityUtil.isUserStoreInUsernameCaseSensitive(authzUser.toString());
String tenantDomain = authzUser.getTenantDomain();
String tenantAwareUsernameWithNoUserDomain = authzUser.getUserName();
String userDomain = authzUser.getUserStoreDomain();
if ((userDomain != null)){
userDomain.toUpperCase();
}
try {
int tenantId = OAuth2Util.getTenantId(tenantDomain);
if (OAuth2Util.checkAccessTokenPartitioningEnabled() &&
OAuth2Util.checkUserNameAssertionEnabled()) {
accessTokenStoreTable = OAuth2Util.getAccessTokenStoreTableFromUserId(authzUser.toString());
}
String sqlQuery = SQLQueries.GET_DISTINCT_APPS_AUTHORIZED_BY_USER_ALL_TIME.replace(
IDN_OAUTH2_ACCESS_TOKEN, accessTokenStoreTable);
if (!isUsernameCaseSensitive) {
sqlQuery = sqlQuery.replace(AUTHZ_USER, LOWER_AUTHZ_USER);
}
ps = connection.prepareStatement(sqlQuery);
if (isUsernameCaseSensitive) {
ps.setString(1, tenantAwareUsernameWithNoUserDomain);
} else {
ps.setString(1, tenantAwareUsernameWithNoUserDomain.toLowerCase());
}
ps.setInt(2, tenantId);
ps.setString(3, userDomain);
rs = ps.executeQuery();
while (rs.next()) {
String consumerKey = persistenceProcessor.getPreprocessedClientId(rs.getString(1));
distinctConsumerKeys.add(consumerKey);
}
} catch (SQLException e) {
throw new IdentityOAuth2Exception(
"Error occurred while retrieving all distinct Client IDs authorized by " +
"User ID : " + authzUser + " until now", e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, rs, ps);
}
return distinctConsumerKeys;
}
public String findScopeOfResource(String resourceUri) throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();;
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = SQLQueries.RETRIEVE_IOS_SCOPE_KEY;
ps = connection.prepareStatement(sql);
ps.setString(1, resourceUri);
rs = ps.executeQuery();
if (rs.next()) {
return rs.getString("SCOPE_KEY");
}
connection.commit();
return null;
} catch (SQLException e) {
String errorMsg = "Error getting scopes for resource - " + resourceUri + " : " + e.getMessage();
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, rs, ps);
}
}
public boolean validateScope(Connection connection, String accessToken, String resourceUri) {
return false;
}
/**
* This method is used invalidate the existing token and generate a new toke within one DB transaction.
*
* @param oldAccessTokenId access token need to be updated.
* @param tokenState token state before generating new token.
* @param consumerKey consumer key of the existing token
* @param tokenStateId new token state id to be updated
* @param accessTokenDO new access token details
* @param userStoreDomain user store domain which is related to this consumer
* @throws IdentityOAuth2Exception
*/
public void invalidateAndCreateNewToken(String oldAccessTokenId, String tokenState,
String consumerKey, String tokenStateId,
AccessTokenDO accessTokenDO, String userStoreDomain)
throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
try {
connection.setAutoCommit(false);
// update existing token as inactive
setAccessTokenState(connection, oldAccessTokenId, tokenState, tokenStateId, userStoreDomain);
String newAccessToken = accessTokenDO.getAccessToken();
// store new token in the DB
storeAccessToken(newAccessToken, consumerKey, accessTokenDO, connection,
userStoreDomain);
// update new access token against authorization code if token obtained via authorization code grant type
updateTokenIdIfAutzCodeGrantType(oldAccessTokenId, accessTokenDO.getTokenId(), connection);
// commit both transactions
connection.commit();
} catch (SQLException e) {
String errorMsg = "Error while regenerating access token";
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeConnection(connection);
}
}
/**
* Revoke the OAuth Consent which is recorded in the IDN_OPENID_USER_RPS table against the user for a particular
* Application
*
* @param username - Username of the Consent owner
* @param applicationName - Name of the OAuth App
* @throws org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception - If an unexpected error occurs.
*/
public void revokeOAuthConsentByApplicationAndUser(String username, String applicationName)
throws IdentityOAuth2Exception {
if (username == null || applicationName == null) {
log.error("Could not remove consent of user " + username + " for application " + applicationName);
return;
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement ps = null;
try {
connection.setAutoCommit(false);
String sql = SQLQueries.DELETE_IDN_OPENID_USER_RPS;
ps = connection.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, applicationName);
ps.execute();
connection.commit();
} catch (SQLException e) {
String errorMsg = "Error deleting OAuth consent of Application " + applicationName + " and User " + username;
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, ps);
}
}
public Set<AccessTokenDO> getAccessTokensOfTenant(int tenantId) throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
Map<String, AccessTokenDO> accessTokenDOMap = new HashMap<>();
try {
String sql = SQLQueries.LIST_ALL_TOKENS_IN_TENANT;
prepStmt = connection.prepareStatement(sql);
prepStmt.setInt(1, tenantId);
resultSet = prepStmt.executeQuery();
while (resultSet.next()) {
String accessToken = persistenceProcessor.
getPreprocessedAccessTokenIdentifier(resultSet.getString(1));
if(accessTokenDOMap.get(accessToken) == null) {
String refreshToken = persistenceProcessor.
getPreprocessedRefreshToken(resultSet.getString(2));
Timestamp issuedTime = resultSet.getTimestamp(3, Calendar.getInstance(TimeZone.getTimeZone(UTC)));
Timestamp refreshTokenIssuedTime = resultSet.getTimestamp(4, Calendar.getInstance(TimeZone
.getTimeZone(UTC)));
long validityPeriodInMillis = resultSet.getLong(5);
long refreshTokenValidityPeriodMillis = resultSet.getLong(6);
String tokenType = resultSet.getString(7);
String[] scope = OAuth2Util.buildScopeArray(resultSet.getString(8));
String tokenId = resultSet.getString(9);
String authzUser = resultSet.getString(10);
String userStoreDomain = resultSet.getString(11);
String consumerKey = resultSet.getString(12);
AuthenticatedUser user = new AuthenticatedUser();
user.setUserName(authzUser);
user.setTenantDomain(OAuth2Util.getTenantDomain(tenantId));
user.setUserStoreDomain(userStoreDomain);
AccessTokenDO dataDO = new AccessTokenDO(consumerKey, user, scope, issuedTime,
refreshTokenIssuedTime, validityPeriodInMillis,
refreshTokenValidityPeriodMillis, tokenType);
dataDO.setAccessToken(accessToken);
dataDO.setRefreshToken(refreshToken);
dataDO.setTokenId(tokenId);
accessTokenDOMap.put(accessToken, dataDO);
} else {
String scope = resultSet.getString(8).trim();
AccessTokenDO accessTokenDO = accessTokenDOMap.get(accessToken);
accessTokenDO.setScope((String[]) ArrayUtils.add(accessTokenDO.getScope(), scope));
}
}
connection.commit();
} catch (SQLException e) {
String errorMsg = "Error occurred while retrieving 'ACTIVE or EXPIRED' access tokens for " +
"user tenant id : " + tenantId;
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
}
return new HashSet<>(accessTokenDOMap.values());
}
public Set<AccessTokenDO> getAccessTokensOfUserStore(int tenantId, String userStoreDomain) throws
IdentityOAuth2Exception {
//we do not support access token partitioning here
Connection connection = IdentityDatabaseUtil.getDBConnection();
if ((userStoreDomain != null)){
userStoreDomain.toUpperCase();
}
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
Map<String, AccessTokenDO> accessTokenDOMap = new HashMap<>();
try {
String sql = SQLQueries.LIST_ALL_TOKENS_IN_USER_STORE;
prepStmt = connection.prepareStatement(sql);
prepStmt.setInt(1, tenantId);
prepStmt.setString(2, userStoreDomain);
resultSet = prepStmt.executeQuery();
while (resultSet.next()) {
String accessToken = persistenceProcessor.getPreprocessedAccessTokenIdentifier(resultSet.getString(1));
if(accessTokenDOMap.get(accessToken) == null) {
String refreshToken = persistenceProcessor.
getPreprocessedRefreshToken(resultSet.getString(2));
Timestamp issuedTime = resultSet.getTimestamp(3, Calendar.getInstance(TimeZone.getTimeZone(UTC)));
Timestamp refreshTokenIssuedTime = resultSet.getTimestamp(4, Calendar.getInstance(TimeZone
.getTimeZone(UTC)));
long validityPeriodInMillis = resultSet.getLong(5);
long refreshTokenValidityPeriodMillis = resultSet.getLong(6);
String tokenType = resultSet.getString(7);
String[] scope = OAuth2Util.buildScopeArray(resultSet.getString(8));
String tokenId = resultSet.getString(9);
String authzUser = resultSet.getString(10);
String consumerKey = resultSet.getString(11);
AuthenticatedUser user = new AuthenticatedUser();
user.setUserName(authzUser);
user.setTenantDomain(OAuth2Util.getTenantDomain(tenantId));
user.setUserStoreDomain(userStoreDomain);
AccessTokenDO dataDO = new AccessTokenDO(consumerKey, user, scope, issuedTime,
refreshTokenIssuedTime, validityPeriodInMillis,
refreshTokenValidityPeriodMillis, tokenType);
dataDO.setAccessToken(accessToken);
dataDO.setRefreshToken(refreshToken);
dataDO.setTokenId(tokenId);
accessTokenDOMap.put(accessToken, dataDO);
} else {
String scope = resultSet.getString(8).trim();
AccessTokenDO accessTokenDO = accessTokenDOMap.get(accessToken);
accessTokenDO.setScope((String[]) ArrayUtils.add(accessTokenDO.getScope(), scope));
}
}
connection.commit();
} catch (SQLException e) {
String errorMsg = "Error occurred while retrieving 'ACTIVE or EXPIRED' access tokens for " +
"user in store domain : " + userStoreDomain + " and tenant id : " + tenantId;
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
}
return new HashSet<>(accessTokenDOMap.values());
}
public void renameUserStoreDomainInAccessTokenTable(int tenantId, String currentUserStoreDomain, String
newUserStoreDomain) throws IdentityOAuth2Exception {
//we do not support access token partitioning here
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement ps = null;
try {
String sqlQuery = SQLQueries.RENAME_USER_STORE_IN_ACCESS_TOKENS_TABLE;
ps = connection.prepareStatement(sqlQuery);
ps.setString(1, newUserStoreDomain.toUpperCase());
ps.setInt(2, tenantId);
ps.setString(3, currentUserStoreDomain.toUpperCase());
int count = ps.executeUpdate();
if (log.isDebugEnabled()) {
log.debug("Number of rows being updated : " + count);
}
connection.commit();
} catch (SQLException e) {
IdentityDatabaseUtil.rollBack(connection);
throw new IdentityOAuth2Exception("Error occurred while renaming user store : " + currentUserStoreDomain +
" in tenant :" + tenantId, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, ps);
}
}
public List<AuthzCodeDO> getLatestAuthorizationCodesOfTenant(int tenantId) throws IdentityOAuth2Exception {
//we do not support access token partitioning here
Connection connection = IdentityDatabaseUtil.getDBConnection();;
PreparedStatement ps = null;
ResultSet rs = null;
List<AuthzCodeDO> latestAuthzCodes = new ArrayList<>();
try {
String sqlQuery = SQLQueries.LIST_LATEST_AUTHZ_CODES_IN_TENANT;
ps = connection.prepareStatement(sqlQuery);
ps.setInt(1, tenantId);
rs = ps.executeQuery();
while (rs.next()) {
String authzCodeId = rs.getString(1);
String authzCode = rs.getString(2);
String consumerKey = rs.getString(3);
String authzUser = rs.getString(4);
String[] scope = OAuth2Util.buildScopeArray(rs.getString(5));
Timestamp issuedTime = rs.getTimestamp(6, Calendar.getInstance(TimeZone.getTimeZone(UTC)));
long validityPeriodInMillis = rs.getLong(7);
String callbackUrl = rs.getString(8);
String userStoreDomain = rs.getString(9);
AuthenticatedUser user = new AuthenticatedUser();
user.setUserName(authzUser);
user.setUserStoreDomain(userStoreDomain);
user.setTenantDomain(OAuth2Util.getTenantDomain(tenantId));
latestAuthzCodes.add(new AuthzCodeDO(user, scope, issuedTime, validityPeriodInMillis, callbackUrl,
consumerKey, authzCode, authzCodeId));
}
connection.commit();
} catch (SQLException e) {
IdentityDatabaseUtil.rollBack(connection);
throw new IdentityOAuth2Exception("Error occurred while retrieving latest authorization codes of tenant " +
":" + tenantId, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, rs, ps);
}
return latestAuthzCodes;
}
public List<AuthzCodeDO> getLatestAuthorizationCodesOfUserStore(int tenantId, String userStorDomain) throws
IdentityOAuth2Exception {
//we do not support access token partitioning here
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement ps = null;
ResultSet rs = null;
List<AuthzCodeDO> latestAuthzCodes = new ArrayList<>();
try {
String sqlQuery = SQLQueries.LIST_LATEST_AUTHZ_CODES_IN_USER_DOMAIN;
ps = connection.prepareStatement(sqlQuery);
ps.setInt(1, tenantId);
ps.setString(2, userStorDomain.toUpperCase());
rs = ps.executeQuery();
while (rs.next()) {
String authzCodeId = rs.getString(1);
String authzCode = rs.getString(2);
String consumerKey = rs.getString(3);
String authzUser = rs.getString(4);
String[] scope = OAuth2Util.buildScopeArray(rs.getString(5));
Timestamp issuedTime = rs.getTimestamp(6, Calendar.getInstance(TimeZone.getTimeZone(UTC)));
long validityPeriodInMillis = rs.getLong(7);
String callbackUrl = rs.getString(8);
AuthenticatedUser user = new AuthenticatedUser();
user.setUserName(authzUser);
user.setUserStoreDomain(userStorDomain);
user.setTenantDomain(OAuth2Util.getTenantDomain(tenantId));
latestAuthzCodes.add(new AuthzCodeDO(user, scope, issuedTime, validityPeriodInMillis, callbackUrl,
consumerKey, authzCode, authzCodeId));
}
connection.commit();
} catch (SQLException e) {
IdentityDatabaseUtil.rollBack(connection);
throw new IdentityOAuth2Exception("Error occurred while retrieving latest authorization codes of user " +
"store : " + userStorDomain + " in tenant :" + tenantId, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, rs, ps);
}
return latestAuthzCodes;
}
public void renameUserStoreDomainInAuthorizationCodeTable(int tenantId, String currentUserStoreDomain, String
newUserStoreDomain) throws IdentityOAuth2Exception {
//we do not support access token partitioning here
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement ps = null;
try {
String sqlQuery = SQLQueries.RENAME_USER_STORE_IN_AUTHORIZATION_CODES_TABLE;
ps = connection.prepareStatement(sqlQuery);
ps.setString(1, newUserStoreDomain.toUpperCase());
ps.setInt(2, tenantId);
ps.setString(3, currentUserStoreDomain.toUpperCase());
int count = ps.executeUpdate();
if (log.isDebugEnabled()) {
log.debug("Number of rows being updated : " + count);
}
connection.commit();
} catch (SQLException e) {
IdentityDatabaseUtil.rollBack(connection);
throw new IdentityOAuth2Exception("Error occurred while renaming user store : " + currentUserStoreDomain +
"in tenant :" + tenantId, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, ps);
}
}
public String getCodeIdByAuthorizationCode(String authzCode) throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
try {
String sql = SQLQueries.RETRIEVE_CODE_ID_BY_AUTHORIZATION_CODE;
prepStmt = connection.prepareStatement(sql);
prepStmt.setString(1, persistenceProcessor.getProcessedAuthzCode(authzCode));
resultSet = prepStmt.executeQuery();
if (resultSet.next()) {
return resultSet.getString("CODE_ID");
}
connection.commit();
return null;
} catch (SQLException e) {
String errorMsg = "Error occurred while retrieving 'Code ID' for " +
"authorization code : " + authzCode;
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
}
}
public String getAuthzCodeByCodeId(String codeId) throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
try {
String sql = SQLQueries.RETRIEVE_AUTHZ_CODE_BY_CODE_ID;
prepStmt = connection.prepareStatement(sql);
prepStmt.setString(1, codeId);
resultSet = prepStmt.executeQuery();
if (resultSet.next()) {
return resultSet.getString("AUTHORIZATION_CODE");
}
connection.commit();
return null;
} catch (SQLException e) {
String errorMsg = "Error occurred while retrieving 'Authorization Code' for " +
"authorization code : " + codeId;
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
}
}
public String getTokenIdByToken(String token) throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
try {
String sql = SQLQueries.RETRIEVE_TOKEN_ID_BY_TOKEN;
prepStmt = connection.prepareStatement(sql);
prepStmt.setString(1, persistenceProcessor.getProcessedAccessTokenIdentifier(token));
resultSet = prepStmt.executeQuery();
if (resultSet.next()) {
return resultSet.getString("TOKEN_ID");
}
connection.commit();
return null;
} catch (SQLException e) {
String errorMsg = "Error occurred while retrieving 'Token ID' for " +
"token : " + token;
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
}
}
public String getTokenByTokenId(String tokenId) throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
ResultSet resultSet = null;
try {
String sql = SQLQueries.RETRIEVE_TOKEN_BY_TOKEN_ID;
prepStmt = connection.prepareStatement(sql);
prepStmt.setString(1, tokenId);
resultSet = prepStmt.executeQuery();
if (resultSet.next()) {
return resultSet.getString("ACCESS_TOKEN");
}
connection.commit();
return null;
} catch (SQLException e) {
String errorMsg = "Error occurred while retrieving 'Access Token' for " +
"token id : " + tokenId;
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
}
}
private void updateTokenIdIfAutzCodeGrantType(String oldAccessTokenId, String newAccessTokenId, Connection
connection) throws IdentityOAuth2Exception {
PreparedStatement prepStmt = null;
try {
String updateNewTokenAgaintAuthzCodeSql;
if (connection.getMetaData().getDriverName().contains("MySQL")){
updateNewTokenAgaintAuthzCodeSql = SQLQueries.UPDATE_NEW_TOKEN_AGAINST_AUTHZ_CODE_MYSQL;
}else{
updateNewTokenAgaintAuthzCodeSql = SQLQueries.UPDATE_NEW_TOKEN_AGAINST_AUTHZ_CODE;
}
prepStmt = connection.prepareStatement(updateNewTokenAgaintAuthzCodeSql);
prepStmt.setString(1, newAccessTokenId);
prepStmt.setString(2, oldAccessTokenId);
prepStmt.executeUpdate();
} catch (SQLException e) {
throw new IdentityOAuth2Exception("Error while updating Access Token against authorization code for " +
"access token with ID : " + oldAccessTokenId, e);
} finally {
IdentityDatabaseUtil.closeStatement(prepStmt);
}
}
/**
* Get the list of roles associated for a given scope.
* @param scopeKey - The Scope Key.
* @return - The Set of roles associated with the given scope.
* @throws IdentityOAuth2Exception - If an SQL error occurs while retrieving the roles.
*/
public Set<String> getRolesOfScopeByScopeKey(String scopeKey) throws IdentityOAuth2Exception {
Connection connection = IdentityDatabaseUtil.getDBConnection();;
PreparedStatement ps = null;
ResultSet rs = null;
Set<String> roles = null;
try {
String sql = SQLQueries.RETRIEVE_ROLES_OF_SCOPE;
ps = connection.prepareStatement(sql);
ps.setString(1, scopeKey);
rs = ps.executeQuery();
if (rs.next()) {
String rolesString = rs.getString("ROLES");
if(!rolesString.isEmpty()){
roles = new HashSet<>(new ArrayList<>(Arrays.asList(rolesString.replaceAll(" ", "").split(","))));
}
}
connection.commit();
return roles;
} catch (SQLException e) {
String errorMsg = "Error getting roles of scope - " + scopeKey + " : " + e.getMessage();
throw new IdentityOAuth2Exception(errorMsg, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, rs, ps);
}
}
}