|
9 | 9 |
|
10 | 10 | package com.mirth.connect.server.migration; |
11 | 11 |
|
| 12 | +import java.security.SecureRandom; |
12 | 13 | import java.sql.Connection; |
13 | 14 | import java.sql.PreparedStatement; |
14 | 15 | import java.sql.ResultSet; |
|
30 | 31 | import org.apache.logging.log4j.LogManager; |
31 | 32 | import org.apache.logging.log4j.Logger; |
32 | 33 |
|
| 34 | +import com.mirth.connect.client.core.PropertiesConfigurationUtil; |
33 | 35 | import com.mirth.connect.client.core.Version; |
34 | 36 | import com.mirth.connect.model.Channel; |
35 | 37 | import com.mirth.connect.model.ExportClearable; |
|
38 | 40 | import com.mirth.connect.model.codetemplates.CodeTemplateLibrary; |
39 | 41 | import com.mirth.connect.model.converters.ObjectXMLSerializer; |
40 | 42 | import com.mirth.connect.model.util.MigrationException; |
| 43 | +import com.mirth.connect.server.Mirth; |
| 44 | +import com.mirth.connect.server.controllers.ConfigurationController; |
| 45 | +import com.mirth.connect.server.controllers.ControllerFactory; |
41 | 46 | import com.mirth.connect.server.util.DatabaseUtil; |
42 | 47 |
|
43 | 48 | public class ServerMigrator extends Migrator { |
| 49 | + private static final String INITIAL_ADMIN_USERNAME = "admin"; |
| 50 | + private static final String INITIAL_ADMIN_PASSWORD_PROPERTY = "server.initialadminpassword"; |
| 51 | + private static final int GENERATED_PASSWORD_LENGTH = 20; |
| 52 | + |
44 | 53 | private Logger logger = LogManager.getLogger(getClass()); |
45 | 54 |
|
46 | 55 | public ServerMigrator() { |
@@ -250,25 +259,71 @@ private void initDatabase(Connection connection) throws MigrationException { |
250 | 259 | if (!DatabaseUtil.tableExists(connection, "CONFIGURATION")) { |
251 | 260 | executeScript("/" + getDatabaseType() + "/" + getDatabaseType() + "-database.sql"); |
252 | 261 |
|
253 | | - /* |
254 | | - * We must update the password date for the initial user. Previously we let the database |
255 | | - * set this via CURRENT_TIMESTAMP, however this could create problems if the database is |
256 | | - * running on a separate machine in a different timezone. (MIRTH-2902) |
257 | | - */ |
258 | | - PreparedStatement statement = null; |
| 262 | + initializeAdminPassword(); |
259 | 263 |
|
260 | | - try { |
261 | | - statement = getConnection().prepareStatement("UPDATE PERSON_PASSWORD SET PASSWORD_DATE = ?"); |
262 | | - statement.setTimestamp(1, new Timestamp(System.currentTimeMillis())); |
263 | | - statement.executeUpdate(); |
264 | | - } catch (SQLException e) { |
265 | | - throw new MigrationException(e); |
266 | | - } finally { |
267 | | - DbUtils.closeQuietly(statement); |
| 264 | + updateVersion(Version.getLatest()); |
| 265 | + } |
| 266 | + } |
| 267 | + |
| 268 | + /** |
| 269 | + * Sets the password for the initial administrator account. The password is taken from the |
| 270 | + * server.initialadminpassword property if it is set, otherwise a random password is generated |
| 271 | + * and logged once so that it can be used to log in for the first time. |
| 272 | + * |
| 273 | + * The password date is set here rather than letting the database default it via |
| 274 | + * CURRENT_TIMESTAMP, since that could create problems if the database is running on a separate |
| 275 | + * machine in a different timezone. (MIRTH-2902) |
| 276 | + */ |
| 277 | + private void initializeAdminPassword() throws MigrationException { |
| 278 | + ConfigurationController configurationController = ControllerFactory.getFactory().createConfigurationController(); |
| 279 | + PropertiesConfiguration mirthProperties = PropertiesConfigurationUtil.create(); |
| 280 | + configurationController.updatePropertiesConfiguration(mirthProperties); |
| 281 | + // Log through the central Mirth.class logger to hit log4j filter configs |
| 282 | + Logger startupLogger = LogManager.getLogger(Mirth.class); |
| 283 | + |
| 284 | + String password = mirthProperties.getString(INITIAL_ADMIN_PASSWORD_PROPERTY); |
| 285 | + if (StringUtils.isBlank(password)) { |
| 286 | + password = generatePassword(); |
| 287 | + startupLogger.warn( |
| 288 | + "Generated an initial password for the \"{}\" user: {}\n" |
| 289 | + + "This password is not logged again, so store it somewhere safe " |
| 290 | + + "and change it after logging in. Set {} in mirth.properties " |
| 291 | + + "before the first startup to choose the password instead.", |
| 292 | + INITIAL_ADMIN_USERNAME, password, INITIAL_ADMIN_PASSWORD_PROPERTY); |
| 293 | + } |
| 294 | + |
| 295 | + PreparedStatement statement = null; |
| 296 | + try { |
| 297 | + String digestedPassword = configurationController.getDigester().digest(password); |
| 298 | + |
| 299 | + statement = getConnection().prepareStatement( |
| 300 | + "INSERT INTO PERSON_PASSWORD (PERSON_ID, PASSWORD, PASSWORD_DATE) " |
| 301 | + + "SELECT ID, ?, ? FROM PERSON WHERE USERNAME = ?"); |
| 302 | + statement.setString(1, digestedPassword); |
| 303 | + statement.setTimestamp(2, new Timestamp(System.currentTimeMillis())); |
| 304 | + statement.setString(3, INITIAL_ADMIN_USERNAME); |
| 305 | + |
| 306 | + if (statement.executeUpdate() != 1) { |
| 307 | + throw new MigrationException("Could not set the initial password for the \"" + INITIAL_ADMIN_USERNAME + "\" user."); |
268 | 308 | } |
| 309 | + } catch (SQLException e) { |
| 310 | + throw new MigrationException(e); |
| 311 | + } finally { |
| 312 | + DbUtils.closeQuietly(statement); |
| 313 | + } |
| 314 | + } |
269 | 315 |
|
270 | | - updateVersion(Version.getLatest()); |
| 316 | + private String generatePassword() { |
| 317 | + String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
| 318 | + SecureRandom random = new SecureRandom(); |
| 319 | + StringBuilder builder = new StringBuilder(); |
| 320 | + // Ensure the password contains at least one lowercase letter, |
| 321 | + // one uppercase letter, one digit, and one special character |
| 322 | + builder.append("Aa1!"); |
| 323 | + for (int i = 0; i < GENERATED_PASSWORD_LENGTH; i++) { |
| 324 | + builder.append(characters.charAt(random.nextInt(characters.length()))); |
271 | 325 | } |
| 326 | + return builder.toString(); |
272 | 327 | } |
273 | 328 |
|
274 | 329 | /** |
|
0 commit comments