/*
* Copyright (c) 2014, 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.mgt.store;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.mgt.dto.UserIdentityClaimsDO;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
import org.wso2.carbon.user.core.UserCoreConstants;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
/**
* //TODO remove method when user is deleted
*/
public class JDBCIdentityDataStore extends InMemoryIdentityDataStore {
private static Log log = LogFactory.getLog(JDBCIdentityDataStore.class);
@Override
public void store(UserIdentityClaimsDO userIdentityDTO, UserStoreManager userStoreManager)
throws IdentityException {
if (userIdentityDTO == null || userIdentityDTO.getUserDataMap().isEmpty()) {
return;
}
// Before putting to cache, has to check this whether this available in the database
// Putting into cache
String userName = userIdentityDTO.getUserName();
String domainName = ((org.wso2.carbon.user.core.UserStoreManager) userStoreManager).getRealmConfiguration().
getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME);
userName = UserCoreUtil.addDomainToName(userName, domainName);
userIdentityDTO.setUserName(userName);
super.store(userIdentityDTO, userStoreManager);
int tenantId = MultitenantConstants.SUPER_TENANT_ID;
try {
tenantId = userStoreManager.getTenantId();
} catch (UserStoreException e) {
log.error("Error while getting tenant Id.", e);
}
Map<String, String> data = userIdentityDTO.getUserDataMap();
for (Map.Entry<String, String> entry : data.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
boolean isUserExists = false;
try {
isUserExists = isExistingUserDataValue(userName, tenantId, key);
} catch (SQLException e) {
throw IdentityException.error("Error occurred while checking if user existing", e);
}
try {
if (isUserExists) {
updateUserDataValue(userName, tenantId, key, value);
} else {
addUserDataValue(userName, tenantId, key, value);
}
} catch (SQLException e) {
throw IdentityException.error("Error occurred while persisting user data", e);
}
}
}
private boolean isExistingUserDataValue(String userName, int tenantId, String key) throws SQLException {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
ResultSet results;
boolean isUsernameCaseSensitive = IdentityUtil.isUserStoreInUsernameCaseSensitive(userName, tenantId);
try {
String query;
if (isUsernameCaseSensitive) {
query = SQLQuery.CHECK_EXIST_USER_DATA;
} else {
query = SQLQuery.CHECK_EXIST_USER_DATA_CASE_INSENSITIVE;
}
prepStmt = connection.prepareStatement(query);
prepStmt.setInt(1, tenantId);
prepStmt.setString(2, userName);
prepStmt.setString(3, key);
results = prepStmt.executeQuery();
if (results.next()) {
return true;
}
connection.commit();
} finally {
IdentityDatabaseUtil.closeStatement(prepStmt);
IdentityDatabaseUtil.closeConnection(connection);
}
return false;
}
private void addUserDataValue(String userName, int tenantId, String key, String value) throws SQLException {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
try {
prepStmt = connection.prepareStatement(SQLQuery.STORE_USER_DATA);
prepStmt.setInt(1, tenantId);
prepStmt.setString(2, userName);
prepStmt.setString(3, key);
prepStmt.setString(4, value);
prepStmt.execute();
connection.commit();
} finally {
IdentityDatabaseUtil.closeStatement(prepStmt);
IdentityDatabaseUtil.closeConnection(connection);
}
}
private void updateUserDataValue(String userName, int tenantId, String key, String value) throws SQLException {
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
boolean isUsernameCaseSensitive = IdentityUtil.isUserStoreInUsernameCaseSensitive(userName, tenantId);
try {
String query;
if (isUsernameCaseSensitive) {
query = SQLQuery.UPDATE_USER_DATA;
} else {
query = SQLQuery.UPDATE_USER_DATA_CASE_INSENSITIVE;
}
prepStmt = connection.prepareStatement(query);
prepStmt.setString(1, value);
prepStmt.setInt(2, tenantId);
prepStmt.setString(3, userName);
prepStmt.setString(4, key);
prepStmt.executeUpdate();
connection.commit();
} finally {
IdentityDatabaseUtil.closeStatement(prepStmt);
IdentityDatabaseUtil.closeConnection(connection);
}
}
@Override
public UserIdentityClaimsDO load(String userName, UserStoreManager userStoreManager) {
String domainName = ((org.wso2.carbon.user.core.UserStoreManager) userStoreManager).getRealmConfiguration().
getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME);
userName = UserCoreUtil.addDomainToName(userName, domainName);
// Getting from cache
UserIdentityClaimsDO dto = super.load(userName, userStoreManager);
if (dto != null) {
return dto;
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
ResultSet results = null;
try {
int tenantId = userStoreManager.getTenantId();
boolean isUsernameCaseSensitive = IdentityUtil.isUserStoreInUsernameCaseSensitive(userName, tenantId);
String query;
if (isUsernameCaseSensitive) {
query = SQLQuery.LOAD_USER_DATA;
} else {
query = SQLQuery.LOAD_USER_DATA_CASE_INSENSITIVE;
}
prepStmt = connection.prepareStatement(query);
prepStmt.setInt(1, tenantId);
prepStmt.setString(2, userName);
results = prepStmt.executeQuery();
Map<String, String> data = new HashMap<String, String>();
while (results.next()) {
data.put(results.getString(1), results.getString(2));
}
connection.commit();
if (log.isDebugEnabled()) {
log.debug("Retrieved identity data for:" + tenantId + ":" + userName);
for (Map.Entry<String, String> dataEntry : data.entrySet()) {
log.debug(dataEntry.getKey() + " : " + dataEntry.getValue());
}
}
dto = new UserIdentityClaimsDO(userName, data);
dto.setTenantId(tenantId);
try {
super.store(dto, userStoreManager);
} catch (IdentityException e) {
log.error("Error while reading user identity data", e);
}
return dto;
} catch (SQLException | UserStoreException e) {
log.error("Error while reading user identity data", e);
} finally {
IdentityDatabaseUtil.closeResultSet(results);
IdentityDatabaseUtil.closeStatement(prepStmt);
IdentityDatabaseUtil.closeConnection(connection);
}
return null;
}
@Override
public void remove(String userName, UserStoreManager userStoreManager) throws IdentityException {
super.remove(userName, userStoreManager);
String domainName = ((org.wso2.carbon.user.core.UserStoreManager) userStoreManager).
getRealmConfiguration().getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME);
userName = UserCoreUtil.addDomainToName(userName, domainName);
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement prepStmt = null;
try {
int tenantId = userStoreManager.getTenantId();
boolean isUsernameCaseSensitive = IdentityUtil.isUserStoreInUsernameCaseSensitive(userName, tenantId);
String query;
if (isUsernameCaseSensitive) {
query = SQLQuery.DELETE_USER_DATA;
} else {
query = SQLQuery.DELETE_USER_DATA_CASE_INSENSITIVE;
}
prepStmt = connection.prepareStatement(query);
prepStmt.setInt(1, tenantId);
prepStmt.setString(2, userName);
prepStmt.execute();
connection.commit();
} catch (SQLException | UserStoreException e) {
throw IdentityException.error("Error while reading user identity data", e);
} finally {
IdentityDatabaseUtil.closeStatement(prepStmt);
IdentityDatabaseUtil.closeConnection(connection);
}
}
/**
* This class contains the SQL queries.
* Schem:
* ||TENANT_ID || USERR_NAME || DATA_KEY || DATA_VALUE ||
* The primary key is tenantId, userName, DatKey combination
*/
private static class SQLQuery {
public static final String CHECK_EXIST_USER_DATA = "SELECT DATA_VALUE FROM IDN_IDENTITY_USER_DATA WHERE " +
"TENANT_ID = ? AND USER_NAME = ? AND DATA_KEY = ?";
public static final String CHECK_EXIST_USER_DATA_CASE_INSENSITIVE = "SELECT DATA_VALUE FROM " +
"IDN_IDENTITY_USER_DATA WHERE TENANT_ID = ? AND LOWER(USER_NAME) = LOWER(?) AND DATA_KEY = ?";
public static final String STORE_USER_DATA = "INSERT INTO IDN_IDENTITY_USER_DATA (TENANT_ID, USER_NAME, " +
"DATA_KEY, DATA_VALUE) VALUES (?,?,?,?)";
public static final String UPDATE_USER_DATA = "UPDATE IDN_IDENTITY_USER_DATA SET DATA_VALUE=? WHERE " +
"TENANT_ID=? AND USER_NAME=? AND DATA_KEY=?";
public static final String UPDATE_USER_DATA_CASE_INSENSITIVE = "UPDATE IDN_IDENTITY_USER_DATA SET " +
"DATA_VALUE=? WHERE TENANT_ID=? AND LOWER(USER_NAME)=LOWER(?) AND DATA_KEY=?";
public static final String LOAD_USER_DATA = "SELECT DATA_KEY, DATA_VALUE FROM IDN_IDENTITY_USER_DATA WHERE " +
"TENANT_ID = ? AND USER_NAME = ?";
public static final String LOAD_USER_DATA_CASE_INSENSITIVE = "SELECT " + "DATA_KEY, DATA_VALUE FROM " +
"IDN_IDENTITY_USER_DATA WHERE TENANT_ID = ? AND LOWER(USER_NAME) = LOWER(?)";
public static final String DELETE_USER_DATA = "DELETE FROM IDN_IDENTITY_USER_DATA WHERE " +
"TENANT_ID = ? AND USER_NAME = ?";
public static final String DELETE_USER_DATA_CASE_INSENSITIVE = "DELETE FROM IDN_IDENTITY_USER_DATA WHERE " +
"TENANT_ID = ? AND LOWER(USER_NAME) = LOWER(?)";
private SQLQuery() {
}
}
}