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

openmrs / openmrs-core / 29867170267

21 Jul 2026 05:53PM UTC coverage: 64.101% (+0.1%) from 63.994%
29867170267

push

github

ibacher
Require Get Patient Programs privilege for by-uuid program lookups (#6318)

* Require Get Patient Programs privilege for by-uuid program lookups

ProgramWorkflowService.getPatientProgramByUuid and getPatientStateByUuid
had no @Authorized annotation. Because AuthorizationAdvice only enforces
when a method declares a privilege, both fell through with no check at
all, letting any authenticated user read program-enrollment PHI by uuid.
Every other read on the service, including getPatientProgram(Integer),
already requires Get Patient Programs; annotate both by-uuid methods to
match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Require Get Patient Programs privilege for patient program attribute lookup

getPatientProgramAttributeByAttributeName returns patient program
attribute values keyed by patient id, yet lacked an @Authorized check.
Its two sibling attribute readers (getPatientProgramAttributeByUuid and
getPatientProgramByAttributeNameAndValue) already guard on Get Patient
Programs, so this was a missed PHI read of the same class addressed by
GHSA-gqf9-38mg-wgqg.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TpKNfxm3QjwUBDePEtFm2H

* Add authorization tests pinning the by-uuid program privilege checks

The existing tests for getPatientProgramByUuid, getPatientStateByUuid and
getPatientProgramAttributeByAttributeName run as the privileged superuser
and assert only find/null behavior, so they pass even if the
@Authorized(Get Patient Programs) checks added by this branch are removed.
getPatientProgramAttributeByAttributeName had no test at all.

Add a test per method that logs out (dropping Get Patient Programs) and
asserts APIAuthenticationException is thrown for otherwise-valid arguments,
mirroring AuthorizationAdviceTest and AdministrationServiceTest. These fail
if any of the three annotations is removed, pinning the fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cl... (continued)

24273 of 37867 relevant lines covered (64.1%)

0.64 hits per line

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

66.67
/web/src/main/java/org/openmrs/web/filter/util/FilterUtil.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.web.filter.util;
11

12
import java.sql.Connection;
13
import java.sql.PreparedStatement;
14
import java.sql.ResultSet;
15
import java.sql.SQLException;
16

17
import org.apache.commons.lang3.StringUtils;
18
import org.openmrs.util.DatabaseUpdater;
19
import org.openmrs.util.OpenmrsConstants;
20
import org.slf4j.Logger;
21
import org.slf4j.LoggerFactory;
22

23
/**
24
 * This class contains convenient methods for storing/retrieving locale parameters into/from DB as
25
 * admin's user property and as default locale property for OpenMRS system
26
 */
27
public class FilterUtil {
28
        
29
        private FilterUtil() {
30
        }
31
        
32
        private static final Logger log = LoggerFactory.getLogger(FilterUtil.class);
1✔
33
        
34
        private static final String DATABASE_CLOSING_ERROR = "Error while closing the database";
35
        
36
        public static final String LOCALE_ATTRIBUTE = "locale";
37
        
38
        public static final String REMEMBER_ATTRIBUTE = "remember";
39
        
40
        public static final String ADMIN_USERNAME = "admin";
41
        
42
        /**
43
         * Tries to retrieve location parameter. First this method makes an attempt to load locale
44
         * parameter as user's property. And next, if user's property is empty it tries to retrieve
45
         * default system locale (i.e system global property). If it also is empty it uses default value
46
         * for system locale
47
         *
48
         * @param username the name of the administrative user whose default locale property will be
49
         *            restored
50
         * @return string with stored location parameter or default OpenMRS locale property's value
51
         */
52
        public static String restoreLocale(String username) {
53
                String currentLocale = null;
1✔
54
                if (StringUtils.isNotBlank(username)) {
1✔
55
                        PreparedStatement statement = null;
1✔
56
                        Connection connection = null;
1✔
57
                        ResultSet results = null;
1✔
58
                        try {
59
                                connection = DatabaseUpdater.getConnection();
1✔
60
                                
61
                                // first we should try to get locale parameter as user's property
62
                                Integer userId = getUserIdByName(username, connection);
1✔
63
                                
64
                                if (userId != null) {
1✔
65
                                        String select = "select property_value from user_property where user_id = ? and property = ?";
1✔
66
                                        statement = connection.prepareStatement(select);
1✔
67
                                        statement.setInt(1, userId);
1✔
68
                                        statement.setString(2, OpenmrsConstants.USER_PROPERTY_DEFAULT_LOCALE);
1✔
69
                                        if (statement.execute()) {
1✔
70
                                                results = statement.getResultSet();
1✔
71
                                                if (results.next()) {
1✔
72
                                                        currentLocale = results.getString(1);
1✔
73
                                                }
74
                                        }
75
                                }
76
                                
77
                                // if locale is still null we should try to retrieve system locale global property's value
78
                                if (currentLocale == null) {
1✔
79
                                        currentLocale = readSystemDefaultLocale(connection);
×
80
                                }
81
                        }
82
                        catch (Exception e) {
×
83
                                log.error("Error while retriving locale property", e);
×
84
                        }
85
                        finally {
86
                                try {
87
                                        if (statement != null) {
1✔
88
                                                statement.close();
1✔
89
                                        }
90
                                }
91
                                catch (SQLException e) {
×
92
                                        log.warn("Error while closing statement");
×
93
                                }
1✔
94
                                
95
                                if (connection != null) {
1✔
96
                                        try {
97
                                                connection.close();
1✔
98
                                        }
99
                                        catch (SQLException e) {
×
100
                                                log.debug(DATABASE_CLOSING_ERROR, e);
×
101
                                        }
1✔
102
                                }
103
                                
104
                                if (results != null) {
1✔
105
                                        try {
106
                                                results.close();
1✔
107
                                        }
108
                                        catch (SQLException e) {
×
109
                                                log.warn("Error while closing ResultSet", e);
×
110
                                        }
1✔
111
                                }
112
                        }
113
                }
114
                // if locale is still null we just simply using default locale value (i.e. en_GB)
115
                if (currentLocale == null) {
1✔
116
                        currentLocale = OpenmrsConstants.GLOBAL_PROPERTY_DEFAULT_LOCALE_DEFAULT_VALUE;
×
117
                }
118
                
119
                return currentLocale;
1✔
120
        }
121
        
122
        /**
123
         * This method uses passed in connection to load system default locale. If connection is passed
124
         * as null it creates separate connection that should be closed before return from method
125
         *
126
         * @param connection (optional) the jdbc connection to be used for extracting default locale
127
         * @return the string that contains system default locale or null
128
         */
129
        public static String readSystemDefaultLocale(Connection connection) {
130
                String systemDefaultLocale = null;
×
131
                boolean needToCloseConection = false;
×
132
                try {
133
                        if (connection == null) {
×
134
                                connection = DatabaseUpdater.getConnection();
×
135
                                needToCloseConection = true;
×
136
                        }
137
                        String select = "select property_value from global_property where property = ?";
×
138
                        PreparedStatement statement = connection.prepareStatement(select);
×
139
                        statement.setString(1, OpenmrsConstants.GLOBAL_PROPERTY_DEFAULT_LOCALE);
×
140
                        if (statement.execute()) {
×
141
                                ResultSet results = statement.getResultSet();
×
142
                                if (results.next()) {
×
143
                                        systemDefaultLocale = results.getString(1);
×
144
                                }
145
                        }
146
                }
147
                catch (Exception e) {
×
148
                        log.error("Error while retrieving system default locale", e);
×
149
                }
150
                finally {
151
                        if (needToCloseConection && connection != null) {
×
152
                                try {
153
                                        connection.close();
×
154
                                }
155
                                catch (SQLException e) {
×
156
                                        log.debug(DATABASE_CLOSING_ERROR, e);
×
157
                                }
×
158
                        }
159
                }
160
                return systemDefaultLocale;
×
161
        }
162
        
163
        /**
164
         * Stores selected by user locale into DB as admin's user property and as system default locale
165
         *
166
         * @param locale the selected by user language
167
         * @return true if locale was stored successfully
168
         */
169
        public static boolean storeLocale(String locale) {
170
                if (StringUtils.isNotBlank(locale)) {
1✔
171
                        
172
                        Connection connection = null;
1✔
173
                        Integer userId = null;
1✔
174
                        try {
175
                                connection = DatabaseUpdater.getConnection();
1✔
176
                                
177
                                // first we should try to get admin user id
178
                                userId = getUserIdByName(ADMIN_USERNAME, connection);
1✔
179
                                
180
                                // first we are saving locale as administrative user's property
181
                                if (userId != null) {
1✔
182
                                        String insert = "insert into user_property (user_id, property, property_value) values (?, 'defaultLocale', ?)";
1✔
183
                                        PreparedStatement statement = null;
1✔
184
                                        try {
185
                                                statement = connection.prepareStatement(insert);
1✔
186
                                                statement.setInt(1, userId);
1✔
187
                                                statement.setString(2, locale);
1✔
188
                                                if (statement.executeUpdate() != 1) {
1✔
189
                                                        log.warn("Unable to save user locale as admin property.");
×
190
                                                }
191
                                        }
192
                                        finally {
193
                                                if (statement != null) {
1✔
194
                                                        try {
195
                                                                statement.close();
1✔
196
                                                        }
197
                                                        catch (Exception statementCloseEx) {
×
198
                                                                log.error("Failed to quietly close Statement", statementCloseEx);
×
199
                                                        }
1✔
200
                                                }
201
                                        }
202
                                        
203
                                }
204
                                
205
                                // and the second step is to save locale as system default locale global property
206
                                String update = "update global_property set property_value = ? where property = ? ";
1✔
207
                                PreparedStatement statement = null;
1✔
208
                                try {
209
                                        statement = connection.prepareStatement(update);
1✔
210
                                        statement.setString(1, locale);
1✔
211
                                        statement.setString(2, OpenmrsConstants.GLOBAL_PROPERTY_DEFAULT_LOCALE);
1✔
212
                                        if (statement.executeUpdate() != 1) {
1✔
213
                                                log.warn("Unable to set system default locale property.");
1✔
214
                                        }
215
                                }
216
                                finally {
217
                                        if (statement != null) {
1✔
218
                                                try {
219
                                                        statement.close();
1✔
220
                                                }
221
                                                catch (Exception statementCloseEx) {
×
222
                                                        log.error("Failed to quietly close Statement", statementCloseEx);
×
223
                                                }
1✔
224
                                        }
225
                                }
226
                        }
227
                        catch (Exception e) {
1✔
228
                                log.warn("Locale " + locale + " could not be set for user with id " + userId + " .", e);
1✔
229
                                return false;
1✔
230
                        }
231
                        finally {
232
                                if (connection != null) {
1✔
233
                                        try {
234
                                                connection.close();
1✔
235
                                        }
236
                                        catch (SQLException e) {
×
237
                                                log.debug(DATABASE_CLOSING_ERROR, e);
×
238
                                        }
1✔
239
                                }
240
                        }
241
                        return true;
1✔
242
                }
243
                return false;
×
244
        }
245
        
246
        /**
247
         * This is a utility method that can be used for retrieving user id by given user name and sql
248
         * connection
249
         *
250
         * @param userNameOrSystemId the name of user
251
         * @param connection the java sql connection to use
252
         * @return not null id of given user in case of success or null otherwise
253
         * @throws SQLException
254
         */
255
        public static Integer getUserIdByName(String userNameOrSystemId, Connection connection) throws SQLException {
256
                
257
                String select = "select user_id from users where system_id = ? or username = ?";
1✔
258
                PreparedStatement statement = connection.prepareStatement(select);
1✔
259
                statement.setString(1, userNameOrSystemId);
1✔
260
                statement.setString(2, userNameOrSystemId);
1✔
261
                Integer userId = null;
1✔
262
                if (statement.execute()) {
1✔
263
                        ResultSet results = statement.getResultSet();
1✔
264
                        if (results.next()) {
1✔
265
                                userId = results.getInt(1);
1✔
266
                        }
267
                }
268
                return userId;
1✔
269
        }
270
        
271
        /**
272
         * Gets the value of a global Property as a string from the database using sql, this method is
273
         * useful when you want to get a value of a global property before the application context has
274
         * been setup
275
         *
276
         * @param globalPropertyName the name of the global property
277
         * @return the global property value
278
         */
279
        public static String getGlobalPropertyValue(String globalPropertyName) {
280
                String propertyValue = null;
1✔
281
                Connection connection = null;
1✔
282
                
283
                try {
284
                        connection = DatabaseUpdater.getConnection();
1✔
285
                        try (PreparedStatement statement = connection
1✔
286
                                .prepareStatement("select property_value from global_property where property = ?")) {
1✔
287
                                statement.setString(1, globalPropertyName);
1✔
288
                                try (ResultSet resultSet = statement.executeQuery()) {
1✔
289
                                        if (resultSet.next()) {
1✔
290
                                                propertyValue = resultSet.getString(1);
1✔
291
                                        }
292
                                }
293
                        }
294
                }
295
                catch (Exception e) {
×
296
                        log.error("Error while retrieving value for global property:" + globalPropertyName, e);
×
297
                }
298
                finally {
299
                        if (connection != null) {
1✔
300
                                try {
301
                                        connection.close();
1✔
302
                                }
303
                                catch (SQLException e) {
×
304
                                        log.debug("Error while closing the database connection", e);
×
305
                                }
1✔
306
                        }
307
                }
308
                
309
                return propertyValue;
1✔
310
        }
311
}
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