SQLConnector.java
/*******************************************************************************
* Copyright (c) 2019, RISE AB
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/
package se.sics.ace.examples;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import com.upokecenter.cbor.CBORObject;
import org.eclipse.californium.cose.CoseException;
import org.eclipse.californium.cose.OneKey;
import se.sics.ace.AceException;
import se.sics.ace.COSEparams;
import se.sics.ace.Constants;
import se.sics.ace.as.AccessTokenFactory;
import se.sics.ace.as.DBConnector;
/**
* This class provides SQL database connectivity for the Attribute Authority.
*
* @author Ludwig Seitz and Marco Tiloca
*
*/
public class SQLConnector implements DBConnector, AutoCloseable {
/**
* The user configured for access.
*/
private String currentUser;
/**
* The password configured for access.
*/
private String currentPassword;
/**
* A prepared connection.
*/
private Connection conn = null;
/**
* Records if the singleton connector is connected or disconnected
*/
private static boolean isConnected = false;
/**
* A prepared INSERT statement to add a new Resource Server.
*
* Parameters: rs id, cose encoding, default expiration time, psk, rpk
*/
protected PreparedStatement insertRS;
/**
* A prepared DELETE statement to remove a Resource Server
*
* Parameter: rs id.
*/
protected PreparedStatement deleteRS;
/**
* A prepared SELECT statement to get a set of RS for an audience
*
* Parameter: audience name
*/
protected PreparedStatement selectRS;
/**
* A prepared SELECT statement to get all RSs
*/
protected PreparedStatement selectAllRS;
/**
* A prepared INSERT statement to add a profile supported
* by a client or Resource Server
*
* Parameters: id, profile name
*/
protected PreparedStatement insertProfile;
/**
* A prepared DELETE statement to remove the profiles supported
* by a client or Resource Server
*
* Parameter: id
*/
protected PreparedStatement deleteProfiles;
/**
* A prepared SELECT statement to get all profiles for
* an audience and a client
*
* Parameters: audience name, client id
*/
protected PreparedStatement selectProfiles;
/**
* A prepared SELECT statement to get the profiles
* for a single client or RS.
*/
protected PreparedStatement selectProfile;
/**
* A prepared INSERT statement to add the key types supported
* by a client or Resource Server
*
* Parameters: id, key type
*/
protected PreparedStatement insertKeyType;
/**
* A prepared DELETE statement to remove the key types supported
* by a client or Resource Server
*
* Parameter: id
*/
protected PreparedStatement deleteKeyTypes;
/**
* A prepared SELECT statement to get a set of key types
*
* Parameters: audience name, client id
*/
protected PreparedStatement selectKeyTypes;
/**
* A prepared INSERT statement to add the scopes supported
* by a Resource Server
*
* Parameters: rs id, scope name
*/
protected PreparedStatement insertScope;
/**
* A prepared DELETE statement to remove the scopes supported
* by a Resource Server
*
* Parameter: rs id
*/
protected PreparedStatement deleteScopes;
/**
* A prepared SELECT statement to get a set of Scopes for a specific audience
*
* Parameter: audience id
*/
protected PreparedStatement selectScopes;
/**
* A prepared SELECT statement to get a set of Scopes for a specific RS
*
* Parameter: rs id
*/
protected PreparedStatement selectScopesForRS;
/**
* A prepared INSERT statement to add an audience a
* Resource Server identifies with
*
* Parameter: rs id, audience name
*/
protected PreparedStatement insertAudience;
/**
* A prepared DELETE statement to remove the audiences
* a Resource Server identifies with
*
* Parameter: rs id
*/
protected PreparedStatement deleteAudiences;
/**
* A prepared SELECT statement to get a set of audiences for an RS
*
* Parameter: rs id
*/
protected PreparedStatement selectAudiences;
/**
* A prepared INSERT statement to add an audience a
* Resource Server acting as OSCORE Group Manager identifies with
*
* Parameter: rs id, audience name
*/
protected PreparedStatement insertOSCOREGroupManager;
/**
* A prepared DELETE statement to remove the audiences
* a Resource Server acting as OSCORE Group Manager identifies with
*
* Parameter: rs id
*/
protected PreparedStatement deleteOSCOREGroupManagers;
/**
* A prepared SELECT statement to get a set of audiences
* an RS acting as OSCORE Group Manager identifies with
*
* Parameter: rs id
*/
protected PreparedStatement selectOSCOREGroupManagers;
/**
* A prepared INSERT statement to add a token type a
* Resource Server supports
*
* Parameters: rs id, token type
*/
protected PreparedStatement insertTokenType;
/**
* A prepared DELETE statement to remove the token types a
* a Resource Server supports
*
* Parameter: rs id
*/
protected PreparedStatement deleteTokenTypes;
/**
* A prepared SELECT statement to get a set of token types for an audience
*
* Parameter: audience name
*/
protected PreparedStatement selectTokenTypes;
/**
* A prepared INSERT statement to add a new client
*
* Parameters: client id, default audience, default scope, psk, rpk
*/
protected PreparedStatement insertClient;
/**
* A prepared DELETE statement to remove a client
*
* Parameter: client id
*/
protected PreparedStatement deleteClient;
/**
* A prepared SELECT statement to get the default audience for a client.
*
* Parameter: client id
*/
protected PreparedStatement selectDefaultAudience;
/**
* A prepared SELECT statement to get the default scope for a client.
*
* Parameter: client id
*/
protected PreparedStatement selectDefaultScope;
/**
* A prepared INSERT statement to add a new supported cose configuration
* for protecting CWTs
*
* Parameters: rs id, cose config
*/
protected PreparedStatement insertCose;
/**
* A prepared DELETE statement to remove a cose configuration
*
* Parameter: rs id
*/
protected PreparedStatement deleteCose;
/**
* A prepared SELECT statement to get the COSE configurations for
* an audience.
*
* Parameter: audience name
*/
protected PreparedStatement selectCOSE;
/**
* A prepared SELECT statement to get the default expiration time for
* a RS
*
* Parameter: audience name
*/
protected PreparedStatement selectExpiration;
/**
* A prepared SELECT statement to get a pre-shared token-protection
* key for an audience
*
* Parameter: audience name
*/
protected PreparedStatement selectRsTokenPSK;
/**
* A prepared SELECT statement to get the pre-shared authentication
* key for an RS
*
* Parameter: RS name
*
*/
protected PreparedStatement selectRsAuthPSK;
/**
* A prepared SELECT statement to get the public keys of an audience.
*
* Parameter: audience name
*/
protected PreparedStatement selectRsRPK;
/**
* A prepared SELECT statement to get a the pre-shared key for
* an client.
*
* Parameter: client id
*/
protected PreparedStatement selectCPSK;
/**
* A prepared SELECT statement to get the public key of a client.
*
* Parameter: client id
*/
protected PreparedStatement selectCRPK;
/**
* A prepared SELECT statement to fetch token ids and their
* expiration time form the claims table.
*/
protected PreparedStatement selectExpirationTime;
/**
* A prepared INSERT statement to add a claim of a token
* to the Claims table.
*
* Parameters: token cti, claim name, claim value
*/
protected PreparedStatement insertClaim;
/**
* A prepared DELETE statement to remove the claims of a token
* from the Claims table.
*
* Parameters: token cti
*/
protected PreparedStatement deleteClaims;
/**
* A prepared SELECT statement to select the claims of a token from
* the Claims table.
*
* Parameter: token cti
*/
protected PreparedStatement selectClaims;
/**
* A prepared INSERT statement to save a token's claims to the
* InvalidTokens table.
*/
protected PreparedStatement logInvalidToken;
/**
* A prepared SELECT statement to select the cti counter value from the
* cti counter table.
*/
protected PreparedStatement selectCtiCtr;
/**
* A prepared UPDATE statement to update the saved cti counter value in the
* cti counter table.
*/
protected PreparedStatement updateCtiCtr;
/**
* A prepared SELECT statement to select the exi Sequence Number value
* of a specific Resource Server from the RSs table.
*/
protected PreparedStatement selectExiSn;
/**
* A prepared UPDATE statement to update the exi Sequence Number value
* of a specific Resource Server in the RSs table.
*/
protected PreparedStatement updateExiSn;
/**
* A prepared INSERT statement to insert a new token to client mapping.
*/
protected PreparedStatement insertCti2Client;
/**
* A prepared SELECT statement to select the client identifier holding a
* token identified by its cti.
*/
protected PreparedStatement selectClientByCti;
/**
* A prepared SELECT statement to select all registered clients.
*/
protected PreparedStatement selectAllClients;
/**
* A prepared SELECT statement to select the token identifiers (cti)
* held by a client
*/
protected PreparedStatement selectCtisByClient;
/**
* A prepared SELECT statement to select the token identifier (cti)
* for an authorization grant
*/
protected PreparedStatement selectCtisByGrant;
/**
* A prepared INSERT statement to add a new authorization grant to
* access token cti mapping.
*/
protected PreparedStatement insertGrant2Cti;
/**
* A prepared DELETE statement to remove the mapping of a grant
* to an access token cti.
*/
protected PreparedStatement deleteGrant2Cti;
/**
* A prepared UPDATE statement to mark an authorization grant
* as used.
*/
protected PreparedStatement updateGrant;
/**
* A prepared SELECT statement to select the RS information
* for an authorization grant
*/
protected PreparedStatement selectRsInfoByGrant;
/**
* A prepared INSERT statement to add the RS Information
* for a given grant.
*/
protected PreparedStatement insertGrant2RsInfo;
/**
* A prepared SELECT statement to check if a grant is marked invalid
*/
protected PreparedStatement selectGrantValid;
/**
* The singleton instance of this connector
*/
private static SQLConnector connector = null;
/**
* The DB adapter
*/
private SQLDBAdapter adapter = null;
/**
* Gets the singleton instance of this connector.
*
* @param dbAdapter an adapter already set up with the database information, specific for each engine.
*
* @return the singleton instance
*
* @throws SQLException
*/
public static SQLConnector getInstance(SQLDBAdapter dbAdapter) throws SQLException {
if (SQLConnector.connector == null) {
SQLConnector.connector
= new SQLConnector(dbAdapter);
}
return SQLConnector.connector;
}
/**
* Create a new database connector either from given values or the
* defaults.
*
* @param dbAdapter handler for engine-db specific commands.
*
* @throws SQLException
*/
protected SQLConnector(SQLDBAdapter dbAdapter) throws SQLException {
this.adapter = dbAdapter;
this.conn = dbAdapter.getDBConnection();
SQLConnector.isConnected = true;
this.insertRS = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.rsTable + " VALUES (?,?,?,?,?,?);"));
this.deleteRS = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("DELETE FROM "
+ DBConnector.rsTable + " WHERE "
+ DBConnector.rsIdColumn + "=?;"));
this.selectRS = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.rsIdColumn
+ " FROM "
+ DBConnector.audiencesTable
+ " WHERE " + DBConnector.audColumn + "=? ORDER BY "
+ DBConnector.rsIdColumn + ";"));
this.selectAllRS = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.rsIdColumn
+ " FROM "
+ DBConnector.rsTable
+ " ORDER BY "
+ DBConnector.rsIdColumn + ";"));
this.insertProfile = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.profilesTable
+ " VALUES (?,?);"));
this.deleteProfiles = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("DELETE FROM "
+ DBConnector.profilesTable
+ " WHERE " + DBConnector.idColumn + "=?;"));
this.selectProfiles = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT * FROM "
+ DBConnector.profilesTable
+ " WHERE " + DBConnector.idColumn + " IN (SELECT "
+ DBConnector.rsIdColumn + " FROM "
+ DBConnector.audiencesTable
+ " WHERE " + DBConnector.audColumn
+ "=?) UNION SELECT * FROM "
+ DBConnector.profilesTable
+ " WHERE " + DBConnector.idColumn + "=? ORDER BY "
+ DBConnector.idColumn + ";"));
this.selectProfile = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT * FROM "
+ DBConnector.profilesTable
+ " WHERE " + DBConnector.idColumn + "=?;"));
this.insertKeyType = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.keyTypesTable
+ " VALUES (?,?);"));
this.deleteKeyTypes = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("DELETE FROM "
+ DBConnector.keyTypesTable
+ " WHERE " + DBConnector.idColumn + "=?;"));
this.selectKeyTypes = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT * FROM "
+ DBConnector.keyTypesTable
+ " WHERE " + DBConnector.idColumn + " IN (SELECT "
+ DBConnector.rsIdColumn + " FROM "
+ DBConnector.audiencesTable
+ " WHERE " + DBConnector.audColumn + "=?) ORDER BY "
+ DBConnector.idColumn + ";"));
this.insertScope = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.scopesTable
+ " VALUES (?,?);"));
this.deleteScopes = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("DELETE FROM "
+ DBConnector.scopesTable
+ " WHERE " + DBConnector.rsIdColumn + "=?;"));
this.selectScopes = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT * FROM "
+ DBConnector.scopesTable
+ " WHERE " + DBConnector.rsIdColumn + " IN (SELECT "
+ DBConnector.rsIdColumn + " FROM "
+ DBConnector.audiencesTable
+ " WHERE " + DBConnector.audColumn + "=?) ORDER BY "
+ DBConnector.rsIdColumn + ";"));
this.selectScopesForRS = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT * FROM "
+ DBConnector.scopesTable
+ " WHERE " + DBConnector.rsIdColumn + "=? ORDER BY "
+ DBConnector.rsIdColumn + ";"));
this.insertAudience = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.audiencesTable
+ " VALUES (?,?);"));
this.deleteAudiences = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("DELETE FROM "
+ DBConnector.audiencesTable
+ " WHERE " + DBConnector.rsIdColumn + "=?;"));
this.selectAudiences = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.audColumn + " FROM "
+ DBConnector.audiencesTable
+ " WHERE " + DBConnector.rsIdColumn + "=? ORDER BY "
+ DBConnector.audColumn + ";"));
this.insertOSCOREGroupManager = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.oscoreGroupManagersTable
+ " VALUES (?,?);"));
this.deleteOSCOREGroupManagers = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("DELETE FROM "
+ DBConnector.oscoreGroupManagersTable
+ " WHERE " + DBConnector.rsIdColumn + "=?;"));
this.selectOSCOREGroupManagers = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.audColumn + " FROM "
+ DBConnector.oscoreGroupManagersTable
+ " WHERE " + DBConnector.rsIdColumn + "=? ORDER BY "
+ DBConnector.audColumn + ";"));
this.insertTokenType = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.tokenTypesTable
+ " VALUES (?,?);"));
this.deleteTokenTypes = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("DELETE FROM "
+ DBConnector.tokenTypesTable
+ " WHERE " + DBConnector.rsIdColumn + "=?;"));
this.selectTokenTypes = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT * FROM "
+ DBConnector.tokenTypesTable
+ " WHERE " + DBConnector.rsIdColumn + " IN (SELECT "
+ DBConnector.rsIdColumn + " FROM "
+ DBConnector.audiencesTable
+ " WHERE " + DBConnector.audColumn + "=?) ORDER BY "
+ DBConnector.rsIdColumn + ";"));
this.insertClient = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.cTable
+ " VALUES (?,?,?,?,?);"));
this.deleteClient = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("DELETE FROM "
+ DBConnector.cTable
+ " WHERE " + DBConnector.clientIdColumn + "=?;"));
this.selectDefaultAudience = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.defaultAud + " FROM "
+ DBConnector.cTable
+ " WHERE " + DBConnector.clientIdColumn + "=?;"));
this.selectDefaultScope = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.defaultScope + " FROM "
+ DBConnector.cTable
+ " WHERE " + DBConnector.clientIdColumn + "=?;"));
this.insertCose = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.coseTable
+ " VALUES (?,?);"));
this.deleteCose = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("DELETE FROM "
+ DBConnector.coseTable
+ " WHERE " + DBConnector.rsIdColumn + "=?;"));
this.selectCOSE = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT * "
+ " FROM " + DBConnector.coseTable
+ " WHERE " + DBConnector.rsIdColumn + " IN (SELECT "
+ DBConnector.rsIdColumn + " FROM "
+ DBConnector.audiencesTable
+ " WHERE " + DBConnector.audColumn + "=?) ORDER BY "
+ DBConnector.rsIdColumn + ";"));
this.selectExpiration = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.expColumn
+ " FROM " + DBConnector.rsTable
+ " WHERE " + DBConnector.rsIdColumn + "=?;"));
this.selectRsTokenPSK = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.tokenPskColumn
+ " FROM " + DBConnector.rsTable
+ " WHERE " + DBConnector.rsIdColumn + " IN (SELECT "
+ DBConnector.rsIdColumn + " FROM "
+ DBConnector.audiencesTable
+ " WHERE " + DBConnector.audColumn + "=?);"));
this.selectRsAuthPSK = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.authPskColumn
+ " FROM " + DBConnector.rsTable
+ " WHERE " + DBConnector.rsIdColumn + "=?;"));
this.selectRsRPK = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.rpkColumn
+ " FROM " + DBConnector.rsTable
+ " WHERE " + DBConnector.rsIdColumn + " IN (SELECT "
+ DBConnector.rsIdColumn + " FROM "
+ DBConnector.audiencesTable
+ " WHERE " + DBConnector.audColumn + "=?);"));
this.selectCPSK = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.authPskColumn
+ " FROM " + DBConnector.cTable
+ " WHERE " + DBConnector.clientIdColumn + "=?;"));
this.selectCRPK = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.rpkColumn
+ " FROM " + DBConnector.cTable
+ " WHERE " + DBConnector.clientIdColumn + "=?;"));
this.selectExpirationTime = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.ctiColumn + ","
+ DBConnector.claimValueColumn
+ " FROM "
+ DBConnector.claimsTable
+ " WHERE " + DBConnector.claimNameColumn + "="
+ Constants.EXP + ";"));
this.insertClaim = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.claimsTable
+ " VALUES (?,?,?);"));
this.deleteClaims = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("DELETE FROM "
+ DBConnector.claimsTable
+ " WHERE " + DBConnector.ctiColumn + "=?;"));
this.selectClaims = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.claimNameColumn + ","
+ DBConnector.claimValueColumn + " FROM "
+ DBConnector.claimsTable
+ " WHERE " + DBConnector.ctiColumn + "=?;"));
this.logInvalidToken = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.oldTokensTable
+ " SELECT * FROM " + DBConnector.claimsTable
+ " WHERE " + DBConnector.ctiColumn + "=?;"));
this.selectCtiCtr = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.ctiCounterColumn + " FROM "
+ DBConnector.ctiCounterTable
+ ";"));
this.updateCtiCtr = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("UPDATE "
+ DBConnector.ctiCounterTable
+ " SET " + DBConnector.ctiCounterColumn + "=?;"));
this.selectExiSn = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.exiSeqNumColumn + " FROM "
+ DBConnector.rsTable
+ " WHERE " + DBConnector.rsIdColumn + "=?;"));
this.updateExiSn = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("UPDATE "
+ DBConnector.rsTable
+ " SET " + DBConnector.exiSeqNumColumn + "=?"
+ " WHERE " + DBConnector.rsIdColumn
+ "=?;"));
this.insertCti2Client = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.cti2clientTable
+ " VALUES (?,?);"));
this.selectAllClients = this.conn.prepareStatement("SELECT "
+ DBConnector.clientIdColumn + " FROM "
+ DBConnector.cTable + ";");
this.selectClientByCti = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.clientIdColumn + " FROM "
+ DBConnector.cti2clientTable
+ " WHERE " + DBConnector.ctiColumn + "=?;"));
this.selectCtisByClient= this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.ctiColumn + " FROM "
+ DBConnector.cti2clientTable
+ " WHERE " + DBConnector.clientIdColumn + "=?;"));
this.selectCtisByGrant = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.ctiColumn + " FROM "
+ DBConnector.grant2ctiTable
+ " WHERE " + DBConnector.grantColumn + "=?;"));
this.insertGrant2Cti = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.grant2ctiTable
+ " VALUES (?,?,?);"));
this.deleteGrant2Cti = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("DELETE FROM "
+ DBConnector.grant2ctiTable
+ " WHERE " + DBConnector.grantColumn + "=?;"));
this.updateGrant = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("UPDATE "
+ DBConnector.grant2ctiTable
+ " SET " + DBConnector.grantValidColumn + "=FALSE"
+ " WHERE " + DBConnector.grantColumn
+ "=?;"));
this.selectRsInfoByGrant = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.claimNameColumn + ","
+ DBConnector.claimValueColumn + " FROM "
+ DBConnector.grant2RSInfoTable
+ " WHERE " + DBConnector.grantColumn + "=?;"));
this.insertGrant2RsInfo = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("INSERT INTO "
+ DBConnector.grant2RSInfoTable
+ " VALUES (?,?,?);"));
this.selectGrantValid = this.conn.prepareStatement(
dbAdapter.updateEngineSpecificSQL("SELECT "
+ DBConnector.grantValidColumn + " FROM "
+ DBConnector.grant2ctiTable
+ " WHERE " + DBConnector.grantColumn + "=?;"));
}
/**
* @return the properties of the current database user
*/
public Properties getCurrentUserProperties() {
Properties connectionProps = new Properties();
connectionProps.put("user", this.currentUser);
connectionProps.put("password", this.currentPassword);
return connectionProps;
}
/**
* Set the current user properties
* @param user the username of the new current user
* @param password the password of the new current user
*/
public void setCurrentUser(String user, String password) {
this.currentUser = user;
this.currentPassword = password;
}
/**
* Create the necessary database and tables. Requires the
* admin user password.
* @param dbAdapter
*
* @param adminUser the admin user name
* @param adminPwd the admin user password
* @throws AceException
*/
public static void createDB(SQLDBAdapter dbAdapter, String adminUser, String adminPwd) throws AceException {
if (adminPwd == null) {
throw new AceException(
"Cannot initialize the database without the password");
}
dbAdapter.createDBAndTables(adminUser, adminPwd);
}
/**
* Deletes the whole database.
*
* CAUTION: This method really does what is says, without asking you again!
* It's main function is to clean the database during test runs.
*
* @param dbAdapter handler for engine-db specific commands, containing DB name and owner data as well.
* @param adminUser the admin user name
* @param adminPwd the admin password
* @throws AceException
* @throws SQLException
*/
public static void wipeDatabase(SQLDBAdapter dbAdapter, String adminUser, String adminPwd) throws AceException {
if(SQLConnector.connector != null)
{
SQLConnector.connector.close();
}
dbAdapter.wipeDB(adminUser, adminPwd);
}
/**
* Close the connections. After this any other method calls to this
* object will lead to an exception.
*
* @throws AceException
*/
@Override
public synchronized void close() throws AceException {
if (SQLConnector.isConnected) {
SQLConnector.isConnected = false;
try {
this.conn.close();
SQLConnector.connector = null;
} catch (SQLException e)
{
throw new AceException(e.getMessage());
}
}
}
/**
* Returns a common value that the client supports (first param )
* and that every RS supports (every set in the map)
*
* @param client the set of values the client supports
* @param rss the map of sets of values the rs support
*
* @return the common value or null if there isn't any
* @throws AceException
*/
private static String getCommonValue(Set<String> client,
Map<String,Set<String>> rss) throws AceException {
if (client == null || rss == null) {
throw new AceException(
"getCommonValue() requires non-null parameters");
}
for (String clientVal : client) {
boolean isSupported = true;
for (String rs : rss.keySet()) {
if (!rss.get(rs).contains(clientVal)) {
isSupported = false;
}
}
if (isSupported) {
return clientVal;
}
}
return null;
}
@Override
public synchronized String getSupportedProfile(
String clientId, Set<String> audience) throws AceException {
if (clientId == null || audience == null) {
throw new AceException(
"getSupportedProfile() requires non-null parameters");
}
Map<String, Set<String>> rsProfiles = new HashMap<>();
Set<String> clientProfiles = new HashSet<>();
for (String aud : audience) {
try {
this.selectProfiles.setString(1, aud);
this.selectProfiles.setString(2, clientId);
ResultSet result = this.selectProfiles.executeQuery();
this.selectProfiles.clearParameters();
while(result.next()) {
String id = result.getString(DBConnector.idColumn);
String profile = result.getString(
DBConnector.profileColumn);
if (id.equals(clientId)) {
clientProfiles.add(profile);
} else if (rsProfiles.containsKey(id)) {
Set<String> foo = rsProfiles.get(id);
foo.add(profile);
rsProfiles.put(id, foo);
} else {
Set<String> bar = new HashSet<>();
bar.add(profile);
rsProfiles.put(id, bar);
}
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
return getCommonValue(clientProfiles, rsProfiles);
}
@Override
public boolean hasDefaultProfile(String clientId) throws AceException {
if (clientId == null ) {
throw new AceException(
"hasDefaultProfile() requires non-null clientId");
}
try {
this.selectProfile.setString(1, clientId);
ResultSet result = this.selectProfile.executeQuery();
this.selectProfile.clearParameters();
int i = 0;
while (result.next()) {
i ++;
}
result.close();
return (i==1 ? true:false);
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized Set<String> getSupportedPopKeyTypes(Set<String> aud)
throws AceException {
if (aud == null) {
throw new AceException(
"getSupportedPopKeyType() requires non-null parameter");
}
Map<String, Set<String>> rsKeyTypes = new HashMap<>();
for (String audE : aud) {
try {
this.selectKeyTypes.setString(1, audE);
ResultSet result = this.selectKeyTypes.executeQuery();
this.selectKeyTypes.clearParameters();
while(result.next()) {
String id = result.getString(DBConnector.idColumn);
String keyType = result.getString(
DBConnector.keyTypeColumn);
if (rsKeyTypes.containsKey(id)) {
Set<String> foo = rsKeyTypes.get(id);
foo.add(keyType);
rsKeyTypes.put(id, foo);
} else {
Set<String> bar = new HashSet<>();
bar.add(keyType);
rsKeyTypes.put(id, bar);
}
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
Set<String> typeSet = null;
for (Map.Entry<String, Set<String>> rs : rsKeyTypes.entrySet()) {
if (typeSet == null) {
typeSet = new HashSet<>();
typeSet.addAll(rs.getValue());
} else {
Set<String> iterSet = new HashSet<>(typeSet);
for (String keyType : iterSet) {
if (!rs.getValue().contains(keyType)) {
typeSet.remove(keyType);
}
}
if (typeSet.isEmpty()) {
return null;
}
}
}
return typeSet;
}
@Override
public synchronized Short getSupportedTokenType(Set<String> aud)
throws AceException {
if (aud == null) {
throw new AceException(
"getSupportedTokenType() requires non-null aud");
}
//Note: We store the token types as Strings in the DB
Map<String, Set<String>> tokenTypes = new HashMap<>();
for (String audE : aud) {
try {
this.selectTokenTypes.setString(1, audE);
ResultSet result = this.selectTokenTypes.executeQuery();
this.selectTokenTypes.clearParameters();
while(result.next()) {
String id = result.getString(DBConnector.rsIdColumn);
String tokenType = result.getString(
DBConnector.tokenTypeColumn);
if (tokenTypes.containsKey(id)) {
Set<String> foo = tokenTypes.get(id);
foo.add(tokenType);
tokenTypes.put(id, foo);
} else {
Set<String> bar = new HashSet<>();
bar.add(tokenType);
tokenTypes.put(id, bar);
}
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
Set<String> refSet = null;
for (Map.Entry<String, Set<String>> rs : tokenTypes.entrySet()) {
if (refSet == null) {
refSet = new HashSet<>();
refSet.addAll(rs.getValue());
} else {
Set<String> iterSet = new HashSet<>(refSet);
for (String tokenType : iterSet) {
if (!rs.getValue().contains(tokenType)) {
refSet.remove(tokenType);
}
}
if (refSet.isEmpty()) {
return null;
}
}
}
//Get the first remaining value
if (refSet != null && !refSet.isEmpty()) {
String tokenType = refSet.iterator().next();
for (short i=0; i<AccessTokenFactory.ABBREV.length; i++) {
if (tokenType.equals(AccessTokenFactory.ABBREV[i])) {
return i;
}
}
}
//The audience was empty or didn't support any token types
throw new AceException("No token types found for audience: " + aud);
}
@Override
public synchronized COSEparams getSupportedCoseParams(Set<String> aud)
throws AceException, CoseException {
if (aud == null) {
throw new AceException(
"getSupportedCoseParams() requires non-null aud");
}
Map<String, Set<String>> cose = new HashMap<>();
for (String audE : aud) {
try {
this.selectCOSE.setString(1, audE);
ResultSet result = this.selectCOSE.executeQuery();
this.selectCOSE.clearParameters();
while(result.next()) {
String id = result.getString(DBConnector.rsIdColumn);
String coseParam = result.getString(
DBConnector.coseColumn);
if (cose.containsKey(id)) {
Set<String> foo = cose.get(id);
foo.add(coseParam);
cose.put(id, foo);
} else {
Set<String> bar = new HashSet<>();
bar.add(coseParam);
cose.put(id, bar);
}
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
Set<String> refSet = null;
for (Map.Entry<String, Set<String>> rs : cose.entrySet()) {
if (refSet == null) {
refSet = new HashSet<>();
refSet.addAll(rs.getValue());
} else {
for (String tokenType : refSet) {
if (!rs.getValue().contains(tokenType)) {
refSet.remove(tokenType);
}
}
if (refSet.isEmpty()) {
return null;
}
}
}
//Get the first remaining value
if (refSet != null && !refSet.isEmpty()) {
String result = refSet.iterator().next();
return COSEparams.parse(result);
}
//The audience was empty or didn't support any token types
throw new AceException("No cose parameters found for audience: " + aud);
}
@Override
public synchronized boolean isScopeSupported(String aud, String scope)
throws AceException {
if (scope == null || aud == null) {
throw new AceException(
"isScopeSupported() requires non-null parameters");
}
Set<String> allRS = getRSS(aud);
Set<String> supportingSope = new HashSet<>();
try {
this.selectScopes.setString(1, aud);
ResultSet result = this.selectScopes.executeQuery();
this.selectScopes.clearParameters();
while (result.next()) {
String scp = result.getString(DBConnector.scopeColumn);
if (scp.equals(scope)) {
supportingSope.add(result.getString(DBConnector.rsIdColumn));
}
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
if (supportingSope.containsAll(allRS)) {
return true;
}
return false;
}
@Override
public synchronized String getDefaultScope(String clientId)
throws AceException {
if (clientId == null) {
throw new AceException(
"getDefaultScope() requires non-null clientId");
}
try {
this.selectDefaultScope.setString(1, clientId);
ResultSet result = this.selectDefaultScope.executeQuery();
this.selectDefaultScope.clearParameters();
if (result.next()) {
String scope = result.getString(DBConnector.defaultScope);
result.close();
return scope;
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return null;
}
@Override
public synchronized String getDefaultAudience(String clientId)
throws AceException {
if (clientId == null) {
throw new AceException(
"getDefaultAudience() requires non-null clientId");
}
try {
this.selectDefaultAudience.setString(1, clientId);
ResultSet result = this.selectDefaultAudience.executeQuery();
this.selectDefaultAudience.clearParameters();
if (result.next()) {
String aud = result.getString(DBConnector.defaultAud);
result.close();
return aud;
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return null;
}
@Override
public synchronized Set<String> getRSS(String aud) throws AceException {
if (aud == null) {
throw new AceException(
"getRSS() requires non-null aud");
}
Set<String> rss = new HashSet<>();
try {
this.selectRS.setString(1, aud);
ResultSet result = this.selectRS.executeQuery();
this.selectRS.clearParameters();
while (result.next()) {
rss.add(result.getString(DBConnector.rsIdColumn));
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
if (rss.isEmpty()) {
return Collections.emptySet();
}
return rss;
}
@Override
public synchronized Set<String> getRSS() throws AceException {
Set<String> rss = new HashSet<>();
try {
ResultSet result = this.selectAllRS.executeQuery();
while (result.next()) {
rss.add(result.getString(DBConnector.rsIdColumn));
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
if (rss.isEmpty()) {
return null;
}
return rss;
}
@Override
public synchronized long getExpTime(Set<String> aud) throws AceException {
if (aud == null) {
throw new AceException(
"getExpTime() requires non-null audience");
}
long smallest = Long.MAX_VALUE;
for (String audE : aud) {
Set<String> rsIds = getRSS(audE);
for (String myRS : rsIds) {
try {
this.selectExpiration.setString(1, myRS);
ResultSet result = this.selectExpiration.executeQuery();
this.selectExpiration.clearParameters();
while (result.next()) {
long val = result.getLong(DBConnector.expColumn);
if (val < smallest) {
smallest = val;
}
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
}
return smallest;
}
@Override
public synchronized Set<String> getAudiences(String rsId)
throws AceException {
if (rsId == null) {
throw new AceException(
"getAudiences() requires non-null rsId");
}
Set<String> auds = new HashSet<>();
try {
this.selectAudiences.setString(1, rsId);
ResultSet result = this.selectAudiences.executeQuery();
this.selectAudiences.clearParameters();
while (result.next()) {
auds.add(result.getString(DBConnector.audColumn));
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return auds;
}
@Override
public synchronized Set<String> getOSCOREGroupManagers(String rsId)
throws AceException {
if (rsId == null) {
throw new AceException(
"getOSCOREGroupManagers() requires non-null rsId");
}
Set<String> auds = new HashSet<>();
try {
this.selectOSCOREGroupManagers.setString(1, rsId);
ResultSet result = this.selectOSCOREGroupManagers.executeQuery();
this.selectOSCOREGroupManagers.clearParameters();
while (result.next()) {
auds.add(result.getString(DBConnector.audColumn));
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return auds;
}
@Override
public synchronized Set<String> getScopes(String rsId) throws AceException
{
if (rsId == null) {
throw new AceException(
"getScopes() requires non-null rsId");
}
Set<String> scopes = new HashSet<>();
try {
this.selectScopesForRS.setString(1, rsId);
ResultSet result = this.selectScopesForRS.executeQuery();
this.selectScopesForRS.clearParameters();
while (result.next()) {
scopes.add(result.getString(DBConnector.scopeColumn));
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return scopes;
}
@Override
public synchronized OneKey getRsTokenPSK(String rsId) throws AceException {
if (rsId == null) {
throw new AceException(
"getRsPSK() requires non-null rsId");
}
try {
this.selectRsTokenPSK.setString(1, rsId);
ResultSet result = this.selectRsTokenPSK.executeQuery();
this.selectRsTokenPSK.clearParameters();
byte[] key = null;
if (result.next()) {
key = result.getBytes(DBConnector.tokenPskColumn);
}
result.close();
if (key != null) {
CBORObject cKey = CBORObject.DecodeFromBytes(key);
return new OneKey(cKey);
}
return null;
} catch (SQLException | CoseException e) {
throw new AceException(e.getMessage());
}
}
@Override
public OneKey getRsAuthPSK(String rsId) throws AceException {
if (rsId == null) {
throw new AceException(
"getRsPSK() requires non-null rsId");
}
try {
this.selectRsAuthPSK.setString(1, rsId);
ResultSet result = this.selectRsAuthPSK.executeQuery();
this.selectRsAuthPSK.clearParameters();
byte[] key = null;
if (result.next()) {
key = result.getBytes(DBConnector.authPskColumn);
}
result.close();
if (key != null) {
CBORObject cKey = CBORObject.DecodeFromBytes(key);
return new OneKey(cKey);
}
return null;
} catch (SQLException | CoseException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized OneKey getRsRPK(String rsId) throws AceException {
if (rsId == null) {
throw new AceException(
"getRsRPK() requires non-null rsId");
}
try {
this.selectRsRPK.setString(1, rsId);
ResultSet result = this.selectRsRPK.executeQuery();
this.selectRsRPK.clearParameters();
byte[] key = null;
if (result.next()) {
key = result.getBytes(DBConnector.rpkColumn);
}
result.close();
if (key != null) {
CBORObject cKey = CBORObject.DecodeFromBytes(key);
return new OneKey(cKey);
}
return null;
} catch (SQLException | CoseException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized OneKey getCPSK(String clientId) throws AceException {
if (clientId == null) {
throw new AceException(
"getCPSK() requires non-null clientId");
}
try {
this.selectCPSK.setString(1, clientId);
ResultSet result = this.selectCPSK.executeQuery();
this.selectCPSK.clearParameters();
byte[] key = null;
if (result.next()) {
key = result.getBytes(DBConnector.authPskColumn);
}
result.close();
if (key != null) {
CBORObject cKey = CBORObject.DecodeFromBytes(key);
return new OneKey(cKey);
}
return null;
} catch (SQLException | CoseException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized OneKey getCRPK(String clientId) throws AceException {
if (clientId == null) {
throw new AceException(
"getCRPK() requires non-null clientId");
}
try {
this.selectCRPK.setString(1, clientId);
ResultSet result = this.selectCRPK.executeQuery();
this.selectCRPK.clearParameters();
byte[] key = null;
if (result.next()) {
key = result.getBytes(DBConnector.rpkColumn);
}
result.close();
if (key != null) {
CBORObject cKey = CBORObject.DecodeFromBytes(key);
return new OneKey(cKey);
}
return null;
} catch (SQLException | CoseException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized void addRS(String rsId, Set<String> profiles,
Set<String> scopes, Set<String> auds, Set<String> keyTypes,
Set<Short> tokenTypes, Set<COSEparams> cose, long expiration,
OneKey authPsk, OneKey tokenPsk, OneKey publicKey)
throws AceException {
if (rsId == null || rsId.isEmpty()) {
throw new AceException("RS must have non-null, non-empty identifier");
}
if (tokenPsk == null && publicKey == null) {
throw new AceException("Cannot register a RS without a key for"
+" protecting tokens");
}
if (profiles.isEmpty()) {
throw new AceException("RS must support at least one profile");
}
if (tokenTypes.isEmpty()) {
throw new AceException("RS must support at least one token type");
}
if (keyTypes.isEmpty()) {
throw new AceException("RS must support at least one PoP key type");
}
if (expiration <= 0L) {
throw new AceException("RS must have default expiration time > 0");
}
// Prevent adding an rs that has an identifier that is equal to an
// existing audience
try {
this.selectRS.setString(1, rsId);
ResultSet result = this.selectRS.executeQuery();
this.selectRS.clearParameters();
if (result.next()) {
result.close();
throw new AceException(
"RsId equal to existing audience id: " + rsId);
}
result.close();
this.insertRS.setString(1, rsId);
this.insertRS.setLong(2, expiration);
if (tokenPsk != null) {
this.insertRS.setBytes(3, tokenPsk.EncodeToBytes());
} else {
this.insertRS.setBytes(3, null);
}
if (authPsk != null) {
this.insertRS.setBytes(4, authPsk.EncodeToBytes());
} else {
this.insertRS.setBytes(4, null);
}
if (publicKey != null) {
this.insertRS.setBytes(5, publicKey.EncodeToBytes());
} else {
this.insertRS.setBytes(5, null);
}
// Initialize to 0 the sequence number to use when
// issuing to this RS tokens with the 'exi' claim
this.insertRS.setInt(6, 0);
this.insertRS.execute();
this.insertRS.clearParameters();
for (String profile : profiles) {
this.insertProfile.setString(1, rsId);
this.insertProfile.setString(2, profile);
this.insertProfile.execute();
}
this.insertProfile.clearParameters();
for (String scope : scopes) {
this.insertScope.setString(1, rsId);
this.insertScope.setString(2, scope);
this.insertScope.execute();
}
this.insertScope.clearParameters();
for (String aud : auds) {
this.insertAudience.setString(1, rsId);
this.insertAudience.setString(2, aud);
this.insertAudience.execute();
}
this.insertAudience.clearParameters();
//The RS always recognizes itself as a singleton audience
this.insertAudience.setString(1, rsId);
this.insertAudience.setString(2, rsId);
this.insertAudience.execute();
this.insertAudience.clearParameters();
for (String keyType : keyTypes) {
this.insertKeyType.setString(1, rsId);
this.insertKeyType.setString(2, keyType);
this.insertKeyType.execute();
}
this.insertKeyType.clearParameters();
for (short tokenType : tokenTypes) {
this.insertTokenType.setString(1, rsId);
this.insertTokenType.setString(2,
AccessTokenFactory.ABBREV[tokenType]);
this.insertTokenType.execute();
}
this.insertTokenType.clearParameters();
for (COSEparams coseP : cose) {
this.insertCose.setString(1, rsId);
this.insertCose.setString(2, coseP.toString());
this.insertCose.execute();
}
this.insertCose.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public void addOSCOREGroupManagers(String rsId, Set<String> auds) throws AceException {
if (rsId == null || rsId.isEmpty()) {
throw new AceException("RS must have non-null, non-empty identifier");
}
// Prevent adding an rs that has an identifier that is equal to an
// existing audience
try {
this.selectOSCOREGroupManagers.setString(1, rsId);
ResultSet result = this.selectOSCOREGroupManagers.executeQuery();
this.selectOSCOREGroupManagers.clearParameters();
if (result.next()) {
result.close();
throw new AceException(
"RsId equal to existing audience id: " + rsId);
}
result.close();
for (String aud : auds) {
this.insertOSCOREGroupManager.setString(1, rsId);
this.insertOSCOREGroupManager.setString(2, aud);
this.insertOSCOREGroupManager.execute();
}
this.insertAudience.clearParameters();
//The RS always recognizes itself as a singleton audience
this.insertOSCOREGroupManager.setString(1, rsId);
this.insertOSCOREGroupManager.setString(2, rsId);
this.insertOSCOREGroupManager.execute();
this.insertOSCOREGroupManager.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized void deleteRS(String rsId) throws AceException {
if (rsId == null) {
throw new AceException("deleteRS() requires non-null rsId");
}
try {
this.deleteRS.setString(1, rsId);
this.deleteRS.execute();
this.deleteRS.clearParameters();
this.deleteProfiles.setString(1, rsId);
this.deleteProfiles.execute();
this.deleteProfiles.clearParameters();
this.deleteScopes.setString(1, rsId);
this.deleteScopes.execute();
this.deleteScopes.clearParameters();
this.deleteAudiences.setString(1, rsId);
this.deleteAudiences.execute();
this.deleteAudiences.clearParameters();
this.deleteOSCOREGroupManagers.setString(1, rsId);
this.deleteOSCOREGroupManagers.execute();
this.deleteOSCOREGroupManagers.clearParameters();
this.deleteKeyTypes.setString(1, rsId);
this.deleteKeyTypes.execute();
this.deleteKeyTypes.clearParameters();
this.deleteTokenTypes.setString(1, rsId);
this.deleteTokenTypes.execute();
this.deleteTokenTypes.clearParameters();
this.deleteCose.setString(1, rsId);
this.deleteCose.execute();
this.deleteCose.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized void addClient(String clientId, Set<String> profiles,
String defaultScope, String defaultAud, Set<String> keyTypes,
OneKey sharedKey, OneKey publicKey)
throws AceException {
if (clientId == null || clientId.isEmpty()) {
throw new AceException(
"Client must have non-null, non-empty identifier");
}
if (profiles == null || profiles.isEmpty()) {
throw new AceException("Client must support at least one profile");
}
if (keyTypes.isEmpty()) {
throw new AceException(
"Client must support at least one PoP key type");
}
if (sharedKey == null && publicKey == null) {
throw new AceException("Cannot register a client without a key");
}
try {
this.insertClient.setString(1, clientId);
this.insertClient.setString(2, defaultAud);
this.insertClient.setString(3, defaultScope);
if (sharedKey != null) {
this.insertClient.setBytes(4, sharedKey.EncodeToBytes());
} else {
this.insertClient.setBytes(4, null);
}
if (publicKey != null) {
this.insertClient.setBytes(5, publicKey.EncodeToBytes());
} else {
this.insertClient.setBytes(5, null);
}
this.insertClient.execute();
this.insertClient.clearParameters();
for (String profile : profiles) {
this.insertProfile.setString(1, clientId);
this.insertProfile.setString(2, profile);
this.insertProfile.execute();
}
this.insertProfile.clearParameters();
for (String keyType : keyTypes) {
this.insertKeyType.setString(1, clientId);
this.insertKeyType.setString(2, keyType);
this.insertKeyType.execute();
}
this.insertKeyType.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized void deleteClient(String clientId) throws AceException {
if (clientId == null) {
throw new AceException(
"deleteClient() requires non-null clientId");
}
try {
this.deleteClient.setString(1, clientId);
this.deleteClient.execute();
this.deleteClient.clearParameters();
this.deleteProfiles.setString(1, clientId);
this.deleteProfiles.execute();
this.deleteProfiles.clearParameters();
this.deleteKeyTypes.setString(1, clientId);
this.deleteKeyTypes.execute();
this.deleteKeyTypes.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized void addToken(String cti,
Map<Short, CBORObject> claims) throws AceException {
if (cti == null || cti.isEmpty()) {
throw new AceException(
"addToken() requires non-null, non-empty cti");
}
if (claims == null || claims.isEmpty()) {
throw new AceException(
"addToken() requires at least one claim");
}
try {
for (Map.Entry<Short, CBORObject> claim : claims.entrySet()) {
this.insertClaim.setString(1, cti);
this.insertClaim.setShort(2, claim.getKey());
this.insertClaim.setBytes(3, claim.getValue().EncodeToBytes());
this.insertClaim.execute();
}
this.insertClaim.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized void deleteToken(String cti) throws AceException {
if (cti == null) {
throw new AceException("deleteToken() requires non-null cti");
}
try {
this.logInvalidToken.setString(1, cti);
this.logInvalidToken.execute();
this.logInvalidToken.clearParameters();
this.deleteClaims.setString(1, cti);
this.deleteClaims.execute();
this.deleteClaims.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized void purgeExpiredTokens(long now) throws AceException {
try {
ResultSet result = this.selectExpirationTime.executeQuery();
while (result.next()) {
byte[] rawTime = result.getBytes(DBConnector.claimValueColumn);
CBORObject cborTime = CBORObject.DecodeFromBytes(rawTime);
long time = cborTime.AsNumber().ToInt64Checked();
if (now > time) {
deleteToken(result.getString(DBConnector.ctiColumn));
}
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized Map<Short, CBORObject> getClaims(String cti)
throws AceException {
if (cti == null) {
throw new AceException("getClaims() requires non-null cti");
}
Map<Short, CBORObject> claims = new HashMap<>();
try {
this.selectClaims.setString(1, cti);
ResultSet result = this.selectClaims.executeQuery();
this.selectClaims.clearParameters();
while (result.next()) {
Short claimName
= result.getShort(DBConnector.claimNameColumn);
CBORObject cbor = CBORObject.DecodeFromBytes(
result.getBytes(DBConnector.claimValueColumn));
claims.put(claimName, cbor);
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return claims;
}
@Override
public synchronized Long getCtiCounter() throws AceException {
Long l = -1L;
try {
ResultSet result = this.selectCtiCtr.executeQuery();
if (result.next()) {
l = result.getLong(DBConnector.ctiCounterColumn);
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return l;
}
@Override
public synchronized void saveCtiCounter(Long cti) throws AceException {
try {
this.updateCtiCtr.setLong(1, cti);
this.updateCtiCtr.execute();
this.updateCtiCtr.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized int getExiSequenceNumber(String rsId) throws AceException {
int sn = -1;
try {
this.selectExiSn.setString(1, rsId);
ResultSet result = this.selectExiSn.executeQuery();
if (result.next()) {
sn = result.getInt(DBConnector.exiSeqNumColumn);
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return sn;
}
@Override
public synchronized void saveExiSequenceNumber(int sn, String rsId) throws AceException {
try {
this.updateExiSn.setInt(1, sn);
this.updateExiSn.setString(2, rsId);
this.updateExiSn.execute();
this.updateExiSn.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
/**
* Creates the user that manages this database.
*
* @param dbAdapter an adapter instance for the specific DB type being used.
* @param adminUser the database admin user name
* @param adminPwd the database admin password
*
* @throws AceException
*/
public synchronized static void createUser(SQLDBAdapter dbAdapter, String adminUser, String adminPwd) throws AceException {
dbAdapter.createUser(adminUser, adminPwd);
}
@Override
public synchronized void addCti2Client(String cti, String clientId)
throws AceException {
if (cti == null || clientId == null) {
throw new AceException(
"addCti2Client() requires non-null parameters");
}
try {
this.insertCti2Client.setString(1, cti);
this.insertCti2Client.setString(2, clientId);
this.insertCti2Client.execute();
this.insertCti2Client.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public synchronized Set<String> getClients() throws AceException {
Set<String> clients = new HashSet<>();
try {
ResultSet result = this.selectAllClients.executeQuery();
while (result.next()) {
clients.add(result.getString(DBConnector.clientIdColumn));
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return clients;
}
@Override
public synchronized String getClient4Cti(String cti) throws AceException {
if (cti == null) {
throw new AceException("getClient4Cti() requires non-null cti");
}
try {
this.selectClientByCti.setString(1, cti);
ResultSet result = this.selectClientByCti.executeQuery();
this.selectClientByCti.clearParameters();
if (result.next()) {
String clientId = result.getString(DBConnector.clientIdColumn);
result.close();
return clientId;
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return null;
}
@Override
public synchronized Set<String> getCtis4Client(String clientId)
throws AceException {
if (clientId == null) {
throw new AceException(
"getCtis4Client() requires non-null clientId");
}
Set<String> ctis = new HashSet<>();
try {
this.selectCtisByClient.setString(1, clientId);
ResultSet result = this.selectCtisByClient.executeQuery();
this.selectCtisByClient.clearParameters();
while (result.next()) {
ctis.add(result.getString(DBConnector.ctiColumn));
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return ctis;
}
@Override
public String getCti4Grant(String code) throws AceException {
if (code == null) {
throw new AceException(
"getCti4Grant() requires non-null code");
}
String cti = null;
try {
this.selectCtisByGrant.setString(1, code);
ResultSet result = this.selectCtisByGrant.executeQuery();
this.selectCtisByGrant.clearParameters();
while (result.next()) {
cti = (result.getString(DBConnector.ctiColumn));
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return cti;
}
@Override
public void addGrant(String code, String cti, Map<Short, CBORObject> claims,
Map<Short, CBORObject> rsInfo) throws AceException {
if (code == null) {
throw new AceException(
"getaddGrant() requires non-null code");
}
if (cti == null) {
throw new AceException(
"getaddGrant() requires non-null cti");
}
if (claims == null || claims.isEmpty()) {
throw new AceException(
"getaddGrant() requires non-null and non-empty"
+ " claims");
}
if (rsInfo == null || rsInfo.isEmpty()) {
throw new AceException(
"getaddGrant() requires non-null and non-empty"
+ " rsInfo");
}
addToken(cti, claims);
try {
this.insertGrant2Cti.setString(1, code);
this.insertGrant2Cti.setString(2, cti);
this.insertGrant2Cti.setBoolean(3, true);
this.insertGrant2Cti.execute();
this.insertGrant2Cti.clearParameters();
} catch (SQLException e) {
deleteToken(cti);
throw new AceException(e.getMessage());
}
try {
this.insertGrant2RsInfo.setString(1, code);
for (Map.Entry<Short, CBORObject> rsEntry : rsInfo.entrySet()) {
this.insertGrant2RsInfo.setShort(2, rsEntry.getKey());
this.insertGrant2RsInfo.setBytes(3, rsEntry.getValue().EncodeToBytes());
this.insertGrant2RsInfo.execute();
}
this.insertGrant2RsInfo.clearParameters();
} catch (SQLException e) {
deleteToken(cti);
try {
this.deleteGrant2Cti.setString(1, code);
this.deleteGrant2Cti.execute();
this.deleteGrant2Cti.clearParameters();
} catch (SQLException e2) {
throw new AceException("Error while tyring to roll-back an "
+ "addGrant(): " + e2.getMessage());
}
throw new AceException(e.getMessage());
}
}
@Override
public void useGrant(String code) throws AceException {
if (code == null) {
throw new AceException(
"useGrant() requires non-null code");
}
try {
this.updateGrant.setString(1, code);
this.updateGrant.execute();
this.updateGrant.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
@Override
public Map<Short, CBORObject> getRsInfo(String code) throws AceException {
if (code == null) {
throw new AceException(
"getRsInfo() requires non-null code");
}
Map<Short, CBORObject> rsInfo = new HashMap<>();
try {
this.selectRsInfoByGrant.setString(1, code);
ResultSet result = this.selectRsInfoByGrant.executeQuery();
this.selectRsInfoByGrant.clearParameters();
while (result.next()) {
Short claimName
= result.getShort(DBConnector.claimNameColumn);
CBORObject cbor = CBORObject.DecodeFromBytes(
result.getBytes(DBConnector.claimValueColumn));
rsInfo.put(claimName, cbor);
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return rsInfo;
}
@Override
public boolean isGrantValid(String code) throws AceException {
if (code == null) {
throw new AceException(
"getRsInfo() requires non-null code");
}
boolean valid = false;
try {
this.selectGrantValid.setString(1, code);
ResultSet result = this.selectGrantValid.executeQuery();
this.selectGrantValid.clearParameters();
if (result.next()) {
valid = result.getBoolean(DBConnector.grantValidColumn);
} else {
valid = false;
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return valid;
}
/**
* Extensibility method to allow other modules to prepare statements.
*
* @param statement the statement string
*
* @return the prepared statement
* @throws AceException
*
*/
public PreparedStatement prepareStatement(String statement) throws AceException {
PreparedStatement stmt = null;
try {
stmt = this.conn.prepareStatement(
this.adapter.updateEngineSpecificSQL(statement));
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return stmt;
}
/**
* Get the SQL database adapter. This method is for external modules
* that need access to the database.
*
* @return the SQL database adapter
*/
public SQLDBAdapter getAdapter() {
return this.adapter;
}
}