Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.att.research.xacml.std.pip.engines.jdbc.ConfigurableJDBCResolver Maven / Gradle / Ivy
/*
*
* Copyright (c) 2013,2019 AT&T Knowledge Ventures
* SPDX-License-Identifier: MIT
*/
package com.att.research.xacml.std.pip.engines.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.att.research.xacml.api.Attribute;
import com.att.research.xacml.api.AttributeValue;
import com.att.research.xacml.api.DataType;
import com.att.research.xacml.api.DataTypeFactory;
import com.att.research.xacml.api.Identifier;
import com.att.research.xacml.api.XACML3;
import com.att.research.xacml.api.pip.PIPEngine;
import com.att.research.xacml.api.pip.PIPException;
import com.att.research.xacml.api.pip.PIPFinder;
import com.att.research.xacml.api.pip.PIPRequest;
import com.att.research.xacml.api.pip.PIPResponse;
import com.att.research.xacml.std.StdAttribute;
import com.att.research.xacml.std.datatypes.DataTypes;
import com.att.research.xacml.std.datatypes.ISO8601Date;
import com.att.research.xacml.std.datatypes.ISO8601DateTime;
import com.att.research.xacml.std.pip.StdPIPRequest;
import com.att.research.xacml.std.pip.engines.Configurables;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
/**
* Implements the {@link com.att.research.xacml.std.pip.engines.jdbc.JDBCResolver} for SQL queries with parameters in
* their prepared statements specified as XACML Attribute values.
*
* @author Christopher A. Rath
* @version $Revision$
*/
public class ConfigurableJDBCResolver implements JDBCResolver {
public static final String PROP_SELECT = "select";
public static final String PROP_SELECT_FIELDS = "fields";
public static final String PROP_SELECT_FIELD = "field";
public static final String PROP_SELECT_PARAMETERS = "parameters";
public static final String PROP_SELECT_PARAMETER = "parameter";
private Logger logger = LoggerFactory.getLogger(this.getClass());
private String defaultIssuer;
private Set supportedRequests = new HashSet();
private Set supportedRequestsNoIssuer = new HashSet();
private Map mapFields = new HashMap();
private String sqlQuery;
private List parameters = new ArrayList();
private static DataTypeFactory dataTypeFactory = null;
static {
try {
dataTypeFactory = DataTypeFactory.newInstance();
} catch (Exception ex) {
LoggerFactory.getLogger(ConfigurableJDBCResolver.class).error("Exception geting DataTypeFactory: " + ex.toString(), ex);
}
}
/**
* Determines if the given {@link com.att.research.xacml.api.pip.PIPRequest} can be answered with this ConfigurableJDBCResolver
.
*
* @param pipRequest the PIPRequest
to check
* @return true if the given PIPRequest
is supported by this ConfigurableJDBCResolver
, else false
*/
protected boolean isSupported(PIPRequest pipRequest) {
if (pipRequest.getIssuer() == null) {
return this.supportedRequestsNoIssuer.contains(pipRequest);
} else {
return this.supportedRequests.contains(pipRequest);
}
}
/**
* Creates a new ConfigurableJDBCResolver
that can provide XACML Attributes for the given Collection
of
* {@link com.att.research.xacml.api.pip.PIPRequests}s. The mapping from database table field names to XACML Attributes is provided by the
* fieldsIn
Map
. The SQL query String
is provided by sqlQueryIn
. The query string may contain
* prepared statement parameter place-holders ('?')
. The XACML Attributes whose values are used for those place-holders are provided
* by the given parametersIn
List
.
*
* @param supportedRequestsIn the Collection
of PIPRequest
s that are supported by the new ConfiurableJDBCResolver
* @param fieldsIn the Map
from String
field names to PIPRequest
s in the database table
* @param sqlQueryIn the String
SQL query that retrieves records that satisfy PIPRequest
s
* @param parametersIn the List
of PIPRequest
s representing parameter values found in sqlQueryIn
.
*/
/*
public ConfigurableJDBCResolver(Collection supportedRequestsIn, Map fieldsIn, String sqlQueryIn, List parametersIn) {
this();
if (supportedRequestsIn != null) {
this.supportedRequests.addAll(supportedRequestsIn);
for (PIPRequest pipRequest : supportedRequestsIn) {
if (pipRequest.getIssuer() != null) {
this.supportedRequestsNoIssuer.add(new StdPIPRequest(pipRequest.getCategory(), pipRequest.getAttributeId(), pipRequest.getDataTypeId()));
} else {
this.supportedRequestsNoIssuer.add(pipRequest);
}
}
}
if (fieldsIn != null) {
for (String field : fieldsIn.keySet()) {
this.mapFields.put(field, fieldsIn.get(field));
}
}
this.sqlQuery = sqlQueryIn;
if (parametersIn != null) {
this.parameters.addAll(parametersIn);
}
}
*/
public ConfigurableJDBCResolver() {
if (dataTypeFactory == null) {
throw new IllegalStateException("No DataTypeFactory instance created");
}
}
public Map getMapFields() {
return mapFields;
}
public List getParameters() {
return parameters;
}
public Properties generateProperties(String id, String select) {
return generateProperties(id, select, this.mapFields, this.parameters);
}
public static Properties generateProperties(String id, String select, Map mapFields, List parameters) {
Properties properties = new Properties();
//
// Set the select statement
//
properties.setProperty(Joiner.on('.').join(id, PROP_SELECT), select);
//
// Set the fields
//
if (mapFields.size() > 0) {
properties.setProperty(Joiner.on('.').join(id, PROP_SELECT_FIELDS), Joiner.on(',').join(mapFields.keySet()));
for (String field : mapFields.keySet()) {
PIPRequest request = mapFields.get(field);
String fieldPrefix = Joiner.on('.').join(id, PROP_SELECT_FIELD);
properties.setProperty(fieldPrefix + ".id", request.getAttributeId().stringValue());
properties.setProperty(fieldPrefix + ".datatype", request.getDataTypeId().stringValue());
properties.setProperty(fieldPrefix + ".category", request.getCategory().stringValue());
if (request.getIssuer() != null) {
properties.setProperty(fieldPrefix + ".issuer", request.getIssuer());
}
}
}
//
// Set the parameters
//
if (parameters.size() > 0) {
String params = "1";
for (int i = 2; i <= parameters.size(); i++) {
params = params + "," + i;
}
properties.setProperty(Joiner.on('.').join(id, PROP_SELECT_PARAMETERS), params);
int position = 1;
for (PIPRequest request : parameters) {
String fieldPrefix = Joiner.on('.').join(id, PROP_SELECT_PARAMETER, position++);
properties.setProperty(fieldPrefix + ".id", request.getAttributeId().stringValue());
properties.setProperty(fieldPrefix + ".datatype", request.getDataTypeId().stringValue());
properties.setProperty(fieldPrefix + ".category", request.getCategory().stringValue());
if (request.getIssuer() != null) {
properties.setProperty(fieldPrefix + ".issuer", request.getIssuer());
}
}
}
return properties;
}
/*
protected PIPRequest getPIPRequest(String idPrefix, Properties properties) throws PIPException {
String stringProp = idPrefix + PROP_ID;
String attributeId = properties.getProperty(stringProp);
if (attributeId == null || attributeId.length() == 0) {
this.logger.error("No '" + stringProp + "' property");
throw new PIPException("No '" + stringProp + "' property");
}
stringProp = idPrefix + PROP_DATATYPE;
String dataTypeId = properties.getProperty(stringProp);
if (dataTypeId == null || dataTypeId.length() == 0) {
this.logger.error("No '" + stringProp + "' property");
throw new PIPException("No '" + stringProp + "' property");
}
stringProp = idPrefix + PROP_CATEGORY;
String categoryId = properties.getProperty(stringProp);
if (categoryId == null) {
this.logger.error("No '" + stringProp + "' property");
throw new PIPException("No '" + stringProp + "' property");
}
stringProp = idPrefix + PROP_ISSUER;
String issuer = properties.getProperty(stringProp);
return new StdPIPRequest(new IdentifierImpl(categoryId), new IdentifierImpl(attributeId), new IdentifierImpl(dataTypeId), issuer);
}
*/
protected void configureField(String id, String fieldName, Properties properties) throws PIPException {
PIPRequest pipRequestField = Configurables.getPIPRequest(id + "." + PROP_SELECT_FIELD + "." + fieldName, properties, this.defaultIssuer);
this.supportedRequests.add(pipRequestField);
this.supportedRequestsNoIssuer.add(new StdPIPRequest(pipRequestField.getCategory(), pipRequestField.getAttributeId(), pipRequestField.getDataTypeId()));
this.mapFields.put(fieldName, pipRequestField);
}
protected void configureParameter(String id, String parameterName, Properties properties) throws PIPException {
PIPRequest pipRequestParameter = Configurables.getPIPRequest(id + "." + PROP_SELECT_PARAMETER + "." + parameterName, properties, null);
this.parameters.add(pipRequestParameter);
}
@Override
public void configure(String id, Properties properties, String defaultIssuer) throws PIPException {
/*
* Save our default issuer
*/
this.defaultIssuer = defaultIssuer;
/*
* Get the SELECT statement to be used in the prepared statement
*/
String idPrefix = id + ".";
String stringProp = idPrefix + PROP_SELECT;
this.sqlQuery = properties.getProperty(stringProp);
if (this.sqlQuery == null || this.sqlQuery.length() == 0) {
this.logger.error("No '" + stringProp + "' property");
throw new PIPException("No '" + stringProp + "' property");
}
/*
* Get the list of database columns returned by the query
*/
stringProp = idPrefix + PROP_SELECT_FIELDS;
String fields = properties.getProperty(stringProp);
if (fields == null || fields.length() == 0) {
this.logger.error("No '" + stringProp + "' property");
throw new PIPException("No '" + stringProp + "' property");
}
for (String field : Splitter.on(',').trimResults().omitEmptyStrings().split(fields)) {
this.configureField(id, field, properties);
}
/*
* Get the list of query parameters. This may be null
*/
stringProp = idPrefix + PROP_SELECT_PARAMETERS;
String parameters = properties.getProperty(stringProp);
if (parameters != null && parameters.length() > 0) {
for (String parameter : Splitter.on(',').trimResults().omitEmptyStrings().split(parameters)) {
this.configureParameter(id, parameter, properties);
}
}
}
@Override
public PreparedStatement getPreparedStatement(PIPEngine pipEngine, PIPRequest pipRequest, PIPFinder pipFinder, Connection connection) throws PIPException {
/*
* Do we support the request?
*/
if (!this.isSupported(pipRequest)) {
return null;
}
PreparedStatement preparedStatement = null;
try {
preparedStatement = connection.prepareStatement(this.sqlQuery);
} catch (SQLException ex) {
this.logger.error("SQLException creating PreparedStatement: " + ex.toString(), ex);
// TODO: throw the exception or return a null PreparedStatement?
return null;
}
if (this.parameters.size() > 0) {
/*
* Gather all of the AttributeValues for parameters to the prepared statement. For now, we assume a single value for each
* parameter. If there are multiple values we will log an error and return a null PreparedStatement.
*
* TODO: Should the interface change to return a cross-product of PreparedStatements to deal with multiple values for parameters?
* If not, should we just take the first value and use it as the parameter value?
*/
for (int i = 0 ; i < this.parameters.size() ; i++) {
PIPRequest pipRequestParameter = this.parameters.get(i);
PIPResponse pipResponse = pipFinder.getMatchingAttributes(pipRequestParameter, null);
if (pipResponse.getStatus() == null || pipResponse.getStatus().isOk()) {
Collection listAttributes = pipResponse.getAttributes();
if (listAttributes.size() > 0) {
if (listAttributes.size() > 1) {
this.logger.error("PIPFinder returned more than one Attribute for " + pipRequestParameter.toString());
throw new PIPException("PIPFinder returned more than one Attribute for " + pipRequestParameter.toString());
}
Collection> listAttributeValuesReturned = listAttributes.iterator().next().getValues();
if (listAttributeValuesReturned.size() > 0) {
if (listAttributeValuesReturned.size() > 1) {
this.logger.warn("PIPFinder returned more than one AttributeValue for " + pipRequestParameter.toString());
return null;
}
AttributeValue> attributeValue = listAttributeValuesReturned.iterator().next();
Identifier identifierAttributeValueDataType = attributeValue.getDataTypeId();
try {
if (identifierAttributeValueDataType.equals(XACML3.ID_DATATYPE_INTEGER)) {
preparedStatement.setInt(i+1, DataTypes.DT_INTEGER.convert(attributeValue.getValue()).intValue());
} else if (identifierAttributeValueDataType.equals(XACML3.ID_DATATYPE_DOUBLE)) {
preparedStatement.setDouble(i+1, DataTypes.DT_DOUBLE.convert(attributeValue.getValue()));
} else if (identifierAttributeValueDataType.equals(XACML3.ID_DATATYPE_BOOLEAN)) {
preparedStatement.setBoolean(i+1, DataTypes.DT_BOOLEAN.convert(attributeValue.getValue()));
} else if (identifierAttributeValueDataType.equals(XACML3.ID_DATATYPE_DATETIME)) {
ISO8601DateTime iso8601DateTime = DataTypes.DT_DATETIME.convert(attributeValue.getValue());
java.sql.Date sqlDate = new java.sql.Date(iso8601DateTime.getCalendar().getTimeInMillis());
preparedStatement.setDate(i+1, sqlDate, iso8601DateTime.getCalendar());
} else if (identifierAttributeValueDataType.equals(XACML3.ID_DATATYPE_DATE)) {
ISO8601Date iso8601Date = DataTypes.DT_DATE.convert(attributeValue.getValue());
java.sql.Date sqlDate = new java.sql.Date(iso8601Date.getCalendar().getTimeInMillis());
preparedStatement.setDate(i+1, sqlDate, iso8601Date.getCalendar());
} else {
preparedStatement.setString(i+1, DataTypes.DT_STRING.convert(attributeValue.getValue()));
}
} catch (Exception ex) {
this.logger.error("Exception setting parameter " + (i+1) + " to " + attributeValue.toString() + ": " + ex.toString(), ex);
return null;
}
} else {
this.logger.warn("No AttributeValues returned for parameter " + pipRequestParameter.toString());
return null;
}
} else {
this.logger.warn("No Attributes returned for parameter " + pipRequestParameter.toString());
return null;
}
} else {
this.logger.warn("PIPFinder returned status " + pipResponse.getStatus().toString());
return null;
}
}
}
return preparedStatement;
}
/**
* Creates an {@link com.att.research.xacml.api.Attribute} from the value associated with the field with the given fieldName
.
*
* @param resultSet the {@link java.sql.ResultSet} containing the current row from the database
* @param fieldName the String
name of the field containing the attribute value
* @param pipRequestAttribute the {@link com.att.research.xacml.api.pip.PIPRequest} for the Attribute
to create
* @return a new Attribute
with the value of the given fieldName
.
*/
protected Attribute getAttributeFromResultSet(ResultSet resultSet, String fieldName, PIPRequest pipRequestAttribute) {
AttributeValue> attributeValue = null;
Identifier identifierDataType = pipRequestAttribute.getDataTypeId();
try {
DataType> dataType = dataTypeFactory.getDataType(identifierDataType);
if (dataType == null) {
this.logger.warn("Unknown data type " + pipRequestAttribute.getDataTypeId().stringValue());
return null;
}
/*
* Try to find the column index
*/
int columnIndex = -1;
try {
columnIndex = resultSet.findColumn(fieldName);
} catch (Exception e) {
/*
* The field name could be an integer, let's try that
*/
try {
columnIndex = Integer.parseInt(fieldName);
} catch (Exception e1) {
logger.error("Failed to find column with label " + fieldName);
}
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Column " + fieldName + " maps to column index " + columnIndex);
}
/*
* Catch special cases for database types
*/
if (identifierDataType.equals(XACML3.ID_DATATYPE_BOOLEAN)) {
attributeValue = dataType.createAttributeValue(resultSet.getBoolean(columnIndex));
} else if (identifierDataType.equals(XACML3.ID_DATATYPE_DATE) || identifierDataType.equals(XACML3.ID_DATATYPE_DATETIME)) {
attributeValue = dataType.createAttributeValue(resultSet.getDate(columnIndex));
} else if (identifierDataType.equals(XACML3.ID_DATATYPE_DOUBLE)) {
attributeValue = dataType.createAttributeValue(resultSet.getDouble(columnIndex));
} else if (identifierDataType.equals(XACML3.ID_DATATYPE_INTEGER)) {
attributeValue = dataType.createAttributeValue(resultSet.getInt(columnIndex));
} else {
/*
* Default to convert the string value from the database to the requested data type
*/
String stringValue = resultSet.getString(columnIndex);
if (stringValue != null) {
attributeValue = dataType.createAttributeValue(stringValue);
}
}
} catch (Exception ex) {
this.logger.error("Exception getting value for fieldName '" + fieldName + "' as a " + identifierDataType.stringValue() + ": " + ex.toString(), ex);
return null;
}
String issuer = this.defaultIssuer;
if (pipRequestAttribute.getIssuer() != null) {
issuer = pipRequestAttribute.getIssuer();
}
return new StdAttribute(pipRequestAttribute.getCategory(), pipRequestAttribute.getAttributeId(), attributeValue, issuer, false);
}
@Override
public List decodeResult(ResultSet resultSet) throws PIPException {
List listAttributes = new ArrayList();
for (String fieldName : this.mapFields.keySet()) {
PIPRequest pipRequestField = this.mapFields.get(fieldName);
assert(pipRequestField != null);
Attribute attribute = this.getAttributeFromResultSet(resultSet, fieldName, pipRequestField);
if (attribute != null) {
listAttributes.add(attribute);
}
}
return listAttributes;
}
@Override
public void attributesRequired(Collection parameters) {
for (PIPRequest parameter : this.parameters) {
parameters.add(new StdPIPRequest(parameter.getCategory(), parameter.getAttributeId(), parameter.getDataTypeId(), parameter.getIssuer()));
}
}
@Override
public void attributesProvided(Collection attributes) {
for (String key : this.mapFields.keySet()) {
PIPRequest attribute = this.mapFields.get(key);
attributes.add(new StdPIPRequest(attribute.getCategory(),
attribute.getAttributeId(),
attribute.getDataTypeId(),
(attribute.getIssuer() != null ? attribute.getIssuer() : this.defaultIssuer)));
}
}
}