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: SubjectTypeManager.java,v 1.5 2009/01/28 05:35:01 ww203982 Exp $
026 *
027 * Portions Copyrighted 2014-2015 ForgeRock AS.
028 */
029
030package com.sun.identity.policy;
031
032import java.util.*;
033
034import com.iplanet.am.sdk.AMCommonUtils;
035import com.iplanet.sso.SSOToken;
036import com.iplanet.sso.SSOException;
037import com.sun.identity.sm.*;
038import com.sun.identity.policy.interfaces.Subject;
039import com.sun.identity.shared.locale.AMResourceBundleCache;
040import com.sun.identity.shared.debug.Debug;
041import com.sun.identity.shared.locale.Locale;
042import org.forgerock.openam.ldap.LDAPUtils;
043
044/**
045 * The class <code>SubjectTypeManager</code> provides
046 * methods to get a list of configured <code>Subject
047 * </code> objects, and to obtain a factory object for it.
048 *
049 * @supported.all.api
050 * @deprecated since 12.0.0
051 */
052@Deprecated
053public class SubjectTypeManager {
054
055    private static String SUBJECT = "Subject";
056
057    private SSOToken token;
058    private PolicyManager pm;
059
060    private ResourceBundle rb;
061    private Subjects realmSubjects = null;
062    private Map sharedSubjects = Collections.synchronizedMap(new HashMap());
063    private static AMResourceBundleCache amCache = 
064            AMResourceBundleCache.getInstance();
065    private String pmRealmName;
066
067    static Debug debug = PolicyManager.debug;
068
069    /**
070     * Constructs a <code>SubjectTypeManager</code> object
071     */
072    protected SubjectTypeManager() throws SSOException {
073        token = ServiceTypeManager.getSSOToken();
074        String lstr = token.getProperty("Locale");
075        java.util.Locale loc = com.sun.identity.shared.locale.Locale.getLocale(
076            lstr);
077        rb = amCache.getResBundle(ResBundleUtils.rbName, loc);
078    }
079
080    /**
081     * Constructs a <code>SubjectTypeManager</code> object
082     * @param pm <code>PolicyManager</code> to initialize
083     * <code>SubjectTypeManager</code> with
084     */
085    protected SubjectTypeManager(PolicyManager pm)  {
086        this.pm = pm;
087        pmRealmName = LDAPUtils.formatToRFC(pm.getOrganizationDN());
088        token = pm.token;
089        java.util.Locale loc;
090        try {
091            String lstr = token.getProperty("Locale");
092            loc = com.sun.identity.shared.locale.Locale.getLocale(lstr);
093        } catch (SSOException ex) {
094            debug.error(
095                "SubjectTypeManager:Unable to retreive locale from SSOToken",
096                ex);
097            loc = Locale.getDefaultLocale();
098        }
099
100         if (debug.messageEnabled()) {
101            debug.message("SubjectManager locale="+loc+"\tI18nFileName = "+
102                     ResBundleUtils.rbName);
103        }
104        rb = amCache.getResBundle(ResBundleUtils.rbName, loc);
105    }
106
107    /**
108     * Returns a set of all valid subject type names defined by the policy
109     * service.
110     * Examples are <code>LDAPRole</code>, <code>LDAPGroup</code>, etc.
111     *
112     * @return a set of all valid subject type names defined by the policy
113     *         service.
114     * @throws SSOException if the <code>SSOToken</code> used to create 
115     *                      the <code>PolicyManager</code> has become invalid
116     * @throws PolicyException for any other abnormal condition
117     */
118    public Set getSubjectTypeNames() throws SSOException,
119            PolicyException {
120        return (PolicyManager.getPluginSchemaNames(SUBJECT));
121    }
122
123    /**
124     * Returns a set of valid subject type names configured for the
125     * organization.
126     * Examples are <code>LDAPRole</code>, <code>LDAPGroup</code>, etc.
127     *
128     * @return a set of valid subject type names configured for the
129     *         organization.
130     * @throws SSOException if the <code>SSOToken</code> used to create 
131     *                      the <code>PolicyManager</code> has become invalid
132     * @throws PolicyException for any other abnormal condition
133     */
134    public Set getSelectedSubjectTypeNames() throws SSOException,
135            PolicyException {
136        Map policyConfig = pm.getPolicyConfig();
137        Set selectedSubjects = null;
138        if (policyConfig != null) {
139            selectedSubjects = 
140                    (Set)policyConfig.get(PolicyConfig.SELECTED_SUBJECTS); 
141        }
142        if ( selectedSubjects == null) {
143            selectedSubjects = Collections.EMPTY_SET;
144        }
145        return selectedSubjects;
146    }
147
148    /**
149     * Returns the type of the <code>Subject</code> implementation.
150     * For example <code>LDAPRoles</code>, <code>LDAPGroups</code> etc.
151     *
152     * @param subject <code>Subject</code> for which this method will
153     * return its associated type
154     *
155     * @return type of the <code>Subject</code>, e.g., <code>LDAPRoles</code>,
156     *         <code>LDAPGroups</code>, etc. Returns <code>null</code> if
157     *         not present.
158     */
159    public String getSubjectTypeName(Subject subject) {
160        return (subjectTypeName(subject));
161    }
162
163    /**
164     * Returns the I18N properties file name that should be
165     * used to localize display names for the given
166     * subject type.
167     *
168     * @param subjectType subject type name
169     *
170     * @return i18n properties file name
171     */
172    protected String getI18NPropertiesFileName(String subjectType) {
173        // %%% Need to get the file name from plugin schema
174        return (null);
175    }
176
177    /**
178     * Returns the I18N key to be used to localize the
179     * display name for the subject type name.
180     *
181     * @param subjectType subject type name
182     *
183     * @return i18n key to obtain the display name
184     */
185    public String getI18NKey(String subjectType) {
186        PluginSchema ps = PolicyManager.getPluginSchema(SUBJECT, subjectType);
187        if (ps != null) {
188            return (ps.getI18NKey());
189        }
190        return (null);
191    }
192
193    /**
194     * Returns the display name for the subject type
195     * @param subjectType subject type
196     * @return display name for the subject type
197     */
198    public String getDisplayName(String subjectType) {
199        String displayName = null;
200        String i18nKey = getI18NKey(subjectType);
201        if (i18nKey == null || i18nKey.length() == 0) {
202            displayName = subjectType;
203        } else {
204            displayName = Locale.getString(rb,i18nKey,debug);
205        }
206        return displayName;
207    }
208
209    /**
210     * Returns an instance of the <code>Subject</code> given the subject type
211     * name.
212     *
213     * @param subjectType subject type.
214     * @return an instance of the <code>Subject</code> given the subject type
215     * name.
216     * @throws NameNotFoundException if the <code>Subject</code> for the
217     *            <code>subjectType</code> name is not found
218     * @throws PolicyException for any other abnormal condition
219     */
220    public Subject getSubject(String subjectType)
221        throws NameNotFoundException, PolicyException {
222        PluginSchema ps = PolicyManager.getPluginSchema(SUBJECT, subjectType);
223        if (ps == null) {
224            throw (new NameNotFoundException(ResBundleUtils.rbName,
225                "invalid_subject", null,
226                subjectType, PolicyException.USER_COLLECTION));
227        }
228
229        // Construct the object
230        Subject answer = null;
231        try {
232            String className = ps.getClassName();
233            answer = (Subject) Class.forName(className).newInstance();
234        } catch (Exception e) {
235            throw (new PolicyException(e));
236        }
237
238        //initialize with policy config
239        answer.initialize(pm.getPolicyConfig());
240        return (answer);
241    }
242
243    /**
244     * Adds a policy subject at realm. 
245     *
246     * @param subjectName name of the Subject instance 
247     * @param subject Subject object to be added 
248     *
249     * @throws NameAlreadyExistsException if a Subject with the given name
250     *          already exists at the realm
251     * @throws InvalidNameException if the subject name is invalid
252     *
253     * @throws PolicyException if can not add the Subject 
254     */
255    public void addSubject(String subjectName, Subject subject) 
256            throws NameAlreadyExistsException, InvalidNameException,
257            PolicyException, SSOException {
258
259        //we  really do not use the exclusive flag at realm level
260        addSubject(subjectName, subject, false);
261    }
262
263    /**
264     * Adds a policy subject at realm. 
265     *
266     * @param subjectName name of the Subject instance 
267     * @param subject Subject object to be added 
268     *
269     * @param exclusive boolean flag indicating whether the subject 
270     *        is to be exclusive subject. If subject is exclusive, 
271     *        policy applies to users who are not members of the 
272     *        subject. Otherwise, policy applies to members of the subject.
273     *
274     * @throws NameAlreadyExistsException if a Subject with the given name
275     *          already exists at the realm
276     * @throws InvalidNameException if the subject name is invalid
277     *
278     * @throws PolicyException if can not add the Subject 
279     *
280     *
281     */
282    private void addSubject(String subjectName, Subject subject, 
283            boolean exclusive) 
284            throws NameAlreadyExistsException, InvalidNameException,
285            PolicyException, SSOException {
286        if (debug.messageEnabled()) {
287            debug.message("Adding realm subject : " + subjectName
288                    + ", in realm:" + pmRealmName);
289        }
290        if (realmSubjects == null) {
291            initRealmSubjects();
292        }
293        realmSubjects.addSubject(subjectName, subject, exclusive);
294        saveSubjects();
295        if (debug.messageEnabled()) {
296            debug.message("Added realm subject : " + subjectName
297                    + ", in realm:" + pmRealmName);
298        }
299    }
300
301    /**
302     * Removes the subject with the given name  from the realm.
303     * This method would throw PolicyException if the subject 
304     * is being used by any policy.
305     *
306     * @param subjectName name of the Subject
307     *
308     * @return returns the Subject object being removed,
309     *         returns <code>null</code> if Subject with 
310     *         the given subjectName is not present 
311     *
312     * @throws PolicyException if can not remove the Subject 
313     */
314    public Subject removeSubject(String subjectName) 
315            throws ObjectInUseException, PolicyException, SSOException {
316        return removeSubject(subjectName, false);
317    }
318
319    /**
320     * Removes the subject with the given name  from the realm.
321     * This method would throw PolicyException if the subject 
322     * is being used by any policy unless <code>forcedRemove</code> 
323     * argument  is set to <code>true</code>. 
324     * If the <code>forcedRemove</code> argument is set to 
325     * <code>true</code> policies that are using the subject would 
326     * be modified to  remove the references to the subject
327     *
328     * @param subjectName name of the Subject
329     * @param forcedRemove if set to <code>true</code>, policies that
330     *    use the subject would be modifed to remove the references
331     *    to the subject. Otherwise, <code>ObjectInUseException</code>
332     *    would be thrown if there is any policy using the subject
333     *
334     * @return returns the Subject object being removed,
335     *         returns <code>null</code> if Subject with 
336     *         the given subjectName is not present 
337     *
338     * @throws PolicyException if can not remove the Subject 
339     */
340    public Subject removeSubject(String subjectName, boolean forcedRemove) 
341            throws ObjectInUseException, PolicyException, SSOException {
342        if (debug.messageEnabled()) {
343            debug.message("Removing realm subject : " + subjectName
344                    + ", in realm:" + pmRealmName);
345        }
346        if (realmSubjects == null) {
347            initRealmSubjects();
348        }
349        if (forcedRemove) {
350            Set userPolicies = pm.getPoliciesUsingRealmSubject(subjectName);
351            for (Iterator policyIter = userPolicies.iterator();
352                    policyIter.hasNext();) {
353                Policy policy = (Policy)policyIter.next();
354                policy.removeSubject(subjectName);
355            }
356        } else {
357            Policy p = pm.getPolicyUsingRealmSubject(subjectName);
358            if ( p != null) {
359                //ObjectInUseException(String rbName, String errCode,         
360                //Object[] args, String name, Object user) 
361                throw new ObjectInUseException(null, null, null, null, null);
362            }
363        }
364        Subject subject = realmSubjects.removeSubject(subjectName);
365        saveSubjects();
366        if (debug.messageEnabled()) {
367            debug.message("Removed realm subject : " + subjectName
368                    + ", in realm:" + pmRealmName);
369        }
370        return subject;
371    }
372
373    /**
374     * Replaces an existing subject with the same name by the
375     * current one at the realm. If a subject with the same name does 
376     * not exist, it will be added.
377     *
378     * @param subjectName name of the Subject instance 
379     * @param subject Subject that will replace an existing Subject
380     *         with the same name
381     *
382     * @throws NameNotFoundException if a Subject instance
383     *         with the given name is not present
384     *
385     * @throws PolicyException if can not replace the Subject 
386     */
387    public void replaceSubject(String subjectName, Subject subject) 
388            throws NameNotFoundException, PolicyException, SSOException {
389
390        //we  really do not use the exclusive flag at realm level
391        replaceSubject(subjectName, subject, false);
392    }
393
394    /**
395     * Replaces an existing subject with the same name by the
396     * current one at the realm. If a subject with the same name does 
397     * not exist, it will be added.
398     *
399     * @param subjectName name of the Subject instance 
400     * @param subject Subject that will replace an existing Subject
401     *         with the same name
402     *
403     * @param exclusive boolean flag indicating whether the subject 
404     *        is to be exclusive subject. If subject is exclusive, 
405     *        policy applies to users who are not members of the 
406     *        subject. Otherwise, policy applies to members of the subject.
407     *
408     * @throws NameNotFoundException if a Subject instance
409     *         with the given name is not present
410     *
411     * @throws PolicyException if can not replace the Subject 
412     *
413     *
414     */
415    private void replaceSubject(String subjectName, Subject subject, 
416            boolean exclusive) 
417            throws NameNotFoundException, PolicyException, SSOException {
418        if (debug.messageEnabled()) {
419            debug.message("Replacing realm subject : " + subjectName
420                    + ", in realm:" + pmRealmName);
421        }
422        if (realmSubjects == null) {
423            initRealmSubjects();
424        }
425        realmSubjects.replaceSubject(subjectName, subject, exclusive);
426        saveSubjects();
427        if (debug.messageEnabled()) {
428            debug.message("Replaced realm subject : " + subjectName
429                    + ", in realm:" + pmRealmName);
430        }
431    }
432
433    /**
434     * Get the set of names of Subject(s) defined at the realm
435     *
436     * @return set of subject names
437     */
438    public Set getSubjectNames() throws PolicyException, SSOException {
439        if (debug.messageEnabled()) {
440            debug.message("Getting subject names from realm: " 
441                    +  pmRealmName);
442        }
443        if (realmSubjects == null) {
444            initRealmSubjects();
445        }
446        Set subjectNames = realmSubjects.getSubjectNames();
447        if (debug.messageEnabled()) {
448            debug.message("Returning subject names from realm: " 
449                    +  pmRealmName + ",subjectNames=" + subjectNames);
450        }
451        return subjectNames;
452    }
453
454    /**
455     * Returns the Subject object identified by subjectName defined at 
456     * the realm
457     *
458     * @param subjectName name of subject.
459     *
460     * @return Subject object
461     *
462     * @throws NameNotFoundException if a Subject with the given name
463     * does not exist
464     *
465     * @throws PolicyException if can not get the Subject
466     */
467    public Subject getSubjectByName(String subjectName) 
468            throws NameNotFoundException, PolicyException {
469        if (debug.messageEnabled()) {
470            debug.message("Getting subject by name from realm: " 
471                    +  pmRealmName + ", subjectName=" + subjectName);
472        }
473        if (realmSubjects == null) {
474            initRealmSubjects();
475        }
476        if (debug.messageEnabled()) {
477            debug.message("Returning subject by name from realm: " 
478                    +  pmRealmName + ", subjectName=" + subjectName);
479        }
480        return (Subject)realmSubjects.getSubject(subjectName).clone();
481    }
482
483    synchronized Subject getCachedSubjectByName(String subjectName) 
484            throws  PolicyException {
485        if (debug.messageEnabled()) {
486            debug.message("Getting cached subject by name from realm: " 
487                    +  pmRealmName + ", subjectName=" + subjectName);
488        }
489        if (realmSubjects == null) {
490            initRealmSubjects();
491        }
492        if (debug.messageEnabled()) {
493            debug.message("Returning cached subject by name from realm: " 
494                    +  pmRealmName + ", subjectName=" + subjectName);
495        }
496        return (Subject)realmSubjects.fetchSubject(subjectName);
497    }
498
499    /**
500     * Returns a handle to the Subject object identified by subjectName 
501     * defined at the realm, to add to a policy. 
502     * Returned Subject is backed by 
503     * the Subject at the realm. However, you can not change the values
504     * using the returned Subject. 
505     *
506     * @param subjectName name of subject.
507     *
508     * @return Subject object
509     *
510     * @throws NameNotFoundException if a Subject with the given name
511     * does not exist
512     *
513     * @throws PolicyException if can not get the Subject
514     *
515     */
516    Subject getSharedSubject(String subjectName) 
517            throws  PolicyException {
518        if (debug.messageEnabled()) {
519            debug.message("Getting shared subject from realm: " 
520                    +  pmRealmName + ", subjectName=" + subjectName);
521        }
522        Subject subject = (Subject)sharedSubjects.get(subjectName);
523        if (subject == null) {
524            subject = new SharedSubject(subjectName, this);
525            sharedSubjects.put(subjectName, subject);
526        }
527        if (debug.messageEnabled()) {
528            debug.message("Returning shared subject from realm: " 
529                    +  pmRealmName + ", subjectName=" + subjectName);
530        }
531        return subject;
532    }
533
534    /**
535     * Returns subject type name for the given <code>subject</code>
536     * @return subject type name for the given <code>subject</code>
537     */
538    static String subjectTypeName(Subject subject) {
539        if (subject == null) {
540            return (null);
541        }
542        String answer = null;
543        String className = subject.getClass().getName();
544        Iterator items = PolicyManager.getPluginSchemaNames(SUBJECT).iterator();
545        while (items.hasNext()) {
546            String pluginName = (String) items.next();
547            PluginSchema ps = PolicyManager.getPluginSchema(SUBJECT, 
548                pluginName);
549            if (className.equals(ps.getClassName())) {
550                answer = pluginName;
551                break;
552            }
553        }
554        return (answer);
555    }
556
557    /**
558     * Returns the view bean URL given the Subject
559     *
560     * @param subject subject for which to get the view bean URL
561     *
562     * @return view bean URL defined for the subject plugin in the policy
563     *         service <code>PluginSchema</code>.
564     */
565    public String getViewBeanURL(Subject subject) {
566        return PolicyManager.getViewBeanURL(SUBJECT, 
567            subject.getClass().getName());
568    }
569
570    /**
571     * Returns <code>PolicyManager</code> used by this object
572     */
573    PolicyManager getPolicyManager() {
574        return pm;
575    }
576
577    /**
578     * Saves the realm scoped <code>Subject</code> objects to persistent store
579     */
580    private void saveSubjects() throws PolicyException, SSOException {
581        if (realmSubjects != null) {
582            pm.saveRealmSubjects(realmSubjects);
583        }
584    }
585
586    /**
587     * Initializes the realm scoped <code>Subject</code> objects reading from
588     * persistent store
589     */
590    private void initRealmSubjects() throws PolicyException {
591        if (debug.messageEnabled()) {
592            debug.message("Initializing realm subjects in realm : " 
593                    +  pmRealmName); 
594        }
595        try {
596            realmSubjects = pm.readRealmSubjects();
597        } catch (SSOException ssoe){
598            throw new PolicyException(ResBundleUtils.rbName,
599                "could_not_initialize_realm_subjects", null, ssoe);
600        }
601        if (debug.messageEnabled()) {
602            debug.message("Initialized realm subjects in realm : " 
603                    +  pmRealmName); 
604        }
605    }
606
607    /**
608     * Resets the cached realm scoped <code>Subject</code> objects. 
609     * Would read from persistent store on next access to realm scoped
610     * <code>Subject</code> object
611     */
612    void resetRealmSubjects() {
613        if (debug.messageEnabled()) {
614            debug.message("Resetting realm subjects in realm : " 
615                    +  pmRealmName); 
616        }
617        synchronized(this) {
618            realmSubjects = null;
619        }
620        if (debug.messageEnabled()) {
621            debug.message("Reset realm subjects in realm : " 
622                    +  pmRealmName); 
623        }
624    }
625
626}