kasi.el

This package provides an easy way to manage secrets via M-x kasi.
Log | Files | Refs | LICENSE

kasi-auth-source.el (4926B)


      1 ;;; kasi-auth-source.el --- auth-sources backend for kasi  -*- lexical-binding: t -*-
      2 
      3 ;; Copyright (C) 2026 Vineet K
      4 
      5 ;; Author: Vineet K <git@vineetk.net>
      6 ;; Version: 1.0
      7 
      8 ;; This program is free software; you can redistribute it and/or modify
      9 ;; it under the terms of the GNU General Public License as published by
     10 ;; the Free Software Foundation, either version 3 of the License, or
     11 ;; (at your option) any later version.
     12 
     13 ;; This program is distributed in the hope that it will be useful,
     14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
     15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16 ;; GNU General Public License for more details.
     17 
     18 ;; You should have received a copy of the GNU General Public License
     19 ;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
     20 
     21 ;;; Commentary:
     22 
     23 ;; Provides an auth-sources backend for kasi.el.  After loading, add
     24 ;; 'kasi to `auth-sources', e.g.:
     25 ;;
     26 ;;   (add-to-list 'auth-sources 'kasi)
     27 ;;   (auth-source-forget-all-cached)
     28 ;;
     29 ;; The kasi file path (e.g. "category/file") is matched against the
     30 ;; `:host' parameter only if it contains a "/" (i.e. looks like a
     31 ;; directory path).  Otherwise the `:host' is ignored and the search
     32 ;; falls back to matching by `:user'.
     33 
     34 ;;; Code:
     35 
     36 (require 'auth-source)
     37 (require 'kasi)
     38 (require 'cl-lib)
     39 (require 'seq)
     40 
     41 (eval-when-compile (require 'subr-x))
     42 
     43 (defgroup kasi-auth-source nil
     44   "auth-sources integration for kasi."
     45   :prefix "kasi-auth-source-"
     46   :group 'auth-source
     47   :group 'kasi)
     48 
     49 (cl-defun kasi-auth-source--search (&rest spec
     50                                           &key backend type host user
     51                                                 port require max
     52                                                 &allow-other-keys)
     53   "Search kasi entries matching SPEC.
     54 
     55 See `auth-source-search' for details on SPEC, BACKEND, TYPE, HOST,
     56 USER, PORT, REQUIRE, and MAX."
     57   (cl-assert (or (null type) (eq type (oref backend type)))
     58              t "Invalid kasi search: %s %s" type (oref backend type))
     59 
     60   (when (eq host t)
     61     (warn "kasi-auth-source does not handle host wildcards.")
     62     (cl-return-from kasi-auth-source--search nil))
     63 
     64   (when-let* ((results (kasi-auth-source--find host user port require)))
     65     (when (and max (> (length results) max))
     66       (setq results (seq-take results max)))
     67     results))
     68 
     69 (defun kasi-auth-source--find (host user port require)
     70   "Find kasi entries matching HOST, USER, PORT and REQUIRE.
     71 
     72 Searches all kasi files under `kasi-dir`.  HOST is matched against
     73 the kasi file path (e.g. \"category/file\").  USER is matched against
     74 the username stored in each kasi entry.  PORT and REQUIRE are
     75 optional filters."
     76   (let ((files (file-expand-wildcards (concat kasi-dir "/*/*")))
     77         result)
     78     (cl-block nil
     79       (dolist (file files)
     80         (message "kasi-auth-source: checking %s" file)
     81         (when-let* ((short-name (kasi--filename-short file))
     82                      ((or (not host) (not (string-match-p "/" host)) (string= short-name host)))
     83                      (entry-user (kasi--get file 'username))
     84                      (entry-email (kasi--get file 'email))
     85                      ((or (not user) (string= entry-user user)))
     86                      (require-match (seq-every-p
     87                                      (lambda (key)
     88                                        (pcase key
     89                                          ('user (and user (string= entry-user user)))
     90                                          ('host t)
     91                                          ('port t)
     92                                          (_ t)))
     93                                      (or require '()))))
     94           (setq result (list (list :host short-name
     95                                    :user entry-user
     96                                    :port port
     97                                    :email entry-email
     98                                    :secret (lambda ()
     99                                              (let ((pw (kasi--get file 'password)))
    100                                                pw)))))
    101           (cl-return-from nil result)))
    102     result)))
    103 
    104 (defvar kasi-auth-source-backend
    105   (auth-source-backend
    106    :source "."
    107    :type 'kasi
    108    :search-function #'kasi-auth-source--search)
    109   "Auth-source backend for kasi.")
    110 
    111 (defun kasi-auth-source-backend-parse (entry)
    112   "Create a kasi auth-source backend from ENTRY."
    113   (when (eq entry 'kasi)
    114     (auth-source-backend-parse-parameters entry kasi-auth-source-backend)))
    115 
    116 (add-hook 'auth-source-backend-parser-functions
    117           #'kasi-auth-source-backend-parse)
    118 
    119 ;;;###autoload
    120 (defun kasi-auth-source-enable ()
    121   "Enable kasi as an auth-source backend.
    122 
    123 This adds 'kasi to `auth-sources' and clears the auth-source cache."
    124   (interactive)
    125   (add-to-list 'auth-sources 'kasi)
    126   (auth-source-forget-all-cached)
    127   (message "kasi auth-source backend enabled"))
    128 
    129 (provide 'kasi-auth-source)
    130 ;;; kasi-auth-source.el ends here