diff --git a/build.gradle b/build.gradle index 51615bb..08a8a33 100644 --- a/build.gradle +++ b/build.gradle @@ -46,20 +46,26 @@ configurations { dependencies { - compile 'org.codehaus.groovy:groovy-all:2.3.11' + compile 'org.codehaus.groovy:groovy-all:3.0.0' testCompile group: 'junit', name: 'junit', version: '4.12' - compile group: 'org.rundeck', name: 'rundeck-core', version: '2.10.1' - compile 'org.slf4j:slf4j-api:1.7.30' + compile group: 'org.rundeck', name: 'rundeck-core', version: '4.14.0-20230615' + compile group: 'org.rundeck', name: 'rundeck-storage-api', version: '4.14.0-20230615' + compile 'org.slf4j:slf4j-api:2.0.7' - pluginLibs( 'org.eclipse.jgit:org.eclipse.jgit:3.7.1.201504261725-r') { + pluginLibs( 'org.eclipse.jgit:org.eclipse.jgit:6.6.0.202305301015-r') { exclude module: 'slf4j-api' exclude module: 'jsch' exclude module: 'commons-logging' } - testCompile "org.codehaus.groovy:groovy-all:2.3.7" - testCompile "org.spockframework:spock-core:0.7-groovy-2.0" + pluginLibs( 'org.eclipse.jgit:org.eclipse.jgit.ssh.jsch:6.6.0.202305301015-r') { + exclude module: 'slf4j-api' + exclude module: 'commons-logging' + } + + testCompile "org.codehaus.groovy:groovy-all:3.0.0" + testCompile "org.spockframework:spock-core:2.4-M1-groovy-3.0" testCompile "cglib:cglib-nodep:2.2.2" testCompile 'org.objenesis:objenesis:1.4' } @@ -85,7 +91,7 @@ jar { task wrapper(type: Wrapper) { - gradleVersion = '3.3' - distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip" + gradleVersion = '3.3' + distributionUrl = "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip" } diff --git a/src/main/groovy/com/rundeck/plugin/GitFailureReason.groovy b/src/main/groovy/com/rundeck/plugin/GitFailureReason.groovy new file mode 100644 index 0000000..b6a91e1 --- /dev/null +++ b/src/main/groovy/com/rundeck/plugin/GitFailureReason.groovy @@ -0,0 +1,9 @@ +package com.rundeck.plugin + +import com.dtolabs.rundeck.core.execution.workflow.steps.FailureReason + +enum GitFailureReason implements FailureReason{ + + KeyStorage + +} \ No newline at end of file diff --git a/src/main/groovy/com/rundeck/plugin/GitManager.groovy b/src/main/groovy/com/rundeck/plugin/GitManager.groovy index 2dcb3bd..1d4bdba 100644 --- a/src/main/groovy/com/rundeck/plugin/GitManager.groovy +++ b/src/main/groovy/com/rundeck/plugin/GitManager.groovy @@ -10,6 +10,9 @@ import org.eclipse.jgit.transport.RemoteRefUpdate import org.eclipse.jgit.transport.URIish import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider import org.eclipse.jgit.util.FileUtils +import org.rundeck.app.spi.Services +import com.dtolabs.rundeck.core.storage.keys.KeyStorageTree +import com.rundeck.plugin.util.GitPluginUtil import java.nio.file.Files import java.nio.file.Path @@ -34,15 +37,20 @@ class GitManager { String sshPrivateKeyPath String gitPassword String gitURL + String gitPasswordKeyStoragePath + String gitSshKeyKeyStoragePath + Services services - GitManager(Properties configuration) { + GitManager(Properties configuration, Services services) { this.gitURL=configuration.getProperty(GitResourceModelFactory.GIT_URL) this.branch = configuration.getProperty(GitResourceModelFactory.GIT_BRANCH) this.fileName=configuration.getProperty(GitResourceModelFactory.GIT_FILE) this.strictHostKeyChecking=configuration.getProperty(GitResourceModelFactory.GIT_HOSTKEY_CHECKING) sshPrivateKeyPath=configuration.getProperty(GitResourceModelFactory.GIT_KEY_STORAGE) gitPassword=configuration.getProperty(GitResourceModelFactory.GIT_PASSWORD_STORAGE) - + this.gitPasswordKeyStoragePath=configuration.getProperty(GitResourceModelFactory.GIT_PASSWORD_KEY_STORAGE_PATH) + this.gitSshKeyKeyStoragePath=configuration.getProperty(GitResourceModelFactory.GIT_SSH_KEY_KEY_STORAGE_PATH) + this.services = services } Map getSshConfig() { @@ -130,6 +138,17 @@ class GitManager { def factory = new PluginSshSessionFactory(keyData) factory.sshConfig = sshConfig command.setTransportConfigCallback(factory) + } else if ((u.scheme == null || u.scheme == 'ssh') && u.user && gitSshKeyKeyStoragePath) { + KeyStorageTree keyStorage = services.getService(KeyStorageTree.class) + String key = GitPluginUtil.getPasswordFromKeyStorage(gitSshKeyKeyStoragePath, keyStorage) + byte[] keyData = key.getBytes(); + def factory = new PluginSshSessionFactory(keyData) + factory.sshConfig = sshConfig + command.setTransportConfigCallback(factory) + } else if (u.user && gitPasswordKeyStoragePath) { + KeyStorageTree keyStorage = services.getService(KeyStorageTree.class) + String key = GitPluginUtil.getPasswordFromKeyStorage(gitPasswordKeyStoragePath, keyStorage) + command.setCredentialsProvider(new UsernamePasswordCredentialsProvider(u.user, key)) } else if (u.user && gitPassword) { logger.debug("using password") diff --git a/src/main/groovy/com/rundeck/plugin/GitResourceModel.groovy b/src/main/groovy/com/rundeck/plugin/GitResourceModel.groovy index 190b7c6..dc841b6 100644 --- a/src/main/groovy/com/rundeck/plugin/GitResourceModel.groovy +++ b/src/main/groovy/com/rundeck/plugin/GitResourceModel.groovy @@ -10,6 +10,7 @@ import com.dtolabs.rundeck.core.resources.format.ResourceFormatParser import com.dtolabs.rundeck.core.resources.format.ResourceFormatParserException import com.dtolabs.rundeck.core.resources.format.UnsupportedFormatException import com.dtolabs.utils.Streams +import org.rundeck.app.spi.Services /** @@ -19,6 +20,7 @@ class GitResourceModel implements ResourceModelSource , WriteableModelSource{ private Properties configuration; private Framework framework; + Services services private boolean writable=false; String extension @@ -31,10 +33,10 @@ class GitResourceModel implements ResourceModelSource , WriteableModelSource{ this.writable=true; } - - GitResourceModel(Properties configuration, Framework framework) { + GitResourceModel(Properties configuration, Framework framework, Services services) { this.configuration = configuration this.framework = framework + this.services = services this.extension=configuration.getProperty(GitResourceModelFactory.GIT_FORMAT_FILE) this.writable=Boolean.valueOf(configuration.getProperty(GitResourceModelFactory.WRITABLE)) @@ -42,9 +44,8 @@ class GitResourceModel implements ResourceModelSource , WriteableModelSource{ this.localPath=configuration.getProperty(GitResourceModelFactory.GIT_BASE_DIRECTORY) if(gitManager==null){ - gitManager = new GitManager(configuration) + gitManager = new GitManager(configuration, services) } - } @Override diff --git a/src/main/groovy/com/rundeck/plugin/GitResourceModelFactory.groovy b/src/main/groovy/com/rundeck/plugin/GitResourceModelFactory.groovy index 3d40811..d200720 100644 --- a/src/main/groovy/com/rundeck/plugin/GitResourceModelFactory.groovy +++ b/src/main/groovy/com/rundeck/plugin/GitResourceModelFactory.groovy @@ -12,6 +12,8 @@ import com.dtolabs.rundeck.plugins.ServiceNameConstants import com.dtolabs.rundeck.plugins.descriptions.PluginDescription import com.dtolabs.rundeck.plugins.util.DescriptionBuilder import com.rundeck.plugin.util.GitPluginUtil +import org.rundeck.app.spi.Services + /** * Created by luistoledo on 12/18/17. @@ -38,11 +40,15 @@ class GitResourceModelFactory implements ResourceModelSourceFactory,Describable public final static String GIT_KEY_STORAGE="gitKeyPath" public final static String GIT_PASSWORD_STORAGE="gitPasswordPath" public static final String WRITABLE="writable"; + public static final String GIT_PASSWORD_KEY_STORAGE_PATH = "gitPasswordKeyStoragePath" + public static final String GIT_SSH_KEY_KEY_STORAGE_PATH = "gitSshKeyKeyStoragePath" + final static Map renderingOptionsAuthentication = GitPluginUtil.getRenderOpt("Authentication",false) final static Map renderingOptionsAuthenticationPassword = GitPluginUtil.getRenderOpt("Authentication",false, true) final static Map renderingOptionsConfig = GitPluginUtil.getRenderOpt("Configuration",false) + final static Map renderingOptionsAuthenticationStorage = GitPluginUtil.getRenderOpt("Authentication",false, false, true) GitResourceModelFactory(Framework framework) { this.framework = framework @@ -79,7 +85,11 @@ Some examples: If `yes`, require remote host SSH key is defined in the `~/.ssh/known_hosts` file, otherwise do not verify.''', false, "yes",GitResourceModelFactory.LIST_HOSTKEY_CHECKING,null, renderingOptionsAuthentication)) .property(PropertyUtil.string(GIT_KEY_STORAGE, "SSH Key Path", 'SSH Key Path', false, - null,null,null, renderingOptionsAuthentication)) + null,null,null, renderingOptionsAuthentication)) + .property(PropertyUtil.string(GIT_PASSWORD_KEY_STORAGE_PATH, "Password Key Storage Path", "Access password from Rundeck key storage path", false, + null,null,null, renderingOptionsAuthenticationStorage)) + .property(PropertyUtil.string(GIT_SSH_KEY_KEY_STORAGE_PATH, "SSH Key Storage Path", "Access SSH key from Rundeck key storage path", false, + null,null,null, renderingOptionsAuthenticationStorage)) .build() @@ -95,4 +105,13 @@ If `yes`, require remote host SSH key is defined in the `~/.ssh/known_hosts` fil return resource } + + @Override + ResourceModelSource createResourceModelSource(Services services, Properties configuration) throws ConfigurationException { + final GitResourceModel resource = new GitResourceModel(configuration,framework,services) + + return resource + } + + } diff --git a/src/main/groovy/com/rundeck/plugin/util/GitPluginUtil.groovy b/src/main/groovy/com/rundeck/plugin/util/GitPluginUtil.groovy index 970e22d..7966b9f 100644 --- a/src/main/groovy/com/rundeck/plugin/util/GitPluginUtil.groovy +++ b/src/main/groovy/com/rundeck/plugin/util/GitPluginUtil.groovy @@ -1,8 +1,11 @@ package com.rundeck.plugin.util +import com.dtolabs.rundeck.core.execution.workflow.steps.StepException import com.dtolabs.rundeck.core.plugins.configuration.StringRenderingConstants import com.dtolabs.rundeck.core.storage.ResourceMeta -import com.dtolabs.rundeck.plugins.step.PluginStepContext +import com.dtolabs.rundeck.core.storage.StorageTree +import com.rundeck.plugin.GitFailureReason + /** * Created by luistoledo on 12/18/17. @@ -26,4 +29,30 @@ class GitPluginUtil { return ret; } + static String getPasswordFromKeyStorage(String path, StorageTree storage) { + try{ + ResourceMeta contents = storage.getResource(path).getContents() + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream() + contents.writeContent(byteArrayOutputStream) + String password = new String(byteArrayOutputStream.toByteArray()) + + return password + }catch(Exception e){ + throw new StepException("error accessing ${path}: ${e.message}", GitFailureReason.KeyStorage) + } + + } + + static String getSshKeyFromKeyStorage(String path, StorageTree storage) { + try{ + ResourceMeta contents = storage.getResource(path).getContents() + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream() + contents.writeContent(byteArrayOutputStream) + return byteArrayOutputStream.toByteArray() + }catch(Exception e){ + throw new StepException("error accessing ${path}: ${e.message}", GitFailureReason.KeyStorage) + } + + } + } diff --git a/src/main/groovy/com/rundeck/plugin/util/PluginSshSessionFactory.groovy b/src/main/groovy/com/rundeck/plugin/util/PluginSshSessionFactory.groovy index e2c87fb..1c77aab 100644 --- a/src/main/groovy/com/rundeck/plugin/util/PluginSshSessionFactory.groovy +++ b/src/main/groovy/com/rundeck/plugin/util/PluginSshSessionFactory.groovy @@ -4,8 +4,8 @@ import com.jcraft.jsch.JSch import com.jcraft.jsch.JSchException import com.jcraft.jsch.Session import org.eclipse.jgit.api.TransportConfigCallback -import org.eclipse.jgit.transport.JschConfigSessionFactory -import org.eclipse.jgit.transport.OpenSshConfig +import org.eclipse.jgit.transport.ssh.jsch.JschConfigSessionFactory +import org.eclipse.jgit.transport.ssh.jsch.OpenSshConfig import org.eclipse.jgit.transport.SshTransport import org.eclipse.jgit.transport.Transport import org.eclipse.jgit.util.FS diff --git a/src/test/groovy/com/rundeck/plugin/GitResourceModelSpec.groovy b/src/test/groovy/com/rundeck/plugin/GitResourceModelSpec.groovy index ca9acbb..b3f869f 100644 --- a/src/test/groovy/com/rundeck/plugin/GitResourceModelSpec.groovy +++ b/src/test/groovy/com/rundeck/plugin/GitResourceModelSpec.groovy @@ -5,7 +5,6 @@ import com.dtolabs.rundeck.core.common.INodeSet import com.dtolabs.rundeck.core.resources.format.ResourceFormatParser import com.dtolabs.rundeck.core.resources.format.ResourceFormatParserService import spock.lang.Specification -import org.apache.log4j.Logger /** * Created by luistoledo on 12/22/17.