You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airavata.apache.org by ma...@apache.org on 2019/10/11 20:42:09 UTC
[airavata-custos] 02/24: Initial credential store framework
This is an automated email from the ASF dual-hosted git repository.
machristie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git
commit d20c5fc1f758e526606b321ab2cdbe738642bae3
Author: Dimuthu Wannipurage <di...@gmail.com>
AuthorDate: Tue Jul 9 01:10:53 2019 -0400
Initial credential store framework
---
.gitignore | 3 +
credntial-store/credential-api/pom.xml | 40 ++++++
.../apache/custos/credential/api/Application.java | 14 ++
.../api/controllers/SSHCredentialsController.java | 27 ++++
.../credential/api/resources/SSHCredential.java | 32 +++++
credntial-store/credential-core/pom.xml | 33 +++++
.../custos/credentials/BaseCredentialEntity.java | 22 +++
.../credentials/ssh/SSHCredentialEntity.java | 39 ++++++
.../apache/airavata/custos/vault/VaultManager.java | 156 +++++++++++++++++++++
.../custos/vault/annotations/VaultPath.java | 14 ++
credntial-store/pom.xml | 25 ++++
pom.xml | 111 +++++++++++++++
12 files changed, 516 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6366460
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.idea/*
+*.iml
+target/*
diff --git a/credntial-store/credential-api/pom.xml b/credntial-store/credential-api/pom.xml
new file mode 100644
index 0000000..f559740
--- /dev/null
+++ b/credntial-store/credential-api/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>credntial-store</artifactId>
+ <groupId>org.apache.airavata.custos</groupId>
+ <version>0.01-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>credential-api</artifactId>
+
+ <properties>
+ <java.version>1.8</java.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>net.sf.dozer</groupId>
+ <artifactId>dozer</artifactId>
+ <version>5.5.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.airavata.custos</groupId>
+ <artifactId>credential-core</artifactId>
+ <version>0.01-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java
new file mode 100644
index 0000000..d4f7ce4
--- /dev/null
+++ b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/Application.java
@@ -0,0 +1,14 @@
+package org.apache.custos.credential.api;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+
+@SpringBootApplication
+@ComponentScan(basePackages = {"org.apache.custos", "org.apache.airavata"})
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
\ No newline at end of file
diff --git a/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
new file mode 100644
index 0000000..cc5094f
--- /dev/null
+++ b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/controllers/SSHCredentialsController.java
@@ -0,0 +1,27 @@
+package org.apache.custos.credential.api.controllers;
+
+import org.apache.airavata.custos.credentials.ssh.SSHCredentialEntity;
+import org.apache.airavata.custos.vault.VaultManager;
+import org.apache.custos.credential.api.resources.SSHCredential;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class SSHCredentialsController {
+
+ @Autowired
+ private VaultManager vaultManager;
+
+ @RequestMapping("/ssh/{gateway}/{token}")
+ public SSHCredential getSSHCredential(@PathVariable("gateway") String gateway, @PathVariable("token") String token) throws Exception {
+ SSHCredentialEntity credentialEntity = vaultManager.getCredentialEntity(SSHCredentialEntity.class, token, gateway);
+ SSHCredential resource = new SSHCredential();
+ resource.setPassphrase(credentialEntity.getPassphrase());
+ resource.setPrivateKey(credentialEntity.getPrivateKey());
+ resource.setPublicKey(credentialEntity.getPublicKey());
+ return resource;
+ }
+}
diff --git a/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java
new file mode 100644
index 0000000..e80e77a
--- /dev/null
+++ b/credntial-store/credential-api/src/main/java/org/apache/custos/credential/api/resources/SSHCredential.java
@@ -0,0 +1,32 @@
+package org.apache.custos.credential.api.resources;
+
+public class SSHCredential {
+
+ private String privateKey;
+ private String publicKey;
+ private String passphrase;
+
+ public String getPrivateKey() {
+ return privateKey;
+ }
+
+ public void setPrivateKey(String privateKey) {
+ this.privateKey = privateKey;
+ }
+
+ public String getPublicKey() {
+ return publicKey;
+ }
+
+ public void setPublicKey(String publicKey) {
+ this.publicKey = publicKey;
+ }
+
+ public String getPassphrase() {
+ return passphrase;
+ }
+
+ public void setPassphrase(String passphrase) {
+ this.passphrase = passphrase;
+ }
+}
diff --git a/credntial-store/credential-core/pom.xml b/credntial-store/credential-core/pom.xml
new file mode 100644
index 0000000..b70ef4b
--- /dev/null
+++ b/credntial-store/credential-core/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>credntial-store</artifactId>
+ <groupId>org.apache.airavata.custos</groupId>
+ <version>0.01-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>credential-core</artifactId>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>com.bettercloud</groupId>
+ <artifactId>vault-java-driver</artifactId>
+ <version>4.0.0</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java
new file mode 100644
index 0000000..45ba2aa
--- /dev/null
+++ b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/BaseCredentialEntity.java
@@ -0,0 +1,22 @@
+package org.apache.airavata.custos.credentials;
+
+public class BaseCredentialEntity {
+ private String token;
+ private String gateway;
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public String getGateway() {
+ return gateway;
+ }
+
+ public void setGateway(String gateway) {
+ this.gateway = gateway;
+ }
+}
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
new file mode 100644
index 0000000..1e8ddcf
--- /dev/null
+++ b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/credentials/ssh/SSHCredentialEntity.java
@@ -0,0 +1,39 @@
+package org.apache.airavata.custos.credentials.ssh;
+
+import org.apache.airavata.custos.credentials.BaseCredentialEntity;
+import org.apache.airavata.custos.vault.annotations.VaultPath;
+
+public class SSHCredentialEntity extends BaseCredentialEntity {
+ @VaultPath(path = "secret/ssh/{gateway}/{token}", name = "private", required = true)
+ private String privateKey;
+
+ @VaultPath(path = "secret/ssh/{gateway}/{token}", name = "passphrase")
+ private String passphrase;
+
+ @VaultPath(path = "secret/ssh/{gateway}/{token}", name = "public", required = true)
+ private String publicKey;
+
+ public String getPrivateKey() {
+ return privateKey;
+ }
+
+ public void setPrivateKey(String privateKey) {
+ this.privateKey = privateKey;
+ }
+
+ public String getPassphrase() {
+ return passphrase;
+ }
+
+ public void setPassphrase(String passphrase) {
+ this.passphrase = passphrase;
+ }
+
+ public String getPublicKey() {
+ return publicKey;
+ }
+
+ public void setPublicKey(String publicKey) {
+ this.publicKey = publicKey;
+ }
+}
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
new file mode 100644
index 0000000..4e4baf4
--- /dev/null
+++ b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/VaultManager.java
@@ -0,0 +1,156 @@
+package org.apache.airavata.custos.vault;
+
+import com.bettercloud.vault.Vault;
+import com.bettercloud.vault.VaultConfig;
+import com.bettercloud.vault.VaultException;
+import com.bettercloud.vault.response.LogicalResponse;
+import org.apache.airavata.custos.credentials.BaseCredentialEntity;
+import org.apache.airavata.custos.credentials.ssh.SSHCredentialEntity;
+import org.apache.airavata.custos.vault.annotations.VaultPath;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import java.lang.reflect.Field;
+import java.util.*;
+
+@Service
+public class VaultManager {
+
+ private String vaultAddress = "http://127.0.0.1:8200";
+ private String vaultToken = "s.PFc3SbOz1N2wSpV5hWZ56yVI";
+
+ private Vault vault;
+
+ @PostConstruct
+ public void init() throws VaultException {
+ final VaultConfig config = new VaultConfig().address(vaultAddress).token(vaultToken).build();
+ vault = new Vault(config);
+ }
+
+ public <T extends BaseCredentialEntity> T getCredentialEntity(Class<T> calzz, final String token, final String gateway) throws Exception {
+
+ Map<String, String> params = new HashMap<String, String>() {{
+ put("token", token);
+ put("gateway", gateway);
+ }};
+
+ for (Class<?> c = calzz; c != null; c = c.getSuperclass()) {
+ Field[] fields = c.getDeclaredFields();
+ for (Field field : fields) {
+ VaultPath vaultPathAnnotation = field.getAnnotation(VaultPath.class);
+ if (vaultPathAnnotation != null) {
+ String path = populatePathWithParams(vaultPathAnnotation.path(), params);
+ Map<String,String> data = vault.logical().read(path).getData();
+ System.out.println("Value for key " + vaultPathAnnotation.name() + " " + data.get(vaultPathAnnotation.name()));
+ }
+ }
+ }
+ return null;
+ }
+
+ public <T extends BaseCredentialEntity> String saveCredentialEntity(final T credentialEntity, final String gateway) throws Exception {
+ final String token = UUID.randomUUID().toString();
+
+ credentialEntity.setGateway(gateway);
+ credentialEntity.setToken(token);
+
+ Map<String, String> params = new HashMap<String, String>() {{
+ put("token", token);
+ put("gateway", gateway);
+ }};
+
+ Map<String, Map<String, Object>> summary = new HashMap<>();
+
+ for (Class<?> c = credentialEntity.getClass(); c != null; c = c.getSuperclass()) {
+ Field[] fields = c.getDeclaredFields();
+ for (Field field : fields) {
+ VaultPath vaultPathAnnotation = field.getAnnotation(VaultPath.class);
+ if (vaultPathAnnotation != null) {
+ field.setAccessible(true);
+ String vaultPathValue = (String ) field.get(credentialEntity);
+ String path = populatePathWithParams(vaultPathAnnotation.path(), params);
+ Map map = summary.computeIfAbsent(path, (k) -> new HashMap());
+ map.put(vaultPathAnnotation.name(), vaultPathValue);
+ }
+ }
+ }
+
+ for (Map.Entry<String, Map<String, Object>> entry : summary.entrySet()) {
+ vault.logical().write(entry.getKey(), entry.getValue());
+ }
+ return token;
+ }
+
+ /**
+ * This will resolve parameterized path strings with the provided param map. Example path is like secret/{gateway}/{token}/value.
+ * Params map should provide the values for gateway and token parameters.
+ *
+ * @param path Path with parameters
+ * @param params Parameter map
+ * @return Resolved path
+ * @throws Exception if the path is not in correct format or required parameters are not found in the params map
+ */
+ private String populatePathWithParams(String path, Map<String, String> params) throws Exception {
+ String newPath = "";
+ int begin = 0;
+ while (true) {
+ int startPos = path.indexOf("{", begin);
+ if (startPos != -1) {
+ int endPos = path.indexOf("}" , begin);
+ if (endPos == -1) {
+ throw new Exception("Path " + path + " is not in the correct format");
+ } else {
+ newPath += path.substring(begin, startPos);
+ String paramName = path.substring(startPos + 1, endPos);
+ String paramValue = params.get(paramName);
+ if (paramValue == null) {
+ throw new Exception("Parameter can not be found for name " + paramName + " in path " + path);
+ }
+ newPath += paramValue;
+ begin = endPos + 1;
+ }
+ } else {
+ return begin == 0? path: newPath + path.substring(begin);
+ }
+ }
+ }
+
+ public String getVaultAddress() {
+ return vaultAddress;
+ }
+
+ public void setVaultAddress(String vaultAddress) {
+ this.vaultAddress = vaultAddress;
+ }
+
+ public String getVaultToken() {
+ return vaultToken;
+ }
+
+ public void setVaultToken(String vaultToken) {
+ this.vaultToken = vaultToken;
+ }
+
+ public static void main(String args[]) throws Exception {
+ final VaultConfig config = new VaultConfig()
+ .address("http://127.0.0.1:8200")
+ .token("s.PFc3SbOz1N2wSpV5hWZ56yVI")
+ .build();
+
+ final Vault vault = new Vault(config);
+
+ Map<String, String> data = vault.logical().read("secret/hello").getData();
+ //System.out.println(data);
+
+ VaultManager manager = new VaultManager();
+ manager.init();
+
+ SSHCredentialEntity credentialEntity = new SSHCredentialEntity();
+ credentialEntity.setPrivateKey("def");
+ credentialEntity.setPublicKey("ddfdfef");
+ credentialEntity.setPassphrase("11111");
+ String token = manager.saveCredentialEntity(credentialEntity, "seagrid");
+ //System.out.println(token);
+ SSHCredentialEntity c = manager.getCredentialEntity(SSHCredentialEntity.class, "1a5a8ba8-384b-40ab-8be4-577a1cdb02c3", "seagrid");
+ }
+}
diff --git a/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java
new file mode 100644
index 0000000..b36d4bb
--- /dev/null
+++ b/credntial-store/credential-core/src/main/java/org/apache/airavata/custos/vault/annotations/VaultPath.java
@@ -0,0 +1,14 @@
+package org.apache.airavata.custos.vault.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface VaultPath {
+ public String name();
+ public String path();
+ public boolean required() default false;
+}
diff --git a/credntial-store/pom.xml b/credntial-store/pom.xml
new file mode 100644
index 0000000..52eb721
--- /dev/null
+++ b/credntial-store/pom.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.airavata.custos</groupId>
+ <artifactId>credntial-store</artifactId>
+ <packaging>pom</packaging>
+ <version>0.01-SNAPSHOT</version>
+ <name>Custos Credential Store</name>
+
+ <modules>
+ <module>credential-core</module>
+ <module>credential-api</module>
+ </modules>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-web</artifactId>
+ <version>2.1.6.RELEASE</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..a141d16
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <prerequisites>
+ <maven>3.0</maven>
+ </prerequisites>
+
+ <parent>
+ <groupId>org.apache</groupId>
+ <artifactId>apache</artifactId>
+ <version>21</version>
+ </parent>
+
+ <groupId>org.apache.airavata</groupId>
+ <artifactId>airavata-custos</artifactId>
+ <packaging>pom</packaging>
+ <name>Airavata Custos</name>
+ <version>0.01-SNAPSHOT</version>
+
+ <url>http://airavata.apache.org/</url>
+ <inceptionYear>2011</inceptionYear>
+
+ <scm>
+ <connection>scm:git:https://github.com/apache/airavata.git</connection>
+ <developerConnection>scm:git:https://github.com/apache/airavata.git</developerConnection>
+ <url>https://github.com/apache/airavata</url>
+ <tag>HEAD</tag>
+ </scm>
+
+ <mailingLists>
+
+ <mailingList>
+ <name>Airavata Custos Developer List</name>
+ <subscribe>custos-subscribe@airavata.apache.org</subscribe>
+ <unsubscribe>custos-unsubscribe@airavata.apache.org</unsubscribe>
+ <post>mailto:custos@airavata.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/airavata-custos/</archive>
+ </mailingList>
+
+ </mailingLists>
+
+ <dependencyManagement>
+ <dependencies>
+ </dependencies>
+ </dependencyManagement>
+
+ <profiles>
+ <profile>
+ <id>default</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.6.1</version>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <activation>
+ <activeByDefault>true</activeByDefault>
+ </activation>
+ <modules>
+ <module>credntial-store</module>
+ </modules>
+ </profile>
+ </profiles>
+
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.9.1</version>
+ <configuration>
+ <aggregate>true</aggregate>
+ <quiet>true</quiet>
+ <minmemory>256m</minmemory>
+ <maxmemory>2g</maxmemory>
+ </configuration>
+ </plugin>
+ </plugins>
+ </reporting>
+
+</project>