aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2021-11-24 10:27:36 -0800
committerMike Crute <mike@crute.us>2021-11-24 10:28:20 -0800
commitff05652956161dd94aa109e2c5d40bd82d4cfd5d (patch)
tree67a9e756e64a1e42538e25e46da92c3dd29ba23c
parent7ba9e94bae1cbeba7fc7e390d09e2821ba46b996 (diff)
downloadcloud-identity-broker-ff05652956161dd94aa109e2c5d40bd82d4cfd5d.tar.bz2
cloud-identity-broker-ff05652956161dd94aa109e2c5d40bd82d4cfd5d.tar.xz
cloud-identity-broker-ff05652956161dd94aa109e2c5d40bd82d4cfd5d.zip
Move credential rendering to server
-rw-r--r--app/controllers/api.go7
-rw-r--r--app/controllers/api_credentials.go84
-rw-r--r--templates/assets/site.js18
-rw-r--r--templates/index.tpl27
4 files changed, 93 insertions, 43 deletions
diff --git a/app/controllers/api.go b/app/controllers/api.go
index 5ee6591..39fc227 100644
--- a/app/controllers/api.go
+++ b/app/controllers/api.go
@@ -10,8 +10,11 @@ import (
10) 10)
11 11
12const ( 12const (
13 contentTypeV1 = "application/vnd.broker.v1+json" // Original type 13 contentTypeV1 = "application/vnd.broker.v1+json" // Original type
14 contentTypeV2 = "application/vnd.broker.v2+json" // Start of migration to multi-cloud 14 contentTypeV2 = "application/vnd.broker.v2+json" // Start of migration to multi-cloud
15 contentTypeV2AWSBash = "application/vnd.broker.v2.credential.aws.sh" // Bash Formatted Credential for AWS
16 contentTypeV2AWSPowershell = "application/vnd.broker.v2.credential.aws.psl" // Powershell Formatted Credential for AWS
17 contentTypeV2AWSConfig = "application/vnd.broker.v2.credential.aws.ini" // INI Formatted Credential for AWS
15) 18)
16 19
17func APIIndexHandler(c echo.Context) error { 20func APIIndexHandler(c echo.Context) error {
diff --git a/app/controllers/api_credentials.go b/app/controllers/api_credentials.go
index cd1a912..d53565e 100644
--- a/app/controllers/api_credentials.go
+++ b/app/controllers/api_credentials.go
@@ -1,7 +1,9 @@
1package controllers 1package controllers
2 2
3import ( 3import (
4 "bytes"
4 "net/http" 5 "net/http"
6 "text/template"
5 "time" 7 "time"
6 8
7 "code.crute.us/mcrute/cloud-identity-broker/cloud/aws" 9 "code.crute.us/mcrute/cloud-identity-broker/cloud/aws"
@@ -23,8 +25,33 @@ type jsonCredential struct {
23 SecretAccessKey *string `json:"secret_key"` 25 SecretAccessKey *string `json:"secret_key"`
24 SessionToken *string `json:"session_token"` 26 SessionToken *string `json:"session_token"`
25 Expiration *time.Time `json:"expiration"` 27 Expiration *time.Time `json:"expiration"`
28 ShortName string `json:"-"`
26} 29}
27 30
31var (
32 bashTemplate = template.Must(template.New("").Parse(
33 `export AWS_CREDS_EXPIRATION="{{ .Expiration }}"
34export AWS_ACCESS_KEY_ID="{{ .AccessKeyId }}"
35export AWS_SECRET_ACCESS_KEY="{{ .SecretAccessKey }}"
36export AWS_SESSION_TOKEN="{{ .SessionToken }}"
37`))
38
39 pslTemplate = template.Must(template.New("").Parse(
40 `Set-Item -path env:AWS_CREDS_EXPIRATION -value '{{ .Expiration }}'
41Set-Item -path env:AWS_ACCESS_KEY_ID -value '{{ .AccessKeyId }}'
42Set-Item -path env:AWS_SECRET_ACCESS_KEY -value '{{ .SecretAccessKey }}'
43Set-Item -path env:AWS_SESSION_TOKEN -value '{{ .SessionToken }}'
44`))
45
46 iniTemplate = template.Must(template.New("").Parse(
47 `[profile {{ .ShortName }}]
48aws_access_key_id={{ .AccessKeyId }}
49aws_secret_access_key={{ .SecretAccessKey }}
50aws_session_token={{ .SessionToken }}
51expiration={{ .Expiration }}
52`))
53)
54
28type APICredentialsHandler struct { 55type APICredentialsHandler struct {
29 *AWSAPI 56 *AWSAPI
30} 57}
@@ -32,29 +59,32 @@ type APICredentialsHandler struct {
32func NewAPICredentialsHandler(a *AWSAPI) echo.HandlerFunc { 59func NewAPICredentialsHandler(a *AWSAPI) echo.HandlerFunc {
33 al := &APICredentialsHandler{a} 60 al := &APICredentialsHandler{a}
34 h := &controller.ContentTypeNegotiatingHandler{ 61 h := &controller.ContentTypeNegotiatingHandler{
35 DefaultHandler: al.Handle, 62 DefaultHandler: al.HandleJSONV1,
36 Handlers: map[string]echo.HandlerFunc{ 63 Handlers: map[string]echo.HandlerFunc{
37 contentTypeV1: al.Handle, 64 contentTypeV1: al.HandleJSONV1,
38 contentTypeV2: al.Handle, 65 contentTypeV2: al.HandleJSONV1,
66 contentTypeV2AWSBash: al.HandleBashV2,
67 contentTypeV2AWSPowershell: al.HandlePSLV2,
68 contentTypeV2AWSConfig: al.HandleINIV2,
39 }, 69 },
40 } 70 }
41 return h.Handle 71 return h.Handle
42} 72}
43 73
44func (h *APICredentialsHandler) Handle(c echo.Context) error { 74func (h *APICredentialsHandler) getAWSCredential(c echo.Context) (*jsonCredential, error) {
45 rc, err := h.GetContext(c) // Does authorization checks 75 rc, err := h.GetContext(c) // Does authorization checks
46 if err != nil { 76 if err != nil {
47 return err 77 return nil, err
48 } 78 }
49 79
50 region := c.Param("region") 80 region := c.Param("region")
51 creds, err := rc.AWS.AssumeRole(rc.Principal.Username, &region) 81 creds, err := rc.AWS.AssumeRole(rc.Principal.Username, &region)
52 if err != nil { 82 if err != nil {
53 if aws.IsRegionNotExist(err) { 83 if aws.IsRegionNotExist(err) {
54 return echo.NotFoundHandler(c) 84 return nil, echo.NotFoundHandler(c)
55 } 85 }
56 c.Logger().Errorf("Error retrieving credentials: %w", err) 86 c.Logger().Errorf("Error retrieving credentials: %w", err)
57 return echo.ErrInternalServerError 87 return nil, echo.ErrInternalServerError
58 } 88 }
59 89
60 c.Logger().Infof( 90 c.Logger().Infof(
@@ -68,10 +98,46 @@ func (h *APICredentialsHandler) Handle(c echo.Context) error {
68 98
69 c.Response().Header().Set("Expires", creds.Expiration.Add(-5*time.Minute).Format(time.RFC1123)) 99 c.Response().Header().Set("Expires", creds.Expiration.Add(-5*time.Minute).Format(time.RFC1123))
70 100
71 return c.JSON(http.StatusOK, &jsonCredential{ 101 return &jsonCredential{
72 AccessKeyId: creds.AccessKeyId, 102 AccessKeyId: creds.AccessKeyId,
73 SecretAccessKey: creds.SecretAccessKey, 103 SecretAccessKey: creds.SecretAccessKey,
74 SessionToken: creds.SessionToken, 104 SessionToken: creds.SessionToken,
75 Expiration: creds.Expiration, 105 Expiration: creds.Expiration,
76 }) 106 ShortName: rc.Account.ShortName,
107 }, nil
108}
109
110func (h *APICredentialsHandler) renderTemplate(c echo.Context, t *template.Template, ct string) error {
111 creds, err := h.getAWSCredential(c)
112 if err != nil {
113 return err
114 }
115
116 buf := &bytes.Buffer{}
117 if err = t.Execute(buf, creds); err != nil {
118 return echo.ErrInternalServerError
119 }
120
121 return c.Blob(http.StatusOK, ct, buf.Bytes())
122}
123
124func (h *APICredentialsHandler) HandleJSONV1(c echo.Context) error {
125 creds, err := h.getAWSCredential(c)
126 if err != nil {
127 return err
128 }
129
130 return c.JSON(http.StatusOK, creds)
131}
132
133func (h *APICredentialsHandler) HandleBashV2(c echo.Context) error {
134 return h.renderTemplate(c, bashTemplate, contentTypeV2AWSBash)
135}
136
137func (h *APICredentialsHandler) HandlePSLV2(c echo.Context) error {
138 return h.renderTemplate(c, pslTemplate, contentTypeV2AWSPowershell)
139}
140
141func (h *APICredentialsHandler) HandleINIV2(c echo.Context) error {
142 return h.renderTemplate(c, iniTemplate, contentTypeV2AWSConfig)
77} 143}
diff --git a/templates/assets/site.js b/templates/assets/site.js
index 485a4cd..ebf9f5f 100644
--- a/templates/assets/site.js
+++ b/templates/assets/site.js
@@ -29,9 +29,7 @@ function accountTableLinkClick(event) {
29 event.preventDefault(); 29 event.preventDefault();
30 30
31 var thisRow = event.target.parentElement.parentElement; 31 var thisRow = event.target.parentElement.parentElement;
32 var template = event.target.getAttribute("data-template"); 32 var account = thisRow.dataset["accountName"];
33 var account = thisRow.getAttribute("data-account-name");
34 var credentialEndpoint = thisRow.getAttribute("data-global-credential-endpoint");
35 var oldText = event.target.text; 33 var oldText = event.target.text;
36 34
37 var existingTr = document.getElementById("credentials-for-" + account); 35 var existingTr = document.getElementById("credentials-for-" + account);
@@ -41,12 +39,14 @@ function accountTableLinkClick(event) {
41 39
42 event.target.text = "Loading..."; 40 event.target.text = "Loading...";
43 41
44 fetch(credentialEndpoint).then(getJSON).then(function(vals) { 42 fetch(thisRow.dataset["globalCredentialEndpoint"], {
45 vals["ShortName"] = account; 43 "headers": {
46 44 "Accept": event.target.dataset["contentType"]
45 }
46 }).then(r => r.text()).then(function(text) {
47 var newTr = fillTemplate("credential_row_template", { 47 var newTr = fillTemplate("credential_row_template", {
48 "Account": account, 48 "Account": account,
49 "Content": fillTemplate(template, vals) 49 "Content": text,
50 }); 50 });
51 51
52 event.target.text = oldText; 52 event.target.text = oldText;
@@ -69,8 +69,8 @@ function populateAccountRow(row) {
69 var out = fillTemplate("account_row_template", row); 69 var out = fillTemplate("account_row_template", row);
70 document.querySelector("#account-table tr").insertAdjacentHTML("afterend", out); 70 document.querySelector("#account-table tr").insertAdjacentHTML("afterend", out);
71 71
72 document.querySelectorAll("#account-row-" + row["short_name"] + " a[data-template]").forEach(function(element) { 72 document.querySelectorAll(".account-row a[data-content-type]").forEach(function(e) {
73 element.addEventListener("click", accountTableLinkClick); 73 e.addEventListener("click", accountTableLinkClick);
74 }); 74 });
75} 75}
76 76
diff --git a/templates/index.tpl b/templates/index.tpl
index da0b59e..c8c95d4 100644
--- a/templates/index.tpl
+++ b/templates/index.tpl
@@ -4,25 +4,6 @@
4 <title>Select Account</title> 4 <title>Select Account</title>
5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6 <link rel="stylesheet" type="text/css" href="/assets/site.css" /> 6 <link rel="stylesheet" type="text/css" href="/assets/site.css" />
7 <script id="shell_template" type="text/template">
8 export AWS_CREDS_EXPIRATION="[[ .expiration ]]"
9 export AWS_ACCESS_KEY_ID="[[ .access_key ]]"
10 export AWS_SECRET_ACCESS_KEY="[[ .secret_key ]]"
11 export AWS_SESSION_TOKEN="[[ .session_token ]]"
12 </script>
13 <script id="powershell_template" type="text/template">
14 Set-Item -path env:AWS_CREDS_EXPIRATION -value '[[ .expiration ]]'
15 Set-Item -path env:AWS_ACCESS_KEY_ID -value '[[ .access_key ]]'
16 Set-Item -path env:AWS_SECRET_ACCESS_KEY -value '[[ .secret_key ]]'
17 Set-Item -path env:AWS_SESSION_TOKEN -value '[[ .session_token ]]'
18 </script>
19 <script id="aws_config_template" type="text/template">
20 [profile [[ .ShortName ]]]
21 aws_access_key_id=[[ .access_key ]]
22 aws_secret_access_key=[[ .secret_key ]]
23 aws_session_token=[[ .session_token ]]
24 expiration=[[ .expiration ]]
25 </script>
26 <script id="credential_row_template" type="text/template"> 7 <script id="credential_row_template" type="text/template">
27 <tr id="credentials-for-[[ .Account ]]"> 8 <tr id="credentials-for-[[ .Account ]]">
28 <td colspan="3"> 9 <td colspan="3">
@@ -32,14 +13,14 @@
32 </tr> 13 </tr>
33 </script> 14 </script>
34 <script id="account_row_template" type="text/template"> 15 <script id="account_row_template" type="text/template">
35 <tr id="account-row-[[ .short_name ]]" data-account-name="[[ .short_name ]]" data-global-credential-endpoint="[[ .global_credential_url ]]"> 16 <tr id="account-row-[[ .short_name ]]" class="account-row" data-account-name="[[ .short_name ]]" data-global-credential-endpoint="[[ .global_credential_url ]]">
36 <td>[[ .name ]]</td> 17 <td>[[ .name ]]</td>
37 <td>[[ .vendor ]]</td> 18 <td>[[ .vendor ]]</td>
38 <td> 19 <td>
39 <a href="[[ .console_redirect_url ]]">Console</a> | 20 <a href="[[ .console_redirect_url ]]">Console</a> |
40 <a data-template="aws_config_template" href="#/cli/[[ .short_name ]]">AWS CLI</a> | 21 <a data-content-type="application/vnd.broker.v2.credential.aws.ini" href="#/cli/[[ .short_name ]]">AWS CLI</a> |
41 <a data-template="shell_template" href="#/sh/[[ .short_name ]]">Bash</a> | 22 <a data-content-type="application/vnd.broker.v2.credential.aws.sh" href="#/sh/[[ .short_name ]]">Bash</a> |
42 <a data-template="powershell_template" href="#/ps/[[ .short_name ]]">Powershell</a> 23 <a data-content-type="application/vnd.broker.v2.credential.aws.psl" href="#/ps/[[ .short_name ]]">Powershell</a>
43 <span class="admin">| <a>Edit</a></span> 24 <span class="admin">| <a>Edit</a></span>
44 </td> 25 </td>
45 </tr> 26 </tr>