emacs-orgmode@gnu.org archives
 help / color / mirror / code / Atom feed
From: jorge.a.alfaro@gmail.com (Jorge A. Alfaro-Murillo)
To: emacs-orgmode@gnu.org
Subject: Re: An org password manager
Date: Sat, 31 May 2014 12:09:22 -0400	[thread overview]
Message-ID: <87zjhy0w25.fsf@gmail.com> (raw)
In-Reply-To: 87k39fpoy0.fsf@bzg.ath.cx


Dear Bastien, 

This is my first time submitting a patch, so I do not know if
this is the way I am supposed to do it.

Best,

Jorge

---
    org-passwords.el: Add an org derived mode for managing passwords
    
    * contrib/lisp/org-passwords.el: new file.
    
    The file gives a mode for consulting a passwords database with entries
    in org format.  The database opens in Read-Only mode and a timer is
    set to close the database.  The file provides functions for making
    usernames and passwords available to the facility for pasting text of
    the window system (clipboard on X and MS-Windows, pasteboard on
    Nextstep/Mac OS, etc.) without inserting them in the kill-ring.  It
    also provides functions for generating passwords as a random string of
    characters or as a number of random words from a dictionary.

1 file changed, 342 insertions(+)
 contrib/lisp/org-passwords.el | 342 ++++++++++++++++++++++++++++++++++++++++++

	New        contrib/lisp/org-passwords.el
