branch: externals/url-http-oauth
commit ee73bb045021eeef119ed638f61226fdcd011ed1
Author: Thomas Fitzsimmons <fitz...@fitzsim.org>
Commit: Thomas Fitzsimmons <fitz...@fitzsim.org>

    Make functions private, fix some bugs
    
    * url-http-oauth.el (url-http-oauth--url-string)
    (url-http-oauth--url-object, url-http-oauth--url-no-query)
    (url-http-oauth--settings, url-http-oauth--update-regexp)
    (url-http-oauth--port, url-http-oauth--auth-info-password)
    (url-http-oauth--json-parse-buffer)
    (url-http-oauth--auth-source-search)
    (url-http-oauth--parse-grant)
    (url-http-oauth--get-access-token-grant)
    (url-http-oauth--expiry-string)
    (url-http-oauth--refresh-token-string)
    (url-http-oauth--extract-authorization-code)
    (url-http-oauth--authorization-url)
    (url-http-oauth--url-build-refresh)
    (url-http-oauth--netrc-delete, url-http-oauth--save-bearer)
    (url-http-oauth--refresh-access-token-grant)
    (url-http-oauth--retrieve-and-save-bearer)
    (url-http-oauth--get-bearer): Rename to indicate private scope.
    (url-http-oauth--parse-grant): Dump grant buffer contents to
    messages buffer.  Fix downcase typo.  Fix error message format.
    (url-http-oauth--get-access-token-grant): Save access-token-url to
    auth-sources.  Fix access-token-object typo.
    (url-http-oauth--expiry-string): Use "expires_on" for absolute
    expiry time.
    (url-http-oauth--refresh-token-string): Fix copy-n-paste error.
    (url-http-oauth--authorization-url): Wrap a long line.
    (url-http-oauth--url-build-refresh): Wrap some long lines.
    (url-http-oauth--netrc-delete): Use pos-bol and pos-eol.
    (url-http-oauth--refresh-access-token-grant): Update docstring.
    (url-http-oauth--retrieve-and-save-bearer): Fix docstring.
    (url-http-oauth-get-bearer): Simplify resource-url handling.
---
 url-http-oauth.el | 190 ++++++++++++++++++++++++++----------------------------
 1 file changed, 93 insertions(+), 97 deletions(-)

