package com.nailabs.abac.process;
import com.nailabs.abac.trust.*;
import com.nailabs.abac.test.*;
import com.nailabs.abac.credential.*;
import edu.stanford.peer.rbtm.credential.*;
import edu.stanford.peer.rbtm.engine.*;
import edu.stanford.rt.credential.CredentialDomain;
import edu.stanford.rt.credential.CredentialStore;
import edu.stanford.rt.credential.RTContext;
import edu.stanford.rt.parser.RTParser;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
/**
* A trust target graph (TTG) is used to represent the state of a trust
* negotiation in process (one per graph).
* @version $Id: TTG.java,v 1.50 2003/02/26 00:45:11 jjacobs Exp $
*/
public class TTG extends Observable implements Observer {
/** The top-level trust target the system is trying to solve. */
protected TargetNode root;
/** An index of TTGNodes based on their trust targets */
protected Hashtable indexByTT = new Hashtable();
/** The old change list for this graph */
protected Message oldDelta = new Message();
/** The current change list for this graph */
protected Message newDelta = new Message();
/** A set of all previoussent credentials */
protected HashSet outCache = new HashSet();
/** A graph engine for storing remotely held credential */
protected GraphEngine oppoCreds = null;
/** An RTML parser for de-serializing RTML evidence */
protected RTParser parser = null;
/** An RTML context for incoming opponent's credentials */
protected RTContext rtc = null;
/** A context for a single trust negotiation */
protected NegotiationContext context;
/** Create a trust target graph for a specific negotiation */
public TTG(NegotiationContext context){
this.context = context;
if(context.getFrontier() instanceof RtmlFrontier) {
try {
parser = new RTParser();
rtc = new RTContext(parser);
oppoCreds = new RtmlEngine(new CredentialStore(parser));
}
catch(Exception ex) {
debug("rtml", ex.getMessage());
}
} else {
oppoCreds = new GraphEngine();
}
}
/** Propagates node state change information to a graph observer */
public void update(Observable o, Object arg) {
//if(o instanceof TTGNode) {
setChanged();
notifyObservers(arg);
//}
//else {
//debug("update", "received unexpected observation from " + arg);
//}
}
/**
* This returns an internal list of all node in the trust target graph.
* WARNING: this should be a privileged function for production use. Also,
* any changes to the TTG will invalidate this iterator!
*/
public Iterator getNodes() {
return indexByTT.values().iterator();
}
public TTGNode getNode(Goal key) {
TTGNode node = getNodeByHash(key);
if(node == null) {
node = TTGNode.createNode(key);
putNodeByHash(node);
debug("node", "initialized node for " + key);
}
else {
debug("node", "found node for " + key);
}
return node;
}
/** Access a trust target node by the goal it represents */
public TTGNode getNodeByHash(Goal key) {
return (TTGNode)indexByTT.get(key.toString());
}
/** Entry point for global or local changes. */
public void putNodeByHash(TTGNode node) {
indexByTT.put(node.getGoal().toString(), node);
debug("node", "putting " + node.getGoal());
node.init(context);
context.getStrategy().addToWorklist(node);
context.getStrategy().addNewNode(node);
setChanged();
notifyObservers((Object)node);
}
public void putRoot(TargetNode node) {
debug("root", "putting root" + node);
root = node;
setChanged();
notifyObservers((Object)node);
}
/** A method to reset the change list at the end of a processing cycle */
public void reset() {
//consolidate credentials
Iterator i = newDelta.getEvidence();
while(i.hasNext()) {
Object potential = i.next();
if(outCache.contains(potential)) {
i.remove();
} else {
outCache.add(potential);
}
}
oldDelta = newDelta; // swap out the deltas
newDelta = new Message();
setChanged(); // notify observers of the changed message
notifyObservers((Object)oldDelta);
}
/** Accessor method for the change list on the graph and resets the list */
public Message getChanges() {
return newDelta;
}
public boolean verifyEvidence(Operation op) {
Entity self = context.getSelf();
if(op instanceof LinkingImplicationEdge) {
return false;
}
if((op instanceof ImplicationEdge)) {
Goal g = ((EdgeOperation)op).getParent();
debug("message", "verifying operation " + op);
return(self.equals(g.getVerifier()));
}
debug("message", "not verifying operation " + op);
return false;
}
/** boolean query to determine whether the sepcified goal needs evidence */
public boolean provideEvidence(Operation edge) {
Goal g = ((EdgeOperation)edge).getParent();
Entity self = context.getSelf();
return (!self.equals(g.getVerifier()));
}
/** Update this graph with changes contained in the specified message */
public void update(Message msg) throws Exception {
setChanged();
//notifyObservers("\n");
notifyObservers("location = remote");
Iterator ev = msg.getEvidence();
while(ev.hasNext()) {
Object eObj = ev.next();
if(eObj instanceof String) {
String xml = (String) eObj;
InputStream in = new ByteArrayInputStream(xml.getBytes());
CredentialDomain domain =
parser.parseCredentialDomain(in, rtc);
((RtmlEngine)oppoCreds).addDomain(domain);
debug("message", "received evidence = " + domain);
}
else {
StaticCredential credential = (StaticCredential)eObj;
Debug.rbtmStart();
oppoCreds.addCredential(credential);
Debug.rbtmStop();
debug("message", "received evidence = " + credential);
}
}
Iterator operations = msg.getOperations();
while(operations.hasNext()) {
Operation op = (Operation)operations.next();
debug("message", "performing op " + op);
if(verifyEvidence(op)) {
EdgeOperation edgeOp = (EdgeOperation)op;
TrustTarget parent = (TrustTarget)edgeOp.getParent();
TrustTarget child = (TrustTarget)edgeOp.getChild();
HashSet evidence = null;
//try {
EntityExpression parentRole = parent.getTargetRole();
EntityExpression childRole = child.getTargetRole();
debug("message", "chain search from " +
parentRole + " to " + childRole);
Debug.rbtmStart();
oppoCreds.backwardSearch(parentRole);
if(parentRole.equals(childRole)) {
debug("message", "roles are equal bailing out now!");
} else {
evidence = oppoCreds.getChain(parentRole, childRole);
// }
Debug.rbtmStop();
//} catch(Exception ex) {
//ex.printStackTrace();
//throw(new java.rmi.RemoteException("Unjustified implication!!"));
}
edgeOp.setEvidence(evidence);
debug("message", "evidence = " + evidence);
}
setChanged();
notifyObservers((Object)op);
op.perform(this);
}
setChanged();
//notifyObservers("\n");
}
/**
* Accessor method for of root of graph. This is necessary to traverse
* the graph.
* @returns the root node which may be any TTGNode descendent.
*/
public TargetNode getRoot() {
return root;
}
// Methods in this section are recorded in the delta list
/**
* Insert a new root node for this graph.
* This is a global operation
*/
public void setRoot(TrustTarget target) {
debug("root", "setting root = " + target);
NodeOperation op = new NodeOperation
(target, target.getInitialProcessingState(context), true);
setChanged();
notifyObservers(op);
op.perform(this);
newDelta.addOperation(op);
}
/**
* Mark a node in the graph as opponent processed.
*/
public void setOpponentProcessed(Goal id) {
//TTGNode node = getNode(id);
//ProcessingState state = node.getProcessingState();
//state.opponentProcess(state);
setNode(id, new ProcessingState(false, true));
}
/**
* Mark a node in the graph as verifier processed.
*/
public void setVerifierProcessed(Goal id) {
//TTGNode node = getNode(id);
//ProcessingState state = node.getProcessingState();
//state.verifierProcess();
setNode(id, new ProcessingState(true, false));
}
/**
* Add a root or modify a node to this TTG and replicate the changes on
* the opponent's TTG.
* This is a global operation
*/
public void setNode(Goal id, ProcessingState state)
{
ProcessingState copy = new ProcessingState();
copy.update(state);
NodeOperation nodeOp = new NodeOperation(id, copy);
context.getStrategy().scheduleOperation(nodeOp);
}
/** actually performs the node operation on this graph */
public void performNode(NodeOperation nodeOp) {
// This is slightly different than perform edge although they
// could possibly be merged into a single performOperation method
nodeOp.perform(this);
newDelta.addOperation((Operation)nodeOp);
setChanged();
notifyObservers((Object)nodeOp);
}
/** actually performs a previously scheduled edge operation */
public void performEdge(EdgeOperation edgeOp)
{
if(edgeOp instanceof LinkingImplicationEdge) {
setChanged();
notifyObservers("location = async");
}
setChanged();
notifyObservers((Object)edgeOp);
edgeOp.perform(this);
if(edgeOp instanceof ImplicationEdge && provideEvidence(edgeOp)) {
debug("message", " %%%% adding evidence for " + edgeOp.toString());
newDelta.addOperation((Operation)edgeOp);
} else {
debug("message", " %%%% not adding evidence for" +
edgeOp.toString());
newDelta.addOperationWithoutEvidence((Operation)edgeOp);
}
if(edgeOp instanceof LinkingImplicationEdge) {
setChanged();
notifyObservers("location = sync");
}
}
/**
* Add a new control edge to this TTG and replicate the changes on the
* opponent's TTG.
* This is a global operation
*/
public void addControlEdge(Goal parent, Goal child)
{
TTGNode cnode = getNodeByHash(child);
ProcessingState state = (cnode == null)?
child.getInitialProcessingState(context): null;
EdgeOperation edgeOp = new ControlEdge(parent, child, state);
context.getStrategy().scheduleOperation(edgeOp);
}
/**
* Add a new implication edge to this TTG and replicate the changes on the
* opponent's TTG.
* This is a global operation
*/
public void addImplicationEdge(Goal parent, Goal child, HashSet creds) {
TTGNode cnode = getNodeByHash(child);
//debug("node", "implication cnode = " + cnode);
ProcessingState state = (cnode == null)?
child.getInitialProcessingState(context): null;
ImplicationEdge edgeOp =
new ImplicationEdge(parent, child, state, creds);
context.getStrategy().scheduleOperation(edgeOp);
}
/**
* Add a new intersection edge to this TTG and replicate the edge on the
* opponent's TTG. This is a global operation
*/
public void addIntersectionEdge(TrustTarget parent, TrustTarget child) {
TargetNode cnode = (TargetNode)getNodeByHash(child);
debug("node", "implication cnode = " + cnode);
ProcessingState state = (cnode == null)?
child.getInitialProcessingState(context): null;
IntersectionEdge edgeOp = new IntersectionEdge(parent, child, state);
context.getStrategy().scheduleOperation(edgeOp);
}
public void addLinkingMonitorEdge(TrustTarget parent, LinkingGoal child) {
TTGNode cnode = getNodeByHash(child);
ProcessingState state = (cnode == null)?
child.getInitialProcessingState(context): null;
EdgeOperation edgeOp = new LinkingMonitorEdge(parent, child, state);
context.getStrategy().scheduleOperation(edgeOp);
}
public void addLinkingSolutionEdge(LinkingGoal monitor, TrustTarget soln) {
TTGNode cnode = getNodeByHash(soln);
ProcessingState state = (cnode == null)?
soln.getInitialProcessingState(context): null;
EdgeOperation edgeOp = new LinkingSolutionEdge(monitor, soln, state);
context.getStrategy().scheduleOperation(edgeOp);
}
public void addLinkingImplicationEdge(TrustTarget parent, TrustTarget child)
{
TTGNode cnode = getNodeByHash(child);
ProcessingState state = (cnode == null)?
child.getInitialProcessingState(context): null;
EdgeOperation edgeOp =
new LinkingImplicationEdge(parent, child, state);
context.getStrategy().scheduleOperation(edgeOp);
}
/** Friendly debugging convenience method */
protected void debug(String level, String message) {
StringBuffer buff = new StringBuffer("TTG[");
buff.append(context.getSelf()).append("]: ").append(message);
Debug.debug(level, buff.toString());
}
public String toString() {
StringBuffer buff = new StringBuffer("[TTG EntityID=");
buff.append(context.getSelf().toString()).append("\n");
Iterator i = indexByTT.keySet().iterator();
while(i.hasNext()) {
buff.append("\t").append(i.next()).append("\n");
}
buff.append("]");
return buff.toString();
}
}