git.fiddlerwoaroof.com
Browse code

feature: add error handling for invalid tokens

Edward Langley authored on 30/09/2019 07:36:04
Showing 3 changed files
... ...
@@ -14,7 +14,9 @@
14 14
                  :serapeum
15 15
                  :ubiquitous
16 16
                  :uiop
17
-                 :yason)
17
+                 :yason
18
+                 :cxml
19
+                 :xpath)
18 20
     :serial t
19 21
     :components ((:module "src"
20 22
                   :serial t
... ...
@@ -7,21 +7,41 @@
7 7
 
8 8
 (defparameter *user_management_account_id* 597974043991)
9 9
 
10
+(defun mfa-serial-number (account user)
11
+  (format nil "arn:aws:iam::~a:mfa/~a"
12
+          account
13
+          user))
14
+
15
+(defun role-arn (account role)
16
+  (format nil "arn:aws:iam::~a:role/cjorganization/~a"
17
+          account
18
+          role))
19
+
20
+(defun read-new-mfa-token ()
21
+  (format *query-io* "~&New MFA token: ")
22
+  (finish-output *query-io*)
23
+  (list (read-line *query-io*)))
10 24
 
11 25
 (defun do-auth (user role token account)
12 26
   (let ((mfa-serial-number
13
-          (format nil "arn:aws:iam::~a:mfa/~a"
14
-                  *user_management_account_id*
15
-                  user))
16
-        (role-arn
17
-          (format nil "arn:aws:iam::~a:role/cjorganization/~a"
18
-                  account
19
-                  role)))
20
-    (aws/sts:assume-role :role-arn role-arn
21
-                         :role-session-name (session-name)
22
-                         :serial-number mfa-serial-number
23
-                         :duration-seconds #.(* 12 60 60)
24
-                         :token-code token)))
27
+          (mfa-serial-number *user_management_account_id*
28
+                             user))
29
+        (role-arn (role-arn account role)))
30
+    (loop
31
+      (restart-case
32
+          (return
33
+            (aws/sts:assume-role :role-arn role-arn
34
+                                 :role-session-name (session-name)
35
+                                 :serial-number mfa-serial-number
36
+                                 :duration-seconds #.(* 12 60 60)
37
+                                 :token-code token))
38
+        (change-mfa-token (new-token)
39
+          :interactive read-new-mfa-token
40
+          (setf token new-token))))))
41
+
42
+(defun change-mfa-token (new-value)
43
+  (when (find-restart 'change-mfa-token)
44
+    (invoke-restart 'change-mfa-token new-value)))
25 45
 
26 46
 (defun get-url (params)
27 47
   (format nil "https://signin.aws.amazon.com/federation?Action=getSigninToken&Session=~a"
... ...
@@ -71,3 +91,17 @@
71 91
                                :url url)
72 92
                 :best-width 1280
73 93
                 :best-height 800))
94
+
95
+(defun sts-error-value (sts-response)
96
+  (let ((parsed-error (dom:first-child
97
+                       (cxml:parse sts-response
98
+                                   (cxml-dom:make-dom-builder)))))
99
+    (flet ((get-node (path)
100
+             (dom:node-value
101
+              (dom:first-child
102
+               (xpath:first-node
103
+                (xpath:with-namespaces (("" "https://sts.amazonaws.com/doc/2011-06-15/"))
104
+                  (xpath:evaluate path parsed-error)))))))
105
+      (values (get-node "//Error/Type")
106
+              (get-node "//Error/Code")
107
+              (get-node "//Error/Message")))))
... ...
@@ -27,7 +27,23 @@
27 27
         (user-name (capi:text-input-pane-text (user-input interface)))
28 28
         (account (cdr (capi:choice-selected-item (account-selector interface)))))
29 29
     (clear-cookies)
30
-    (multiple-value-bind (signin-token creds) (run-process account user-name token)
30
+    (multiple-value-bind (signin-token creds)
31
+        (handler-bind (((or dexador:http-request-forbidden
32
+                            dexador:http-request-bad-request)
33
+                         (lambda (c)
34
+                           (let ((message (nth-value 2 (sts-error-value (dex:response-body c)))))
35
+                             (multiple-value-bind (new-code completed)
36
+                                 (capi:prompt-for-string (format nil "~a~%Enter new MFA token:"
37
+                                                                 message)
38
+                                                         :ok-check (lambda (v)
39
+                                                                     (and (every 'digit-char-p v)
40
+                                                                          (= (length v) 6))))
41
+                               (if completed
42
+                                   (progn (setf (capi:text-input-pane-text (mfa-input interface))
43
+                                                new-code)
44
+                                          (change-mfa-token new-code))
45
+                                   (capi:abort-callback)))))))
46
+          (run-process account user-name token))
31 47
       (with-open-file (stream (make-pathname :name ""
32 48
                                              :type "cj-aws"
33 49
                                              :defaults (user-homedir-pathname))
... ...
@@ -59,12 +75,14 @@
59 75
            (capi:convert-to-screen))))
60 76
     (typep active-interface '(not mfa-tool))))
61 77
 
62
-(defun load-accounts ()
78
+(defun load-accounts (&optional account-source)
63 79
   (yason:parse
64 80
    (alexandria:read-file-into-string
65
-    (merge-pathnames (make-pathname :name "accounts"
66
-                                    :type "json")
67
-                     (bundle-resource-root)))))
81
+    (if account-source
82
+        account-source
83
+        (merge-pathnames (make-pathname :name "accounts"
84
+                                        :type "json")
85
+                         (bundle-resource-root))))))
68 86
 
69 87
 (defun reprocess-accounts (accounts)
70 88
   (let ((accounts (gethash "Accounts" accounts))