/*
* Copyright (c) 2015, 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.application.authenticator.fido.dao;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.yubico.u2f.data.DeviceRegistration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.identity.application.authenticator.fido.exception.FIDOAuthenticatorServerException;
import org.wso2.carbon.identity.application.authenticator.fido.util.FIDOAuthenticatorConstants;
import org.wso2.carbon.identity.core.util.IdentityCoreConstants;
import org.wso2.carbon.identity.core.util.IdentityDatabaseUtil;
import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.TimeZone;
/**
* Performs DAO operations related to the FIDO Device Store.
*/
public class DeviceStoreDAO {
private static Log log = LogFactory.getLog(DeviceStoreDAO.class);
private DeviceStoreDAO(){
}
public static DeviceStoreDAO getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final DeviceStoreDAO INSTANCE = new DeviceStoreDAO();
}
/**
* Add Device Registration to store.
*
* @param username The username of Device Registration.
* @param registration The FIDO Registration.
* @param timestamp
* @throws FIDOAuthenticatorServerException when SQL statement can not be executed.
*/
public void addDeviceRegistration(String username, DeviceRegistration registration, String tenantDomain,
String userStoreDomain, Timestamp timestamp)
throws FIDOAuthenticatorServerException {
if (log.isDebugEnabled()) {
log.debug("addDeviceRegistration inputs {username: " + username + ", tenantDomain: " + tenantDomain +
", userStoreDomain : " + userStoreDomain +", registration :" +
registration.toJsonWithAttestationCert() + "}");
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(FIDOAuthenticatorConstants.SQLQueries.ADD_DEVICE_REGISTRATION_QUERY);
preparedStatement.setInt(1, IdentityTenantUtil.getTenantId(tenantDomain));
preparedStatement.setString(2, userStoreDomain);
preparedStatement.setString(3, username);
preparedStatement.setTimestamp(4, timestamp, Calendar.getInstance(TimeZone.getTimeZone(IdentityCoreConstants.UTC)));
preparedStatement.setString(5, registration.getKeyHandle());
preparedStatement.setString(6, registration.toJson());
preparedStatement.executeUpdate();
if (!connection.getAutoCommit()) {
connection.commit();
}
} catch (SQLException e) {
throw new FIDOAuthenticatorServerException("Error when executing FIDO registration SQL : " +
FIDOAuthenticatorConstants.SQLQueries.ADD_DEVICE_REGISTRATION_QUERY, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, preparedStatement);
}
}
/**
* Retrieves Device Registration data from store.
*
* @param username The username of the Device Registration.
* @return Collection of Device Registration.
* @throws FIDOAuthenticatorServerException when SQL statement can not be executed.
*/
public Collection getDeviceRegistration(String username,String tenantDomain, String userStoreDomain)
throws FIDOAuthenticatorServerException {
if (log.isDebugEnabled()) {
log.debug("getDeviceRegistration inputs {username: " + username + ", tenantDomain: " + tenantDomain +
", userStoreDomain : " + userStoreDomain +"}");
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
Multimap<String, String> devices = ArrayListMultimap.create();
try {
preparedStatement = connection.prepareStatement(FIDOAuthenticatorConstants.SQLQueries.GET_DEVICE_REGISTRATION_QUERY);
preparedStatement.setInt(1, IdentityTenantUtil.getTenantId(tenantDomain));
preparedStatement.setString(2, userStoreDomain);
preparedStatement.setString(3, username);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
String keyHandle = resultSet.getString(FIDOAuthenticatorConstants.U2F_KEY_HANDLE);
String deviceData = resultSet.getString(FIDOAuthenticatorConstants.U2F_DEVICE_DATA);
devices.put(keyHandle, deviceData);
}
} catch (SQLException e) {
throw new FIDOAuthenticatorServerException(
"Error executing get device registration SQL : " +
FIDOAuthenticatorConstants.SQLQueries.GET_DEVICE_REGISTRATION_QUERY, e
);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, preparedStatement);
}
return devices.values();
}
/**
* Retrieves Device Registration metadata data from store.
*
* @param username The username of the Device Registration.
* @return Collection of Device Registration.
* @throws FIDOAuthenticatorServerException when SQL statement can not be executed.
*/
public ArrayList<String> getDeviceMetadata(String username, String tenantDomain, String userStoreDomain)
throws FIDOAuthenticatorServerException {
ResultSet resultSet = null;
ArrayList<String> devicesMetadata = new ArrayList<String>();
if (log.isDebugEnabled()) {
log.debug("getDeviceRegistration inputs {username: " + username + ", tenantDomain: " + tenantDomain +
", userStoreDomain : " + userStoreDomain +"}");
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(FIDOAuthenticatorConstants.SQLQueries.GET_DEVICE_REGISTRATION_QUERY);
preparedStatement.setInt(1, IdentityTenantUtil.getTenantId(tenantDomain));
preparedStatement.setString(2, userStoreDomain);
preparedStatement.setString(3, username);
resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
String deviceRemark = resultSet.getTimestamp(FIDOAuthenticatorConstants.U2F_DEVICE_METADATA).toString();
devicesMetadata.add(deviceRemark);
}
} catch (SQLException e) {
throw new FIDOAuthenticatorServerException(
"Error executing get device registration SQL : " +
FIDOAuthenticatorConstants.SQLQueries.GET_DEVICE_REGISTRATION_QUERY, e
);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, preparedStatement);
}
return devicesMetadata;
}
/**
* Remove all registered device from store.
*
* @param username
* @param tenantDomain
* @param userStoreDomain
* @throws FIDOAuthenticatorServerException
*/
public void removeAllRegistrations(String username, String tenantDomain, String userStoreDomain)
throws FIDOAuthenticatorServerException {
if (log.isDebugEnabled()) {
log.debug("removeRegistration inputs {username:" + username + "}");
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(FIDOAuthenticatorConstants.SQLQueries.REMOVE_ALL_REGISTRATION_QUERY);
preparedStatement.setInt(1, IdentityTenantUtil.getTenantId(tenantDomain));
preparedStatement.setString(2, userStoreDomain);
preparedStatement.setString(3, username);
preparedStatement.executeUpdate();
if (!connection.getAutoCommit()) {
connection.commit();
}
} catch (SQLException e) {
throw new FIDOAuthenticatorServerException(
"Error executing remove all registrations SQL : " +
FIDOAuthenticatorConstants.SQLQueries.REMOVE_ALL_REGISTRATION_QUERY, e
);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, preparedStatement);
}
}
/**
* Remove all registered device from store.
*
* @param username
* @param tenantDomain
* @param userStoreDomain
* @throws FIDOAuthenticatorServerException
*/
public void removeRegistration(String username, String tenantDomain, String userStoreDomain, Timestamp timestamp )
throws FIDOAuthenticatorServerException {
if (log.isDebugEnabled()) {
log.debug("removeRegistration inputs {username: " + username + ", tenantDomain: " + tenantDomain +
", userStoreDomain : " + userStoreDomain + "}");
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(FIDOAuthenticatorConstants.SQLQueries.REMOVE_REGISTRATION_QUERY);
preparedStatement.setInt(1, IdentityTenantUtil.getTenantId(tenantDomain));
preparedStatement.setString(2, userStoreDomain);
preparedStatement.setString(3, username);
preparedStatement.setTimestamp(4,timestamp);
preparedStatement.executeUpdate();
if (!connection.getAutoCommit()) {
connection.commit();
}
} catch (SQLException e) {
throw new FIDOAuthenticatorServerException(
"Error executing remove registrations SQL : " +
FIDOAuthenticatorConstants.SQLQueries.REMOVE_REGISTRATION_QUERY, e
);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, preparedStatement);
}
}
/**
* Update registration entry once domain name changed.
*
* @param tenantId
* @param currentUserStoreName
* @param newUserStoreName
* @throws FIDOAuthenticatorServerException
*/
public void updateDomainNameOfRegistration(int tenantId, String currentUserStoreName,
String newUserStoreName) throws FIDOAuthenticatorServerException{
if (log.isDebugEnabled()) {
log.debug("updateDomainNameOfRegistration inputs {tenantId: " + tenantId + ", currentUserStoreName: " +
currentUserStoreName +", newUserStoreName: " + newUserStoreName + "}");
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(FIDOAuthenticatorConstants.SQLQueries.UPDATE_USER_DOMAIN_NAME);
preparedStatement.setString(1, newUserStoreName.toUpperCase());
preparedStatement.setString(2, currentUserStoreName.toUpperCase());
preparedStatement.setInt(3, tenantId);
preparedStatement.executeUpdate();
if (!connection.getAutoCommit()) {
connection.commit();
}
} catch (SQLException e) {
throw new FIDOAuthenticatorServerException("Error when executing FIDO update domain name SQL : " +
FIDOAuthenticatorConstants.SQLQueries.UPDATE_USER_DOMAIN_NAME, e);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, preparedStatement);
}
}
/**
* Remove registration entry once user store domain deleted.
*
* @param tenantId
* @param userStoreName
* @throws FIDOAuthenticatorServerException
*/
public void deleteRegistrationFromDomain(int tenantId, String userStoreName) throws FIDOAuthenticatorServerException{
if (log.isDebugEnabled()) {
log.debug("deleteRegistrationFromDomain inputs {tenantId: " + tenantId + ", userStoreName: " + userStoreName +"}");
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(FIDOAuthenticatorConstants.SQLQueries.DELETE_DEVICE_REGISTRATION_FROM_DOMAIN);
preparedStatement.setInt(1, tenantId);
preparedStatement.setString(2, userStoreName.toUpperCase());
preparedStatement.executeUpdate();
if (!connection.getAutoCommit()) {
connection.commit();
}
} catch (SQLException e) {
throw new FIDOAuthenticatorServerException(
"Error executing remove registrations SQL on domain delete: " +
FIDOAuthenticatorConstants.SQLQueries.DELETE_DEVICE_REGISTRATION_FROM_DOMAIN, e
);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, null, preparedStatement);
}
}
private ResultSet getDeviceData(String username, String tenantDomain, String userStoreDomain)
throws FIDOAuthenticatorServerException {
if (log.isDebugEnabled()) {
log.debug("getDeviceRegistration inputs {username: " + username + ", tenantDomain: " + tenantDomain +
", userStoreDomain : " + userStoreDomain +"}");
}
Connection connection = IdentityDatabaseUtil.getDBConnection();
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
ArrayList<String> devicesMetadata = new ArrayList<String>();
try {
preparedStatement = connection.prepareStatement(FIDOAuthenticatorConstants.SQLQueries.GET_DEVICE_REGISTRATION_QUERY);
preparedStatement.setInt(1, IdentityTenantUtil.getTenantId(tenantDomain));
preparedStatement.setString(2, userStoreDomain);
preparedStatement.setString(3, username);
resultSet = preparedStatement.executeQuery();
} catch (SQLException e) {
throw new FIDOAuthenticatorServerException(
"Error executing get device registration SQL : " +
FIDOAuthenticatorConstants.SQLQueries.GET_DEVICE_REGISTRATION_QUERY, e
);
} finally {
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, preparedStatement);
}
return resultSet;
}
}