001    /**
002     * Copyright 2005-2012 The Kuali Foundation
003     *
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.opensource.org/licenses/ecl2.php
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.rice.krad;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.core.api.config.property.ConfigContext;
020    import org.kuali.rice.core.api.exception.RiceRuntimeException;
021    import org.kuali.rice.kim.api.identity.Person;
022    import org.kuali.rice.kim.api.services.KimApiServiceLocator;
023    import org.kuali.rice.krad.util.SessionTicket;
024    
025    import java.io.Serializable;
026    import java.util.ArrayList;
027    import java.util.Collections;
028    import java.util.HashMap;
029    import java.util.List;
030    import java.util.Map;
031    import java.util.concurrent.atomic.AtomicInteger;
032    
033    /**
034     * Holds info about the User Session
035     *
036     * @author Kuali Rice Team (rice.collab@kuali.org)
037     */
038    public class UserSession implements Serializable {
039        private static final long serialVersionUID = 4532616762540067557L;
040    
041        private Person person;
042        private Person backdoorUser;
043        private AtomicInteger nextObjectKey;
044        private Map<String, Object> objectMap;
045        private String kualiSessionId;
046    
047        /**
048         * Returns the session id. The session id is a unique identifier for the session.
049         * @return the kualiSessionId
050         */
051        public String getKualiSessionId() {
052            return this.kualiSessionId;
053        }
054    
055        /**
056         * Sets the session id.
057         * @param kualiSessionId the kualiSessionId to set
058         */
059        public void setKualiSessionId(String kualiSessionId) {
060            this.kualiSessionId = kualiSessionId;
061        }
062    
063        /**
064         * Creates a user session for the principal specified in the parameter.
065         * Take in a netid, and construct the user from that.
066         *
067         * @param principalName
068         */
069        public UserSession(String principalName) {
070            initPerson(principalName);
071            this.nextObjectKey = new AtomicInteger(0);
072            this.objectMap = Collections.synchronizedMap(new HashMap<String,Object>());
073        }
074    
075        /**
076         * Loads the Person object from KIM. Factored out for testability.
077         * @param principalName the principalName
078         */
079        protected void initPerson(String principalName) {
080            this.person = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(principalName);
081            if (this.person == null) {
082                throw new IllegalArgumentException(
083                        "Failed to locate a principal with principal name '" + principalName + "'");
084            }
085        }
086    
087        /**
088         * Returns the id of the current user.
089         * @return the principalId of the current user in the system, backdoor principalId if backdoor is set
090         */
091        public String getPrincipalId() {
092            if (backdoorUser != null) {
093                return backdoorUser.getPrincipalId();
094            }
095            return person.getPrincipalId();
096        }
097    
098        /**
099         * Returns the name of the current user.
100         * @return the principalName of the current user in the system, backdoor principalName if backdoor is set
101         */
102        public String getPrincipalName() {
103            if (backdoorUser != null) {
104                return backdoorUser.getPrincipalName();
105            }
106            return person.getPrincipalName();
107        }
108    
109        /**
110         * Returns who is logged in. If the backdoor is in use, this will return the network id of the person that is
111         * standing in as the backdoor user.
112         *
113         * @return String
114         */
115        public String getLoggedInUserPrincipalName() {
116            if (person != null) {
117                return person.getPrincipalName();
118            }
119            return "";
120        }
121    
122        /**
123         * Returns a Person object for the current user.
124         * @return the KualiUser which is the current user in the system, backdoor if backdoor is set
125         */
126        public Person getPerson() {
127            if (backdoorUser != null) {
128                return backdoorUser;
129            }
130            return person;
131        }
132    
133        /**
134         * Returns the actual current user even if the backdoor is in use.
135         * @return the KualiUser which is the current user in the system
136         */
137        public Person getActualPerson() {
138            return person;
139        }
140    
141        /**
142         * override the current user in the system by setting the backdoor networkId, which is useful when dealing with
143         * routing or other reasons why you would need to assume an identity in the system
144         *
145         * @param principalName
146         */
147        public void setBackdoorUser(String principalName) {
148            // only allow backdoor in non-production environments
149            if (!isProductionEnvironment()) {
150                this.backdoorUser = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(principalName);
151            }
152        }
153    
154        private boolean isProductionEnvironment() {
155            return ConfigContext.getCurrentContextConfig().isProductionEnvironment();
156        }
157    
158        /**
159         * clear the backdoor user
160         */
161        public void clearBackdoorUser() {
162            this.backdoorUser = null;
163        }
164    
165        /**
166         * allows adding an arbitrary object to the session and returns a string key that can be used to later access this
167         * object from
168         * the session using the retrieveObject method in this class. This allows for a prefix to be placed in front of the
169         * incremented key. So if the prefix is "searchResults" and the nextObjectKey (local int that holds the key value)
170         * is 2 then
171         * the new key will be "searchResults3". "searchResults3" will be returned from the method.
172         *
173         * @param object
174         */
175        public String addObjectWithGeneratedKey(Serializable object, String keyPrefix) {
176            String objectKey = keyPrefix + nextObjectKey.incrementAndGet();
177            objectMap.put(objectKey, object);
178            return objectKey;
179        }
180    
181        /**
182         * allows adding an arbitrary object to the session and returns a string key that can be used to later access this
183         * object from
184         * the session using the retrieveObject method in this class. The key is generated from an integer and incremented
185         * for every
186         * object added.  So the first object added with have a key of "1".  This key will be returned from the method.
187         *
188         * @param object
189         */
190        public String addObjectWithGeneratedKey(Object object) {
191            String objectKey = nextObjectKey.incrementAndGet() + "";
192            objectMap.put(objectKey, object);
193            return objectKey;
194        }
195    
196        /**
197         * allows adding an arbitrary object to the session with static a string key that can be used to later access this
198         * object from
199         * the session using the retrieveObject method in this class
200         *
201         * @param object
202         */
203        public void addObject(String key, Object object) {
204            objectMap.put(key, object);
205        }
206    
207        /**
208         * allows for fetching an object that has been put into the userSession based on the key that would have been
209         * returned when
210         * adding the object
211         *
212         * @param objectKey
213         */
214        public Object retrieveObject(String objectKey) {
215            return this.objectMap.get(objectKey);
216        }
217    
218        /**
219         * allows for removal of an object from session that has been put into the userSession based on the key that would
220         * have been
221         * assigned
222         *
223         * @param objectKey
224         */
225        public void removeObject(String objectKey) {
226            this.objectMap.remove(objectKey);
227        }
228    
229        /**
230         * allows for removal of an object from session that has been put into the userSession based on a key that starts
231         * with the given
232         * prefix
233         */
234        public void removeObjectsByPrefix(String objectKeyPrefix) {
235            synchronized (objectMap) {
236                List<String> removeKeys = new ArrayList<String>();
237                for (String key : objectMap.keySet()) {
238                    if (key.startsWith(objectKeyPrefix)) {
239                        removeKeys.add(key);
240                    }
241                }
242    
243                for (String key : removeKeys) {
244                    this.objectMap.remove(key);
245                }
246            }
247        }
248    
249        /**
250         * @return boolean indicating if the backdoor is in use
251         */
252        public boolean isBackdoorInUse() {
253            return backdoorUser != null;
254        }
255    
256        /**
257         * Adds the given SessionTicket to the objectMap and returns the associated key
258         *
259         * @param ticket - SessionTicket to add
260         * @return the objectMap key for the ticket as a String
261         */
262        public String putSessionTicket(SessionTicket ticket) {
263            return addObjectWithGeneratedKey(ticket);
264        }
265    
266        /**
267         * Retrieves all SessionTicket instances currently in the UserSession#objectMap
268         *
269         * @return List<SessionTicket> contained in user session
270         */
271        public List<SessionTicket> getAllSessionTickets() {
272            List<SessionTicket> sessionTickets = new ArrayList<SessionTicket>();
273    
274            synchronized (objectMap) {
275                for (Object object : objectMap.values()) {
276                    if (object instanceof SessionTicket) {
277                        sessionTickets.add((SessionTicket) object);
278                    }
279                }
280            }
281    
282            return sessionTickets;
283        }
284    
285        /**
286         * Retrieves all SessionTicket instances currently in the UserSession#objectMap that are of a given ticket type
287         *
288         * @return List<SessionTicket> contained in user session
289         */
290        public List<SessionTicket> getAllSessionTicketsByType(String ticketTypeName) {
291            List<SessionTicket> sessionTickets = new ArrayList<SessionTicket>();
292    
293            for (SessionTicket ticket : getAllSessionTickets()) {
294                if (StringUtils.equalsIgnoreCase(ticket.getTicketTypeName(), ticketTypeName)) {
295                    sessionTickets.add(ticket);
296                }
297            }
298    
299            return sessionTickets;
300        }
301    
302        /**
303         * Determines if the UserSession contains a ticket of the given type that matches the given context. To match context
304         * the ticket must
305         * contain all the same keys at the given context and the values must be equal with the exception of case
306         *
307         * @param ticketTypeName - Name of the ticket type to match
308         * @param matchContext - Map on context parameters to match on
309         * @return true if a ticket was found in the UserSession that matches the request, false if one was not found
310         */
311        public boolean hasMatchingSessionTicket(String ticketTypeName, Map<String, String> matchContext) {
312            boolean hasTicket = false;
313    
314            for (SessionTicket ticket : getAllSessionTicketsByType(ticketTypeName)) {
315                Map<String, String> ticketContext = ticket.getTicketContext();
316    
317                boolean keySetMatch = ticketContext.keySet().equals(matchContext.keySet());
318                if (keySetMatch) {
319                    boolean valuesMatch = true;
320                    for (String contextKey : ticketContext.keySet()) {
321                        String ticketValue = ticketContext.get(contextKey);
322                        String matchValue = matchContext.get(contextKey);
323                        if (!StringUtils.equalsIgnoreCase(ticketValue, matchValue)) {
324                            valuesMatch = false;
325                        }
326                    }
327    
328                    if (valuesMatch) {
329                        hasTicket = true;
330                        break;
331                    }
332                }
333            }
334    
335            return hasTicket;
336        }
337    
338        /**
339         * retrieves an unmodifiable view of the objectMap.
340         */
341        public Map<String, Object> getObjectMap() {
342            return Collections.unmodifiableMap(this.objectMap);
343        }
344    }