summaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorKiran Kamineni <kiran.k.kamineni@intel.com>2018-05-04 16:50:39 -0700
committerGirish Havaldar <hg0071052@techmahindra.com>2018-05-15 17:21:53 +0000
commitac134cb099afa6be09fbc5cdad5db0bcf7aee08a (patch)
treecbcdf280db9285edfe8c6e62e094c6c4c6db302e /docs
parentd4b81c05a255a847bbf7f08caebe032492ad2ca5 (diff)
Adding a docs folder under sms repo
WORK IN PROGRESS Adding a docs folder under sms Issue-ID: AAF-185 Change-Id: I5ee3560cfda2100ad5207bb7e98d5cb9472e1325 Signed-off-by: Girish Havaldar <hg0071052@techmahindra.com>
Diffstat (limited to 'docs')
-rw-r--r--docs/api_swagger.html732
-rw-r--r--docs/api_swagger.yaml288
-rw-r--r--docs/apiswagger.rst745
-rw-r--r--docs/coverage.html1471
-rw-r--r--docs/coverage.md41
-rw-r--r--docs/index.rst37
-rw-r--r--docs/installation.rst33
-rw-r--r--docs/sms_high_level.pngbin0 -> 141997 bytes
-rw-r--r--docs/usage.rst54
9 files changed, 3401 insertions, 0 deletions
diff --git a/docs/api_swagger.html b/docs/api_swagger.html
new file mode 100644
index 0000000..7f987a3
--- /dev/null
+++ b/docs/api_swagger.html
@@ -0,0 +1,732 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Secret Management Service</title>
+ <style type="text/css">
+ body {
+ font-family: Trebuchet MS, sans-serif;
+ font-size: 15px;
+ color: #444;
+ margin-right: 24px;
+}
+
+h1 {
+ font-size: 25px;
+}
+h2 {
+ font-size: 20px;
+}
+h3 {
+ font-size: 16px;
+ font-weight: bold;
+}
+hr {
+ height: 1px;
+ border: 0;
+ color: #ddd;
+ background-color: #ddd;
+}
+
+.app-desc {
+ clear: both;
+ margin-left: 20px;
+}
+.param-name {
+ width: 100%;
+}
+.license-info {
+ margin-left: 20px;
+}
+
+.license-url {
+ margin-left: 20px;
+}
+
+.model {
+ margin: 0 0 0px 20px;
+}
+
+.method {
+ margin-left: 20px;
+}
+
+.method-notes {
+ margin: 10px 0 20px 0;
+ font-size: 90%;
+ color: #555;
+}
+
+pre {
+ padding: 10px;
+ margin-bottom: 2px;
+}
+
+.http-method {
+ text-transform: uppercase;
+}
+
+pre.get {
+ background-color: #0f6ab4;
+}
+
+pre.post {
+ background-color: #10a54a;
+}
+
+pre.put {
+ background-color: #c5862b;
+}
+
+pre.delete {
+ background-color: #a41e22;
+}
+
+.huge {
+ color: #fff;
+}
+
+pre.example {
+ background-color: #f3f3f3;
+ padding: 10px;
+ border: 1px solid #ddd;
+}
+
+code {
+ white-space: pre;
+}
+
+.nickname {
+ font-weight: bold;
+}
+
+.method-path {
+ font-size: 1.5em;
+ background-color: #0f6ab4;
+}
+
+.up {
+ float:right;
+}
+
+.parameter {
+ width: 500px;
+}
+
+.param {
+ width: 500px;
+ padding: 10px 0 0 20px;
+ font-weight: bold;
+}
+
+.param-desc {
+ width: 700px;
+ padding: 0 0 0 20px;
+ color: #777;
+}
+
+.param-type {
+ font-style: italic;
+}
+
+.param-enum-header {
+width: 700px;
+padding: 0 0 0 60px;
+color: #777;
+font-weight: bold;
+}
+
+.param-enum {
+width: 700px;
+padding: 0 0 0 80px;
+color: #777;
+font-style: italic;
+}
+
+.field-label {
+ padding: 0;
+ margin: 0;
+ clear: both;
+}
+
+.field-items {
+ padding: 0 0 15px 0;
+ margin-bottom: 15px;
+}
+
+.return-type {
+ clear: both;
+ padding-bottom: 10px;
+}
+
+.param-header {
+ font-weight: bold;
+}
+
+.method-tags {
+ text-align: right;
+}
+
+.method-tag {
+ background: none repeat scroll 0% 0% #24A600;
+ border-radius: 3px;
+ padding: 2px 10px;
+ margin: 2px;
+ color: #FFF;
+ display: inline-block;
+ text-decoration: none;
+}
+
+ </style>
+ </head>
+ <body>
+ <h1>Secret Management Service</h1>
+ <div class="app-desc">This is a service that provides secret management facilities</div>
+ <div class="app-desc">More information: <a href="https://helloreverb.com">https://helloreverb.com</a></div>
+ <div class="app-desc">Contact Info: <a href="kiran.k.kamineni@intel.com">kiran.k.kamineni@intel.com</a></div>
+ <div class="app-desc">Version: 1.0.0</div>
+ <div class="app-desc">BasePath:/v1/sms/</div>
+ <div class="license-info">Apache 2.0</div>
+ <div class="license-url">http://www.apache.org/licenses/LICENSE-2.0.html</div>
+ <h2>Access</h2>
+ <ol>
+ <li>APIKey KeyParamName:token KeyInQuery:false KeyInHeader:true</li>
+ </ol>
+
+ <h2><a name="__Methods">Methods</a></h2>
+ [ Jump to <a href="#__Models">Models</a> ]
+
+ <h3>Table of Contents </h3>
+ <div class="method-summary"></div>
+ <h4><a href="#Domain">Domain</a></h4>
+ <ul>
+ <li><a href="#domainDomainNameDelete"><code><span class="http-method">delete</span> /domain/{domainName}</code></a></li>
+ <li><a href="#domainPost"><code><span class="http-method">post</span> /domain</code></a></li>
+ </ul>
+ <h4><a href="#Login">Login</a></h4>
+ <ul>
+ <li><a href="#loginPost"><code><span class="http-method">post</span> /login</code></a></li>
+ </ul>
+ <h4><a href="#Secret">Secret</a></h4>
+ <ul>
+ <li><a href="#domainDomainNameSecretGet"><code><span class="http-method">get</span> /domain/{domainName}/secret</code></a></li>
+ <li><a href="#domainDomainNameSecretPost"><code><span class="http-method">post</span> /domain/{domainName}/secret</code></a></li>
+ <li><a href="#domainDomainNameSecretSecretNameDelete"><code><span class="http-method">delete</span> /domain/{domainName}/secret/{secretName}</code></a></li>
+ <li><a href="#domainDomainNameSecretSecretNameGet"><code><span class="http-method">get</span> /domain/{domainName}/secret/{secretName}</code></a></li>
+ </ul>
+ <h4><a href="#System">System</a></h4>
+ <ul>
+ <li><a href="#statusGet"><code><span class="http-method">get</span> /status</code></a></li>
+ <li><a href="#unsealPost"><code><span class="http-method">post</span> /unseal</code></a></li>
+ </ul>
+
+ <h1><a name="Domain">Domain</a></h1>
+ <div class="method"><a name="domainDomainNameDelete"/>
+ <div class="method-path">
+ <a class="up" href="#__Methods">Up</a>
+ <pre class="delete"><code class="huge"><span class="http-method">delete</span> /domain/{domainName}</code></pre></div>
+ <div class="method-summary">Deletes a domain by name (<span class="nickname">domainDomainNameDelete</span>)</div>
+ <div class="method-notes">Deletes a domain with provided name</div>
+
+ <h3 class="field-label">Path parameters</h3>
+ <div class="field-items">
+ <div class="param">domainName (required)</div>
+
+ <div class="param-desc"><span class="param-type">Path Parameter</span> &mdash; Name of the domain </div>
+ </div> <!-- field-items -->
+
+
+
+
+
+
+
+ <!--Todo: process Response Object and its headers, schema, examples -->
+
+
+ <h3 class="field-label">Produces</h3>
+ This API call produces the following media types according to the <span class="header">Accept</span> request header;
+ the media type will be conveyed by the <span class="heaader">Content-Type</span> response header.
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Responses</h3>
+ <h4 class="field-label">204</h4>
+ Successful Deletion
+ <a href="#"></a>
+ <h4 class="field-label">404</h4>
+ Invalid Path or Path not found
+ <a href="#"></a>
+ </div> <!-- method -->
+ <hr/>
+ <div class="method"><a name="domainPost"/>
+ <div class="method-path">
+ <a class="up" href="#__Methods">Up</a>
+ <pre class="post"><code class="huge"><span class="http-method">post</span> /domain</code></pre></div>
+ <div class="method-summary">Add a new domain (<span class="nickname">domainPost</span>)</div>
+ <div class="method-notes"></div>
+
+
+ <h3 class="field-label">Consumes</h3>
+ This API call consumes the following media types via the <span class="heaader">Content-Type</span> request header:
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Request body</h3>
+ <div class="field-items">
+ <div class="param">body <a href="#Domain">Domain</a> (required)</div>
+
+ <div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; </div>
+
+ </div> <!-- field-items -->
+
+
+
+
+ <h3 class="field-label">Return type</h3>
+ <div class="return-type">
+ <a href="#Domain">Domain</a>
+
+ </div>
+
+ <!--Todo: process Response Object and its headers, schema, examples -->
+
+ <h3 class="field-label">Example data</h3>
+ <div class="example-data-content-type">Content-Type: application/json</div>
+ <pre class="example"><code>{
+ "name" : "name",
+ "uuid" : "uuid"
+}</code></pre>
+
+ <h3 class="field-label">Produces</h3>
+ This API call produces the following media types according to the <span class="header">Accept</span> request header;
+ the media type will be conveyed by the <span class="heaader">Content-Type</span> response header.
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Responses</h3>
+ <h4 class="field-label">201</h4>
+ Successful Creation
+ <a href="#Domain">Domain</a>
+ <h4 class="field-label">400</h4>
+ Invalid input
+ <a href="#"></a>
+ <h4 class="field-label">500</h4>
+ Internal Server Error
+ <a href="#"></a>
+ </div> <!-- method -->
+ <hr/>
+ <h1><a name="Login">Login</a></h1>
+ <div class="method"><a name="loginPost"/>
+ <div class="method-path">
+ <a class="up" href="#__Methods">Up</a>
+ <pre class="post"><code class="huge"><span class="http-method">post</span> /login</code></pre></div>
+ <div class="method-summary">Login with username and password (<span class="nickname">loginPost</span>)</div>
+ <div class="method-notes">Operations related to logging in via username and Password</div>
+
+
+ <h3 class="field-label">Consumes</h3>
+ This API call consumes the following media types via the <span class="heaader">Content-Type</span> request header:
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Request body</h3>
+ <div class="field-items">
+ <div class="param">body <a href="#Credential">Credential</a> (required)</div>
+
+ <div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; </div>
+
+ </div> <!-- field-items -->
+
+
+
+
+ <h3 class="field-label">Return type</h3>
+ <div class="return-type">
+ <a href="#inline_response_200">inline_response_200</a>
+
+ </div>
+
+ <!--Todo: process Response Object and its headers, schema, examples -->
+
+ <h3 class="field-label">Example data</h3>
+ <div class="example-data-content-type">Content-Type: application/json</div>
+ <pre class="example"><code>{
+ "ttl" : 0,
+ "token" : "token"
+}</code></pre>
+
+ <h3 class="field-label">Produces</h3>
+ This API call produces the following media types according to the <span class="header">Accept</span> request header;
+ the media type will be conveyed by the <span class="heaader">Content-Type</span> response header.
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Responses</h3>
+ <h4 class="field-label">200</h4>
+ Successful Login returns a token
+ <a href="#inline_response_200">inline_response_200</a>
+ <h4 class="field-label">404</h4>
+ Invalid Username or Password
+ <a href="#"></a>
+ </div> <!-- method -->
+ <hr/>
+ <h1><a name="Secret">Secret</a></h1>
+ <div class="method"><a name="domainDomainNameSecretGet"/>
+ <div class="method-path">
+ <a class="up" href="#__Methods">Up</a>
+ <pre class="get"><code class="huge"><span class="http-method">get</span> /domain/{domainName}/secret</code></pre></div>
+ <div class="method-summary">List secret Names in this domain (<span class="nickname">domainDomainNameSecretGet</span>)</div>
+ <div class="method-notes">Gets all secret names in this domain</div>
+
+ <h3 class="field-label">Path parameters</h3>
+ <div class="field-items">
+ <div class="param">domainName (required)</div>
+
+ <div class="param-desc"><span class="param-type">Path Parameter</span> &mdash; Name of the domain in which to look at </div>
+ </div> <!-- field-items -->
+
+
+
+
+
+
+ <h3 class="field-label">Return type</h3>
+ <div class="return-type">
+ <a href="#inline_response_200_2">inline_response_200_2</a>
+
+ </div>
+
+ <!--Todo: process Response Object and its headers, schema, examples -->
+
+ <h3 class="field-label">Example data</h3>
+ <div class="example-data-content-type">Content-Type: application/json</div>
+ <pre class="example"><code>"{\"secretnames\":[\"secretname1\",\"secretname2\",\"secretname3\"]}"</code></pre>
+
+ <h3 class="field-label">Produces</h3>
+ This API call produces the following media types according to the <span class="header">Accept</span> request header;
+ the media type will be conveyed by the <span class="heaader">Content-Type</span> response header.
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Responses</h3>
+ <h4 class="field-label">200</h4>
+ Successful operation
+ <a href="#inline_response_200_2">inline_response_200_2</a>
+ <h4 class="field-label">404</h4>
+ Invalid Path or Path not found
+ <a href="#"></a>
+ </div> <!-- method -->
+ <hr/>
+ <div class="method"><a name="domainDomainNameSecretPost"/>
+ <div class="method-path">
+ <a class="up" href="#__Methods">Up</a>
+ <pre class="post"><code class="huge"><span class="http-method">post</span> /domain/{domainName}/secret</code></pre></div>
+ <div class="method-summary">Add a new secret (<span class="nickname">domainDomainNameSecretPost</span>)</div>
+ <div class="method-notes"></div>
+
+ <h3 class="field-label">Path parameters</h3>
+ <div class="field-items">
+ <div class="param">domainName (required)</div>
+
+ <div class="param-desc"><span class="param-type">Path Parameter</span> &mdash; Name of the domain </div>
+ </div> <!-- field-items -->
+
+ <h3 class="field-label">Consumes</h3>
+ This API call consumes the following media types via the <span class="heaader">Content-Type</span> request header:
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Request body</h3>
+ <div class="field-items">
+ <div class="param">body <a href="#Secret">Secret</a> (required)</div>
+
+ <div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; </div>
+
+ </div> <!-- field-items -->
+
+
+
+
+
+ <!--Todo: process Response Object and its headers, schema, examples -->
+
+
+ <h3 class="field-label">Produces</h3>
+ This API call produces the following media types according to the <span class="header">Accept</span> request header;
+ the media type will be conveyed by the <span class="heaader">Content-Type</span> response header.
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Responses</h3>
+ <h4 class="field-label">201</h4>
+ Successful Creation
+ <a href="#"></a>
+ <h4 class="field-label">404</h4>
+ Invalid Path or Path not found
+ <a href="#"></a>
+ </div> <!-- method -->
+ <hr/>
+ <div class="method"><a name="domainDomainNameSecretSecretNameDelete"/>
+ <div class="method-path">
+ <a class="up" href="#__Methods">Up</a>
+ <pre class="delete"><code class="huge"><span class="http-method">delete</span> /domain/{domainName}/secret/{secretName}</code></pre></div>
+ <div class="method-summary">Deletes a Secret (<span class="nickname">domainDomainNameSecretSecretNameDelete</span>)</div>
+ <div class="method-notes"></div>
+
+ <h3 class="field-label">Path parameters</h3>
+ <div class="field-items">
+ <div class="param">secretName (required)</div>
+
+ <div class="param-desc"><span class="param-type">Path Parameter</span> &mdash; Name of Secret to Delete </div><div class="param">domainName (required)</div>
+
+ <div class="param-desc"><span class="param-type">Path Parameter</span> &mdash; Path to the SecretDomain which contains the Secret </div>
+ </div> <!-- field-items -->
+
+
+
+
+
+
+
+ <!--Todo: process Response Object and its headers, schema, examples -->
+
+
+ <h3 class="field-label">Produces</h3>
+ This API call produces the following media types according to the <span class="header">Accept</span> request header;
+ the media type will be conveyed by the <span class="heaader">Content-Type</span> response header.
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Responses</h3>
+ <h4 class="field-label">204</h4>
+ Successful Deletion
+ <a href="#"></a>
+ <h4 class="field-label">404</h4>
+ Invalid Path or Path not found
+ <a href="#"></a>
+ </div> <!-- method -->
+ <hr/>
+ <div class="method"><a name="domainDomainNameSecretSecretNameGet"/>
+ <div class="method-path">
+ <a class="up" href="#__Methods">Up</a>
+ <pre class="get"><code class="huge"><span class="http-method">get</span> /domain/{domainName}/secret/{secretName}</code></pre></div>
+ <div class="method-summary">Find Secret by Name (<span class="nickname">domainDomainNameSecretSecretNameGet</span>)</div>
+ <div class="method-notes">Returns a single secret</div>
+
+ <h3 class="field-label">Path parameters</h3>
+ <div class="field-items">
+ <div class="param">domainName (required)</div>
+
+ <div class="param-desc"><span class="param-type">Path Parameter</span> &mdash; Name of the domain in which to look at </div><div class="param">secretName (required)</div>
+
+ <div class="param-desc"><span class="param-type">Path Parameter</span> &mdash; Name of the secret which is needed </div>
+ </div> <!-- field-items -->
+
+
+
+
+
+
+ <h3 class="field-label">Return type</h3>
+ <div class="return-type">
+ <a href="#Secret">Secret</a>
+
+ </div>
+
+ <!--Todo: process Response Object and its headers, schema, examples -->
+
+ <h3 class="field-label">Example data</h3>
+ <div class="example-data-content-type">Content-Type: application/json</div>
+ <pre class="example"><code>{
+ "values" : {
+ "name" : "john",
+ "Age" : 40,
+ "admin" : true
+ },
+ "name" : "name"
+}</code></pre>
+
+ <h3 class="field-label">Produces</h3>
+ This API call produces the following media types according to the <span class="header">Accept</span> request header;
+ the media type will be conveyed by the <span class="heaader">Content-Type</span> response header.
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Responses</h3>
+ <h4 class="field-label">200</h4>
+ successful operation
+ <a href="#Secret">Secret</a>
+ <h4 class="field-label">404</h4>
+ Invalid Path or Path not found
+ <a href="#"></a>
+ </div> <!-- method -->
+ <hr/>
+ <h1><a name="System">System</a></h1>
+ <div class="method"><a name="statusGet"/>
+ <div class="method-path">
+ <a class="up" href="#__Methods">Up</a>
+ <pre class="get"><code class="huge"><span class="http-method">get</span> /status</code></pre></div>
+ <div class="method-summary">Get backend status (<span class="nickname">statusGet</span>)</div>
+ <div class="method-notes">Gets current backend status. This API is used only by quorum clients</div>
+
+
+
+
+
+
+
+ <h3 class="field-label">Return type</h3>
+ <div class="return-type">
+ <a href="#inline_response_200_1">inline_response_200_1</a>
+
+ </div>
+
+ <!--Todo: process Response Object and its headers, schema, examples -->
+
+ <h3 class="field-label">Example data</h3>
+ <div class="example-data-content-type">Content-Type: application/json</div>
+ <pre class="example"><code>{
+ "sealstatus" : "sealstatus"
+}</code></pre>
+
+ <h3 class="field-label">Produces</h3>
+ This API call produces the following media types according to the <span class="header">Accept</span> request header;
+ the media type will be conveyed by the <span class="heaader">Content-Type</span> response header.
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Responses</h3>
+ <h4 class="field-label">200</h4>
+ Successful operation
+ <a href="#inline_response_200_1">inline_response_200_1</a>
+ <h4 class="field-label">404</h4>
+ Invalid Path or Path not found
+ <a href="#"></a>
+ </div> <!-- method -->
+ <hr/>
+ <div class="method"><a name="unsealPost"/>
+ <div class="method-path">
+ <a class="up" href="#__Methods">Up</a>
+ <pre class="post"><code class="huge"><span class="http-method">post</span> /unseal</code></pre></div>
+ <div class="method-summary">Unseal backend (<span class="nickname">unsealPost</span>)</div>
+ <div class="method-notes">Sends unseal shard to unseal if backend is sealed</div>
+
+
+ <h3 class="field-label">Consumes</h3>
+ This API call consumes the following media types via the <span class="heaader">Content-Type</span> request header:
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Request body</h3>
+ <div class="field-items">
+ <div class="param">body <a href="#body">body</a> (required)</div>
+
+ <div class="param-desc"><span class="param-type">Body Parameter</span> &mdash; </div>
+
+ </div> <!-- field-items -->
+
+
+
+
+
+ <!--Todo: process Response Object and its headers, schema, examples -->
+
+
+ <h3 class="field-label">Produces</h3>
+ This API call produces the following media types according to the <span class="header">Accept</span> request header;
+ the media type will be conveyed by the <span class="heaader">Content-Type</span> response header.
+ <ul>
+ <li><code>application/json</code></li>
+ </ul>
+
+ <h3 class="field-label">Responses</h3>
+ <h4 class="field-label">201</h4>
+ Submitted unseal key
+ <a href="#"></a>
+ <h4 class="field-label">404</h4>
+ Invalid Path or Path not found
+ <a href="#"></a>
+ </div> <!-- method -->
+ <hr/>
+
+ <h2><a name="__Models">Models</a></h2>
+ [ Jump to <a href="#__Methods">Methods</a> ]
+
+ <h3>Table of Contents</h3>
+ <ol>
+ <li><a href="#Credential"><code>Credential</code> - </a></li>
+ <li><a href="#Domain"><code>Domain</code> - </a></li>
+ <li><a href="#Secret"><code>Secret</code> - </a></li>
+ <li><a href="#body"><code>body</code> - </a></li>
+ <li><a href="#inline_response_200"><code>inline_response_200</code> - </a></li>
+ <li><a href="#inline_response_200_1"><code>inline_response_200_1</code> - </a></li>
+ <li><a href="#inline_response_200_2"><code>inline_response_200_2</code> - </a></li>
+ </ol>
+
+ <div class="model">
+ <h3><a name="Credential"><code>Credential</code> - </a> <a class="up" href="#__Models">Up</a></h3>
+ <div class='model-description'></div>
+ <div class="field-items">
+ <div class="param">username (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> </div>
+<div class="param">password (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> </div>
+ </div> <!-- field-items -->
+ </div>
+ <div class="model">
+ <h3><a name="Domain"><code>Domain</code> - </a> <a class="up" href="#__Models">Up</a></h3>
+ <div class='model-description'></div>
+ <div class="field-items">
+ <div class="param">uuid (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> Optional value provided by user. If user does not provide, server will auto generate </div>
+<div class="param">name (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> Name of the secret domain under which all secrets will be stored </div>
+ </div> <!-- field-items -->
+ </div>
+ <div class="model">
+ <h3><a name="Secret"><code>Secret</code> - </a> <a class="up" href="#__Models">Up</a></h3>
+ <div class='model-description'></div>
+ <div class="field-items">
+ <div class="param">name (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> Name of the secret </div>
+<div class="param">values (optional)</div><div class="param-desc"><span class="param-type"><a href="#object">map[String, Object]</a></span> Map of key value pairs that constitute the secret </div>
+ </div> <!-- field-items -->
+ </div>
+ <div class="model">
+ <h3><a name="body"><code>body</code> - </a> <a class="up" href="#__Models">Up</a></h3>
+
+ <div class="field-items">
+ <div class="param">unsealshard (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> Unseal shard that will be used along with other shards to unseal backend </div>
+ </div> <!-- field-items -->
+ </div>
+ <div class="model">
+ <h3><a name="inline_response_200"><code>inline_response_200</code> - </a> <a class="up" href="#__Models">Up</a></h3>
+
+ <div class="field-items">
+ <div class="param">token (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> </div>
+<div class="param">ttl (optional)</div><div class="param-desc"><span class="param-type"><a href="#integer">Integer</a></span> ttl of returned token in seconds </div>
+ </div> <!-- field-items -->
+ </div>
+ <div class="model">
+ <h3><a name="inline_response_200_1"><code>inline_response_200_1</code> - </a> <a class="up" href="#__Models">Up</a></h3>
+
+ <div class="field-items">
+ <div class="param">sealstatus (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">String</a></span> seal status of backend </div>
+ </div> <!-- field-items -->
+ </div>
+ <div class="model">
+ <h3><a name="inline_response_200_2"><code>inline_response_200_2</code> - </a> <a class="up" href="#__Models">Up</a></h3>
+
+ <div class="field-items">
+ <div class="param">secretnames (optional)</div><div class="param-desc"><span class="param-type"><a href="#string">array[String]</a></span> Array of strings referencing the secret names </div>
+ </div> <!-- field-items -->
+ </div>
+ </body>
+</html>
diff --git a/docs/api_swagger.yaml b/docs/api_swagger.yaml
new file mode 100644
index 0000000..61cd091
--- /dev/null
+++ b/docs/api_swagger.yaml
@@ -0,0 +1,288 @@
+swagger: '2.0'
+info:
+ description: This is a service that provides secret management facilities
+ version: 1.0.0
+ title: Secret Management Service
+ contact:
+ email: kiran.k.kamineni@intel.com
+ license:
+ name: Apache 2.0
+ url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
+host: 'aaf.onap.org:10443'
+basePath: /v1/sms/
+tags:
+ - name: system
+ description: Operations related to quorum client which are not useful to clients
+ - name: login
+ description: Operations related to username password based authentication
+ - name: domain
+ description: Operations related to Secret Domains
+ - name: secret
+ description: Operations related to Secrets
+schemes:
+ - https
+paths:
+ /login:
+ post:
+ tags:
+ - login
+ summary: Login with username and password
+ description: Operations related to logging in via username and Password
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - name: body
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/Credential'
+ responses:
+ '200':
+ description: Successful Login returns a token
+ schema:
+ type: object
+ properties:
+ token:
+ type: string
+ ttl:
+ type: integer
+ description: ttl of returned token in seconds
+ '404':
+ description: Invalid Username or Password
+ /status:
+ get:
+ tags:
+ - system
+ description: Gets current backend status. This API is used only by quorum clients
+ summary: Get backend status
+ produces:
+ - application/json
+ responses:
+ '200':
+ description: Successful operation
+ schema:
+ type: object
+ properties:
+ sealstatus:
+ type: string
+ description: seal status of backend
+ '404':
+ description: Invalid Path or Path not found
+ /unseal:
+ post:
+ tags:
+ - system
+ description: Sends unseal shard to unseal if backend is sealed
+ summary: Unseal backend
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ required: true
+ schema:
+ type: object
+ properties:
+ unsealshard:
+ type: string
+ description: >-
+ Unseal shard that will be used along with other shards to
+ unseal backend
+ responses:
+ '201':
+ description: Submitted unseal key
+ '404':
+ description: Invalid Path or Path not found
+ /domain:
+ post:
+ tags:
+ - domain
+ summary: Add a new domain
+ description: ''
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - in: body
+ name: body
+ required: true
+ schema:
+ $ref: '#/definitions/Domain'
+ responses:
+ '201':
+ description: Successful Creation
+ schema:
+ $ref: '#/definitions/Domain'
+ '400':
+ description: Invalid input
+ '500':
+ description: Internal Server Error
+ '/domain/{domainName}':
+ delete:
+ tags:
+ - domain
+ description: Deletes a domain with provided name
+ summary: Deletes a domain by name
+ produces:
+ - application/json
+ parameters:
+ - name: domainName
+ in: path
+ description: Name of the domain
+ required: true
+ type: string
+ responses:
+ '204':
+ description: Successful Deletion
+ '404':
+ description: Invalid Path or Path not found
+ '/domain/{domainName}/secret':
+ post:
+ tags:
+ - secret
+ summary: Add a new secret
+ description: ''
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - name: domainName
+ in: path
+ description: Name of the domain
+ required: true
+ type: string
+ - name: body
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/Secret'
+ responses:
+ '201':
+ description: Successful Creation
+ '404':
+ description: Invalid Path or Path not found
+ get:
+ tags:
+ - secret
+ description: Gets all secret names in this domain
+ summary: List secret Names in this domain
+ produces:
+ - application/json
+ parameters:
+ - name: domainName
+ in: path
+ description: Name of the domain in which to look at
+ required: true
+ type: string
+ responses:
+ '200':
+ description: Successful operation
+ schema:
+ type: object
+ properties:
+ secretnames:
+ type: array
+ items:
+ type: string
+ description: Array of strings referencing the secret names
+ example:
+ secretnames: ["secretname1", "secretname2", "secretname3"]
+ '404':
+ description: Invalid Path or Path not found
+ '/domain/{domainName}/secret/{secretName}':
+ get:
+ tags:
+ - secret
+ summary: Find Secret by Name
+ description: Returns a single secret
+ produces:
+ - application/json
+ parameters:
+ - name: domainName
+ in: path
+ description: Name of the domain in which to look at
+ required: true
+ type: string
+ - name: secretName
+ in: path
+ description: Name of the secret which is needed
+ required: true
+ type: string
+ responses:
+ '200':
+ description: successful operation
+ schema:
+ $ref: '#/definitions/Secret'
+ '404':
+ description: Invalid Path or Path not found
+ delete:
+ tags:
+ - secret
+ summary: Deletes a Secret
+ description: ''
+ produces:
+ - application/json
+ parameters:
+ - name: secretName
+ in: path
+ description: Name of Secret to Delete
+ required: true
+ type: string
+ - name: domainName
+ in: path
+ required: true
+ description: Path to the SecretDomain which contains the Secret
+ type: string
+ responses:
+ '204':
+ description: Successful Deletion
+ '404':
+ description: Invalid Path or Path not found
+securityDefinitions:
+ token:
+ type: apiKey
+ name: token
+ in: header
+definitions:
+ Credential:
+ type: object
+ properties:
+ username:
+ type: string
+ password:
+ type: string
+ Domain:
+ type: object
+ properties:
+ uuid:
+ type: string
+ description: >-
+ Optional value provided by user. If user does not provide, server will
+ auto generate
+ name:
+ type: string
+ description: Name of the secret domain under which all secrets will be stored
+ Secret:
+ type: object
+ properties:
+ name:
+ type: string
+ description: Name of the secret
+ values:
+ description: Map of key value pairs that constitute the secret
+ type: object
+ additionalProperties:
+ type: object
+ example:
+ name: john
+ Age: 40
+ admin: true
+externalDocs:
+ description: Find out more about Swagger
+ url: 'http://swagger.io'
diff --git a/docs/apiswagger.rst b/docs/apiswagger.rst
new file mode 100644
index 0000000..e35c6e8
--- /dev/null
+++ b/docs/apiswagger.rst
@@ -0,0 +1,745 @@
+SMS 1.0.0 API
+===============================
+
+.. toctree::
+ :maxdepth: 3
+
+
+Description
+~~~~~~~~~~~
+
+This is a service that provides secret management facilities
+
+
+
+Contact Information
+~~~~~~~~~~~~~~~~~~~
+
+
+
+kiran.k.kamineni@intel.com
+
+
+
+
+
+License
+~~~~~~~
+
+
+`Apache 2.0 <http://www.apache.org/licenses/LICENSE-2.0.html>`_
+
+
+
+
+Base URL
+~~~~~~~~
+
+https://aaf.onap.org:10443/v1/sms/
+
+Security
+~~~~~~~~
+
+
+.. _securities_token:
+
+token (API Key)
+---------------
+
+
+
+**Name:** token
+
+**Located in:** header
+
+
+
+
+DOMAIN
+~~~~~~
+
+
+Operations related to Secret Domains
+
+
+
+
+
+DELETE ``/domain/{domainName}``
+-------------------------------
+
+
+Summary
++++++++
+
+Deletes a domain by name
+
+Description
++++++++++++
+
+.. raw:: html
+
+ Deletes a domain with provided name
+
+Parameters
+++++++++++
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 15, 10, 10, 10, 20, 30
+
+ domainName | path | Yes | string | | | Name of the domain
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**204**
+^^^^^^^
+
+Successful Deletion
+
+
+**404**
+^^^^^^^
+
+Invalid Path or Path not found
+
+
+
+
+
+
+POST ``/domain``
+----------------
+
+
+Summary
++++++++
+
+Add a new domain
+
+
+
+Request
++++++++
+
+
+
+.. _d_c7bdcff9aff0692da98e588abdbc895b:
+
+Body
+^^^^
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+ name | No | string | | | Name of the secret domain under which all secrets will be stored
+ uuid | No | string | | | Optional value provided by user. If user does not provide, server will auto generate
+
+.. code-block:: javascript
+
+ {
+ "name": "somestring",
+ "uuid": "somestring"
+ }
+
+Responses
++++++++++
+
+**201**
+^^^^^^^
+
+Successful Creation
+
+
+Type: :ref:`Domain <d_c7bdcff9aff0692da98e588abdbc895b>`
+
+**Example:**
+
+.. code-block:: javascript
+
+ {
+ "name": "somestring",
+ "uuid": "somestring"
+ }
+
+**400**
+^^^^^^^
+
+Invalid input
+
+
+**500**
+^^^^^^^
+
+Internal Server Error
+
+
+
+
+
+LOGIN
+~~~~~
+
+
+Operations related to username password based authentication
+
+
+
+
+
+POST ``/login``
+---------------
+
+
+Summary
++++++++
+
+Login with username and password
+
+Description
++++++++++++
+
+.. raw:: html
+
+ Operations related to logging in via username and Password
+
+
+Request
++++++++
+
+
+
+.. _d_8e36d758bad367e4538a291a5dd5355f:
+
+Body
+^^^^
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+ password | No | string | | |
+ username | No | string | | |
+
+.. code-block:: javascript
+
+ {
+ "password": "somestring",
+ "username": "somestring"
+ }
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Successful Login returns a token
+
+
+.. _i_bbceffdf8441c1c476ca77c42ad12f85:
+
+**Response Schema:**
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+ token | No | string | | |
+ ttl | No | integer | | | ttl of returned token in seconds
+
+
+**Example:**
+
+.. code-block:: javascript
+
+ {
+ "token": "somestring",
+ "ttl": 1
+ }
+
+**404**
+^^^^^^^
+
+Invalid Username or Password
+
+
+
+
+
+SECRET
+~~~~~~
+
+
+Operations related to Secrets
+
+
+
+
+
+DELETE ``/domain/{domainName}/secret/{secretName}``
+---------------------------------------------------
+
+
+Summary
++++++++
+
+Deletes a Secret
+
+
+Parameters
+++++++++++
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 15, 10, 10, 10, 20, 30
+
+ secretName | path | Yes | string | | | Name of Secret to Delete
+ domainName | path | Yes | string | | | Path to the SecretDomain which contains the Secret
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**204**
+^^^^^^^
+
+Successful Deletion
+
+
+**404**
+^^^^^^^
+
+Invalid Path or Path not found
+
+
+
+
+
+
+GET ``/domain/{domainName}/secret``
+-----------------------------------
+
+
+Summary
++++++++
+
+List secret Names in this domain
+
+Description
++++++++++++
+
+.. raw:: html
+
+ Gets all secret names in this domain
+
+Parameters
+++++++++++
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 15, 10, 10, 10, 20, 30
+
+ domainName | path | Yes | string | | | Name of the domain in which to look at
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Successful operation
+
+
+.. _i_1dcddfd6f11cba3fb2516d3a61cd1b77:
+
+**Response Schema:**
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+ secretnames | No | array of string | | | Array of strings referencing the secret names
+
+
+**Example:**
+
+.. code-block:: javascript
+
+ {
+ "secretnames": [
+ "secretname1",
+ "secretname2",
+ "secretname3"
+ ]
+ }
+
+**404**
+^^^^^^^
+
+Invalid Path or Path not found
+
+
+
+
+
+
+GET ``/domain/{domainName}/secret/{secretName}``
+------------------------------------------------
+
+
+Summary
++++++++
+
+Find Secret by Name
+
+Description
++++++++++++
+
+.. raw:: html
+
+ Returns a single secret
+
+Parameters
+++++++++++
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 15, 10, 10, 10, 20, 30
+
+ domainName | path | Yes | string | | | Name of the domain in which to look at
+ secretName | path | Yes | string | | | Name of the secret which is needed
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+successful operation
+
+
+Type: :ref:`Secret <d_5e5fddd9ede6eb091e8496a9c55b84c3>`
+
+**Example:**
+
+.. code-block:: javascript
+
+ {
+ "name": "somestring",
+ "values": {
+ "Age": 40,
+ "admin": true,
+ "name": "john"
+ }
+ }
+
+**404**
+^^^^^^^
+
+Invalid Path or Path not found
+
+
+
+
+
+
+POST ``/domain/{domainName}/secret``
+------------------------------------
+
+
+Summary
++++++++
+
+Add a new secret
+
+
+Parameters
+++++++++++
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Located in", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 15, 10, 10, 10, 20, 30
+
+ domainName | path | Yes | string | | | Name of the domain
+
+
+Request
++++++++
+
+
+
+.. _d_5e5fddd9ede6eb091e8496a9c55b84c3:
+
+Body
+^^^^
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+ name | No | string | | | Name of the secret
+ values | No | :ref:`values <i_a9213c9639162b77082e257e19cca0d0>` | | | Map of key value pairs that constitute the secret
+
+.. _i_a9213c9639162b77082e257e19cca0d0:
+
+**Values schema:**
+
+
+Map of key value pairs that constitute the secret
+
+Map of {"key":":ref:`values-mapped <m_4d863967ef9a9d9efdadd1b250c76bd6>`"}
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+
+
+.. code-block:: javascript
+
+ {
+ "name": "somestring",
+ "values": {
+ "Age": 40,
+ "admin": true,
+ "name": "john"
+ }
+ }
+
+Responses
++++++++++
+
+**201**
+^^^^^^^
+
+Successful Creation
+
+
+**404**
+^^^^^^^
+
+Invalid Path or Path not found
+
+
+
+
+
+SYSTEM
+~~~~~~
+
+
+Operations related to quorum client which are not useful to clients
+
+
+
+
+
+GET ``/status``
+---------------
+
+
+Summary
++++++++
+
+Get backend status
+
+Description
++++++++++++
+
+.. raw:: html
+
+ Gets current backend status. This API is used only by quorum clients
+
+
+Request
++++++++
+
+
+Responses
++++++++++
+
+**200**
+^^^^^^^
+
+Successful operation
+
+
+.. _i_ac1bc8e82eadbd8c03f852e15be4d03b:
+
+**Response Schema:**
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+ sealstatus | No | string | | | seal status of backend
+
+
+**Example:**
+
+.. code-block:: javascript
+
+ {
+ "sealstatus": "somestring"
+ }
+
+**404**
+^^^^^^^
+
+Invalid Path or Path not found
+
+
+
+
+
+
+POST ``/unseal``
+----------------
+
+
+Summary
++++++++
+
+Unseal backend
+
+Description
++++++++++++
+
+.. raw:: html
+
+ Sends unseal shard to unseal if backend is sealed
+
+
+Request
++++++++
+
+
+
+.. _i_9d32e021ba68855cbb6e633520b7cd2d:
+
+Body
+^^^^
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+ unsealshard | No | string | | | Unseal shard that will be used along with other shards to unseal backend
+
+.. code-block:: javascript
+
+ {
+ "unsealshard": "somestring"
+ }
+
+Responses
++++++++++
+
+**201**
+^^^^^^^
+
+Submitted unseal key
+
+
+**404**
+^^^^^^^
+
+Invalid Path or Path not found
+
+
+
+
+
+Data Structures
+~~~~~~~~~~~~~~~
+
+.. _d_8e36d758bad367e4538a291a5dd5355f:
+
+Credential Model Structure
+--------------------------
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+ password | No | string | | |
+ username | No | string | | |
+
+.. _d_c7bdcff9aff0692da98e588abdbc895b:
+
+Domain Model Structure
+----------------------
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+ name | No | string | | | Name of the secret domain under which all secrets will be stored
+ uuid | No | string | | | Optional value provided by user. If user does not provide, server will auto generate
+
+.. _d_5e5fddd9ede6eb091e8496a9c55b84c3:
+
+Secret Model Structure
+----------------------
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+ name | No | string | | | Name of the secret
+ values | No | :ref:`values <i_a9213c9639162b77082e257e19cca0d0>` | | | Map of key value pairs that constitute the secret
+
+.. _i_a9213c9639162b77082e257e19cca0d0:
+
+**Values schema:**
+
+
+Map of key value pairs that constitute the secret
+
+Map of {"key":":ref:`values-mapped <m_4d863967ef9a9d9efdadd1b250c76bd6>`"}
+
+.. csv-table::
+ :delim: |
+ :header: "Name", "Required", "Type", "Format", "Properties", "Description"
+ :widths: 20, 10, 15, 15, 30, 25
+
+
+
diff --git a/docs/coverage.html b/docs/coverage.html
new file mode 100644
index 0000000..39ee191
--- /dev/null
+++ b/docs/coverage.html
@@ -0,0 +1,1471 @@
+
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <style>
+ body {
+ background: black;
+ color: rgb(80, 80, 80);
+ }
+ body, pre, #legend span {
+ font-family: Menlo, monospace;
+ font-weight: bold;
+ }
+ #topbar {
+ background: black;
+ position: fixed;
+ top: 0; left: 0; right: 0;
+ height: 42px;
+ border-bottom: 1px solid rgb(80, 80, 80);
+ }
+ #content {
+ margin-top: 50px;
+ }
+ #nav, #legend {
+ float: left;
+ margin-left: 10px;
+ }
+ #legend {
+ margin-top: 12px;
+ }
+ #nav {
+ margin-top: 10px;
+ }
+ #legend span {
+ margin: 0 5px;
+ }
+ .cov0 { color: rgb(192, 0, 0) }
+.cov1 { color: rgb(128, 128, 128) }
+.cov2 { color: rgb(116, 140, 131) }
+.cov3 { color: rgb(104, 152, 134) }
+.cov4 { color: rgb(92, 164, 137) }
+.cov5 { color: rgb(80, 176, 140) }
+.cov6 { color: rgb(68, 188, 143) }
+.cov7 { color: rgb(56, 200, 146) }
+.cov8 { color: rgb(44, 212, 149) }
+.cov9 { color: rgb(32, 224, 152) }
+.cov10 { color: rgb(20, 236, 155) }
+
+ </style>
+ </head>
+ <body>
+ <div id="topbar">
+ <div id="nav">
+ <select id="files">
+
+ <option value="file0">sms/auth/auth.go (76.1%)</option>
+
+ <option value="file1">sms/backend/backend.go (80.0%)</option>
+
+ <option value="file2">sms/backend/vault.go (72.5%)</option>
+
+ <option value="file3">sms/config/config.go (78.6%)</option>
+
+ <option value="file4">sms/handler/handler.go (63.0%)</option>
+
+ <option value="file5">sms/log/logger.go (65.6%)</option>
+
+ <option value="file6">sms/sms.go (77.8%)</option>
+
+ </select>
+ </div>
+ <div id="legend">
+ <span>not tracked</span>
+
+ <span class="cov0">no coverage</span>
+ <span class="cov1">low coverage</span>
+ <span class="cov2">*</span>
+ <span class="cov3">*</span>
+ <span class="cov4">*</span>
+ <span class="cov5">*</span>
+ <span class="cov6">*</span>
+ <span class="cov7">*</span>
+ <span class="cov8">*</span>
+ <span class="cov9">*</span>
+ <span class="cov10">high coverage</span>
+
+ </div>
+ </div>
+ <div id="content">
+
+ <pre class="file" id="file0" style="display: none">/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed 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.
+ */
+
+package auth
+
+import (
+ "bytes"
+ "crypto"
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/base64"
+ "golang.org/x/crypto/openpgp"
+ "golang.org/x/crypto/openpgp/packet"
+ "io/ioutil"
+
+ smslogger "sms/log"
+)
+
+// GetTLSConfig initializes a tlsConfig using the CA's certificate
+// This config is then used to enable the server for mutual TLS
+func GetTLSConfig(caCertFile string) (*tls.Config, error) <span class="cov10" title="3">{
+
+ // Initialize tlsConfig once
+ caCert, err := ioutil.ReadFile(caCertFile)
+
+ if err != nil </span><span class="cov1" title="1">{
+ return nil, err
+ }</span>
+
+ <span class="cov6" title="2">caCertPool := x509.NewCertPool()
+ caCertPool.AppendCertsFromPEM(caCert)
+
+ tlsConfig := &amp;tls.Config{
+ // Change to RequireAndVerify once we have mandatory certs
+ ClientAuth: tls.VerifyClientCertIfGiven,
+ ClientCAs: caCertPool,
+ MinVersion: tls.VersionTLS12,
+ }
+ tlsConfig.BuildNameToCertificate()
+ return tlsConfig, nil</span>
+}
+
+// GeneratePGPKeyPair produces a PGP key pair and returns
+// two things:
+// A base64 encoded form of the public part of the entity
+// A base64 encoded form of the private key
+func GeneratePGPKeyPair() (string, string, error) <span class="cov10" title="3">{
+
+ var entity *openpgp.Entity
+ config := &amp;packet.Config{
+ DefaultHash: crypto.SHA256,
+ }
+
+ entity, err := openpgp.NewEntity("aaf.sms.init", "PGP Key for unsealing", "", config)
+ if smslogger.CheckError(err, "Create Entity") != nil </span><span class="cov0" title="0">{
+ return "", "", err
+ }</span>
+
+ // Sign the identity in the entity
+ <span class="cov10" title="3">for _, id := range entity.Identities </span><span class="cov10" title="3">{
+ err = id.SelfSignature.SignUserId(id.UserId.Id, entity.PrimaryKey, entity.PrivateKey, nil)
+ if smslogger.CheckError(err, "Sign Entity") != nil </span><span class="cov0" title="0">{
+ return "", "", err
+ }</span>
+ }
+
+ // Sign the subkey in the entity
+ <span class="cov10" title="3">for _, subkey := range entity.Subkeys </span><span class="cov10" title="3">{
+ err := subkey.Sig.SignKey(subkey.PublicKey, entity.PrivateKey, nil)
+ if smslogger.CheckError(err, "Sign Subkey") != nil </span><span class="cov0" title="0">{
+ return "", "", err
+ }</span>
+ }
+
+ <span class="cov10" title="3">buffer := new(bytes.Buffer)
+ entity.Serialize(buffer)
+ pbkey := base64.StdEncoding.EncodeToString(buffer.Bytes())
+
+ buffer.Reset()
+ entity.SerializePrivate(buffer, nil)
+ prkey := base64.StdEncoding.EncodeToString(buffer.Bytes())
+
+ return pbkey, prkey, nil</span>
+}
+
+// EncryptPGPString takes data and a public key and encrypts using that
+// public key
+func EncryptPGPString(data string, pbKey string) (string, error) <span class="cov6" title="2">{
+
+ pbKeyBytes, err := base64.StdEncoding.DecodeString(pbKey)
+ if smslogger.CheckError(err, "Decoding Base64 Public Key") != nil </span><span class="cov0" title="0">{
+ return "", err
+ }</span>
+
+ <span class="cov6" title="2">dataBytes := []byte(data)
+
+ pbEntity, err := openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(pbKeyBytes)))
+ if smslogger.CheckError(err, "Reading entity from PGP key") != nil </span><span class="cov0" title="0">{
+ return "", err
+ }</span>
+
+ // encrypt string
+ <span class="cov6" title="2">buf := new(bytes.Buffer)
+ out, err := openpgp.Encrypt(buf, []*openpgp.Entity{pbEntity}, nil, nil, nil)
+ if smslogger.CheckError(err, "Creating Encryption Pipe") != nil </span><span class="cov0" title="0">{
+ return "", err
+ }</span>
+
+ <span class="cov6" title="2">_, err = out.Write(dataBytes)
+ if smslogger.CheckError(err, "Writing to Encryption Pipe") != nil </span><span class="cov0" title="0">{
+ return "", err
+ }</span>
+
+ <span class="cov6" title="2">err = out.Close()
+ if smslogger.CheckError(err, "Closing Encryption Pipe") != nil </span><span class="cov0" title="0">{
+ return "", err
+ }</span>
+
+ <span class="cov6" title="2">crp := base64.StdEncoding.EncodeToString(buf.Bytes())
+ return crp, nil</span>
+}
+
+// DecryptPGPString decrypts a PGP encoded input string and returns
+// a base64 representation of the decoded string
+func DecryptPGPString(data string, prKey string) (string, error) <span class="cov1" title="1">{
+
+ // Convert private key to bytes from base64
+ prKeyBytes, err := base64.StdEncoding.DecodeString(prKey)
+ if smslogger.CheckError(err, "Decoding Base64 Private Key") != nil </span><span class="cov0" title="0">{
+ return "", err
+ }</span>
+
+ <span class="cov1" title="1">dataBytes, err := base64.StdEncoding.DecodeString(data)
+ if smslogger.CheckError(err, "Decoding base64 data") != nil </span><span class="cov0" title="0">{
+ return "", err
+ }</span>
+
+ <span class="cov1" title="1">prEntity, err := openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(prKeyBytes)))
+ if smslogger.CheckError(err, "Read Entity") != nil </span><span class="cov0" title="0">{
+ return "", err
+ }</span>
+
+ <span class="cov1" title="1">prEntityList := &amp;openpgp.EntityList{prEntity}
+ message, err := openpgp.ReadMessage(bytes.NewBuffer(dataBytes), prEntityList, nil, nil)
+ if smslogger.CheckError(err, "Decrypting Message") != nil </span><span class="cov0" title="0">{
+ return "", err
+ }</span>
+
+ <span class="cov1" title="1">var retBuf bytes.Buffer
+ retBuf.ReadFrom(message.UnverifiedBody)
+
+ return retBuf.String(), nil</span>
+}
+
+// ReadFromFile reads a file and loads the PGP key into
+// a string
+func ReadFromFile(fileName string) (string, error) <span class="cov6" title="2">{
+
+ data, err := ioutil.ReadFile(fileName)
+ if smslogger.CheckError(err, "Read from file") != nil </span><span class="cov0" title="0">{
+ return "", err
+ }</span>
+ <span class="cov6" title="2">return string(data), nil</span>
+}
+
+// WriteToFile writes a PGP key into a file.
+// It will truncate the file if it exists
+func WriteToFile(data string, fileName string) error <span class="cov0" title="0">{
+
+ err := ioutil.WriteFile(fileName, []byte(data), 0600)
+ if smslogger.CheckError(err, "Write to file") != nil </span><span class="cov0" title="0">{
+ return err
+ }</span>
+ <span class="cov0" title="0">return nil</span>
+}
+</pre>
+
+ <pre class="file" id="file1" style="display: none">/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed 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.
+ */
+
+package backend
+
+import (
+ smsconfig "sms/config"
+ smslogger "sms/log"
+)
+
+// SecretDomain is where Secrets are stored.
+// A single domain can have any number of secrets
+type SecretDomain struct {
+ UUID string `json:"uuid"`
+ Name string `json:"name"`
+}
+
+// Secret is the struct that defines the structure of a secret
+// It consists of a name and map containing key value pairs
+type Secret struct {
+ Name string `json:"name"`
+ Values map[string]interface{} `json:"values"`
+}
+
+// SecretBackend interface that will be implemented for various secret backends
+type SecretBackend interface {
+ Init() error
+ GetStatus() (bool, error)
+ Unseal(shard string) error
+ RegisterQuorum(pgpkey string) (string, error)
+
+ GetSecret(dom string, sec string) (Secret, error)
+ ListSecret(dom string) ([]string, error)
+
+ CreateSecretDomain(name string) (SecretDomain, error)
+ CreateSecret(dom string, sec Secret) error
+
+ DeleteSecretDomain(name string) error
+ DeleteSecret(dom string, name string) error
+}
+
+// InitSecretBackend returns an interface implementation
+func InitSecretBackend() (SecretBackend, error) <span class="cov8" title="1">{
+ backendImpl := &amp;Vault{
+ vaultAddress: smsconfig.SMSConfig.BackendAddress,
+ vaultToken: smsconfig.SMSConfig.VaultToken,
+ }
+
+ err := backendImpl.Init()
+ if smslogger.CheckError(err, "InitSecretBackend") != nil </span><span class="cov0" title="0">{
+ return nil, err
+ }</span>
+
+ <span class="cov8" title="1">return backendImpl, nil</span>
+}
+
+// LoginBackend Interface that will be implemented for various login backends
+type LoginBackend interface {
+}
+</pre>
+
+ <pre class="file" id="file2" style="display: none">/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed 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.
+ */
+
+package backend
+
+import (
+ uuid "github.com/hashicorp/go-uuid"
+ vaultapi "github.com/hashicorp/vault/api"
+ smsauth "sms/auth"
+ smslogger "sms/log"
+
+ "errors"
+ "fmt"
+ "strings"
+ "sync"
+ "time"
+)
+
+// Vault is the main Struct used in Backend to initialize the struct
+type Vault struct {
+ sync.Mutex
+ initRoleDone bool
+ policyName string
+ roleID string
+ secretID string
+ vaultAddress string
+ vaultClient *vaultapi.Client
+ vaultMountPrefix string
+ internalDomain string
+ internalDomainMounted bool
+ vaultTempTokenTTL time.Time
+ vaultToken string
+ shards []string
+ prkey string
+}
+
+// initVaultClient will create the initial
+// Vault strcuture and populate it with the
+// right values and it will also create
+// a vault client
+func (v *Vault) initVaultClient() error <span class="cov6" title="11">{
+
+ vaultCFG := vaultapi.DefaultConfig()
+ vaultCFG.Address = v.vaultAddress
+ client, err := vaultapi.NewClient(vaultCFG)
+ if smslogger.CheckError(err, "Create new vault client") != nil </span><span class="cov0" title="0">{
+ return err
+ }</span>
+
+ <span class="cov6" title="11">v.initRoleDone = false
+ v.policyName = "smsvaultpolicy"
+ v.vaultClient = client
+ v.vaultMountPrefix = "sms"
+ v.internalDomain = "smsinternaldomain"
+ v.internalDomainMounted = false
+ v.prkey = ""
+ return nil</span>
+}
+
+// Init will initialize the vault connection
+// It will also initialize vault if it is not
+// already initialized.
+// The initial policy will also be created
+func (v *Vault) Init() error <span class="cov1" title="1">{
+
+ v.initVaultClient()
+ // Initialize vault if it is not already
+ // Returns immediately if it is initialized
+ v.initializeVault()
+
+ err := v.initRole()
+ if smslogger.CheckError(err, "InitRole First Attempt") != nil </span><span class="cov0" title="0">{
+ smslogger.WriteInfo("InitRole will try again later")
+ }</span>
+
+ <span class="cov1" title="1">return nil</span>
+}
+
+// GetStatus returns the current seal status of vault
+func (v *Vault) GetStatus() (bool, error) <span class="cov3" title="3">{
+
+ sys := v.vaultClient.Sys()
+ sealStatus, err := sys.SealStatus()
+ if smslogger.CheckError(err, "Getting Status") != nil </span><span class="cov0" title="0">{
+ return false, errors.New("Error getting status")
+ }</span>
+
+ <span class="cov3" title="3">return sealStatus.Sealed, nil</span>
+}
+
+// RegisterQuorum registers the PGP public key for a quorum client
+// We will return a shard to the client that is registering
+func (v *Vault) RegisterQuorum(pgpkey string) (string, error) <span class="cov0" title="0">{
+
+ v.Lock()
+ defer v.Unlock()
+
+ if v.shards == nil </span><span class="cov0" title="0">{
+ smslogger.WriteError("Invalid operation in RegisterQuorum")
+ return "", errors.New("Invalid operation")
+ }</span>
+ // Pop the slice
+ <span class="cov0" title="0">var sh string
+ sh, v.shards = v.shards[len(v.shards)-1], v.shards[:len(v.shards)-1]
+ if len(v.shards) == 0 </span><span class="cov0" title="0">{
+ v.shards = nil
+ }</span>
+
+ // Decrypt with SMS pgp Key
+ <span class="cov0" title="0">sh, _ = smsauth.DecryptPGPString(sh, v.prkey)
+ // Encrypt with Quorum client pgp key
+ sh, _ = smsauth.EncryptPGPString(sh, pgpkey)
+
+ return sh, nil</span>
+}
+
+// Unseal is a passthrough API that allows any
+// unseal or initialization processes for the backend
+func (v *Vault) Unseal(shard string) error <span class="cov0" title="0">{
+
+ sys := v.vaultClient.Sys()
+ _, err := sys.Unseal(shard)
+ if smslogger.CheckError(err, "Unseal Operation") != nil </span><span class="cov0" title="0">{
+ return errors.New("Unable to execute unseal operation with specified shard")
+ }</span>
+
+ <span class="cov0" title="0">return nil</span>
+}
+
+// GetSecret returns a secret mounted on a particular domain name
+// The secret itself is referenced via its name which translates to
+// a mount path in vault
+func (v *Vault) GetSecret(dom string, name string) (Secret, error) <span class="cov5" title="7">{
+
+ err := v.checkToken()
+ if smslogger.CheckError(err, "Tocken Check") != nil </span><span class="cov0" title="0">{
+ return Secret{}, errors.New("Token check failed")
+ }</span>
+
+ <span class="cov5" title="7">dom = v.vaultMountPrefix + "/" + dom
+
+ sec, err := v.vaultClient.Logical().Read(dom + "/" + name)
+ if smslogger.CheckError(err, "Read Secret") != nil </span><span class="cov0" title="0">{
+ return Secret{}, errors.New("Unable to read Secret at provided path")
+ }</span>
+
+ // sec and err are nil in the case where a path does not exist
+ <span class="cov5" title="7">if sec == nil </span><span class="cov0" title="0">{
+ smslogger.WriteWarn("Vault read was empty. Invalid Path")
+ return Secret{}, errors.New("Secret not found at the provided path")
+ }</span>
+
+ <span class="cov5" title="7">return Secret{Name: name, Values: sec.Data}, nil</span>
+}
+
+// ListSecret returns a list of secret names on a particular domain
+// The values of the secret are not returned
+func (v *Vault) ListSecret(dom string) ([]string, error) <span class="cov3" title="3">{
+
+ err := v.checkToken()
+ if smslogger.CheckError(err, "Token Check") != nil </span><span class="cov0" title="0">{
+ return nil, errors.New("Token check failed")
+ }</span>
+
+ <span class="cov3" title="3">dom = v.vaultMountPrefix + "/" + dom
+
+ sec, err := v.vaultClient.Logical().List(dom)
+ if smslogger.CheckError(err, "Read Secret") != nil </span><span class="cov0" title="0">{
+ return nil, errors.New("Unable to read Secret at provided path")
+ }</span>
+
+ // sec and err are nil in the case where a path does not exist
+ <span class="cov3" title="3">if sec == nil </span><span class="cov0" title="0">{
+ smslogger.WriteWarn("Vaultclient returned empty data")
+ return nil, errors.New("Secret not found at the provided path")
+ }</span>
+
+ <span class="cov3" title="3">val, ok := sec.Data["keys"].([]interface{})
+ if !ok </span><span class="cov0" title="0">{
+ smslogger.WriteError("Secret not found at the provided path")
+ return nil, errors.New("Secret not found at the provided path")
+ }</span>
+
+ <span class="cov3" title="3">retval := make([]string, len(val))
+ for i, v := range val </span><span class="cov5" title="7">{
+ retval[i] = fmt.Sprint(v)
+ }</span>
+
+ <span class="cov3" title="3">return retval, nil</span>
+}
+
+// Mounts the internal Domain if its not already mounted
+func (v *Vault) mountInternalDomain(name string) error <span class="cov5" title="8">{
+
+ if v.internalDomainMounted </span><span class="cov1" title="1">{
+ return nil
+ }</span>
+
+ <span class="cov5" title="7">name = strings.TrimSpace(name)
+ mountPath := v.vaultMountPrefix + "/" + name
+ mountInput := &amp;vaultapi.MountInput{
+ Type: "kv",
+ Description: "Mount point for domain: " + name,
+ Local: false,
+ SealWrap: false,
+ Config: vaultapi.MountConfigInput{},
+ }
+
+ err := v.vaultClient.Sys().Mount(mountPath, mountInput)
+ if smslogger.CheckError(err, "Mount internal Domain") != nil </span><span class="cov1" title="1">{
+ if strings.Contains(err.Error(), "existing mount") </span><span class="cov1" title="1">{
+ // It is already mounted
+ v.internalDomainMounted = true
+ return nil
+ }</span>
+ // Ran into some other error mounting it.
+ <span class="cov0" title="0">return errors.New("Unable to mount internal Domain")</span>
+ }
+
+ <span class="cov5" title="6">v.internalDomainMounted = true
+ return nil</span>
+}
+
+// Stores the UUID created for secretdomain in vault
+// under v.vaultMountPrefix / smsinternal domain
+func (v *Vault) storeUUID(uuid string, name string) error <span class="cov5" title="8">{
+
+ // Check if token is still valid
+ err := v.checkToken()
+ if smslogger.CheckError(err, "Token Check") != nil </span><span class="cov0" title="0">{
+ return errors.New("Token Check failed")
+ }</span>
+
+ <span class="cov5" title="8">err = v.mountInternalDomain(v.internalDomain)
+ if smslogger.CheckError(err, "Mount Internal Domain") != nil </span><span class="cov0" title="0">{
+ return err
+ }</span>
+
+ <span class="cov5" title="8">secret := Secret{
+ Name: name,
+ Values: map[string]interface{}{
+ "uuid": uuid,
+ },
+ }
+
+ err = v.CreateSecret(v.internalDomain, secret)
+ if smslogger.CheckError(err, "Write UUID to domain") != nil </span><span class="cov0" title="0">{
+ return err
+ }</span>
+
+ <span class="cov5" title="8">return nil</span>
+}
+
+// CreateSecretDomain mounts the kv backend on a path with the given name
+func (v *Vault) CreateSecretDomain(name string) (SecretDomain, error) <span class="cov5" title="8">{
+
+ // Check if token is still valid
+ err := v.checkToken()
+ if smslogger.CheckError(err, "Token Check") != nil </span><span class="cov0" title="0">{
+ return SecretDomain{}, errors.New("Token Check failed")
+ }</span>
+
+ <span class="cov5" title="8">name = strings.TrimSpace(name)
+ mountPath := v.vaultMountPrefix + "/" + name
+ mountInput := &amp;vaultapi.MountInput{
+ Type: "kv",
+ Description: "Mount point for domain: " + name,
+ Local: false,
+ SealWrap: false,
+ Config: vaultapi.MountConfigInput{},
+ }
+
+ err = v.vaultClient.Sys().Mount(mountPath, mountInput)
+ if smslogger.CheckError(err, "Create Domain") != nil </span><span class="cov0" title="0">{
+ return SecretDomain{}, errors.New("Unable to create Secret Domain")
+ }</span>
+
+ <span class="cov5" title="8">uuid, _ := uuid.GenerateUUID()
+ err = v.storeUUID(uuid, name)
+ if smslogger.CheckError(err, "Store UUID") != nil </span><span class="cov0" title="0">{
+ // Mount was successful at this point.
+ // Rollback the mount operation since we could not
+ // store the UUID for the mount.
+ v.vaultClient.Sys().Unmount(mountPath)
+ return SecretDomain{}, errors.New("Unable to store Secret Domain UUID. Retry")
+ }</span>
+
+ <span class="cov5" title="8">return SecretDomain{uuid, name}, nil</span>
+}
+
+// CreateSecret creates a secret mounted on a particular domain name
+// The secret itself is mounted on a path specified by name
+func (v *Vault) CreateSecret(dom string, sec Secret) error <span class="cov7" title="18">{
+
+ err := v.checkToken()
+ if smslogger.CheckError(err, "Token Check") != nil </span><span class="cov0" title="0">{
+ return errors.New("Token check failed")
+ }</span>
+
+ <span class="cov7" title="18">dom = v.vaultMountPrefix + "/" + dom
+
+ // Vault return is empty on successful write
+ // TODO: Check if values is not empty
+ _, err = v.vaultClient.Logical().Write(dom+"/"+sec.Name, sec.Values)
+ if smslogger.CheckError(err, "Create Secret") != nil </span><span class="cov0" title="0">{
+ return errors.New("Unable to create Secret at provided path")
+ }</span>
+
+ <span class="cov7" title="18">return nil</span>
+}
+
+// DeleteSecretDomain deletes a secret domain which translates to
+// an unmount operation on the given path in Vault
+func (v *Vault) DeleteSecretDomain(name string) error <span class="cov3" title="3">{
+
+ err := v.checkToken()
+ if smslogger.CheckError(err, "Token Check") != nil </span><span class="cov0" title="0">{
+ return errors.New("Token Check Failed")
+ }</span>
+
+ <span class="cov3" title="3">name = strings.TrimSpace(name)
+ mountPath := v.vaultMountPrefix + "/" + name
+
+ err = v.vaultClient.Sys().Unmount(mountPath)
+ if smslogger.CheckError(err, "Delete Domain") != nil </span><span class="cov0" title="0">{
+ return errors.New("Unable to delete domain specified")
+ }</span>
+
+ <span class="cov3" title="3">return nil</span>
+}
+
+// DeleteSecret deletes a secret mounted on the path provided
+func (v *Vault) DeleteSecret(dom string, name string) error <span class="cov5" title="7">{
+
+ err := v.checkToken()
+ if smslogger.CheckError(err, "Token Check") != nil </span><span class="cov0" title="0">{
+ return errors.New("Token check failed")
+ }</span>
+
+ <span class="cov5" title="7">dom = v.vaultMountPrefix + "/" + dom
+
+ // Vault return is empty on successful delete
+ _, err = v.vaultClient.Logical().Delete(dom + "/" + name)
+ if smslogger.CheckError(err, "Delete Secret") != nil </span><span class="cov0" title="0">{
+ return errors.New("Unable to delete Secret at provided path")
+ }</span>
+
+ <span class="cov5" title="7">return nil</span>
+}
+
+// initRole is called only once during SMS bring up
+// It initially creates a role and secret id associated with
+// that role. Later restarts will use the existing role-id
+// and secret-id stored on disk
+func (v *Vault) initRole() error <span class="cov10" title="56">{
+
+ if v.initRoleDone </span><span class="cov9" title="48">{
+ return nil
+ }</span>
+
+ // Use the root token once here
+ <span class="cov5" title="8">v.vaultClient.SetToken(v.vaultToken)
+ defer v.vaultClient.ClearToken()
+
+ // Check if roleID and secretID has already been created
+ rID, error := smsauth.ReadFromFile("auth/role")
+ if error != nil </span><span class="cov5" title="7">{
+ smslogger.WriteWarn("Unable to find RoleID. Generating...")
+ }</span><span class="cov1" title="1"> else {
+ sID, error := smsauth.ReadFromFile("auth/secret")
+ if error != nil </span><span class="cov0" title="0">{
+ smslogger.WriteWarn("Unable to find secretID. Generating...")
+ }</span><span class="cov1" title="1"> else {
+ v.roleID = rID
+ v.secretID = sID
+ v.initRoleDone = true
+ return nil
+ }</span>
+ }
+
+ <span class="cov5" title="7">rules := `path "sms/*" { capabilities = ["create", "read", "update", "delete", "list"] }
+ path "sys/mounts/sms*" { capabilities = ["update","delete","create"] }`
+ err := v.vaultClient.Sys().PutPolicy(v.policyName, rules)
+ if smslogger.CheckError(err, "Creating Policy") != nil </span><span class="cov0" title="0">{
+ return errors.New("Unable to create policy for approle creation")
+ }</span>
+
+ //Check if applrole is mounted
+ <span class="cov5" title="7">authMounts, err := v.vaultClient.Sys().ListAuth()
+ if smslogger.CheckError(err, "Mount Auth Backend") != nil </span><span class="cov0" title="0">{
+ return errors.New("Unable to get mounted auth backends")
+ }</span>
+
+ <span class="cov5" title="7">approleMounted := false
+ for k, v := range authMounts </span><span class="cov5" title="7">{
+ if v.Type == "approle" &amp;&amp; k == "approle/" </span><span class="cov0" title="0">{
+ approleMounted = true
+ break</span>
+ }
+ }
+
+ // Mount approle in case its not already mounted
+ <span class="cov5" title="7">if !approleMounted </span><span class="cov5" title="7">{
+ v.vaultClient.Sys().EnableAuth("approle", "approle", "")
+ }</span>
+
+ <span class="cov5" title="7">rName := v.vaultMountPrefix + "-role"
+ data := map[string]interface{}{
+ "token_ttl": "60m",
+ "policies": [2]string{"default", v.policyName},
+ }
+
+ // Create a role-id
+ v.vaultClient.Logical().Write("auth/approle/role/"+rName, data)
+ sec, err := v.vaultClient.Logical().Read("auth/approle/role/" + rName + "/role-id")
+ if smslogger.CheckError(err, "Create RoleID") != nil </span><span class="cov0" title="0">{
+ return errors.New("Unable to create role ID for approle")
+ }</span>
+ <span class="cov5" title="7">v.roleID = sec.Data["role_id"].(string)
+
+ // Create a secret-id to go with it
+ sec, err = v.vaultClient.Logical().Write("auth/approle/role/"+rName+"/secret-id",
+ map[string]interface{}{})
+ if smslogger.CheckError(err, "Create SecretID") != nil </span><span class="cov0" title="0">{
+ return errors.New("Unable to create secret ID for role")
+ }</span>
+
+ <span class="cov5" title="7">v.secretID = sec.Data["secret_id"].(string)
+ v.initRoleDone = true
+ /*
+ * Revoke the Root token.
+ * If a new Root Token is needed, it will need to be created
+ * using the unseal shards.
+ */
+ err = v.vaultClient.Auth().Token().RevokeSelf(v.vaultToken)
+ if smslogger.CheckError(err, "Revoke Root Token") != nil </span><span class="cov0" title="0">{
+ smslogger.WriteWarn("Unable to Revoke Token")
+ }</span><span class="cov5" title="7"> else {
+ // Revoked successfully and clear it
+ v.vaultToken = ""
+ }</span>
+
+ // Store the role-id and secret-id
+ // We will need this if SMS restarts
+ <span class="cov5" title="7">smsauth.WriteToFile(v.roleID, "auth/role")
+ smsauth.WriteToFile(v.secretID, "auth/secret")
+
+ return nil</span>
+}
+
+// Function checkToken() gets called multiple times to create
+// temporary tokens
+func (v *Vault) checkToken() error <span class="cov9" title="54">{
+
+ v.Lock()
+ defer v.Unlock()
+
+ // Init Role if it is not yet done
+ // Role needs to be created before token can be created
+ err := v.initRole()
+ if err != nil </span><span class="cov0" title="0">{
+ smslogger.WriteError(err.Error())
+ return errors.New("Unable to initRole in checkToken")
+ }</span>
+
+ // Return immediately if token still has life
+ <span class="cov9" title="54">if v.vaultClient.Token() != "" &amp;&amp;
+ time.Since(v.vaultTempTokenTTL) &lt; time.Minute*50 </span><span class="cov9" title="47">{
+ return nil
+ }</span>
+
+ // Create a temporary token using our roleID and secretID
+ <span class="cov5" title="7">out, err := v.vaultClient.Logical().Write("auth/approle/login",
+ map[string]interface{}{"role_id": v.roleID, "secret_id": v.secretID})
+ if smslogger.CheckError(err, "Create Temp Token") != nil </span><span class="cov0" title="0">{
+ return errors.New("Unable to create Temporary Token for Role")
+ }</span>
+
+ <span class="cov5" title="7">tok, err := out.TokenID()
+
+ v.vaultTempTokenTTL = time.Now()
+ v.vaultClient.SetToken(tok)
+ return nil</span>
+}
+
+// vaultInit() is used to initialize the vault in cases where it is not
+// initialized. This happens once during intial bring up.
+func (v *Vault) initializeVault() error <span class="cov2" title="2">{
+
+ // Check for vault init status and don't exit till it is initialized
+ for </span><span class="cov2" title="2">{
+ init, err := v.vaultClient.Sys().InitStatus()
+ if smslogger.CheckError(err, "Get Vault Init Status") != nil </span><span class="cov0" title="0">{
+ smslogger.WriteInfo("Trying again in 10s...")
+ time.Sleep(time.Second * 10)
+ continue</span>
+ }
+ // Did not get any error
+ <span class="cov2" title="2">if init == true </span><span class="cov1" title="1">{
+ smslogger.WriteInfo("Vault is already Initialized")
+ return nil
+ }</span>
+
+ // init status is false
+ // break out of loop and finish initialization
+ <span class="cov1" title="1">smslogger.WriteInfo("Vault is not initialized. Initializing...")
+ break</span>
+ }
+
+ // Hardcoded this to 3. We should make this configurable
+ // in the future
+ <span class="cov1" title="1">initReq := &amp;vaultapi.InitRequest{
+ SecretShares: 3,
+ SecretThreshold: 3,
+ }
+
+ pbkey, prkey, err := smsauth.GeneratePGPKeyPair()
+
+ if smslogger.CheckError(err, "Generating PGP Keys") != nil </span><span class="cov0" title="0">{
+ smslogger.WriteError("Error Generating PGP Keys. Vault Init will not use encryption!")
+ }</span><span class="cov1" title="1"> else {
+ initReq.PGPKeys = []string{pbkey, pbkey, pbkey}
+ initReq.RootTokenPGPKey = pbkey
+ }</span>
+
+ <span class="cov1" title="1">resp, err := v.vaultClient.Sys().Init(initReq)
+ if smslogger.CheckError(err, "Initialize Vault") != nil </span><span class="cov0" title="0">{
+ return errors.New("FATAL: Unable to initialize Vault")
+ }</span>
+
+ <span class="cov1" title="1">if resp != nil </span><span class="cov1" title="1">{
+ v.prkey = prkey
+ v.shards = resp.KeysB64
+ v.vaultToken, _ = smsauth.DecryptPGPString(resp.RootToken, prkey)
+ return nil
+ }</span>
+
+ <span class="cov0" title="0">return errors.New("FATAL: Init response was empty")</span>
+}
+</pre>
+
+ <pre class="file" id="file3" style="display: none">/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed 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.
+ */
+
+package config
+
+import (
+ "encoding/json"
+ "os"
+ smslogger "sms/log"
+)
+
+// SMSConfiguration loads up all the values that are used to configure
+// backend implementations
+// TODO: Review these and see if they can be created/discovered dynamically
+type SMSConfiguration struct {
+ CAFile string `json:"cafile"`
+ ServerCert string `json:"servercert"`
+ ServerKey string `json:"serverkey"`
+
+ BackendAddress string `json:"smsdbaddress"`
+ VaultToken string `json:"vaulttoken"`
+ DisableTLS bool `json:"disable_tls"`
+ BackendAddressEnvVariable string `json:"smsdburlenv"`
+}
+
+// SMSConfig is the structure that stores the configuration
+var SMSConfig *SMSConfiguration
+
+// ReadConfigFile reads the specified smsConfig file to setup some env variables
+func ReadConfigFile(file string) (*SMSConfiguration, error) <span class="cov10" title="3">{
+ if SMSConfig == nil </span><span class="cov10" title="3">{
+ f, err := os.Open(file)
+ if err != nil </span><span class="cov1" title="1">{
+ return nil, err
+ }</span>
+ <span class="cov6" title="2">defer f.Close()
+
+ // Default behaviour is to enable TLS
+ SMSConfig = &amp;SMSConfiguration{DisableTLS: false}
+ decoder := json.NewDecoder(f)
+ err = decoder.Decode(SMSConfig)
+ if err != nil </span><span class="cov0" title="0">{
+ return nil, err
+ }</span>
+
+ <span class="cov6" title="2">if SMSConfig.BackendAddress == "" &amp;&amp; SMSConfig.BackendAddressEnvVariable != "" </span><span class="cov0" title="0">{
+ // Get the value from ENV variable
+ smslogger.WriteInfo("Using Environment Variable: " + SMSConfig.BackendAddressEnvVariable)
+ SMSConfig.BackendAddress = os.Getenv(SMSConfig.BackendAddressEnvVariable)
+ }</span>
+ }
+
+ <span class="cov6" title="2">return SMSConfig, nil</span>
+}
+</pre>
+
+ <pre class="file" id="file4" style="display: none">/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed 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.
+ */
+
+package handler
+
+import (
+ "encoding/json"
+ "github.com/gorilla/mux"
+ "net/http"
+
+ smsbackend "sms/backend"
+ smslogger "sms/log"
+)
+
+// handler stores two interface implementations that implement
+// the backend functionality
+type handler struct {
+ secretBackend smsbackend.SecretBackend
+ loginBackend smsbackend.LoginBackend
+}
+
+// createSecretDomainHandler creates a secret domain with a name provided
+func (h handler) createSecretDomainHandler(w http.ResponseWriter, r *http.Request) <span class="cov6" title="3">{
+ var d smsbackend.SecretDomain
+
+ err := json.NewDecoder(r.Body).Decode(&amp;d)
+ if smslogger.CheckError(err, "CreateSecretDomainHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }</span>
+
+ <span class="cov6" title="3">dom, err := h.secretBackend.CreateSecretDomain(d.Name)
+ if smslogger.CheckError(err, "CreateSecretDomainHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+
+ <span class="cov6" title="3">w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+ err = json.NewEncoder(w).Encode(dom)
+ if smslogger.CheckError(err, "CreateSecretDomainHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+}
+
+// deleteSecretDomainHandler deletes a secret domain with the name provided
+func (h handler) deleteSecretDomainHandler(w http.ResponseWriter, r *http.Request) <span class="cov6" title="3">{
+ vars := mux.Vars(r)
+ domName := vars["domName"]
+
+ err := h.secretBackend.DeleteSecretDomain(domName)
+ if smslogger.CheckError(err, "DeleteSecretDomainHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+
+ <span class="cov6" title="3">w.WriteHeader(http.StatusNoContent)</span>
+}
+
+// createSecretHandler handles creation of secrets on a given domain name
+func (h handler) createSecretHandler(w http.ResponseWriter, r *http.Request) <span class="cov10" title="7">{
+ // Get domain name from URL
+ vars := mux.Vars(r)
+ domName := vars["domName"]
+
+ // Get secrets to be stored from body
+ var b smsbackend.Secret
+ err := json.NewDecoder(r.Body).Decode(&amp;b)
+ if smslogger.CheckError(err, "CreateSecretHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }</span>
+
+ <span class="cov10" title="7">err = h.secretBackend.CreateSecret(domName, b)
+ if smslogger.CheckError(err, "CreateSecretHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+
+ <span class="cov10" title="7">w.WriteHeader(http.StatusCreated)</span>
+}
+
+// getSecretHandler handles reading a secret by given domain name and secret name
+func (h handler) getSecretHandler(w http.ResponseWriter, r *http.Request) <span class="cov10" title="7">{
+ vars := mux.Vars(r)
+ domName := vars["domName"]
+ secName := vars["secretName"]
+
+ sec, err := h.secretBackend.GetSecret(domName, secName)
+ if smslogger.CheckError(err, "GetSecretHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+
+ <span class="cov10" title="7">w.Header().Set("Content-Type", "application/json")
+ err = json.NewEncoder(w).Encode(sec)
+ if smslogger.CheckError(err, "GetSecretHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+}
+
+// listSecretHandler handles listing all secrets under a particular domain name
+func (h handler) listSecretHandler(w http.ResponseWriter, r *http.Request) <span class="cov6" title="3">{
+ vars := mux.Vars(r)
+ domName := vars["domName"]
+
+ secList, err := h.secretBackend.ListSecret(domName)
+ if smslogger.CheckError(err, "ListSecretHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+
+ // Creating an anonymous struct to store the returned list of data
+ <span class="cov6" title="3">var retStruct = struct {
+ SecretNames []string `json:"secretnames"`
+ }{
+ secList,
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ err = json.NewEncoder(w).Encode(retStruct)
+ if smslogger.CheckError(err, "ListSecretHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+}
+
+// deleteSecretHandler handles deleting a secret by given domain name and secret name
+func (h handler) deleteSecretHandler(w http.ResponseWriter, r *http.Request) <span class="cov10" title="7">{
+ vars := mux.Vars(r)
+ domName := vars["domName"]
+ secName := vars["secretName"]
+
+ err := h.secretBackend.DeleteSecret(domName, secName)
+ if smslogger.CheckError(err, "DeleteSecretHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+
+ <span class="cov10" title="7">w.WriteHeader(http.StatusNoContent)</span>
+}
+
+// statusHandler returns information related to SMS and SMS backend services
+func (h handler) statusHandler(w http.ResponseWriter, r *http.Request) <span class="cov7" title="4">{
+ s, err := h.secretBackend.GetStatus()
+ if smslogger.CheckError(err, "StatusHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+
+ <span class="cov7" title="4">status := struct {
+ Seal bool `json:"sealstatus"`
+ }{
+ s,
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ err = json.NewEncoder(w).Encode(status)
+ if smslogger.CheckError(err, "StatusHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+}
+
+// loginHandler handles login via password and username
+func (h handler) loginHandler(w http.ResponseWriter, r *http.Request) {<span class="cov0" title="0">
+
+}</span>
+
+// unsealHandler is a pass through that sends requests from quorum client
+// to the backend.
+func (h handler) unsealHandler(w http.ResponseWriter, r *http.Request) <span class="cov0" title="0">{
+ // Get shards to be used for unseal
+ type unsealStruct struct {
+ UnsealShard string `json:"unsealshard"`
+ }
+
+ var inp unsealStruct
+ decoder := json.NewDecoder(r.Body)
+ decoder.DisallowUnknownFields()
+ err := decoder.Decode(&amp;inp)
+ if smslogger.CheckError(err, "UnsealHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, "Bad input JSON", http.StatusBadRequest)
+ return
+ }</span>
+
+ <span class="cov0" title="0">err = h.secretBackend.Unseal(inp.UnsealShard)
+ if smslogger.CheckError(err, "UnsealHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+}
+
+// registerHandler allows the quorum clients to register with SMS
+// with their PGP public keys that are then used by sms for backend
+// initialization
+func (h handler) registerHandler(w http.ResponseWriter, r *http.Request) <span class="cov1" title="1">{
+ // Get shards to be used for unseal
+ type registerStruct struct {
+ PGPKey string `json:"pgpkey"`
+ QuorumID string `json:"quorumid"`
+ }
+
+ var inp registerStruct
+ decoder := json.NewDecoder(r.Body)
+ decoder.DisallowUnknownFields()
+ err := decoder.Decode(&amp;inp)
+ if smslogger.CheckError(err, "RegisterHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, "Bad input JSON", http.StatusBadRequest)
+ return
+ }</span>
+
+ <span class="cov1" title="1">sh, err := h.secretBackend.RegisterQuorum(inp.PGPKey)
+ if smslogger.CheckError(err, "RegisterHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+
+ // Creating a struct for return data
+ <span class="cov1" title="1">shStruct := struct {
+ Shard string `json:"shard"`
+ }{
+ sh,
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ err = json.NewEncoder(w).Encode(shStruct)
+ if smslogger.CheckError(err, "RegisterHandler") != nil </span><span class="cov0" title="0">{
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }</span>
+}
+
+// CreateRouter returns an http.Handler for the registered URLs
+// Takes an interface implementation as input
+func CreateRouter(b smsbackend.SecretBackend) http.Handler <span class="cov4" title="2">{
+ h := handler{secretBackend: b}
+
+ // Create a new mux to handle URL endpoints
+ router := mux.NewRouter()
+
+ router.HandleFunc("/v1/sms/login", h.loginHandler).Methods("POST")
+
+ // Initialization APIs which will be used by quorum client
+ // to unseal and to provide root token to sms service
+ router.HandleFunc("/v1/sms/quorum/status", h.statusHandler).Methods("GET")
+ router.HandleFunc("/v1/sms/quorum/unseal", h.unsealHandler).Methods("POST")
+ router.HandleFunc("/v1/sms/quorum/register", h.registerHandler).Methods("POST")
+
+ router.HandleFunc("/v1/sms/domain", h.createSecretDomainHandler).Methods("POST")
+ router.HandleFunc("/v1/sms/domain/{domName}", h.deleteSecretDomainHandler).Methods("DELETE")
+
+ router.HandleFunc("/v1/sms/domain/{domName}/secret", h.createSecretHandler).Methods("POST")
+ router.HandleFunc("/v1/sms/domain/{domName}/secret", h.listSecretHandler).Methods("GET")
+ router.HandleFunc("/v1/sms/domain/{domName}/secret/{secretName}", h.getSecretHandler).Methods("GET")
+ router.HandleFunc("/v1/sms/domain/{domName}/secret/{secretName}", h.deleteSecretHandler).Methods("DELETE")
+
+ return router
+}</span>
+</pre>
+
+ <pre class="file" id="file5" style="display: none">/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed 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.
+ */
+
+package log
+
+import (
+ "fmt"
+ "log"
+ "os"
+)
+
+var errL, warnL, infoL *log.Logger
+var stdErr, stdWarn, stdInfo *log.Logger
+
+// Init will be called by sms.go before any other packages use it
+func Init(filePath string) <span class="cov1" title="1">{
+
+ stdErr = log.New(os.Stderr, "ERROR: ", log.Lshortfile|log.LstdFlags)
+ stdWarn = log.New(os.Stdout, "WARNING: ", log.Lshortfile|log.LstdFlags)
+ stdInfo = log.New(os.Stdout, "INFO: ", log.Lshortfile|log.LstdFlags)
+
+ if filePath == "" </span><span class="cov0" title="0">{
+ // We will just to std streams
+ return
+ }</span>
+
+ <span class="cov1" title="1">f, err := os.Create(filePath)
+ if err != nil </span><span class="cov0" title="0">{
+ stdErr.Println("Unable to create log file: " + err.Error())
+ return
+ }</span>
+
+ <span class="cov1" title="1">errL = log.New(f, "ERROR: ", log.Lshortfile|log.LstdFlags)
+ warnL = log.New(f, "WARNING: ", log.Lshortfile|log.LstdFlags)
+ infoL = log.New(f, "INFO: ", log.Lshortfile|log.LstdFlags)</span>
+}
+
+// WriteError writes output to the writer we have
+// defined during its creation with ERROR prefix
+func WriteError(msg string) <span class="cov0" title="0">{
+ if errL != nil </span><span class="cov0" title="0">{
+ errL.Output(2, fmt.Sprintln(msg))
+ }</span>
+ <span class="cov0" title="0">if stdErr != nil </span><span class="cov0" title="0">{
+ stdErr.Output(2, fmt.Sprintln(msg))
+ }</span>
+}
+
+// WriteWarn writes output to the writer we have
+// defined during its creation with WARNING prefix
+func WriteWarn(msg string) <span class="cov0" title="0">{
+ if warnL != nil </span><span class="cov0" title="0">{
+ warnL.Output(2, fmt.Sprintln(msg))
+ }</span>
+ <span class="cov0" title="0">if stdWarn != nil </span><span class="cov0" title="0">{
+ stdWarn.Output(2, fmt.Sprintln(msg))
+ }</span>
+}
+
+// WriteInfo writes output to the writer we have
+// defined during its creation with INFO prefix
+func WriteInfo(msg string) <span class="cov1" title="1">{
+ if infoL != nil </span><span class="cov1" title="1">{
+ infoL.Output(2, fmt.Sprintln(msg))
+ }</span>
+ <span class="cov1" title="1">if stdInfo != nil </span><span class="cov1" title="1">{
+ stdInfo.Output(2, fmt.Sprintln(msg))
+ }</span>
+}
+
+//CheckError is a helper function to reduce
+//repitition of error checkign blocks of code
+func CheckError(err error, topic string) error <span class="cov10" title="116">{
+ if err != nil </span><span class="cov1" title="1">{
+ msg := topic + ": " + err.Error()
+ if errL != nil </span><span class="cov1" title="1">{
+ errL.Output(2, fmt.Sprintln(msg))
+ }</span>
+ <span class="cov1" title="1">if stdErr != nil </span><span class="cov1" title="1">{
+ stdErr.Output(2, fmt.Sprintln(msg))
+ }</span>
+ <span class="cov1" title="1">return err</span>
+ }
+ <span class="cov9" title="115">return nil</span>
+}
+</pre>
+
+ <pre class="file" id="file6" style="display: none">/*
+ * Copyright 2018 Intel Corporation, Inc
+ *
+ * Licensed 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.
+ */
+
+package main
+
+import (
+ "context"
+ "log"
+ "net/http"
+ "os"
+ "os/signal"
+
+ smsauth "sms/auth"
+ smsbackend "sms/backend"
+ smsconfig "sms/config"
+ smshandler "sms/handler"
+ smslogger "sms/log"
+)
+
+func main() <span class="cov8" title="1">{
+ // Initialize logger
+ smslogger.Init("sms.log")
+
+ // Read Configuration File
+ smsConf, err := smsconfig.ReadConfigFile("smsconfig.json")
+ if err != nil </span><span class="cov0" title="0">{
+ log.Fatal(err)
+ }</span>
+
+ <span class="cov8" title="1">backendImpl, err := smsbackend.InitSecretBackend()
+ if err != nil </span><span class="cov0" title="0">{
+ log.Fatal(err)
+ }</span>
+
+ <span class="cov8" title="1">httpRouter := smshandler.CreateRouter(backendImpl)
+
+ httpServer := &amp;http.Server{
+ Handler: httpRouter,
+ Addr: ":10443",
+ }
+
+ // Listener for SIGINT so that it returns cleanly
+ connectionsClose := make(chan struct{})
+ go func() </span><span class="cov8" title="1">{
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, os.Interrupt)
+ &lt;-c
+ httpServer.Shutdown(context.Background())
+ close(connectionsClose)
+ }</span>()
+
+ // Start in TLS mode by default
+ <span class="cov8" title="1">if smsConf.DisableTLS == true </span><span class="cov0" title="0">{
+ smslogger.WriteWarn("TLS is Disabled")
+ err = httpServer.ListenAndServe()
+ }</span><span class="cov8" title="1"> else {
+ // TODO: Use CA certificate from AAF
+ tlsConfig, err := smsauth.GetTLSConfig(smsConf.CAFile)
+ if err != nil </span><span class="cov0" title="0">{
+ log.Fatal(err)
+ }</span>
+
+ <span class="cov8" title="1">httpServer.TLSConfig = tlsConfig
+ err = httpServer.ListenAndServeTLS(smsConf.ServerCert, smsConf.ServerKey)</span>
+ }
+
+ <span class="cov8" title="1">if err != nil &amp;&amp; err != http.ErrServerClosed </span><span class="cov0" title="0">{
+ log.Fatal(err)
+ }</span>
+
+ <span class="cov8" title="1">&lt;-connectionsClose</span>
+}
+</pre>
+
+ </div>
+ </body>
+ <script>
+ (function() {
+ var files = document.getElementById('files');
+ var visible;
+ files.addEventListener('change', onChange, false);
+ function select(part) {
+ if (visible)
+ visible.style.display = 'none';
+ visible = document.getElementById(part);
+ if (!visible)
+ return;
+ files.value = part;
+ visible.style.display = 'block';
+ location.hash = part;
+ }
+ function onChange() {
+ select(files.value);
+ window.scrollTo(0, 0);
+ }
+ if (location.hash != "") {
+ select(location.hash.substr(1));
+ }
+ if (!visible) {
+ select("file0");
+ }
+ })();
+ </script>
+</html>
diff --git a/docs/coverage.md b/docs/coverage.md
new file mode 100644
index 0000000..6168342
--- /dev/null
+++ b/docs/coverage.md
@@ -0,0 +1,41 @@
+## Code Coverage Reports for Golang Applications ##
+
+This document covers how to generate HTML Code Coverage Reports for
+Golang Applications.
+
+#### Generate a test executable which calls your main()
+
+```sh
+$ go test -c -covermode=count -coverpkg ./...
+```
+
+#### Run the generated application to produce a new coverage report
+
+```sh
+$ ./sms.test -test.run "^TestMain$" -test.coverprofile=coverage.cov
+```
+
+#### Run your unit tests to produce their coverage report
+
+```sh
+$ go test -test.covermode=count -test.coverprofile=unit.out ./...
+```
+
+#### Merge the two coverage Reports
+
+```sh
+$ go get github.com/wadey/gocovmerge
+$ gocovmerge unit.out coverage.cov > all.out
+```
+
+#### Generate HTML Report
+
+```sh
+$ go tool cover -html all.out -o coverage.html
+```
+
+#### Generate Function Report
+
+```sh
+$ go tool cover -func all.out
+``` \ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..5f17a04
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,37 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. Copyright 2018 Intel Corporation, Inc
+
+SMS-Secret Management Service
+==================================
+
+.. toctree::
+ :maxdepth: 1
+
+ installation
+ usage
+ apiswagger
+
+
+Introduction
+------------
+
+This project aims at the Storage of sensitive information such as passwords.
+
+**Current state and gaps**
+
+Many services in ONAP use password based authentication. Eg: Database servers, publish/subscribe brokers etc.
+Passwords are stored in plain text files in many services.
+With multiple instances of these services, the attach surface area becomes very big.
+Hence there is a need to ensure that attack surface related to password exposure is reduced.
+
+**Requirement:**
+
+Need for secure secret management. Services are expected to get the secret only on needed basis using secret reference and remove the secrets once they are used up.
+
+**Secret Service High Level Flow Diagram**
+
+.. image:: sms_high_level.png
+ :width: 4555550px
+ :height: 300px
+ :alt: SMS Flow Diagram
diff --git a/docs/installation.rst b/docs/installation.rst
new file mode 100644
index 0000000..b22d133
--- /dev/null
+++ b/docs/installation.rst
@@ -0,0 +1,33 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. Copyright 2018 Intel Corporation, Inc
+
+Installation
+============
+
+The Secret Managment Project is a subproject of AAF and will deployed via Helm on Kubernetes
+under the OOM Project
+
+.. code-block:: console
+
+ # Set Datastore as Consul
+ DATASTORE="consul"
+ # Set IP address of where Consul is running
+ DATASTORE_IP="localhost"
+ # Set mountpath inside the container where persistent data is stored.
+ MOUNTPATH="/dkv_mount_path/configs/"
+ # Place all Config data which needs to be loaded in default directory.
+ DEFAULT_CONFIGS=$(pwd)/mountpath/default
+ # Create the directories.
+ mkdir -p mountpath/default
+ # Login to Nexus.
+ docker login -u docker -p docker nexus3.onap.org:10001
+ # Pull distributed-kv-store image.
+ docker pull nexus3.onap.org:10001/onap/music/distributed-kv-store
+ # Run the distributed-kv-store image.
+ docker run -e DATASTORE=$DATASTORE -e DATASTORE_IP=$DATASTORE_IP -e MOUNTPATH=$MOUNTPATH -d \
+ --name dkv \
+ -v $DEFAULT_CONFIGS:/dkv_mount_path/configs/default \
+ -p 8200:8200 -p 8080:8080 nexus3.onap.org:10001/onap/music/distributed-kv-store
+
+.. end
diff --git a/docs/sms_high_level.png b/docs/sms_high_level.png
new file mode 100644
index 0000000..3cd14ba
--- /dev/null
+++ b/docs/sms_high_level.png
Binary files differ
diff --git a/docs/usage.rst b/docs/usage.rst
new file mode 100644
index 0000000..b35e9b5
--- /dev/null
+++ b/docs/usage.rst
@@ -0,0 +1,54 @@
+.. This work is licensed under a Creative Commons Attribution 4.0 International License.
+.. http://creativecommons.org/licenses/by/4.0
+.. Copyright 2018 Intel Corporation, Inc
+
+Typical Usage Scenario
+======================
+
+.. code-block:: guess
+
+ ## Create a Domain
+ ## This is where all your secrets will be stored
+ curl -H "Accept: application/json" --cacert ca.pem --cert client.cert --key client.key
+ -X POST \
+ -d '{
+ "name": "mysecretdomain"
+ }'
+ https://sms:10443/v1/sms/domain
+
+ ## Add a new Secret
+ curl -H "Accept: application/json" --cacert ca.pem --cert client.cert --key client.key
+ -X POST \
+ -d '{
+ "name": "mysecret",
+ "values": {
+ "name": "rah",
+ "age": 35,
+ "password": "mypassword"
+ }
+ }'
+ https://sms:10443/v1/sms/domain/<domaincurltestdomain/secret
+
+
+ ## List all Secrets under a Domain
+ curl -H "Accept: application/json" --cacert ca.pem --cert client.cert --key client.key
+ -X GET \
+ https://sms:10443/v1/sms/domain/curltestdomain/secret
+
+ ## Get a Secret in a Domain
+ curl -H "Accept: application/json" --cacert ca.pem --cert client.cert --key client.key
+ -X GET \
+ https://sms:10443/v1/sms/domain/curltestdomain/secret/curltestsecret1
+
+ ## Delete a Secret in specified Domain
+ curl -H "Accept: application/json" --cacert ca.pem --cert client.cert --key client.key
+ -X DELETE \
+ https://sms:10443/v1/sms/domain/curltestdomain/secret/curltestsecret1
+
+ ## Delete a Domain
+ ## This will delete all the secrets in that Domain
+ curl -H "Accept: application/json" --cacert ca.pem --cert client.cert --key client.key
+ -X DELETE \
+ https://sms:10443/v1/sms/domain/curltestdomain
+
+.. end