Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
ltamaster authored Aug 23, 2018
2 parents bc10961 + 2774f1c commit 0aa659b
Show file tree
Hide file tree
Showing 29 changed files with 1,119 additions and 211 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ dependencies {
// add any third-party jar dependencies you wish to include in the plugin
// using the `pluginLibs` configuration as shown here:

pluginLibs group: 'com.bettercloud', name: 'vault-java-driver', version: '3.0.0', ext: 'jar'
pluginLibs group: 'com.bettercloud', name: 'vault-java-driver', version: '3.1.0', ext: 'jar'


//the compile dependency won't add the rundeck-core jar to the plugin contents
Expand Down
1 change: 1 addition & 0 deletions run-docker-vault-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ run_tests(){

export RUNDECK_VERSION=$RUNDECK_VERSION
bash $DIR/test-vault.sh
bash $DIR/test-existing-vault.sh
}
run_docker_test(){
local FARGS=("$@")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ class ConfigOptions {
static final String VAULT_APPROLE_ID = "approleId";
static final String VAULT_APPROLE_SECRET_ID = "approleSecretId";
static final String VAULT_SECRET_BACKEND = "secretBackend";
static final String VAULT_STORAGE_BEHAVIOUR = "storageBehaviour";
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ static Description getDescription() {
.description("The secret backend to use in vault")
.defaultValue("secret")
)
.property(PropertyBuilder.builder()
.string(VAULT_STORAGE_BEHAVIOUR)
.title("Storage Behaviour")
.description("Use the default Rundeck Behaviour for key storage (with rundeck headers) or use just the key/value behaviour from vault. Options are: rundeck, vault")
.defaultValue("rundeck")
)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package io.github.valfadeev.rundeck.plugin.vault;

import com.bettercloud.vault.api.Logical;
import com.dtolabs.rundeck.core.storage.ResourceMeta;
import org.rundeck.storage.api.Path;
import org.rundeck.storage.impl.ResourceBase;

import java.io.ByteArrayOutputStream;
import java.util.Map;

public abstract class KeyObject {

protected boolean rundeckObject;
protected boolean multiplesKeys;
protected Map<String, String> payload;
protected Map<String, Object> keys;
protected Path path;

protected boolean error;
protected String errorMessage;

abstract Map<String, Object> saveResource(ResourceMeta content, String event, ByteArrayOutputStream baoStream);
abstract ResourceBase loadResource();
abstract boolean delete(Logical vault,String vaultPrefix);

//empty object or null object
public KeyObject(Path path) {
this.path=path;
}

public boolean isRundeckObject() {
return rundeckObject;
}

public void setRundeckObject(final boolean rundeckObject) {
this.rundeckObject = rundeckObject;
}

public boolean isMultiplesKeys() {
return multiplesKeys;
}

public void setMultiplesKeys(final boolean multiplesKeys) {
this.multiplesKeys = multiplesKeys;
}

public Map<String, String> getPayload() {
return payload;
}

public void setPayload(final Map<String, String> payload) {
this.payload = payload;
}

public Map<String, Object> getKeys() {
return keys;
}

public void setKeys(final Map<String, Object> keys) {
this.keys = keys;
}

public Path getPath() {
return path;
}

public void setPath(final Path path) {
this.path = path;
}

public String getErrorMessage() {
return errorMessage;
}

public void setErrorMessage(final String errorMessage) {
this.errorMessage = errorMessage;
}

public boolean isError() {
return error;
}

public void setError(final boolean error) {
this.error = error;
}

@Override
public String toString() {
return "KeyObject{" +
"rundeckObject=" + rundeckObject +
", multiplesKeys=" + multiplesKeys +
", payload=" + payload +
", keys=" + keys +
", path=" + path +
", error=" + error +
", errorMessage='" + errorMessage + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package io.github.valfadeev.rundeck.plugin.vault;

import com.bettercloud.vault.VaultException;
import com.bettercloud.vault.api.Logical;
import com.bettercloud.vault.response.LogicalResponse;
import org.rundeck.storage.api.Path;
import org.rundeck.storage.api.PathUtil;

public class KeyObjectBuilder {

Path path;
Logical vault;
String vaultPrefix;

static KeyObjectBuilder builder() {
return new KeyObjectBuilder();
}

KeyObjectBuilder path(Path path){
this.path = path;
return this;
}

KeyObjectBuilder vault(Logical vault){
this.vault = vault;
return this;
}

KeyObjectBuilder vaultPrefix(String vaultPrefix){
this.vaultPrefix = vaultPrefix;
return this;
}

private String getVaultPath(String rawPath) {
return String.format("secret/%s/%s", vaultPrefix, rawPath);
}

KeyObject build(){
LogicalResponse response;
KeyObject object;
try {
response = vault.read(getVaultPath(path.getPath()));
String data = response.getData().get(VaultStoragePlugin.VAULT_STORAGE_KEY);

if(data !=null) {
object = new RundeckKey(response,path);
}else{
object = new VaultKey(response,path);
}

} catch (VaultException e) {
object = new RundeckKey(path);
object.setErrorMessage(e.getMessage());
object.setError(true);
}

//check if parent path exists (vault entry with multiples keys)
//multiples keys inside a secret will be reading on Rundeck as different keys inside a folder
if(object.isError()) {
KeyObject parentObject=getVaultParentObject(path);

if(parentObject!=null) {
object = new VaultKey(path, parentObject);
Path parentPath = PathUtil.parentPath(path);
String key = PathUtil.removePrefix(parentPath.toString(), path.toString());

object.setError(false);
object.setErrorMessage(null);
object.setMultiplesKeys(true);

if (parentObject.getKeys().containsKey(key)) {
object.getKeys().put(key, parentObject.getKeys().get(key));
}
}
}

return object;
}

public KeyObject getVaultParentObject(Path path){
KeyObject parentObject=null;
LogicalResponse response;

Path parentPath = PathUtil.parentPath(path);
try {
response = vault.read(getVaultPath(parentPath.getPath()));
parentObject=new VaultKey(response, parentPath);
} catch (VaultException e) {

}

return parentObject;
}
}
121 changes: 121 additions & 0 deletions src/main/java/io/github/valfadeev/rundeck/plugin/vault/RundeckKey.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package io.github.valfadeev.rundeck.plugin.vault;

import com.bettercloud.vault.VaultException;
import com.bettercloud.vault.api.Logical;
import com.bettercloud.vault.response.LogicalResponse;
import com.dtolabs.rundeck.core.storage.ResourceMeta;
import com.dtolabs.rundeck.core.storage.ResourceMetaBuilder;
import com.dtolabs.rundeck.core.storage.StorageUtil;
import org.rundeck.storage.api.Path;
import org.rundeck.storage.api.Resource;
import org.rundeck.storage.api.StorageException;
import org.rundeck.storage.impl.ResourceBase;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

public class RundeckKey extends KeyObject {

public RundeckKey(LogicalResponse response, Path path) {
super(path);
this.payload = response.getData();
this.path = path;
this.rundeckObject=true;
this.multiplesKeys=false;
}

public RundeckKey(final Path path) {
super(path);
}

public Map<String, Object> saveResource(ResourceMeta content, String event, ByteArrayOutputStream baoStream){

Map<String, Object> payload = new HashMap<>();

Map<String, String> meta = content.getMeta();

for (String k : meta.keySet()) {
payload.put(k, meta.get(k));
}

if (event.equals("update")) {
DateFormat df = new SimpleDateFormat(StorageUtil.ISO_8601_FORMAT, Locale.ENGLISH);
Resource<ResourceMeta> existing = this.loadResource();
payload.put(
StorageUtil.RES_META_RUNDECK_CONTENT_CREATION_TIME,
df.format(existing.getContents().getCreationTime())
);
}

try {
String data = baoStream.toString("UTF-8");
payload.put(VaultStoragePlugin.VAULT_STORAGE_KEY, data);
} catch (UnsupportedEncodingException e) {
throw new StorageException(
String.format(
"Encountered unsupported encoding error: %s",
e.getMessage()
),
StorageException.Event.valueOf(event.toUpperCase()),
this.getPath()
);
}

return payload;
}

ResourceBase loadResource(){
Map<String, String> payload = this.getPayload();
String data = payload.get(VaultStoragePlugin.VAULT_STORAGE_KEY);

ResourceMetaBuilder builder = new ResourceMetaBuilder();
builder.setContentLength(Long.parseLong(payload.get(StorageUtil.RES_META_RUNDECK_CONTENT_LENGTH)));
builder.setContentType(payload.get(StorageUtil.RES_META_RUNDECK_CONTENT_TYPE));

DateFormat df = new SimpleDateFormat(StorageUtil.ISO_8601_FORMAT, Locale.ENGLISH);
try {
builder.setCreationTime(df.parse(payload.get(StorageUtil.RES_META_RUNDECK_CONTENT_CREATION_TIME)));
builder.setModificationTime(df.parse(payload.get(StorageUtil.RES_META_RUNDECK_CONTENT_MODIFY_TIME)));
} catch (ParseException e) {
}

String type = payload.get(StorageUtil.RES_META_RUNDECK_CONTENT_TYPE);
if (type.equals(VaultStoragePlugin.PRIVATE_KEY_MIME_TYPE)) {
builder.setMeta(VaultStoragePlugin.RUNDECK_CONTENT_MASK, "content");
builder.setMeta(VaultStoragePlugin.RUNDECK_KEY_TYPE, "private");
}
else if (type.equals(VaultStoragePlugin.PUBLIC_KEY_MIME_TYPE)) {
builder.setMeta(VaultStoragePlugin.RUNDECK_KEY_TYPE, "public");
}
else if (type.equals(VaultStoragePlugin.PASSWORD_MIME_TYPE)) {
builder.setMeta(VaultStoragePlugin.RUNDECK_CONTENT_MASK, "content");
builder.setMeta(VaultStoragePlugin.RUNDECK_DATA_TYPE, "password");
}

ByteArrayInputStream baiStream = new ByteArrayInputStream(data.getBytes());
return new ResourceBase<>(
this.getPath(),
StorageUtil.withStream(baiStream, builder.getResourceMeta()),
false
);
}

@Override
boolean delete(final Logical vault,String vaultPrefix) {

try {
vault.delete(VaultStoragePlugin.getVaultPath(path.getPath(),vaultPrefix));
return true;
} catch (VaultException e) {
return false;
}

}
}
Loading

0 comments on commit 0aa659b

Please sign in to comment.