GroupOSCOREJoinPDP.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.oscore.as;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.upokecenter.cbor.CBORObject;
import com.upokecenter.cbor.CBORType;
import se.sics.ace.AceException;
import se.sics.ace.as.DBConnector;
import se.sics.ace.as.PDP;
import se.sics.ace.Constants;
import se.sics.ace.Util;
import se.sics.ace.examples.SQLConnector;
/**
* A PDP implementation supporting scopes as Text Strings or Byte Strings. A byte string scope is used to join OSCORE groups.
*
* This PDP backs up it's ACL's in the database.
*
* NOTE: This PDP needs a SQL connector it won't work with other DBConnectors.
*
* @author Marco Tiloca
*
*/
public class GroupOSCOREJoinPDP implements PDP, AutoCloseable {
private SQLConnector db = null;
/**
* The name of the Token access control table
*/
public static String tokenTable = "PdpToken";
/**
* The name of the Introspect access control table
*/
public static String introspectTable = "PdpIntrospect";
/**
* The name of the ACL table
*/
public static String accessTable = "PdpAccess";
/**
* The name of the OSCORE Group Managers table
*/
public static String oscoreGroupManagersTable = "OSCOREGroupManagersTable";
/**
* The name of the column that indicates if this device has access to all detailed claims when introspecting.
*/
public static String introspectClaimsColumn = "claimsAccess";
private PreparedStatement canToken;
private PreparedStatement canIntrospect;
private PreparedStatement canAccess;
private PreparedStatement addTokenAccess;
private PreparedStatement addIntrospectAccess;
private PreparedStatement addAccess;
private PreparedStatement deleteTokenAccess;
private PreparedStatement deleteIntrospectAccess;
private PreparedStatement deleteAccess;
private PreparedStatement deleteAllAccess;
private PreparedStatement deleteAllRsAccess;
private PreparedStatement getAllAccess;
private PreparedStatement addOSCOREGroupManager;
private PreparedStatement deleteOSCOREGroupManagers;
private PreparedStatement selectOSCOREGroupManagers;
private Map<String, Short> rolesToInt = new HashMap<>();
/**
* Constructor, can supply an initial configuration.
* All configuration parameters that are null are expected
* to already be in the database.
*
* @param connection the database connector
* @throws AceException
*/
public GroupOSCOREJoinPDP(SQLConnector connection) throws AceException {
this.db = connection;
String createToken = this.db.getAdapter().updateEngineSpecificSQL(
"CREATE TABLE IF NOT EXISTS "
+ tokenTable + "("
+ DBConnector.idColumn + " varchar(255) NOT NULL);");
String createIntrospect = this.db.getAdapter().updateEngineSpecificSQL(
"CREATE TABLE IF NOT EXISTS "
+ introspectTable + "("
+ DBConnector.idColumn + " varchar(255) NOT NULL,"
+ introspectClaimsColumn + " boolean NOT NULL);");
String createAccess = this.db.getAdapter().updateEngineSpecificSQL(
"CREATE TABLE IF NOT EXISTS "
+ accessTable + "("
+ DBConnector.idColumn + " varchar(255) NOT NULL,"
+ DBConnector.rsIdColumn + " varchar(255) NOT NULL,"
+ DBConnector.scopeColumn + " varchar(255) NOT NULL);");
String createOSCOREGroupManagers = this.db.getAdapter().updateEngineSpecificSQL(
"CREATE TABLE IF NOT EXISTS "
+ oscoreGroupManagersTable + "("
+ DBConnector.rsIdColumn + " varchar(255) NOT NULL,"
+ DBConnector.audColumn + " varchar(255) NOT NULL);");
try (Connection conn = this.db.getAdapter().getDBConnection();
Statement stmt = conn.createStatement()) {
stmt.execute(createToken);
stmt.execute(createIntrospect);
stmt.execute(createAccess);
stmt.execute(createOSCOREGroupManagers);
} catch (SQLException e) {
e.printStackTrace();
throw new AceException(e.getMessage());
}
this.canToken = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("SELECT * FROM "
+ tokenTable
+ " WHERE " + DBConnector.idColumn + "=?;"));
this.canIntrospect = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("SELECT * FROM "
+ introspectTable
+ " WHERE " + DBConnector.idColumn + "=?;"));
//Gets only the access of the client, the PDP sorts out the audiences and scopes
this.canAccess = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("SELECT * FROM "
+ accessTable
+ " WHERE " + DBConnector.idColumn + "=?"
+ " AND " + DBConnector.rsIdColumn + "=?;"));
this.addTokenAccess = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("INSERT INTO "
+ tokenTable + " VALUES (?);"));
this.addIntrospectAccess = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("INSERT INTO "
+ introspectTable + " VALUES (?,?);"));
this.addAccess = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("INSERT INTO "
+ accessTable + " VALUES (?,?,?);"));
this.addOSCOREGroupManager = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("INSERT INTO "
+ oscoreGroupManagersTable + " VALUES (?,?);"));
this.deleteTokenAccess = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("DELETE FROM "
+ tokenTable + " WHERE "
+ DBConnector.idColumn + "=?;"));
this.deleteIntrospectAccess = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("DELETE FROM "
+ introspectTable + " WHERE "
+ DBConnector.idColumn + "=?;"));
this.deleteAccess = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("DELETE FROM "
+ accessTable + " WHERE "
+ DBConnector.idColumn + "=?"
+ " AND " + DBConnector.rsIdColumn + "=?"
+ " AND " + DBConnector.scopeColumn + "=?;"));
this.deleteAllAccess = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("DELETE FROM "
+ accessTable + " WHERE "
+ DBConnector.idColumn + "=?;"));
this.deleteAllRsAccess = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("DELETE FROM "
+ accessTable + " WHERE "
+ DBConnector.idColumn + "=?"
+ " AND " + DBConnector.rsIdColumn + "=?;"));
this.deleteOSCOREGroupManagers = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("DELETE FROM "
+ oscoreGroupManagersTable + " WHERE "
+ DBConnector.rsIdColumn + "=?;"));
this.getAllAccess = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("SELECT * FROM "
+ accessTable + " WHERE "
+ DBConnector.idColumn + "=?;"));
this.selectOSCOREGroupManagers = this.db.prepareStatement(
this.db.getAdapter().updateEngineSpecificSQL("SELECT "
+ DBConnector.audColumn + " FROM "
+ oscoreGroupManagersTable + " WHERE "
+ DBConnector.rsIdColumn + "=? ORDER BY "
+ DBConnector.audColumn +";"));
rolesToInt.put("requester", Constants.GROUP_OSCORE_REQUESTER);
rolesToInt.put("responder", Constants.GROUP_OSCORE_RESPONDER);
rolesToInt.put("monitor", Constants.GROUP_OSCORE_MONITOR);
rolesToInt.put("verifier", Constants.GROUP_OSCORE_VERIFIER);
}
@Override
public boolean canAccessToken(String clientId) throws AceException {
if (clientId == null) {
throw new AceException(
"canAccessToken() requires non-null clientId");
}
try {
this.canToken.setString(1, clientId);
ResultSet result = this.canToken.executeQuery();
this.canToken.clearParameters();
if (result.next()) {
result.close();
return true;
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return false;
}
/**
* FIXME: Add description of method, parameters and return value.
*
* @param rsId
* @param aud
* @return FIXME
* @throws AceException
*/
public boolean isOSCOREGroupManager(String rsId, Set<String> aud) throws AceException {
if (rsId == null || rsId.isEmpty()) {
throw new AceException("RS must have non-null, non-empty identifier");
}
if (aud == null) {
throw new AceException("Audience must be non-null");
}
for (String audE : aud) {
try {
this.selectOSCOREGroupManagers.setString(1, rsId);
ResultSet result = this.selectOSCOREGroupManagers.executeQuery();
this.selectOSCOREGroupManagers.clearParameters();
while (result.next()) {
if (result.getString(DBConnector.audColumn).equals(audE)) {
result.close();
return true;
}
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
return false;
}
@Override
public IntrospectAccessLevel getIntrospectAccessLevel(String rsId) throws AceException {
if (rsId == null) {
throw new AceException(
"getIntrospectAccessLevel() requires non-null rsId");
}
try {
this.canIntrospect.setString(1, rsId);
ResultSet result = this.canIntrospect.executeQuery();
this.canIntrospect.clearParameters();
if (result.next()) {
boolean canAccessClaims = result.getBoolean(introspectClaimsColumn);
result.close();
if (canAccessClaims)
{
return IntrospectAccessLevel.ACTIVE_AND_CLAIMS;
}
return IntrospectAccessLevel.ACTIVE_ONLY;
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
return IntrospectAccessLevel.NONE;
}
@Override
public Object canAccess(String clientId, Set<String> aud, Object scope)
throws AceException {
if (clientId == null) {
throw new AceException(
"canAccess() requires non-null clientId");
}
if (aud == null) {
throw new AceException(
"canAccess() requires non-null audience");
}
if (scope == null) {
throw new AceException(
"canAccess() requires non-null scope");
}
Set<String> rss = new HashSet<>();
for (String audE : aud) {
rss.addAll(this.db.getRSS(audE));
}
if (rss.isEmpty()) {
return null;
}
Set<Set<String>> clientACL = new HashSet<>();
for (String rs : rss) {
Set<String> scopes = new HashSet<>();
try {
this.canAccess.setString(1, clientId);
this.canAccess.setString(2, rs);
ResultSet result = this.canAccess.executeQuery();
this.canAccess.clearParameters();
while (result.next()) {
scopes.add(result.getString(DBConnector.scopeColumn));
}
result.close();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
if (scopes.isEmpty()) {
//The client can access nothing on this RS
return null;
}
clientACL.add(scopes);
}
Set<String> scopes = null;
for (Set<String> rs : clientACL) {
if (scopes == null) {
scopes = new HashSet<>();
if (rs != null) {
scopes.addAll(rs);
}
} else {
Set<String> remains = new HashSet<>(scopes);
for (String foo : scopes) {
if (rs == null ) {
//The client can access nothing on this RS
return null;
}
if (!rs.contains(foo)) {
remains.remove(foo);
}
}
scopes = remains;
}
}
if (scopes == null || scopes.isEmpty()) {
return null;
}
String scopeStr;
String grantedScopesString = "";
Object grantedScopes = null;
// If the RS and the audience point at an OSCORE Group Manager,
// the scope must be encoded as a CBOR Byte String
boolean scopeMustBeBinary = false;
boolean rsOSCOREGroupManager = false;
for (String rs : rss) {
rsOSCOREGroupManager = isOSCOREGroupManager(rs, aud);
scopeMustBeBinary = rsOSCOREGroupManager;
if (scopeMustBeBinary) break;
}
// Handling of a Text String scope, just as in KissPDP
if (scope instanceof String) {
if (scopeMustBeBinary)
throw new AceException("Scope for this audience must be a byte string");
scopeStr = (String)scope;
String[] requestedScopes = scopeStr.split(" ");
for (int i=0; i<requestedScopes.length; i++) {
if (scopes.contains(requestedScopes[i])) {
if (!(grantedScopesString).isEmpty()) {
grantedScopesString += " ";
}
grantedScopesString += requestedScopes[i];
}
}
if (!grantedScopesString.isEmpty())
grantedScopes = grantedScopesString;
}
// Handling of a Byte String scope, formatted as per draft-ietf-ace-key-groupcomm , Section 3.1
// This type of scope is expected to have this structure for each RS acting as OSCORE Group Manager
else if (scope instanceof byte[] && rsOSCOREGroupManager) {
// Allowed scope to be returned to the /token endpoint
CBORObject cborArrayScope = CBORObject.NewArray();
// Retrieve the scope as CBOR Array
CBORObject scopeCBOR = CBORObject.DecodeFromBytes((byte[])scope);
if (scopeCBOR.getType().equals(CBORType.Array)) {
// Inspect each scope entry, i.e. group name followed by role(s)
for (int entryIndex = 0; entryIndex < scopeCBOR.size(); entryIndex++) {
CBORObject scopeEntry = scopeCBOR.get(entryIndex);
if (scopeEntry.getType().equals(CBORType.Array)) {
if (scopeEntry.size() != 2)
throw new AceException("Scope must have two elements, i.e. Group ID and list of roles");
String groupName = "";
Set<String> roles = new HashSet<>();
// Retrieve the group name of the OSCORE group
CBORObject scopeElement = scopeEntry.get(0);
if (scopeElement.getType().equals(CBORType.TextString)) {
groupName = scopeElement.AsString();
}
else {throw new AceException("The group name must be a CBOR Text String");}
// Retrieve the role or list of roles
scopeElement = scopeEntry.get(1);
// NEW VERSION USING the AIF-BASED ENCODING AS SINGLE INTEGER
if (scopeElement.getType().equals(CBORType.Integer)) {
int roleSet = scopeElement.AsInt32();
if (roleSet <= 0)
throw new AceException("The roles must be encoded as a CBOR Unsigned Integer greater than 0");
Set<Integer> roleIdSet = Util.getGroupOSCORERoles(roleSet);
short[] roleIdArray = new short[roleIdSet.size()];
int index = 0;
for (Integer elem : roleIdSet)
roleIdArray[index++] = elem.shortValue();
for (int i=0; i<roleIdArray.length; i++) {
short roleIdentifier = roleIdArray[i];
// Silently ignore unrecognized roles
if (roleIdentifier < Constants.GROUP_OSCORE_ROLES.length)
roles.add(Constants.GROUP_OSCORE_ROLES[roleIdentifier]);
}
}
else {throw new AceException("Invalid format of roles");}
// Check if the client can access the specified group on the RS with the specified roles
// Note: this assumes that there is only one RS acting as Group Manager specified as audience
// Then, each element of 'scopes' refers to one OSCORE group under that Group Manager
boolean canJoin = false;
Set<String> allowedRoles = new HashSet<>();
for (String foo : scopes) {
String[] scopeParts = foo.split("_");
if(groupName.equals(scopeParts[0])) {
canJoin = true;
for (int i=1; i<scopeParts.length; i++) {
if (roles.contains(scopeParts[i]))
allowedRoles.add(scopeParts[i]);
}
}
}
if (canJoin == true && !allowedRoles.isEmpty()) {
CBORObject cborArrayScopeEntry = CBORObject.NewArray();
cborArrayScopeEntry.Add(groupName);
int grantedRoles = 0;
for (String foo : allowedRoles)
grantedRoles = Util.addGroupOSCORERole(grantedRoles, rolesToInt.get(foo));
cborArrayScopeEntry.Add(grantedRoles);
cborArrayScope.Add(cborArrayScopeEntry);
}
} else {
throw new AceException(
"Invalid scope entry format for joining OSCORE groups");
}
} // End of scope entries inspection
if (cborArrayScope.size() != 0)
grantedScopes = cborArrayScope.EncodeToBytes();
} else {
throw new AceException(
"Invalid scope format for joining OSCORE groups");
}
}
// This includes the case where the scope is encoded as a CBOR Byte String,
// but the audience is not registered as related to an OSCORE Group Manager.
// In fact, no processing for byte string scopes are defined, other than
// the one implemented above according to draft-ietf-ace-key-groupcomm-oscore
else if (scope instanceof byte[]) {
throw new AceException(
"Unknown processing for this byte string scope");
}
else {
throw new AceException(
"Scopes must be Text Strings or Byte Strings");
}
return grantedScopes;
}
@Override
public void close() throws Exception {
this.db.close();
}
/**
* Add access permission for the token endpoint
*
* @param id the identifier of the entity to be allowed access
*
* @throws AceException
*/
public void addTokenAccess(String id) throws AceException {
if (id == null) {
throw new AceException(
"addTokenAccess() requires non-null id");
}
try {
this.addTokenAccess.setString(1, id);
this.addTokenAccess.execute();
this.addTokenAccess.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
/**
* Add access permission for the introspect endpoint, defaulting to access to activeness and claims.
*
* @param id the identifier of the entity to be allowed access
*
* @throws AceException
*/
public void addIntrospectAccess(String id) throws AceException {
addIntrospectAccess(id, IntrospectAccessLevel.ACTIVE_AND_CLAIMS);
}
/**
* Add access permission for the introspect endpoint
*
* @param id the identifier of the entity to be allowed access
* @param accessLevel the level of access to give when introspecting
*
* @throws AceException
*/
public void addIntrospectAccess(String id, IntrospectAccessLevel accessLevel) throws AceException {
if (id == null) {
throw new AceException(
"addIntrospectAccess() requires non-null id");
}
if (accessLevel.equals(IntrospectAccessLevel.NONE)) {
throw new AceException(
"addIntrospectAccess() requires non-NONE access level");
}
try {
boolean hasClaimsAccess = accessLevel.equals(IntrospectAccessLevel.ACTIVE_AND_CLAIMS);
this.addIntrospectAccess.setString(1, id);
this.addIntrospectAccess.setBoolean(2, hasClaimsAccess);
this.addIntrospectAccess.execute();
this.addIntrospectAccess.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
/**
* Add access permission for a client
*
* @param cid the identifier of the client to be allowed access
* @param rid the identifier of the RS to which access is allowed
* @param scope the identifier of the scope for which access is allowed
*
* @throws AceException
*/
public void addAccess(String cid, String rid, String scope)
throws AceException {
if (cid == null) {
throw new AceException(
"addAccess() requires non-null cid");
}
if (rid == null) {
throw new AceException(
"addAccess() requires non-null rid");
}
if (scope == null) {
throw new AceException(
"addAccess() requires non-null scope");
}
try {
this.addAccess.setString(1, cid);
this.addAccess.setString(2, rid);
this.addAccess.setString(3, scope);
this.addAccess.execute();
this.addAccess.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
/**
* Revoke an access right to the Token endpoint
*
* @param id the identifier if the entity for which access is revoked
*
* @throws AceException
*/
public void revokeTokenAccess(String id) throws AceException {
if (id == null) {
throw new AceException(
"revokeTokenAccess() requires non-null id");
}
try {
this.deleteTokenAccess.setString(1, id);
this.deleteTokenAccess.execute();
this.deleteTokenAccess.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
/**
* Revoke an access right to the Introspect endpoint.
*
* @param id the identifier of the entity for which access is revoked
*
* @throws AceException
*/
public void revokeIntrospectAccess(String id) throws AceException {
if (id == null) {
throw new AceException(
"revokeIntrospectAccess() requires non-null id");
}
try {
this.deleteIntrospectAccess.setString(1, id);
this.deleteIntrospectAccess.execute();
this.deleteIntrospectAccess.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
/**
* Revoke a specific access right from a client.
*
* @param cid the client's identifier
* @param rid the RS's identifier
* @param scope the scope to be revoked
*
* @throws AceException
*/
public void revokeAccess(String cid, String rid, String scope)
throws AceException {
if (cid == null) {
throw new AceException(
"revokeAccess() requires non-null cid");
}
if (rid == null) {
throw new AceException(
"revokeAccess() requires non-null rid");
}
if (scope == null) {
throw new AceException(
"revokeAccess() requires non-null scope");
}
try {
this.deleteAccess.setString(1, cid);
this.deleteAccess.setString(2, rid);
this.deleteAccess.setString(3, scope);
this.deleteAccess.execute();
this.deleteAccess.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
/**
* Revoke all access for a given client.
*
* @param id the client's identifier
*
* @throws AceException
*/
public void revokeAllAccess(String id) throws AceException {
if (id == null) {
throw new AceException(
"revokeAllAccess() requires non-null id");
}
try {
this.deleteAllAccess.setString(1, id);
this.deleteAllAccess.execute();
this.deleteAllAccess.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
/**
* Revoke all access to a specific RS for a given client.
*
* @param cid the client's identifier
* @param rid the RS's identifier
*
* @throws AceException
*/
public void revokeAllRsAccess(String cid, String rid)
throws AceException {
if (cid == null) {
throw new AceException(
"revokeAllRsAccess() requires non-null cid");
}
if (rid == null) {
throw new AceException(
"revokeAllRsAccess() requires non-null rid");
}
try {
this.deleteAllRsAccess.setString(1, cid);
this.deleteAllRsAccess.setString(2, rid);
this.deleteAllRsAccess.execute();
this.deleteAllRsAccess.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
/**
* Gets a map of all access configured for a given client.
*
* @param id the client's identifier
*
* @return A map of RS ids associated to sets of scopes, configured for the given client id.
* @throws AceException
*/
public Map<String, Set<String>> getAllAccess(String id) throws AceException {
if (id == null) {
throw new AceException(
"getAllAccess() requires non-null id");
}
try {
this.getAllAccess.setString(1, id);
ResultSet result = this.getAllAccess.executeQuery();
this.getAllAccess.clearParameters();
Map<String, Set<String>> accessMap = new HashMap<>();
while(result.next()) {
String rsId = result.getString(DBConnector.rsIdColumn);
String scope = result.getString(DBConnector.scopeColumn);
if(!accessMap.containsKey(rsId)) {
accessMap.put(rsId, new HashSet<>());
}
accessMap.get(rsId).add(scope);
}
result.close();
return accessMap;
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
/**
* Add a pre-registered audience as an OSCORE Group Manager
*
* @param rsId the identifier of the RS whose audiences are registered as OSCORE Group Manager
* @param auds the identifiers of the audiences the RS acting as OSCORE Group Manager identifies with
*
* @throws AceException
*/
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.addOSCOREGroupManager.setString(1, rsId);
this.addOSCOREGroupManager.setString(2, aud);
this.addOSCOREGroupManager.execute();
}
this.addOSCOREGroupManager.clearParameters();
//The RS always recognizes itself as a singleton audience
this.addOSCOREGroupManager.setString(1, rsId);
this.addOSCOREGroupManager.setString(2, rsId);
this.addOSCOREGroupManager.execute();
this.addOSCOREGroupManager.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
/**
* Remove all audiences an RS identifies with as an OSCORE Group Manager
*
* @param rsId the identifier of the RS whose audiences are registered as OSCORE Group Manager
*
* @throws AceException
*/
public void deleteOSCOREGroupManagers(String rsId) throws AceException {
if (rsId == null || rsId.isEmpty()) {
throw new AceException("RS must have non-null, non-empty identifier");
}
try {
this.deleteOSCOREGroupManagers.setString(1, rsId);
this.deleteOSCOREGroupManagers.execute();
this.deleteOSCOREGroupManagers.clearParameters();
} catch (SQLException e) {
throw new AceException(e.getMessage());
}
}
}