summaryrefslogtreecommitdiff
path: root/kasi-auth-source.el
blob: fa9f6299f8edfc4384c27a57218e10c983685f6b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
;;; kasi-auth-source.el --- auth-sources backend for kasi  -*- lexical-binding: t -*-

;; Copyright (C) 2026 Vineet K

;; Author: Vineet K <git@vineetk.net>
;; Version: 1.0

;; 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:

;; Provides an auth-sources backend for kasi.el.  After loading, add
;; 'kasi to `auth-sources', e.g.:
;;
;;   (add-to-list 'auth-sources 'kasi)
;;   (auth-source-forget-all-cached)
;;
;; The kasi file path (e.g. "category/file") is matched against the
;; `:host' parameter.  The `:user' parameter can optionally be used to
;; filter by the username stored in the kasi entry.

;;; Code:

(require 'auth-source)
(require 'kasi)
(require 'cl-lib)
(require 'seq)

(eval-when-compile (require 'subr-x))

(defgroup kasi-auth-source nil
  "auth-sources integration for kasi."
  :prefix "kasi-auth-source-"
  :group 'auth-source
  :group 'kasi)

(cl-defun kasi-auth-source--search (&rest spec
                                          &key backend type host user
                                                port require max
                                                &allow-other-keys)
  "Search kasi entries matching SPEC.

See `auth-source-search' for details on SPEC, BACKEND, TYPE, HOST,
USER, PORT, REQUIRE, and MAX."
  (cl-assert (or (null type) (eq type (oref backend type)))
             t "Invalid kasi search: %s %s" type (oref backend type))

  (when (eq host t)
    (warn "kasi-auth-source does not handle host wildcards.")
    (cl-return-from kasi-auth-source--search nil))

  (when-let* ((results (kasi-auth-source--find host user port require)))
    (when (and max (> (length results) max))
      (setq results (seq-take results max)))
    results))

(defun kasi-auth-source--find (host user port require)
  "Find kasi entries matching HOST, USER, PORT and REQUIRE.

Searches all kasi files under `kasi-dir`.  HOST is matched against
the kasi file path (e.g. \"category/file\").  USER is matched against
the username stored in each kasi entry.  PORT and REQUIRE are
optional filters."
  (let ((files (file-expand-wildcards (concat kasi-dir "/*/*")))
        result)
    (cl-block nil
      (dolist (file files)
        (message "kasi-auth-source: checking %s" file)
        (when-let* ((short-name (kasi--filename-short file))
                     ((or (not host) (string= short-name host)))
                     (entry-user (kasi--get file 'username))
                     (entry-email (kasi--get file 'email))
                     ((or (not user) (string= entry-user user)))
                     (require-match (seq-every-p
                                     (lambda (key)
                                       (pcase key
                                         ('user (and user (string= entry-user user)))
                                         ('host t)
                                         ('port t)
                                         (_ t)))
                                     (or require '()))))
          (setq result (list (list :host short-name
                                   :user entry-user
                                   :port port
                                   :email entry-email
                                   :secret (lambda ()
                                             (let ((pw (kasi--get file 'password)))
                                               pw)))))
          (cl-return-from nil result)))
    result)))

(defvar kasi-auth-source-backend
  (auth-source-backend
   :source "."
   :type 'kasi
   :search-function #'kasi-auth-source--search)
  "Auth-source backend for kasi.")

(defun kasi-auth-source-backend-parse (entry)
  "Create a kasi auth-source backend from ENTRY."
  (when (eq entry 'kasi)
    (auth-source-backend-parse-parameters entry kasi-auth-source-backend)))

(add-hook 'auth-source-backend-parser-functions
          #'kasi-auth-source-backend-parse)

;;;###autoload
(defun kasi-auth-source-enable ()
  "Enable kasi as an auth-source backend.

This adds 'kasi to `auth-sources' and clears the auth-source cache."
  (interactive)
  (add-to-list 'auth-sources 'kasi)
  (auth-source-forget-all-cached)
  (message "kasi auth-source backend enabled"))

(provide 'kasi-auth-source)
;;; kasi-auth-source.el ends here