Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 Jun 28, 2021
761f912
JPERF-729 Log dataset password update
mgrzaslewicz-atlassian Nov 16, 2021
e71d277
JPERF-729 Fix unit test
mgrzaslewicz-atlassian Nov 16, 2021
64b388b
JPERF-729 Handle both plain text and encrypted admin password dataset…
mgrzaslewicz-atlassian Nov 19, 2021
8e2bd06
JPERF-729 Add changelog entry, deprecate Database.setup(ssh) instead …
mgrzaslewicz-atlassian Nov 24, 2021
9e9fbc3
JPERF-729 Move changelog entry to unreleased, remove all API breaking…
mgrzaslewicz-atlassian Nov 24, 2021
341818c
JPERF-729 Apply suggestions from PR comments
mgrzaslewicz-atlassian Nov 24, 2021
9b8ae41
JPERF-729 Move username out of extension function to builder with def…
mgrzaslewicz-atlassian Nov 25, 2021
10f9d35
JPERF-729 Move username out of extension function to builder with def…
mgrzaslewicz-atlassian Nov 25, 2021
26aeb0e
JPERF-729 Make loggers non-static again to have the same approach eve…
mgrzaslewicz-atlassian Nov 25, 2021
881f9b2
JPERF-729 Add link to issue in changelog
mgrzaslewicz-atlassian Nov 25, 2021
fb2be73
JPERF-729 Unify logger creation
mgrzaslewicz-atlassian Nov 25, 2021
56ad890
JPERF-729 Change log level to debug for password update
mgrzaslewicz-atlassian Nov 25, 2021
0471a2a
JPERF-729 Extract password encryptor to follow single responsibility …
mgrzaslewicz-atlassian Nov 25, 2021
13bcd56
JPERF-729 Fix typo in test method name
mgrzaslewicz-atlassian Nov 25, 2021
4c49b61
JPERF-729 Refactor JiraUserPasswordOverridingDatabase
mgrzaslewicz-atlassian Nov 29, 2021
bfc5e2e
JPERF-729 Change withAdminPassword extension function return type to …
mgrzaslewicz-atlassian Dec 2, 2021
f5fb3b8
JPERF-729 Add .idea folder to gitignore
mgrzaslewicz-atlassian Jan 5, 2022
64c957f
JPERF-729 Use java.util.Function instead of kotlin interface - for ja…
mgrzaslewicz-atlassian Jan 11, 2022
614475f
JPERF-729 Simplify password encryptor API
mgrzaslewicz-atlassian Jan 11, 2022
6009d11
JPERF-729 Simplify password encryptor API even more
mgrzaslewicz-atlassian Jan 11, 2022
148c9ab
JPERF-729 Remove duplication in extension function creating builder
mgrzaslewicz-atlassian Jan 11, 2022
9be53d5
JPERF-729 Use shorter names in builder
mgrzaslewicz-atlassian Jan 11, 2022
16425ea
JPERF-729 Rename file containing JiraUserEncryptedPasswordProvider class
mgrzaslewicz-atlassian Jan 11, 2022
bab7c59
JPERF-729 Move CrowdEncryptedPasswordProvider to a separate file
mgrzaslewicz-atlassian Jan 12, 2022
9b2fbec
JPERF-729 Explicitly setup test context in JiraUserPasswordOverriding…
mgrzaslewicz-atlassian Jan 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,30 @@ class JiraUserPasswordOverridingDatabase internal constructor(
if (userPassword == null) {
logger.info("Password not provided, skipping dataset update")
Comment thread
dagguh marked this conversation as resolved.
Outdated
} else {
sqlClient.runSql(ssh, "UPDATE $cwdUserTableName SET credential='${userPassword.encrypted}' WHERE user_name='$username';")
if (shouldUseEncryption(ssh)) {
logger.info("Updating credential with encrypted password")
sqlClient.runSql(ssh, "UPDATE $cwdUserTableName SET credential='${userPassword.encrypted}' WHERE user_name='$username';")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is $cwdUserTableName a variable, but cwd_directory_attribute a constant?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to remove table name parameters if there is no reason to keep it. @pczuj do you remember why you made it as a parameter?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason was to split the responsibility from a class that knows only the structure of table, but doesn't know it's name. It was also to allow overwrites in case the table name changes in the future or is incorrect for some DBs. We are relying on something that is not really an Jira API here.

We could make the fields also configurable for better portability, but then the class will have nothing specific about Jira DB.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then why isn't cwd_directory_attribute also parametrized? YAGNI TBH.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's because the table name parameter was made by me with first version of the code was created. Then @mgrzaslewicz took over the PR and his addition didn't follow the same approach. That's where the 2 parts of the code differ.

I don't think YAGNI applies here. As mentioned in my previous comment there are at least 2 reasons for the delegation of table name definition (responsibility split + uncertain contract). Both of those are essentially preparing for easier modification, e.g. it would be easier to adapt the table name from the lib consumer repository rather than relying on a new release.

I'd promote a quite from Matrin Fowler here: https://www.martinfowler.com/bliki/Yagni.html

Yagni only applies to capabilities built into the software to support a presumptive feature, it does not apply to effort to make the software easier to modify.

} else {
logger.info("Updating credential with plain text password")
sqlClient.runSql(ssh, "UPDATE $cwdUserTableName 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 dbNamePrefix = if (cwdUserTableName.contains(".")) cwdUserTableName.substringBefore(".") + "." else ""
Comment thread
dagguh marked this conversation as resolved.
Outdated
val sqlResult = sqlClient.runSql(ssh, "select attribute_value from ${dbNamePrefix}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 username: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
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.infrastructure.mock.RememberingSshSqlClient
import com.atlassian.performance.tools.ssh.api.SshConnection
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.net.URI
Expand Down Expand Up @@ -52,11 +53,39 @@ class JiraUserPasswordOverridingDatabaseTest {
}

@Test
fun shouldExecuteUpdateOnCwdUserTable() {
fun shouldExecuteUpdateOnCwdUserTableWithEncryptedPassword_whenUnknownUserEncryptionMethod() {
Comment thread
dagguh marked this conversation as resolved.
Outdated
// given
val cwdUserTableName = "jiradb.cwd_user"
val underlyingDatabase = RememberingDatabase()
val sqlClient = MockSshSqlClient()
val database = JiraUserPasswordOverridingDatabase(
databaseDelegate = underlyingDatabase,
sqlClient = sqlClient,
username = "admin",
userPassword = samplePassword,
cwdUserTableName = cwdUserTableName
)
val sshConnection = RememberingSshConnection()

// 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 $cwdUserTableName SET credential='${samplePassword.encrypted}' WHERE user_name='admin';"
)
}

@Test
fun shouldExecuteUpdateOnCwdUserTableWithEncryptedPassword_whenAtlassianSecurityUserEncryptionMethod() {
Comment thread
dagguh marked this conversation as resolved.
Outdated
// given
val cwdUserTableName = "cwd_user"
val underlyingDatabase = RememberingDatabase()
val sqlClient = RememberingSshSqlClient()
val sqlClient = MockSshSqlClient()
val database = JiraUserPasswordOverridingDatabase(
databaseDelegate = underlyingDatabase,
sqlClient = sqlClient,
Expand All @@ -65,14 +94,63 @@ class JiraUserPasswordOverridingDatabaseTest {
cwdUserTableName = cwdUserTableName
)
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 cwd_directory_attribute where attribute_name = 'user_encryption_method';",
"UPDATE $cwdUserTableName SET credential='${samplePassword.encrypted}' WHERE user_name='admin';"
)
}

@Test
fun shouldExecuteUpdateOnCwdUserTableWithEncryptedPassword_whenPlainTextSecurityUserEncryptionMethod() {
Comment thread
dagguh marked this conversation as resolved.
Outdated
// given
val cwdUserTableName = "cwd_user"
val underlyingDatabase = RememberingDatabase()
val sqlClient = MockSshSqlClient()
val database = JiraUserPasswordOverridingDatabase(
databaseDelegate = underlyingDatabase,
sqlClient = sqlClient,
username = "admin",
userPassword = samplePassword,
cwdUserTableName = cwdUserTableName
)
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 command executed")
.contains("UPDATE $cwdUserTableName SET credential='${samplePassword.encrypted}' WHERE user_name='admin';")
.`as`("sql queries executed")
.containsExactly(
"select attribute_value from cwd_directory_attribute where attribute_name = 'user_encryption_method';",
"UPDATE $cwdUserTableName SET credential='${samplePassword.plainText}' WHERE user_name='admin';"
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.atlassian.performance.tools.infrastructure.mock

import com.atlassian.performance.tools.infrastructure.database.SshSqlClient
import com.atlassian.performance.tools.ssh.api.SshConnection
import java.io.File
import java.util.*

class MockSshSqlClient : SshSqlClient {
private val log = mutableListOf<String>()
private val defaultSshResult = SshConnection.SshResult(
exitStatus = 0,
output = "",
errorOutput = ""
)
private val returnedCommandResults = ArrayDeque<SshConnection.SshResult>()

fun queueReturnedSqlCommandResult(result: SshConnection.SshResult) {
returnedCommandResults.add(result)
}

override fun runSql(
ssh: SshConnection,
sql: String
) = (if (returnedCommandResults.isEmpty()) defaultSshResult else returnedCommandResults.pop())
.also { log.add(sql) }

override fun runSql(
ssh: SshConnection,
sql: File
) = (if (returnedCommandResults.isEmpty()) defaultSshResult else returnedCommandResults.pop())
.also { log.add(sql.readText()) }

fun getLog(): List<String> = log
}

This file was deleted.