001/*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 2005 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: IdRepoListener.java,v 1.16 2009/01/28 05:34:59 ww203982 Exp $
026 *
027 * Portions Copyrighted 2011-2016 ForgeRock AS.
028 */
029package com.sun.identity.idm;
030
031import java.security.AccessController;
032import java.util.ArrayList;
033import java.util.Collections;
034import java.util.HashSet;
035import java.util.List;
036import java.util.Map;
037import java.util.Set;
038
039import org.forgerock.openam.ldap.LDAPUtils;
040import org.forgerock.openam.ldap.PersistentSearchChangeType;
041import org.forgerock.openam.utils.StringUtils;
042
043import com.iplanet.sso.SSOException;
044import com.iplanet.sso.SSOToken;
045import com.sun.identity.security.AdminTokenAction;
046import com.sun.identity.shared.debug.Debug;
047import com.sun.identity.shared.jaxrpc.SOAPClient;
048import com.sun.identity.sm.SMSException;
049import com.sun.identity.sm.ServiceConfig;
050import com.sun.identity.sm.ServiceConfigManager;
051import com.sun.identity.sm.ServiceManager;
052
053/**
054 * Provides methods that can be called by IdRepo plugins to notify change
055 * events. Used to update cache and also to send notifications to registered
056 * listeners. Each IdRepo plugin will be given a unique instance of this object.
057 * 
058 * Additionally, this class maintains the configuration data for the IdRepo
059 * plugin and also to store the SMS Service attributes for the organization.
060 *
061 * @supported.all.api
062 */
063public final class IdRepoListener {
064
065    // Configuration data for the IdRepo plugin
066    // Must have "realm" key to correctly send the notifications to clients
067    private Map configMap = null;
068
069    // Listener registed by JAXRPC Impl to send notifications
070    private static IdEventListener remoteListener = null;
071
072    private static Debug debug = Debug.getInstance("idrepoListener");
073
074    // To serialize and deserialize configMap
075    protected static SOAPClient sclient;
076    
077    // Configured Identity Types
078    private static IdType[] defaultIdTypes;
079    
080    // Flags to check if caching is enabled and to clear them
081    private static boolean cacheChecked;
082    private static boolean cacheEnabled;
083    private static IdServices idServices;
084
085    /*
086     * (non-Javadoc)
087     * 
088     * @see com.iplanet.am.sdk.AMObjectListener#allObjectsChanged()
089     */
090    public void allObjectsChanged() {
091        if (debug.messageEnabled()) {
092            debug.message("IdRepoListener: allObjectsChanged Called!");
093        }
094
095        // Check if caching is enabled
096        if (!cacheChecked) {
097            idServices = IdServicesFactory.getDataStoreServices();
098            if (idServices instanceof IdCachedServices) {
099                // If Caching was enabled - then clear the cache!!
100                cacheEnabled = true;
101            }
102            cacheChecked = true;
103        }
104        if (cacheEnabled) {
105            // If Caching was enabled - then clear the cache!!
106            ((IdCachedServices) idServices).clearCache();
107        }
108
109        // Get the list of listeners setup with idRepo
110        String org = (String) configMap.get("realm");
111        ArrayList list = (ArrayList) AMIdentityRepository.listeners.get(org);
112        // Update any listeners registered with IdRepo
113        if (list != null) {
114            int size = list.size();
115            for (int j = 0; j < size; j++) {
116                IdEventListener l = (IdEventListener) list.get(j);
117                l.allIdentitiesChanged();
118            }
119        }
120        if (remoteListener != null) {
121            remoteListener.allIdentitiesChanged();
122        }
123    }
124
125    /**
126     * 
127     * This method has been deprecated as of OpenSSO Enterprise 8.0.
128     * 
129     * @param name name of the identity that changed
130     * @param type change type i.e., add, delete, modify, etc.
131     * @param cMap configuration map that contains realm and plugin-name
132     *
133     * @deprecated  As of Sun Java System Access Manager 7.1.
134     */
135    public void objectChanged(String name, int type, Map cMap) {
136        objectChanged(name, null, type, cMap);
137    }
138    
139    /**
140     * Notification mechanism for IdRepo plugins to specify the identiy name
141     * and identity type that has been changed.
142     * 
143     * @param name name of the identity that changed
144     * @param idType IdType i.e., user, group, etc.
145     * @param changeType change type i.e., add, delete, modify, etc.
146     * @param cMap configuration map that contains realm and plugin-name
147     */
148    public void objectChanged(String name, IdType idType, int changeType,
149        Map cMap) {
150        if (debug.messageEnabled()) {
151            debug.message("objectChanged called with IdType= name: " + name +
152                " IdType: " + idType + " ChangeType: " + changeType +
153                "\nConfigmap = " + cMap);
154        }
155        // Get the list of listeners setup with idRepo
156        String org = (String) configMap.get("realm");
157        List<IdEventListener> list = (List<IdEventListener>) AMIdentityRepository.listeners.get(org);
158        list = list == null ? new ArrayList<IdEventListener>() : new ArrayList<>(list);
159        if (remoteListener != null) {
160            list.add(remoteListener);
161        }
162
163        // Check if caching is enabled
164        if (!cacheChecked) {
165            idServices = IdServicesFactory.getDataStoreServices();
166            if (idServices instanceof IdCachedServices) {
167                // If Caching was enabled - then clear the cache!!
168                cacheEnabled = true;
169            }
170            cacheChecked = true;
171        }
172        
173        if (StringUtils.isNotEmpty(name)) {
174            String[] changed = getChangedIds(name, idType, cMap);
175            for (int i = 0; i < changed.length; i++) {
176
177                if (cacheEnabled) {
178                    ((IdCachedServices) idServices).dirtyCache(changed[i],
179                        changeType, false, false, Collections.EMPTY_SET);
180                }
181
182                for (IdEventListener l : list) {
183                    // Update any listeners registered with IdRepo
184                    if(changeType == OBJECT_CHANGED || changeType == OBJECT_ADDED) {
185                        l.identityChanged(changed[i]);
186                    } else if (changeType == OBJECT_REMOVED) {
187                        l.identityDeleted(changed[i]);
188                    } else if (changeType == OBJECT_RENAMED) {
189                        l.identityRenamed(changed[i]);
190                    }
191                }
192            }
193        } else if (debug.warningEnabled()) {
194            debug.warning("objectChanged called with an empty name");
195        }
196    }
197
198    public static void addRemoteListener(IdEventListener l) {
199        remoteListener = l;
200    }
201    
202    /*
203     * Returns the configurations for the IdRepo plugins
204     */
205    public Map getConfigMap() {
206        return configMap;
207    }
208
209    /*
210     * Maintains the configurations for the IdRepo plugins
211     */
212    public void setConfigMap(Map cMap) {
213        configMap = cMap;
214    }
215
216    /**
217     * Stores service's dynamic attributes within the IdRepo plugin
218     * configuration. In the current implementation changes to dynamic
219     * attributes to LDAPv3Repo restart the plugin, since it triggers
220     * a configuration change notification.
221     * 
222     * @param sName service name for which attributes are being set
223     * @param attrs service synamic attributes
224     * @throws com.sun.identity.idm.IdRepoException
225     */
226    public void setServiceAttributes(String sName, Map attrs)
227            throws IdRepoException {
228        String realm = (String) configMap.get("realm");
229        String pluginName = (String) configMap.get("plugin-name");
230        if (realm == null || pluginName == null) {
231            AMIdentityRepository.debug.error(
232                    "IdRepoListener.setServiveAttribute: realm or plugin name"
233                    + " is null");
234            Object[] args = { sName, IdType.ROLE.getName() };
235            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.SERVICE_ALREADY_ASSIGNED, args);
236        }
237        try {
238            SSOToken token = (SSOToken) AccessController
239                    .doPrivileged(AdminTokenAction.getInstance());
240            ServiceConfigManager scm = new ServiceConfigManager(token,
241                    IdConstants.REPO_SERVICE, "1.0");
242            ServiceConfig sc = scm.getOrganizationConfig(realm, null);
243            if (sc == null) {
244                return;
245            }
246            
247            ServiceConfig subConfig = sc.getSubConfig(pluginName);
248            if (subConfig == null) {
249                return;
250            }
251            Map attributes = subConfig.getAttributes();
252            Set vals = (Set) attributes.get(IdConstants.SERVICE_ATTRS);
253            if (vals == null || vals == Collections.EMPTY_SET) {
254                vals = new HashSet();
255            }
256            if (sclient == null) {
257                sclient = new SOAPClient("dummy");    
258            }
259            String mapStr = sclient.encodeMap("result", attrs);
260            vals = new HashSet();
261            vals.add(mapStr);
262            attributes.put(IdConstants.SERVICE_ATTRS, vals);
263            subConfig.setAttributes(attributes);
264        } catch (SMSException smse) {
265            AMIdentityRepository.debug.error(
266                    "IdRepoListener: Unable to set service attributes", smse);
267            Object[] args = { sName, IdType.ROLE.getName() };
268            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.SERVICE_ALREADY_ASSIGNED, args);
269        } catch (SSOException ssoe) {
270            AMIdentityRepository.debug.error(
271                    "IdRepoListener: Unable to set service attributes", ssoe);
272            Object[] args = { sName, IdType.ROLE.getName() };
273            throw new IdRepoException(IdRepoBundle.BUNDLE_NAME, IdRepoErrorCode.SERVICE_ALREADY_ASSIGNED, args);
274        }
275    }
276
277    private String[] getChangedIds(String name, IdType type, Map cMap) {
278        int size = IdUtils.supportedTypes.size();
279        // If configMap is null, then this is a "remote" cache update
280        if ((cMap == null) || cMap.isEmpty()) {
281            String ct[] = new String[1];
282            if (LDAPUtils.isDN(name)) {
283                // Name should be the universal id
284                ct[0] = name;
285            } else {
286                if (type == null) {
287                    // Default to user
288                    type = IdType.USER;
289                }
290                ct[0] = "id=" + name + ",ou=" + type.getName() + "," +
291                        ServiceManager.getBaseDN();
292            }
293            return ct;
294        }
295        String changedTypes[] = null;
296        IdType types[] = null;
297        if (type == null) {
298            changedTypes = new String[size];
299            if (defaultIdTypes  == null) {
300                Set idtypes = IdUtils.supportedTypes;
301                defaultIdTypes = new IdType[idtypes.size()];
302                defaultIdTypes = (IdType[]) idtypes.toArray(defaultIdTypes);
303            }
304            types = defaultIdTypes;
305        } else {
306            changedTypes = new String[1];
307            types = new IdType[1];
308            types[0] = type;
309        }
310        String realm = (String) cMap.get("realm");
311        String Amsdk = (String) cMap.get("amsdk");
312        boolean isAmsdk = Amsdk != null;
313
314        for (int i = 0; i < types.length; i++) {
315            IdType itype = types[i];
316            String n = LDAPUtils.isDN(name) ? LDAPUtils.rdnValueFromDn(name) : name;
317            String id = "id=" + LDAPUtils.escapeValue(n) + ",ou=" + itype.getName() + "," + realm;
318            if (isAmsdk) {
319                id = id + ",amsdkdn=" + name;
320            }
321            changedTypes[i] = id;
322        }
323        return changedTypes;
324    }
325    
326    // Constants for change type recevied from the IdRepo plugins
327
328    /**
329     * Represents an object addition event type.
330     */
331    public static final int OBJECT_ADDED = PersistentSearchChangeType.ADDED;
332
333    /**
334     * Represents an object change event type.
335     */
336    public static final int OBJECT_CHANGED = PersistentSearchChangeType.MODIFIED;
337
338    /**
339     * Represents an object removal event type.
340     */
341    public static final int OBJECT_REMOVED = PersistentSearchChangeType.REMOVED;
342
343    /**
344     * Represents an object renaming event type.
345     */
346    public static final int OBJECT_RENAMED = PersistentSearchChangeType.RENAMED;
347}