aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael R. Mauger2019-02-18 23:15:54 -0500
committerMichael R. Mauger2019-02-18 23:15:54 -0500
commit1a6bcc91e3e468e5a6d3e0b121bb675b576d3362 (patch)
treeca325907ed02edcd6a463b5f5e5482bbc6f81ab9
parented1e805af7d4892e7354e8c9e2246d5017d4ff52 (diff)
downloademacs-wallet.tar.gz
emacs-wallet.zip
* lisp/progmodes/sql.el: Added password wallet usingwallet
`auth-source' package. (sql-auth-source-search-wallet): New function. (sql-password-wallet): New variable. (sql-password-search-wallet-function): New variable. (sql-get-login): Handle password wallet search. (sql-product-interactive): Handle password function. * test/lisp/progmodes/sql-test.el: Test wallet changes. (sql-test-login-params): New test variable. (with-sql-test-connect-harness): New macro to wrap test configuration around calls to `sql-connect'. (sql-test-connect, sql-test-connect-password-func) (sql-test-connect-wallet-server-database) (sql-test-connect-wallet-database) (sql-test-connect-wallet-server): New ERT tests. * etc/NEWS: Updated SQL Mode descriptions.
-rw-r--r--etc/NEWS48
-rw-r--r--lisp/progmodes/sql.el151
-rw-r--r--test/lisp/progmodes/sql-tests.el101
3 files changed, 284 insertions, 16 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 0cafbaae96c..253da499899 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -515,27 +515,45 @@ end.
515 515
516** SQL 516** SQL
517 517
518*** Installation of 'sql-indent' from ELPA is strongly encouraged. 518*** SQL Indent Minor Mode
519This package support sophisticated rules for properly indenting SQL 519
520statements. SQL is not like other programming languages like C, Java, 520SQL Mode now supports the ELPA 'sql-indent' package for assisting
521or Python where code is sparse and rules for formatting are fairly 521sophisticated SQL indenting rules. Note, however, that SQL is not
522well established. Instead SQL is more like COBOL (from which it came) 522like other programming languages like C, Java, or Python where code is
523and code tends to be very dense and line ending decisions driven by 523sparse and rules for formatting are fairly well established. Instead
524syntax and line length considerations to make readable code. 524SQL is more like COBOL (from which it came) and code tends to be very
525Experienced SQL developers may prefer to rely upon existing Emacs 525dense and line ending decisions driven by syntax and line length
526facilities for formatting code but the 'sql-indent' package provides 526considerations to make readable code. Experienced SQL developers may
527facilities to aid more casual SQL developers layout queries and 527prefer to rely upon existing Emacs facilities for formatting code but
528complex expressions. 528the 'sql-indent' package provides facilities to aid more casual SQL
529 529developers layout queries and complex expressions.
530*** 'sql-use-indent-support' (default t) enables SQL indention support. 530
531**** 'sql-use-indent-support' (default t) enables SQL indention support.
531The 'sql-indent' package from ELPA must be installed to get the 532The 'sql-indent' package from ELPA must be installed to get the
532indentation support in 'sql-mode' and 'sql-interactive-mode'. 533indentation support in 'sql-mode' and 'sql-interactive-mode'.
533 534
534*** 'sql-mode-hook' and 'sql-interactive-mode-hook' changed. 535**** 'sql-mode-hook' and 'sql-interactive-mode-hook' changed.
535Both hook variables have had 'sql-indent-enable' added to their 536Both hook variables have had 'sql-indent-enable' added to their
536default values. If youhave existing customizations to these variables, 537default values. If you have existing customizations to these variables,
537you should make sure that the new default entry is included. 538you should make sure that the new default entry is included.
538 539
540*** Connection Wallet
541
542Database passwords can now by stored in NETRC or JSON data files that
543may optionally be encrypted. When establishing an interactive session
544with the database via 'sql-connect' or a product specific function,
545like 'sql-mysql' or 'my-postgres', the password wallet will be
546searched for the password. The 'sql-product', 'sql-server',
547'sql-database', and the 'sql-username' will be used to identify the
548appropriate authorization. This eliminates the discouraged practice of
549embedding database passwords in your Emacs initialization.
550
551See the `auth-source' module for complete documentation on the file
552formats. By default, the wallet file is expected to be in the
553`user-emacs-directory', named 'sql-wallet' or '.sql-wallet', with
554'.json' (JSON) or no (NETRC) suffix. Both file formats can optionally
555be encrypted with GPG by adding an additional '.gpg' suffix.
556
539** Term 557** Term
540 558
541--- 559---
diff --git a/lisp/progmodes/sql.el b/lisp/progmodes/sql.el
index 2df62585a0d..c72070b8923 100644
--- a/lisp/progmodes/sql.el
+++ b/lisp/progmodes/sql.el
@@ -748,6 +748,126 @@ The package must be available to be loaded and activated."
748 (when (sql-is-indent-available) 748 (when (sql-is-indent-available)
749 (sqlind-minor-mode (if sql-use-indent-support +1 -1)))) 749 (sqlind-minor-mode (if sql-use-indent-support +1 -1))))
750 750
751;; Secure Password wallet
752
753(require 'auth-source)
754
755(defun sql-auth-source-search-wallet (wallet product user server database port)
756 "Read auth source WALLET to locate the USER secret.
757Sets `auth-sources' to WALLET and uses `auth-source-search' to locate the entry.
758The DATABASE and SERVER are concatenated with a slash between them as the
759host key."
760 (let* ((auth-sources wallet)
761 host
762 secret h-secret sd-secret)
763
764 ;; product
765 (setq product (symbol-name product))
766
767 ;; user
768 (setq user (unless (string-empty-p user) user))
769
770 ;; port
771 (setq port
772 (when (and port (numberp port) (not (zerop port)))
773 (number-to-string port)))
774
775 ;; server
776 (setq server (unless (string-empty-p server) server))
777
778 ;; database
779 (setq database (unless (string-empty-p database) database))
780
781 ;; host
782 (setq host (if server
783 (if database
784 (concat server "/" database)
785 server)
786 database))
787
788 ;; Perform search
789 (dolist (s (auth-source-search :max 1000))
790 (when (and
791 ;; Is PRODUCT specified, in the enty, and they are equal
792 (if product
793 (if (plist-member s :product)
794 (equal (plist-get s :product) product)
795 t)
796 t)
797 ;; Is USER specified, in the entry, and they are equal
798 (if user
799 (if (plist-member s :user)
800 (equal (plist-get s :user) user)
801 t)
802 t)
803 ;; Is PORT specified, in the entry, and they are equal
804 (if port
805 (if (plist-member s :port)
806 (equal (plist-get s :port) port)
807 t)
808 t))
809 ;; Is HOST specified, in the entry, and they are equal
810 ;; then the H-SECRET list
811 (if (and host
812 (plist-member s :host)
813 (equal (plist-get s :host) host))
814 (push s h-secret)
815 ;; Are SERVER and DATABASE specified, present, and equal
816 ;; then the SD-SECRET list
817 (if (and server
818 (plist-member s :server)
819 database
820 (plist-member s :database)
821 (equal (plist-get s :server) server)
822 (equal (plist-get s :database) database))
823 (push s sd-secret)
824 ;; Is SERVER specified, in the entry, and they are equal
825 ;; then the base SECRET list
826 (if (and server
827 (plist-member s :server)
828 (equal (plist-get s :server) server))
829 (push s secret)
830 ;; Is DATABASE specified, in the entry, and they are equal
831 ;; then the base SECRET list
832 (if (and database
833 (plist-member s :database)
834 (equal (plist-get s :database) database))
835 (push s secret)))))))
836 (setq secret (or h-secret sd-secret secret))
837
838 ;; If we found a single secret, return the password
839 (when (= 1 (length secret))
840 (setq secret (car secret))
841 (if (plist-member secret :secret)
842 (plist-get secret :secret)
843 nil))))
844
845(defcustom sql-password-wallet
846 (let (wallet w)
847 (dolist (ext '(".json.gpg" ".gpg" ".json" "") wallet)
848 (unless wallet
849 (setq w (locate-user-emacs-file (concat "sql-wallet" ext)
850 (concat ".sql-wallet" ext)))
851 (when (file-exists-p w)
852 (setq wallet w)))))
853 "Identification of the password wallet.
854See `sql-password-search-wallet-function' to understand how this value
855is used to locate the password wallet."
856 :type `(plist-get (symbol-plist 'auth-sources) 'custom-type)
857 :group 'SQL
858 :version "27.1")
859
860(defvar sql-password-search-wallet-function #'sql-auth-source-search-wallet
861 "Function to handle the lookup of the database password.
862The specified function will be called as:
863 (wallet-func WALLET PRODUCT USER SERVER DATABASE PORT)
864
865It is expected to return either a string containing the password,
866a function returning the password, or nil, If you want to support
867another format of password file, then implement a different
868search wallet function and identify the location of the password
869store with `sql-password-wallet'.")
870
751;; misc customization of sql.el behavior 871;; misc customization of sql.el behavior
752 872
753(defcustom sql-electric-stuff nil 873(defcustom sql-electric-stuff nil
@@ -3199,6 +3319,10 @@ symbol `password', for the server if it contains the symbol
3199`database'. The members of WHAT are processed in the order in 3319`database'. The members of WHAT are processed in the order in
3200which they are provided. 3320which they are provided.
3201 3321
3322If the `sql-password-wallet' is non-nil and WHAT contains the
3323`password' token, then the `password' token will be pushed to the
3324end to be sure that all of the values can be fed to the wallet.
3325
3202Each token may also be a list with the token in the car and a 3326Each token may also be a list with the token in the car and a
3203plist of options as the cdr. The following properties are 3327plist of options as the cdr. The following properties are
3204supported: 3328supported:
@@ -3210,6 +3334,15 @@ supported:
3210 3334
3211In order to ask the user for username, password and database, call the 3335In order to ask the user for username, password and database, call the
3212function like this: (sql-get-login \\='user \\='password \\='database)." 3336function like this: (sql-get-login \\='user \\='password \\='database)."
3337
3338 ;; Push the password to the end if we have a wallet
3339 (when (and sql-password-wallet
3340 (fboundp sql-password-search-wallet-function)
3341 (member 'password what))
3342 (setq what (append (cl-delete 'password what)
3343 '(password))))
3344
3345 ;; Prompt for each parameter
3213 (dolist (w what) 3346 (dolist (w what)
3214 (let ((plist (cdr-safe w))) 3347 (let ((plist (cdr-safe w)))
3215 (pcase (or (car-safe w) w) 3348 (pcase (or (car-safe w) w)
@@ -3218,7 +3351,19 @@ function like this: (sql-get-login \\='user \\='password \\='database)."
3218 3351
3219 ('password 3352 ('password
3220 (setq-default sql-password 3353 (setq-default sql-password
3221 (read-passwd "Password: " nil (sql-default-value 'sql-password)))) 3354 (if (and sql-password-wallet
3355 (fboundp sql-password-search-wallet-function))
3356 (let ((password (funcall sql-password-search-wallet-function
3357 sql-password-wallet
3358 sql-product
3359 sql-user
3360 sql-server
3361 sql-database
3362 sql-port)))
3363 (if password
3364 password
3365 (read-passwd "Password: " nil (sql-default-value 'sql-password))))
3366 (read-passwd "Password: " nil (sql-default-value 'sql-password)))))
3222 3367
3223 ('server 3368 ('server
3224 (sql-get-login-ext 'sql-server "Server: " 'sql-server-history plist)) 3369 (sql-get-login-ext 'sql-server "Server: " 'sql-server-history plist))
@@ -4481,6 +4626,10 @@ the call to \\[sql-product-interactive] with
4481 (or sql-default-directory 4626 (or sql-default-directory
4482 default-directory))) 4627 default-directory)))
4483 4628
4629 ;; The password wallet returns a function which supplies the password.
4630 (when (functionp sql-password)
4631 (setq sql-password (funcall sql-password)))
4632
4484 ;; Call the COMINT service 4633 ;; Call the COMINT service
4485 (funcall (sql-get-product-feature product :sqli-comint-func) 4634 (funcall (sql-get-product-feature product :sqli-comint-func)
4486 product 4635 product
diff --git a/test/lisp/progmodes/sql-tests.el b/test/lisp/progmodes/sql-tests.el
index 604c02172ea..a68f9319c2f 100644
--- a/test/lisp/progmodes/sql-tests.el
+++ b/test/lisp/progmodes/sql-tests.el
@@ -53,5 +53,106 @@
53 (error "some error")))) 53 (error "some error"))))
54 (should-not (sql-postgres-list-databases)))) 54 (should-not (sql-postgres-list-databases))))
55 55
56(defvar sql-test-login-params nil)
57(defmacro with-sql-test-connect-harness (id login-params connection expected)
58 "Set-up and tear-down SQL connect related test.
59
60Identify tests by ID. Set :sql-login dialect attribute to
61LOGIN-PARAMS. Provide the CONNECTION parameters and the EXPECTED
62string of values passed to the comint function for validation."
63 (declare (indent 2))
64 `(cl-letf
65 ((sql-test-login-params ' ,login-params)
66 ((symbol-function 'sql-comint-test)
67 (lambda (product options &optional buf-name)
68 (with-current-buffer (get-buffer-create buf-name)
69 (insert (pp-to-string (list product options sql-user sql-password sql-server sql-database))))))
70 ((symbol-function 'sql-run-test)
71 (lambda (&optional buffer)
72 (interactive "P")
73 (sql-product-interactive 'sqltest buffer)))
74 (sql-user nil)
75 (sql-server nil)
76 (sql-database nil)
77 (sql-product-alist
78 '((ansi)
79 (sqltest
80 :name "SqlTest"
81 :sqli-login sql-test-login-params
82 :sqli-comint-func sql-comint-test)))
83 (sql-connection-alist
84 '((,(format "test-%s" id)
85 ,@connection)))
86 (sql-password-wallet
87 (list
88 (make-temp-file
89 "sql-test-netrc" nil nil
90 (mapconcat #'identity
91 '("machine aMachine user aUserName password \"netrc-A aPassword\""
92 "machine aServer user aUserName password \"netrc-B aPassword\""
93 "machine aMachine server aServer user aUserName password \"netrc-C aPassword\""
94 "machine aMachine database aDatabase user aUserName password \"netrc-D aPassword\""
95 "machine aDatabase user aUserName password \"netrc-E aPassword\""
96 "machine aMachine server aServer database aDatabase user aUserName password \"netrc-F aPassword\""
97 "machine \"aServer/aDatabase\" user aUserName password \"netrc-G aPassword\""
98 ) "\n")))))
99
100 (let* ((connection ,(format "test-%s" id))
101 (buffername (format "*SQL: ERT TEST <%s>*" connection)))
102 (when (get-buffer buffername)
103 (kill-buffer buffername))
104 (sql-connect connection buffername)
105 (should (get-buffer buffername))
106 (should (string-equal (with-current-buffer buffername (buffer-string)) ,expected))
107 (when (get-buffer buffername)
108 (kill-buffer buffername))
109 (delete-file (car sql-password-wallet)))))
110
111(ert-deftest sql-test-connect ()
112 "Test of basic `sql-connect'."
113 (with-sql-test-connect-harness 1 (user password server database)
114 ((sql-product 'sqltest)
115 (sql-user "aUserName")
116 (sql-password "test-1 aPassword")
117 (sql-server "aServer")
118 (sql-database "aDatabase"))
119 "(sqltest nil \"aUserName\" \"test-1 aPassword\" \"aServer\" \"aDatabase\")\n"))
120
121(ert-deftest sql-test-connect-password-func ()
122 "Test of password function."
123 (with-sql-test-connect-harness 2 (user password server database)
124 ((sql-product 'sqltest)
125 (sql-user "aUserName")
126 (sql-password (lambda () (concat [?t ?e ?s ?t ?- ?2 ?\s
127 ?a ?P ?a ?s ?s ?w ?o ?r ?d])))
128 (sql-server "aServer")
129 (sql-database "aDatabase"))
130 "(sqltest nil \"aUserName\" \"test-2 aPassword\" \"aServer\" \"aDatabase\")\n"))
131
132(ert-deftest sql-test-connect-wallet-server-database ()
133 "Test of password function."
134 (with-sql-test-connect-harness 3 (user password server database)
135 ((sql-product 'sqltest)
136 (sql-user "aUserName")
137 (sql-server "aServer")
138 (sql-database "aDatabase"))
139 "(sqltest nil \"aUserName\" \"netrc-G aPassword\" \"aServer\" \"aDatabase\")\n"))
140
141(ert-deftest sql-test-connect-wallet-database ()
142 "Test of password function."
143 (with-sql-test-connect-harness 4 (user password database)
144 ((sql-product 'sqltest)
145 (sql-user "aUserName")
146 (sql-database "aDatabase"))
147 "(sqltest nil \"aUserName\" \"netrc-E aPassword\" nil \"aDatabase\")\n"))
148
149(ert-deftest sql-test-connect-wallet-server ()
150 "Test of password function."
151 (with-sql-test-connect-harness 5 (user password server)
152 ((sql-product 'sqltest)
153 (sql-user "aUserName")
154 (sql-server "aServer"))
155 "(sqltest nil \"aUserName\" \"netrc-B aPassword\" \"aServer\" nil)\n"))
156
56(provide 'sql-tests) 157(provide 'sql-tests)
57;;; sql-tests.el ends here 158;;; sql-tests.el ends here