diff --git a/url-http-oauth.el b/url-http-oauth.el
index 5d141674c2..5394846391 100644
--- a/url-http-oauth.el
+++ b/url-http-oauth.el
@@ -37,8 +37,6 @@
 (require 'url-util)
 (require 'json)
 
-;; FIXME: make functions private.
-
 (defvar url-http-oauth--interposed nil
   "A list of OAuth 2.0 settings association lists.")
 
@@ -47,20 +45,20 @@
 If a URL matches this regular expression, `url' will use this
 `url-http-oauth' to access resources at the URL via OAuth 2.0.")
 
-(defun url-http-oauth-url-string (url)
+(defun url-http-oauth--url-string (url)
   "Return URL as a string.
 URL is string or an object."
   (if (stringp url) url (url-recreate-url url)))
 
-(defun url-http-oauth-url-object (url)
+(defun url-http-oauth--url-object (url)
   "Return URL as a parsed URL object.
 URL is a string or an object."
   (if (stringp url) (url-generic-parse-url url) url))
 
-(defun url-http-oauth-url-no-query (url)
+(defun url-http-oauth--url-no-query (url)
   "Return an object representing URL with no query components.
 URL is a string or an object."
-  (let ((url (url-http-oauth-url-object url)))
+  (let ((url (url-http-oauth--url-object url)))
     (url-parse-make-urlobj
      (url-type url)
      nil nil
@@ -69,10 +67,10 @@ URL is a string or an object."
      (car (url-path-and-query url))
      nil nil t)))
 
-(defun url-http-oauth-settings (url)
+(defun url-http-oauth--settings (url)
   "Return a settings list if URL needs OAuth 2.0, nil otherwise.
 URL is an object or a string."
-  (let* ((url (url-http-oauth-url-string url)))
+  (let* ((url (url-http-oauth--url-string url)))
     (catch 'found
       (dolist (settings url-http-oauth--interposed)
         (when (or (string-prefix-p (cdr (assoc "resource-url" settings)) url)
@@ -83,7 +81,7 @@ URL is an object or a string."
                         (throw 'match t)))))
           (throw 'found settings))))))
 
-(defun url-http-oauth-update-regexp ()
+(defun url-http-oauth--update-regexp ()
   "Update `url-http-oauth--interposed-regexp'."
   (let (all-urls)
     (dolist (settings url-http-oauth--interposed)
@@ -138,7 +136,7 @@ REDIRECT_URI."
       (error "Unrecognized client-secret-method value"))
     (prog1
         (add-to-list 'url-http-oauth--interposed url-settings)
-      (url-http-oauth-update-regexp))))
+      (url-http-oauth--update-regexp))))
 
 (defun url-http-oauth-uninterpose (url-settings)
   "Arrange for Emacs not to use OAuth 2.0 when accessing URL in URL-SETTINGS.
@@ -146,33 +144,28 @@ This function does the opposite of 
`url-http-oauth-interpose'."
   (prog1
       (setq url-http-oauth--interposed
             (delete url-settings url-http-oauth--interposed))
-    (url-http-oauth-update-regexp)))
+    (url-http-oauth--update-regexp)))
 
 (defun url-http-oauth-interposed-p (url)
   "Return non-nil if `url' will use OAuth 2.0 to access URL.
 URL is an object."
   (string-match-p url-http-oauth--interposed-regexp
-                  (url-http-oauth-url-string url)))
+                  (url-http-oauth--url-string url)))
 
 (defvar url-http-response-status)
 (defvar auth-source-creation-prompts)
 
-;; FIXME: if anything goes wrong during the authentication steps,
-;; `url-http-end-of-document-sentinel' calls back into
-;; `url-oauth-auth' somehow.  Maybe `url-http-no-retry' can help here?
-(defvar url-http-no-retry)
-
-(defun url-http-oauth-port (url)
+(defun url-http-oauth--port (url)
   "Return port of URL.
 Assume an HTTPS URL that does not specify a port uses 443.  URL
 is a string or an object."
-  (let ((port-number (url-port (url-http-oauth-url-object url))))
+  (let ((port-number (url-port (url-http-oauth--url-object url))))
     (if port-number
         (number-to-string port-number)
       (when (string= "https" (url-type url)) "443"))))
 
 ;; Backport of `auth-info-password'.
-(defun url-http-oauth-auth-info-password (auth-info)
+(defun url-http-oauth--auth-info-password (auth-info)
   "Return the :secret password from the AUTH-INFO."
   (let ((secret (plist-get auth-info :secret)))
     (if (functionp secret)
@@ -180,14 +173,14 @@ is a string or an object."
       secret)))
 
 ;; Backport (roughly) of `json-parse-buffer'.
-(defun url-http-oauth-json-parse-buffer ()
+(defun url-http-oauth--json-parse-buffer ()
   "See `json-parse-buffer'."
   (let ((json-object-type 'hash-table))
     (json-read-from-string
      (buffer-substring (point) (point-max)))))
 
-(defun url-http-oauth-auth-source-search (url &optional user secret prompt
-                                              expiry refresh-token)
+(defun url-http-oauth--auth-source-search (url &optional user secret prompt
+                                               expiry refresh-token)
   "Find the `auth-source' entry for USER and URL.
 Arrange for the entry to be created if it is not already saved in
 on of `auth-sources'.  URL is a string or an object.  USER is a
@@ -215,9 +208,9 @@ server to receive a new access token."
                        ;; arbitrary fields would have too many forward
                        ;; and backward compatibility implications for
                        ;; netrc-formatted authinfo files.
-                       :host ,(url-http-oauth-url-string
-                               (url-http-oauth-url-no-query url))
-                       :port ,(url-http-oauth-port url)
+                       :host ,(url-http-oauth--url-string
+                               (url-http-oauth--url-no-query url))
+                       :port ,(url-http-oauth--port url)
                        ,@(when secret (list :secret secret))
                        ,@(when expiry (list :expiry expiry))
                        ,@(when refresh-token
@@ -227,27 +220,24 @@ server to receive a new access token."
      (let ((auth-source-do-cache nil)) ; Do not cache nil result.
        (apply #'auth-source-search spec)))))
 
-;; This monstrosity is required because the `auth-source' netrc
-;; backend does not support deletion, yet we need to refresh the
-;; bearer token.
-
 (defun url-http-oauth--parse-grant ()
   "Parse the JSON grant structure in the current buffer.
 Return the parsed JSON object."
+  (message "url-http-oauth grant: %s" (buffer-string))
   (progn
     (goto-char (point-min))
     (re-search-forward "\n\n")
-    (let* ((grant (url-http-oauth-json-parse-buffer))
+    (let* ((grant (url-http-oauth--json-parse-buffer))
            (type (gethash "token_type" grant)))
-      (unless (equal (dowcase type) "bearer" )
-        (error "Unrecognized token type %s for %s" type url-settings))
+      (unless (equal (downcase type) "bearer")
+        (error "Unrecognized token type %s" type))
       ;; Return grant object.
       grant)))
 
-(defun url-http-oauth-get-access-token-grant (url-settings code)
+(defun url-http-oauth--get-access-token-grant (url-settings code)
   "Get an access token for using CODE.
-URL-SETTINGS are OAuth 2.0 settings needed by URL."
-  ;; (message "url-http-oauth-get-access-token-grant: %S, %S" url-settings 
code)
+URL-SETTINGS contain the client identifier and access token
+endpoint."
   (let* ((url-request-method "POST")
          (access-token-url (cdr (assoc "access-token-endpoint" url-settings)))
          (client-identifier (cdr (assoc "client-identifier" url-settings)))
@@ -255,9 +245,10 @@ URL-SETTINGS are OAuth 2.0 settings needed by URL."
                                            url-settings)))
          (auth-result
           (when client-secret-method
-            (url-http-oauth-auth-source-search
-             url client-identifier "Client secret for %u at %h: ")))
-         (client-secret (url-http-oauth-auth-info-password auth-result))
+            (url-http-oauth--auth-source-search
+             access-token-url client-identifier
+             "Client secret for %u at %h: ")))
+         (client-secret (url-http-oauth--auth-info-password auth-result))
          (save-function (plist-get auth-result :save-function))
          (authorization (when client-secret
                           (concat
@@ -275,33 +266,33 @@ URL-SETTINGS are OAuth 2.0 settings needed by URL."
          (url-request-data
           (url-build-query-string
            (apply #'list (list "code" code)
-                 (list "client_id" client-identifier)
-                 (list "grant_type" "authorization_code")
-                 (when redirect-uri
-                   (list (list "redirect_uri" redirect-uri)))))))
-    ;; (message "URL: %S\nAUTH: %S\nDAT: %S" url-settings authorization 
url-request-data)
-    (with-current-buffer (url-retrieve-synchronously access-token-object)
-      ;; (message "GRANT BUFFER: %S" (buffer-string))
+                  (list "client_id" client-identifier)
+                  (list "grant_type" "authorization_code")
+                  (when redirect-uri
+                    (list (list "redirect_uri" redirect-uri)))))))
+    (with-current-buffer (url-retrieve-synchronously access-token-url)
       (if (eq 'OK (car (alist-get url-http-response-status url-http-codes)))
           (prog1
               (url-http-oauth--parse-grant)
             ;; Success, so save client secret, if necessary.
             (when (functionp save-function)
-              (funcall save-function))
+              (funcall save-function)))
         (error "url-http-oauth: Failed to get access token with %s"
                (buffer-string))))))
 
-(defun url-http-oauth-expiry-string (grant)
+(defun url-http-oauth--expiry-string (grant)
   "Return as a string a number representing the expiry time of GRANT.
 The time is in seconds since the epoch."
-  (format-time-string "%s" (time-add nil (gethash "expires_in" grant))))
+  (let ((expiry (gethash "expires_on" grant)))
+    (unless expiry (error "Did not find expiry time in grant"))
+    expiry))
 
-(defun url-http-oauth-refresh-token-string (grant)
+(defun url-http-oauth--refresh-token-string (grant)
   "Return the refresh token from GRANT.
 The refresh token is an opaque string."
-  (format-time-string "%s" (time-add nil (gethash "refresh_token" grant))))
+  (gethash "refresh_token" grant))
 
-(defun url-http-oauth-extract-authorization-code (url)
+(defun url-http-oauth--extract-authorization-code (url)
   "Extract the value of the code parameter in URL."
   (let ((query (cdr (url-path-and-query (url-generic-parse-url url)))))
     (unless query
@@ -311,7 +302,7 @@ The refresh token is an opaque string."
         (error "url-http-oauth: Failed to find code in query component"))
       code)))
 
-(defun url-http-oauth-authorization-url (url-settings)
+(defun url-http-oauth--authorization-url (url-settings)
   "Return the authorization URL for URL-SETTINGS."
   (let ((base (cdr (assoc "authorization-endpoint" url-settings)))
         (client
@@ -320,23 +311,26 @@ The refresh token is an opaque string."
         (scope (list "scope" (cdr (assoc "scope" url-settings))))
         (extra (mapcar (lambda (entry)
                          (list (car entry) (cdr entry)))
-                (cdr (assoc "authorization-extra-arguments" url-settings)))))
+                       (cdr (assoc "authorization-extra-arguments"
+                                   url-settings)))))
     (concat base "?" (url-build-query-string
                       (apply #'list client response-type scope extra)))))
 
-(defun url-http-oauth-url-build-refresh (url-settings)
+(defun url-http-oauth--url-build-refresh (url-settings)
   "Build a refresh token URL query string from URL-SETTINGS."
   (let* ((client-identifier (cdr (assoc "client-identifier" url-settings)))
-         (authorization-extra-arguments (cdr (assoc 
"authorization-extra-arguments" url-settings)))
+         (authorization-extra-arguments
+          (cdr (assoc "authorization-extra-arguments" url-settings)))
          (resource (cdr (assoc "resource" authorization-extra-arguments)))
-         (redirect-uri (cdr (assoc "redirect_uri" 
authorization-extra-arguments))))
+         (redirect-uri
+          (cdr (assoc "redirect_uri" authorization-extra-arguments))))
     (url-build-query-string
      (apply #'list
             (let ((resource-url
                    (cdr (assoc "resource-url" url-settings))))
               (list "refresh_token"
                     (or (plist-get
-                         (url-http-oauth-auth-source-search resource-url)
+                         (url-http-oauth--auth-source-search resource-url)
                          :refresh-token)
                         (error "Failed to retrieve refresh token for %s"
                                resource-url))))
@@ -346,6 +340,9 @@ The refresh token is an opaque string."
             (when redirect-uri
               (list (list "redirect_uri" redirect-uri)))))))
 
+;; This monstrosity is required because the `auth-source' netrc
+;; backend does not support deletion, yet we need to refresh the
+;; bearer token.
 (defun url-http-oauth--netrc-delete (host &optional user port)
   "Delete a netrc entry matching HOST, USER and PORT.
 Delete the first matching line from any `auth-source' backend.
@@ -378,7 +375,6 @@ The entry is cleared from the `password-data' cache after 
the
                   (setq epa-file-encrypt-to auth-source-gpg-encrypt-to)))
             ;; we want the new data to be found first, so insert at beginning
             (goto-char (point-min))
-
             ;; Ask AFTER we've successfully opened the file.
             (let* ((allow-null t)
                    (start-point (point-min))
@@ -422,17 +418,17 @@ The entry is cleared from the `password-data' cache after 
the
                 (message "prior start point: %s" prior-start-point)
                 (goto-char prior-start-point)
                 (auth-source-netrc-parse-next-interesting)
-                (goto-char (point-at-bol))
+                (goto-char (pos-bol))
                 (let ((extents
                        (if (bobp)
                            (progn
-                             (goto-char (point-at-eol))
+                             (goto-char (pos-eol))
                              (if (eobp)
-                                 (cons (point-at-bol) (point-at-eol))
-                               (cons (point-at-bol) (1+ (point-at-eol)))))
+                                 (cons (pos-bol) (pos-eol))
+                               (cons (pos-bol) (1+ (pos-eol)))))
                          (progn
-                           (goto-char (point-at-eol))
-                           (cons (1- (point-at-bol)) (point-at-eol))))))
+                           (goto-char (pos-eol))
+                           (cons (1- (pos-bol)) (pos-eol))))))
                   (let ((region-to-delete (buffer-substring (car extents)
                                                             (cdr extents))))
                     (when (or (not (eq auth-source-save-behavior 'ask))
@@ -440,8 +436,8 @@ The entry is cleared from the `password-data' cache after 
the
                                                 region-to-delete)))
                       (delete-region (car extents) (cdr extents))
                       (write-region (point-min) (point-max) file nil 'silent)
-                     ;; Make the .authinfo file non-world-readable.
-                     (set-file-modes file #o600)
+                      ;; Make the .authinfo file non-world-readable.
+                      (set-file-modes file #o600)
                       (auth-source-do-debug
                        "auth-source-netrc-create: deleted region %S from %s"
                        region-to-delete file)
@@ -450,17 +446,17 @@ The entry is cleared from the `password-data' cache after 
the
                                                  :port (or port t)))
                       nil)))))))))))
 
-(defun url-http-oauth-save-bearer (url grant)
+(defun url-http-oauth--save-bearer (url grant)
   "Save bearer access token for URL from GRANT.
 URL is a string or an object.  GRANT is a parsed JSON object.
 Save the bearer token to `auth-sources' then return it."
   (url-http-oauth--netrc-delete url)
   (let* ((bearer-retrieved (gethash "access_token" grant))
          (auth-result
-          (url-http-oauth-auth-source-search
+          (url-http-oauth--auth-source-search
            url nil bearer-retrieved nil
-           (url-http-oauth-expiry-string grant)
-           (url-http-oauth-refresh-token-string grant)))
+           (url-http-oauth--expiry-string grant)
+           (url-http-oauth--refresh-token-string grant)))
          (save-function (plist-get auth-result :save-function)))
     (when (functionp save-function)
       (funcall save-function))
@@ -471,53 +467,53 @@ Save the bearer token to `auth-sources' then return it."
 ;; client_id="00000000-0000-0000-0000-000000000000",
 ;; trusted_issuers="00000000-0000-0000-0000-000000000000@*",
 ;; token_types="app_asserted_user_v1 service_asserted_app_v1",
-(defun url-http-oauth-refresh-access-token-grant (url-settings)
-  "Refresh the access token for URL."
 ;; authorization_uri=
 ;; "https://login.microsoftonline.com/common/oauth2/authorize";,
 ;; error="invalid_token",Basic Realm="" in which case, call refresh on
 ;; URL before proceeding.
+(defun url-http-oauth--refresh-access-token-grant (url-settings)
+  "Refresh access token using URL-SETTINGS."
   (let* ((url-request-method "POST")
          (access-token-url (cdr (assoc "access-token-endpoint" url-settings)))
-         (url-request-data (url-http-oauth-url-build-refresh url-settings)))
-    ;; (message "URL: %S\nREQ: %S" url url-request-data)
+         (url-request-data (url-http-oauth--url-build-refresh url-settings)))
     (with-current-buffer (url-retrieve-synchronously access-token-url)
       (if (eq 'OK (car (alist-get url-http-response-status url-http-codes)))
           (url-http-oauth--parse-grant)
         (error "url-http-oauth: Failed to get access token with %s"
                (buffer-string))))))
 
-(defun url-http-oauth-retrieve-and-save-bearer (url url-settings)
-  "Retrieve the bearer token required to access resources needing URL-SETTINGS.
+(defun url-http-oauth--retrieve-and-save-bearer (url url-settings)
+  "Retrieve the bearer token required for URL, using URL-SETTINGS.
 Save the bearer token to `auth-sources' upon success."
   (let* ((response-url
           ;; FIXME: Make this a per-provider function.
           (read-from-minibuffer
            (format "Browse to %s and paste the redirected code URL: "
-                   (url-http-oauth-authorization-url url-settings))))
+                   (url-http-oauth--authorization-url url-settings))))
          (code
-          (url-http-oauth-extract-authorization-code response-url))
-         (grant (url-http-oauth-get-access-token-grant url-settings code)))
-    (url-http-oauth-save-bearer url grant)))
+          (url-http-oauth--extract-authorization-code response-url))
+         (grant (url-http-oauth--get-access-token-grant url-settings code)))
+    (url-http-oauth--save-bearer url grant)))
 
-(defun url-http-oauth-get-bearer (url)
+;; FIXME: if anything goes wrong during the authentication steps,
+;; `url-http-end-of-document-sentinel' calls back into
+;; `url-oauth-auth' somehow.  Maybe `url-http-no-retry' can help here?
+(defun url-http-oauth--get-bearer (url)
   "Prompt the user with the authorization endpoint for URL.
 URL is a parsed object."
-  (let* ((url-settings (url-http-oauth-settings url))
-         (url (url-http-oauth-url-object url))
-         (path-and-query (url-path-and-query url))
-         (path (car path-and-query)))
-    (unless url-settings
-      (error "%s is not interposed by url-http-oauth"
-             (url-http-oauth-url-string url)))
-    (let ((expiry (plist-get (url-http-oauth-auth-source-search url) :expiry)))
-      (when (and expiry (> (time-to-seconds) expiry))
-        (url-http-oauth-save-bearer
-         url (url-http-oauth-refresh-access-token-grant url-settings))))
-    (let ((bearer-current (url-http-oauth-auth-info-password
-                           (url-http-oauth-auth-source-search url))))
+  (let* ((url-settings (url-http-oauth--settings url))
+         (resource-url (cdr (assoc "resource-url" url-settings))))
+    (let ((expiry (plist-get (url-http-oauth--auth-source-search resource-url)
+                             :expiry)))
+      (when (and expiry (> (time-to-seconds) (string-to-number expiry)))
+        (url-http-oauth--save-bearer
+         resource-url
+         (url-http-oauth--refresh-access-token-grant url-settings))))
+    (let ((bearer-current (url-http-oauth--auth-info-password
+                           (url-http-oauth--auth-source-search resource-url))))
       (or bearer-current
-          (url-http-oauth-retrieve-and-save-bearer url-settings)))))
+          (url-http-oauth--retrieve-and-save-bearer resource-url
+                                                    url-settings)))))
 
 ;;; Public function called by `url-get-authentication'.
 ;;;###autoload
@@ -527,7 +523,7 @@ URL is an object representing a parsed URL.  It should 
specify a
 user, and contain a \"scope\" query argument representing the
 permissions that the caller is requesting."
   (when (url-http-oauth-interposed-p url)
-    (let ((bearer (url-http-oauth-get-bearer url)))
+    (let ((bearer (url-http-oauth--get-bearer url)))
       (if bearer
           (concat "Bearer " bearer)
         (error "Bearer retrieval failed for %s" url)))))

Reply via email to