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: Policy.java,v 1.9 2010/01/10 01:19:35 veiming Exp $
026 *
027 */
028
029/*
030 * Portions Copyright 2011-2013 ForgeRock, Inc.
031 */
032package com.sun.identity.policy;
033
034import com.sun.identity.policy.interfaces.Subject;
035import com.sun.identity.policy.interfaces.Condition;
036import com.sun.identity.policy.interfaces.ResponseProvider;
037import com.sun.identity.policy.interfaces.Referral;
038
039import java.util.*;
040import com.sun.identity.shared.ldap.util.DN;
041
042import org.w3c.dom.*;
043
044import com.iplanet.sso.*;
045import com.sun.identity.shared.debug.Debug;
046import com.iplanet.am.util.Cache;
047import com.sun.identity.shared.xml.XMLUtils;
048import com.sun.identity.sm.AttributeSchema;
049import com.sun.identity.policy.plugins.OrgReferral;
050
051/**
052 * The class <code>Policy</code> represents a policy definition.
053 * A policy contains a set of rules associated with a collection of 
054 * users and conditions. The policy object is saved in the data store
055 * only when the <code>store</code> method of the <code>Policy</code> is 
056 * called, or if the methods <code>addPolicy</code> or <code>replacePolicy
057 * </code> of <code>PolicyManager</code> instance is invoked with this policy.
058 * The <code>Policy</code> object is accessible to policy evaluation and 
059 * enforcement points only after it is saved in data store. 
060 *
061 * @supported.api
062 */
063public class Policy implements Cloneable {
064
065    static final String REFERRAL_POLICY = "referralPolicy";
066    static final String ACTIVE_FLAG = "active";
067
068    private static final int SUBJECTS_CONDITIONS_RULES = 1;
069    private static final int CONDITIONS_SUBJECTS_RULES = 2;
070    private static final int RULES_SUBJECTS_CONDITIONS = 3;
071    private static final int RULES_CONDITIONS_SUBJECTS = 4;
072    private static final int SUBJECTS_RULES_CONDITIONS = 5;
073    private static final int CONDITIONS_RULES_SUBJECTS = 6;
074
075    private static String EVALUATION_WEIGHTS = null;
076    private static String DEFAULT_EVALUATION_WEIGHTS = "10:10:10";
077    private final static String EVALUATION_WEIGHTS_KEY 
078            = "com.sun.identity.policy.Policy.policy_evaluation_weights";
079    private static final Debug DEBUG = PolicyManager.debug;
080
081    private int evaluationOrder = RULES_SUBJECTS_CONDITIONS;
082
083    private static int ruleWeight;
084    private static int conditionWeight;
085    private static int subjectWeight;
086
087    private int prWeight;
088    private int pcWeight;
089    private int psWeight;
090
091    static {
092        initializeStaticEvaluationWeights();
093    }
094
095    private String origPolicyName;
096    private String policyName;
097    private String description = "";
098    private String createdBy;
099    private String lastModifiedBy;
100    private long creationDate;
101    private long lastModifiedDate;
102    private boolean referralPolicy=false;
103    private boolean active = true;
104
105    private int priority;
106    private Map rules = new HashMap();
107    private Subjects users = new Subjects();
108    private Conditions conditions = new Conditions();
109    private ResponseProviders respProviders = new ResponseProviders();
110    private Referrals referrals = new Referrals();
111    private String organizationName;
112    private final static int MATCHED_RULE_RESULTS_CACHE_SIZE = 1000;
113    private final static int MATCHED_REFERRAL_RULES_CACHE_SIZE = 100;
114    private Cache matchRulesResultsCache 
115            = new Cache(MATCHED_RULE_RESULTS_CACHE_SIZE);
116    private String subjectRealm;
117
118    /**
119     * No-arg constructor.
120     */
121    private Policy() {
122        // do nothing
123    }
124
125    /**
126     * Constructs a policy given the policy name.
127     *
128     * @param policyName name of the policy
129     *
130     * @exception InvalidNameException if policy name is not valid
131     *
132     * @supported.api
133     *
134     */
135    public Policy(String policyName) throws InvalidNameException {
136        this(policyName, null);
137    }
138
139    /**
140     * Constructs a policy given the policy name and priority. 
141     *
142     * @param policyName name of the policy
143     * @param priority priority assigned to the policy
144     *
145     * @exception InvalidNameException if policy name is not valid
146     */
147    private Policy(String policyName, int priority) throws InvalidNameException 
148    {
149        validateName(policyName);
150        this.policyName = policyName;
151        // Set the policy priority
152        this.priority = priority;
153    }
154
155    /**
156     * Constructs a policy given  the policy name and description.
157     *
158     * @param policyName name of the policy
159     * @param description description for the policy
160     *
161     * @exception InvalidNameException if policy name is not valid
162     *
163     * @supported.api
164     *
165     */
166    public Policy(String policyName, String description)
167        throws InvalidNameException {
168
169        this(policyName, description, false, true);
170    }
171
172    /**
173     * Constructs a policy given  the policy name,description and a 
174     * referralPolicy flag.
175     *
176     * @param policyName name of the policy
177     * @param description description for the policy
178     * @param referralPolicy indicates whether the policy is a 
179     *        referral policy or a standard policy.
180     * A referral policy is used only to delegate policy definitions to 
181     * sub/peer organizations. A referral policy does not make use of any
182     * action values
183     *
184     *
185     * @exception InvalidNameException if policy name is not valid
186     *
187     * @supported.api
188     *
189     */
190    public Policy(String policyName, String description, 
191        boolean referralPolicy) throws InvalidNameException 
192    {
193        this(policyName, description, referralPolicy, true);
194    }
195
196    /**
197     * Constructs a policy given  the policy name , description,
198     * referralPolicy flag, and active flag
199     *
200     * @param policyName name of the policy
201     * @param description description for the policy
202     * @param referralPolicy indicates whether the policy is a 
203     *        referral policy or a standard policy.
204     * @param active indicates if the policy is active or not.
205     * A referral policy is used only to delegate policy definitions to 
206     * sub/peer organizations. A referral policy does not make use of any
207     * action values
208     *
209     * @exception InvalidNameException if policy name is not valid
210     *
211     * @supported.api
212     *
213     */
214    public Policy(String policyName, String description, 
215        boolean referralPolicy, boolean active) throws InvalidNameException 
216    {
217        validateName(policyName);
218        this.policyName = policyName;
219        if (description != null) {
220            this.description = description;
221        }
222        this.referralPolicy = referralPolicy;
223        this.active = active;
224    }
225
226    /**
227     * Constructs a policy given the Policy Node. 
228     * This is used by PolicyManager
229     * @param pm <code>PolicyManager</code> requesting the operation
230     *
231     * @param policyNode XML node in W3C DOM format representing 
232     * the policy object which needs to be created.
233     * @exception InvalidFormatException, InvalidNameException,
234     * NameNotFoundException, PolicyException
235     */
236    public Policy(PolicyManager pm, Node policyNode)
237        throws InvalidFormatException, InvalidNameException,
238        NameNotFoundException, PolicyException {
239        // Check if the node name is PolicyManager.POLICY_ROOT_NODE
240        if (!policyNode.getNodeName().equalsIgnoreCase(
241            PolicyManager.POLICY_ROOT_NODE)) {
242            if (PolicyManager.debug.warningEnabled()) {
243                PolicyManager.debug.warning(
244                    "invalid policy xml blob given to construct policy");
245            }
246            throw (new InvalidFormatException(ResBundleUtils.rbName,
247                "invalid_xml_policy_root_node", null, "", 
248                PolicyException.POLICY));
249        }
250
251        // Get the policy name
252        policyName = XMLUtils.getNodeAttributeValue(policyNode,
253            PolicyManager.NAME_ATTRIBUTE);
254        validateName(policyName);
255
256        // Get descrition, can be null
257        description = XMLUtils.getNodeAttributeValue(policyNode,
258                PolicyManager.DESCRIPTION_ATTRIBUTE);
259
260        getModificationInfo(policyNode);
261
262        // Get referralPolicy flag
263        String referralPolicy = XMLUtils.getNodeAttributeValue(policyNode,
264                Policy.REFERRAL_POLICY);
265        if ( (referralPolicy != null) &&
266                (referralPolicy.equalsIgnoreCase("true")) ) {
267            this.referralPolicy = true;
268        }
269
270        // Get active flag
271        String active = XMLUtils.getNodeAttributeValue(policyNode,
272                Policy.ACTIVE_FLAG);
273        if ( (active != null) &&
274                (active.equalsIgnoreCase("false")) ) {
275            this.active = false;
276        }
277
278        // Get priority
279        String pri = XMLUtils.getNodeAttributeValue(policyNode,
280            PolicyManager.PRIORITY_ATTRIBUTE);
281        if (pri != null) {
282            try {
283                priority = Integer.parseInt(pri);
284            } catch (NumberFormatException nfe) {
285                // write to debug and continue
286                PolicyManager.debug.error("Number format exception in " +
287                   "determining policy's priority: " + pri, nfe);
288            }
289        }
290
291
292        // Get the rule nodes and instantiate them
293        Set ruleNodes = XMLUtils.getChildNodes(policyNode,
294            PolicyManager.POLICY_RULE_NODE);
295        if ( ruleNodes != null ) {
296            Iterator items = ruleNodes.iterator();
297            while (items.hasNext()) {
298                Node ruleNode = (Node) items.next();
299                Rule rule = new Rule(ruleNode);
300                addRule(rule);
301            }
302        }
303
304        if (!this.referralPolicy) {
305            // Get the users collection and instantiate Subjects
306            Node subjectsNode = XMLUtils.getChildNode(policyNode,
307                    PolicyManager.POLICY_SUBJECTS_NODE);
308            if ( subjectsNode != null ) {
309                users = new Subjects(pm, subjectsNode);
310            }
311        
312            // Get the conditions collection and instantiate Conditions
313            Node conditionsNode = XMLUtils.getChildNode(policyNode,
314                    PolicyManager.POLICY_CONDITIONS_NODE);
315            if ( conditionsNode != null ) {
316                conditions = new Conditions(pm.getConditionTypeManager(), 
317                    conditionsNode);
318            }
319            // Get the respProviders collection and instantiate 
320            // ResponseProviders
321            Node respProvidersNode = XMLUtils.getChildNode(policyNode,
322                    PolicyManager.POLICY_RESP_PROVIDERS_NODE);
323            if ( respProvidersNode != null ) {
324                respProviders = new ResponseProviders(
325                    pm.getResponseProviderTypeManager(), 
326                    respProvidersNode);
327            }
328        } else {
329            // Get the referrals collection and instantiate Referrals
330            Node referralsNode = XMLUtils.getChildNode(policyNode,
331                    PolicyManager.POLICY_REFERRALS_NODE);
332            if ( referralsNode != null ) {
333                referrals = new Referrals(pm, referralsNode);
334            }
335        }
336    }
337
338    private void getModificationInfo(Node policyNode) {
339        String strCreationDate = XMLUtils.getNodeAttributeValue(policyNode,
340                PolicyManager.CREATION_DATE_ATTRIBUTE);
341        if ((strCreationDate != null) && (strCreationDate.length() > 0)) {
342            try {
343                creationDate = Long.parseLong(strCreationDate);
344            } catch (NumberFormatException e) {
345                //ignore
346            }
347        }
348        String strLastModifiediDate = XMLUtils.getNodeAttributeValue(
349            policyNode, PolicyManager.LAST_MODIFIED_DATE_ATTRIBUTE);
350        if ((strLastModifiediDate != null) &&
351            (strLastModifiediDate.length() > 0)
352        ) {
353            try {
354                lastModifiedDate = Long.parseLong(strLastModifiediDate);
355            } catch (NumberFormatException e) {
356                //ignore
357            }
358        }
359
360        createdBy = XMLUtils.getNodeAttributeValue(policyNode,
361            PolicyManager.CREATED_BY_ATTRIBUTE);
362        lastModifiedBy = XMLUtils.getNodeAttributeValue(policyNode,
363            PolicyManager.LAST_MODIFIED_BY_ATTRIBUTE);
364    }
365
366    /**
367     * Gets the name of the policy.
368     *
369     * @return name of the policy
370     *
371     * @supported.api
372     *
373     */
374    public String getName() {
375        return (policyName);
376    }
377
378    /**
379     * Sets the name of the policy.
380     *
381     * @param policyName name of the policy.
382     * @exception InvalidNameException if <code>policyName</code> is an invalid
383     * name.
384     *
385     * @supported.api
386     *
387     */
388    public void setName(String policyName) throws InvalidNameException {
389        validateName(policyName);
390        if (this.policyName.equals(policyName)) {
391            return;
392        }
393        if (origPolicyName == null) {
394            origPolicyName = this.policyName;
395        }
396        this.policyName = policyName;
397    }
398
399    /**
400     * Gets the original policy name. 
401     * This is used to track policies called via
402     * <code>PolicyManager::replacePolicy()</code>
403     * with the changed policy name.
404     *
405     * @return the policy name that was present when
406     *          the object was instantiated
407     */
408    protected String getOriginalName() {
409        return (origPolicyName);
410    }
411
412    /**
413     * Sets the organization name under which the policy is created
414     * This would be set only for policies that have been read from data store.
415     * Otherwise this would be <code>null</code>
416     *
417     * @param  organizationName name of the organization name in which the 
418     * policy is created.
419     */
420    void setOrganizationName(String organizationName) {
421        this.organizationName = organizationName;
422    }
423
424    /**
425     * Gets the organization name under which the policy is created
426     * This would be set only for policies that have been read from data store.
427     * Otherwise this would be <code>null</code>
428     *
429     * @return the organization name under which the policy is created
430     *
431     * @supported.api
432     *
433     */
434    public String getOrganizationName() {
435        return organizationName;
436    }
437
438    /**
439     * Resets the original policy name
440     */
441    protected void resetOriginalName() {
442        origPolicyName = null;
443    }
444
445    /**
446     * Gets the description for the policy.
447     * If the description for the policy has not been set
448     * the method will return an empty string; not <code>
449     * null</code>.
450     *
451     * @return description of the policy
452     *
453     * @supported.api
454     *
455     */
456    public String getDescription() {
457        return description;
458    }
459
460    /**
461     * Sets the description for the policy.
462     *
463     * @param description description for the policy
464     * @exception InvalidNameException if the description is invalid
465     *
466     * @supported.api
467     *
468     */
469    public void setDescription(String description)
470        throws InvalidNameException {
471        if (description != null) {
472            this.description = description;
473        }
474    }
475
476    /**
477     * Checks whether the policy is a referral policy.
478     * A referral policy is used only to delegate policy definitions to 
479     * sub/peer organizations. A referral policy does not make use of any
480     * action values
481     *
482     * @return <code>true</code> if this is a referral policy.
483     *         Otherwise returns <code>false</code>
484     *
485     * @supported.api
486     *
487     */
488    public boolean isReferralPolicy() {
489        return referralPolicy;
490    }
491
492    /**
493     * Checks whether the policy is active or inactive
494     * An inactive policy is not used to make policy evaluations.
495     *
496     * @return <code>true</code> if this is an active policy.
497     *         Otherwise returns <code>false</code>
498     *
499     * @supported.api
500     *
501     */
502    public boolean isActive() {
503        return active;
504    }
505
506    /**
507     * Set the active flag for policy.
508     * An inactive policy is not used to make policy evaluations.
509     * @param active <code>boolean</code> representing active or inactive.
510     *
511     * @supported.api
512     *
513     */
514    public void setActive(boolean active) {
515        this.active = active;
516    }
517
518    /**
519     * Gets the priority of the policy.
520     *
521     * @return priority of the policy
522     */
523    public int getPriority() {
524        return (priority);
525    }
526
527    /**
528     * Sets a priority of the policy.
529     *
530     * @param priority priority of the policy
531     */
532    public void setPriority(int priority) {
533        this.priority = priority;
534    }
535
536    /**
537     * Gets the set of rule names associated with the policy.
538     *
539     * @return <code>Set</code> of rule names
540     *
541     * @supported.api
542     *
543     */
544    public Set getRuleNames() {
545        return (new HashSet(rules.keySet()));
546    }
547
548    /**
549     * Gets the rule object identified by name.
550     *
551     * @param ruleName name of rule.
552     *
553     * @return <code>Rule</code> object.
554     *
555     * @exception NameNotFoundException if a <code>Rule</code> with the given 
556     *            name does not exist
557     * @supported.api
558     *
559     */
560    public Rule getRule(String ruleName) throws NameNotFoundException {
561        Rule rule = (Rule) rules.get(ruleName);
562        if (rule == null) {
563            throw (new NameNotFoundException(ResBundleUtils.rbName,
564                "rule_not_found", null, ruleName, PolicyException.RULE));
565        }
566        return (rule);
567    }
568
569    /**
570     * Adds a new policy rule.
571     *
572     * @param rule rule object to be added to the policy
573     * @exception NameAlreadyExistsException a rule with the given name
574     *            already exists
575     * @exception InvalidNameException if the rule name is invalid
576     *            same service name as the policy
577     * @supported.api
578     *
579     */
580    public void addRule(Rule rule) throws NameAlreadyExistsException ,
581            InvalidNameException {
582        // Since 5.0 does not support rule name, it can be null
583        if (rule.getName() == null) {
584            // Assign a name dynamically
585            rule.setName("rule" + ServiceTypeManager.generateRandomName());
586        }
587
588        // Check if the rule name or rule itself already exists
589        if (rules.containsKey(rule.getName())) {
590            throw (new NameAlreadyExistsException(ResBundleUtils.rbName,
591                "rule_name_already_present", null, rule.getName(),
592                PolicyException.RULE));
593        } else if (rules.containsValue(rule)) {
594            throw (new NameAlreadyExistsException(ResBundleUtils.rbName,
595                "rule_already_present", null, rule.getName(), 
596                PolicyException.RULE));
597        }
598
599        rules.put(rule.getName(), rule);
600    }
601
602    /**
603     * Replaces an existing rule with the same name by the
604     * current one. If a <code>Rule</code> with the same name does not exist,
605     * it will be added.
606     *
607     * @param rule <code>Rule</code> that will replace an existing rule
608     *        with the same name
609     *
610     * @exception InvalidNameException if <code>Rule</code> name is invalid
611     *
612     * @supported.api
613     *
614     */
615    public void replaceRule(Rule rule) throws InvalidNameException {
616        // Since 5.0 does not support rule name, it can be null
617        if (rule.getName() == null) {
618            // Assign a name dynamically
619            rule.setName("rule" + ServiceTypeManager.generateRandomName());
620        }
621
622        rules.put(rule.getName(), rule);
623    }
624
625    /**
626     * Removes the <code>Rule</code> with the given name. 
627     *
628     * @param ruleName name of the rule
629     *
630     * @return returns the <code>Rule</code> object being removed;
631     *         if not present returns <code>null</code>
632     *
633     * @supported.api
634     *
635     */
636    public Rule removeRule(String ruleName) {
637        return ((Rule) rules.remove(ruleName));
638    }
639
640    /**
641     * Returns a <code>Subjects</code> object that contains
642     * a set of <code>Subject</code> instances for which the
643     * policy is applied.
644     *
645     * @return Subjects object of the policy
646     */
647    Subjects getSubjects() {
648        return (users);
649    }
650
651    /**
652     * Get the <code>Set</code> of subject names associated with the policy.
653     *
654     * @return <code>Set</code> of String objects representing subject names
655     *
656     * @supported.api
657     *
658     */
659    public Set getSubjectNames() {
660        return users.getSubjectNames();
661    }
662
663    /**
664     * Gets the Subject object identified by name.
665     *
666     * @param subjectName name of subject.
667     *
668     * @return <code>Subject</code> object
669     *
670     * @exception NameNotFoundException if a Subject with the given name
671     * does not exist
672     *
673     * @supported.api
674     *
675     */
676    public Subject getSubject(String subjectName) throws NameNotFoundException {
677        return users.getSubject(subjectName);
678    }
679
680    /**
681     * Adds a new policy subject.
682     * The subject is added as a normal (non exclusive) subject.
683     * So, policy will apply to members of the subject.
684     * The policy will apply to a user if he is a member of 
685     * any normal (non exclusive) subject in the policy
686     * or not a member of any exclusive subject in the policy.
687     *
688     * @param name name of the Subject instance 
689     * @param subject Subject object to be added to the policy
690     *
691     * @exception NameAlreadyExistsException if a Subject with the given name
692     *          already exists
693     * @exception InvalidNameException if the subject name is invalid
694     *
695     * @supported.api
696     *
697     */
698    public void addSubject(String name, Subject subject) 
699            throws NameAlreadyExistsException, InvalidNameException {
700        users.addSubject(name, subject, false);
701    }
702
703    /**
704     * Adds a reference in the policy to a Subject defined at the realm.
705     *
706     *
707     * @param token SSOToken of the user adding the subject
708     * @param subjectName name of the Subject as defined at the realm
709     * @param realmName name of the realm in which the subject is defined
710     *
711     * @exception NameAlreadyExistsException if a Subject with the given name
712     *          already exists in the policy
713     * @exception InvalidNameException if the subject name is invalid 
714     *         or the subject is not found at the realm
715     * @exception SSOException if the SSO token is invalid
716     * @exception PolicyException if the subject could not be added 
717     *               for any other reason
718     *
719     * @supported.api
720     *
721     */
722    public void addRealmSubject(SSOToken token, String subjectName, 
723            String realmName, boolean exclusive) 
724            throws NameAlreadyExistsException, InvalidNameException,
725            PolicyException, SSOException {
726        PolicyManager pm = new PolicyManager(token, realmName);
727        SubjectTypeManager stm = pm.getSubjectTypeManager();
728        addRealmSubject(subjectName, stm, exclusive);
729    }
730
731    /**
732     * Adds a reference in the policy to a Subject defined at the realm.
733     *
734     *
735     * @param subjectName name of the Subject as defined at the realm
736     * @param stm <code>SubjectTypeManager<code> of the realm.
737     *       You have to pass the SubjectTypeManager of realm in which 
738     *       you would save the policy. Trying to save the policy at 
739     *       a different realm would throw PolicyException.
740     *
741     * @exception NameAlreadyExistsException if a Subject with the given name
742     *          already exists in the policy
743     * @exception InvalidNameException if the subject name is invalid 
744     *         or the subject is not found at the realm
745     * @exception SSOException if the SSO token is invalid
746     * @exception PolicyException if the subject could not be added 
747     *               for any other reason
748     *
749     * @supported.api
750     *
751     */
752    public void addRealmSubject(String subjectName, SubjectTypeManager stm,
753            boolean exclusive) 
754            throws NameAlreadyExistsException, InvalidNameException,
755            PolicyException, SSOException {
756        String realmName = stm.getPolicyManager().getOrganizationDN();
757        realmName = new DN(realmName).toRFCString().toLowerCase();
758        if ((subjectRealm != null) && !subjectRealm.equals(realmName)) {
759            String[] objs = {realmName, subjectRealm};
760            if (DEBUG.messageEnabled()) {
761                DEBUG.message("Policy.addRealmSubject():can not add"
762                        + " realm subject " + subjectName
763                        + " , from realm : " + realmName
764                        + " , policy already has subject from different realm:"
765                        + subjectRealm);
766            }
767
768            throw (new InvalidNameException(ResBundleUtils.rbName,
769                "policy_realms_do_not_match", objs, null, realmName, 
770                PolicyException.POLICY));
771        }
772        if (subjectRealm == null) {
773            subjectRealm = realmName;
774        }
775        /**
776         *  would result in NameNotFoundException if the subject does not exist
777         *  we would propogate the exception without catching
778         */
779        stm.getSubjectByName(subjectName); 
780
781        users.addSubject(subjectName, stm.getSharedSubject(subjectName),
782                exclusive);
783
784        if (DEBUG.messageEnabled()) {
785            DEBUG.message("Policy.addRealmSubject():added "
786                    + " realm subject " + subjectName
787                    + " , from realm : " + realmName);
788        }
789    }
790
791    /**
792     * Adds a new policy subject.
793     * The policy will apply to a user if he is a member of 
794     * any normal (non exclusive) subject  in the policy
795     * or not a member of any exclusive subject in the policy.
796     *
797     * @param name name of the Subject instance 
798     * @param subject Subject object to be added to the policy
799     *
800     * @param exclusive boolean flag indicating whether the subject 
801     *        is to be exclusive subject. If subject is exclusive, 
802     *        policy applies to users who are not members of the 
803     *        subject. Otherwise, policy applies to members of the subject.
804     *
805     * @exception NameAlreadyExistsException if a Subject with the given name
806     *          already exists
807     * @exception InvalidNameException if the subject name is invalid
808     *
809     * @supported.api
810     *
811     */
812    public void addSubject(String name, Subject subject, boolean exclusive) 
813            throws NameAlreadyExistsException, InvalidNameException {
814        users.addSubject(name, subject, exclusive);
815    }
816
817    /**
818     * Replaces an existing subject with the same name by the
819     * current one. If a subject with the same name does not exist,
820     * it will be added.
821     * The subject is replaced as a normal (non exclusive) subject.
822     * So, policy will apply to members of the subject.
823     * The policy will apply to a user if he is a member of 
824     * any normal (non exclusive) subject subject in the policy 
825     * or not a member of any exclusive subject subject in the policy.
826     *
827     * @param name name of the Subject instance 
828     * @param subject Subject that will replace an existing Subject
829     *        with the same name
830     *
831     * @exception NameNotFoundException if a Subject instance
832     *        with the given name is not present
833     *
834     * @supported.api
835     *
836     */
837    public void replaceSubject(String name, Subject subject) 
838            throws NameNotFoundException {
839        users.replaceSubject(name, subject, false);
840    }
841
842    /**
843     * Replaces an existing subject with the same name by the
844     * current one. If a subject with the same name does not exist,
845     * it will be added.
846     * The policy will apply to a user if he is a member of 
847     * any normal (non exclusive) subject in the policy 
848     * or not a member of any exclusive subject in the policy.
849     *
850     * @param name name of the Subject instance 
851     * @param subject Subject that will replace an existing Subject
852     * with the same name
853     *
854     * @param exclusive boolean flag indicating whether the subject 
855     *        is to be exclusive subject. If subject is exclusive, 
856     *        policy applies to users who are not members of the 
857     *        subject. Otherwise, policy applies to members of the subject.
858     *
859     * @exception NameNotFoundException if a Subject instance
860     *        with the given name is not present
861     *
862     * @supported.api
863     *
864     */
865    public void replaceSubject(String name, Subject subject, boolean exclusive) 
866            throws NameNotFoundException {
867        users.replaceSubject(name, subject, exclusive);
868    }
869
870    /**
871     * Removes the subject with the given name. 
872     *
873     * @param subjectName name of the Subject
874     *
875     * @return returns the Subject object being removed.
876     *         if not present returns <code>null</code>
877     *
878     * @supported.api
879     *
880     */
881    public Subject removeSubject(String subjectName) {
882        return users.removeSubject(subjectName);
883    }
884
885    /**
886     * Removes the <code>Subject</code> object identified by
887     * object's <code>equals</code> method. If a Subject instance
888     * does not exist, the method will return silently.
889     *
890     * @param subject Subject object that
891     *        will be removed from the user collection
892     *
893     * @supported.api
894     *
895     */
896    public void removeSubject(Subject subject) {
897        String subjectName = users.getSubjectName(subject);
898        if (subjectName != null) {
899            removeSubject(subjectName);
900        }
901    }
902
903
904    /**
905     * Checks if the subject is exclusive. 
906     * If subject is exclusive, policy applies to users who are not members of
907     * the subject. Otherwise, policy applies to members of the subject.
908     * The policy will apply to a user if he is a member of 
909     * any normal (non exclusive) subject in the policy
910     * or not a member of any exclusive subject in the policy.
911     *
912     * @param subjectName name of the subject 
913     * @return <code>true</code> if the subject is exclusive, <code>false</code>
914     *         otherwise.
915     * @exception NameNotFoundException if the subject with the given
916     *         <code>subjectName</code> does not exist in the policy.
917     *
918     * @supported.api
919     *
920     */
921    public boolean isSubjectExclusive(String subjectName) 
922            throws NameNotFoundException {
923        return users.isSubjectExclusive(subjectName);
924    }
925
926    /**
927     * Checks if the subjectName is a reference to a Subject 
928     * defined at the realm
929     *
930     * @param subjectName name of the subject 
931     * @return <code>true</code> if the subject is a reference to a 
932     *         Subject defined at the realm, <code>false</code>
933     *        otherwise.
934     * @exception NameNotFoundException if the subject with the given
935     *         <code>subjectName</code> does not exist in the policy.
936     *
937     * @supported.api
938     *
939     */
940    public boolean isRealmSubject(String subjectName) 
941            throws NameNotFoundException {
942        return users.isRealmSubject(subjectName);
943    }
944
945    /**
946     * Returns a <code>Referrals</code> object that contains
947     * a set of <code>Referral</code> instances for whom the
948     * policy is applied.
949     *
950     * @return Referrals object of the policy
951     */
952    Referrals getReferrals() {
953        return (referrals);
954    }
955
956    /**
957     * Get the <code>Set</code> of referral names associated with the policy.
958     *
959     * @return <code>Set</code> of referral names
960     *
961     * @supported.api
962     *
963     */
964    public Set getReferralNames() {
965        return referrals.getReferralNames();
966    }
967
968    /**
969     * Gets the Referral object identified by name.
970     *
971     * @param referralName name of referral.
972     *
973     * @return <code>Referral</code> object
974     *
975     * @exception NameNotFoundException if a Referral with the given name
976     * does not exist
977     *
978     * @supported.api
979     *
980     */
981    public Referral getReferral(String referralName) throws 
982        NameNotFoundException 
983    {
984        return referrals.getReferral(referralName);
985    }
986
987    /**
988     * Adds a new policy referral.
989     *
990     * @param name name of the <code>Referral</code> instance 
991     * @param referral <code>Referral</code> object to be added to the policy
992     *
993     * @exception NameAlreadyExistsException if a Referral with the given name
994     *            already exists
995     * @exception InvalidNameException if the referral name is invalid
996     *
997     * @supported.api
998     *
999     */
1000    public void addReferral(String name, Referral referral) 
1001            throws NameAlreadyExistsException, InvalidNameException {
1002        referrals.addReferral(name, referral);
1003    }
1004
1005    /**
1006     * Replaces an existing referral with the same name by the
1007     * current one. If a referral with the same name does not exist,
1008     * it will be added.
1009     *
1010     * @param name name of the <code>Referral</code> instance 
1011     * @param     referral <code>Referral</code> that will replace an existing 
1012     *            Referral with the same name
1013     *
1014     * @exception NameNotFoundException if a Referral instance
1015     *            with the given name is not present
1016     *
1017     * @supported.api
1018     *
1019     */
1020    public void replaceReferral(String name, Referral referral) 
1021            throws NameNotFoundException {
1022        referrals.replaceReferral(name, referral);
1023    }
1024
1025    /**
1026     * Removes the referral with the given name. 
1027     *
1028     * @param referralName name of the <code>Referral</code>
1029     *
1030     * @return returns the <code>Referral</code> object being removed;
1031     *         if not present returns <code>null</code>
1032     *
1033     * @supported.api
1034     *
1035     */
1036    public Referral removeReferral(String referralName) {
1037        return referrals.removeReferral(referralName);
1038    }
1039    /**
1040     * Removes the <code>Referral</code> object identified by
1041     * object's <code>equals</code> method. If a Referral instance
1042     * does not exist, the method will return silently.
1043     *
1044     * @param referral Referral object that will be removed 
1045     *
1046     * @supported.api
1047     *
1048     */
1049    public void removeReferral(Referral referral) {
1050        String referralName = referrals.getReferralName(referral);
1051        if (referralName != null) {
1052            removeReferral(referralName);
1053        }
1054    }
1055
1056
1057    /**
1058     * Returns a <code>Conditions</code> object that contains
1059     * a set of <code>Condition</code> objects that apply
1060     * to the policy
1061     *
1062     * @return <code>Conditions</code> object of the policy
1063     */
1064    Conditions getConditions() {
1065        return (conditions);
1066    }
1067
1068    /**
1069     * Get the set of condition names associated with the policy.
1070     *
1071     * @return <code>Set</code> of condition names
1072     *
1073     * @supported.api
1074     *
1075     */
1076    public Set getConditionNames() {
1077        return conditions.getConditionNames();
1078    }
1079
1080    /**
1081     * Gets the condition object identified by name.
1082     *
1083     * @param condition name of condition.
1084     *
1085     * @return <code>Condition</code> object.
1086     *
1087     * @exception NameNotFoundException if a Condition with the given name
1088     * does not exist.
1089     *
1090     * @supported.api
1091     *
1092     */
1093    public Condition getCondition(String condition) throws 
1094        NameNotFoundException 
1095    {
1096        return conditions.getCondition(condition);
1097    }
1098
1099    /**
1100     * Adds a new policy condition.
1101     *
1102     * @param name name of the Condition instance 
1103     * @param condition Condition object to be added to the policy
1104     *
1105     * @exception NameAlreadyExistsException if a Condition with the given name
1106     *            already exists
1107     * @exception InvalidNameException if the condition name is invalid
1108     *
1109     * @supported.api
1110     *
1111     */
1112    public void addCondition(String name, Condition condition) 
1113            throws NameAlreadyExistsException, InvalidNameException {
1114        conditions.addCondition(name, condition);
1115    }
1116
1117    /**
1118     * Replaces an existing condition with the same name by the
1119     * current one. If a condition with the same name does not exist,
1120     * it will be added.
1121     *
1122     * @param name name of the <code>Condition</code> instance 
1123     * @param     condition <code>Condition</code> that will replace an 
1124     *            existing Condition with the same name
1125     *
1126     * @exception NameNotFoundException if a Condition instance
1127     *            with the given name is not present
1128     *
1129     * @supported.api
1130     *
1131     */
1132    public void replaceCondition(String name, Condition condition) 
1133            throws NameNotFoundException {
1134        conditions.replaceCondition(name, condition);
1135    }
1136
1137    /**
1138     * Removes the condition with the given name. 
1139     *
1140     * @param condition name of the <code>Condition</code>
1141     *
1142     * @return returns the Condition object being removed;
1143     *         if not present returns <code>null</code>
1144     *
1145     * @supported.api
1146     *
1147     */
1148    public Condition removeCondition(String condition) {
1149        return conditions.removeCondition(condition);
1150    }
1151
1152    /**
1153     * Removes the <code>Condition</code> object identified by
1154     * object's <code>equals</code> method. If a condition instance
1155     * does not exist, the method will return silently.
1156     *
1157     * @param condition Condition object that will be removed 
1158     *
1159     * @supported.api
1160     *
1161     */
1162    public void removeCondition(Condition condition) {
1163        String conditionName = conditions.getConditionName(condition);
1164        if (conditionName != null) {
1165            removeCondition(conditionName);
1166        }
1167    }
1168
1169
1170    /**
1171     * Returns a <code>ResponseProviders</code> object that contains
1172     * a set of <code>ResponseProvider</code> objects that apply
1173     * to the policy
1174     *
1175     * @return <code>ResponseProviders</code> object found in the policy
1176     */
1177    ResponseProviders getResponseProviders() {
1178        return (respProviders);
1179    }
1180
1181    /**
1182     * Get a <code>Set</code> of <code>String</code> objects representing
1183     * the responseProvider names associated with the policy.
1184     *
1185     * @return <code>Set</code> of responseProvider names
1186     *
1187     *
1188     */
1189    public Set getResponseProviderNames() {
1190        return respProviders.getResponseProviderNames();
1191    }
1192
1193    /**
1194     * Gets the <code>ResponseProvider</code> object identified by name.
1195     *
1196     * @param respProvider name of <code>ResponseProvider</code>.
1197     *
1198     * @return <code>ResponseProvider</code> object.
1199     *
1200     * @exception NameNotFoundException if a ResponseProvider with the given 
1201     *            name does not exist.
1202     *
1203     *
1204     */
1205    public ResponseProvider getResponseProvider(String respProvider) 
1206        throws NameNotFoundException {
1207        return respProviders.getResponseProvider(respProvider);
1208    }
1209
1210    /**
1211     * Adds a new <code>ResponseProvider</code> to the policy.
1212     *
1213     * @param name name of the <code>ResponseProvider</code> instance 
1214     * @param respProvider <code>ResponseProvider</code> object to be added to 
1215     *            the policy
1216     *
1217     * @exception NameAlreadyExistsException if a ResponseProvider with the 
1218     *            given name already exists
1219     * @exception InvalidNameException if the <code>respProvider</code>
1220     *             name is invalid
1221     *
1222     *
1223     */
1224    public void addResponseProvider(String name, ResponseProvider respProvider) 
1225            throws NameAlreadyExistsException {
1226        respProviders.addResponseProvider(name, respProvider);
1227    }
1228
1229    /**
1230     * Replaces an existing <code>ResponseProvider</code> with the same name 
1231     * by the current one. If a respProvider with the same name does not exist,
1232     * it will be added.
1233     *
1234     * @param name name of the ResponseProvider instance 
1235     * @param respProvider ResponseProvider that will replace an existing 
1236     *            ResponseProvider with the same name
1237     *
1238     * @exception NameNotFoundException if a ResponseProvider instance
1239     *            with the given name is not present.
1240     *
1241     *
1242     */
1243    public void replaceResponseProvider(String name, 
1244        ResponseProvider respProvider) throws NameNotFoundException {
1245        respProviders.replaceResponseProvider(name, respProvider);
1246    }
1247
1248    /**
1249     * Removes the <code>ResponseProvider</code> with the given name. 
1250     *
1251     * @param respProvider name of the ResponseProvider
1252     *
1253     * @return returns the ResponseProvider object being removed;
1254     * if not present returns null.
1255     *
1256     *
1257     */
1258    public ResponseProvider removeResponseProvider(String respProvider) {
1259        return respProviders.removeResponseProvider(respProvider);
1260    }
1261
1262    /**
1263     * Removes the <code>ResponseProvider</code> object.
1264     * If a respProvider instance does not exist, the method will 
1265     * return silently.
1266     *
1267     * @param respProvider ResponseProvider object that
1268     * will be removed 
1269     *
1270     *
1271     */
1272    public void removeResponseProvider(ResponseProvider respProvider) {
1273        String respProviderName = respProviders.getResponseProviderName(
1274            respProvider);
1275        if (respProviderName != null) {
1276            removeResponseProvider(respProviderName);
1277        }
1278    }
1279
1280    /**
1281     * Stores the policy object in a persistent data store
1282     * under the organization, sub-organization or a container
1283     * object, specified as a parameter. The organization,
1284     * sub-organization, or the container can be either
1285     * a LDAP distinguished name (<code>dn</code>) or slash "/" separated
1286     * as per SMS. This method
1287     * uses the <code>SSOToken</code> provided to perform the store
1288     * operation, and hence if the single sign token has expired
1289     * <code>SSOException</code> will be thrown, and if the
1290     * user does not have the required privileges
1291     * <code>NoPermissionException</code> exception will be thrown.
1292     * <p>
1293     * If a policy with the same name exists for the organization
1294     * the method will throw <code>NameAlreadyExistsException</code>.
1295     * And if the organization name does not exist, the method
1296     * will throw <code>NameNotFoundException</code>.
1297     *
1298     * @param token  SSO token of the user managing policy
1299     * @param name name of the organization, sub-organization or
1300     * a container in which the policy will be stored.
1301     *
1302     * @exception SSOException invalid or expired single-sign-on token
1303     * @exception NoPermissionException user does not have sufficient
1304     * privileges to add policy
1305     *
1306     * @exception NameAlreadyExistsException a policy with the same
1307     * name already exists
1308     *
1309     * @exception NameNotFoundException the given organization name
1310     * does not exist
1311     *
1312     * @exception PolicyException for any other abnormal condition
1313     *
1314     * @supported.api
1315     *
1316     */
1317    public void store(SSOToken token, String name) throws SSOException,
1318        NoPermissionException, NameAlreadyExistsException,
1319        NameNotFoundException, PolicyException {
1320        PolicyManager pm = new PolicyManager(token, name);
1321        pm.addPolicy(this);
1322    }
1323
1324    /**
1325     * Checks if two policy objects are equal.
1326     * This method does not check the policy name and description 
1327     * for equality.
1328     *
1329     * @param obj object againt which the policy object
1330     * will be checked for equality
1331     *
1332     * @return <code>true</code> if policies are equal,
1333     * <code>false</code> otherwise.
1334     */
1335    public boolean equals(Object obj) {
1336        if (obj instanceof Policy) {
1337            Policy p = (Policy) obj;
1338            if (rules.equals(p.rules) && users.equals(p.users)
1339                        && referrals.equals(p.referrals)
1340                        && respProviders.equals(p.respProviders)
1341                        && conditions.equals(p.conditions) ) {
1342                return (true);
1343            }
1344        }
1345        return (false);
1346    }
1347
1348    /**
1349     * Creates and returns a copy of this object. The returned
1350     * <code>Policy</code> object will have the same policy
1351     * name, rules, subjects, referrals and conditions
1352     * such that <code>x.clone().equals(x)</code> will be
1353     * <code>true</code>. However <code>x.clone()</code>
1354     * will not be the same as <code>x</code>, i.e.,
1355     * <code>x.clone() != x</code>.
1356     *
1357     * @return a copy of this object
1358     */
1359    public Object clone() {
1360        Policy answer = null;
1361        try {
1362            answer = (Policy) super.clone();
1363        } catch (CloneNotSupportedException se) {
1364            answer = new Policy();
1365        }
1366        // Copy state variables
1367        answer.origPolicyName = origPolicyName;
1368        answer.policyName = policyName;
1369        answer.description = description;
1370        answer.active = active;
1371
1372        // Copy rules
1373        answer.rules = new HashMap();
1374        Iterator items = rules.keySet().iterator();
1375        while (items.hasNext()) {
1376            Object o = items.next();
1377            Rule rule = (Rule) rules.get(o);
1378            answer.rules.put(o, rule.clone());
1379        }
1380
1381        // Copy subjects
1382        answer.users = (Subjects) users.clone();
1383
1384        // Copy referrals
1385        answer.referrals = (Referrals) referrals.clone();
1386
1387        // Copy responseProviders
1388        answer.respProviders = (ResponseProviders) respProviders.clone();
1389
1390        // Copy conditions
1391        answer.conditions = (Conditions) conditions.clone();
1392
1393        return (answer);
1394    }
1395
1396    /**
1397     * Returns the serialized policy in XML 
1398     * @return serialized policy in XML
1399     *
1400     * @supported.api
1401     *
1402     */
1403    public String toXML() {
1404        return toXML(true);
1405    }
1406
1407    public String toXML(boolean withHeader) {
1408        StringBuilder answer = new StringBuilder(200);
1409
1410        if (withHeader) {
1411            answer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1412        }
1413
1414        answer.append("<Policy name=\"");
1415        answer.append(XMLUtils.escapeSpecialCharacters(policyName));
1416        if ((description != null) && (description.length() > 0)) {
1417            answer.append("\" description=\"");
1418            answer.append(XMLUtils.escapeSpecialCharacters(description));
1419        }
1420
1421        if ((createdBy != null) && (createdBy.length() > 0)) {
1422            answer.append("\" ")
1423                .append(PolicyManager.CREATED_BY_ATTRIBUTE)
1424                .append("=\"")
1425                .append(XMLUtils.escapeSpecialCharacters(createdBy));
1426        }
1427        if ((lastModifiedBy != null) && (lastModifiedBy.length() > 0)) {
1428            answer.append("\" ")
1429                .append(PolicyManager.LAST_MODIFIED_BY_ATTRIBUTE)
1430                .append("=\"")
1431                .append(XMLUtils.escapeSpecialCharacters(lastModifiedBy));
1432        }
1433        if (creationDate > 0) {
1434            answer.append("\" ")
1435                .append(PolicyManager.CREATION_DATE_ATTRIBUTE)
1436                .append("=\"")
1437                .append(XMLUtils.escapeSpecialCharacters(
1438                    Long.toString(creationDate)));
1439        }
1440        if (lastModifiedDate > 0) {
1441            answer.append("\" ")
1442                .append(PolicyManager.LAST_MODIFIED_DATE_ATTRIBUTE)
1443                .append("=\"")
1444                .append(XMLUtils.escapeSpecialCharacters(
1445                    Long.toString(lastModifiedDate)));
1446        }
1447
1448        answer.append("\" referralPolicy=\"").append(referralPolicy);
1449        answer.append("\" active=\"").append(active);
1450        answer.append("\" >");
1451        for (Iterator i = getRuleNames().iterator(); i.hasNext(); ) {
1452            String ruleName = (String)i.next();
1453            try {
1454                Rule rule = getRule(ruleName);
1455                answer.append(rule.toXML());
1456            } catch (Exception e) {
1457                // Ignore the exception
1458                DEBUG.error("Error in policy.toXML():" + e.getMessage());
1459            }
1460        }
1461
1462        if (!this.referralPolicy) {
1463            // Add the users
1464            if ( !(users.getSubjectNames().isEmpty()) ) {
1465                answer.append(users.toXML());
1466            }
1467
1468            // Add the conditions
1469            if ( !(conditions.getConditionNames().isEmpty()) ) {
1470                answer.append(conditions.toXML());
1471            }
1472            // Add the responseProviders
1473            if ( !(respProviders.getResponseProviderNames().isEmpty()) ) {
1474                answer.append(respProviders.toXML());
1475            }
1476        } else {
1477            // Add the referrals
1478            if ( !(referrals.getReferralNames().isEmpty()) ) {
1479                answer.append(referrals.toXML());
1480            }
1481        }
1482
1483        answer.append("\n").append("</Policy>");
1484        return (answer.toString());
1485    }
1486
1487    /**
1488     * Gets string representation of the policy object.
1489     *
1490     * @return XML string representation of the policy object
1491     *
1492     * @supported.api
1493     */
1494    public String toString() {
1495        return (toXML());
1496    }
1497
1498    /**
1499     * Checks for the char <code>c</code> in the String
1500     * @param name String in which the character needs to be checked for.
1501     * @param c <code>char</code> which needs to be checked.
1502     * @exception InvalidNameException if <code>c</code> does not occur
1503     *            anywhere in <code>name</code>.
1504     */
1505    static void checkForCharacter(String name, char c)
1506        throws InvalidNameException {
1507        if (name.indexOf(c) != -1) {
1508            Character objs[] =  { new Character(c) };
1509            throw (new InvalidNameException(ResBundleUtils.rbName,
1510                "invalid_char_in_name", objs, name,
1511                PolicyException.POLICY));
1512        }
1513    }
1514
1515    /** 
1516     * Gets policy decision 
1517     * @param token sso token identifying the user for who the policy has to 
1518     *        be evaluated.
1519     * @param resourceTypeName resourceType name
1520     * @param resourceName resourceName
1521     * @param actionNames a set of action names for which policy results
1522     *        are to be evaluated. Each element of the set should be a
1523     *        String
1524     * @param envParameters a  <code>Map</code> of environment parameters
1525     *        Each key of the <code>Map</code> is a String valued parameter name
1526     *        Each value of the map is a <code>Set</code> of String values
1527     * @return a <code>PolicyDecision</code>
1528     * @exception NameNotFoundException if the action name or resource name
1529     *         is not found
1530     * @exception SSOException if token is invalid
1531     * @exception PolicyException for any other exception condition
1532     */
1533   public PolicyDecision getPolicyDecision(SSOToken token, 
1534            String resourceTypeName,String resourceName, Set actionNames, 
1535            Map envParameters) throws SSOException, NameNotFoundException, 
1536            PolicyException 
1537   {
1538
1539        PolicyDecision policyDecision = new PolicyDecision();
1540
1541        ServiceTypeManager stm = ServiceTypeManager.getServiceTypeManager();
1542        ServiceType resourceType 
1543                = stm.getServiceType(resourceTypeName);
1544
1545        /**
1546         * get the evaluation order that is likely to be least expensive 
1547         * in terms of cpu.
1548         */
1549        if (token != null) {
1550            evaluationOrder = getEvaluationOrder(token);
1551        } else {
1552            evaluationOrder = SUBJECTS_RULES_CONDITIONS;
1553        }
1554        if (DEBUG.messageEnabled()) {
1555            DEBUG.message("Policy " + getName() 
1556                + " is Using Policy evaluation order :" + evaluationOrder);
1557        }
1558        if (isReferralPolicy() && !referrals.isEmpty()) {
1559
1560            //process referrals irrespective subjects and conditions
1561            PolicyDecision referralDecision 
1562                    = referrals.getPolicyDecision(token, 
1563                    resourceTypeName, resourceName, actionNames, 
1564                    envParameters);
1565            if (referralDecision != null) {
1566                PolicyEvaluator.mergePolicyDecisions(
1567                        resourceType, referralDecision, policyDecision);
1568            } 
1569            if (DEBUG.messageEnabled()) {
1570                String tokenPrincipal = 
1571                        (token != null) ? token.getPrincipal().getName()
1572                        : PolicyUtils.EMPTY_STRING;
1573                DEBUG.message(
1574                    new StringBuffer("at Policy.getPolicyDecision()")
1575                    .append(" after processing referrals only:")
1576                    .append(" principal, resource name, action names,")
1577                    .append(" policyName, referralResults = ")
1578                    .append(tokenPrincipal) .append(",  ")
1579                    .append(resourceName) .append(",  ")
1580                    .append(actionNames) .append(",  ")
1581                    .append(this.getName()).append(",  ")
1582                    .append(referralDecision).toString());
1583            }
1584        } else if (evaluationOrder == SUBJECTS_CONDITIONS_RULES) {
1585            if (DEBUG.messageEnabled()) {
1586                DEBUG.message("Using policy evaluation order:" 
1587                        + "SUBJECTS_CONDITIONS_RULES");
1588            }
1589            getPolicyDecisionSCR(token, resourceType, resourceName, 
1590                    actionNames, envParameters, policyDecision);
1591        } else if (evaluationOrder == CONDITIONS_SUBJECTS_RULES) {
1592            if (DEBUG.messageEnabled()) {
1593                DEBUG.message("Using policy evaluation order:" 
1594                        + "CONDITIONS_SUBJECTS_RULES");
1595            }
1596            getPolicyDecisionCSR(token, resourceType, resourceName, 
1597                    actionNames, envParameters, policyDecision);
1598        }  else if (evaluationOrder == RULES_SUBJECTS_CONDITIONS) {
1599            if (DEBUG.messageEnabled()) {
1600                DEBUG.message("Using policy evaluation order:" 
1601                        + "RULES_SUBJECTS_CONDITIONS");
1602            }
1603            getPolicyDecisionRSC(token, resourceType, resourceName, 
1604                    actionNames, envParameters, policyDecision);
1605        }  else if (evaluationOrder == RULES_CONDITIONS_SUBJECTS) {
1606            if (DEBUG.messageEnabled()) {
1607                DEBUG.message("Using policy evaluation order:" 
1608                        + "RULES_CONDITIONS_SUBJECTS");
1609            }
1610            getPolicyDecisionRCS(token, resourceType, resourceName, 
1611                    actionNames, envParameters, policyDecision);
1612        }  else if (evaluationOrder == SUBJECTS_RULES_CONDITIONS) {
1613            if (DEBUG.messageEnabled()) {
1614                DEBUG.message("Using policy evaluation order:" 
1615                        + "SUBJECTS_RULES_CONDITIONS");
1616            }
1617            getPolicyDecisionSRC(token, resourceType, resourceName, 
1618                    actionNames, envParameters, policyDecision);
1619        }  else if (evaluationOrder == CONDITIONS_RULES_SUBJECTS) {
1620            if (DEBUG.messageEnabled()) {
1621                DEBUG.message("Using policy evaluation order:" 
1622                        + "CONDITIONS_RULES_SUBJECTS");
1623            }
1624            getPolicyDecisionCRS(token, resourceType, resourceName, 
1625                    actionNames, envParameters, policyDecision);
1626        }  else { //default:RULES_CONDITIONS_SUBJECTS
1627            if (DEBUG.messageEnabled()) {
1628                DEBUG.message("Using default policy evaluation order:" 
1629                        + "RULES_CONDITIONS_SUBJECTS");
1630            }
1631            getPolicyDecisionRCS(token, resourceType, resourceName, 
1632                    actionNames, envParameters, policyDecision);
1633        }
1634
1635        if (DEBUG.messageEnabled()) {
1636            String tokenPrincipal = 
1637                    (token != null) ? token.getPrincipal().getName()
1638                    : PolicyUtils.EMPTY_STRING;
1639            DEBUG.message(
1640                new StringBuffer("at Policy.getPolicyDecision()")
1641                .append(" principal, resource name, action names,")
1642                .append(" policyName, policyDecision = ")
1643                .append(tokenPrincipal) .append(",  ")
1644                .append(resourceName) .append(",  ")
1645                .append(actionNames) .append(",  ")
1646                .append(this.getName()).append(",  ")
1647                .append(policyDecision).toString());
1648        }
1649        Map actionDecisionMap = policyDecision.getActionDecisions();
1650        if (actionDecisionMap != null && !actionDecisionMap.isEmpty())
1651        {
1652            Collection actionDecisions = null;
1653            if ((actionDecisions = actionDecisionMap.values()) != null &&
1654                !actionDecisions.isEmpty()) {
1655                Iterator it = actionDecisions.iterator();
1656                while (it.hasNext()) {
1657                    Set actionValues = ((ActionDecision)it.next()).getValues();
1658                    if (actionValues != null && !actionValues.isEmpty())
1659                        { // put the response Attrs in the PolicyDecision
1660                        Map responseAttributes = 
1661                           respProviders.getResponseProviderDecision(token, 
1662                                envParameters);
1663                            policyDecision.setResponseAttributes(
1664                                responseAttributes);
1665                        break;/**
1666                               * even if one action Value found, set the 
1667                               * resp attributes
1668                               */
1669                    }
1670                }
1671            }
1672        }
1673        return policyDecision;
1674    }
1675
1676    /** Gets matched rule results given resource type, resource name and 
1677     *  action names
1678     *  @param resourceType resource type(<code>ServiceType</code> of resource
1679     *  @param resourceName resource name for which to get action values
1680     *  @param actionNames action names for which to get values
1681     *  @return <code>Map</code> of action values keyed by action names
1682     *  @exception NameNotFoundException
1683     */
1684    private Map getMatchedRuleResults(ServiceType resourceType,
1685            String resourceName, Set actionNames) throws NameNotFoundException {
1686        String resourceTypeName = resourceType.getName();
1687        Map answer = null;
1688        StringBuilder cacheKeyBuffer = new StringBuilder(100);
1689        String cacheKey = cacheKeyBuffer.append(resourceTypeName)
1690                .append(resourceName).append(actionNames).toString();
1691        answer = (Map) matchRulesResultsCache.get(cacheKey);
1692        if ( answer == null ) {
1693            answer = new HashMap();
1694
1695            //Process rules
1696            Iterator ruleIterator = rules.values().iterator();
1697            while (ruleIterator.hasNext()) {
1698                Rule rule = (Rule) ruleIterator.next();
1699                Map actionResults = rule.getActionValues(resourceTypeName, 
1700                        resourceName, actionNames);
1701                PolicyUtils.appendMapToMap(actionResults, answer);
1702            }
1703
1704            Iterator actions = answer.keySet().iterator();
1705            while ( actions.hasNext() ) {
1706                String action = (String) actions.next();
1707                Set actionValues = (Set) answer.get(action);
1708                if ( actionValues.size() == 2 ) {
1709                    ActionSchema actionSchema = null;
1710                    AttributeSchema.Syntax actionSyntax = null;
1711                    try {
1712                        actionSchema = resourceType.getActionSchema(action);
1713                        actionSyntax = actionSchema.getSyntax();
1714                    } catch(InvalidNameException e) {
1715                        PolicyManager.debug.error(
1716                            "can not find action schmea for action = " 
1717                            + action, e );
1718                    }
1719                    if (AttributeSchema.Syntax.BOOLEAN.equals(
1720                            actionSyntax)) {
1721                        String trueValue = actionSchema.getTrueValue();
1722                        actionValues.remove(trueValue);
1723                    }
1724                }
1725            }
1726
1727            // Add to cache
1728            matchRulesResultsCache.put(cacheKey, answer);
1729        }
1730        return cloneRuleResults(answer);
1731    }
1732
1733    /**Gets resource names that are exact matches, sub resources or 
1734     * wild card matches of argument resource name.
1735     * To determine whether to include a
1736     * resource name of a resource,  we compare argument resource name and 
1737     * policy resource name, treating wild characters in the policy 
1738     * resource name as wild. If the comparsion resulted in EXACT_MATCH,
1739     * WILD_CARD_MATCH or SUB_RESOURCE_MATCH, the resource result would be
1740     * included.
1741     *
1742     * @param token sso token
1743     * @param serviceTypeName service type name
1744     * @param resourceName resource name
1745     * @param followReferrals indicates whether to follow the referrals to
1746     *                        compute the resources
1747     * @return resource names that match to be exact match, sub 
1748     *         resource match or wild card match of the argument
1749     *         resourceName
1750     *
1751     * @exception PolicyException
1752     * @exception SSOException
1753     *
1754     * @see ResourceMatch#EXACT_MATCH
1755     * @see ResourceMatch#SUB_RESOURCE_MATCH
1756     * @see ResourceMatch#WILDCARD_MATCH
1757     *
1758     */
1759    Set getResourceNames(SSOToken token, String serviceTypeName,
1760            String resourceName, boolean followReferrals) 
1761            throws PolicyException, SSOException {
1762        Set resourceNames = new HashSet();
1763        ServiceType st = ServiceTypeManager.getServiceTypeManager()
1764                .getServiceType( serviceTypeName);
1765        Iterator ruleIterator = rules.values().iterator();
1766        while (ruleIterator.hasNext()) {
1767            Rule rule = (Rule) ruleIterator.next();
1768            if (rule.getServiceType().getName().equals(serviceTypeName)) {
1769                String ruleResource = rule.getResourceName();
1770                ResourceMatch resourceMatch = st.compare(resourceName,
1771                        ruleResource, true); // interpret wild char
1772                if (resourceMatch.equals(ResourceMatch.SUB_RESOURCE_MATCH)
1773                      || resourceMatch.equals(ResourceMatch.EXACT_MATCH)
1774                      || resourceMatch.equals(ResourceMatch.WILDCARD_MATCH)) {
1775                    resourceNames.add(ruleResource);
1776                } 
1777            if ( DEBUG.messageEnabled() ) {
1778                StringBuilder sb = new StringBuilder(200);
1779                sb.append("at Policy.getResourceNames : ");
1780                sb.append(" for policyName, serviceType, resourceName, ");
1781                sb.append(" ruleResource, resourceMatch :");
1782                sb.append(getName()).append( ",").append( serviceTypeName);
1783                sb.append(",").append(resourceName).append(",");
1784                sb.append(ruleResource).append(",").append(resourceMatch);
1785                DEBUG.message(sb.toString());
1786            }
1787            }
1788        }
1789        if (!resourceNames.isEmpty() && followReferrals) {
1790            Set rResourceNames = referrals.getResourceNames(token, 
1791                    serviceTypeName, resourceName);
1792            resourceNames.addAll(rResourceNames);
1793        }
1794        if ( DEBUG.messageEnabled() ) {
1795            StringBuilder sb = new StringBuilder(200);
1796            sb.append("at Policy.getResourceNames : ");
1797            sb.append(" for policyName, serviceType, resourceName, ");
1798            sb.append(" followReferral, resourceNames :");
1799            sb.append(getName()).append( ",").append( serviceTypeName);
1800            sb.append(",").append(resourceName).append(",");
1801            sb.append(followReferrals).append(",").append(resourceNames);
1802            DEBUG.message(sb.toString());
1803        }
1804        return resourceNames;
1805    }
1806
1807    /** Gets the resource names of a given serviceType managed by this
1808     *  policy.
1809     *  @param  serviceTypeName name of service type for which to 
1810     *          find resource names
1811     *  @return a set of resource names of serviceTypeName managed
1812     *          by this policy
1813     *  @exception SSOException
1814     *  @exception NameNotFoundException
1815     */
1816    Set getResourceNames(String serviceTypeName) throws SSOException,
1817            NameNotFoundException {
1818        Set resourceNames = new HashSet();
1819        Iterator ruleIterator = rules.values().iterator();
1820        while (ruleIterator.hasNext()) {
1821            Rule rule = (Rule) ruleIterator.next();
1822            String rSvcTypeName = (rule.getServiceType() == null) ?
1823                rule.getServiceTypeName() : rule.getServiceType().getName();
1824            if (rSvcTypeName.equals(serviceTypeName)) {
1825                String ruleResource = rule.getResourceName();
1826                resourceNames.add(ruleResource);
1827            }
1828        }
1829        return resourceNames;
1830    }
1831
1832//    public String getServiceTypeName() {
1833        /* com.iplanet.am.admin.cli uses this method. 
1834         * Need to clean up cli not to use this 
1835         * method. Without this method build breaks - 03/05/02 */
1836 //       return null;
1837  //  }
1838
1839    /**
1840     *  Gets organizations referred to in this policy by OrgReferral(s)
1841     *  defined in this policy.
1842     *  
1843     *  @return names of organization (DNs) of organizations referred
1844     *          to in this policy via <code>OrgReferral</code>(s) defined in 
1845     *          this policy.
1846     *          Please note that <code>PeerOrgReferral</code> and 
1847     *          <code>SubOrgReferral</code> extend <code>OrgReferral</code> 
1848     *          and hence qualify as OrgReferral.
1849     *  @exception PolicyException
1850     */
1851    Set getReferredToOrganizations() throws PolicyException {
1852        Set referredToOrgs = new HashSet();
1853        Iterator referralNames = referrals.getReferralNames().iterator();
1854        while ( referralNames.hasNext() ) {
1855            String referralName = (String) referralNames.next();
1856            Referral referral = (Referral) referrals.getReferral(referralName);
1857            if ( referral instanceof OrgReferral ) {
1858                Set values = referral.getValues();
1859                if ( (values != null) && (!values.isEmpty()) ) {
1860                    String orgName = (String) values.iterator().next();
1861                    referredToOrgs.add(orgName.toLowerCase());
1862                }
1863            }
1864        }
1865        return referredToOrgs;
1866    }
1867
1868    /** Sets time to live for Subjects result. 
1869     *  @param ttl time to live for Subjects result
1870     */
1871    void setSubjectsResultTtl(long ttl) {
1872        users.setResultTtl(ttl);
1873    }
1874
1875
1876    /**
1877     * validates the String <code>name</code>.
1878     * @param name String to be validated.
1879     * @exception throws InvalidNameException is name is null or
1880     *            does contain invalid character "/".
1881     */
1882
1883    private void validateName(String name) throws InvalidNameException {
1884        if ( (name == null) || (name.length() == 0) )  {
1885            DEBUG.message("Invalid policy name:" + name);
1886            throw (new InvalidNameException(ResBundleUtils.rbName,
1887                "null_name", null, "", PolicyException.POLICY));
1888        } else if ( name.indexOf('/') != -1 ) {
1889            DEBUG.message("Invalid policy name:" + name );
1890            DEBUG.message("Index Of /:" + name.indexOf('/'));
1891            throw (new InvalidNameException(ResBundleUtils.rbName,
1892                "illegal_character_/_in_name", null, "", 
1893                PolicyException.POLICY));
1894        }
1895    }
1896
1897    /** Gets policy decision  computing Subjects, Conditions and Rules
1898     *  in this order. Referrals in the policy are ignored.
1899     *
1900     * @param token sso token identifying the user for who the policy has to 
1901     *        be evaluated.
1902     * @param resourceType service type
1903     * @param resourceName resource name
1904     * @param actionNames a set of action names for which policy results
1905     *        are to be evaluated. Each element of the set should be a
1906     *        String
1907     * @param envParameters a map of environment parameters
1908     *        Each key of the map is a String valued parameter name
1909     *        Each value of the map is a set of String values
1910     * @param policyDecision a collecting argument. Computed policy decisions
1911     *        in this method are merged to this policy decision
1912     * @return computed and merged policy decision
1913     * @exception NameNotFoundException if the action name or resource name
1914     *         is not found
1915     * @exception SSOException if token is invalid
1916     * @exception PolicyException for any other exception condition
1917     */
1918    private PolicyDecision getPolicyDecisionSCR(SSOToken token, 
1919            ServiceType resourceType,
1920            String resourceName, Set actionNames, Map envParameters,
1921            PolicyDecision policyDecision) 
1922            throws SSOException, NameNotFoundException, PolicyException {
1923        boolean resourceMatched = false;
1924        ConditionDecision conditionDecision = null;
1925        boolean allowedByConditions = false;
1926        boolean allowedBySubjects = false;
1927        Map advicesFromConditions = null;
1928        long conditionsTtl = Long.MIN_VALUE;
1929        long timeToLive = Long.MIN_VALUE;
1930        Map actionResults = null;
1931
1932        if (token != null) {
1933            allowedBySubjects = users.isMember(token);
1934            timeToLive = users.getResultTtl(token);
1935        } else {
1936            allowedBySubjects = true;
1937            timeToLive = Long.MAX_VALUE;
1938        }
1939
1940        if (allowedBySubjects) { //subjects+ 
1941            conditionDecision = conditions.getConditionDecision(
1942                    token, envParameters);
1943            allowedByConditions = conditionDecision.isAllowed();
1944            advicesFromConditions = conditionDecision.getAdvices();
1945            conditionsTtl = conditionDecision.getTimeToLive();
1946            if ( conditionsTtl < timeToLive ) {
1947                timeToLive = conditionsTtl;
1948            }
1949            if (allowedByConditions) { //subjects+, conditions+
1950                actionResults = getMatchedRuleResults(resourceType,
1951                        resourceName, actionNames);
1952                resourceMatched = !actionResults.isEmpty();
1953                if (resourceMatched) { //subjects+,conditions+,resourceMatch+
1954                    Iterator resultActionNames 
1955                            = actionResults.keySet().iterator();
1956                    while ( resultActionNames.hasNext() ) {
1957                        String resultActionName 
1958                                = (String) resultActionNames.next();
1959                        Set resultActionValues 
1960                            = (Set)actionResults.get(resultActionName);
1961
1962                        /*  ActionDecision to include values, no advices
1963                         */
1964                        ActionDecision actionDecision 
1965                                = new ActionDecision(resultActionName,
1966                                resultActionValues, 
1967                                advicesFromConditions, timeToLive);
1968                        policyDecision.addActionDecision(
1969                                actionDecision, resourceType);
1970                    }
1971                } else { // subjects+,conditions+,resourceMatch-
1972                    policyDecision.setTimeToLive(Long.MAX_VALUE);
1973                }
1974            } else { //subjects+,conditions-
1975                //ActionDecision to include advices only
1976
1977                if (!advicesFromConditions.isEmpty()) {
1978                    actionResults = getMatchedRuleResults(resourceType,
1979                            resourceName, actionNames);
1980                    Iterator resultActionNames 
1981                            = actionResults.keySet().iterator();
1982                    while ( resultActionNames.hasNext() ) {
1983                        String resultActionName 
1984                                = (String) resultActionNames.next();
1985
1986                        /*  ActionDecision to include advices, no values
1987                         */
1988                        ActionDecision actionDecision 
1989                                = new ActionDecision(resultActionName,
1990                                Collections.EMPTY_SET, 
1991                                advicesFromConditions, timeToLive);
1992                        policyDecision.addActionDecision(
1993                                actionDecision, resourceType);
1994                    }
1995                } else {
1996                    policyDecision.setTimeToLive(timeToLive);
1997                }
1998            }
1999        } else { //subjects-
2000            policyDecision.setTimeToLive(timeToLive);
2001        }
2002        return policyDecision;
2003    }
2004
2005    /** Gets policy decision  computing Subjects, Rules and Conditions
2006     *  in this order. Referrals in the policy are ignored.
2007     *
2008     * @param token sso token identifying the user for who the policy has to 
2009     *        be evaluated.
2010     * @param resourceType service type
2011     * @param resourceName resourceName
2012     * @param actionNames a set of action names for which policy results
2013     *        are to be evaluated. Each element of the set should be a
2014     *        String
2015     * @param envParameters a map of environment parameters
2016     *        Each key of the map is a String valued parameter name
2017     *        Each value of the map is a set of String values
2018     * @param policyDecision a collecting argument. Computed policy decisions
2019     *        in this method are merged to this policy decision
2020     * @return computed and merged policy decision
2021     * @exception NameNotFoundException if the action name or resource name
2022     *         is not found
2023     * @exception SSOException if token is invalid
2024     * @exception PolicyException for any other exception condition
2025     */
2026    private PolicyDecision getPolicyDecisionSRC(SSOToken token, 
2027            ServiceType resourceType,
2028            String resourceName, Set actionNames, Map envParameters,
2029            PolicyDecision policyDecision) 
2030            throws SSOException, NameNotFoundException, PolicyException {
2031        boolean resourceMatched = false;
2032        ConditionDecision conditionDecision = null;
2033        boolean allowedByConditions = false;
2034        boolean allowedBySubjects = false;
2035        Map advicesFromConditions = null;
2036        long conditionsTtl = Long.MIN_VALUE;
2037        long timeToLive = Long.MIN_VALUE;
2038        Map actionResults = null;
2039
2040        if (token != null) {
2041            allowedBySubjects = users.isMember(token);
2042            timeToLive = users.getResultTtl(token);
2043        } else {
2044            allowedBySubjects = true;
2045            timeToLive = Long.MAX_VALUE;
2046        }
2047        if (allowedBySubjects) { //subjects+
2048            actionResults = getMatchedRuleResults(resourceType,
2049                    resourceName, actionNames);
2050            resourceMatched = !actionResults.isEmpty();
2051            if (resourceMatched) { //subjects+, resourceMatch+
2052                conditionDecision = conditions.getConditionDecision(
2053                        token, envParameters);
2054                allowedByConditions = conditionDecision.isAllowed();
2055                advicesFromConditions = conditionDecision.getAdvices();
2056                conditionsTtl = conditionDecision.getTimeToLive();
2057                if ( conditionsTtl < timeToLive ) {
2058                    timeToLive = conditionsTtl;
2059                }
2060                if (allowedByConditions) { 
2061                    //subjects+, resourceMatch+,conditions+
2062                    Iterator resultActionNames 
2063                            = actionResults.keySet().iterator();
2064                    while ( resultActionNames.hasNext() ) {
2065                        String resultActionName 
2066                                = (String) resultActionNames.next();
2067                        Set resultActionValues 
2068                            = (Set)actionResults.get(resultActionName);
2069
2070                        /*  ActionDecision to include values, no advices
2071                         */
2072                        ActionDecision actionDecision 
2073                                = new ActionDecision(resultActionName,
2074                                resultActionValues, 
2075                                advicesFromConditions, timeToLive);
2076                        policyDecision.addActionDecision(
2077                                actionDecision, resourceType);
2078                    }
2079                } else { //subjects+, resourceMatch+,conditions-
2080                    if (!advicesFromConditions.isEmpty()) {
2081                        Iterator resultActionNames 
2082                                = actionResults.keySet().iterator();
2083                        while ( resultActionNames.hasNext() ) {
2084                            String resultActionName 
2085                                    = (String) resultActionNames.next();
2086
2087                            /*  ActionDecision to include advices, no values
2088                             */
2089                            ActionDecision actionDecision 
2090                                    = new ActionDecision(resultActionName,
2091                                    Collections.EMPTY_SET, 
2092                                    advicesFromConditions, timeToLive);
2093                            policyDecision.addActionDecision(
2094                                    actionDecision, resourceType);
2095                        }
2096                    } else {
2097                        policyDecision.setTimeToLive(timeToLive);
2098                    }
2099                }
2100            } else { //subjects+,resourceMatch-
2101                policyDecision.setTimeToLive(Long.MAX_VALUE);
2102            }
2103        } else { //subjects-
2104            policyDecision.setTimeToLive(timeToLive);
2105        }
2106        return policyDecision;
2107    }
2108
2109    /** Gets policy decision  computing Conditions, Subject and Rules
2110     *  in this order. Referrals in the policy are ignored.
2111     *
2112     * @param token sso token identifying the user for who the policy has to 
2113     *        be evaluated.
2114     * @param resourceType service type
2115     * @param resourceName resourceName
2116     * @param actionNames a set of action names for which policy results
2117     *        are to be evaluated. Each element of the set should be a
2118     *        String
2119     * @param envParameters a map of environment parameters
2120     *        Each key of the map is a String valued parameter name
2121     *        Each value of the map is a set of String values
2122     * @param policyDecision a collecting arugment. Computed policy decisions
2123     *        in this method are merged to this policy decision
2124     * @return computed and merged policy decision
2125     * @exception NameNotFoundException if the action name or resource name
2126     *         is not found
2127     * @exception SSOException if token is invalid
2128     * @exception PolicyException for any other exception condition
2129     */
2130    private PolicyDecision getPolicyDecisionCSR(SSOToken token, 
2131            ServiceType resourceType,
2132            String resourceName, Set actionNames, Map envParameters,
2133            PolicyDecision policyDecision) 
2134            throws SSOException, NameNotFoundException, PolicyException {
2135        boolean resourceMatched = false;
2136        ConditionDecision conditionDecision = null;
2137        boolean allowedByConditions = false;
2138        boolean allowedBySubjects = false;
2139        Map advicesFromConditions = null;
2140        long timeToLive = Long.MIN_VALUE;
2141        long subjectsTtl = Long.MIN_VALUE;
2142        Map actionResults = null;
2143
2144        conditionDecision = conditions.getConditionDecision(
2145                token, envParameters);
2146        allowedByConditions = conditionDecision.isAllowed();
2147        advicesFromConditions = conditionDecision.getAdvices();
2148        timeToLive = conditionDecision.getTimeToLive();
2149        if (allowedByConditions) { //conditions+
2150            allowedBySubjects = users.isMember(token);
2151            subjectsTtl = users.getResultTtl(token);
2152            if (subjectsTtl < timeToLive) {
2153                timeToLive = subjectsTtl;
2154            }
2155            if (allowedBySubjects) { //conditions+, subjects+
2156                actionResults = getMatchedRuleResults(resourceType,
2157                        resourceName, actionNames);
2158                resourceMatched = !actionResults.isEmpty();
2159                if (resourceMatched) { 
2160                    //conditions+, subjects+, resourceMatched+
2161                    Iterator resultActionNames 
2162                            = actionResults.keySet().iterator();
2163                    while ( resultActionNames.hasNext() ) {
2164                        String resultActionName 
2165                                = (String) resultActionNames.next();
2166                        Set resultActionValues 
2167                            = (Set)actionResults.get(resultActionName);
2168
2169                        /*  ActionDecision to include values, no advices
2170                         */
2171                        ActionDecision actionDecision 
2172                                = new ActionDecision(resultActionName,
2173                                resultActionValues, 
2174                                advicesFromConditions, timeToLive);
2175                        policyDecision.addActionDecision(
2176                                actionDecision, resourceType);
2177                    }
2178                } else { //conditions+, subjects+, resourceMatched-
2179                    policyDecision.setTimeToLive(Long.MAX_VALUE);
2180                }
2181            } else { //conditions+,subjects-
2182                policyDecision.setTimeToLive(timeToLive);
2183            }
2184        } else { //conditions-
2185            boolean reportAdvices = false;
2186            if (!advicesFromConditions.isEmpty()) {
2187                reportAdvices = users.isMember(token);
2188                subjectsTtl = users.getResultTtl(token);
2189                if (subjectsTtl < timeToLive) {
2190                    timeToLive = subjectsTtl;
2191                }
2192            }
2193            if (reportAdvices) {
2194                actionResults = getMatchedRuleResults(resourceType,
2195                        resourceName, actionNames);
2196                Iterator resultActionNames 
2197                        = actionResults.keySet().iterator();
2198                while ( resultActionNames.hasNext() ) {
2199                    String resultActionName 
2200                            = (String) resultActionNames.next();
2201
2202                    /*  ActionDecision to include advices, no values
2203                     */
2204                    ActionDecision actionDecision 
2205                            = new ActionDecision(resultActionName,
2206                            Collections.EMPTY_SET, 
2207                            advicesFromConditions, timeToLive);
2208                    policyDecision.addActionDecision(
2209                            actionDecision, resourceType);
2210                }
2211            } else { //no advices to report
2212                policyDecision.setTimeToLive(timeToLive);
2213            }
2214        }
2215        return policyDecision;
2216    }
2217
2218    /** Gets policy decision  computing Conditions, Rules and Subjects
2219     *  in this order. Referrals in the policy are ignored.
2220     *
2221     * @param token sso token identifying the user for who the policy has to 
2222     *        be evaluated.
2223     * @param resourceType service type
2224     * @param resourceName resourceName
2225     * @param actionNames a set of action names for which policy results
2226     *        are to be evaluated. Each element of the set should be a
2227     *        String
2228     * @param envParameters a map of environment parameters
2229     *        Each key of the map is a String valued parameter name
2230     *        Each value of the map is a set of String values
2231     * @param policyDecision a collecting arugment. Computed policy decisions
2232     *        in this method are merged to this policy decision
2233     * @return computed and merged policy decision
2234     * @exception NameNotFoundException if the action name or resource name
2235     *         is not found
2236     * @exception SSOException if token is invalid
2237     * @exception PolicyException for any other exception condition
2238     */
2239    private PolicyDecision getPolicyDecisionCRS(SSOToken token, 
2240            ServiceType resourceType,
2241            String resourceName, Set actionNames, Map envParameters,
2242            PolicyDecision policyDecision) 
2243            throws SSOException, NameNotFoundException, PolicyException {
2244        boolean resourceMatched = false;
2245        ConditionDecision conditionDecision = null;
2246        boolean allowedByConditions = false;
2247        boolean allowedBySubjects = false;
2248        Map advicesFromConditions = null;
2249        long subjectsTtl = Long.MIN_VALUE;
2250        long timeToLive = Long.MIN_VALUE;
2251        Map actionResults = null;
2252
2253        conditionDecision = conditions.getConditionDecision(
2254                token, envParameters);
2255        allowedByConditions = conditionDecision.isAllowed();
2256        advicesFromConditions = conditionDecision.getAdvices();
2257        timeToLive = conditionDecision.getTimeToLive();
2258        actionResults = getMatchedRuleResults(resourceType,
2259                resourceName, actionNames);
2260        if (allowedByConditions) { //conditions+
2261            resourceMatched = !actionResults.isEmpty();
2262            if (resourceMatched) { ///conditions+, resourceMatched+
2263                allowedBySubjects = users.isMember(token);
2264                subjectsTtl = users.getResultTtl(token);
2265                if (subjectsTtl < timeToLive) {
2266                    timeToLive = subjectsTtl;
2267                }
2268                if (allowedBySubjects) {
2269
2270                    //conditions+, resourceMatched+, subjects+
2271                    Iterator resultActionNames 
2272                            = actionResults.keySet().iterator();
2273                    while ( resultActionNames.hasNext() ) {
2274                        String resultActionName 
2275                                = (String) resultActionNames.next();
2276                        Set resultActionValues 
2277                            = (Set)actionResults.get(resultActionName);
2278
2279                        /*  ActionDecision to include values, no advices
2280                         */
2281                        ActionDecision actionDecision 
2282                                = new ActionDecision(resultActionName,
2283                                resultActionValues, 
2284                                advicesFromConditions, timeToLive);
2285                        policyDecision.addActionDecision(
2286                                actionDecision, resourceType);
2287                    }
2288                } else { //conditions+, resourceMatched+, subjects-
2289                    policyDecision.setTimeToLive(timeToLive);
2290                }
2291            } else { //conditions+, resourceMatched-
2292                policyDecision.setTimeToLive(Long.MAX_VALUE);
2293            }
2294        } else { //conditions-
2295            boolean reportAdvices = false;
2296            if (!advicesFromConditions.isEmpty()) {
2297                reportAdvices = users.isMember(token);
2298                subjectsTtl = users.getResultTtl(token);
2299                if (subjectsTtl < timeToLive) {
2300                    timeToLive = subjectsTtl;
2301                }
2302            }
2303            if (reportAdvices) {
2304                actionResults = getMatchedRuleResults(resourceType,
2305                        resourceName, actionNames);
2306                Iterator resultActionNames 
2307                        = actionResults.keySet().iterator();
2308                while ( resultActionNames.hasNext() ) {
2309                    String resultActionName 
2310                            = (String) resultActionNames.next();
2311
2312                    /*  ActionDecision to include advices, no values
2313                     */
2314                    ActionDecision actionDecision 
2315                            = new ActionDecision(resultActionName,
2316                            Collections.EMPTY_SET, 
2317                            advicesFromConditions, timeToLive);
2318                    policyDecision.addActionDecision(
2319                            actionDecision, resourceType);
2320                }
2321            } else { //no advices to report
2322                policyDecision.setTimeToLive(timeToLive);
2323            }
2324        }
2325        return policyDecision;
2326    }
2327
2328    /** Gets policy decision  computing Rules, Subjects and Conditions
2329     *  in this order. Referrals in the policy are ignored.
2330     *
2331     * @param token sso token identifying the user for who the policy has to 
2332     *        be evaluated.
2333     * @param resourceType service type
2334     * @param resourceName resourceName
2335     * @param actionNames a set of action names for which policy results
2336     *        are to be evaluated. Each element of the set should be a
2337     *        String
2338     * @param envParameters a map of environment parameters
2339     *        Each key of the map is a String valued parameter name
2340     *        Each value of the map is a set of String values
2341     * @param policyDecision a collecting arugment. Computed policy decisions
2342     *        in this method are merged to this policy decision
2343     * @return computed and merged policy decision
2344     * @exception NameNotFoundException if the action name or resource name
2345     *         is not found
2346     * @exception SSOException if token is invalid
2347     * @exception PolicyException for any other exception condition
2348     */
2349    private PolicyDecision getPolicyDecisionRSC(SSOToken token, 
2350            ServiceType resourceType,
2351            String resourceName, Set actionNames, Map envParameters,
2352            PolicyDecision policyDecision) 
2353            throws SSOException, NameNotFoundException, PolicyException {
2354        boolean resourceMatched = false;
2355        ConditionDecision conditionDecision = null;
2356        boolean allowedByConditions = false;
2357        boolean allowedBySubjects = false;
2358        Map advicesFromConditions = null;
2359        long conditionsTtl = Long.MIN_VALUE;
2360        long timeToLive = Long.MIN_VALUE;
2361
2362        Map actionResults = getMatchedRuleResults(resourceType,
2363                resourceName, actionNames);
2364        resourceMatched = !actionResults.isEmpty();
2365        if (resourceMatched) { //resourceMatched+
2366            allowedBySubjects = users.isMember(token);
2367            timeToLive = users.getResultTtl(token);
2368            if (allowedBySubjects) { //resourceMatched+, subjects+
2369                conditionDecision = conditions.getConditionDecision(
2370                        token, envParameters);
2371                allowedByConditions = conditionDecision.isAllowed();
2372                advicesFromConditions = conditionDecision.getAdvices();
2373                conditionsTtl = conditionDecision.getTimeToLive();
2374                if (conditionsTtl < timeToLive) {
2375                    timeToLive = conditionsTtl;
2376                }
2377                if (allowedByConditions) {
2378
2379                    //resourceMatched+, subjects+, conditions+
2380                    Iterator resultActionNames 
2381                            = actionResults.keySet().iterator();
2382                    while ( resultActionNames.hasNext() ) {
2383                        String resultActionName 
2384                                = (String) resultActionNames.next();
2385                        Set resultActionValues 
2386                            = (Set)actionResults.get(resultActionName);
2387
2388                        /*  ActionDecision to include values, no advices
2389                         */
2390                        ActionDecision actionDecision 
2391                                = new ActionDecision(resultActionName,
2392                                resultActionValues, 
2393                                advicesFromConditions, timeToLive);
2394                        policyDecision.addActionDecision(
2395                                actionDecision, resourceType);
2396                    }
2397                } else { //resourceMatched+, subjects+, conditions-
2398                    Iterator resultActionNames  
2399                            = actionResults.keySet().iterator();
2400                    if (!advicesFromConditions.isEmpty()) {
2401                        while ( resultActionNames.hasNext() ) {
2402                            String resultActionName 
2403                                    = (String) resultActionNames.next();
2404
2405                            /*  ActionDecision to include advices, no values
2406                             */
2407                            ActionDecision actionDecision 
2408                                    = new ActionDecision(resultActionName,
2409                                    Collections.EMPTY_SET, 
2410                                    advicesFromConditions, timeToLive);
2411                            policyDecision.addActionDecision(
2412                                    actionDecision, resourceType);
2413                        }
2414                    } else {
2415                        policyDecision.setTimeToLive(timeToLive);
2416                    }
2417                }
2418            } else { //resourceMatched+, subjects-
2419                policyDecision.setTimeToLive(timeToLive);
2420            }
2421        } else { //resourceMached-
2422            policyDecision.setTimeToLive(Long.MAX_VALUE);
2423        }
2424        return policyDecision;
2425    }
2426
2427    /** Gets policy decision  computing Rules, Conditions and Subjects
2428     *  in this order. Referrals in the policy are ignored.
2429     *
2430     * @param token sso token identifying the user for who the policy has to 
2431     *        be evaluated.
2432     * @param resourceType service type
2433     * @param resourceName resourceName
2434     * @param actionNames a set of action names for which policy results
2435     *        are to be evaluated. Each element of the set should be a
2436     *        String
2437     * @param envParameters a map of environment parameters
2438     *        Each key of the map is a String valued parameter name
2439     *        Each value of the map is a set of String values
2440     * @param policyDecision a collecting argument. Computed policy decisions
2441     *        in this method are merged to this policy decision
2442     * @return computed and merged policy decision
2443     * @exception NameNotFoundException if the action name or resource name
2444     *         is not found
2445     * @exception SSOException if token is invalid
2446     * @exception PolicyException for any other exception condition
2447     */
2448    private PolicyDecision getPolicyDecisionRCS(SSOToken token, 
2449            ServiceType resourceType,
2450            String resourceName, Set actionNames, Map envParameters,
2451            PolicyDecision policyDecision) 
2452            throws SSOException, NameNotFoundException, PolicyException {
2453        boolean resourceMatched = false;
2454        ConditionDecision conditionDecision = null;
2455        boolean allowedByConditions = false;
2456        boolean allowedBySubjects = false;
2457        Map advicesFromConditions = null;
2458        long conditionsTtl = Long.MIN_VALUE;
2459        long subjectsTtl = Long.MIN_VALUE;
2460        long timeToLive = Long.MIN_VALUE;
2461
2462        Map actionResults = getMatchedRuleResults(resourceType,
2463                resourceName, actionNames);
2464        resourceMatched = !actionResults.isEmpty();
2465        if (resourceMatched) { //resourceMatch+
2466            conditionDecision = conditions.getConditionDecision(
2467                    token, envParameters);
2468            allowedByConditions = conditionDecision.isAllowed();
2469            advicesFromConditions = conditionDecision.getAdvices();
2470            conditionsTtl = conditionDecision.getTimeToLive();
2471            timeToLive = conditionsTtl;
2472            if (allowedByConditions) { //resourceMatch+, conditions+
2473                allowedBySubjects = users.isMember(token);
2474                subjectsTtl = users.getResultTtl(token);
2475                if (subjectsTtl < timeToLive) {
2476                    timeToLive = subjectsTtl;
2477                }
2478                if (allowedBySubjects) {
2479                    //resourceMatch+, conditions+, subjects+
2480                    Iterator resultActionNames 
2481                            = actionResults.keySet().iterator();
2482                    while ( resultActionNames.hasNext() ) {
2483                        String resultActionName 
2484                                = (String) resultActionNames.next();
2485                        Set resultActionValues 
2486                            = (Set)actionResults.get(resultActionName);
2487
2488                        /*  ActionDecision to include values, no advices
2489                         */
2490                        ActionDecision actionDecision 
2491                                = new ActionDecision(resultActionName,
2492                                resultActionValues, 
2493                                advicesFromConditions, timeToLive);
2494                        policyDecision.addActionDecision(
2495                                actionDecision, resourceType);
2496                    }
2497                } else { //resourceMatch+, conditions+, subjects-
2498                    policyDecision.setTimeToLive(timeToLive);
2499                }
2500            } else { //resourceMatch+, conditions-
2501                boolean reportAdvices = false;
2502                if (!advicesFromConditions.isEmpty()) {
2503                    reportAdvices = users.isMember(token);
2504                    subjectsTtl = users.getResultTtl(token);
2505                    if (subjectsTtl < timeToLive) {
2506                        timeToLive = subjectsTtl;
2507                    }
2508                }
2509                if (reportAdvices) {
2510                    Iterator resultActionNames 
2511                            = actionResults.keySet().iterator();
2512                    while ( resultActionNames.hasNext() ) {
2513                        String resultActionName 
2514                                = (String) resultActionNames.next();
2515
2516                        /*  ActionDecision to include advices, no values
2517                         */
2518                        ActionDecision actionDecision 
2519                                = new ActionDecision(resultActionName,
2520                                Collections.EMPTY_SET, 
2521                                advicesFromConditions, timeToLive);
2522                        policyDecision.addActionDecision(
2523                                actionDecision, resourceType);
2524                    }
2525                } else { //no advices to report
2526                    policyDecision.setTimeToLive(timeToLive);
2527                }
2528            }
2529        } else { //resourceMatch-
2530            policyDecision.setTimeToLive(Long.MAX_VALUE);
2531        }
2532        return policyDecision;
2533    }
2534
2535
2536    /** Gets evaluation order of Subjects, Rules and Conditions for this policy
2537     *  that is likely to be least expensive in terms of cpu.
2538     *
2539     *  @return int representing preferred evaluation order for this policy
2540     */
2541    private int getEvaluationOrder(SSOToken token) throws SSOException {
2542
2543        int evaluationOrder = RULES_CONDITIONS_SUBJECTS;
2544
2545        //treat subject weight as 0, if sub result is in cache
2546        int mpsWeight = users.isSubjectResultCached(token) ? 0 : psWeight;
2547        if (( mpsWeight <= pcWeight) && (pcWeight <= prWeight)) {
2548            evaluationOrder = SUBJECTS_CONDITIONS_RULES;
2549        }  else if (( pcWeight <= mpsWeight) && (mpsWeight <= prWeight)) {
2550            evaluationOrder = CONDITIONS_SUBJECTS_RULES;
2551        }  else if (( prWeight <= pcWeight) && (pcWeight <= mpsWeight)) {
2552            evaluationOrder = RULES_CONDITIONS_SUBJECTS;
2553        }  else if (( prWeight <= mpsWeight) && (mpsWeight <= pcWeight)) {
2554            evaluationOrder = RULES_SUBJECTS_CONDITIONS;
2555        }  else if (( mpsWeight <= prWeight) && (prWeight <= pcWeight)) {
2556            evaluationOrder = SUBJECTS_RULES_CONDITIONS;
2557        }  else if (( pcWeight <= prWeight) && (prWeight <= mpsWeight)) {
2558            evaluationOrder = CONDITIONS_RULES_SUBJECTS;
2559        } 
2560        return evaluationOrder;
2561    }
2562
2563    /** Initializes global values of evaluation weight 
2564     *  per Subject, per Condition and per Rule element
2565     *  of the policies by reading value of property
2566     * <code>EVALUATION_WEIGHTS_KEY</code> from AMConfig.properties.
2567     * If the value is not defined in AMConfig.properties, the value defaults
2568     * to <code>DEFAULT_EVALUATION_WEIGHTS</code>.
2569     * @see #DEFAULT_EVALUATION_WEIGHTS
2570     */
2571    private static void initializeStaticEvaluationWeights() {
2572        EVALUATION_WEIGHTS = com.iplanet.am.util.SystemProperties.get(
2573                EVALUATION_WEIGHTS_KEY, DEFAULT_EVALUATION_WEIGHTS);
2574        StringTokenizer st = new StringTokenizer(EVALUATION_WEIGHTS, ":");
2575        int tokenCount = st.countTokens();
2576        if ( tokenCount != 3) {
2577            if (PolicyManager.debug.warningEnabled()) {
2578                PolicyManager.debug.warning(
2579                    "Policy.initializeStaticEvaluationWeights:"
2580                    + " invalid evaulationWeights defined, "
2581                    + " defaulting to " + DEFAULT_EVALUATION_WEIGHTS);
2582            }
2583            EVALUATION_WEIGHTS = DEFAULT_EVALUATION_WEIGHTS;
2584        } else {
2585            String weight = st.nextToken();
2586            try {
2587                subjectWeight = Integer.parseInt(weight);
2588            } catch (NumberFormatException nfe) {
2589                if (PolicyManager.debug.warningEnabled()) {
2590                    PolicyManager.debug.warning(
2591                        "Policy.initializeStaticEvaluationWeights:"
2592                        + " invalid subjectWeight defined, defaulting to 0");
2593                }
2594                subjectWeight = 0;
2595            }
2596            weight = st.nextToken();
2597            try {
2598                ruleWeight = Integer.parseInt(weight);
2599            } catch (NumberFormatException nfe) {
2600                if (PolicyManager.debug.warningEnabled()) {
2601                    PolicyManager.debug.warning(
2602                        "Policy.initializeStaticEvaluationWeights:"
2603                        + " invalid ruleWeight defined, defaulting to 0");
2604                }
2605                ruleWeight = 0;
2606            }
2607            weight = st.nextToken();
2608            try {
2609                conditionWeight = Integer.parseInt(weight);
2610            } catch (NumberFormatException nfe) {
2611                if (PolicyManager.debug.warningEnabled()) {
2612                    PolicyManager.debug.warning(
2613                        "Policy.initializeStaticEvaluationWeights:"
2614                        + " invalid conditionWeight defined, defaulting to 0");
2615                }
2616                conditionWeight = 0;
2617            }
2618        }
2619    }
2620
2621
2622    /** Initializes  evaluation weights for 
2623     *  Subjects, Conditions and rules of this policy object.
2624     */
2625    void initializeEvaluationWeights() {
2626        psWeight = users.size() * subjectWeight;
2627        prWeight = rules.size() * ruleWeight;
2628        pcWeight = conditions.size() * conditionWeight;
2629    }
2630
2631    /**
2632     * Checks whether the policy is applicable to user identified by sso token
2633     * @return <code>true</code> if the policy is applicable to the  user 
2634     *          identified by sso token, else <code>false</code>
2635     */
2636    boolean isApplicableToUser(SSOToken token)
2637        throws PolicyException, SSOException 
2638    {
2639        return  users.isMember(token);
2640    }
2641
2642    static  Map cloneRuleResults(Map ruleResults) {
2643        Map clonedResults = new HashMap();
2644        if ( (ruleResults != null) && !ruleResults.isEmpty()) {
2645            Iterator keys = ruleResults.keySet().iterator();
2646            while (keys.hasNext()) {
2647                String key = (String)keys.next();
2648                Set values = (Set)ruleResults.get(key);
2649                Set clonedValues = new HashSet();
2650                clonedValues.addAll(values);
2651                clonedResults.put(key, clonedValues);
2652            }
2653        }
2654        return clonedResults;
2655    }
2656
2657    /*
2658     * We track the subject realm when a realm subject is added to the policy.
2659     * We use this information to enforce that a policy has 
2660     * realm subjects only from one realm. We also use this information
2661     * to enforce that policy is not saved into a different realm.
2662     */
2663    String getSubjectRealm() {
2664        return subjectRealm;
2665    }
2666
2667    /**
2668     * Clears the cached membership evaluation results corresponding
2669     * to the <code>tokenIdString</code>. This is triggered through
2670     * <code>PolicySSOTokenListener</code> and <code>PolicyCache</code>
2671     * when session property
2672     * of a logged in user is changed
2673     *
2674     * @param tokenIdString sessionId of the user whose session property changed
2675     */
2676    void clearSubjectResultCache(String tokenIdString) throws PolicyException {
2677        if (DEBUG.messageEnabled()) {
2678            DEBUG.message("Policy.clearSubjectResultCache(tokenIdString): "
2679                    + " clearing cached subject evaluation result for "
2680                    + " tokenId XXXXX");
2681        }
2682        users.clearSubjectResultCache(tokenIdString);
2683    }
2684
2685    /**
2686     * Returns creation date.
2687     *
2688     * @return creation date.
2689     */
2690    public long getCreationDate() {
2691        return creationDate;
2692    }
2693    
2694    /**
2695     * Sets the creation date.
2696     * 
2697     * @param creationDate creation date.
2698     */
2699    public void setCreationDate(long creationDate) {
2700        this.creationDate = creationDate;
2701    }
2702    
2703    /**
2704     * Returns last modified date.
2705     * 
2706     * @return last modified date.
2707     */
2708    public long getLastModifiedDate() {
2709        return lastModifiedDate;
2710    }
2711
2712    /**
2713     * Sets the last modified date.
2714     *
2715     * @param lastModifiedDate last modified date.
2716     */
2717    public void setLastModifiedDate(long lastModifiedDate) {
2718        this.lastModifiedDate = lastModifiedDate;
2719    }
2720
2721    /**
2722     * Returns the user ID who last modified the policy.
2723     *
2724     * @return user ID who last modified the policy.
2725     */
2726    public String getLastModifiedBy() {
2727        return lastModifiedBy;
2728    }
2729
2730    /**
2731     * Sets the user ID who last modified the policy.
2732     *
2733     * @param lastModifiedBy user ID who last modified the policy.
2734     */
2735    public void setLastModifiedBy(String lastModifiedBy) {
2736        this.lastModifiedBy = lastModifiedBy;
2737    }
2738
2739    /**
2740     * Returns the user ID who created the policy.
2741     *
2742     * @return user ID who created the policy.
2743     */
2744    public String getCreatedBy() {
2745        return createdBy;
2746    }
2747
2748    /**
2749     * Sets the user ID who created the policy.
2750     *
2751     * @param createdBy user ID who created the policy.
2752     */
2753    public void setCreatedBy(String createdBy) {
2754        this.createdBy = createdBy;
2755    }
2756}