Start of bitwarden.el

This commit is contained in:
Lucien Cartier-Tilet 2022-02-13 14:12:29 +01:00
parent a3f6539920
commit 778befee9b
Signed by: phundrak
GPG Key ID: BD7789E705CB8DCA
3 changed files with 368 additions and 13 deletions

View File

@ -4,8 +4,8 @@
First of all, thank you for being interested in contributing! You rock!
In this document, you will find some guidelines for contributing to
~TEMPLATE-NAME.el~. These are more guidelines than rules, so dont try
too hard to follow them.
~bitwarden.el~. These are more guidelines than rules, so dont try too
hard to follow them.
* Table of Contents :TOC_5_gh:
- [[#how-can-i-contribute][How Can I Contribute?]]

View File

@ -1,34 +1,42 @@
#+title: TEMPLATE-NAME
#+title: bitwarden.el
#+author: Lucien Cartier-Tilet
#+email: lucien@phundrak.com
* Introduction
Describe briefly what the package is, what it does, and who it is for.
Bitwarden.el is a Bitwarden porcelain for Emacs. It aims to be a
complete text-based interface for the [[https://github.com/bitwarden/cli][Bitwarden CLI]].
Most of its public function are transient functions from the
transient library to provide the user an easy-to-use interface with
most of its options exposed.
* Table of Contents :TOC_2_gh:
- [[#introduction][Introduction]]
- [[#installation][Installation]]
- [[#notes][Notes]]
- [[#login][Login]]
- [[#customizing][Customizing]]
- [[#contributing][Contributing]]
- [[#license][License]]
* Installation
A couple of options are available for installing ~TEMPLATE-NAME~.
A couple of options are available for installing bitwarden.el.
The first one is to clone the repository in your ~load-path~ and add the
following to your ~.emacs~ or your ~init.el~:
#+begin_src emacs-lisp
(require 'TEMPLATE-NAME)
(require 'bitwarden.el)
#+end_src
In my case, I prefer using ~use-package~ with ~straight~:
#+begin_src emacs-lisp
(use-package TEMPLATE-NAME
(use-package bitwarden
:ensure t
:defer t
:straight (TEMPLATE-NAME :type git
:straight (bitwarden :type git
:host nil
:repo "https://labs.phundrak.com/phundrak/TEMPLATE-NAME"))
:repo "https://labs.phundrak.com/phundrak/bitwarden.el"))
#+end_src
I personally also added ~:build t~ in the straight recipe to ensure
@ -42,14 +50,56 @@ a PR to add some more installation instructions!
There is currently no plans of making this package available on MELPA
or non-gnu elpa.
* Notes
** Login
Loging in with the ~--apikey~ option is not supported due to its
interactive nature.
Bitwarden allows three different sources for your password:
- a plain password as an argument following the username
- an environment variable containing the password
- a file containing the password
Bitwarden.el allows a fourth option: the authinfo file on computer. To
use this option, simply add the following line in your ~.authinfo~ or
~.authinfo.gpg~ file:
#+begin_src text
machine bitwarden.example.com login yourusername password yourpassword
#+end_src
Of course, you will have to replace ~bitwarden.example.com~ with the
actual server, ~yourusername~ with your actual username, and
~yourpassword~ with your actual password. If you do not set your
username or your password in bitwarden.el, the package will look for
them in your auth source file on login. Bitwarden.el retrieves the
server name from the command
#+begin_src bash
$ bw config server
#+end_src
and it strips the result from any ~http://~ or ~https://~ prefix. For
instance, if the command returns ~https://example.com/bitwarden~,
bitwarden.el will look for ~example.com/bitwarden~ in your authinfo
file.
* Customizing
Indicate the various customization options available, e.g. variables
and faces. For each one of them, give a brief description and their
default value.
Bitwarden.el has a couple of customizable variables you can find in
the ~bitwarden~ group when executing ~M-x customize-group~. Here is a
quick description of these variables:
- ~bitwarden-cli-executable~ :: Your Bitwarden CLI executable. Set this
variable if Emacs doesnt find ~bw~ in your ~$PATH~ or if ~bw~ does not
refer to your Bitwarden CLI.
Default value: ~bw~
- ~bitwarden-default-cli-arguments~ :: A list of the default arguments
to pass to Bitwarden CLI. By default, only the package only passes
~--nointeraction~ in order to inhibit any attempt from the CLI to
launch anything interactive --- it should be taken care of by the
package itself.
Default value: ~'("--nointeraction")~
* Contributing
See [[file:CONTRIBUTING.org]].
* License
~TEMPLATE-NAME.el~ is available under the GNU GPL-3.0 license. You can
~bitwarden.el~ is available under the GNU GPL-3.0 license. You can
find the full text in [[file:LICENSE.md][LICENSE.md]].

305
bitwarden.el Normal file
View File

@ -0,0 +1,305 @@
;;; bitwarden.el --- Interact with Bitwarden from Emacs -*- lexical-binding: t -*-
;; Author: Lucien Cartier-Tilet <lucien@phundrak.com>
;; Maintainer: Lucien Cartier-Tilet <lucien@phundrak.com>
;; Version: 0.1.0
;; Package-Requires: ((emacs "27.1") (transient "0.3.7"))
;; Homepage: https://labs.phundrak.com/phundrak/bitwarden.el
;; Keywords:
;; 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 this program. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; # Introduction
;; bitwarden.el is a Bitwarden porcelain for Emacs. It aims to be a
;; complete text-based interface for the Bitwarden CLI
;; (https://github.com/bitwarden/cli).
;;
;; Most of its public function are transient functions from the
;; transient library to provide the user an easy-to-use interface with
;; most of its options exposed.
;;
;; # Notes
;; ## Login
;;
;; Loging in with the --apikey option is not supported due to its
;; interactive nature.
;;
;; Bitwarden allows three different sources for your password:
;; - a plain password as an argument following the username
;; - an environment variable containing the password
;; - a file containing the password
;; Bitwarden.el allows a fourth option: the authinfo file on computer.
;; To use this option, simply add the following line in your .authinfo
;; or .authinfo.gpg file:
;;
;; machine bitwarden.example.com login yourusername password yourpassword
;;
;; Of course, you will have to replace `bitwarden.example.com` with
;; the actual server, `yourusername` with your actual username, and
;; `yourpassword` with your actual password. If you do not set your
;; username or your password in bitwarden.el, the package will look
;; for them in your auth source file on login. Bitwarden.el retrieves
;; the server name from the command
;;
;; bw config server
;;
;; and it strips the result from any http:// or https:// prefix. For
;; instance, if the command returns "https://example.com/bitwarden",
;; bitwarden.el will look for "example.com/bitwarden" in your authinfo
;; file.
;;; Code:
;;; Requires
(require 'cl-lib)
(require 'eshell)
(require 'auth-source)
(require 'transient)
;;; Constants
(defconst bitwarden-version "0.1.0")
(defconst bitwarden-login-methods '(("Authentificator" . "0") ("Email" . "1") ("YubiKey" . "3"))
"Bitwarden login methods.")
;;; Group and Custom Variables
(defgroup bitwarden ()
"Interact with Bitwarden from Emacs."
:group 'applications
:prefix "bitwarden-"
:link '(url-link :tag "Gitea" "https://labs.phundrak.com/phundrak/bitwarden.el"))
(defcustom bitwarden-cli-executable "bw"
"Path to the Bitwarden CLI executable."
:type 'string
:group 'bitwarden
:safe #'stringp
:version "0.1.0")
(defcustom bitwarden-default-cli-arguments '("--nointeraction")
"Default arguments for the Bitwarden CLI."
:group 'bitwarden
:type '(repeat string)
:version "0.1.0")
(defvar bitwarden--shell-status)
;;; Internal Functions
(defun bitwarden--run-cli (&rest args)
"Run the Bitwarden cli with ARGS.
The default arguments specified by
`bitwarden-default-cli-arguments' immediately follow the clis
name defined in `bitwarden-cli-executable'.
The output status of the command is stored in RESULT, which
should be a variables symbol."
(eshell-command-result
(mapconcat #'identity
(flatten-tree `(,bitwarden-cli-executable ,bitwarden-default-cli-arguments ,args))
" ")
'bitwarden--shell-status))
(defun bitwarden-hello ()
"Dummy function."
(interactive)
(message "hello"))
;;; Transient Infixes
(eval-when-compile
(defmacro bitwarden--define-infix (key name description type version
default &rest reader)
"Define infix and its corresponding variable at once.
The variable is named bitwarden-NAME is of type TYPE, has a
DESCRIPTION and a specified VERSION.
The KEY and READER are for the infix declaration.
This macro is largely copied from Tecosaurs screenshot.el"
(let ((var-name (concat "bitwarden-" name)))
`(progn
(defcustom ,(intern var-name) ,default
,description
:type ,type
:group 'bitwarden
:version ,version)
(transient-define-infix ,(intern (concat "bitwarden--set-" name)) ()
"Set Bitwarden options."
:class 'transient-lisp-variable
:variable ',(intern var-name)
:key ,key
:description ,description
:argument ,(concat "--" name)
:reader (lambda (&rest _) ,@reader)))))
;; Config
(bitwarden--define-infix
"-s" "server" "On-premises hosted installation URL"
'string "0.1.0"
(let ((output-str (bitwarden--run-cli "config" "server")))
(if (eq 0 bitwarden--shell-status)
(string-trim output-str "https?://")
""))
(read-string "Server URL: "))
;; Login
(bitwarden--define-infix
"-u" "username" "Email of the user."
'string "0.1.0"
""
(read-string "Email address: "))
(bitwarden--define-infix
"-p" "password" "Password of the user"
'string "0.1.0"
""
(read-string "Password: "))
(bitwarden--define-infix
"-s" "sso" "Log in with Single-Sign On"
'boolean "0.1.0" nil
(not bitwarden-sso))
(bitwarden--define-infix
"-e" "passwordenv" "Environment variable storing your password"
'string "0.1.0"
""
(read-string "Environment variable: "))
(bitwarden--define-infix
"-f" "passwordfile"
"Path to a file containing your password as its first line"
'string "0.1.0" ""
(read-file-name "Password file: "))
(bitwarden--define-infix
"-c" "code"
"Two-step login code"
'string "0.1.0" ""
(read-string "Two-step code: "))
(bitwarden--define-infix
"-m" "method"
"Two-step login method"
'string "0.1.0" "0"
(cdr (assoc (completing-read "Two-step login method: "
bitwarden-login-methods)
bitwarden-login-methods))))
;;; Transient Actions
(eval-when-compile
(defmacro bitwarden--def-action (name description transient &rest body)
"Create a function called from TRANSIENT.
DESCRIPTION is the docstring of the function named
bitwarden--NAME, and it gets BODY as its body."
`(defun ,(intern (concat "bitwarden--action-" name)) (&optional _args)
,(concat description "
This function is meant to be called by a transient.")
(interactive
(list (transient-args ,transient)))
,@body))
;; Config
(bitwarden--def-action
"set-server"
"Set the Bitwarden server."
'bitwarden--server
(message "%s" (format "%S" _args)))
;; Login
(bitwarden--def-action
"login"
"Log in Bitwarden."
'bitwarden--login
(if bitwarden-sso
(bitwarden--run-cli "login" "--raw" "--sso")
(bitwarden--run-cli "login" "--raw"
;; bitwarden-username
(format "\"%s\""
(if (string= "" bitwarden-username))
(plist-get (car (auth-source-search
:max 1
:host bitwarden-server))
:user)
bitwarden-username)
(format "\"%s\""
(cond
((not (string= "" bitwarden-password))
bitwarden-password)
((! (string= "" bitwarden-passwordenv))
bitwarden-passwordenv)
((! (string= "" bitwarden-passwordfile))
bitwarden-passwordfile)
(t (funcall
(plist-get
(car (auth-source-search :max 1
:host bitwarden-server))
:secret))))))))
(bitwarden--def-action
"logout"
"Log out of Bitwarden."
'bitwarden--login
(shell-command (concat bitwarden-cli-executable " logout"))))
;;; Transient Prefixes
(transient-define-prefix bitwarden-config ()
["Options"
(bitwarden--set-server)]
["Actions"
("g" "Get current server" bitwarden-hello)
("s" "Set current server" bitwarden-hello)])
(transient-define-prefix bitwarden-login ()
["Options"
(bitwarden--set-username)
(bitwarden--set-password)
(bitwarden--set-passwordenv)
(bitwarden--set-passwordfile)
(bitwarden--set-method)
(bitwarden--set-code)]
["Actions"
("l" "Login" bitwarden-login)
("L" "Logout" bitwarden-logout)])
(transient-define-prefix bitwarden-transient ()
["Actions"
("c" "Config" bitwarden--config)
("l" "Login" bitwarden--login)
("L" "Lock" bitwarden-hello)
("s" "sync" bitwarden-hello)])
;; (defun bitwarden ()
;; (interactive)
;; (call-interactively #'bitwarden-transient))
(provide 'bitwarden)
;;; bitwarden.el ends here