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>