diff --git a/contrib/lisp/org-passwords.el b/contrib/lisp/org-passwords.el
new file mode 100644
index 0000000..8c00d61
--- /dev/null
+++ b/contrib/lisp/org-passwords.el
@@ -0,0 +1,342 @@
+;;; org-passwords.el --- org derived mode for managing passwords
+
+;; Author: Jorge A. Alfaro-Murillo <jorge.alfaro-murillo@yale.edu>
+;; Created: December 26, 2012
+;; Keywords: passwords, password
+
+;; This file is NOT part of GNU Emacs.
+;;
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Commentary:
+
+;; This file contains the code for managing your passwords with
+;; Org-mode.
+
+;; A basic setup needs to indicate a passwords file, and a dictionary
+;; for the random words:
+
+;;   (require org-passwords)
+;;   (setq org-passwords-file "~/documents/passwords.gpg")
+;;   (setq org-passwords-random-words-dictionary "/etc/dictionaries-common/words")
+
+;; Basic usage:
+
+;;   `M-x org-passwords' opens the passwords file in
+;;   `org-passwords-mode'.
+
+;;   `M-x org-passwords-generate-password' generates a random string
+;;   of numbers, lowercase letters and uppercase letters.
+
+;;   `C-u M-x org-passwords-generate-password' generates a random
+;;   string of numbers, lowercase letters, uppercase letters and
+;;   symbols.
+
+;;   `M-x org-passwords-random-words' concatenates random words from
+;;   the dictionary defined by `org-passwords-random-words-dictionary'
+;;   into a string, each word separated by the string defined in
+;;   `org-passwords-random-words-separator'.
+
+;;   `C-u M-x org-passwords-random-words' does the same as above, and
+;;   also makes substitutions according to
+;;   `org-passwords-random-words-substitutions'.
+
+;; It is also useful to set up keybindings for the functions
+;; `org-passwords-copy-username' and
+;; `org-passwords-copy-password' in the
+;; `org-passwords-mode', to easily make the passwords and usernames
+;; available to the facility for pasting text of the window system
+;; (clipboard on X and MS-Windows, pasteboard on Nextstep/Mac OS,
+;; etc.), without inserting them in the kill-ring. You can set for
+;; example:
+
+;;   (eval-after-load "org-passwords"
+;;     '(progn
+;;        (define-key org-passwords-mode-map
+;; 	 (kbd "C-c u")
+;; 	 'org-passwords-copy-username)
+;;        (define-key org-passwords-mode-map
+;; 	 (kbd "C-c p")
+;; 	 'org-passwords-copy-password)))
+
+;; Finally, to enter new passwords, you can use `org-capture' and a minimal template like:
+         
+;;   ("p" "password" entry (file "~/documents/passwords.gpg")
+;;    "* %^{Title}\n  %^{PASSWORD}p %^{USERNAME}p")
+
+;; When asked for the password you can then call either
+;; `org-passwords-generate-password' or `org-passwords-random-words'.
+;; Be sure to enable recursive minibuffers to call those functions
+;; from the minibuffer:
+
+;;   (setq enable-recursive-minibuffers t)
+
+;;; Code:
+
+(require 'org)
+
+(define-derived-mode org-passwords-mode org-mode
+  "org-passwords-mode"
+  "Mode for storing passwords"
+  nil)
+
+(defgroup org-passwords nil
+  "Options for password management."
+  :group 'org)
+
+(defcustom org-passwords-password-property "PASSWORD"
+  "Name of the property for password entry password."
+  :type 'string
+  :group 'org-passwords)
+
+(defcustom org-passwords-username-property "USERNAME"
+  "Name of the property for password entry user name."
+  :type 'string
+  :group 'org-passwords)
+
+(defcustom org-passwords-file nil
+  "Default file name for the file that contains the passwords."
+  :type 'file
+  :group 'org-passwords)
+
+(defcustom org-passwords-time-opened "1 min"
+  "Time that the password file will remain open. It has to be a
+string, a number followed by units."
+  :type 'str
+  :group 'org-passwords)
+
+(defcustom org-passwords-random-words-dictionary nil
+  "Default file name for the file that contains a dictionary of
+words for `org-passwords-random-words'. Each non-empty line in
+the file is considered a word."
+  :type 'file
+  :group 'org-passwords)
+
+(defvar org-passwords-random-words-separator "-"
+  "A string to separate words in `org-passwords-random-words'.")
+
+(defvar org-passwords-random-words-substitutions
+  '(("a" . "@")
+    ("e" . "3")
+    ("o" . "0"))
+"A list of substitutions to be made with
+`org-passwords-random-words' if it is called with
+`universal-argument'. Each element is pair of
+strings (SUBSTITUTE-THIS . BY-THIS).")
+
+(defun org-passwords-copy-password ()
+  "Makes the password available to other programs. Puts the
+password of the entry at the location of the cursor in the
+facility for pasting text of the window system (clipboard on X
+and MS-Windows, pasteboard on Nextstep/Mac OS, etc.), without
+putting it in the kill ring."
+  (interactive)
+  (save-excursion
+    (search-backward-regexp "^\\*")
+    (search-forward-regexp (concat "^[[:space:]]*:"
+				   org-passwords-password-property 
+				   ":[[:space:]]*"))
+    (funcall interprogram-cut-function 
+	     (buffer-substring-no-properties (point)
+					     (funcall (lambda ()
+							(end-of-line)
+							(point)))))))
+
+(defun org-passwords-copy-username ()
+  "Makes the password available to other programs. Puts the
+username of the entry at the location of the cursor in the
+facility for pasting text of the window system (clipboard on X
+and MS-Windows, pasteboard on Nextstep/Mac OS, etc.), without
+putting it in the kill ring."
+  (interactive)
+  (save-excursion
+    (search-backward-regexp "^\\*")
+    (search-forward-regexp (concat "^[[:space:]]*:" 
+				   org-passwords-username-property 
+				   ":[[:space:]]*"))
+    (funcall interprogram-cut-function 
+	     (buffer-substring-no-properties (point)
+					     (funcall (lambda ()
+							(end-of-line)
+							(point)))))))
+
+(defun org-passwords ()
+  "Open the password file. Open the password file defined by the
+variable `org-password-file' in read-only mode and kill that
+buffer later according to the value of the variable
+`org-passwords-time-opened'. It also adds the `org-password-file'
+to the auto-mode-alist so that it is opened with its mode being
+`org-passwords-mode'."
+  (interactive)
+  (if org-passwords-file
+      (progn
+	(add-to-list 'auto-mode-alist 
+		     (cons 
+		      (regexp-quote 
+		       (expand-file-name org-passwords-file)) 
+		      'org-passwords-mode))
+	(find-file-read-only org-passwords-file)
+	(org-passwords-set-up-kill-password-buffer))
+    (minibuffer-message "No default password file defined. Set the variable `org-password-file'.")))
+
+(defun org-passwords-set-up-kill-password-buffer ()
+  (run-at-time org-passwords-time-opened
+	       nil 
+	       '(lambda ()
+		  (if (get-file-buffer org-passwords-file)
+		      (kill-buffer
+		       (get-file-buffer org-passwords-file))))))
+
+;;; Password generator
+
+;; Set random number seed from current time and pid. Otherwise
+;; `random' gives the same results every time emacs restarts.
+(random t) 
+
+(defun org-passwords-generate-password (arg)
+  "Ask a number of characters and insert a password of that size.
+Password has a random string of numbers, lowercase letters, and
+uppercase letters.  Argument ARG include symbols."
+  (interactive "P")
+  (let ((number-of-chars 
+	 (string-to-number 
+	  (read-from-minibuffer "Number of Characters: "))))
+    (if arg
+	(insert (org-passwords-generate-password-with-symbols "" number-of-chars))
+	(insert (org-passwords-generate-password-without-symbols "" number-of-chars)))))
+
+(defun org-passwords-generate-password-with-symbols (previous-string nums-of-chars)
+  "Return a string consisting of PREVIOUS-STRING and
+NUMS-OF-CHARS random characters."
+  (if (eq nums-of-chars 0) previous-string
+    (insert (org-passwords-generate-password-with-symbols 
+	     (concat previous-string
+		     (char-to-string
+		      ;; symbols, letters, numbers are from 33 to 126
+		      (+ (random (- 127 33)) 33)))
+	     (1- nums-of-chars)))))
+
+(defun org-passwords-generate-password-without-symbols (previous-string nums-of-chars)
+  "Return string consisting of PREVIOUS-STRING and NUMS-OF-CHARS
+random numbers, lowercase letters, and numbers."
+  (if (eq nums-of-chars 0)
+      previous-string
+      ; There are 10 numbers, 26 lowercase letters and 26 uppercase
+      ; letters. 10 + 26 + 26 = 62. The number characters go from 48
+      ; to 57, the uppercase letters from 65 to 90, and the lowercase
+      ; from 97 to 122. The following makes each equally likely.
+      (let ((temp-value (random 62)))
+ 	(cond ((< temp-value 10) 
+	       ; If temp-value<10, then add a number
+	       (org-passwords-generate-password-without-symbols 
+		(concat previous-string
+			(char-to-string (+ 48 temp-value)))
+		(1- nums-of-chars)))
+	      ((and (> temp-value 9) (< temp-value 36)) 
+	       ; If 9<temp-value<36, then add an uppercase letter
+	       (org-passwords-generate-password-without-symbols 
+		(concat previous-string
+			(char-to-string (+ 65 (- temp-value 10))))
+		(1- nums-of-chars)))
+	      ((> temp-value 35) 
+               ; If temp-value>35, then add a lowecase letter
+	       (org-passwords-generate-password-without-symbols 
+		(concat previous-string
+			(char-to-string (+ 97 (- temp-value 36))))
+		(1- nums-of-chars)))))))
+
+;;; Random words
+
+(defun org-passwords-random-words (arg)
+  "Ask for a number of words and inserts a sequence of that many
+random words from the list in the file
+`org-passwords-random-words-dictionary' separated by
+`org-passwords-random-words-separator'. ARG make substitutions in
+the words as defined by
+`org-passwords-random-words-substitutions'."
+  (interactive "P")
+  (if org-passwords-random-words-dictionary
+      (let ((number-of-words
+	     (string-to-number
+	      (read-from-minibuffer "Number of words: ")))
+	    (list-of-words
+	     (with-temp-buffer
+	       (insert-file-contents
+		org-passwords-random-words-dictionary)
+	       (split-string (buffer-string) "\n" t))))
+	(insert
+	 (org-passwords-substitute
+	  (org-passwords-random-words-attach-number-of-words
+	   (nth (random (length list-of-words))
+		list-of-words)
+	   (1- number-of-words)
+	   list-of-words
+	   org-passwords-random-words-separator)
+	  (if arg
+	      org-passwords-random-words-substitutions
+	    nil))))
+    (minibuffer-message 
+     "No default dictionary file defined. Set the variable `org-passwords-random-words-dictionary'.")))
+
+(defun org-passwords-random-words-attach-number-of-words
+  (previous-string number-of-words list-of-words separator)
+  "Returns a string consisting of PREVIOUS-STRING followed by a
+succession of NUMBER-OF-WORDS random words from the list LIST-OF-WORDS
+separated SEPARATOR."
+  (if (eq number-of-words 0)
+      previous-string
+      (org-passwords-random-words-attach-number-of-words
+       (concat previous-string
+	       separator
+	       (nth (random (length list-of-words)) list-of-words))
+       (1- number-of-words)
+       list-of-words
+       separator)))
+
+(defun org-passwords-substitute (string-to-change list-of-substitutions)
+  "Substitutes each appearence in STRING-TO-CHANGE of the `car' of
+each element of LIST-OF-SUBSTITUTIONS by the `cdr' of that
+element. For example:
+ (org-passwords-substitute \"ab\" \'((\"a\" . \"b\") (\"b\" . \"c\")))
+       => \"bc\"
+Substitutions are made in order of the list, so for example:
+ (org-passwords-substitute \"ab\" \'((\"ab\" . \"c\") (\"b\" . \"d\")))
+       => \"c\""
+  (if list-of-substitutions
+      (concat (org-passwords-concat-this-with-string
+	       (cdar list-of-substitutions)
+	       (mapcar (lambda (x)
+			 (org-passwords-substitute 
+			  x 
+			  (cdr list-of-substitutions)))
+		       (split-string string-to-change 
+				     (caar list-of-substitutions)))))
+    string-to-change))
+
+(defun org-passwords-concat-this-with-string (this list-of-strings)
+  "Put the string THIS in between every string in LIST-OF-STRINGS. For example:
+ (org-passwords-concat-this-with-string \"Here\" \'(\"First\" \"Second\" \"Third\"))
+      => \"FirstHereSencondHereThird\""
+  (if (cdr list-of-strings)
+      (concat (car list-of-strings)
+	      this
+	      (org-passwords-concat-this-with-string 
+	       (cdr list-of-strings)
+	       this))
+    (car list-of-strings)))
+
+(provide 'org-passwords)
+
+;;; org-passwords.el ends here

[back]

  parent reply	other threads:[~2014-05-31 16:09 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-05-11  5:21 An org password manager Jorge A. Alfaro-Murillo
2014-05-11  5:47 ` Igor Sosa Mayor
2014-05-11  9:15 ` Ramon Diaz-Uriarte
2014-05-11 13:56   ` Colin Baxter
2014-05-12  8:20     ` Ramon Diaz-Uriarte
2014-05-12 14:35       ` Colin Baxter
2014-05-12 22:12         ` Ramon Diaz-Uriarte
2014-05-13 11:51           ` Colin Baxter
2014-05-13 12:40             ` Jorge A. Alfaro-Murillo
2014-05-14 10:01               ` Colin Baxter
2014-05-16 13:03               ` Ramon Diaz-Uriarte
2014-05-11 17:13   ` Jorge A. Alfaro-Murillo
2014-05-11 17:20     ` Jorge A. Alfaro-Murillo
2014-05-12  8:22       ` Ramon Diaz-Uriarte
2014-05-12 20:51 ` Cayetano Santos
2014-05-21  7:32 ` Bastien
2014-05-21 11:53   ` Michael Albinus
2014-06-05 14:36     ` Jorge A. Alfaro-Murillo
2014-06-06  9:39       ` Michael Albinus
2014-05-31 16:09   ` Jorge A. Alfaro-Murillo [this message]
2014-06-01 19:17     ` Bastien
2014-06-02 17:38     ` Bastien
2014-06-02 17:53       ` Jorge A. Alfaro-Murillo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.orgmode.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87zjhy0w25.fsf@gmail.com \
    --to=jorge.a.alfaro@gmail.com \
    --cc=emacs-orgmode@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).