Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forge uses APIHOST including full url for auth-source-search bugging passwords-store #720

Closed
Thaodan opened this issue Oct 24, 2024 · 8 comments
Labels
enhancement New feature or request

Comments

@Thaodan
Copy link

Thaodan commented Oct 24, 2024

Preconditions

  • auth-source-pass-extra-query-keywords set to t
    • This option can be enabled to deal with usernames that contain [email protected] for other hosts to not break retrieving secrets for user foo
      on bar.host.
  • password-store inside auth-sources.

Environment

  • Magit-version: 4.1.1-3-gf7e20b84
    Same for All dependencies
  • Emacs 31.x
  • OpenSUSE Tumbleweed

Expected

Secret is found.

Observed

Forge uses the full url that includes not just the host for Gitlab based Forges. Forge should not use the full url to retrieve passwords I think.

Backtrace

Debugger entered--Lisp error: (error "Required Gitlab token (\"Thaodan^forge\" for \"gitlab.com/api/v4\") does not exist.\nSee https://magit.vc/manual/ghub/Getting-Started.html\nor (info \"(ghub)Getting Started\") for instructions.\n(The setup wizard no longer exists.)")
  apply(debug (error (error "Required Gitlab token (\"Thaodan^forge\" for \"gitlab.com/api/v4\") does not exist.\nSee https://magit.vc/manual/ghub/Getting-Started.html\nor (info \"(ghub)Getting Started\") for instructions.\n(The setup wizard no longer exists.)")))
  transient--exit-and-debug(error (error "Required Gitlab token (\"Thaodan^forge\" for \"gitlab.com/api/v4\") does not exist.\nSee https://magit.vc/manual/ghub/Getting-Started.html\nor (info \"(ghub)Getting Started\") for instructions.\n(The setup wizard no longer exists.)"))
  error("Required %s token (\"%s\" for \"%s\") does not exist.\nSee https://magit.vc/manual/ghub/Getting-Started.html\nor (info \"(ghub)Getting Started\") for instructions.\n(The setup wizard no longer exists.)" "Gitlab" "Thaodan^forge" "gitlab.com/api/v4")
  ghub--token("gitlab.com/api/v4" "Thaodan" forge nil gitlab)
  ghub--auth("gitlab.com/api/v4" forge "Thaodan" gitlab)
  #f(compiled-function () #<bytecode 0xf6950f34267783b>)()
  ghub--retrieve(nil #s(ghub--req :url #s(url :type "https" :user nil :password nil :host "gitlab.com" :portspec nil :filename "/api/v4/projects/CalcProgrammer1%2FOpenRGB" :target nil :attributes nil :fullness t :silent nil :use-cookies t :asynchronous t) :forge gitlab :silent nil :method "GET" :headers #f(compiled-function () #<bytecode 0xf6950f34267783b>) :handler ghub--handle-response :unpaginate nil :noerror nil :reader nil :buffer #<buffer magit: OpenRGB> :callback nil :errorback nil :value nil :extra nil))
  ghub-request("GET" "/projects/CalcProgrammer1%2FOpenRGB" nil :forge gitlab :query nil :payload nil :headers nil :silent nil :unpaginate nil :noerror nil :reader nil :username nil :auth forge :host "gitlab.com/api/v4" :callback nil :errorback nil :extra nil)
  glab-get("/projects/CalcProgrammer1%2FOpenRGB" nil :username nil :auth forge :host "gitlab.com/api/v4")
  glab-repository-id("CalcProgrammer1" "OpenRGB" :username nil :auth forge :host "gitlab.com/api/v4")
  ghub-repository-id("CalcProgrammer1" "OpenRGB" :host "gitlab.com/api/v4" :auth forge :forge gitlab :noerror nil)
  #f(compiled-function (class host owner name &optional stub noerror) "Return (OUR-ID . THEIR-ID) of the specified repository.\nIf optional STUB is non-nil, then the IDs are not guaranteed to\nbe unique.  Otherwise this method has to make an API request to\nretrieve THEIR-ID, the repository's ID on the forge.  In that\ncase OUR-ID derives from THEIR-ID and is unique across all\nforges and hosts." #<bytecode 0x182a9261fd84f3c3>)(forge-gitlab-repository "gitlab.com" "CalcProgrammer1" "OpenRGB" nil nil)
  apply(#f(compiled-function (class host owner name &optional stub noerror) "Return (OUR-ID . THEIR-ID) of the specified repository.\nIf optional STUB is non-nil, then the IDs are not guaranteed to\nbe unique.  Otherwise this method has to make an API request to\nretrieve THEIR-ID, the repository's ID on the forge.  In that\ncase OUR-ID derives from THEIR-ID and is unique across all\nforges and hosts." #<bytecode 0x182a9261fd84f3c3>) forge-gitlab-repository ("gitlab.com" "CalcProgrammer1" "OpenRGB" nil nil))
  forge--repository-ids(forge-gitlab-repository "gitlab.com" "CalcProgrammer1" "OpenRGB" nil nil)
  #f(compiled-function (&rest rest) "((HOST OWNER NAME) &optional REMOTE DEMAND)\n\nReturn the repository identified by HOST, OWNER and NAME.\nSee `forge-alist' for valid Git hosts." #<bytecode 0xb693faf21aa5d25>)(("gitlab.com" "CalcProgrammer1" "OpenRGB") nil :insert!)
  apply(#f(compiled-function (&rest rest) "((HOST OWNER NAME) &optional REMOTE DEMAND)\n\nReturn the repository identified by HOST, OWNER and NAME.\nSee `forge-alist' for valid Git hosts." #<bytecode 0xb693faf21aa5d25>) ("gitlab.com" "CalcProgrammer1" "OpenRGB") (nil :insert!))
  forge-get-repository(("gitlab.com" "CalcProgrammer1" "OpenRGB") nil :insert!)
  #f(compiled-function (repo &optional noerror demand) #<bytecode 0x1ff657b70ca11cd8>)(#<forge-gitlab-repository forge-gitlab-repository-103a787ede19> nil :insert!)
  apply(#f(compiled-function (repo &optional noerror demand) #<bytecode 0x1ff657b70ca11cd8>) #<forge-gitlab-repository forge-gitlab-repository-103a787ede19> (nil :insert!))
  forge-get-repository(#<forge-gitlab-repository forge-gitlab-repository-103a787ede19> nil :insert!)
  forge-add-repository(#<forge-gitlab-repository forge-gitlab-repository-103a787ede19>)
  #f(compiled-function (repo) (interactive #f(compiled-function () #<bytecode 0x7abd6776d812>)) #<bytecode 0x1e2aed1a03b8ded3>)(#<forge-gitlab-repository forge-gitlab-repository-103a787ede19>)
  apply(#f(compiled-function (repo) (interactive #f(compiled-function () #<bytecode 0x7abd6776d812>)) #<bytecode 0x1e2aed1a03b8ded3>) #<forge-gitlab-repository forge-gitlab-repository-103a787ede19>)
  (let ((debugger #'transient--exit-and-debug)) (apply fn args))
  (unwind-protect (let ((debugger #'transient--exit-and-debug)) (apply fn args)) (let* ((unwind (and t (eieio-oref prefix 'unwind-suffix)))) (if unwind (progn (transient--debug 'unwind-command) (funcall unwind suffix)) nil)) (advice-remove suffix advice) (eieio-oset prefix 'unwind-suffix nil))
  #f(lambda (fn &rest args) [(advice #0) (suffix transient:forge-add-repository::454) (prefix #<transient-prefix transient-prefix-103a7b52e9e7>)] (interactive #'(lambda (spec) (let ((abort t)) (unwind-protect (prog1 (let ((debugger #'transient--exit-and-debug)) (advice-eval-interactive-spec spec)) (setq abort nil)) (if abort (progn (let* ((unwind (and t (eieio-oref prefix 'unwind-suffix)))) (if unwind (progn (transient--debug 'unwind-interactive) (funcall unwind suffix)) nil)) (advice-remove suffix advice) (eieio-oset prefix 'unwind-suffix nil))))))) (unwind-protect (let ((debugger #'transient--exit-and-debug)) (apply fn args)) (let* ((unwind (and t (eieio-oref prefix 'unwind-suffix)))) (if unwind (progn (transient--debug 'unwind-command) (funcall unwind suffix)) nil)) (advice-remove suffix advice) (eieio-oset prefix 'unwind-suffix nil)))(#f(compiled-function (repo) (interactive #f(compiled-function () #<bytecode 0x7abd6776d812>)) #<bytecode 0x1e2aed1a03b8ded3>) #<forge-gitlab-repository forge-gitlab-repository-103a787ede19>)
  apply(#f(lambda (fn &rest args) [(advice #1) (suffix transient:forge-add-repository::454) (prefix #<transient-prefix transient-prefix-103a7b52e9e7>)] (interactive #'(lambda (spec) (let ((abort t)) (unwind-protect (prog1 ... ...) (if abort ...))))) (unwind-protect (let ((debugger #'transient--exit-and-debug)) (apply fn args)) (let* ((unwind (and t (eieio-oref prefix ...)))) (if unwind (progn (transient--debug 'unwind-command) (funcall unwind suffix)) nil)) (advice-remove suffix advice) (eieio-oset prefix 'unwind-suffix nil))) #f(compiled-function (repo) (interactive #f(compiled-function () #<bytecode 0x7abd6776d812>)) #<bytecode 0x1e2aed1a03b8ded3>) #<forge-gitlab-repository forge-gitlab-repository-103a787ede19>)
  transient:forge-add-repository::454(#<forge-gitlab-repository forge-gitlab-repository-103a787ede19>)
  funcall-interactively(transient:forge-add-repository::454 #<forge-gitlab-repository forge-gitlab-repository-103a787ede19>)
  command-execute(transient:forge-add-repository::454)
@tarsius
Copy link
Member

tarsius commented Oct 28, 2024

I am considering

@@ -764,9 +764,14 @@ (defun ghub--basic-auth (host username)
 
 (defun ghub--token (host username package &optional nocreate forge)
   (let* ((user (ghub--ident username package))
+         (host* (and (string-match "/" host)
+                     (substring host 0 (match-beginning 0))))
          (token
           (or (car (ghub--auth-source-get (list :secret)
                      :host host :user user))
+              (and host*
+                   (car (ghub--auth-source-get (list :secret)
+                          :host host :user user)))
               (progn
                 ;; Auth-Source caches the information that there is no
                 ;; value, but in our case that is a situation that needs
@@ -775,6 +780,8 @@ (defun ghub--token (host username package &optional nocreate forge)
                 ;; The (:max 1) is needed and has to be placed at the
                 ;; end for Emacs releases before 26.1.  #24 #64 #72
                 (auth-source-forget (list :host host :user user :max 1))
+                (when host*
+                  (auth-source-forget (list :host host* :user user :max 1)))
                 (and (not nocreate)
                      (error "\
 Required %s token (\"%s\" for \"%s\") does not exist.

but to many suggestions are coming in right now and I won't be able to deal with this any time soon.

@Thaodan
Copy link
Author

Thaodan commented Oct 28, 2024

but to many suggestions are coming in right now and I won't be able to deal with this any time soon.

Take your time!

What I did to workaround/fix the issue was this:

diff --git a/lisp/ghub.el b/lisp/ghub.el
index 4451526..d98949d 100644
--- a/lisp/ghub.el
+++ b/lisp/ghub.el
@@ -775,7 +775,7 @@ and call `auth-source-forget+'."
   (let* ((user (ghub--ident username package))
          (token
           (or (car (ghub--auth-source-get (list :secret)
-                     :host host :user user))
+                     :host (url-host (url-generic-parse-url (concat "//" host))) :user user))
               (progn
                 ;; Auth-Source caches the information that there is no
                 ;; value, but in our case that is a situation that needs

I was going to post this earlier but forgot.

Thaodan added a commit to Thaodan/ghub that referenced this issue Nov 3, 2024
Thaodan added a commit to Thaodan/emacs.d that referenced this issue Nov 3, 2024
@tarsius tarsius added the enhancement New feature or request label Dec 9, 2024
tarsius added a commit to magit/ghub that referenced this issue Jan 13, 2025
@tarsius
Copy link
Member

tarsius commented Jan 13, 2025

  • auth-source-pass-extra-query-keywords set to t
    • This option can be enabled to deal with usernames that contain
      [email protected] for other hosts to not break retrieving secrets for
      user foo on bar.host.

I was unable to reproduce this issue (both with nil and t as the value of this variable. Below I include my experiments. I would be interested in learning about the failure case, so if you can still reproduce this, please provide reproduction steps, similar to what I have tried here.

Just in case I misunderstood, you are saying that if some host (e.g., example.com) uses email addresses (e.g., [email protected]) as "user names", and we add entries for both to the password store, then auth-source-pass can no longer retrieve the password for the email address (a.k.a. user me at host gmail.com), right?

;;; Test utility

(defun pass-entry-p (host user)
  "If an entry is found for USER on HOST, return USER, else nil."
  (plist-get (car (auth-source-pass-search :host host :user user)) :user))

;;; Commands for copy-pasting into terminal

pass add [email protected]
pass add example.com/[email protected]
pass add [email protected]@example.com

pass rm [email protected]
pass rm example.com/[email protected]
pass rm [email protected]@example.com

;;; Test using email as user, no extra

;; $ pass ls
;; Password Store
;; └── [email protected]
(setq auth-source-pass-extra-query-keywords nil)
(pass-entry-p "gmail.com" "me") => "me"

;; $ pass ls
;; Password Store
;; ├── example.com              This service uses
;; │   └── [email protected]         email addresses as users
;; └── [email protected]
(setq auth-source-pass-extra-query-keywords nil)
(pass-entry-p "gmail.com" "me") => "me"
(pass-entry-p "example.com" "[email protected]") => "[email protected]"

;; $ pass ls
;; Password Store
;; ├── [email protected]@example.com
;; └── [email protected]
(setq auth-source-pass-extra-query-keywords nil)
(pass-entry-p "gmail.com" "me") => "me"
(pass-entry-p "example.com" "[email protected]") => "[email protected]"

;;; Test using email as user, with extra

;; $ pass ls
;; Password Store
;; └── [email protected]
(setq auth-source-pass-extra-query-keywords t)
(pass-entry-p "gmail.com" "me") => "me"

;; $ pass ls
;; Password Store
;; ├── example.com              This service uses
;; │   └── [email protected]         email addresses as users
;; └── [email protected]
(setq auth-source-pass-extra-query-keywords t)
(pass-entry-p "gmail.com" "me") => "me"
(pass-entry-p "example.com" "[email protected]") => "[email protected]"

;; $ pass ls
;; Password Store
;; ├── [email protected]@example.com
;; └── [email protected]
(setq auth-source-pass-extra-query-keywords t)
(pass-entry-p "gmail.com" "me") => "me"
(pass-entry-p "example.com" "[email protected]") => "[email protected]"

I can however reproduce the issue you are having with ghub and auth-source-pass.

Ghub includes the path as part of the host for Gitlab hosts. This is problematic, but worked until people started using alternative auth-source back-ends. I would not do it if I were to start today, but changing it now would be hard and risk regressions. For now I am limiting changes to kludges, which come with a smaller risk of breaking existing working setups.

What Ghub sees as "host with a path", in pass becomes an entry that specifies a user using the prefix notation, as well as using the postfix notation, and the two users are not the same.

At least in the most simple of tests, pass itself can actually deal with this:

$ pass ls
Password Store
└── [email protected]
    └── api
        └── v4
$ pass show [email protected]/api/v4
abc

auth-source-pass-source however returns nil.

;; $ pass ls
;; Password Store
;; └── [email protected]
;;     └── api
;;         └── v4
;; $ pass show [email protected]/api/v4
;; abc
(setq auth-source-pass-extra-query-keywords nil)
(list
 (auth-source-pass-search :host "gitlab.com")
 (auth-source-pass-search :host "gitlab.com" :user "me")
 (auth-source-pass-search :host "gitlab.com" :user "api/v4")
 (auth-source-pass-search :host "gitlab.com/api/v4")
 (auth-source-pass-search :host "gitlab.com/api/v4" :user "me")
 (auth-source-pass-search :host "gitlab.com/api/v4" :user "api/v4")
 ) => (nil nil nil nil nil nil)

@Thaodan
Copy link
Author

Thaodan commented Jan 13, 2025 via email

@tarsius
Copy link
Member

tarsius commented Jan 14, 2025

Setting auth-source-pass-extra-query-keywords to nil broke retrieving secrets where the username is inside the password but that does not affect Forge only setting it to t broke Github with forge.

If it is possible to demonstrate that in, say, five commands, then please do.

An option would be to first to look with the proper hostname and then if there is no result to use the full path.

That's what we do now. If by "proper hostname" you mean <actual-host>/<path>, i.e, "what forge traditionally needed". I wouldn't use the word "proper" for that, given that the issue we are dealing with, is exactly caused/triggered by using something that isn't actually just a hostname, in a field intended hold just an actual hostname.

What Ghub sees as "host with a path", in pass becomes an entry that specifies a user using the prefix notation, as well as using the postfix notation, and the two users are not the same.

I don't know of these notations sorry.

  • User as a prefix: user@host.
  • User as a suffix: host/user. (I called it "postfix" before.)

I.e., if we use something that in our interpretation means user@host/path, then pass sees user@host/another-user?!?.

@Thaodan
Copy link
Author

Thaodan commented Jan 16, 2025 via email

@tarsius
Copy link
Member

tarsius commented Jan 17, 2025

Thanks! I don't have the time to study this now, but will make sure to not let it go to waste.

Ps: When you post by email, email addresses get obfuscated. Could you please post the parts that ended up looking like this again?:

***@***.*** i.e. ***@***.***

@Thaodan
Copy link
Author

Thaodan commented Jan 17, 2025 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants