From 10a750fbf9d42c4802bd6af3aa0388498e9eb217 Mon Sep 17 00:00:00 2001 From: az Date: Sun, 26 Feb 2023 19:15:23 +0100 Subject: [PATCH 01/11] [skip ci] remove parameter remoteHostSetSudoWithoutPasswordRequired --- .../provs/desktop/application/Application.kt | 2 +- .../provs/framework/core/cli/CliUtils.kt | 33 ++++++++----------- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/application/Application.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/application/Application.kt index 780bd37..5f9fef5 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/application/Application.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/desktop/application/Application.kt @@ -17,7 +17,7 @@ fun main(args: Array) { exitProcess(1) } - val prov = createProvInstance(cmd.target, remoteHostSetSudoWithoutPasswordRequired = true) + val prov = createProvInstance(cmd.target) try { provisionDesktopCommand(prov, cmd) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt index 7e3055f..f8b3df9 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt @@ -20,10 +20,7 @@ import kotlin.system.exitProcess * If the target is remote and if parameter remoteHostSetSudoWithoutPasswordRequired is set to true, * it will enable sudo without password on the remote machine (in case this was not yet enabled). */ -fun createProvInstance( - targetCommand: TargetCliCommand, - remoteHostSetSudoWithoutPasswordRequired: Boolean = false -): Prov { +fun createProvInstance(targetCommand: TargetCliCommand): Prov { if (targetCommand.isValid()) { val password: Secret? = targetCommand.remoteTarget()?.password @@ -35,8 +32,7 @@ fun createProvInstance( remoteTarget.host, remoteTarget.user, remoteTarget.password == null, - password, - remoteHostSetSudoWithoutPasswordRequired + password ) } else { throw IllegalArgumentException("Error: neither a valid localHost nor a valid remoteHost was specified! Use option -h for help.") @@ -50,8 +46,10 @@ fun createProvInstance( private fun createLocalProvInstance(): Prov { val prov = local() if (!prov.currentUserCanSudoWithoutPassword()) { - val password = PromptSecretSource("Please enter password to configure sudo without password in the future." + - "\nWarning: This will permanently allow your user to use sudo privileges without a password.").secret() + val password = PromptSecretSource( + "Please enter password to configure sudo without password in the future." + + "\nWarning: This will permanently allow your user to use sudo privileges without a password." + ).secret() prov.makeUserSudoerWithNoSudoPasswordRequired(password) } return prov @@ -62,8 +60,7 @@ private fun createRemoteProvInstance( host: String, remoteUser: String, sshWithKey: Boolean, - password: Secret?, - remoteHostSetSudoWithoutPasswordRequired: Boolean + password: Secret? ): Prov { val prov = if (sshWithKey) { @@ -76,17 +73,13 @@ private fun createRemoteProvInstance( } if (!prov.currentUserCanSudoWithoutPassword()) { - if (remoteHostSetSudoWithoutPasswordRequired) { - require( - password != null, - { "User ${prov.whoami()} not able to sudo on remote machine without password and no password available for the user." }) - prov.makeUserSudoerWithNoSudoPasswordRequired(password) + require( + password != null, + { "User ${prov.whoami()} not able to sudo on remote machine without password and no password available for the user." }) + prov.makeUserSudoerWithNoSudoPasswordRequired(password) - // a new session is required after making the user a sudoer without password - return remote(host, remoteUser, password) - } else { - throw IllegalStateException("User ${prov.whoami()} not able to sudo on remote machine without password and option not set to enable user to sudo without password.") - } + // a new session is required after making the user a sudoer without password + return remote(host, remoteUser, password) } return prov } From 482280574b54ca47a9d25e40f07b4ed998e0bbaf Mon Sep 17 00:00:00 2001 From: az Date: Sun, 26 Feb 2023 19:18:25 +0100 Subject: [PATCH 02/11] [skip ci] rename makeUserSudoerWithoutPasswordRequired --- .../provs/framework/core/cli/CliUtils.kt | 6 +++--- .../provs/framework/ubuntu/user/base/User.kt | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt index f8b3df9..f3be177 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt @@ -7,7 +7,7 @@ import org.domaindrivenarchitecture.provs.framework.core.local import org.domaindrivenarchitecture.provs.framework.core.remote import org.domaindrivenarchitecture.provs.framework.ubuntu.secret.secretSources.PromptSecretSource import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.currentUserCanSudoWithoutPassword -import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.makeUserSudoerWithNoSudoPasswordRequired +import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.makeUserSudoerWithoutPasswordRequired import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.whoami import kotlin.system.exitProcess @@ -50,7 +50,7 @@ private fun createLocalProvInstance(): Prov { "Please enter password to configure sudo without password in the future." + "\nWarning: This will permanently allow your user to use sudo privileges without a password." ).secret() - prov.makeUserSudoerWithNoSudoPasswordRequired(password) + prov.makeUserSudoerWithoutPasswordRequired(password) } return prov } @@ -76,7 +76,7 @@ private fun createRemoteProvInstance( require( password != null, { "User ${prov.whoami()} not able to sudo on remote machine without password and no password available for the user." }) - prov.makeUserSudoerWithNoSudoPasswordRequired(password) + prov.makeUserSudoerWithoutPasswordRequired(password) // a new session is required after making the user a sudoer without password return remote(host, remoteUser, password) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/user/base/User.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/user/base/User.kt index 5da27f4..cd5978b 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/user/base/User.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/user/base/User.kt @@ -33,7 +33,7 @@ fun Prov.createUser( } password?.let { cmdNoLog("sudo echo \"$userName:${password.plain()}\" | sudo chpasswd") } ?: ProvResult(true) if (userCanSudoWithoutPassword) { - makeUserSudoerWithNoSudoPasswordRequired(userName) + makeUserSudoerWithoutPasswordRequired(userName) } val authorizedKeysFile = userHome() + ".ssh/authorized_keys" if (copyAuthorizedSshKeysFromCurrentUser && checkFile(authorizedKeysFile)) { @@ -85,7 +85,7 @@ fun Prov.deleteUser(userName: String, deleteHomeDir: Boolean = false): ProvResul * The current (executing) user must already be a sudoer. If he is a sudoer with password required then * his password must be provided. */ -fun Prov.makeUserSudoerWithNoSudoPasswordRequired( +fun Prov.makeUserSudoerWithoutPasswordRequired( userName: String, password: Secret? = null, overwriteFile: Boolean = false @@ -108,10 +108,10 @@ fun Prov.makeUserSudoerWithNoSudoPasswordRequired( * IMPORTANT: Current user must already by sudoer when calling this function. */ @Suppress("unused") // used externally -fun Prov.makeUserSudoerWithNoSudoPasswordRequired(password: Secret) = task { +fun Prov.makeUserSudoerWithoutPasswordRequired(password: Secret) = task { val currentUser = whoami() if (currentUser != null) { - makeUserSudoerWithNoSudoPasswordRequired(currentUser, password, overwriteFile = true) + makeUserSudoerWithoutPasswordRequired(currentUser, password, overwriteFile = true) } else { ProvResult(false, "Current user could not be determined.") } From c72e40fb65655c1a36c96d43284ede03962393d8 Mon Sep 17 00:00:00 2001 From: az Date: Sun, 26 Feb 2023 19:25:14 +0100 Subject: [PATCH 03/11] [skip ci] enlarge password prompt window --- .../framework/ubuntu/secret/secretSources/PromptSecretSource.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/secret/secretSources/PromptSecretSource.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/secret/secretSources/PromptSecretSource.kt index bbb9919..bf69f2f 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/secret/secretSources/PromptSecretSource.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/secret/secretSources/PromptSecretSource.kt @@ -8,7 +8,7 @@ import javax.swing.* class PasswordPanel : JPanel(FlowLayout()) { - private val passwordField = JPasswordField(20) + private val passwordField = JPasswordField(30) private var entered = false val enteredPassword From bf36a6283c3f0419888f76efd5b4ac3c0b3d7f8b Mon Sep 17 00:00:00 2001 From: az Date: Sun, 26 Feb 2023 19:28:38 +0100 Subject: [PATCH 04/11] [skip ci] set ssh connection timeout --- .../provs/framework/core/processors/RemoteUbuntuProcessor.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/processors/RemoteUbuntuProcessor.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/processors/RemoteUbuntuProcessor.kt index f6e08ff..aebd0e7 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/processors/RemoteUbuntuProcessor.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/processors/RemoteUbuntuProcessor.kt @@ -38,6 +38,8 @@ class RemoteProcessor(host: InetAddress, user: String, password: Secret? = null) // Attention: host key is not verified ssh.addHostKeyVerifier(PromiscuousVerifier()) + + ssh.connectTimeout = 30000 // ms ssh.connect(host) if (password != null) { From 082c0827e3f1495c0a1fe6ba6a0e7c11d1bd6326 Mon Sep 17 00:00:00 2001 From: az Date: Sun, 26 Feb 2023 19:32:44 +0100 Subject: [PATCH 05/11] [skip ci] make makeUserSudoerWithoutPasswordRequired taskWithResult --- .../provs/framework/core/cli/CliUtils.kt | 6 +++--- .../provs/framework/ubuntu/user/base/User.kt | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt index f3be177..f229ac9 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt @@ -7,7 +7,7 @@ import org.domaindrivenarchitecture.provs.framework.core.local import org.domaindrivenarchitecture.provs.framework.core.remote import org.domaindrivenarchitecture.provs.framework.ubuntu.secret.secretSources.PromptSecretSource import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.currentUserCanSudoWithoutPassword -import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.makeUserSudoerWithoutPasswordRequired +import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.makeCurrentUserSudoerWithoutPasswordRequired import org.domaindrivenarchitecture.provs.framework.ubuntu.user.base.whoami import kotlin.system.exitProcess @@ -50,7 +50,7 @@ private fun createLocalProvInstance(): Prov { "Please enter password to configure sudo without password in the future." + "\nWarning: This will permanently allow your user to use sudo privileges without a password." ).secret() - prov.makeUserSudoerWithoutPasswordRequired(password) + prov.makeCurrentUserSudoerWithoutPasswordRequired(password) } return prov } @@ -76,7 +76,7 @@ private fun createRemoteProvInstance( require( password != null, { "User ${prov.whoami()} not able to sudo on remote machine without password and no password available for the user." }) - prov.makeUserSudoerWithoutPasswordRequired(password) + prov.makeCurrentUserSudoerWithoutPasswordRequired(password) // a new session is required after making the user a sudoer without password return remote(host, remoteUser, password) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/user/base/User.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/user/base/User.kt index cd5978b..1405542 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/user/base/User.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/ubuntu/user/base/User.kt @@ -89,7 +89,7 @@ fun Prov.makeUserSudoerWithoutPasswordRequired( userName: String, password: Secret? = null, overwriteFile: Boolean = false -): ProvResult = task { +): ProvResult = taskWithResult { val userSudoFile = "/etc/sudoers.d/$userName" if (!checkFile(userSudoFile) || overwriteFile) { val sudoPrefix = if (password == null) "sudo" else "echo ${password.plain()} | sudo -S" @@ -107,8 +107,7 @@ fun Prov.makeUserSudoerWithoutPasswordRequired( * Makes the current (executing) user be able to sudo without password. * IMPORTANT: Current user must already by sudoer when calling this function. */ -@Suppress("unused") // used externally -fun Prov.makeUserSudoerWithoutPasswordRequired(password: Secret) = task { +fun Prov.makeCurrentUserSudoerWithoutPasswordRequired(password: Secret) = taskWithResult { val currentUser = whoami() if (currentUser != null) { makeUserSudoerWithoutPasswordRequired(currentUser, password, overwriteFile = true) From 8bb2e6e950fa637a84c4e05e6b91b59f94d2fedb Mon Sep 17 00:00:00 2001 From: az Date: Sun, 26 Feb 2023 19:39:54 +0100 Subject: [PATCH 06/11] [skip ci] simplify and rename retrievePassword --- .../provs/framework/core/cli/CliUtils.kt | 12 +++--------- .../configuration/domain/CliTargetCommandTest.kt | 8 ++++---- .../provs/desktop/application/ApplicationKtTest.kt | 8 ++++---- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt index f229ac9..3cacf98 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt @@ -85,12 +85,6 @@ private fun createRemoteProvInstance( } -internal fun retrievePassword(cliCommand: TargetCliCommand): Secret? { - var password: Secret? = null - if (cliCommand.isValidRemote() && cliCommand.passwordInteractive) { - password = - PromptSecretSource("Password for user $cliCommand.userName!! on $cliCommand.remoteHost!!").secret() - - } - return password -} +internal fun getPasswordToConfigureSudoWithoutPassword(): Secret { + return PromptSecretSource("password to configure sudo without password.").secret() +} \ No newline at end of file diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/CliTargetCommandTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/CliTargetCommandTest.kt index c69409b..bacb2b3 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/CliTargetCommandTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/configuration/domain/CliTargetCommandTest.kt @@ -4,7 +4,7 @@ import io.mockk.* import org.domaindrivenarchitecture.provs.framework.core.Prov import org.domaindrivenarchitecture.provs.framework.core.Secret import org.domaindrivenarchitecture.provs.framework.core.cli.createProvInstance -import org.domaindrivenarchitecture.provs.framework.core.cli.retrievePassword +import org.domaindrivenarchitecture.provs.framework.core.cli.getPasswordToConfigureSudoWithoutPassword import org.domaindrivenarchitecture.provs.framework.core.local import org.domaindrivenarchitecture.provs.framework.core.processors.PrintOnlyProcessor import org.domaindrivenarchitecture.provs.framework.core.remote @@ -23,8 +23,8 @@ internal class CliTargetCommandKtTest { mockkStatic(::remote) every { remote(any(), any(), any(), any()) } returns Prov.newInstance(PrintOnlyProcessor()) - mockkStatic(::retrievePassword) - every { retrievePassword(any()) } returns Secret("sec") + mockkStatic(::getPasswordToConfigureSudoWithoutPassword) + every { getPasswordToConfigureSudoWithoutPassword() } returns Secret("sec") } @AfterAll @@ -33,7 +33,7 @@ internal class CliTargetCommandKtTest { unmockkObject(Prov) unmockkStatic(::local) unmockkStatic(::remote) - unmockkStatic(::retrievePassword) + unmockkStatic(::getPasswordToConfigureSudoWithoutPassword) } } diff --git a/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/application/ApplicationKtTest.kt b/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/application/ApplicationKtTest.kt index 25aadb7..0e260c8 100644 --- a/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/application/ApplicationKtTest.kt +++ b/src/test/kotlin/org/domaindrivenarchitecture/provs/desktop/application/ApplicationKtTest.kt @@ -7,7 +7,7 @@ import org.domaindrivenarchitecture.provs.configuration.domain.TargetCliCommand import org.domaindrivenarchitecture.provs.desktop.domain.* import org.domaindrivenarchitecture.provs.desktop.infrastructure.getConfig import org.domaindrivenarchitecture.provs.framework.core.* -import org.domaindrivenarchitecture.provs.framework.core.cli.retrievePassword +import org.domaindrivenarchitecture.provs.framework.core.cli.getPasswordToConfigureSudoWithoutPassword import org.domaindrivenarchitecture.provs.framework.core.processors.DummyProcessor import org.domaindrivenarchitecture.provs.test.setRootLoggingLevel import org.junit.jupiter.api.AfterAll @@ -52,8 +52,8 @@ internal class ApplicationKtTest { cmd = "mocked command" ) - mockkStatic(::retrievePassword) - every { retrievePassword(any()) } returns Secret("sec") + mockkStatic(::getPasswordToConfigureSudoWithoutPassword) + every { getPasswordToConfigureSudoWithoutPassword() } returns Secret("sec") } @Suppress("unused") // false positive @@ -65,7 +65,7 @@ internal class ApplicationKtTest { unmockkStatic(::remote) unmockkStatic(::getConfig) unmockkStatic(Prov::provisionDesktop) - unmockkStatic(::retrievePassword) + unmockkStatic(::getPasswordToConfigureSudoWithoutPassword) } } From a06d47ff3062ac7fd1f84f83056f14c8788753cd Mon Sep 17 00:00:00 2001 From: az Date: Sun, 26 Feb 2023 19:48:25 +0100 Subject: [PATCH 07/11] [skip ci] remove sudo without password check from UbuntuProv --- .../provs/framework/core/platforms/UbuntuProv.kt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/platforms/UbuntuProv.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/platforms/UbuntuProv.kt index 254e333..c85db20 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/platforms/UbuntuProv.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/platforms/UbuntuProv.kt @@ -13,13 +13,6 @@ class UbuntuProv internal constructor( progressType: ProgressType ) : Prov(processor, name, progressType) { - init { - val user = cmdNoLog("whoami").out?.trim() - if ("root" != user && !cmdNoLog("timeout 1 sudo id").success) { - println("IMPORTANT INFO:\nUser $user cannot sudo without entering a password, i.e. some functions may fail!\nIf you need to run functions with sudo, please ensure $user can sudo without password.") - } - } - override fun cmd(cmd: String, dir: String?, sudo: Boolean): ProvResult = taskWithResult { exec(SHELL, "-c", commandWithDirAndSudo(cmd, dir, sudo)) } From df2a47bb6a009b99e5876272cddb5851779a1ccc Mon Sep 17 00:00:00 2001 From: az Date: Sun, 26 Feb 2023 19:49:45 +0100 Subject: [PATCH 08/11] [skip ci] improve error message when failing ssh connection --- .../provs/framework/core/processors/RemoteUbuntuProcessor.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/processors/RemoteUbuntuProcessor.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/processors/RemoteUbuntuProcessor.kt index aebd0e7..ead6482 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/processors/RemoteUbuntuProcessor.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/processors/RemoteUbuntuProcessor.kt @@ -52,8 +52,9 @@ class RemoteProcessor(host: InetAddress, user: String, password: Secret? = null) try { ssh.disconnect() } finally { - log.error("Got exception when initializing ssh (Username, password or ssh-key might be wrong): " + e.message) - throw RuntimeException("Error when initializing ssh (Username, password or ssh-key might be wrong) ", e) + val errorMag = "Error when initializing ssh (Host, username, password or ssh-key might be wrong) " + log.error(errorMag + e.message) + throw RuntimeException(errorMag, e) } } } From 804bfd00400dbebebad4967d18021724933c2097 Mon Sep 17 00:00:00 2001 From: az Date: Sun, 26 Feb 2023 20:01:47 +0100 Subject: [PATCH 09/11] refactor CliUtils.kt --- .../provs/framework/core/cli/CliUtils.kt | 67 ++++++++++++------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt index 3cacf98..0107ea6 100644 --- a/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt +++ b/src/main/kotlin/org/domaindrivenarchitecture/provs/framework/core/cli/CliUtils.kt @@ -14,31 +14,32 @@ import kotlin.system.exitProcess /** * Returns a Prov instance according to the targetCommand. - * E.g. it returns a local Prov instance if targetCommand.isValidLocalhost() is true or + * Returns a local Prov instance if targetCommand.isValidLocalhost() is true resp. * returns a remote Prov instance if targetCommand.isValidRemote() is true. - * - * If the target is remote and if parameter remoteHostSetSudoWithoutPasswordRequired is set to true, - * it will enable sudo without password on the remote machine (in case this was not yet enabled). */ fun createProvInstance(targetCommand: TargetCliCommand): Prov { if (targetCommand.isValid()) { val password: Secret? = targetCommand.remoteTarget()?.password val remoteTarget = targetCommand.remoteTarget() - if (targetCommand.isValidLocalhost()) { - return createLocalProvInstance() + + return if (targetCommand.isValidLocalhost()) { + createLocalProvInstance() } else if (targetCommand.isValidRemote() && remoteTarget != null) { - return createRemoteProvInstance( + createRemoteProvInstance( remoteTarget.host, remoteTarget.user, remoteTarget.password == null, password ) } else { - throw IllegalArgumentException("Error: neither a valid localHost nor a valid remoteHost was specified! Use option -h for help.") + throw IllegalArgumentException( + "Error: neither a valid localHost nor a valid remoteHost was specified! Use option -h for help." + ) } } else { - println("Invalid command line options.\nPlease use option -h for help.") + println("ERROR: Invalid target (${targetCommand.target}). Please use option -h for help.") + System.out.flush() exitProcess(1) } } @@ -46,11 +47,13 @@ fun createProvInstance(targetCommand: TargetCliCommand): Prov { private fun createLocalProvInstance(): Prov { val prov = local() if (!prov.currentUserCanSudoWithoutPassword()) { - val password = PromptSecretSource( - "Please enter password to configure sudo without password in the future." + - "\nWarning: This will permanently allow your user to use sudo privileges without a password." - ).secret() - prov.makeCurrentUserSudoerWithoutPasswordRequired(password) + val passwordNonNull = getPasswordToConfigureSudoWithoutPassword() + + prov.makeCurrentUserSudoerWithoutPasswordRequired(passwordNonNull) + + check(prov.currentUserCanSudoWithoutPassword()) { + "ERROR: User ${prov.whoami()} cannot sudo without enteringa password." + } } return prov } @@ -66,25 +69,37 @@ private fun createRemoteProvInstance( if (sshWithKey) { remote(host, remoteUser) } else { - require( - password != null, - { "No password available for provisioning without ssh keys. Either specify provisioning by ssh-keys or provide password." }) + require(password != null) { + "No password available for provisioning without ssh keys. " + + "Either specify provisioning by ssh-keys or provide password." + } remote(host, remoteUser, password) } - if (!prov.currentUserCanSudoWithoutPassword()) { - require( - password != null, - { "User ${prov.whoami()} not able to sudo on remote machine without password and no password available for the user." }) - prov.makeCurrentUserSudoerWithoutPasswordRequired(password) + return if (prov.currentUserCanSudoWithoutPassword()) { + prov + } else { - // a new session is required after making the user a sudoer without password - return remote(host, remoteUser, password) + val passwordNonNull = password + ?: getPasswordToConfigureSudoWithoutPassword() + + val result = prov.makeCurrentUserSudoerWithoutPasswordRequired(passwordNonNull) + + check(result.success) { + "Could not make user a sudoer without password required. (Maybe the provided password is incorrect.)" + } + + // a new session is required after the user has become a sudoer without password + val provWithNewSshClient = remote(host, remoteUser, password) + + check(provWithNewSshClient.currentUserCanSudoWithoutPassword()) { + "ERROR: User ${provWithNewSshClient.whoami()} on $host cannot sudo without entering a password." + } + + provWithNewSshClient } - return prov } - internal fun getPasswordToConfigureSudoWithoutPassword(): Secret { return PromptSecretSource("password to configure sudo without password.").secret() } \ No newline at end of file From eda6e6b218da45dd954bd38ad2adad6d15eb6a62 Mon Sep 17 00:00:00 2001 From: az Date: Sun, 26 Feb 2023 20:16:50 +0100 Subject: [PATCH 10/11] [skip ci] correct DesktopCliParsingSequence.md --- doc/DesktopCliParsingSequence.md | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/DesktopCliParsingSequence.md b/doc/DesktopCliParsingSequence.md index 5ba2960..025426e 100644 --- a/doc/DesktopCliParsingSequence.md +++ b/doc/DesktopCliParsingSequence.md @@ -24,7 +24,6 @@ CliArgumentsParser -> DesktopCliCommand : create(desktopType, cliTargetCmd, ...) CliArgumentsParser --> Application: desktopCliCommand Application -> DesktopCliCommand : isValid ? Application -> CliUtils : createProvInstance -ProvInstance <- CliUtils : create alt target.isValidLocal CliUtils -> CliUtils : createLocalProv else target.isValidRemote From d353dd1fc215e00e305d9610504e92ae9df9ff7b Mon Sep 17 00:00:00 2001 From: az Date: Sun, 26 Feb 2023 20:54:59 +0100 Subject: [PATCH 11/11] [skip ci] add CreateProvInstanceSequence.md --- doc/CreateProvInstanceSequence.md | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 doc/CreateProvInstanceSequence.md diff --git a/doc/CreateProvInstanceSequence.md b/doc/CreateProvInstanceSequence.md new file mode 100644 index 0000000..c3e18aa --- /dev/null +++ b/doc/CreateProvInstanceSequence.md @@ -0,0 +1,55 @@ +```plantuml +@startuml + +autonumber + +skinparam sequenceBox { + borderColor White +} + +participant Cli +participant Application +participant CliArgumentsParser +participant CliTargetCommand +participant CliUtils +participant "CliUtils\ncreateLocalProv" as CliUtilsL +participant "CliUtils\ncreateRemoteProv" as CliUtilsR +participant Prov +participant PromptSecretSource +participant User + +Cli -> Application ++ : main(args...) +Application -> CliArgumentsParser : parseCommand + +CliArgumentsParser -> CliTargetCommand : create() +Application -> CliUtils : createProvInstance( targetCliCommand ) +alt target.isValidLocal +CliUtils -> CliUtilsL : createLocalProv +CliUtilsL -> Prov : createLocalInstance +alt userCannotSudoWithoutPw +CliUtilsL -> PromptSecretSource : getPassword +CliUtilsL -> User : makeUserSudoWithoutPw +CliUtilsL --> CliUtils : provInstance +CliUtils --> Application : provInstance +end +else target.isValidRemote +CliUtils -> CliUtilsR : createRemoteProv +CliUtilsR -> Prov : createRemoteInstance +alt userCannotSudoWithoutPw +CliUtilsR -> PromptSecretSource : getPassword +CliUtilsR -> User : makeUserSudoWithoutPw +CliUtilsR -> Prov : createRemoteInstance\n[new ssh-client is required] +CliUtilsR --> CliUtils : provInstance +CliUtils --> Application : provInstance +end +end + +Application -> DesktopService1 : provisionDesktopCommand ( provInstance, desktopCliCommand ) + + +'DesktopService1 -> DesktopService2 : provisionDesktop( config ) +'DesktopService1 -> ConfigRepository : getConfig + +@enduml + +``` \ No newline at end of file