• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmrs / openmrs-core / 37518343506

06 Oct 2026 07:21PM UTC coverage: 66.431% (-0.03%) from 66.459%
37518343506

push

github

dkayiwa
TRUNK-6819: Start the JobRunr job server only once OpenMRS has started (#6654)

The background job server was started while its application context was
still being created, so jobs could run before the context was ready. On
jetty:run this logged "EntityManagerFactory is closed" for a job that ran
during the context refresh after modules start, against the closed session
factory of the context being replaced, and "Task class ... not found" for a
module's job that ran after the first, core-only refresh, before modules
were loaded.

The server is now started by a SmartLifecycle once its context has finished
refreshing and stopped before the context closes. The webapp defers that
start until SchedulerService.onStartup(), which runs once modules have
started; contexts refreshed after startup start their server right away.

The webapp never closes its application context, so Context.shutdown() now
calls SchedulerService.onShutdown() first, as it did before the move to
JobRunr, and JobRunrSchedulerService stops the job server there, before the
modules and the session factory are shut down.

On 2.9.x, JobRunr 6 does not return the server in its configuration
result, so the lifecycle takes it from JobRunr, and the scheduler service
is declared in applicationContext-service.xml, so its lifecycle is set
there rather than with @Autowired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NK4kFG8qyevx9Bk9jWoEgS
Claude-Session: https://claude.ai/code/session_01PdDdZrB8Q5eMgej5FmRx4R
Claude-Session: https://claude.ai/code/session_01LZ92BDLHrn6xACczVMtSiR

33 of 37 new or added lines in 5 files covered. (89.19%)

38 existing lines in 9 files now uncovered.

25960 of 39078 relevant lines covered (66.43%)

0.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.23
/api/src/main/java/org/openmrs/api/context/UserContext.java
1
/**
2
 * This Source Code Form is subject to the terms of the Mozilla Public License,
3
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
4
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6
 *
7
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8
 * graphic logo is a trademark of OpenMRS Inc.
9
 */
10
package org.openmrs.api.context;
11

12
import org.apache.commons.lang3.StringUtils;
13
import org.openmrs.Location;
14
import org.openmrs.PrivilegeListener;
15
import org.openmrs.Role;
16
import org.openmrs.User;
17
import org.openmrs.UserSessionListener;
18
import org.openmrs.UserSessionListener.Event;
19
import org.openmrs.UserSessionListener.Status;
20
import org.openmrs.api.APIAuthenticationException;
21
import org.openmrs.api.LocationService;
22
import org.openmrs.api.cache.RolePrivilegeCache;
23
import org.openmrs.api.cache.RolePrivileges;
24
import org.openmrs.util.LocaleUtility;
25
import org.openmrs.util.OpenmrsConstants;
26
import org.openmrs.util.RoleConstants;
27
import org.slf4j.Logger;
28
import org.slf4j.LoggerFactory;
29

30
import java.io.Serializable;
31
import java.util.ArrayList;
32
import java.util.Arrays;
33
import java.util.Collections;
34
import java.util.HashSet;
35
import java.util.List;
36
import java.util.Locale;
37
import java.util.Objects;
38
import java.util.Set;
39
import java.util.concurrent.atomic.AtomicBoolean;
40

41
/**
42
 * Represents an OpenMRS <code>User Context</code> which stores the current user information. Only
43
 * one <code>User</code> may be authenticated within a UserContext at any given time. The
44
 * UserContext should not be accessed directly, but rather used through the <code>Context</code>.
45
 * This class should be kept light-weight. There is one instance of this class per user that is
46
 * logged into the system.
47
 *
48
 * @see org.openmrs.api.context.Context
49
 */
50
public class UserContext implements Serializable {
51
        
52
        private static final long serialVersionUID = -806631231941890648L;
53
        
54
        /**
55
         * Logger - shared by entire class
56
         */
57
        private static final Logger log = LoggerFactory.getLogger(UserContext.class);
1 ✔
58
        
59
        /**
60
         * Guards the one-time warning emitted when the role privilege cache component cannot be obtained
61
         * outside of a context refresh. Static so the warning is emitted once across all user contexts
62
         * rather than once per session.
63
         */
64
        private static final AtomicBoolean rolePrivilegeCacheUnavailableWarned = new AtomicBoolean(false);
1 ✔
65

66
        /**
67
         * User object containing details about the authenticated user
68
         */
69
        private User user = null;
1 ✔
70
        
71
        /**
72
         * User's permission proxies
73
         */
74
        private final List<String> proxies = Collections.synchronizedList(new ArrayList<>());
1 ✔
75
        
76
        /**
77
         * User's locale
78
         */
79
        private Locale locale = null;
1 ✔
80
        
81
        /**
82
         * Cached Role given to all authenticated users
83
         */
84
        private Role authenticatedRole = null;
1 ✔
85
        
86
        /**
87
         * Cache Role given to all users
88
         */
89
        private Role anonymousRole = null;
1 ✔
90
        
91
        /**
92
         * User's defined location
93
         */
94
        private Integer locationId;
95
        
96
        /**
97
         * The authentication scheme for this user
98
         */
99
        private final AuthenticationScheme authenticationScheme;
100
        
101
        /**
102
         * Creates a user context based on the provided auth. scheme.
103
         *
104
         * @param authenticationScheme The auth. scheme that applies for this user context.
105
         * @since 2.3.0
106
         */
107
        public UserContext(AuthenticationScheme authenticationScheme) {
1 ✔
108
                this.authenticationScheme = authenticationScheme;
1 ✔
109
        }
1 ✔
110
        
111
        /**
112
         * Authenticate user with the provided credentials. The authentication scheme must be Spring wired, see {@link Context#getAuthenticationScheme()}.
113
         *
114
         * @param credentials The credentials to use to authenticate
115
         * @return The authenticated client information
116
         * @throws ContextAuthenticationException if authentication fails
117
         * @since 2.3.0
118
         */
119
        public Authenticated authenticate(Credentials credentials)
120
                throws ContextAuthenticationException {
121
                
122
                log.debug("Authenticating client '{}' with scheme '{}'", credentials.getClientName(),
1 ✔
123
                        credentials.getAuthenticationScheme());
1 ✔
124
                
125
                Authenticated authenticated = null;
1 ✔
126
                try {
127
                        authenticated = authenticationScheme.authenticate(credentials);
1 ✔
128
                        this.user = authenticated.getUser();
1 ✔
129
                        notifyUserSessionListener(this.user, Event.LOGIN, Status.SUCCESS);
1 ✔
130
                }
131
                catch (ContextAuthenticationException e) {
1 ✔
132
                        User loggingInUser = new User();
1 ✔
133
                        loggingInUser.setUsername(credentials.getClientName());
1 ✔
134
                        notifyUserSessionListener(loggingInUser, Event.LOGIN, Status.FAIL);
1 ✔
135
                        throw e;
1 ✔
136
                }
1 ✔
137
                
138
                setUserLocation(true);
1 ✔
139
                setUserLocale(true);
1 ✔
140
                
141
                log.debug("Authenticated as: {}", this.user);
1 ✔
142
                
143
                return authenticated;
1 ✔
144
        }
145
        
146
        /**
147
         * Refresh the authenticated user object in this UserContext. This should be used when updating
148
         * information in the database about the current user and it needs to be reflecting in the
149
         * (cached) {@link #getAuthenticatedUser()} User object.
150
         *
151
         * @since 1.5
152
         */
153
        public void refreshAuthenticatedUser() {
154
                log.debug("Refreshing authenticated user");
1 ✔
155
                
156
                if (user != null) {
1 ✔
157
                        user = Context.getUserService().getUser(user.getUserId());
1 ✔
158
                        //update the stored location in the user's session
159
                        setUserLocation(false);
1 ✔
160
                        setUserLocale(false);
1 ✔
161
                }
162
        }
1 ✔
163
        
164
        /**
165
         * Change current authentication to become another user. (You can only do this if you're already
166
         * authenticated as a superuser.)
167
         *
168
         * @param systemId
169
         * @return The new user that this context has been set to. (null means no change was made)
170
         * @throws ContextAuthenticationException
171
         */
172
        public User becomeUser(String systemId) throws ContextAuthenticationException {
173
                if (!Daemon.isDaemonThread() && !Context.getAuthenticatedUser().isSuperUser()) {
1 ✔
174
                        throw new APIAuthenticationException("You must be a superuser to assume another user's identity");
×
175
                }
176
                
177
                log.debug("Turning the authenticated user into user with systemId: {}", systemId);
1 ✔
178
                
179
                User userToBecome = Context.getUserService().getUserByUsername(systemId);
1 ✔
180
                
181
                if (userToBecome == null) {
1 ✔
UNCOV
182
                        throw new ContextAuthenticationException("User not found with systemId: " + systemId);
×
183
                }
184
                
185
                // hydrate the user object
186
                if (userToBecome.getAllRoles() != null) {
1 ✔
187
                        userToBecome.getAllRoles().size();
1 ✔
188
                }
189
                
190
                if (userToBecome.getUserProperties() != null) {
1 ✔
191
                        userToBecome.getUserProperties().size();
1 ✔
192
                }
193
                
194
                if (userToBecome.getPrivileges() != null) {
1 ✔
195
                        userToBecome.getPrivileges().size();
1 ✔
196
                }
197
                
198
                this.user = userToBecome;
1 ✔
199
                
200
                //update the user's location and locale
201
                setUserLocation(false);
1 ✔
202
                setUserLocale(false);
1 ✔
203
                
204
                log.debug("Becoming user: {}", user);
1 ✔
205
                
206
                return userToBecome;
1 ✔
207
        }
208
        
209
        /**
210
         * @return "active" user who has been authenticated, otherwise <code>null</code>
211
         */
212
        public User getAuthenticatedUser() {
213
                return user;
1 ✔
214
        }
215
        
216
        /**
217
         * @return true if user has been authenticated in this UserContext
218
         */
219
        public boolean isAuthenticated() {
220
                return user != null;
1 ✔
221
        }
222
        
223
        /**
224
         * logs out the "active" (authenticated) user within this UserContext
225
         *
226
         * @see #authenticate
227
         */
228
        public void logout() {
229
                log.debug("setting user to null on logout");
1 ✔
230
                notifyUserSessionListener(user, Event.LOGOUT, Status.SUCCESS);
1 ✔
231
                user = null;
1 ✔
232
                locationId = null;
1 ✔
233
                locale = null;
1 ✔
234
                proxies.clear();
1 ✔
235
        }
1 ✔
236
        
237
        /**
238
         * Gives the given privilege to all calls to hasPrivilege. This method was visualized as being
239
         * used as follows (try/finally is important):
240
         *
241
         * <pre>
242
         * try {
243
         *   Context.addProxyPrivilege(&quot;AAA&quot;);
244
         *   Context.get*Service().methodRequiringAAAPrivilege();
245
         * }
246
         * finally {
247
         *   Context.removeProxyPrivilege(&quot;AAA&quot;);
248
         * }
249
         * </pre>
250
         *
251
         * @param privilege to give to users
252
         */
253
        public void addProxyPrivilege(String privilege) {
254
                if (privilege == null) {
1 ✔
255
                        throw new IllegalArgumentException("UserContext.addProxyPrivilege does not accept null privileges");
1 ✔
256
                }
257

258
                log.debug("Adding proxy privilege: {}", privilege);
1 ✔
259
                proxies.add(privilege);
1 ✔
260
        }
1 ✔
261
        
262
        /**
263
         * Will remove one instance of privilege from the privileges that are currently proxied
264
         *
265
         * @param privilege Privilege to remove in string form
266
         */
267
        public void removeProxyPrivilege(String privilege) {
268
                if (privilege == null) {
1 ✔
269
                        return;
1 ✔
270
                }
271

272
                log.debug("Removing privilege: {}", privilege);
1 ✔
273
                proxies.remove(privilege);
1 ✔
274
        }
1 ✔
275
        
276
        /**
277
         * Adds one or more privileges to the list of privileges that that {@link #hasPrivilege(String)} will
278
         * regard as available regardless of whether the user would otherwise have the privilege.
279
         * <p/>
280
         * This is useful for situations where a system process may need access to some piece of data that the
281
         * user would not otherwise have access to, like a GlobalProperty. <strong>This facility should not be
282
         * used to return data to the user that they otherwise would be unable to see.</strong>
283
         * <p/>
284
         * The expected usage is:
285
         * <p/>
286
         * <pre>{@code
287
         * try {
288
         *   Context.addProxyPrivilege(&quot;AAA&quot;);
289
         *   Context.get*Service().methodRequiringAAAPrivilege();
290
         * }
291
         * finally {
292
         *   Context.removeProxyPrivilege(&quot;AAA&quot;);
293
         * }}
294
         * </pre>
295
         * <p/>
296
         *
297
         * @param privileges privileges to add in string form
298
         * @see #hasPrivilege(String)
299
         * @see #removeProxyPrivilege(String...)
300
         */
301
        public void addProxyPrivilege(String... privileges) {
302
                if (privileges == null || Arrays.stream(privileges).anyMatch(Objects::isNull)) {
1 ✔
303
                        throw new IllegalArgumentException("UserContext.addProxyPrivilege does not accept null privileges");
1 ✔
304
                }
305
                
306
                for (String privilege : privileges) {
1 ✔
307
                        addProxyPrivilege(privilege);
1 ✔
308
                }
309
        }
1 ✔
310
        
311
        /**
312
         * Removes one or more privileges to the list of privileges that that {@link #hasPrivilege(String)} will
313
         * regard as available regardless of whether the user would otherwise have the privilege.
314
         * <p/>
315
         * This is the compliment for {@link #addProxyPrivilege(String...)} to clean-up the context.
316
         * <p/>
317
         *
318
         * @param privileges privileges to remove in string form
319
         * @see #hasPrivilege(String)
320
         * @see #addProxyPrivilege(String...)
321
         */
322
        public void removeProxyPrivilege(String... privileges) {
323
                if (privileges == null || Arrays.stream(privileges).allMatch(Objects::isNull)) {
1 ✔
324
                        return;
1 ✔
325
                }
326
                
327
                for (String privilege : privileges) {
1 ✔
328
                        if (privilege != null) {
1 ✔
329
                                removeProxyPrivilege(privilege);
1 ✔
330
                        }
331
                }
332
        }
1 ✔
333

334
        /**
335
         * @return true if there are any proxy privileges
336
         * @since 2.9.0
337
         */
338
        public boolean hasProxyPrivileges() {
339
                return !proxies.isEmpty();
1 ✔
340
        }
341
        
342
        /**
343
         * @param locale new locale for this context
344
         */
345
        public void setLocale(Locale locale) {
346
                this.locale = locale;
1 ✔
347
        }
1 ✔
348
        
349
        /**
350
         * @return current locale for this context
351
         */
352
        public Locale getLocale() {
353
                if (locale == null) {
1 ✔
354
                        // don't cache default locale - allows recognition of changed default at login page
355
                        return LocaleUtility.getDefaultLocale();
1 ✔
356
                }
357
                
358
                return locale;
1 ✔
359
        }
360
        
361
        /**
362
         * Gets all the roles for the (un)authenticated user. Anonymous and Authenticated roles are
363
         * appended if necessary
364
         *
365
         * @return all expanded roles for a user
366
         * @throws Exception
367
         */
368
        public Set<Role> getAllRoles() throws Exception {
369
                return getAllRoles(getAuthenticatedUser());
×
370
        }
371
        
372
        /**
373
         * Gets all the roles for a user. Anonymous and Authenticated roles are appended if necessary
374
         *
375
         * @param user
376
         * @return all expanded roles for a user
377
         * <strong>Should</strong> not fail with null user
378
         * <strong>Should</strong> add anonymous role to all users
379
         * <strong>Should</strong> add authenticated role to all authenticated users
380
         * <strong>Should</strong> return same roles as user getAllRoles method
381
         */
382
        public Set<Role> getAllRoles(User user) throws Exception {
383
                Set<Role> roles = new HashSet<>();
×
384
                
385
                // add the Anonymous Role
386
                roles.add(getAnonymousRole());
×
387
                
388
                // add the Authenticated role
389
                if (getAuthenticatedUser() != null && getAuthenticatedUser().equals(user)) {
×
390
                        roles.addAll(user.getAllRoles());
×
391
                        roles.add(getAuthenticatedRole());
×
392
                }
393
                
394
                return roles;
×
395
        }
396
        
397
        /**
398
         * Tests whether the currently authenticated user has a particular privilege
399
         *
400
         * @param privilege The privilege to check if it's available
401
         * @return true if authenticated user has given privilege
402
         * <strong>Should</strong> authorize if authenticated user has specified privilege
403
         * <strong>Should</strong> authorize if authenticated role has specified privilege
404
         * <strong>Should</strong> authorize if proxied user has specified privilege
405
         * <strong>Should</strong> authorize if anonymous user has specified privilege
406
         * <strong>Should</strong> not authorize if authenticated user does not have specified privilege
407
         * <strong>Should</strong> not authorize if authenticated role does not have specified privilege
408
         * <strong>Should</strong> not authorize if proxied user does not have specified privilege
409
         * <strong>Should</strong> not authorize if anonymous user does not have specified privilege
410
         */
411
        public boolean hasPrivilege(String privilege) {
412
                return hasPrivilege(privilege, true);
1 ✔
413
        }
414

415
        /**
416
         * Tests whether the current user has a particular privilege, optionally excluding proxy privileges.
417
         * <p>
418
         * Passing <code>false</code> resolves the privilege only from the user's roles (and the anonymous
419
         * and authenticated roles), ignoring any {@link #addProxyPrivilege(String...) proxy privileges}.
420
         * This is intended for per-resource access checks that must reflect the user's actual granted roles
421
         * and must not be satisfied by a proxy privilege added merely to invoke the surrounding service
422
         * method. Role privileges are still resolved authoritatively (from the role privilege cache),
423
         * unlike {@link User#hasPrivilege(String)} which reads a potentially stale in-memory role graph.
424
         *
425
         * @param privilege The privilege to check if it's available
426
         * @param includeProxyPrivileges whether proxy privileges may satisfy the check
427
         * @return true if the current user has the given privilege
428
         * @since 3.0.0, 2.9.0, 2.8.9
429
         */
430
        public boolean hasPrivilege(String privilege, boolean includeProxyPrivileges) {
431
                if (includeProxyPrivileges) {
1 ✔
432
                        log.debug("Checking '{}' against proxies: {}", privilege, proxies);
1 ✔
433
                        // check proxied privileges; ArrayList so we have a consistent view
434
                        for (String s : new ArrayList<>(proxies)) {
1 ✔
435
                                if (s.equals(privilege)) {
1 ✔
436
                                        notifyPrivilegeListeners(getAuthenticatedUser(), privilege, true);
1 ✔
437
                                        return true;
1 ✔
438
                                }
439
                        }
1 ✔
440
                }
441

442
                boolean hasPrivilege = resolvePrivilege(privilege);
1 ✔
443
                notifyPrivilegeListeners(getAuthenticatedUser(), privilege, hasPrivilege);
1 ✔
444
                return hasPrivilege;
1 ✔
445
        }
446

447
        /**
448
         * Resolves whether the current user (authenticated or anonymous) holds the given privilege by
449
         * consulting the per-role privilege cache instead of recursively re-expanding the role graph on
450
         * every call. Proxy privileges are handled separately by {@link #hasPrivilege(String)}.
451
         *
452
         * @param privilege the privilege to check
453
         * @return true if the privilege is granted
454
         */
455
        private boolean resolvePrivilege(String privilege) {
456
                RolePrivilegeCache cache = getRolePrivilegeCache();
1 ✔
457

458
                // if a user has logged in, check their privileges
459
                if (isAuthenticated()) {
1 ✔
460
                        // All authenticated users have the "" (empty) privilege, matching User.hasPrivilege.
461
                        if (StringUtils.isEmpty(privilege)) {
1 ✔
462
                                return true;
1 ✔
463
                        }
464

465
                        User authenticatedUser = getAuthenticatedUser();
1 ✔
466
                        if (authenticatedUser.getRoles() != null) {
1 ✔
467
                                for (Role role : authenticatedUser.getRoles()) {
1 ✔
468
                                        if (roleGrants(cache, role, privilege)) {
1 ✔
469
                                                return true;
1 ✔
470
                                        }
471
                                }
1 ✔
472
                        }
473

474
                        if (roleGrants(cache, getAuthenticatedRole(), privilege)) {
1 ✔
475
                                return true;
1 ✔
476
                        }
477
                }
478

479
                return roleGrants(cache, getAnonymousRole(), privilege);
1 ✔
480
        }
481

482
        /**
483
         * Tests whether a single role (and its inherited closure) grants the given privilege, using the
484
         * cached flattening when available and falling back to a direct computation otherwise.
485
         *
486
         * @param cache the role privilege cache, or <code>null</code> if unavailable
487
         * @param role the directly assigned role to test
488
         * @param privilege the privilege to check
489
         * @return true if the role grants superuser status or contains the privilege
490
         */
491
        private boolean roleGrants(RolePrivilegeCache cache, Role role, String privilege) {
492
                RolePrivileges rolePrivileges = (cache != null) ? cache.getRolePrivileges(role)
1 ✔
493
                        : RolePrivilegeCache.computeRolePrivileges(role);
1 ✔
494
                return rolePrivileges.grantsSuperuser() || rolePrivileges.containsPrivilege(privilege);
1 ✔
495
        }
496

497
        /**
498
         * Looks up the {@link RolePrivilegeCache} component, or returns <code>null</code> so callers fall
499
         * back to direct computation. Absence during a context refresh is expected (logged at debug);
500
         * absence otherwise is a misconfiguration that silently drops every check to the uncached path, so
501
         * it is warned once.
502
         *
503
         * @return the cache component, or <code>null</code> if unavailable
504
         */
505
        private RolePrivilegeCache getRolePrivilegeCache() {
506
                try {
507
                        return Context.getRegisteredComponent("rolePrivilegeCache", RolePrivilegeCache.class);
1 ✔
508
                } catch (Exception e) {
×
509
                        if (Context.isRefreshingContext()) {
×
510
                                log.debug("Role privilege cache is unavailable while the context is refreshing; "
×
511
                                        + "computing role privileges directly",
512
                                    e);
513
                        } else if (rolePrivilegeCacheUnavailableWarned.compareAndSet(false, true)) {
×
514
                                log.warn("Could not obtain the rolePrivilegeCache component; privilege checks will recompute role "
×
515
                                        + "privileges on every call until this is resolved",
516
                                    e);
517
                        }
518
                        return null;
×
519
                }
520
        }
521
        
522
        /**
523
         * Convenience method to get the Role in the system designed to be given to all users
524
         *
525
         * @return Role
526
         * <strong>Should</strong> fail if database doesn't contain anonymous role
527
         */
528
        private Role getAnonymousRole() {
529
                if (anonymousRole != null) {
1 ✔
530
                        return anonymousRole;
1 ✔
531
                }
532
                
533
                anonymousRole = Context.getUserService().getRole(RoleConstants.ANONYMOUS);
1 ✔
534
                if (anonymousRole == null) {
1 ✔
535
                        throw new RuntimeException(
×
536
                                "Database out of sync with code: " + RoleConstants.ANONYMOUS + " role does not exist");
537
                }
538
                
539
                return anonymousRole;
1 ✔
540
        }
541
        
542
        /**
543
         * Convenience method to get the Role in the system designed to be given to all users that have
544
         * authenticated in some manner
545
         *
546
         * @return Role
547
         * <strong>Should</strong> fail if database doesn't contain authenticated role
548
         */
549
        private Role getAuthenticatedRole() {
550
                if (authenticatedRole != null) {
1 ✔
551
                        return authenticatedRole;
1 ✔
552
                }
553
                
554
                authenticatedRole = Context.getUserService().getRole(RoleConstants.AUTHENTICATED);
1 ✔
555
                if (authenticatedRole == null) {
1 ✔
556
                        throw new RuntimeException("Database out of sync with code: " + RoleConstants.AUTHENTICATED
×
557
                                + " role does not exist");
558
                }
559
                
560
                return authenticatedRole;
1 ✔
561
        }
562
        
563
        /**
564
         * @return locationId for this user context if any is set
565
         * @since 1.10
566
         */
567
        public Integer getLocationId() {
568
                return locationId;
×
569
        }
570
        
571
        /**
572
         * @param locationId locationId to set
573
         * @since 1.10
574
         */
575
        public void setLocationId(Integer locationId) {
576
                this.locationId = locationId;
1 ✔
577
        }
1 ✔
578
        
579
        /**
580
         * @return current location for this user context if any is set
581
         * @since 1.9
582
         */
583
        public Location getLocation() {
584
                if (locationId == null) {
1 ✔
585
                        return null;
1 ✔
586
                }
587
                return Context.getLocationService().getLocation(locationId);
1 ✔
588
        }
589
        
590
        /**
591
         * @param location the location to set to
592
         * @since 1.9
593
         */
594
        public void setLocation(Location location) {
595
                if (location != null) {
1 ✔
596
                        this.locationId = location.getLocationId();
1 ✔
597
                }
598
        }
1 ✔
599
        
600
        /**
601
         * Convenience method that sets the default location of the currently authenticated user using
602
         * the value of the user's default location property
603
         */
604
        private void setUserLocation(boolean useDefault) {
605
                // location should be null if no user is logged in
606
                if (this.user == null) {
1 ✔
607
                        this.locationId = null;
×
608
                        return;
×
609
                }
610
                
611
                // intended to be when the user initially authenticates
612
                if (this.locationId == null && useDefault) {
1 ✔
613
                        this.locationId = getDefaultLocationId(this.user);
1 ✔
614
                }
615
        }
1 ✔
616

617
        /**
618
         * Convenience method that sets the default locale used by the currently authenticated user, using
619
         * the value of the user's default local property
620
         */
621
        private void setUserLocale(boolean useDefault) {
622
                // local should be null if no user is logged in
623
                if (this.user == null) {
1 ✔
624
                        this.locale = null;
×
625
                        return;
×
626
                }
627

628
                // intended to be when the user initially authenticates
629
                if (user.getUserProperties().containsKey("defaultLocale")) {
1 ✔
630
                        String localeString = user.getUserProperty("defaultLocale");
1 ✔
631
                        locale = LocaleUtility.fromSpecification(localeString);
1 ✔
632
                }
633

634
                if (locale == null && useDefault) {
1 ✔
635
                        locale = LocaleUtility.getDefaultLocale();
1 ✔
636
                }
637

638
        }
1 ✔
639
        
640
        protected Integer getDefaultLocationId(User user) {
641
                String defaultLocation = user.getUserProperty(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCATION);
1 ✔
642
                if (StringUtils.isNotBlank(defaultLocation)) {
1 ✔
643
                        LocationService ls = Context.getLocationService();
1 ✔
644
                        //only go ahead if it has actually changed OR if wasn't set before
645
                        try {
646
                                int defaultId = Integer.parseInt(defaultLocation);
1 ✔
647
                                if (this.locationId == null || this.locationId != defaultId) {
1 ✔
648
                                        // validate that the id is a valid id
649
                                        if (ls.getLocation(defaultId) != null) {
1 ✔
650
                                                return defaultId;
1 ✔
651
                                        }
652
                                }
653
                        }
654
                        catch (NumberFormatException ignored) {
1 ✔
655
                        }
1 ✔
656

657
                        Location possibleLocation = ls.getLocationByUuid(defaultLocation);
1 ✔
658

659
                        if (possibleLocation != null && (this.locationId == null || !this.locationId.equals(possibleLocation.getId()))) {
1 ✔
660
                                return possibleLocation.getId();
1 ✔
661
                        }
662

663
                        log.warn("The default location for user '{}' is set to '{}', which is not a valid location",
1 ✔
664
                                user.getUsername(), defaultLocation);
1 ✔
665
                }
666
                
667
                return null;
1 ✔
668
        }
669
        
670
        /**
671
         * Notifies privilege listener beans about any privilege check.
672
         * <p>
673
         * It is called by {@link UserContext#hasPrivilege(java.lang.String)}.
674
         *
675
         * @param user         the authenticated user or <code>null</code> if not authenticated
676
         * @param privilege    the checked privilege
677
         * @param hasPrivilege <code>true</code> if the authenticated user has the required privilege or
678
         *                     if it is a proxy privilege
679
         * @see PrivilegeListener
680
         * @since 1.8.4, 1.9.1, 1.10
681
         */
682
        private void notifyPrivilegeListeners(User user, String privilege, boolean hasPrivilege) {
683
                for (PrivilegeListener privilegeListener : Context.getRegisteredComponents(PrivilegeListener.class)) {
1 ✔
684
                        try {
685
                                privilegeListener.privilegeChecked(user, privilege, hasPrivilege);
1 ✔
686
                        }
687
                        catch (Exception e) {
×
688
                                log.error("Privilege listener has failed", e);
×
689
                        }
1 ✔
690
                }
1 ✔
691
        }
1 ✔
692
        
693
        private void notifyUserSessionListener(User user, Event event, Status status) {
694
                for (UserSessionListener userSessionListener : Context.getRegisteredComponents(UserSessionListener.class)) {
1 ✔
695
                        userSessionListener.loggedInOrOut(user, event, status);
1 ✔
696
                }
1 ✔
697
        }
1 ✔
698
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc