1 /**
2 * Copyright 2005-2016 The Kuali Foundation
3 *
4 * Licensed under the Educational Community License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.opensource.org/licenses/ecl2.php
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.kuali.rice.krad;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.kuali.rice.core.api.config.property.ConfigContext;
20 import org.kuali.rice.kim.api.identity.Person;
21 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
22 import org.kuali.rice.krad.util.SessionTicket;
23
24 import java.io.Serializable;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.atomic.AtomicInteger;
31
32 /**
33 * Holds info about the User Session
34 *
35 * @author Kuali Rice Team (rice.collab@kuali.org)
36 */
37 public class UserSession implements Serializable {
38 private static final long serialVersionUID = 4532616762540067557L;
39
40 private static final Object NULL_VALUE = new Object();
41
42 private Person person;
43 private Person backdoorUser;
44 private AtomicInteger nextObjectKey;
45 private ConcurrentHashMap<String, Object> objectMap;
46 private String kualiSessionId;
47
48 /**
49 * Returns the session id. The session id is a unique identifier for the session.
50 * @return the kualiSessionId
51 */
52 public String getKualiSessionId() {
53 return this.kualiSessionId;
54 }
55
56 /**
57 * Sets the session id.
58 * @param kualiSessionId the kualiSessionId to set
59 */
60 public void setKualiSessionId(String kualiSessionId) {
61 this.kualiSessionId = kualiSessionId;
62 }
63
64 /**
65 * Creates a user session for the principal specified in the parameter.
66 * Take in a netid, and construct the user from that.
67 *
68 * @param principalName
69 */
70 public UserSession(String principalName) {
71 initPerson(principalName);
72 this.nextObjectKey = new AtomicInteger(0);
73 this.objectMap = new ConcurrentHashMap<String, Object>();
74 }
75
76 /**
77 * Loads the Person object from KIM. Factored out for testability.
78 * @param principalName the principalName
79 */
80 protected void initPerson(String principalName) {
81 this.person = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(principalName);
82 if (this.person == null) {
83 throw new IllegalArgumentException(
84 "Failed to locate a principal with principal name '" + principalName + "'");
85 }
86 }
87
88 /**
89 * Returns the id of the current user.
90 * @return the principalId of the current user in the system, backdoor principalId if backdoor is set
91 */
92 public String getPrincipalId() {
93 if (backdoorUser != null) {
94 return backdoorUser.getPrincipalId();
95 }
96 return person.getPrincipalId();
97 }
98
99 /**
100 * Returns the name of the current user.
101 * @return the principalName of the current user in the system, backdoor principalName if backdoor is set
102 */
103 public String getPrincipalName() {
104 if (backdoorUser != null) {
105 return backdoorUser.getPrincipalName();
106 }
107 return person.getPrincipalName();
108 }
109
110 /**
111 * Returns who is logged in. If the backdoor is in use, this will return the network id of the person that is
112 * standing in as the backdoor user.
113 *
114 * @return String
115 */
116 public String getLoggedInUserPrincipalName() {
117 if (person != null) {
118 return person.getPrincipalName();
119 }
120 return "";
121 }
122
123 /**
124 * Returns who is logged in. If the backdoor is in use, this will return the network id of the person that is
125 * standing in as the backdoor user.
126 *
127 * @return String
128 */
129 public String getLoggedInUserPrincipalId() {
130 if (person != null) {
131 return person.getPrincipalId();
132 }
133 return "";
134 }
135
136 /**
137 * Returns a Person object for the current user.
138 * @return the KualiUser which is the current user in the system, backdoor if backdoor is set
139 */
140 public Person getPerson() {
141 if (backdoorUser != null) {
142 return backdoorUser;
143 }
144 return person;
145 }
146
147 /**
148 * Returns the actual current user even if the backdoor is in use.
149 * @return the KualiUser which is the current user in the system
150 */
151 public Person getActualPerson() {
152 return person;
153 }
154
155 /**
156 * override the current user in the system by setting the backdoor networkId, which is useful when dealing with
157 * routing or other reasons why you would need to assume an identity in the system
158 *
159 * @param principalName
160 */
161 public void setBackdoorUser(String principalName) {
162 // only allow backdoor in non-production environments
163 if (!isProductionEnvironment()) {
164 this.backdoorUser = KimApiServiceLocator.getPersonService().getPersonByPrincipalName(principalName);
165 }
166 }
167
168 /**
169 * Helper method to check if we are in a production environment.
170 *
171 * @return boolean indicating if we are in a production environment
172 */
173 public boolean isProductionEnvironment() {
174 return ConfigContext.getCurrentContextConfig().isProductionEnvironment();
175 }
176 //KULRICE-12287:Add a banner to Rice screens to show which environment you are in
177 public String getCurrentEnvironment() {
178 return ConfigContext.getCurrentContextConfig().getEnvironment();
179 }
180
181 public boolean isDisplayTestBanner() {
182 boolean isProd = this.isProductionEnvironment();
183 boolean isBannerEnabled = ConfigContext.getCurrentContextConfig().getBooleanProperty("test.banner.enabled", false);
184 return !isProd && isBannerEnabled;
185 }
186
187 /**
188 * clear the backdoor user
189 */
190 public void clearBackdoorUser() {
191 this.backdoorUser = null;
192 }
193
194 /**
195 * allows adding an arbitrary object to the session and returns a string key that can be used to later access this
196 * object from
197 * the session using the retrieveObject method in this class. This allows for a prefix to be placed in front of the
198 * incremented key. So if the prefix is "searchResults" and the nextObjectKey (local int that holds the key value)
199 * is 2 then
200 * the new key will be "searchResults3". "searchResults3" will be returned from the method.
201 *
202 * @param object
203 */
204 public String addObjectWithGeneratedKey(Serializable object, String keyPrefix) {
205 String objectKey = keyPrefix + nextObjectKey.incrementAndGet();
206 addObject(objectKey, object);
207 return objectKey;
208 }
209
210 /**
211 * allows adding an arbitrary object to the session and returns a string key that can be used to later access this
212 * object from
213 * the session using the retrieveObject method in this class. The key is generated from an integer and incremented
214 * for every
215 * object added. So the first object added with have a key of "1". This key will be returned from the method.
216 *
217 * @param object
218 */
219 public String addObjectWithGeneratedKey(Object object) {
220 String objectKey = nextObjectKey.incrementAndGet() + "";
221 addObject(objectKey, object);
222 return objectKey;
223 }
224
225 /**
226 * Allows adding an arbitrary object to the session with static a string key that can be used to later access this
227 * object from the session using the retrieveObject method in this class.
228 *
229 * @param key the mapping key
230 * @param object the object to store
231 */
232 public void addObject(String key, Object object) {
233 if (object != null) {
234 objectMap.put(key, object);
235 } else {
236 objectMap.put(key, NULL_VALUE);
237 }
238 }
239
240 /**
241 * Either allows adding an arbitrary object to the session based on a key (if there is not currently an object
242 * associated with that key) or returns the object already associated with that key.
243 *
244 * @see ConcurrentHashMap#putIfAbsent(Object, Object)
245 *
246 * @param key the mapping key
247 * @param object the object to store
248 */
249 public void addObjectIfAbsent(String key, Object object) {
250 if (object != null) {
251 objectMap.putIfAbsent(key, object);
252 } else {
253 objectMap.putIfAbsent(key, NULL_VALUE);
254 }
255 }
256
257 /**
258 * Allows for fetching an object that has been put into the userSession based on the key that would have been
259 * returned when adding the object.
260 *
261 * @param objectKey the mapping key
262 *
263 * @return the stored object
264 */
265 public Object retrieveObject(String objectKey) {
266 if (objectKey == null) {
267 return null;
268 }
269 Object object = objectMap.get(objectKey);
270
271 if (!NULL_VALUE.equals(object)) {
272 return object;
273 } else {
274 return null;
275 }
276 }
277
278 /**
279 * allows for removal of an object from session that has been put into the userSession based on the key that would
280 * have been
281 * assigned
282 *
283 * @param objectKey
284 */
285 public void removeObject(String objectKey) {
286 if (objectKey != null) {
287 this.objectMap.remove(objectKey);
288 }
289 }
290
291 /**
292 * allows for removal of an object from session that has been put into the userSession based on a key that starts
293 * with the given
294 * prefix
295 */
296 public void removeObjectsByPrefix(String objectKeyPrefix) {
297 synchronized (objectMap) {
298 List<String> removeKeys = new ArrayList<String>();
299 for (String key : objectMap.keySet()) {
300 if (key.startsWith(objectKeyPrefix)) {
301 removeKeys.add(key);
302 }
303 }
304
305 for (String key : removeKeys) {
306 this.objectMap.remove(key);
307 }
308 }
309 }
310
311 /**
312 * @return boolean indicating if the backdoor is in use
313 */
314 public boolean isBackdoorInUse() {
315 return backdoorUser != null;
316 }
317
318 /**
319 * Adds the given SessionTicket to the objectMap and returns the associated key
320 *
321 * @param ticket - SessionTicket to add
322 * @return the objectMap key for the ticket as a String
323 */
324 public String putSessionTicket(SessionTicket ticket) {
325 return addObjectWithGeneratedKey(ticket);
326 }
327
328 /**
329 * Retrieves all SessionTicket instances currently in the UserSession#objectMap
330 *
331 * @return List<SessionTicket> contained in user session
332 */
333 public List<SessionTicket> getAllSessionTickets() {
334 List<SessionTicket> sessionTickets = new ArrayList<SessionTicket>();
335
336 synchronized (objectMap) {
337 for (Object object : objectMap.values()) {
338 if (object instanceof SessionTicket) {
339 sessionTickets.add((SessionTicket) object);
340 }
341 }
342 }
343
344 return sessionTickets;
345 }
346
347 /**
348 * Retrieves all SessionTicket instances currently in the UserSession#objectMap that are of a given ticket type
349 *
350 * @return List<SessionTicket> contained in user session
351 */
352 public List<SessionTicket> getAllSessionTicketsByType(String ticketTypeName) {
353 List<SessionTicket> sessionTickets = new ArrayList<SessionTicket>();
354
355 for (SessionTicket ticket : getAllSessionTickets()) {
356 if (StringUtils.equalsIgnoreCase(ticket.getTicketTypeName(), ticketTypeName)) {
357 sessionTickets.add(ticket);
358 }
359 }
360
361 return sessionTickets;
362 }
363
364 /**
365 * Determines if the UserSession contains a ticket of the given type that matches the given context. To match context
366 * the ticket must
367 * contain all the same keys at the given context and the values must be equal with the exception of case
368 *
369 * @param ticketTypeName - Name of the ticket type to match
370 * @param matchContext - Map on context parameters to match on
371 * @return true if a ticket was found in the UserSession that matches the request, false if one was not found
372 */
373 public boolean hasMatchingSessionTicket(String ticketTypeName, Map<String, String> matchContext) {
374 boolean hasTicket = false;
375
376 for (SessionTicket ticket : getAllSessionTicketsByType(ticketTypeName)) {
377 Map<String, String> ticketContext = ticket.getTicketContext();
378
379 boolean keySetMatch = ticketContext.keySet().equals(matchContext.keySet());
380 if (keySetMatch) {
381 boolean valuesMatch = true;
382 for (String contextKey : ticketContext.keySet()) {
383 String ticketValue = ticketContext.get(contextKey);
384 String matchValue = matchContext.get(contextKey);
385 if (!StringUtils.equalsIgnoreCase(ticketValue, matchValue)) {
386 valuesMatch = false;
387 }
388 }
389
390 if (valuesMatch) {
391 hasTicket = true;
392 break;
393 }
394 }
395 }
396
397 return hasTicket;
398 }
399
400 /**
401 * retrieves an unmodifiable view of the objectMap.
402 */
403 public Map<String, Object> getObjectMap() {
404 return Collections.unmodifiableMap(this.objectMap);
405 }
406
407 /**
408 * clear the objectMap
409 */
410 public void clearObjectMap() {
411 this.objectMap = new ConcurrentHashMap<String, Object>();
412 }
413 }