;;; kasi-auth-source.el --- auth-sources backend for kasi -*- lexical-binding: t -*- ;; Copyright (C) 2026 Vineet K ;; Author: Vineet K ;; 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 . ;;; 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 only if it contains a "/" (i.e. looks like a ;; directory path). Otherwise the `:host' is ignored and the search ;; falls back to matching by `:user'. ;;; 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) (not (string-match-p "/" 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