001/**
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved
005 *
006 * The contents of this file are subject to the terms
007 * of the Common Development and Distribution License
008 * (the License). You may not use this file except in
009 * compliance with the License.
010 *
011 * You can obtain a copy of the License at
012 * https://opensso.dev.java.net/public/CDDLv1.0.html or
013 * opensso/legal/CDDLv1.0.txt
014 * See the License for the specific language governing
015 * permission and limitations under the License.
016 *
017 * When distributing Covered Code, include this CDDL
018 * Header Notice in each file and include the License file
019 * at opensso/legal/CDDLv1.0.txt.
020 * If applicable, add the following below the CDDL Header,
021 * with the fields enclosed by brackets [] replaced by
022 * your own identifying information:
023 * "Portions Copyrighted [year] [name of copyright owner]"
024 *
025 * $Id: ActionDecision.java,v 1.5 2008/06/25 05:43:43 qcheng Exp $
026 *
027 */
028
029/*
030 * Portions Copyrighted [2011] [ForgeRock AS]
031 */
032package com.sun.identity.policy;
033
034import com.sun.identity.shared.debug.Debug;
035import com.sun.identity.shared.xml.XMLUtils;
036import java.util.Map;
037import java.util.HashMap;
038import java.util.Set;
039import java.util.HashSet;
040import java.util.Iterator;
041import org.w3c.dom.*;
042
043
044/**
045 * The <code>ActionDecision</code> class represents the action results of a 
046 * policy evaluation. It has action values for a given <code>action</code> and 
047 * <code>advice</code>. 
048 *
049 * @supported.api
050 */
051public class ActionDecision {
052
053    static final String ACTION_DECISION = "ActionDecision";
054    static final String ADVICES = "Advices";
055    static final String VALUES = "Values";
056    static final String TIME_TO_LIVE = "timeToLive";
057    static Debug debug = Debug.getInstance("amPolicy");
058
059    private String actionName;
060    private Set values;
061    private long timeToLive = Long.MAX_VALUE;
062    private Map advices;
063
064    /**
065     *  Difference of system clock on the client machine compared to 
066     * policy server machine. Valid life of policy decisions are extended 
067     * by this skew on the client side.
068     * Value for this is set by reading property 
069     * com.sun.identity.policy.client.clockSkew
070     * from SystemProperties
071     * If the value is not defined in AMConfig.properties, 
072     * this would default to 0.
073     */
074    private static long clientClockSkew = 0;
075
076    /**
077     * No argument constructor
078     * @deprecated No replacement API provided. 
079     * There should be no need to invoke this constructor.
080     */
081    public ActionDecision() {
082    }
083
084    /**
085     * Constructor
086     * @param actionName name of the action.
087     * @param values a <code>Set></code> of <code>String</code> values for the 
088     * action
089     * @supported.api
090     */
091    public ActionDecision(String actionName, Set values) {
092        this.actionName = actionName;
093        this.values = values;
094    }
095
096    /**
097     * Constructor
098     * @param actionName action name
099     * @param values a <code>Set</code> of <code>String</code> values for the 
100     * action
101     * @param advices <code>advices</code> associated with this action 
102     *       decision. The advice name is the key to the Map. The
103     *       value is a set of advice message Strings corresponding 
104     *       to the  advice name.
105     * @param timeToLive the GMT time in milliseconds since epoch 
106     *       when this object is to  be treated as expired. 
107     *       That is the action values would likely be different
108     *       after that time.
109     * @supported.api
110     */
111    public ActionDecision(String actionName, Set values, Map advices,
112        long timeToLive
113    ) {
114        this.actionName = actionName;
115        this.values = values;
116        this.advices = advices;
117        this.timeToLive = timeToLive;
118    }
119
120    /**
121     * Gets the name of the action
122     *
123     * @return name of the action
124     * @supported.api
125     */
126    public String getActionName() {
127        return actionName;
128    }
129
130    /**
131     * Sets the action values for the action.  
132     *
133     * @param values a <code>Set</code> of String values
134     * @supported.api
135     */
136    public void setValues(Set values) {
137        this.values = values;
138    }
139
140    /**
141     * Gets the action values for the action.  
142     *
143     * @return a <code>Set>/code> of String values
144     * @supported.api
145     */
146    public Set getValues() {
147        return values;
148    }
149
150    /**
151     * Gets the GMT time in milliseconds since epoch when this object is to
152     * be treated as expired. That is the action values would likely be 
153     * different after that time.
154     * This is computed as a result of <code>SimpleTimeCondition(s)</code>
155     * specified in the Policy definition. 
156     *
157     * @return long represeting the time to live for this object.
158     * @supported.api
159     */
160    public long getTimeToLive() {
161        return timeToLive;
162    }
163
164    /**
165     * Sets the GMT time in milliseconds since epoch when this object is to
166     * be treated as expired. That is the action values would likely be 
167     * different after that time.
168     * This is computed as a result of <code>SimpleTimeCondition(s)</code> 
169     * specified in the Policy definition. 
170     *
171     * @param timeToLive time to live
172     * @supported.api
173     */
174    public void setTimeToLive(long timeToLive) {
175        this.timeToLive = timeToLive;
176    }
177
178    /**
179     * Sets <code>advices</code> associated with this <code>ActionDecision
180     * </code>.
181     * The advice name is the key to the <code>Map</code>. The
182     * value is a <code>Set</code> of advice message Strings corresponding to 
183     * the advice name. The two  possible advices are authentication 
184     * level(<code>AuthLevel</code>) and authentication modules
185     * (<code>AuthSchemes</code>). The advice message Strings for
186     * <code>AuthLevel</code> are integer valued.
187     *
188     * @param advices map of advices
189     * @supported.api
190     */
191    public void setAdvices(Map advices) {
192        this.advices = advices;
193    }
194
195    /**
196     * Returns a <code>Map</code> of <code>advices</code> associated with this 
197     * object. 
198     * The advice name is the key to the <code>Map</code>. The
199     * value is a <code>Set</code> of advice message Strings corresponding to 
200     * the advice name. The two  possible advices are authentication 
201     * level(<code>AuthLevel</code>) and authentication modules
202     * (<code>AuthSchemes</code>). The advice message Strings for
203     * <code>AuthLevel</code> are integer valued.
204     *
205     * @return advices associated with this <code>ActionDecision</code>.
206     * @supported.api
207     */
208    public Map getAdvices() {
209        return advices;
210    }
211
212    /**
213     * Gets a String representation of this object
214     *
215     * @return a String representation of this object
216     * @supported.api
217     */
218    public String toString() {
219        return actionName + "=" + values;
220    }
221
222    /**
223     * Gets an XML representation of this object
224     *
225     * @return XML representation of this object
226     * @supported.api
227     */
228     public String toXML() {
229        StringBuilder sb  = new StringBuilder(300);
230        sb.append("<").append(ACTION_DECISION).append(" ");
231        sb.append(TIME_TO_LIVE).append("=").append(
232                PolicyUtils.quote(timeToLive)).append(">");
233        sb.append(PolicyUtils.CRLF);
234        sb.append(PolicyUtils.attributeValuePairToXMLString(getActionName(), 
235            values));
236        sb.append("<").append(ADVICES).append(">").append(PolicyUtils.CRLF);
237        if (advices != null) {
238            sb.append(PolicyUtils.mapToXMLString(advices));
239        }
240        sb.append("</").append(ADVICES).append(">").append(PolicyUtils.CRLF);
241        sb.append("</").append(ACTION_DECISION).append(">").append(
242            PolicyUtils.CRLF);
243        return sb.toString();
244     }
245
246     /**
247     * Creates an ActionDecisions object given a w3c DOM node
248      *  @param actionDecisionNode w3c DOM node for action decision
249      *
250      *  @return ActionDecisions object created using the w3c DOM node
251      *  @throws PolicyException if any error occurs during parsing.
252      */
253     public static ActionDecision parseActionDecision(Node actionDecisionNode) 
254             throws PolicyException {
255        ActionDecision actionDecision = null;
256        //process action name and values
257        Set nodeSet = XMLUtils.getChildNodes(actionDecisionNode, 
258        PolicyUtils.ATTRIBUTE_VALUE_PAIR);
259        if ( (nodeSet == null) ||  (nodeSet.isEmpty()) ) {
260            debug.error("parseActionDecision: missing element " 
261            + PolicyUtils.ATTRIBUTE_VALUE_PAIR);
262            return null;
263        }
264        Iterator nodes = nodeSet.iterator();
265        Node node = (Node)nodes.next();
266        String actionName = PolicyUtils.getAttributeName(node);
267        Set actionValues = PolicyUtils.getAttributeValues(node);
268        actionDecision = new ActionDecision(actionName,
269        actionValues);
270
271        //process timeToLive
272        long timeToLive = Long.MAX_VALUE;
273        String ttlString = XMLUtils.getNodeAttributeValue(actionDecisionNode,
274        ActionDecision.TIME_TO_LIVE) ;
275        if ( ttlString != null ) {
276            try {
277                timeToLive = Long.parseLong(ttlString);
278                if (timeToLive != Long.MAX_VALUE) {
279                    timeToLive += clientClockSkew;
280                }
281            } catch (Exception e) {
282                debug.error("Error while parsing timeToLive in "
283                + " ActionDecision:" + ttlString);
284                Object [] args = { new Long(timeToLive) };
285                throw new PolicyException(ResBundleUtils.rbName,
286                        "invalid_time_to_live",
287                    args,e);
288            }
289        }
290        actionDecision.setTimeToLive(timeToLive);
291        
292        //process advices
293        Map advices = new HashMap();
294        nodeSet = XMLUtils.getChildNodes(actionDecisionNode,
295        ActionDecision.ADVICES);
296        if (nodeSet != null) {
297            nodes = nodeSet.iterator();
298            node = (Node) nodes.next();
299            nodeSet = XMLUtils.getChildNodes(node, 
300                PolicyUtils.ATTRIBUTE_VALUE_PAIR);
301            if ( nodeSet != null ) {
302                nodes = nodeSet.iterator();
303                while ( nodes.hasNext() ) {
304                    node = (Node) nodes.next();
305                    String adviceName = PolicyUtils.getAttributeName(node);
306                    if ( adviceName != null ) {
307                        Set adviceMessages = PolicyUtils.
308                            getAttributeValues(node);
309                        advices.put(adviceName, adviceMessages);
310                    }
311                }
312            }
313        }
314        actionDecision.setAdvices(advices);
315
316        return actionDecision;
317     }
318
319    /**
320     * Creates and returns a copy of this object.
321     *
322     * @return a copy of this object
323     */
324    public Object clone() {
325        ActionDecision clone = new ActionDecision();
326        clone.actionName = actionName;
327        clone.timeToLive = timeToLive;
328
329        if (values != null) {
330            Iterator valuesIter = values.iterator();
331            clone.values = new HashSet(values.size());
332            while (valuesIter.hasNext()) {
333                clone.values.add(valuesIter.next());
334            }
335        }
336
337        if (advices != null) {
338            Iterator adviceIter = advices.keySet().iterator();
339            clone.advices = new HashMap(advices.size());
340
341            while (adviceIter.hasNext()) {
342                String key = (String) adviceIter.next();
343                clone.advices.put(key, advices.get(key));
344            }
345        }
346
347        return clone;
348    }
349
350    /**
351     * Sets the client clock skew 
352     * @param skew the time skew in milliseconds, serverTime - clientTime
353     */
354    public static void setClientClockSkew(long skew) {
355        clientClockSkew = skew;
356    }
357
358}