-
Notifications
You must be signed in to change notification settings - Fork 23
JPERF-729: Create a way to configure Jira admin user password during database setup #107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
mgrzaslewicz
merged 26 commits into
atlassian:master
from
pczuj:issue/JPERF-729-admin-password
Jan 13, 2022
Merged
Changes from 11 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
a5f1e36
JPERF-729: Create a way to configure Jira admin user password during …
pczuj 761f912
JPERF-729 Log dataset password update
mgrzaslewicz-atlassian e71d277
JPERF-729 Fix unit test
mgrzaslewicz-atlassian 64b388b
JPERF-729 Handle both plain text and encrypted admin password dataset…
mgrzaslewicz-atlassian 8e2bd06
JPERF-729 Add changelog entry, deprecate Database.setup(ssh) instead …
mgrzaslewicz-atlassian 9e9fbc3
JPERF-729 Move changelog entry to unreleased, remove all API breaking…
mgrzaslewicz-atlassian 341818c
JPERF-729 Apply suggestions from PR comments
mgrzaslewicz-atlassian 9b8ae41
JPERF-729 Move username out of extension function to builder with def…
mgrzaslewicz-atlassian 10f9d35
JPERF-729 Move username out of extension function to builder with def…
mgrzaslewicz-atlassian 26aeb0e
JPERF-729 Make loggers non-static again to have the same approach eve…
mgrzaslewicz-atlassian 881f9b2
JPERF-729 Add link to issue in changelog
mgrzaslewicz-atlassian fb2be73
JPERF-729 Unify logger creation
mgrzaslewicz-atlassian 56ad890
JPERF-729 Change log level to debug for password update
mgrzaslewicz-atlassian 0471a2a
JPERF-729 Extract password encryptor to follow single responsibility …
mgrzaslewicz-atlassian 13bcd56
JPERF-729 Fix typo in test method name
mgrzaslewicz-atlassian 4c49b61
JPERF-729 Refactor JiraUserPasswordOverridingDatabase
mgrzaslewicz-atlassian bfc5e2e
JPERF-729 Change withAdminPassword extension function return type to …
mgrzaslewicz-atlassian f5fb3b8
JPERF-729 Add .idea folder to gitignore
mgrzaslewicz-atlassian 64c957f
JPERF-729 Use java.util.Function instead of kotlin interface - for ja…
mgrzaslewicz-atlassian 614475f
JPERF-729 Simplify password encryptor API
mgrzaslewicz-atlassian 6009d11
JPERF-729 Simplify password encryptor API even more
mgrzaslewicz-atlassian 148c9ab
JPERF-729 Remove duplication in extension function creating builder
mgrzaslewicz-atlassian 9be53d5
JPERF-729 Use shorter names in builder
mgrzaslewicz-atlassian 16425ea
JPERF-729 Rename file containing JiraUserEncryptedPasswordProvider class
mgrzaslewicz-atlassian bab7c59
JPERF-729 Move CrowdEncryptedPasswordProvider to a separate file
mgrzaslewicz-atlassian 9b2fbec
JPERF-729 Explicitly setup test context in JiraUserPasswordOverriding…
mgrzaslewicz-atlassian File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
...ssian/performance/tools/infrastructure/api/database/JiraUserPasswordOverridingDatabase.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| package com.atlassian.performance.tools.infrastructure.api.database | ||
|
|
||
| import com.atlassian.performance.tools.infrastructure.database.SshMysqlClient | ||
| import com.atlassian.performance.tools.infrastructure.database.SshSqlClient | ||
| import com.atlassian.performance.tools.ssh.api.SshConnection | ||
| import org.apache.logging.log4j.LogManager | ||
| import org.apache.logging.log4j.Logger | ||
| import java.net.URI | ||
|
|
||
| class JiraUserPassword( | ||
| val plainText: String, | ||
|
dagguh marked this conversation as resolved.
Outdated
|
||
| val encrypted: String | ||
| ) | ||
|
|
||
| /** | ||
| * Based on https://confluence.atlassian.com/jira/retrieving-the-jira-administrator-192836.html | ||
| * | ||
| * To encode the password use [com.atlassian.crowd.password.encoder.AtlassianSecurityPasswordEncoder](https://docs.atlassian.com/atlassian-crowd/4.2.2/com/atlassian/crowd/password/encoder/AtlassianSecurityPasswordEncoder.html) | ||
|
dagguh marked this conversation as resolved.
Outdated
|
||
| * from the [com.atlassian.crowd.crowd-password-encoders](https://mvnrepository.com/artifact/com.atlassian.crowd/crowd-password-encoders/4.2.2). | ||
| */ | ||
| class JiraUserPasswordOverridingDatabase internal constructor( | ||
| private val databaseDelegate: Database, | ||
| private val sqlClient: SshSqlClient, | ||
| private val username: String, | ||
| private val userPassword: JiraUserPassword, | ||
| private val jiraDatabaseSchemaName: String | ||
| ) : Database { | ||
|
dagguh marked this conversation as resolved.
Outdated
|
||
| private val logger: Logger = LogManager.getLogger(JiraUserPasswordOverridingDatabase::class.java) | ||
|
|
||
| override fun setup(ssh: SshConnection): String = databaseDelegate.setup(ssh) | ||
|
|
||
| override fun start( | ||
| jira: URI, | ||
| ssh: SshConnection | ||
| ) { | ||
| databaseDelegate.start(jira, ssh) | ||
| if (shouldUseEncryption(ssh)) { | ||
|
dagguh marked this conversation as resolved.
Outdated
|
||
| logger.info("Updating credential with encrypted password") | ||
|
dagguh marked this conversation as resolved.
Outdated
|
||
| sqlClient.runSql(ssh, "UPDATE ${jiraDatabaseSchemaName}.cwd_user SET credential='${userPassword.encrypted}' WHERE user_name='$username';") | ||
| } else { | ||
| logger.info("Updating credential with plain text password") | ||
| sqlClient.runSql(ssh, "UPDATE ${jiraDatabaseSchemaName}.cwd_user SET credential='${userPassword.plainText}' WHERE user_name='$username';") | ||
| } | ||
| logger.info("Password for user '$username' updated to '${userPassword.plainText}'") | ||
| } | ||
|
|
||
| private fun shouldUseEncryption(ssh: SshConnection): Boolean { | ||
| val sqlResult = | ||
| sqlClient.runSql(ssh, "select attribute_value from ${jiraDatabaseSchemaName}.cwd_directory_attribute where attribute_name = 'user_encryption_method';").output | ||
| return when { | ||
| sqlResult.contains("plaintext") -> false | ||
| sqlResult.contains("atlassian-security") -> true | ||
| else -> { | ||
| logger.warn("Unknown user_encryption_method. Assuming encrypted password should be used") | ||
| true | ||
| } | ||
| } | ||
| } | ||
|
|
||
| class Builder( | ||
| private var databaseDelegate: Database, | ||
| private var userPassword: JiraUserPassword | ||
| ) { | ||
| private var sqlClient: SshSqlClient = SshMysqlClient() | ||
| private var jiraDatabaseSchemaName: String = "jiradb" | ||
| private var username: String = "admin" | ||
|
|
||
| fun databaseDelegate(databaseDelegate: Database) = apply { this.databaseDelegate = databaseDelegate } | ||
| fun username(username: String) = apply { this.username = username } | ||
| fun userPassword(userPassword: JiraUserPassword) = apply { this.userPassword = userPassword } | ||
| fun sqlClient(sqlClient: SshSqlClient) = apply { this.sqlClient = sqlClient } | ||
| fun jiraDatabaseSchemaName(jiraDatabaseSchemaName: String) = apply { this.jiraDatabaseSchemaName = jiraDatabaseSchemaName } | ||
|
|
||
| fun build() = JiraUserPasswordOverridingDatabase( | ||
| databaseDelegate = databaseDelegate, | ||
| sqlClient = sqlClient, | ||
| username = username, | ||
| userPassword = userPassword, | ||
| jiraDatabaseSchemaName = jiraDatabaseSchemaName | ||
| ) | ||
| } | ||
|
|
||
| } | ||
|
|
||
| fun Database.withAdminPassword(adminPassword: JiraUserPassword) = JiraUserPasswordOverridingDatabase.Builder( | ||
| databaseDelegate = this, | ||
| userPassword = adminPassword | ||
| ).build() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
151 changes: 151 additions & 0 deletions
151
...n/performance/tools/infrastructure/api/database/JiraUserPasswordOverridingDatabaseTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,151 @@ | ||
| package com.atlassian.performance.tools.infrastructure.api.database | ||
|
|
||
| import com.atlassian.performance.tools.infrastructure.mock.MockSshSqlClient | ||
| import com.atlassian.performance.tools.infrastructure.mock.RememberingDatabase | ||
| import com.atlassian.performance.tools.infrastructure.mock.RememberingSshConnection | ||
| import com.atlassian.performance.tools.ssh.api.SshConnection | ||
| import org.assertj.core.api.Assertions.assertThat | ||
| import org.junit.Test | ||
| import java.net.URI | ||
|
|
||
| class JiraUserPasswordOverridingDatabaseTest { | ||
|
dagguh marked this conversation as resolved.
Outdated
|
||
|
|
||
| private val jira = URI("http://localhost/") | ||
| private val samplePassword = JiraUserPassword( | ||
| plainText = "**plain text**", | ||
| encrypted = "**encrypted**" | ||
| ) | ||
|
|
||
| @Test | ||
| fun shouldSetupUnderlyingDatabase() { | ||
| val underlyingDatabase = RememberingDatabase() | ||
| val database = JiraUserPasswordOverridingDatabase.Builder( | ||
| databaseDelegate = underlyingDatabase, | ||
| userPassword = samplePassword | ||
| ).build() | ||
| val sshConnection = RememberingSshConnection() | ||
|
|
||
| database.setup(sshConnection) | ||
| database.start(jira, sshConnection) | ||
|
|
||
| assertThat(underlyingDatabase.isSetup) | ||
| .`as`("underlying database setup") | ||
| .isTrue() | ||
| } | ||
|
|
||
| @Test | ||
| fun shouldStartUnderlyingDatabase() { | ||
| val underlyingDatabase = RememberingDatabase() | ||
| val database = JiraUserPasswordOverridingDatabase.Builder( | ||
| databaseDelegate = underlyingDatabase, | ||
| userPassword = samplePassword | ||
| ).build() | ||
| val sshConnection = RememberingSshConnection() | ||
|
|
||
| database.setup(sshConnection) | ||
| database.start(jira, sshConnection) | ||
|
|
||
| assertThat(underlyingDatabase.isStarted) | ||
| .`as`("underlying database started") | ||
| .isTrue() | ||
| } | ||
|
|
||
| @Test | ||
| fun shouldUpdateEncryptedPasswordByDefault() { | ||
| // given | ||
| val underlyingDatabase = RememberingDatabase() | ||
| val sqlClient = MockSshSqlClient() | ||
| val database = JiraUserPasswordOverridingDatabase( | ||
| databaseDelegate = underlyingDatabase, | ||
| sqlClient = sqlClient, | ||
| username = "admin", | ||
| userPassword = samplePassword, | ||
| jiraDatabaseSchemaName = "jira" | ||
| ) | ||
| val sshConnection = RememberingSshConnection() | ||
|
|
||
| // when | ||
| database.setup(sshConnection) | ||
| database.start(jira, sshConnection) | ||
|
|
||
| // then | ||
| assertThat(sqlClient.getLog()) | ||
| .`as`("sql queries executed") | ||
| .containsExactly( | ||
| "select attribute_value from jira.cwd_directory_attribute where attribute_name = 'user_encryption_method';", | ||
| "UPDATE jira.cwd_user SET credential='${samplePassword.encrypted}' WHERE user_name='admin';" | ||
| ) | ||
| } | ||
|
|
||
| @Test | ||
| fun shouldUpdateEncryptedPassword() { | ||
| // given | ||
| val underlyingDatabase = RememberingDatabase() | ||
| val sqlClient = MockSshSqlClient() | ||
| val database = JiraUserPasswordOverridingDatabase( | ||
| databaseDelegate = underlyingDatabase, | ||
| sqlClient = sqlClient, | ||
| username = "admin", | ||
| userPassword = samplePassword, | ||
| jiraDatabaseSchemaName = "jiradb" | ||
| ) | ||
| val sshConnection = RememberingSshConnection() | ||
| sqlClient.queueReturnedSqlCommandResult( | ||
| SshConnection.SshResult( | ||
| exitStatus = 0, | ||
| output = """attribute_value | ||
| atlassian-security | ||
| """.trimMargin(), | ||
| errorOutput = "" | ||
| ) | ||
| ) | ||
|
|
||
| // when | ||
| database.setup(sshConnection) | ||
| database.start(jira, sshConnection) | ||
|
|
||
| // then | ||
| assertThat(sqlClient.getLog()) | ||
| .`as`("sql queries executed") | ||
| .containsExactly( | ||
| "select attribute_value from jiradb.cwd_directory_attribute where attribute_name = 'user_encryption_method';", | ||
| "UPDATE jiradb.cwd_user SET credential='${samplePassword.encrypted}' WHERE user_name='admin';" | ||
| ) | ||
| } | ||
|
|
||
| @Test | ||
| fun shouldUpdatePlaintextPassword() { | ||
| // given | ||
| val underlyingDatabase = RememberingDatabase() | ||
| val sqlClient = MockSshSqlClient() | ||
| val database = JiraUserPasswordOverridingDatabase( | ||
| databaseDelegate = underlyingDatabase, | ||
| sqlClient = sqlClient, | ||
| username = "admin", | ||
| userPassword = samplePassword, | ||
| jiraDatabaseSchemaName = "jira" | ||
| ) | ||
| val sshConnection = RememberingSshConnection() | ||
| sqlClient.queueReturnedSqlCommandResult( | ||
| SshConnection.SshResult( | ||
| exitStatus = 0, | ||
| output = """attribute_value | ||
| plaintext | ||
| """.trimMargin(), | ||
| errorOutput = "" | ||
| ) | ||
| ) | ||
|
|
||
| // when | ||
| database.setup(sshConnection) | ||
| database.start(jira, sshConnection) | ||
|
|
||
| // then | ||
| assertThat(sqlClient.getLog()) | ||
| .`as`("sql queries executed") | ||
| .containsExactly( | ||
| "select attribute_value from jira.cwd_directory_attribute where attribute_name = 'user_encryption_method';", | ||
| "UPDATE jira.cwd_user SET credential='${samplePassword.plainText}' WHERE user_name='admin';" | ||
| ) | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.