git.fiddlerwoaroof.com
Raw Blame History
(in-package :mfa-tool)

(defun account-selected (account)
  (setf (ubiquitous:value :default-account)
        (cdr account)))

(defun region-selected (region)
  (setf (ubiquitous:value :default-region)
        region))

(defparameter *developer-p* (equal "elangley" (uiop/os:getenv "USER")))

(defgeneric assumed-credentials (store))
(defgeneric (setf assumed-credentials) (value store))

(defun current-account (interface)
  (cdr (capi:choice-selected-item (account-selector interface))))
(defun current-role (interface)
  (capi:choice-selected-item (role-selector interface)))

(defun credentials-for-account (interface account)
  (gethash account
           (assumed-credentials interface)))
(defun (setf credentials-for-account) (new-credentials interface account)
  (setf (gethash account
                 (assumed-credentials interface))
        new-credentials))

(defun current-credentials (interface)
  (credentials-for-account interface
                           (current-account interface)))


(defmethod mfa-tool.store:execute ((store (eql :mfa-tool.debugger))
                                   (action mfa-tool.read-credentials:credential-update))
  (with-accessors ((access-key mfa-tool.read-credentials:access-key)
                   (secret-access-key mfa-tool.read-credentials:secret-access-key)) action
    (mfa-tool.credential-provider:save-ubiquitous-credentials
     (aws-sdk:make-credentials :access-key-id access-key
                               :secret-access-key secret-access-key
                               :session-token nil))))

(defun authenticate (user-name role token)
  (handler-bind ((aws-sdk:no-credentials
                   (lambda (c)
                     (alexandria:when-let
                         ((result (mfa-tool.read-credentials:prompt-for-aws-credentials :mfa-tool.debugger)))
                       (set-aws-credentials (mfa-tool.read-credentials:access-key result)
                                            (mfa-tool.read-credentials:secret-access-key result)
                                            c)))))
    (run-process user-name
                 role
                 token)))

(defun go-on (_ interface)
  (declare (ignore _))
  (let ((token (capi:text-input-pane-text (mfa-input interface)))
        (user-name (capi:text-input-pane-text (user-input interface)))
        (account (current-account interface)))
    (clear-cookies)
    (multiple-value-bind (signin-token creds)
        (handler-bind (((or dexador:http-request-forbidden
                            dexador:http-request-bad-request)
                         (lambda (c)
                           (let ((message (nth-value 2 (sts-error-value (dex:response-body c)))))
                             (multiple-value-bind (new-code completed)
                                 (capi:prompt-for-string (format nil "~a~%Enter new MFA token:"
                                                                 message)
                                                         :ok-check (lambda (v)
                                                                     (and (every 'digit-char-p v)
                                                                          (= (length v) 6))))
                               (if completed
                                   (progn (setf (capi:text-input-pane-text (mfa-input interface))
                                                new-code)
                                          (change-mfa-token new-code))
                                   (capi:abort-callback)))))))
          (authenticate user-name
                        (ecase (current-role interface)
                          (:|Developer Role| (cj-developer-role account))
                          (:|Provisioner Role| (cj-provisioner-role account)))
                        token))
      (with-open-file (stream (make-pathname :name ""
                                             :type "cj-aws"
                                             :defaults (user-homedir-pathname))
                              :direction :output
                              :if-exists :rename
                              :if-does-not-exist :create)
        (let ((cred-stream (make-broadcast-stream stream
                                                  (capi:collector-pane-stream (output interface)))))
          (format cred-stream
                  "export AWS_ACCESS_KEY_ID='~a'~%export AWS_SECRET_ACCESS_KEY='~a'~%export AWS_SESSION_TOKEN='~a'~%"
                  (session-id creds)
                  (session-key creds)
                  (session-token creds))))
      (capi:set-button-panel-enabled-items (slot-value interface 'action-buttons)
                                           :set t)
      (setf (capi:button-enabled (slot-value interface 'open-console-button))
            t)
      (setf (credentials-for-account interface account) (session-credentials creds)
            (signin-url interface) (url-from-signin-token signin-token)))))

(defun close-active-screen ()
  (let ((active-interface
          (capi:screen-active-interface
           (capi:convert-to-screen))))
    (unless (typep active-interface 'mfa-tool)
      (capi:destroy active-interface))))


(defun close-active-screen-enabled ()
  (let ((active-interface
          (capi:screen-active-interface
           (capi:convert-to-screen))))
    (typep active-interface '(not mfa-tool))))

(defun load-accounts (&optional account-source)
  (yason:parse
   (alexandria:read-file-into-string
    (if account-source
        account-source
        (json-resource "accounts")))))

(defun reprocess-accounts (accounts)
  (let ((accounts (gethash "Accounts" accounts))
        (result ()))
    (mapc (lambda (account)
            (push (cons (format nil "~a: ~a (~a)"
                                (gethash "Name" account)
                                (gethash "Id" account)
                                (gethash "Type" account))
                        (gethash "Id" account))
                  result))
          accounts)
    (coerce (sort result 'string-lessp
                  :key 'car)
            'list)))