/* Copyright 2012-2015 SAP SE
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.aniketos.securebpmn.xacml.pdp.runtimeEvaluation;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import eu.aniketos.securebpmn.xacml.api.autho.AttributeIdentifier;
import eu.aniketos.securebpmn.xacml.pdp.runtimeEvaluation.attributes.AbstractAttribute;
import eu.aniketos.securebpmn.xacml.pdp.runtimeEvaluation.attributes.AttributeHelper;
import com.sun.xacml.AbstractPolicy;
import com.sun.xacml.attr.AttributeDesignator;
import com.sun.xacml.attr.AttributeValue;
import com.sun.xacml.attr.BagAttribute;
import com.sun.xacml.attr.TimeAttribute;
import com.sun.xacml.combine.CombiningAlgorithm;
import com.sun.xacml.cond.Apply;
import com.sun.xacml.cond.BagFunction;
import com.sun.xacml.cond.EqualFunction;
import com.sun.xacml.cond.Expression;
import com.sun.xacml.cond.Function;
import com.sun.xacml.cond.HigherOrderFunction;
import com.sun.xacml.cond.LogicalFunction;
import com.sun.xacml.cond.TimeInRangeFunction;
import com.sun.xacml.ctx.Result;
public class HOLEncoder implements ExtToolTypeEncoder {
private static HOLEncoder instance = new HOLEncoder();
private static Map<URI, FunctionInfo> functions;
private static final String VALUE = "#";
private static final String VAR = "$";
private static final String EQUAL = "==";
//private static final String NOT_EQUAL = "<>";
private static final String LESS_OR_EQUAL = "<=";
private static final String MORE_OR_EQUAL = ">=";
//private static final String LESS = "<";
private static final String MORE = ">";
private static final String AND = "AND";
private static final String OR = "OR";
//Higher order functions
private static final String ANYOFANY = "anyOfAny";
//bag functions
private static final String BAGSIZE = "bagsize";
private static final String time_max = 24*60*60*1000 + "";
static {
functions = new HashMap<URI, FunctionInfo>();
//FunctionInfo(FunctionID functionID, String operator, Notation notation, boolean defaultConstr)
functions.put(
URI.create(EqualFunction.NAME_STRING_EQUAL),
new FunctionInfo(FunctionID.STRING_EQUAL, EQUAL, Notation.INFIX));
functions.put(
URI.create(EqualFunction.NAME_ANYURI_EQUAL),
new FunctionInfo(FunctionID.ANYURI_EQUAL, EQUAL, Notation.INFIX));
functions.put(
URI.create(EqualFunction.NAME_BOOLEAN_EQUAL),
new FunctionInfo(FunctionID.BOOLEAN_EQUAL, EQUAL, Notation.INFIX));
functions.put(
URI.create(EqualFunction.NAME_INTEGER_EQUAL),
new FunctionInfo(FunctionID.INTEGER_EQUAL, EQUAL, Notation.INFIX));
functions.put(
URI.create(LogicalFunction.NAME_AND),
new FunctionInfo(FunctionID.AND, AND, Notation.INFIX));
functions.put(
URI.create(LogicalFunction.NAME_OR),
new FunctionInfo(FunctionID.OR, OR, Notation.INFIX));
functions.put(
URI.create("urn:oasis:names:tc:xacml:1.0:function:string-one-and-only"),
new FunctionInfo(FunctionID.ONE_AND_ONLY, "", Notation.PREFIX));
functions.put(
URI.create("urn:oasis:names:tc:xacml:1.0:function:time-one-and-only"),
new FunctionInfo(FunctionID.ONE_AND_ONLY, "", Notation.PREFIX));
functions.put(
URI.create("urn:oasis:names:tc:xacml:1.0:function:boolean-one-and-only"),
new FunctionInfo(FunctionID.ONE_AND_ONLY, "", Notation.PREFIX));
functions.put(
URI.create(TimeInRangeFunction.NAME),
new FunctionInfo(FunctionID.TIME_IN_RANGE, null, Notation.CUSTOM));
// TODO
functions.put(
URI.create(HigherOrderFunction.NAME_ANY_OF_ANY),
new FunctionInfo(FunctionID.ANY_OF_ANY, ANYOFANY, Notation.FUNCTION));
functions.put(
URI.create(BagFunction.FUNCTION_NS + "string" + BagFunction.NAME_BASE_BAG_SIZE),
new FunctionInfo(FunctionID.STRING_BAGSIZE, BAGSIZE, Notation.FUNCTION));
}
private enum FunctionID {
STRING_EQUAL,
ANYURI_EQUAL,
BOOLEAN_EQUAL,
INTEGER_EQUAL,
AND,
OR,
TIME_IN_RANGE,
ONE_AND_ONLY,
ANY_OF_ANY,
STRING_BAGSIZE
}
private enum Notation {
PREFIX,
INFIX,
POSTFIX,
FUNCTION,
CUSTOM
}
private KnownAttrStore attrReslv;
public static HOLEncoder getInstance() {
return instance;
}
public void setKnownAttrStore(KnownAttrStore attrReslv) {
this.attrReslv = attrReslv;
}
public String getFunctionEnc(Function func) {
if ( functions.containsKey(func.getIdentifier()) ) {
return functions.get(func.getIdentifier()).operator;
} else {
throw new RuntimeException("Function " + func.getIdentifier() + " currently not supported!");
}
}
// private void printInfix(Apply apply, Function func, FunctionInfo info, PrintStream out) {
// out.print("(");
// List<Expression> childs = apply.getChildren();
// if ( childs.size() != 2) {
// throw new RuntimeException("Infix notation currently only possible for 2 parameters!");
// }
// printExpression(childs.get(0), out);
// out.print(" " + info.operator + " ");
// printExpression(childs.get(1), out);
// out.print(") ");
// }
private void printInfix(Apply apply, Function func, FunctionInfo info, PrintStream out) {
out.print("(");
List<Expression> childs = apply.getChildren();
if ( childs.size() < 2) {
throw new RuntimeException("Infix notation currently only possible for 2 or more parameters!");
}
printExpression(childs.get(0), out);
for ( int i = 1; i < childs.size(); ++i ) {
out.print(" " + info.operator + " ");
printExpression(childs.get(i), out);
}
out.print(") ");
}
private void printPrefix(Apply apply, Function func, FunctionInfo info, PrintStream out) {
out.print(" " + info.operator + " (");
for ( Expression child : apply.getChildren() ) {
printExpression(child, out);
}
out.print(")");
}
/**
* prints functions which are part of an apply element
* @param apply
* @param func
* @param info
* @param out
*/
private void printFunction(Apply apply, Function func, FunctionInfo info, PrintStream out) {
out.print(" (" + info.operator + " ( ");
boolean first = true;
for ( Expression child : apply.getChildren() ) {
if ( first ) {
first = false;
} else {
out.print(", ");
}
printExpression(child, out);
}
out.print(")");
}
/**
* prints functions which are "standaline", i.e., there is a <Function> element in the policy
* <br/>
* mainly/only (? - TODO) used for higher order functions?
* @param func
* @param out
*/
private void printFunction(Function func, PrintStream out) {
FunctionInfo info = getFunctionInfo(func);
out.print(info.operator);
}
private void printExpression(Expression expression, PrintStream out) {
if ( expression instanceof BagAttribute ) {
out.print(getBagEnc((BagAttribute)expression));
} else if ( expression instanceof AttributeValue ) {
out.print(getAttrValueEnc( ( AttributeValue) expression));
} else if ( expression instanceof AttributeDesignator ) {
AttributeDesignator attr = (AttributeDesignator) expression;
AttributeIdentifier attrId = AttributeHelper.getAttributeIdentifier(attr);
AttributeValue attrValRes = attrReslv.getAttributeValue(attrId);
out.print(getAttrDesignatorEnc(attr, attrValRes));
} else if ( expression instanceof Apply ) {
printApply ( (Apply) expression, out);
} else if ( expression instanceof Function ) {
printFunction ( (Function)expression, out);
}
else {
throw new RuntimeException("Unexpected element: " + expression);
}
}
public void printApply(Apply apply, PrintStream out) {
Function func = apply.getFunction();
FunctionInfo info = getFunctionInfo(func);
switch ( info.notation ) {
case INFIX:
printInfix(apply, func, info, out);
break;
case PREFIX:
printPrefix(apply, func, info, out);
break;
case POSTFIX:
throw new RuntimeException("TODO: currently not implemented");
case FUNCTION:
printFunction(apply, func, info, out);
break;
case CUSTOM:
switch ( info.functionID ) {
case TIME_IN_RANGE:
printTimeInRange(apply, func, info, out);
break;
default:
throw new RuntimeException("For function " + func.getIdentifier() + " no Custom notation is available");
}
break;
default:
throw new RuntimeException("Notation " + info.notation + " currently not supported");
}
}
public String getBagEnc(BagAttribute bag) {
if ( bag.size() != 1) {
throw new RuntimeException("BagAttributes with size != 1 currently not supported!");
}
return getAttrValueEnc( bag.iterator().next());
}
public String getAttrValueEnc(AttributeValue val) {
if ( val instanceof BagAttribute) {
return getBagEnc( (BagAttribute) val);
} else if ( val instanceof TimeAttribute ) {
return VALUE + ((TimeAttribute)val).getMilliseconds();
} else {
return VALUE + val.encode();
}
}
public String getAttrDesignatorEnc(AttributeDesignator attr, AttributeValue attrVal) {
if (attrVal != null && ! AttributeHelper.isAbstractAttribute(attrVal) ) {
return getAttrValueEnc(attrVal);
} else {
return VAR + attr.getId();
}
}
public String getAND() {
return AND;
}
public String getOR() {
// TODO Auto-generated method stub
return OR;
}
public String getDecision(int decision) {
switch ( decision) {
case Result.DECISION_DENY:
return "DENY";
case Result.DECISION_PERMIT:
return "PERMIT";
case Result.DECISION_INDETERMINATE:
return "INDETERMINATE";
case Result.DECISION_NOT_APPLICABLE:
return "NOT_APPLICABLE";
}
throw new RuntimeException("Currently not supported decision: " + decision);
}
public String getCombiningAlg(CombiningAlgorithm alg) {
String name = alg.getIdentifier().toString();
return name.substring(name.lastIndexOf(":")+1);
}
private FunctionInfo getFunctionInfo(Function func) {
FunctionInfo info = functions.get(func.getIdentifier());
if ( info == null ) {
throw new RuntimeException("Function " + func.getIdentifier() + " currently not supported!");
}
return info;
}
private static class FunctionInfo {
FunctionID functionID;
String operator;
Notation notation;
FunctionInfo(FunctionID functionID, String operator, Notation notation) {
this.functionID = functionID;
this.operator = operator;
this.notation = notation;
}
}
/*
* CUSTOM FUNCTION
*/
/**
* urn:oasis:names:tc:xacml:2.0:function:time-in-range Function:
*
* three values: a, b, c: true, if a is within b and c <br/>
* two cases:
* <li>
* <ul> b <= c: a >= b AND a <= c </ul>
* <ul> b > c: ( a >= b AND a <= 24h) OR ( a >= 0h AND a <= c ) </ul>
* </li>
* if either b or c are variables, then <br/>
* (b<=c AND a >= b AND a <= c ) OR ( b>=c AND (( a >= b AND a <= 24h) OR ( a >= 0h AND a <= c )) )
*/
private void printTimeInRange(Apply apply, Function func, FunctionInfo info, PrintStream out) {
List<Expression> childs = apply.getChildren();
if ( childs.size() != 3) {
throw new RuntimeException("TimeInRange function only possible for 3 parameters!");
}
String[] vars = new String[3];
for ( int i = 0; i < 3; ++i ) {
if ( childs.get(i) instanceof AttributeValue ) {
vars[i] = getAttrValueEnc( (AttributeValue) childs.get(i));
} else if ( childs.get(i) instanceof AttributeDesignator ) {
AttributeDesignator attrDsgn = (AttributeDesignator) childs.get(i);
AttributeValue attrVal = attrReslv.getAttributeValue(
AttributeHelper.getAttributeIdentifier(attrDsgn));
if (attrVal != null && ! (attrVal instanceof AbstractAttribute )) {
vars[i] = getAttrValueEnc(attrVal);
} else {
vars[i] = attrDsgn.getId().toString();
}
} else if ( childs.get(i) instanceof Apply ) {
ByteArrayOutputStream bOS = new ByteArrayOutputStream();
PrintStream out2 = new PrintStream(bOS);
printApply( (Apply)childs.get(i), out2);
out2.flush();
vars[i] = bOS.toString();
} else {
throw new RuntimeException("Unrecognised type for param " + i + " for TimeInRange function: " + childs.get(i).getClass());
}
}
if ( vars[1].startsWith(VAR) || vars[2].startsWith(VAR) ) {
//b or c are variables - case distinction b < c or b > c
out.print("( ( " + vars[1] + LESS_OR_EQUAL + vars[2] + " " + AND + " " + getBsC(vars) + " ) " +
OR + " ( " + vars[1] + MORE + vars[2] + " " + AND + " " + getBsC(vars) + " ) )");
} else {
int b = Integer.parseInt(vars[1].substring(1));
int c = Integer.parseInt(vars[2].substring(1));
if ( b <= c ) {
out.print(getBsC(vars));
} else {
out.print(getBgC(vars));
}
}
}
/**
* helper function for printTimeInRange - when second value smaller equal third value
* @param vars
* @return
*/
private String getBsC(String[] vars) {
// b < c: a >= b AND a <= c
return "( " + vars[0] + MORE_OR_EQUAL + vars[1] + " " + AND + " " + vars[0] + LESS_OR_EQUAL + vars[2] + ")";
}
/**
* helper function for printTimeInRange - when second value larger third value
* @param vars
* @return
*/
private String getBgC(String[] vars) {
// b > c: ( a >= b AND a <= 24h)
// OR ( a >= 0h AND a <= c )
return "( ( " + vars[0] + MORE_OR_EQUAL + vars[1] + " " + AND + " " + vars[0] + LESS_OR_EQUAL + time_max + ") " +
OR + " ( " + vars[0] + MORE_OR_EQUAL + "0 " + AND + " " + vars[0] + LESS_OR_EQUAL + vars[2] + ") )";
}
public String getPolicy(AbstractPolicy policy) {
return getCombiningAlg(policy.getCombiningAlg());
}
}