git.fiddlerwoaroof.com
Browse code

feature: dialog for entering aws credentials

Edward Langley authored on 16/10/2019 18:01:33
Showing 7 changed files
... ...
@@ -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)