[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/url-http-oauth fcb5929654 16/24: Fix auth-source lookup
|
From: |
Thomas Fitzsimmons |
|
Subject: |
[elpa] externals/url-http-oauth fcb5929654 16/24: Fix auth-source lookup conflicts and config cdrs |
|
Date: |
Mon, 8 May 2023 21:10:46 -0400 (EDT) |
branch: externals/url-http-oauth
commit fcb592965458196b4e0982b0c3fc3fd6a4bbc16e
Author: Thomas Fitzsimmons <fitzsim@fitzsim.org>
Commit: Thomas Fitzsimmons <fitzsim@fitzsim.org>
Fix auth-source lookup conflicts and config cdrs
* url-http-oauth.el (url-http-oauth-interpose): Change url to
urls. Assume conses, not lists in url-settings.
(url-http-oauth-uninterpose): Likewise.
(url-http-no-retry): Define special variable.
(url-http-oauth-auth-source-search): Do not warn on zero results.
(url-http-oauth-encode-scope): New function.
(url-http-oauth-auth-info-password): Likewise.
(url-http-oauth-json-parse-buffer): Likewise.
(url-http-oauth-get-access-token-grant): Assume conses, not lists
in url-settings. Do not set auth-source-do-cache to nil.
Simplify url-http-oauth-auth-source-search call. Encode scope
with no spaces for authinfo storage. Use
url-http-oauth-json-parse-buffer instead of json-parse-buffer
(url-http-oauth-authorization-url): Assume conses, not lists in
url-settings. Apply list instead of using macro for
url-build-query-string argument.
(url-http-oauth-get-bearer): Use dummy "BEARER" user name.
Simplify call to auth-source-search.
---
url-http-oauth.el | 135 +++++++++++++++++++++++++++++++++---------------------
1 file changed, 83 insertions(+), 52 deletions(-)
diff --git a/url-http-oauth.el b/url-http-oauth.el
index 40e7b4e72f..1591a0eaa6 100644
--- a/url-http-oauth.el
+++ b/url-http-oauth.el
@@ -35,6 +35,7 @@
(require 'url-auth)
(require 'url-http)
(require 'url-util)
+(require 'json)
(defvar url-http-oauth--interposed nil
"A hash table mapping URL strings to lists of OAuth 2.0 settings.")
@@ -80,24 +81,30 @@ CLIENT-SECRET-METHOD is the symbol `prompt' if a client
secret is
required, nil otherwise."
(unless url-http-oauth--interposed
(setq url-http-oauth--interposed (make-hash-table :test #'equal)))
- (let* ((url (cadr (assoc "url" url-settings)))
- (key (url-http-oauth-url-string url))
+ (let* ((urls (cdr (assoc "urls" url-settings)))
(client-secret-method
- (cadr (assoc "client-secret-method" url-settings))))
+ (cdr (assoc "client-secret-method" url-settings))))
(unless (or (eq client-secret-method 'prompt) (eq client-secret-method
nil))
(error "Unrecognized client-secret-method value"))
- (puthash key url-settings url-http-oauth--interposed)))
+ (dolist (url urls)
+ (puthash (url-http-oauth-url-string url) url-settings
+ url-http-oauth--interposed))))
(defun url-http-oauth-uninterpose (url-settings)
"Arrange for Emacs not to use OAuth 2.0 when accessing URL in URL-SETTINGS.
This function does the opposite of `url-http-oauth-interpose'."
(when url-http-oauth--interposed
- (let* ((url (cadr (assoc "url" url-settings)))
- (key (url-http-oauth-url-string url)))
- (remhash key url-http-oauth--interposed))))
+ (let* ((urls (cdr (assoc "urls" url-settings))))
+ (dolist (url urls)
+ (remhash (url-http-oauth-url-string url)
+ url-http-oauth--interposed)))))
(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)
"Return port of URL object.
@@ -110,8 +117,8 @@ Assume an HTTPS URL that does not specify a port uses 443."
(defun url-http-oauth-auth-source-search (&rest spec)
"Like `auth-source-search' but search for all of SPEC in all backends.
Filter out nil spec entries prior to searching."
- (let* ((auth-source-do-cache nil)
- (all (apply #'auth-source-search :max 5001 spec)) ; no :max 'all
+ (let* ((auth-source-do-cache nil) ; do not cache nil result.
+ (all (apply #'auth-source-search :max 5001 spec)) ; hmm, no :max 'all.
(spec (cl-loop for i below (length spec) by 2
unless (null (nth (1+ i) spec))
collect (nth i spec)
@@ -120,40 +127,56 @@ Filter out nil spec entries prior to searching."
(result (cl-loop for entry in all
when (auth-source-specmatchp spec entry)
collect entry)))
- (unless (eq (length result) 1)
+ (unless (or (eq (length result) 0)
+ (eq (length result) 1))
(warn "url-http-oauth-auth-source-search produced multiple results for
%s"
spec))
result))
+(defun url-http-oauth-encode-scope (scope)
+ "Replace spaces in SCOPE with plus signs."
+ (replace-regexp-in-string " " "+" scope))
+
+;; Backport of `auth-info-password'.
+(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)
+ (funcall secret)
+ secret)))
+
+;; Backport (roughly) of `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-get-access-token-grant (url code)
"Get an access token for URL using CODE."
(let* ((url-request-method "POST")
(url-settings (url-http-oauth-settings url))
(access-token-object
(url-http-oauth-url-object
- (cadr (assoc "access-token-endpoint" url-settings))))
- (client-identifier (cadr (assoc "client-identifier" url-settings)))
- (scope (cadr (assoc "scope" url-settings)))
- (client-secret-method (cadr (assoc "client-secret-method"
- url-settings)))
+ (cdr (assoc "access-token-endpoint" url-settings))))
+ (client-identifier (cdr (assoc "client-identifier" url-settings)))
+ (scope (cdr (assoc "scope" url-settings)))
+ (client-secret-method (cdr (assoc "client-secret-method"
+ url-settings)))
(auth-result
(when client-secret-method
(car (let* ((auth-source-creation-prompts
- '((secret . "Client secret for %u at %h")))
- ;; Do not cache nil result.
- (auth-source-do-cache nil)
+ '((secret . "Client secret for %u at %h: ")))
(spec (list :user client-identifier
:host (url-host access-token-object)
:port (url-http-oauth-port
access-token-object)
:path (url-filename access-token-object)
- :scope scope))
- (existing-entry
- (apply #'url-http-oauth-auth-source-search spec)))
- (or existing-entry
- (apply #'auth-source-search
- :create '(path scope) spec))))))
- (client-secret (auth-info-password auth-result))
+ :scope
+ (url-http-oauth-encode-scope scope))))
+ (or (apply #'url-http-oauth-auth-source-search spec)
+ (apply #'auth-source-search :create '(path scope)
spec))))))
+ (client-secret (url-http-oauth-auth-info-password auth-result))
(save-function (plist-get auth-result :save-function))
(authorization (when client-secret
(concat
@@ -176,7 +199,7 @@ Filter out nil spec entries prior to searching."
(progn
(goto-char (point-min))
(re-search-forward "\n\n")
- (let* ((grant (json-parse-buffer))
+ (let* ((grant (url-http-oauth-json-parse-buffer))
(type (gethash "token_type" grant)))
(unless (equal type "bearer" )
(error "Unrecognized token type %s for %s at %s" type
@@ -206,14 +229,16 @@ The time is in seconds since the epoch."
(defun url-http-oauth-authorization-url (url-settings)
"Return the authorization URL for URL-SETTINGS."
- (let ((base (cadr (assoc "authorization-endpoint" url-settings)))
+ (let ((base (cdr (assoc "authorization-endpoint" url-settings)))
(client
- (list "client_id" (cadr (assoc "client-identifier" url-settings))))
+ (list "client_id" (cdr (assoc "client-identifier" url-settings))))
(response-type (list "response_type" "code"))
- (scope (assoc "scope" url-settings))
- (extra (cadr (assoc "authorization-extra-arguments" url-settings))))
+ (scope (list "scope" (cdr (assoc "scope" url-settings))))
+ (extra (mapcar (lambda (entry)
+ (list (car entry) (cdr entry)))
+ (cdr (assoc "authorization-extra-arguments" url-settings)))))
(concat base "?" (url-build-query-string
- `(,client ,response-type ,scope ,@extra)))))
+ (apply #'list client response-type scope extra)))))
(defun url-http-oauth-get-bearer (url)
"Prompt the user with the authorization endpoint for URL.
@@ -222,12 +247,12 @@ URL is a parsed object."
(url-settings (url-http-oauth-settings url))
(path-and-query (url-path-and-query url))
(path (car path-and-query))
- (scope (cadr (assoc "scope" url-settings)))
- (bearer-current (auth-info-password
+ (scope (cdr (assoc "scope" url-settings)))
+ (bearer-current (url-http-oauth-auth-info-password
(car
(let ((auth-source-do-cache nil))
(url-http-oauth-auth-source-search
- :user (url-user url)
+ :user "BEARER"
:host (url-host url)
:port (url-http-oauth-port url)
:path path
@@ -244,24 +269,30 @@ URL is a parsed object."
(url-http-oauth-extract-authorization-code response-url))
(grant (url-http-oauth-get-access-token-grant url code))
(bearer-retrieved (gethash "access_token" grant))
- (spec (list :user (or (url-user url) "")
- :host (url-host url)
- :port (url-http-oauth-port url)
- :path path
- :scope (if (string= (gethash "scope" grant)
- scope)
- scope
- (error
- (concat "url-http-oauth:"
- " Returned scope did not"
- " match requested scope")))
- :expiry (url-http-oauth-expiry-string grant)
- :secret bearer-retrieved))
- (auth-result
- (unless (apply #'url-http-oauth-auth-source-search spec)
- (let ((auth-source-do-cache nil))
- (apply #'auth-source-search
- :create '(path scope expiry) spec))))
+ (auth-result (auth-source-search
+ :create '(path scope expiry)
+ ;; If :user is nil, then
+ ;; (auth-source-search :create ...) will
+ ;; find the client-identifier username.
+ ;; :user isn't used for bearer tokens
+ ;; anyway, so use this dummy name to
+ ;; differentiate the bearer token
+ ;; authinfo line from the
+ ;; client-identifier/client-secret
+ ;; authinfo line.
+ :user "BEARER"
+ :host (url-host url)
+ :port (url-http-oauth-port url)
+ :path path
+ :scope (if (string= (gethash "scope" grant)
+ scope)
+ (url-http-oauth-encode-scope scope)
+ (error
+ (concat "url-http-oauth:"
+ " Returned scope did not"
+ " match requested scope")))
+ :expiry (url-http-oauth-expiry-string grant)
+ :secret bearer-retrieved))
(save-function (plist-get (car auth-result) :save-function)))
;; Success; save bearer.
(when (functionp save-function)
- [elpa] externals/url-http-oauth 4441b79a9a 13/24: Remove explicit extra argument handling, (continued)
- [elpa] externals/url-http-oauth 4441b79a9a 13/24: Remove explicit extra argument handling, Thomas Fitzsimmons, 2023/05/08
- [elpa] externals/url-http-oauth 085d57961a 20/24: Make message formatting consistent, Thomas Fitzsimmons, 2023/05/08
- [elpa] externals/url-http-oauth 0bf4a7633d 08/24: Complete confidential client support, Thomas Fitzsimmons, 2023/05/08
- [elpa] externals/url-http-oauth b884e725af 05/24: Get basics working for Sourcehut, Thomas Fitzsimmons, 2023/05/08
- [elpa] externals/url-http-oauth 38157dfb3a 09/24: Reduce overloading of "register", Thomas Fitzsimmons, 2023/05/08
- [elpa] externals/url-http-oauth e1408ed406 02/24: Implement provider registration, Thomas Fitzsimmons, 2023/05/08
- [elpa] externals/url-http-oauth f5b953197b 15/24: Fix auth-source-search for path and scope, Thomas Fitzsimmons, 2023/05/08
- [elpa] externals/url-http-oauth 40c46af10c 24/24: Bump version to 0.8.0, Thomas Fitzsimmons, 2023/05/08
- [elpa] externals/url-http-oauth e95f685771 06/24: Begin auth-source implementation, Thomas Fitzsimmons, 2023/05/08
- [elpa] externals/url-http-oauth 1ecb40b545 17/24: Make some adjustments based on testing, Thomas Fitzsimmons, 2023/05/08
- [elpa] externals/url-http-oauth fcb5929654 16/24: Fix auth-source lookup conflicts and config cdrs,
Thomas Fitzsimmons <=