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

openmrs / openmrs-core / 18321298560

07 Oct 2025 05:43PM UTC coverage: 65.263% (-0.001%) from 65.264%
18321298560

push

github

web-flow
TRUNK-6435: Password resets must go through service API (#5379)

58 of 76 new or added lines in 3 files covered. (76.32%)

6 existing lines in 5 files now uncovered.

23689 of 36298 relevant lines covered (65.26%)

0.65 hits per line

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

86.85
/api/src/main/java/org/openmrs/api/impl/UserServiceImpl.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.impl;
11

12
import java.util.ArrayList;
13
import java.util.Collection;
14
import java.util.Date;
15
import java.util.HashSet;
16
import java.util.List;
17
import java.util.Locale;
18
import java.util.Map;
19
import java.util.Objects;
20
import java.util.Optional;
21
import java.util.Properties;
22
import java.util.Set;
23
import java.util.stream.Collectors;
24

25
import org.apache.commons.lang.RandomStringUtils;
26
import org.apache.commons.lang3.StringUtils;
27
import org.openmrs.Person;
28
import org.openmrs.Privilege;
29
import org.openmrs.Role;
30
import org.openmrs.User;
31
import org.openmrs.annotation.Authorized;
32
import org.openmrs.annotation.Logging;
33
import org.openmrs.api.APIException;
34
import org.openmrs.api.AdministrationService;
35
import org.openmrs.api.CannotDeleteRoleWithChildrenException;
36
import org.openmrs.api.InvalidActivationKeyException;
37
import org.openmrs.api.UserService;
38
import org.openmrs.api.context.Context;
39
import org.openmrs.api.db.DAOException;
40
import org.openmrs.api.db.LoginCredential;
41
import org.openmrs.api.db.UserDAO;
42
import org.openmrs.messagesource.MessageSourceService;
43
import org.openmrs.notification.MessageException;
44
import org.openmrs.patient.impl.LuhnIdentifierValidator;
45
import org.openmrs.util.LocaleUtility;
46
import org.openmrs.util.OpenmrsConstants;
47
import org.openmrs.util.OpenmrsUtil;
48
import org.openmrs.util.PrivilegeConstants;
49
import org.openmrs.util.RoleConstants;
50
import org.openmrs.util.Security;
51
import org.slf4j.Logger;
52
import org.slf4j.LoggerFactory;
53
import org.springframework.cache.annotation.CacheEvict;
54
import org.springframework.transaction.annotation.Transactional;
55

56
/**
57
 * Default implementation of the user service. This class should not be used on its own. The current
58
 * OpenMRS implementation should be fetched from the Context
59
 *
60
 * @see org.openmrs.api.UserService
61
 * @see org.openmrs.api.context.Context
62
 */
63
@Transactional
64
public class UserServiceImpl extends BaseOpenmrsService implements UserService {
65
        
66
        private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
1✔
67
        
68
        protected UserDAO dao;
69
        
70
        private static final int MAX_VALID_TIME = 12 * 60 * 60 * 1000; //Period of 12 hours
71
        
72
        private static final int MIN_VALID_TIME = 60 * 1000; //Period of 1 minute
73
        
74
        private static final int DEFAULT_VALID_TIME = 10 * 60 * 1000; //Default time of 10 minute
75
        
76
        public UserServiceImpl() {
1✔
77
        }
1✔
78
        
79
        public void setUserDAO(UserDAO dao) {
80
                this.dao = dao;
1✔
81
        }
1✔
82
        
83
        /**
84
         * @return the validTime for which the password reset activation key will be valid
85
         */
86
        private int getValidTime() {
87
                String validTimeGp = Context.getAdministrationService()
1✔
88
                        .getGlobalProperty(OpenmrsConstants.GP_PASSWORD_RESET_VALIDTIME);
1✔
89
                final int validTime = StringUtils.isBlank(validTimeGp) ? DEFAULT_VALID_TIME : Integer.parseInt(validTimeGp);
1✔
90
                //if valid time is less that a minute or greater than 12hrs reset valid time to 1 minutes else set it to the required time.
91
                return (validTime < MIN_VALID_TIME) || (validTime > MAX_VALID_TIME) ? DEFAULT_VALID_TIME : validTime;
1✔
92
        }
93
        
94
        /**
95
         * @see org.openmrs.api.UserService#createUser(org.openmrs.User, java.lang.String)
96
         */
97
        @Override
98
        public User createUser(User user, String password) throws APIException {
99
                if (user.getUserId() != null) {
1✔
100
                        throw new APIException("This method can be used for only creating new users");
1✔
101
                }
102
                
103
                Context.requirePrivilege(PrivilegeConstants.ADD_USERS);
1✔
104
                
105
                checkPrivileges(user);
1✔
106
                
107
                // if a password wasn't supplied, throw an error
108
                if (password == null || password.isEmpty()) {
1✔
109
                        throw new APIException("User.creating.password.required", (Object[]) null);
×
110
                }
111
                
112
                if (hasDuplicateUsername(user)) {
1✔
113
                        throw new DAOException("Username " + user.getUsername() + " or system id " + user.getSystemId()
1✔
114
                                + " is already in use.");
115
                }
116
                
117
                // TODO Check required fields for user!!
118
                OpenmrsUtil.validatePassword(user.getUsername(), password, user.getSystemId());
1✔
119
                
120
                return dao.saveUser(user, password);
1✔
121
        }
122
        
123
        /**
124
         * @see org.openmrs.api.UserService#getUser(java.lang.Integer)
125
         */
126
        @Override
127
        @Transactional(readOnly = true)
128
        public User getUser(Integer userId) throws APIException {
129
                return dao.getUser(userId);
1✔
130
        }
131
        
132
        /**
133
         * @see org.openmrs.api.UserService#getUserByUsername(java.lang.String)
134
         */
135
        @Override
136
        @Transactional(readOnly = true)
137
        public User getUserByUsername(String username) throws APIException {
138
                return dao.getUserByUsername(username);
1✔
139
        }
140
        
141
        /**
142
         * @see org.openmrs.api.UserService#hasDuplicateUsername(org.openmrs.User)
143
         */
144
        @Override
145
        @Transactional(readOnly = true)
146
        public boolean hasDuplicateUsername(User user) throws APIException {
147
                return dao.hasDuplicateUsername(user.getUsername(), user.getSystemId(), user.getUserId());
1✔
148
        }
149
        
150
        /**
151
         * @see org.openmrs.api.UserService#getUsersByRole(org.openmrs.Role)
152
         */
153
        @Override
154
        @Transactional(readOnly = true)
155
        public List<User> getUsersByRole(Role role) throws APIException {
156
                List<Role> roles = new ArrayList<>();
1✔
157
                roles.add(role);
1✔
158
                
159
                return Context.getUserService().getUsers(null, roles, false);
1✔
160
        }
161
        
162
        /**
163
         * @see org.openmrs.api.UserService#saveUser(org.openmrs.User)
164
         */
165
        @Override
166
        @CacheEvict(value = "userSearchLocales", allEntries = true)
167
        public User saveUser(User user) throws APIException {
168
                if (user.getUserId() == null) {
1✔
169
                        throw new APIException("This method can be called only to update existing users");
×
170
                }
171
                
172
                Context.requirePrivilege(PrivilegeConstants.EDIT_USERS);
1✔
173
                
174
                checkPrivileges(user);
1✔
175
                
176
                if (hasDuplicateUsername(user)) {
1✔
177
                        throw new DAOException("Username " + user.getUsername() + " or system id " + user.getSystemId()
×
178
                                + " is already in use.");
179
                }
180
                
181
                return dao.saveUser(user, null);
1✔
182
        }
183
        
184
        public User voidUser(User user, String reason) throws APIException {
185
                return Context.getUserService().retireUser(user, reason);
×
186
        }
187
        
188
        /**
189
         * @see org.openmrs.api.UserService#retireUser(org.openmrs.User, java.lang.String)
190
         */
191
        @Override
192
        public User retireUser(User user, String reason) throws APIException {
193
                user.setRetired(true);
1✔
194
                user.setRetireReason(reason);
1✔
195
                user.setRetiredBy(Context.getAuthenticatedUser());
1✔
196
                user.setDateRetired(new Date());
1✔
197
                
198
                return saveUser(user);
1✔
199
        }
200
        
201
        public User unvoidUser(User user) throws APIException {
202
                return Context.getUserService().unretireUser(user);
×
203
        }
204
        
205
        /**
206
         * @see org.openmrs.api.UserService#unretireUser(org.openmrs.User)
207
         */
208
        @Override
209
        public User unretireUser(User user) throws APIException {
210
                user.setRetired(false);
1✔
211
                user.setRetireReason(null);
1✔
212
                user.setRetiredBy(null);
1✔
213
                user.setDateRetired(null);
1✔
214
                
215
                return saveUser(user);
1✔
216
        }
217
        
218
        /**
219
         * @see org.openmrs.api.UserService#getAllUsers()
220
         */
221
        @Override
222
        @Transactional(readOnly = true)
223
        public List<User> getAllUsers() throws APIException {
224
                return dao.getAllUsers();
1✔
225
        }
226
        
227
        /**
228
         * @see org.openmrs.api.UserService#getAllPrivileges()
229
         */
230
        @Override
231
        @Transactional(readOnly = true)
232
        public List<Privilege> getAllPrivileges() throws APIException {
233
                return dao.getAllPrivileges();
1✔
234
        }
235
        
236
        /**
237
         * @see org.openmrs.api.UserService#getPrivilege(java.lang.String)
238
         */
239
        @Override
240
        @Transactional(readOnly = true)
241
        public Privilege getPrivilege(String p) throws APIException {
242
                return dao.getPrivilege(p);
1✔
243
        }
244
        
245
        /**
246
         * @see org.openmrs.api.UserService#purgePrivilege(org.openmrs.Privilege)
247
         */
248
        @Override
249
        public void purgePrivilege(Privilege privilege) throws APIException {
250
                if (OpenmrsUtil.getCorePrivileges().keySet().contains(privilege.getPrivilege())) {
1✔
251
                        throw new APIException("Privilege.cannot.delete.core", (Object[]) null);
1✔
252
                }
253
                
254
                dao.deletePrivilege(privilege);
1✔
255
        }
1✔
256
        
257
        /**
258
         * @see org.openmrs.api.UserService#savePrivilege(org.openmrs.Privilege)
259
         */
260
        @Override
261
        public Privilege savePrivilege(Privilege privilege) throws APIException {
262
                return dao.savePrivilege(privilege);
1✔
263
        }
264
        
265
        /**
266
         * @see org.openmrs.api.UserService#getAllRoles()
267
         */
268
        @Override
269
        @Transactional(readOnly = true)
270
        public List<Role> getAllRoles() throws APIException {
271
                return dao.getAllRoles();
1✔
272
        }
273
        
274
        /**
275
         * @see org.openmrs.api.UserService#getRole(java.lang.String)
276
         */
277
        @Override
278
        @Transactional(readOnly = true)
279
        public Role getRole(String r) throws APIException {
280
                return dao.getRole(r);
1✔
281
        }
282
        
283
        /**
284
         * @see org.openmrs.api.UserService#purgeRole(org.openmrs.Role)
285
         */
286
        @Override
287
        public void purgeRole(Role role) throws APIException {
288
                if (role == null || role.getRole() == null) {
1✔
289
                        return;
1✔
290
                }
291
                
292
                if (OpenmrsUtil.getCoreRoles().keySet().contains(role.getRole())) {
1✔
293
                        throw new APIException("Role.cannot.delete.core", (Object[]) null);
1✔
294
                }
295
                
296
                if (role.hasChildRoles()) {
1✔
297
                        throw new CannotDeleteRoleWithChildrenException();
1✔
298
                }
299
                
300
                dao.deleteRole(role);
1✔
301
        }
1✔
302
        
303
        /**
304
         * @see org.openmrs.api.UserService#saveRole(org.openmrs.Role)
305
         */
306
        @Override
307
        public Role saveRole(Role role) throws APIException {
308
                // make sure one of the parents of this role isn't itself...this would
309
                // cause an infinite loop
310
                if (role.getAllParentRoles().contains(role)) {
1✔
311
                        throw new APIException("Role.cannot.inherit.descendant", (Object[]) null);
1✔
312
                }
313
                
314
                checkPrivileges(role);
1✔
315
                
316
                return dao.saveRole(role);
1✔
317
        }
318
        
319
        /**
320
         * @see org.openmrs.api.UserService#changePassword(java.lang.String, java.lang.String)
321
         */
322
        @Override
323
        public void changePassword(String oldPassword, String newPassword) throws APIException {
324
                User user = Context.getAuthenticatedUser();
1✔
325
                changePassword(user, oldPassword, newPassword);
1✔
326
        }
1✔
327
        
328
        /**
329
         * @see org.openmrs.api.UserService#changeHashedPassword(User, String, String)
330
         */
331
        @Override
332
        public void changeHashedPassword(User user, String hashedPassword, String salt) throws APIException {
333
                dao.changeHashedPassword(user, hashedPassword, salt);
1✔
334
        }
1✔
335
        
336
        /**
337
         * @see org.openmrs.api.UserService#changeQuestionAnswer(User, String, String)
338
         */
339
        @Override
340
        public void changeQuestionAnswer(User u, String question, String answer) throws APIException {
341
                dao.changeQuestionAnswer(u, question, answer);
1✔
342
        }
1✔
343
        
344
        /**
345
         * @see org.openmrs.api.UserService#changeQuestionAnswer(java.lang.String, java.lang.String,
346
         * java.lang.String)
347
         */
348
        @Override
349
        public void changeQuestionAnswer(String pw, String q, String a) {
350
                dao.changeQuestionAnswer(pw, q, a);
1✔
351
        }
1✔
352
        
353
        /**
354
         * @see org.openmrs.api.UserService#isSecretAnswer(org.openmrs.User, java.lang.String)
355
         */
356
        @Override
357
        @Transactional(readOnly = true)
358
        public boolean isSecretAnswer(User u, String answer) {
359
                return dao.isSecretAnswer(u, answer);
1✔
360
        }
361
        
362
        /**
363
         * @see org.openmrs.api.UserService#getUsersByName(java.lang.String, java.lang.String, boolean)
364
         */
365
        @Override
366
        @Transactional(readOnly = true)
367
        public List<User> getUsersByName(String givenName, String familyName, boolean includeVoided) throws APIException {
368
                return dao.getUsersByName(givenName, familyName, includeVoided);
1✔
369
        }
370
        
371
        /**
372
         * @see org.openmrs.api.UserService#getUsersByPerson(org.openmrs.Person, boolean)
373
         */
374
        @Override
375
        @Transactional(readOnly = true)
376
        public List<User> getUsersByPerson(Person person, boolean includeRetired) throws APIException {
377
                return dao.getUsersByPerson(person, includeRetired);
1✔
378
        }
379
        
380
        /**
381
         * @see org.openmrs.api.UserService#getUsers(java.lang.String, java.util.List, boolean)
382
         */
383
        @Override
384
        @Transactional(readOnly = true)
385
        public List<User> getUsers(String nameSearch, List<Role> roles, boolean includeVoided) throws APIException {
386
                return Context.getUserService().getUsers(nameSearch, roles, includeVoided, null, null);
1✔
387
        }
388
        
389
        /**
390
         * Convenience method to check if the authenticated user has all privileges they are giving out
391
         *
392
         * @param user user that has privileges
393
         */
394
        private void checkPrivileges(User user) {
395
                List<String> requiredPrivs = user.getAllRoles().stream().peek(this::checkSuperUserPrivilege)
1✔
396
                        .map(Role::getPrivileges).filter(Objects::nonNull).flatMap(Collection::stream)
1✔
397
                        .map(Privilege::getPrivilege).filter(p -> !Context.hasPrivilege(p)).sorted().collect(Collectors.toList());
1✔
398
                if (requiredPrivs.size() == 1) {
1✔
399
                        throw new APIException("User.you.must.have.privilege", new Object[] { requiredPrivs.get(0) });
1✔
400
                } else if (requiredPrivs.size() > 1) {
1✔
401
                        throw new APIException("User.you.must.have.privileges", new Object[] { String.join(", ", requiredPrivs) });
1✔
402
                }
403
        }
1✔
404
        
405
        private void checkSuperUserPrivilege(Role r) {
406
                if (r.getRole().equals(RoleConstants.SUPERUSER)
1✔
407
                        && !Context.hasPrivilege(PrivilegeConstants.ASSIGN_SYSTEM_DEVELOPER_ROLE)) {
1✔
408
                        throw new APIException("User.you.must.have.role", new Object[] { RoleConstants.SUPERUSER });
1✔
409
                }
410
        }
1✔
411
        
412
        /**
413
         * @see org.openmrs.api.UserService#setUserProperty(User, String, String)
414
         */
415
        @Override
416
        public User setUserProperty(User user, String key, String value) {
417
                if (user != null) {
1✔
418
                        if (!Context.hasPrivilege(PrivilegeConstants.EDIT_USERS) && !user.equals(Context.getAuthenticatedUser())) {
1✔
419
                                throw new APIException("you.are.not.authorized.change.properties", new Object[] { user.getUserId() });
×
420
                        }
421
                        
422
                        user.setUserProperty(key, value);
1✔
423
                        try {
424
                                Context.addProxyPrivilege(PrivilegeConstants.EDIT_USERS);
1✔
425
                                Context.getUserService().saveUser(user);
1✔
426
                        }
427
                        finally {
428
                                Context.removeProxyPrivilege(PrivilegeConstants.EDIT_USERS);
1✔
429
                        }
430
                }
431
                
432
                return user;
1✔
433
        }
434
        
435
        /**
436
         * @see org.openmrs.api.UserService#removeUserProperty(org.openmrs.User, java.lang.String)
437
         */
438
        @Override
439
        public User removeUserProperty(User user, String key) {
440
                if (user != null) {
1✔
441
                        
442
                        // if the current user isn't allowed to edit users and
443
                        // the user being edited is not the current user, throw an
444
                        // exception
445
                        if (!Context.hasPrivilege(PrivilegeConstants.EDIT_USERS) && !user.equals(Context.getAuthenticatedUser())) {
1✔
446
                                throw new APIException("you.are.not.authorized.change.properties", new Object[] { user.getUserId() });
×
447
                        }
448
                        
449
                        user.removeUserProperty(key);
1✔
450
                        
451
                        try {
452
                                Context.addProxyPrivilege(PrivilegeConstants.EDIT_USERS);
1✔
453
                                Context.getUserService().saveUser(user);
1✔
454
                        }
455
                        finally {
456
                                Context.removeProxyPrivilege(PrivilegeConstants.EDIT_USERS);
1✔
457
                        }
458
                }
459
                
460
                return user;
1✔
461
        }
462
        
463
        /**
464
         * Generates system ids based on the following algorithm scheme: user_id-check digit
465
         *
466
         * @see org.openmrs.api.UserService#generateSystemId()
467
         */
468
        @Override
469
        @Transactional(readOnly = true)
470
        public String generateSystemId() {
471
                // Hardcoding Luhn algorithm since all existing openmrs user ids have
472
                // had check digits generated this way.
473
                LuhnIdentifierValidator liv = new LuhnIdentifierValidator();
1✔
474
                
475
                String systemId;
476
                Integer offset = 0;
1✔
477
                do {
478
                        // generate and increment the system id if necessary
479
                        Integer generatedId = dao.generateSystemId() + offset++;
1✔
480
                        
481
                        systemId = generatedId.toString();
1✔
482
                        
483
                        try {
484
                                systemId = liv.getValidIdentifier(systemId);
1✔
485
                        }
486
                        catch (Exception e) {
×
487
                                log.error("error getting check digit", e);
×
488
                                return systemId;
×
489
                        }
1✔
490
                        
491
                        // loop until we find a system id that no one has 
492
                } while (dao.hasDuplicateUsername(null, systemId, null));
1✔
493
                
494
                return systemId;
1✔
495
        }
496
        
497
        /**
498
         * @see org.openmrs.api.UserService#purgeUser(org.openmrs.User)
499
         */
500
        @Override
501
        public void purgeUser(User user) throws APIException {
502
                dao.deleteUser(user);
1✔
503
        }
1✔
504
        
505
        /**
506
         * @see org.openmrs.api.UserService#purgeUser(org.openmrs.User, boolean)
507
         */
508
        @Override
509
        public void purgeUser(User user, boolean cascade) throws APIException {
510
                if (cascade) {
1✔
511
                        throw new APIException("cascade.do.not.think", (Object[]) null);
1✔
512
                }
513
                
514
                dao.deleteUser(user);
1✔
515
        }
1✔
516
        
517
        /**
518
         * Convenience method to check if the authenticated user has all privileges they are giving out
519
         * to the new role
520
         *
521
         * @param role
522
         */
523
        private void checkPrivileges(Role role) {
524
                Optional.ofNullable(role.getPrivileges())
1✔
525
                        .map(p -> p.stream().filter(pr -> !Context.hasPrivilege(pr.getPrivilege())).map(Privilege::getPrivilege)
1✔
526
                                .distinct().collect(Collectors.joining(", ")))
1✔
527
                        .ifPresent(missing -> {
1✔
528
                                if (StringUtils.isNotBlank(missing)) {
1✔
529
                                        throw new APIException("Role.you.must.have.privileges", new Object[] { missing });
1✔
530
                                }
531
                        });
1✔
532
        }
1✔
533
        
534
        /**
535
         * @see org.openmrs.api.UserService#getPrivilegeByUuid(java.lang.String)
536
         */
537
        @Override
538
        @Transactional(readOnly = true)
539
        public Privilege getPrivilegeByUuid(String uuid) throws APIException {
540
                return dao.getPrivilegeByUuid(uuid);
1✔
541
        }
542
        
543
        /**
544
         * @see org.openmrs.api.UserService#getRoleByUuid(java.lang.String)
545
         */
546
        @Override
547
        @Transactional(readOnly = true)
548
        public Role getRoleByUuid(String uuid) throws APIException {
549
                return dao.getRoleByUuid(uuid);
1✔
550
        }
551
        
552
        /**
553
         * @see org.openmrs.api.UserService#getUserByUuid(java.lang.String)
554
         */
555
        @Override
556
        @Transactional(readOnly = true)
557
        public User getUserByUuid(String uuid) throws APIException {
558
                return dao.getUserByUuid(uuid);
1✔
559
        }
560
        
561
        /**
562
         * @see UserService#getCountOfUsers(String, List, boolean)
563
         */
564
        @Override
565
        @Transactional(readOnly = true)
566
        public Integer getCountOfUsers(String name, List<Role> roles, boolean includeRetired) {
567
                if (name != null) {
×
568
                        name = StringUtils.replace(name, ", ", " ");
×
569
                }
570
                
571
                // if the authenticated role is in the list of searched roles, then all
572
                // persons should be searched
573
                Role authRole = getRole(RoleConstants.AUTHENTICATED);
×
574
                if (roles.contains(authRole)) {
×
575
                        return dao.getCountOfUsers(name, new ArrayList<>(), includeRetired);
×
576
                }
577
                
578
                return dao.getCountOfUsers(name, roles, includeRetired);
×
579
        }
580
        
581
        /**
582
         * @see UserService#getUsers(String, List, boolean, Integer, Integer)
583
         */
584
        @Override
585
        @Transactional(readOnly = true)
586
        public List<User> getUsers(String name, List<Role> roles, boolean includeRetired, Integer start, Integer length)
587
                throws APIException {
588
                if (name != null) {
1✔
589
                        name = StringUtils.replace(name, ", ", " ");
1✔
590
                }
591
                
592
                if (roles == null) {
1✔
593
                        roles = new ArrayList<>();
1✔
594
                }
595
                
596
                // if the authenticated role is in the list of searched roles, then all
597
                // persons should be searched
598
                Role authRole = getRole(RoleConstants.AUTHENTICATED);
1✔
599
                if (roles.contains(authRole)) {
1✔
600
                        return dao.getUsers(name, new ArrayList<>(), includeRetired, start, length);
×
601
                }
602
                
603
                // add the requested roles and all child roles for consideration
604
                Set<Role> allRoles = new HashSet<>();
1✔
605
                for (Role r : roles) {
1✔
606
                        allRoles.add(r);
1✔
607
                        allRoles.addAll(r.getAllChildRoles());
1✔
608
                }
1✔
609
                
610
                return dao.getUsers(name, new ArrayList<>(allRoles), includeRetired, start, length);
1✔
611
        }
612
        
613
        @Override
614
        public User saveUserProperty(String key, String value) {
615
                User user = Context.getAuthenticatedUser();
1✔
616
                if (user == null) {
1✔
617
                        throw new APIException("no.authenticated.user.found", (Object[]) null);
×
618
                }
619
                user.setUserProperty(key, value);
1✔
620
                return dao.saveUser(user, null);
1✔
621
        }
622
        
623
        @Override
624
        public User saveUserProperties(Map<String, String> properties) {
625
                User user = Context.getAuthenticatedUser();
1✔
626
                if (user == null) {
1✔
627
                        throw new APIException("no.authenticated.user.found", (Object[]) null);
×
628
                }
629
                user.getUserProperties().clear();
1✔
630
                for (Map.Entry<String, String> entry : properties.entrySet()) {
1✔
631
                        user.setUserProperty(entry.getKey(), entry.getValue());
1✔
632
                }
1✔
633
                return dao.saveUser(user, null);
1✔
634
        }
635
        
636
        /**
637
         * @see UserService#changePassword(User, String, String)
638
         */
639
        @Override
640
        @Authorized(PrivilegeConstants.EDIT_USER_PASSWORDS)
641
        @Logging(ignoredArgumentIndexes = { 1, 2 })
642
        public void changePassword(User user, String oldPassword, String newPassword) throws APIException {
643
                if (user.getUserId() == null) {
1✔
644
                        throw new APIException("user.must.exist", (Object[]) null);
1✔
645
                }
646
                
647
                if (oldPassword == null) {
1✔
648
                        if (!Context.hasPrivilege(PrivilegeConstants.EDIT_USER_PASSWORDS)) {
1✔
649
                                throw new APIException("null.old.password.privilege.required", (Object[]) null);
×
650
                        }
651
                } else if (!dao.getLoginCredential(user).checkPassword(oldPassword)) {
1✔
652
                        throw new APIException("old.password.not.correct", (Object[]) null);
×
653
                        
654
                } else if (oldPassword.equals(newPassword)) {
1✔
655
                        throw new APIException("new.password.equal.to.old", (Object[]) null);
×
656
                }
657
                
658
                if (("admin".equals(user.getSystemId()) || "admin".equals(user.getUsername())) && Boolean.parseBoolean(
1✔
659
                        Context.getRuntimeProperties().getProperty(ADMIN_PASSWORD_LOCKED_PROPERTY, "false"))) {
1✔
660
                        throw new APIException("admin.password.is.locked");
1✔
661
                }
662
                
663
                updatePassword(user, newPassword);
1✔
664
        }
1✔
665
        
666
        @Override
667
        @Logging(ignoredArgumentIndexes = { 1 })
668
        public void changePassword(User user, String newPassword) {
NEW
669
                Context.getUserService().changePassword(user, null, newPassword);
×
670
        }
×
671
        
672
        @Override
673
        @Logging(ignoredArgumentIndexes = { 1, 2 })
674
        public void changePasswordUsingSecretAnswer(String secretAnswer, String pw) throws APIException {
675
                User user = Context.getAuthenticatedUser();
1✔
676
                
677
                if (!isSecretAnswer(user, secretAnswer)) {
1✔
678
                        throw new APIException("secret.answer.not.correct", (Object[]) null);
1✔
679
                }
680
                
681
                updatePassword(user, pw);
1✔
682
        }
1✔
683

684
        private void updatePassword(User user, String newPassword) {
685
                OpenmrsUtil.validatePassword(user.getUsername(), newPassword, user.getSystemId());
1✔
686
                dao.changePassword(user, newPassword);
1✔
687
        }
1✔
688
        
689
        @Override
690
        public String getSecretQuestion(User user) throws APIException {
691
                if (user.getUserId() != null) {
×
692
                        LoginCredential loginCredential = dao.getLoginCredential(user);
×
693
                        return loginCredential.getSecretQuestion();
×
694
                } else {
695
                        return null;
×
696
                }
697
        }
698
        
699
        /**
700
         * @see org.openmrs.api.UserService#getUserByUsernameOrEmail(java.lang.String)
701
         */
702
        @Override
703
        @Transactional(readOnly = true)
704
        public User getUserByUsernameOrEmail(String usernameOrEmail) {
705
                if (StringUtils.isNotBlank(usernameOrEmail)) {
1✔
706
                        User user = dao.getUserByEmail(usernameOrEmail);
1✔
707
                        if (user == null) {
1✔
708
                                return getUserByUsername(usernameOrEmail);
1✔
709
                        }
710
                        return user;
1✔
711
                }
712
                throw new APIException("error.usernameOrEmail.notNullOrBlank", (Object[]) null);
1✔
713
        }
714
        
715
        /**
716
         * @see org.openmrs.api.UserService#getUserByActivationKey(java.lang.String)
717
         */
718
        @Override
719
        @Transactional(readOnly = true)
720
        public User getUserByActivationKey(String activationKey) {
721
                LoginCredential loginCred = dao.getLoginCredentialByActivationKey(activationKey);
1✔
722
                if (loginCred != null) {
1✔
723
                        String[] credTokens = loginCred.getActivationKey().split(":");
1✔
724
                        if (System.currentTimeMillis() <= Long.parseLong(credTokens[1])) {
1✔
725
                                return getUser(loginCred.getUserId());
1✔
726
                        }
727
                }
728
                return null;
1✔
729
        }
730
        
731
        /**
732
         * @throws APIException
733
         * @throws MessageException
734
         * @see org.openmrs.api.UserService#setUserActivationKey(org.openmrs.User)
735
         */
736
        @Override
737
        public User setUserActivationKey(User user) throws MessageException {
738
                String token = RandomStringUtils.randomAlphanumeric(20);
1✔
739
                long time = System.currentTimeMillis() + getValidTime();
1✔
740
                String hashedKey = Security.encodeString(token);
1✔
741
                String activationKey = hashedKey + ":" + time;
1✔
742
                LoginCredential credentials = dao.getLoginCredential(user);
1✔
743
                credentials.setActivationKey(activationKey);
1✔
744
                dao.setUserActivationKey(credentials);
1✔
745
                
746
                MessageSourceService messages = Context.getMessageSourceService();
1✔
747
                AdministrationService adminService = Context.getAdministrationService();
1✔
748
                Locale locale = getDefaultLocaleForUser(user);
1✔
749
                
750
                //                Delete this method call when removing {@link OpenmrsConstants#GP_HOST_URL}
751
                copyHostURLGlobalPropertyToPasswordResetGlobalProperty(adminService);
1✔
752
                
753
                String link = adminService.getGlobalProperty(OpenmrsConstants.GP_PASSWORD_RESET_URL)
1✔
754
                        .replace("{activationKey}", token);
1✔
755
                
756
                Properties mailProperties = Context.getMailProperties();
1✔
757
                
758
                String sender = mailProperties.getProperty("mail.from");
1✔
759
                
760
                String subject = messages.getMessage("mail.passwordreset.subject", null, locale);
1✔
761
                
762
                String msg = messages.getMessage("mail.passwordreset.content", null, locale)
1✔
763
                        .replace("{name}", user.getUsername())
1✔
764
                        .replace("{link}", link)
1✔
765
                        .replace("{time}", String.valueOf(getValidTime() / 60000));
1✔
766
                
767
                Context.getMessageService().sendMessage(user.getEmail(), sender, subject, msg);
×
768
                
769
                return user;
×
770
        }
771
        
772
        /**
773
         * Delete this method when deleting {@link OpenmrsConstants#GP_HOST_URL}
774
         */
775
        private void copyHostURLGlobalPropertyToPasswordResetGlobalProperty(AdministrationService adminService) {
776
                String hostURLGP = adminService.getGlobalProperty(OpenmrsConstants.GP_HOST_URL);
1✔
777
                String passwordResetGP = adminService.getGlobalProperty(OpenmrsConstants.GP_PASSWORD_RESET_URL);
1✔
778
                if (StringUtils.isNotBlank(hostURLGP) && StringUtils.isBlank(passwordResetGP)) {
1✔
779
                        adminService.setGlobalProperty(OpenmrsConstants.GP_PASSWORD_RESET_URL, hostURLGP);
×
780
                }
781
        }
1✔
782
        
783
        /**
784
         * @see UserService#getDefaultLocaleForUser(User)
785
         */
786
        @Override
787
        public Locale getDefaultLocaleForUser(User user) {
788
                Locale locale = null;
1✔
789
                if (user != null) {
1✔
790
                        try {
791
                                String preferredLocale = user.getUserProperty(OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCALE);
1✔
792
                                if (StringUtils.isNotBlank(preferredLocale)) {
1✔
793
                                        locale = LocaleUtility.fromSpecification(preferredLocale);
1✔
794
                                }
795
                        }
796
                        catch (Exception e) {
×
797
                                log.warn("Unable to parse user locale into a Locale", e);
×
798
                        }
1✔
799
                }
800
                if (locale == null) {
1✔
801
                        locale = Context.getLocale();
1✔
802
                }
803
                return locale;
1✔
804
        }
805
        
806
        /**
807
         * @see org.openmrs.api.UserService#changePasswordUsingActivationKey(String, String);
808
         */
809
        @Override
810
        public void changePasswordUsingActivationKey(String activationKey, String newPassword) {
811
                User user = getUserByActivationKey(activationKey);
1✔
812
                if (user == null) {
1✔
813
                        throw new InvalidActivationKeyException("activation.key.not.correct");
1✔
814
                }
815
                
816
                updatePassword(user, newPassword);
1✔
817
        }
1✔
818

819
        /**
820
         * @see org.openmrs.api.UserService#getLastLoginTime(User)
821
         */
822
        public String getLastLoginTime(User user) {
823
                return dao.getLastLoginTime(user);
1✔
824
        }
825
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc