blob: 9d20783e4a264111ce360a579a09f1c2e796daeb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
/*
* AT&T - PROPRIETARY
* THIS FILE CONTAINS PROPRIETARY INFORMATION OF
* AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
* ACCORDANCE WITH APPLICABLE AGREEMENTS.
*
* Copyright (c) 2013 AT&T Knowledge Ventures
* Unpublished and Not for Publication
* All Rights Reserved
*/
package org.apache.openaz.xacml.pdp.std.functions;
import java.util.List;
import org.apache.openaz.xacml.api.AttributeValue;
import org.apache.openaz.xacml.api.Identifier;
import org.apache.openaz.xacml.pdp.eval.EvaluationContext;
import org.apache.openaz.xacml.pdp.policy.ExpressionResult;
import org.apache.openaz.xacml.pdp.policy.FunctionArgument;
import org.apache.openaz.xacml.std.StdStatus;
import org.apache.openaz.xacml.std.StdStatusCode;
import org.apache.openaz.xacml.std.datatypes.DataTypes;
/**
* FunctionDefinitionLogical extends
* {@link org.apache.openaz.xacml.pdp.std.functions.FunctionDefinitionHomogeneousSimple} to implement the
* XACML Logic predicates as functions taking zero, one, or multiple arguments of type <code>Boolean</code>
* and returning a <code>Boolean</code>. In the first implementation of XACML we had separate files for each
* XACML Function. This release combines multiple Functions in fewer files to minimize code duplication. This
* file supports the following XACML codes: or and n-of not
*/
public class FunctionDefinitionLogical extends FunctionDefinitionHomogeneousSimple<Boolean, Boolean> {
/**
* List of Logical Operations types
*/
public enum OPERATION {
OR,
AND,
N_OF,
NOT
}
// the operation that this instance is being asked to do
private final OPERATION operation;
public FunctionDefinitionLogical(Identifier idIn, OPERATION op) {
super(idIn, DataTypes.DT_BOOLEAN, DataTypes.DT_BOOLEAN, null);
operation = op;
}
@Override
public ExpressionResult evaluate(EvaluationContext evaluationContext, List<FunctionArgument> arguments) {
switch (operation) {
case OR:
if (arguments == null || arguments.size() == 0) {
return ER_FALSE;
}
try {
// evaluate the arguments one at a time and abort on the first true
for (int i = 0; i < arguments.size(); i++) {
ConvertedArgument<Boolean> argument = new ConvertedArgument<Boolean>(
arguments.get(i),
this.getDataTypeArgs(),
false);
if (!argument.isOk()) {
// return a decorated message
return ExpressionResult.newError(getFunctionStatus(argument.getStatus()));
}
if (argument.getValue()) {
return ER_TRUE;
}
}
} catch (Exception e) {
String message = e.getMessage();
if (e.getCause() != null) {
message = e.getCause().getMessage();
}
return ExpressionResult.newError(new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR,
this.getShortFunctionId() + " " + message));
}
return ER_FALSE;
case AND:
if (arguments == null || arguments.size() == 0) {
return ER_TRUE;
}
try {
// evaluate the arguments one at a time and abort on the first false
for (int i = 0; i < arguments.size(); i++) {
ConvertedArgument<Boolean> argument = new ConvertedArgument<Boolean>(
arguments.get(i),
this.getDataTypeArgs(),
false);
if (!argument.isOk()) {
return ExpressionResult.newError(getFunctionStatus(argument.getStatus()));
}
if (!argument.getValue()) {
return ER_FALSE;
}
}
} catch (Exception e) {
String message = e.getMessage();
if (e.getCause() != null) {
message = e.getCause().getMessage();
}
return ExpressionResult.newError(new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR,
this.getShortFunctionId() + " " + message));
}
return ER_TRUE;
case N_OF:
Integer argumentCountNeeded;
int trueArgumentsSeen = 0;
if (arguments == null || arguments.size() == 0) {
return ExpressionResult.newError(new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR,
this.getShortFunctionId()
+ " Expected 1 argument, got 0"));
}
try {
//
// Special case:
// The first argument in the list (an Integer) is not homogeneous with the rest of the
// arguments (Booleans).
// While this is technically not a FunctionDefinitionHomogeneousSimple type of object, we
// derive from that class anyway
// so that we can take advantage of the validateArgument() method in that class.
// Unfortunately we cannot re-use that same code (because of generics - it gets messy) for the
// Integer argument.
// The following code essentially does the same job as validateArgument() on the first
// argument in the list.
//
// first arg is the number of remaining arguments that must be TRUE
if (arguments.get(0) == null) {
return ER_TRUE;
}
if (!arguments.get(0).getStatus().isOk()) {
return ExpressionResult.newError(getFunctionStatus(arguments.get(0).getStatus()));
}
if (arguments.get(0).isBag()) {
return ExpressionResult
.newError(new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR, this
.getShortFunctionId() + " Expected a simple value, saw a bag"));
}
AttributeValue<?> attributeValue = arguments.get(0).getValue();
if (attributeValue == null) {
// assume this is the same as "first argument is 0"
return ER_TRUE;
}
argumentCountNeeded = DataTypes.DT_INTEGER.convert(attributeValue.getValue()).intValue();
if (argumentCountNeeded == 0) {
return ER_TRUE;
}
if (arguments.size() - 1 < argumentCountNeeded) {
// return a non-OK status to signal indeterminate
return ExpressionResult
.newError(new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR,
this.getShortFunctionId() + " Expected "
+ argumentCountNeeded + " arguments but only "
+ (arguments.size() - 1)
+ " arguments in list after the count"));
}
for (int i = 1; i < arguments.size(); i++) {
ConvertedArgument<Boolean> argument = new ConvertedArgument<Boolean>(
arguments.get(i),
this.getDataTypeArgs(),
false);
if (!argument.isOk()) {
return ExpressionResult.newError(getFunctionStatus(argument.getStatus()));
}
if (argument.getValue()) {
trueArgumentsSeen++;
if (trueArgumentsSeen >= argumentCountNeeded) {
return ER_TRUE;
}
}
// if we cannot reach the goal, stop now.
// remaining entries to be looked at = list size - i - 1, which is the most additional
// TRUEs that we could get.
if ((arguments.size() - i - 1) + trueArgumentsSeen < argumentCountNeeded) {
// do not evaluate remaining entries
return ER_FALSE;
}
}
// did not reach our goal
return ER_FALSE;
} catch (Exception e) {
String message = e.getMessage();
if (e.getCause() != null) {
message = e.getCause().getMessage();
}
return ExpressionResult.newError(new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR,
this.getShortFunctionId() + " " + message));
}
case NOT:
if (arguments == null || arguments.size() != 1) {
return ExpressionResult.newError(new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR,
this.getShortFunctionId()
+ " Expected 1 argument, got "
+ ((arguments == null)
? "null" : arguments.size())));
}
try {
ConvertedArgument<Boolean> argument = new ConvertedArgument<Boolean>(arguments.get(0),
this.getDataTypeArgs(),
false);
if (!argument.isOk()) {
return ExpressionResult.newError(getFunctionStatus(argument.getStatus()));
}
if (argument.getValue()) {
return ER_FALSE;
} else {
return ER_TRUE;
}
} catch (Exception e) {
String message = e.getMessage();
if (e.getCause() != null) {
message = e.getCause().getMessage();
}
return ExpressionResult.newError(new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR,
this.getShortFunctionId() + " " + message));
}
}
// all cases should have been covered by above - should never get here
return ExpressionResult.newError(new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR, this
.getShortFunctionId() + " Could not evaluate Logical function " + operation));
}
}