aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDamien Cassou2017-11-09 10:40:19 +0100
committerNicolas Petton2018-06-05 15:51:14 +0200
commitb43ed61ef985e01975b90d7e0ec3cac70d0afefa (patch)
treebc4414f8595bc6ddee73cb4822062fb34b8a9cef
parenta52661b58bc9cffa13cb5f0749cdb3a4c24fbf74 (diff)
downloademacs-b43ed61ef985e01975b90d7e0ec3cac70d0afefa.tar.gz
emacs-b43ed61ef985e01975b90d7e0ec3cac70d0afefa.zip
auth-source-pass: Take care of matching hosts when port is provided
* lisp/auth-source-pass.el (auth-source-pass--find-match): Add PORT parameter and reorganize code by extracting `find-match-unambiguous'. (auth-source-pass--find-match-unambiguous): New function. (auth-source-pass--build-result): Fix the call to `find-match'. (auth-source-pass--hostname, auth-source-pass--hostname-with-user, auth-source-pass--user): Remove functions. * test/lisp/auth-source-pass-tests.el: Fix the calls to `find-match'. (auth-source-pass-find-host-without-port) Add corresponding test.
-rw-r--r--lisp/auth-source-pass.el68
-rw-r--r--test/lisp/auth-source-pass-tests.el51
2 files changed, 52 insertions, 67 deletions
diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el
index 1785ca32550..96aefc8dd7e 100644
--- a/lisp/auth-source-pass.el
+++ b/lisp/auth-source-pass.el
@@ -52,7 +52,7 @@ See `auth-source-search' for details on SPEC."
52 52
53(defun auth-source-pass--build-result (host port user) 53(defun auth-source-pass--build-result (host port user)
54 "Build auth-source-pass entry matching HOST, PORT and USER." 54 "Build auth-source-pass entry matching HOST, PORT and USER."
55 (let ((entry (auth-source-pass--find-match host user))) 55 (let ((entry (auth-source-pass--find-match host user port)))
56 (when entry 56 (when entry
57 (let ((retval (list 57 (let ((retval (list
58 :host host 58 :host host
@@ -139,26 +139,6 @@ CONTENTS is the contents of a password-store formatted file."
139 (mapconcat #'identity (cdr pair) ":"))))) 139 (mapconcat #'identity (cdr pair) ":")))))
140 (cdr lines))))) 140 (cdr lines)))))
141 141
142(defun auth-source-pass--hostname (host)
143 "Extract hostname from HOST."
144 (let ((url (url-generic-parse-url host)))
145 (or (url-host url) host)))
146
147(defun auth-source-pass--hostname-with-user (host)
148 "Extract hostname and user from HOST."
149 (let* ((url (url-generic-parse-url host))
150 (user (url-user url))
151 (hostname (url-host url)))
152 (cond
153 ((and user hostname) (format "%s@%s" user hostname))
154 (hostname hostname)
155 (t host))))
156
157(defun auth-source-pass--user (host)
158 "Extract user from HOST and return it.
159Return nil if no match was found."
160 (url-user (url-generic-parse-url host)))
161
162(defun auth-source-pass--do-debug (&rest msg) 142(defun auth-source-pass--do-debug (&rest msg)
163 "Call `auth-source-do-debug` with MSG and a prefix." 143 "Call `auth-source-do-debug` with MSG and a prefix."
164 (apply #'auth-source-do-debug 144 (apply #'auth-source-do-debug
@@ -230,27 +210,39 @@ matching USER."
230 (car matching-entries)) 210 (car matching-entries))
231 (_ (auth-source-pass--select-one-entry matching-entries user))))) 211 (_ (auth-source-pass--select-one-entry matching-entries user)))))
232 212
233(defun auth-source-pass--find-match (host user) 213(defun auth-source-pass--find-match (host user port)
234 "Return a password-store entry name matching HOST and USER. 214 "Return a password-store entry name matching HOST, USER and PORT.
235If many matches are found, return the first one. If no match is 215
236found, return nil." 216Disambiguate between user provided inside HOST (e.g., user@server.com) and
217inside USER by giving priority to USER. Same for PORT."
218 (let* ((url (url-generic-parse-url (if (string-match-p ".*://" host)
219 host
220 (format "https://%s" host)))))
221 (auth-source-pass--find-match-unambiguous
222 (or (url-host url) host)
223 (or user (url-user url))
224 ;; url-port returns 443 (because of the https:// above) by default
225 (or port (number-to-string (url-port url))))))
226
227(defun auth-source-pass--find-match-unambiguous (hostname user port)
228 "Return a password-store entry name matching HOSTNAME, USER and PORT.
229If many matches are found, return the first one. If no match is found,
230return nil.
231
232HOSTNAME should not contain any username or port number."
237 (or 233 (or
238 (if (auth-source-pass--user host) 234 (and user port (auth-source-pass--find-one-by-entry-name (format "%s@%s:%s" user hostname port) user))
239 ;; if HOST contains a user (e.g., "user@host.com"), <HOST> 235 (and user (auth-source-pass--find-one-by-entry-name (format "%s@%s" user hostname) user))
240 (auth-source-pass--find-one-by-entry-name (auth-source-pass--hostname-with-user host) user) 236 (and port (auth-source-pass--find-one-by-entry-name (format "%s:%s" hostname port) nil))
241 ;; otherwise, if USER is provided, search for <USER>@<HOST> 237 (auth-source-pass--find-one-by-entry-name hostname user)
242 (when (stringp user)
243 (auth-source-pass--find-one-by-entry-name (concat user "@" (auth-source-pass--hostname host)) user)))
244 ;; if that didn't work, search for HOST without its user component, if any
245 (auth-source-pass--find-one-by-entry-name (auth-source-pass--hostname host) user)
246 ;; if that didn't work, search for HOST with user extracted from it
247 (auth-source-pass--find-one-by-entry-name
248 (auth-source-pass--hostname host) (auth-source-pass--user host))
249 ;; if that didn't work, remove subdomain: foo.bar.com -> bar.com 238 ;; if that didn't work, remove subdomain: foo.bar.com -> bar.com
250 (let ((components (split-string host "\\."))) 239 (let ((components (split-string hostname "\\.")))
251 (when (= (length components) 3) 240 (when (= (length components) 3)
252 ;; start from scratch 241 ;; start from scratch
253 (auth-source-pass--find-match (mapconcat 'identity (cdr components) ".") user))))) 242 (auth-source-pass--find-match-unambiguous
243 (mapconcat 'identity (cdr components) ".")
244 user
245 port)))))
254 246
255(provide 'auth-source-pass) 247(provide 'auth-source-pass)
256;;; auth-source-pass.el ends here 248;;; auth-source-pass.el ends here
diff --git a/test/lisp/auth-source-pass-tests.el b/test/lisp/auth-source-pass-tests.el
index 6d471f4e342..0f072592d00 100644
--- a/test/lisp/auth-source-pass-tests.el
+++ b/test/lisp/auth-source-pass-tests.el
@@ -75,107 +75,100 @@ This function is intended to be set to `auth-source-debug`."
75 75
76(ert-deftest auth-source-pass-find-match-matching-at-entry-name () 76(ert-deftest auth-source-pass-find-match-matching-at-entry-name ()
77 (auth-source-pass--with-store '(("foo")) 77 (auth-source-pass--with-store '(("foo"))
78 (should (equal (auth-source-pass--find-match "foo" nil) 78 (should (equal (auth-source-pass--find-match "foo" nil nil)
79 "foo")))) 79 "foo"))))
80 80
81(ert-deftest auth-source-pass-find-match-matching-at-entry-name-part () 81(ert-deftest auth-source-pass-find-match-matching-at-entry-name-part ()
82 (auth-source-pass--with-store '(("foo")) 82 (auth-source-pass--with-store '(("foo"))
83 (should (equal (auth-source-pass--find-match "https://foo" nil) 83 (should (equal (auth-source-pass--find-match "https://foo" nil nil)
84 "foo")))) 84 "foo"))))
85 85
86(ert-deftest auth-source-pass-find-match-matching-at-entry-name-ignoring-user () 86(ert-deftest auth-source-pass-find-match-matching-at-entry-name-ignoring-user ()
87 (auth-source-pass--with-store '(("foo")) 87 (auth-source-pass--with-store '(("foo"))
88 (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil) 88 (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil nil)
89 "foo")))) 89 "foo"))))
90 90
91(ert-deftest auth-source-pass-find-match-matching-at-entry-name-with-user () 91(ert-deftest auth-source-pass-find-match-matching-at-entry-name-with-user ()
92 (auth-source-pass--with-store '(("SomeUser@foo")) 92 (auth-source-pass--with-store '(("SomeUser@foo"))
93 (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil) 93 (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil nil)
94 "SomeUser@foo")))) 94 "SomeUser@foo"))))
95 95
96(ert-deftest auth-source-pass-find-match-matching-at-entry-name-prefer-full () 96(ert-deftest auth-source-pass-find-match-matching-at-entry-name-prefer-full ()
97 (auth-source-pass--with-store '(("SomeUser@foo") ("foo")) 97 (auth-source-pass--with-store '(("SomeUser@foo") ("foo"))
98 (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil) 98 (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil nil)
99 "SomeUser@foo")))) 99 "SomeUser@foo"))))
100 100
101(ert-deftest auth-source-pass-find-match-matching-at-entry-name-prefer-full-reversed () 101(ert-deftest auth-source-pass-find-match-matching-at-entry-name-prefer-full-reversed ()
102 (auth-source-pass--with-store '(("foo") ("SomeUser@foo")) 102 (auth-source-pass--with-store '(("foo") ("SomeUser@foo"))
103 (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil) 103 (should (equal (auth-source-pass--find-match "https://SomeUser@foo" nil nil)
104 "SomeUser@foo")))) 104 "SomeUser@foo"))))
105 105
106(ert-deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain () 106(ert-deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain ()
107 (auth-source-pass--with-store '(("bar.com")) 107 (auth-source-pass--with-store '(("bar.com"))
108 (should (equal (auth-source-pass--find-match "foo.bar.com" nil) 108 (should (equal (auth-source-pass--find-match "foo.bar.com" nil nil)
109 "bar.com")))) 109 "bar.com"))))
110 110
111(ert-deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain-with-user () 111(ert-deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain-with-user ()
112 (auth-source-pass--with-store '(("someone@bar.com")) 112 (auth-source-pass--with-store '(("someone@bar.com"))
113 (should (equal (auth-source-pass--find-match "foo.bar.com" "someone") 113 (should (equal (auth-source-pass--find-match "foo.bar.com" "someone" nil)
114 "someone@bar.com")))) 114 "someone@bar.com"))))
115 115
116(ert-deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain-with-bad-user () 116(ert-deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain-with-bad-user ()
117 (auth-source-pass--with-store '(("someoneelse@bar.com")) 117 (auth-source-pass--with-store '(("someoneelse@bar.com"))
118 (should (equal (auth-source-pass--find-match "foo.bar.com" "someone") 118 (should (equal (auth-source-pass--find-match "foo.bar.com" "someone" nil)
119 nil)))) 119 nil))))
120 120
121(ert-deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain-prefer-full () 121(ert-deftest auth-source-pass-find-match-matching-at-entry-name-without-subdomain-prefer-full ()
122 (auth-source-pass--with-store '(("bar.com") ("foo.bar.com")) 122 (auth-source-pass--with-store '(("bar.com") ("foo.bar.com"))
123 (should (equal (auth-source-pass--find-match "foo.bar.com" nil) 123 (should (equal (auth-source-pass--find-match "foo.bar.com" nil nil)
124 "foo.bar.com")))) 124 "foo.bar.com"))))
125 125
126(ert-deftest auth-source-pass-dont-match-at-folder-name () 126(ert-deftest auth-source-pass-dont-match-at-folder-name ()
127 (auth-source-pass--with-store '(("foo.bar.com/foo")) 127 (auth-source-pass--with-store '(("foo.bar.com/foo"))
128 (should (equal (auth-source-pass--find-match "foo.bar.com" nil) 128 (should (equal (auth-source-pass--find-match "foo.bar.com" nil nil)
129 nil)))) 129 nil))))
130 130
131(ert-deftest auth-source-pass-find-match-matching-extracting-user-from-host () 131(ert-deftest auth-source-pass-find-match-matching-extracting-user-from-host ()
132 (auth-source-pass--with-store '(("foo.com/bar")) 132 (auth-source-pass--with-store '(("foo.com/bar"))
133 (should (equal (auth-source-pass--find-match "https://bar@foo.com" nil) 133 (should (equal (auth-source-pass--find-match "https://bar@foo.com" nil nil)
134 "foo.com/bar")))) 134 "foo.com/bar"))))
135 135
136(ert-deftest auth-source-pass-search-with-user-first () 136(ert-deftest auth-source-pass-search-with-user-first ()
137 (auth-source-pass--with-store '(("foo") ("user@foo")) 137 (auth-source-pass--with-store '(("foo") ("user@foo"))
138 (should (equal (auth-source-pass--find-match "foo" "user") 138 (should (equal (auth-source-pass--find-match "foo" "user" nil)
139 "user@foo")) 139 "user@foo"))
140 (auth-source-pass--should-have-message-containing "Found 1 match"))) 140 (auth-source-pass--should-have-message-containing "Found 1 match")))
141 141
142(ert-deftest auth-source-pass-give-priority-to-desired-user () 142(ert-deftest auth-source-pass-give-priority-to-desired-user ()
143 (auth-source-pass--with-store '(("foo") ("subdir/foo" ("user" . "someone"))) 143 (auth-source-pass--with-store '(("foo") ("subdir/foo" ("user" . "someone")))
144 (should (equal (auth-source-pass--find-match "foo" "someone") 144 (should (equal (auth-source-pass--find-match "foo" "someone" nil)
145 "subdir/foo")) 145 "subdir/foo"))
146 (auth-source-pass--should-have-message-containing "Found 2 matches") 146 (auth-source-pass--should-have-message-containing "Found 2 matches")
147 (auth-source-pass--should-have-message-containing "matching user field"))) 147 (auth-source-pass--should-have-message-containing "matching user field")))
148 148
149(ert-deftest auth-source-pass-give-priority-to-desired-user-reversed () 149(ert-deftest auth-source-pass-give-priority-to-desired-user-reversed ()
150 (auth-source-pass--with-store '(("foo" ("user" . "someone")) ("subdir/foo")) 150 (auth-source-pass--with-store '(("foo" ("user" . "someone")) ("subdir/foo"))
151 (should (equal (auth-source-pass--find-match "foo" "someone") 151 (should (equal (auth-source-pass--find-match "foo" "someone" nil)
152 "foo")) 152 "foo"))
153 (auth-source-pass--should-have-message-containing "Found 2 matches") 153 (auth-source-pass--should-have-message-containing "Found 2 matches")
154 (auth-source-pass--should-have-message-containing "matching user field"))) 154 (auth-source-pass--should-have-message-containing "matching user field")))
155 155
156(ert-deftest auth-source-pass-return-first-when-several-matches () 156(ert-deftest auth-source-pass-return-first-when-several-matches ()
157 (auth-source-pass--with-store '(("foo") ("subdir/foo")) 157 (auth-source-pass--with-store '(("foo") ("subdir/foo"))
158 (should (equal (auth-source-pass--find-match "foo" nil) 158 (should (equal (auth-source-pass--find-match "foo" nil nil)
159 "foo")) 159 "foo"))
160 (auth-source-pass--should-have-message-containing "Found 2 matches") 160 (auth-source-pass--should-have-message-containing "Found 2 matches")
161 (auth-source-pass--should-have-message-containing "the first one"))) 161 (auth-source-pass--should-have-message-containing "the first one")))
162 162
163(ert-deftest auth-source-pass-make-divansantana-happy () 163(ert-deftest auth-source-pass-make-divansantana-happy ()
164 (auth-source-pass--with-store '(("host.com")) 164 (auth-source-pass--with-store '(("host.com"))
165 (should (equal (auth-source-pass--find-match "smtp.host.com" "myusername@host.co.za") 165 (should (equal (auth-source-pass--find-match "smtp.host.com" "myusername@host.co.za" nil)
166 "host.com")))) 166 "host.com"))))
167 167
168(ert-deftest auth-source-pass-hostname () 168(ert-deftest auth-source-pass-find-host-without-port ()
169 (should (equal (auth-source-pass--hostname "https://foo.bar:443") "foo.bar")) 169 (auth-source-pass--with-store '(("host.com"))
170 (should (equal (auth-source-pass--hostname "https://foo.bar") "foo.bar")) 170 (should (equal (auth-source-pass--find-match "host.com:8888" "someuser" nil)
171 (should (equal (auth-source-pass--hostname "http://foo.bar") "foo.bar")) 171 "host.com"))))
172 (should (equal (auth-source-pass--hostname "https://SomeUser@foo.bar") "foo.bar")))
173
174(ert-deftest auth-source-pass-hostname-with-user ()
175 (should (equal (auth-source-pass--hostname-with-user "https://foo.bar:443") "foo.bar"))
176 (should (equal (auth-source-pass--hostname-with-user "https://foo.bar") "foo.bar"))
177 (should (equal (auth-source-pass--hostname-with-user "http://foo.bar") "foo.bar"))
178 (should (equal (auth-source-pass--hostname-with-user "https://SomeUser@foo.bar") "SomeUser@foo.bar")))
179 172
180(defmacro auth-source-pass--with-store-find-foo (store &rest body) 173(defmacro auth-source-pass--with-store-find-foo (store &rest body)
181 "Use STORE while executing BODY. \"foo\" is the matched entry." 174 "Use STORE while executing BODY. \"foo\" is the matched entry."
@@ -207,7 +200,7 @@ This function is intended to be set to `auth-source-debug`."
207(ert-deftest auth-source-pass-build-result-passes-full-host-to-find-match () 200(ert-deftest auth-source-pass-build-result-passes-full-host-to-find-match ()
208 (let (passed-host) 201 (let (passed-host)
209 (cl-letf (((symbol-function 'auth-source-pass--find-match) 202 (cl-letf (((symbol-function 'auth-source-pass--find-match)
210 (lambda (host _user) (setq passed-host host)))) 203 (lambda (host _user _port) (setq passed-host host))))
211 (auth-source-pass--build-result "https://user@host.com:123" nil nil) 204 (auth-source-pass--build-result "https://user@host.com:123" nil nil)
212 (should (equal passed-host "https://user@host.com:123")) 205 (should (equal passed-host "https://user@host.com:123"))
213 (auth-source-pass--build-result "https://user@host.com" nil nil) 206 (auth-source-pass--build-result "https://user@host.com" nil nil)