Browse code
feature: dialog for entering aws credentials
Edward Langley authored on 16/10/2019 18:01:33
Showing 7 changed files
Showing 7 changed files
- aws-access.asd
- src/capi-interface.lisp
- src/credential-provider.lisp
- src/domain.lisp
- src/mfa-tool.lisp
- src/read-credentials.lisp
- src/store.lisp
... | ... |
@@ -28,10 +28,11 @@ |
28 | 28 |
(:file "aws-dispatcher") |
29 | 29 |
(:file "domain") |
30 | 30 |
(:file "objc-utils") |
31 |
+ (:file "read-credentials") |
|
32 |
+ (:file "credential-provider") |
|
31 | 33 |
(:file "mfa-tool") |
32 | 34 |
(:file "stack-store") |
33 | 35 |
(:file "stack") |
34 |
- (:file "credential-provider") |
|
35 | 36 |
(:file "capi-interface"))))) |
36 | 37 |
|
37 | 38 |
(defsystem :aws-access/tests |
... | ... |
@@ -185,30 +185,14 @@ |
185 | 185 |
(out (make-instance 'capi:collector-pane))) |
186 | 186 |
(princ condition (capi:collector-pane-stream out)) |
187 | 187 |
(prin1 (mapcar 'restart-name |
188 |
- (compute-restarts condition) ) |
|
188 |
+ (compute-restarts condition)) |
|
189 | 189 |
(capi:collector-pane-stream out)) |
190 |
- (typecase condition |
|
191 |
- (aws-sdk:no-credentials |
|
192 |
- (fresh-line (capi:collector-pane-stream out)) |
|
193 |
- (format (capi:collector-pane-stream out) "Credentials file ~:[doesn't~;does~] exist for me~%" |
|
194 |
- (probe-file (merge-pathnames ".aws/credentials" |
|
195 |
- (user-homedir-pathname)))) |
|
196 |
- (when (probe-file (merge-pathnames ".aws/credentials" |
|
197 |
- (user-homedir-pathname))) |
|
198 |
- (princ (alexandria:read-file-into-string (merge-pathnames ".aws/credentials" |
|
199 |
- (user-homedir-pathname))) |
|
200 |
- (capi:collector-pane-stream out))) |
|
201 |
- (terpri (capi:collector-pane-stream out)) |
|
202 |
- (mfa-tool.credential-provider:debug-provider (capi:collector-pane-stream out)) |
|
203 |
- (terpri (capi:collector-pane-stream out)) |
|
204 |
- (terpri) |
|
205 |
- )) |
|
206 | 190 |
(dbg:output-backtrace :stream (capi:collector-pane-stream out)) |
207 | 191 |
(terpri) |
208 | 192 |
(terpri) |
209 | 193 |
(capi:contain out) |
210 |
- (dbg:log-bug-form "fail") |
|
211 |
- (abort))) |
|
194 |
+ (dbg:log-bug-form "fail")) |
|
195 |
+ (abort)) |
|
212 | 196 |
|
213 | 197 |
(defun main () |
214 | 198 |
(mfa-tool.credential-provider:setup-default-chain) |
... | ... |
@@ -2,7 +2,8 @@ |
2 | 2 |
(:use :cl ) |
3 | 3 |
(:export #:make-aws-session |
4 | 4 |
#:debug-provider |
5 |
- #:setup-default-chain)) |
|
5 |
+ #:setup-default-chain |
|
6 |
+ #:save-ubiquitous-credentials)) |
|
6 | 7 |
(in-package :mfa-tool.credential-provider) |
7 | 8 |
|
8 | 9 |
(defstruct hash-ref name hash-table) |
... | ... |
@@ -93,14 +94,16 @@ |
93 | 94 |
:provider-name "ubiquitous-provider"))) |
94 | 95 |
|
95 | 96 |
(defun save-ubiquitous-credentials (credentials) |
96 |
- (setf (ubiquitous:value :aws :access-key-id) |
|
97 |
- (aws-sdk/credentials/base:credentials-access-key-id credentials) |
|
97 |
+ (prog1 credentials |
|
98 |
+ (setf (ubiquitous:value :aws :access-key-id) |
|
99 |
+ (aws-sdk/credentials/base:credentials-access-key-id credentials) |
|
98 | 100 |
|
99 |
- (ubiquitous:value :aws :secret-access-key) |
|
100 |
- (aws-sdk/credentials/base:credentials-secret-access-key credentials) |
|
101 |
+ (ubiquitous:value :aws :secret-access-key) |
|
102 |
+ (aws-sdk/credentials/base:credentials-secret-access-key credentials) |
|
101 | 103 |
|
102 |
- (ubiquitous:value :aws :session-token) |
|
103 |
- (aws-sdk/credentials/base:credentials-session-token credentials))) |
|
104 |
+ (ubiquitous:value :aws :session-token) |
|
105 |
+ (aws-sdk/credentials/base:credentials-session-token credentials)) |
|
106 |
+ (ubiquitous:offload))) |
|
104 | 107 |
|
105 | 108 |
(defun make-aws-session () |
106 | 109 |
(let ((aws-sdk/credentials::*chained-providers* |
... | ... |
@@ -120,6 +120,10 @@ |
120 | 120 |
(values signin-token |
121 | 121 |
parser)))))) |
122 | 122 |
|
123 |
+(defun set-aws-credentials (access-key-id secret-access-key &optional condition) |
|
124 |
+ (alexandria:when-let ((restart (find-restart 'set-aws-credentials condition))) |
|
125 |
+ (invoke-restart restart access-key-id secret-access-key))) |
|
126 |
+ |
|
123 | 127 |
(defun open-url (url) |
124 | 128 |
(capi:contain (make-instance 'capi:browser-pane |
125 | 129 |
:url url) |
... | ... |
@@ -1,7 +1,6 @@ |
1 | 1 |
(in-package :mfa-tool) |
2 | 2 |
|
3 | 3 |
(defun account-selected (account) |
4 |
- (format t "~s" account) |
|
5 | 4 |
(setf (ubiquitous:value :default-account) |
6 | 5 |
(cdr account))) |
7 | 6 |
|
... | ... |
@@ -25,6 +24,26 @@ |
25 | 24 |
(credentials-for-account interface |
26 | 25 |
(current-account interface))) |
27 | 26 |
|
27 |
+ |
|
28 |
+(defmethod mfa-tool.store:execute ((store (eql :mfa-tool.debugger)) |
|
29 |
+ (action mfa-tool.read-credentials:credential-update)) |
|
30 |
+ (with-accessors ((access-key mfa-tool.read-credentials:access-key) |
|
31 |
+ (secret-access-key mfa-tool.read-credentials:secret-access-key)) action |
|
32 |
+ (mfa-tool.credential-provider:save-ubiquitous-credentials |
|
33 |
+ (aws-sdk:make-credentials :access-key-id access-key |
|
34 |
+ :secret-access-key secret-access-key |
|
35 |
+ :session-token nil)))) |
|
36 |
+ |
|
37 |
+(defun authenticate (account user-name token) |
|
38 |
+ (handler-bind ((aws-sdk:no-credentials |
|
39 |
+ (lambda (c) |
|
40 |
+ (alexandria:when-let |
|
41 |
+ ((result (mfa-tool.read-credentials:prompt-for-aws-credentials :mfa-tool.debugger))) |
|
42 |
+ (set-aws-credentials (mfa-tool.read-credentials:access-key result) |
|
43 |
+ (mfa-tool.read-credentials:secret-access-key result) |
|
44 |
+ c))))) |
|
45 |
+ (run-process account user-name token))) |
|
46 |
+ |
|
28 | 47 |
(defun go-on (_ interface) |
29 | 48 |
(declare (ignore _)) |
30 | 49 |
(let ((token (capi:text-input-pane-text (mfa-input interface))) |
... | ... |
@@ -47,19 +66,20 @@ |
47 | 66 |
new-code) |
48 | 67 |
(change-mfa-token new-code)) |
49 | 68 |
(capi:abort-callback))))))) |
50 |
- (run-process account user-name token)) |
|
69 |
+ (authenticate account user-name token)) |
|
51 | 70 |
(with-open-file (stream (make-pathname :name "" |
52 | 71 |
:type "cj-aws" |
53 | 72 |
:defaults (user-homedir-pathname)) |
54 | 73 |
:direction :output |
55 | 74 |
:if-exists :rename |
56 | 75 |
:if-does-not-exist :create) |
57 |
- (format (make-broadcast-stream stream |
|
58 |
- (capi:collector-pane-stream (output interface))) |
|
59 |
- "export AWS_ACCESS_KEY_ID='~a'~%export AWS_SECRET_ACCESS_KEY='~a'~%export AWS_SESSION_TOKEN='~a'~%" |
|
60 |
- (session-id creds) |
|
61 |
- (session-key creds) |
|
62 |
- (session-token creds))) |
|
76 |
+ (let ((cred-stream (make-broadcast-stream stream |
|
77 |
+ (capi:collector-pane-stream (output interface))))) |
|
78 |
+ (format cred-stream |
|
79 |
+ "export AWS_ACCESS_KEY_ID='~a'~%export AWS_SECRET_ACCESS_KEY='~a'~%export AWS_SESSION_TOKEN='~a'~%" |
|
80 |
+ (session-id creds) |
|
81 |
+ (session-key creds) |
|
82 |
+ (session-token creds)))) |
|
63 | 83 |
(capi:set-button-panel-enabled-items (slot-value interface 'action-buttons) |
64 | 84 |
:set t) |
65 | 85 |
(setf (credentials-for-account interface account) (session-credentials creds) |
66 | 86 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,130 @@ |
1 |
+(defpackage :mfa-tool.read-credentials |
|
2 |
+ (:use :cl) |
|
3 |
+ (:export #:access-key |
|
4 |
+ #:secret-access-key |
|
5 |
+ #:credential-update |
|
6 |
+ #:prompt-for-aws-credentials)) |
|
7 |
+(in-package :mfa-tool.read-credentials) |
|
8 |
+ |
|
9 |
+(defvar *store*) |
|
10 |
+ |
|
11 |
+(defun column (attrs &rest children) |
|
12 |
+ (apply 'make-instance 'capi:column-layout |
|
13 |
+ :children children |
|
14 |
+ attrs)) |
|
15 |
+ |
|
16 |
+(defun row (attrs &rest children) |
|
17 |
+ (apply 'make-instance 'capi:row-layout |
|
18 |
+ :children children |
|
19 |
+ attrs)) |
|
20 |
+ |
|
21 |
+(defun dispatch-with-action-creator (action-creator) |
|
22 |
+ (let ((store *store*)) |
|
23 |
+ (lambda (data) |
|
24 |
+ (mfa-tool.store:dispatch store |
|
25 |
+ (funcall action-creator data))))) |
|
26 |
+ |
|
27 |
+(defun dispatch-with-item (store) |
|
28 |
+ (lambda (data) |
|
29 |
+ (mfa-tool.store:dispatch store data))) |
|
30 |
+ |
|
31 |
+(defun list-panel (action attrs items) |
|
32 |
+ (apply 'make-instance 'capi:list-panel |
|
33 |
+ :items items |
|
34 |
+ :selection-callback (dispatch-with-action-creator action) |
|
35 |
+ :callback-type :data |
|
36 |
+ attrs)) |
|
37 |
+ |
|
38 |
+(defun select (action attrs items) |
|
39 |
+ (apply 'make-instance 'capi:option-pane |
|
40 |
+ :items items |
|
41 |
+ :selection-callback (dispatch-with-action-creator action) |
|
42 |
+ :callback-type :data |
|
43 |
+ attrs)) |
|
44 |
+ |
|
45 |
+(defun text-input (action attrs &optional (text "")) |
|
46 |
+ (apply 'make-instance 'capi:text-input-pane |
|
47 |
+ :text text |
|
48 |
+ :change-callback (dispatch-with-action-creator action) |
|
49 |
+ attrs)) |
|
50 |
+ |
|
51 |
+(defun button-panel (attrs &rest items) |
|
52 |
+ (apply 'make-instance 'capi:push-button-panel |
|
53 |
+ :items items |
|
54 |
+ :selection-callback (dispatch-with-item *store*) |
|
55 |
+ :callback-type :data |
|
56 |
+ attrs)) |
|
57 |
+ |
|
58 |
+(defclass update-access-key () |
|
59 |
+ ((%key :initarg :key :reader key))) |
|
60 |
+(defun update-access-key (key) |
|
61 |
+ (fw.lu:new 'update-access-key key)) |
|
62 |
+ |
|
63 |
+(defclass update-secret-access-key () |
|
64 |
+ ((%key :initarg :key :reader key))) |
|
65 |
+(defun update-secret-access-key (key) |
|
66 |
+ (fw.lu:new 'update-secret-access-key key)) |
|
67 |
+ |
|
68 |
+(defun layout () |
|
69 |
+ (column () |
|
70 |
+ (make-instance 'capi:grid-layout |
|
71 |
+ :x-adjust :right |
|
72 |
+ :y-adjust :center |
|
73 |
+ :description |
|
74 |
+ (list "Access Key:" |
|
75 |
+ (text-input 'update-access-key |
|
76 |
+ ()) |
|
77 |
+ |
|
78 |
+ "Secret Access Key:" |
|
79 |
+ (text-input 'update-secret-access-key |
|
80 |
+ (list :min-width '(:character 45) |
|
81 |
+ :callback-type nil |
|
82 |
+ :callback (alexandria:compose |
|
83 |
+ (dispatch-with-item *store*) |
|
84 |
+ (lambda () :|Ok|)))))) |
|
85 |
+ (row () |
|
86 |
+ nil |
|
87 |
+ (button-panel '(:default-button :|Ok| |
|
88 |
+ :cancel-button :|Cancel| |
|
89 |
+ :accepts-focus-p t) |
|
90 |
+ :|Cancel| |
|
91 |
+ :|Ok|)))) |
|
92 |
+ |
|
93 |
+ |
|
94 |
+(defclass credential-input-store (mfa-tool.store:store) |
|
95 |
+ ((access-key :accessor access-key) |
|
96 |
+ (secret-access-key :accessor secret-access-key) |
|
97 |
+ (%done-cb :writer bind :reader done-callback))) |
|
98 |
+(defun done-callback-p (store) |
|
99 |
+ (slot-boundp store '%done-cb)) |
|
100 |
+ |
|
101 |
+(fw.lu:defclass+ credential-update () |
|
102 |
+ ((access-key :reader access-key :initarg :access-key) |
|
103 |
+ (secret-access-key :accessor secret-access-key :initarg :secret-access-key))) |
|
104 |
+ |
|
105 |
+(defmethod mfa-tool.store:execute ((store credential-input-store) (action update-access-key)) |
|
106 |
+ (setf (access-key store) (key action))) |
|
107 |
+(defmethod mfa-tool.store:execute ((store credential-input-store) (action update-secret-access-key)) |
|
108 |
+ (setf (secret-access-key store) (key action))) |
|
109 |
+ |
|
110 |
+(defmethod print-object ((object credential-update) s) |
|
111 |
+ (print-unreadable-object (object s :type t :identity t) |
|
112 |
+ (format s "~s ~s" |
|
113 |
+ (access-key object) |
|
114 |
+ (secret-access-key object)))) |
|
115 |
+ |
|
116 |
+(defmethod mfa-tool.store:dispatch :after ((store credential-input-store) (action (eql :|Ok|))) |
|
117 |
+ (fw.lu:with-accessors* (access-key secret-access-key) store |
|
118 |
+ (let ((credential-update (credential-update access-key secret-access-key))) |
|
119 |
+ (mfa-tool.store:propagate store credential-update) |
|
120 |
+ (capi:exit-dialog credential-update)))) |
|
121 |
+ |
|
122 |
+(defmethod mfa-tool.store:dispatch :after ((store credential-input-store) (action (eql :|Cancel|))) |
|
123 |
+ (capi:abort-dialog)) |
|
124 |
+ |
|
125 |
+(defun prompt-for-aws-credentials (next-store) |
|
126 |
+ (let* ((*store* (make-instance 'credential-input-store :next-store next-store)) |
|
127 |
+ (interface (make-instance 'capi:interface |
|
128 |
+ :layout (layout) |
|
129 |
+ :title "Enter Credentials"))) |
|
130 |
+ (capi:display-dialog interface))) |
... | ... |
@@ -1,20 +1,33 @@ |
1 | 1 |
(defpackage :mfa-tool.store |
2 | 2 |
(:use :cl) |
3 |
- (:export #:store #:execute #:dispatch)) |
|
3 |
+ (:export #:store #:execute #:dispatch |
|
4 |
+ #:next-store |
|
5 |
+ #:next-store-p |
|
6 |
+ #:propagate)) |
|
4 | 7 |
(in-package :mfa-tool.store) |
5 | 8 |
|
6 | 9 |
(defclass store () |
7 |
- ()) |
|
10 |
+ ((%next-store :initarg :next-store :reader next-store))) |
|
11 |
+(defun next-store-p (store) |
|
12 |
+ (slot-boundp store '%next-store)) |
|
8 | 13 |
|
9 | 14 |
(defgeneric execute (store action) |
10 | 15 |
(:argument-precedence-order action store) |
11 | 16 |
(:method :around (store action) |
12 |
- (call-next-method) |
|
13 |
- store) |
|
17 |
+ (call-next-method) |
|
18 |
+ store) |
|
14 | 19 |
(:method (store action) |
15 |
- store)) |
|
20 |
+ store)) |
|
16 | 21 |
|
17 | 22 |
(defgeneric dispatch (store action) |
18 | 23 |
(:argument-precedence-order action store) |
19 | 24 |
(:method ((store store) action) |
20 |
- (execute store action))) |
|
21 | 25 |
\ No newline at end of file |
26 |
+ (execute store action)) |
|
27 |
+ (:method ((store symbol) action) |
|
28 |
+ (execute store action))) |
|
29 |
+ |
|
30 |
+(defun propagate (store action) |
|
31 |
+ (when (next-store-p store) |
|
32 |
+ (dispatch (next-store store) |
|
33 |
+ action)) |
|
34 |
+ store) |