Implement Phase 9: client libraries (Go, Rust, Lisp, Python)
- clients/README.md: canonical API surface and error type reference - clients/testdata/: shared JSON response fixtures - clients/go/: mciasgoclient package; net/http + TLS 1.2+; sync.RWMutex token state; DisallowUnknownFields on all decoders; 25 tests pass - clients/rust/: async mcias-client crate; reqwest+rustls (no OpenSSL); thiserror MciasError enum; Arc<RwLock> token state; 22+1 tests pass; cargo clippy -D warnings clean - clients/lisp/: ASDF mcias-client; dexador HTTP, yason JSON; mcias-error condition hierarchy; Hunchentoot mock-dispatcher; 37 fiveam checks pass on SBCL 2.6.1; yason boolean normalisation in validate-token - clients/python/: mcias_client package (Python 3.11+); httpx sync; py.typed; dataclasses; 32 pytest tests; mypy --strict + ruff clean - test/mock/mockserver.go: in-memory mock server for Go client tests - ARCHITECTURE.md §19: updated per-language notes to match implementation - PROGRESS.md: Phase 9 marked complete - .gitignore: exclude clients/rust/target/, python .venv, .pytest_cache, .fasl files Security: token never logged or exposed in error messages in any library; TLS enforced in all four languages; token stored under lock/mutex/RwLock
This commit is contained in:
102
clients/lisp/README.md
Normal file
102
clients/lisp/README.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# mcias-client (Common Lisp)
|
||||
|
||||
Common Lisp client library for the [MCIAS](../../README.md) identity and access management API.
|
||||
|
||||
## Requirements
|
||||
|
||||
- SBCL 2.x (primary), CCL (secondary)
|
||||
- Quicklisp
|
||||
|
||||
## Installation
|
||||
|
||||
Place the `clients/lisp/` directory on ASDF's central registry or load it
|
||||
via Quicklisp local-projects:
|
||||
|
||||
```sh
|
||||
ln -s /path/to/mcias/clients/lisp ~/.quicklisp/local-projects/mcias-client
|
||||
```
|
||||
|
||||
Then in your Lisp image:
|
||||
|
||||
```lisp
|
||||
(ql:quickload :mcias-client)
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```lisp
|
||||
(use-package :mcias-client)
|
||||
|
||||
;; Connect to the MCIAS server.
|
||||
(defvar *client* (make-client "https://auth.example.com"))
|
||||
|
||||
;; Authenticate.
|
||||
(multiple-value-bind (token expires-at)
|
||||
(login *client* "alice" "s3cret")
|
||||
(format t "token expires at ~A~%" expires-at))
|
||||
|
||||
;; The token is stored in the client automatically.
|
||||
(let ((accounts (list-accounts *client*)))
|
||||
(format t "~A accounts~%" (length accounts)))
|
||||
|
||||
;; Revoke the token when done.
|
||||
(logout *client*)
|
||||
```
|
||||
|
||||
## Custom CA Certificate
|
||||
|
||||
```lisp
|
||||
(defvar *client*
|
||||
(make-client "https://auth.example.com"
|
||||
:ca-cert "/etc/mcias/ca.pem"))
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
All functions signal typed conditions on error:
|
||||
|
||||
```lisp
|
||||
(handler-case
|
||||
(login *client* "alice" "wrongpass")
|
||||
(mcias-auth-error (e)
|
||||
(format t "auth failed: ~A~%" (mcias-error-message e)))
|
||||
(mcias-forbidden-error (e)
|
||||
(format t "forbidden: ~A~%" (mcias-error-message e)))
|
||||
(mcias-not-found-error (e)
|
||||
(format t "not found: ~A~%" (mcias-error-message e)))
|
||||
(mcias-input-error (e)
|
||||
(format t "bad input: ~A~%" (mcias-error-message e)))
|
||||
(mcias-conflict-error (e)
|
||||
(format t "conflict: ~A~%" (mcias-error-message e)))
|
||||
(mcias-server-error (e)
|
||||
(format t "server error ~A: ~A~%"
|
||||
(mcias-error-status e)
|
||||
(mcias-error-message e))))
|
||||
```
|
||||
|
||||
All condition types are subclasses of `mcias-error`, which has slots:
|
||||
- `mcias-error-status` — HTTP status code (integer)
|
||||
- `mcias-error-message` — server error message (string)
|
||||
|
||||
## `validate-token` Return Value
|
||||
|
||||
`validate-token` returns a property list. The `:valid` key is `T` if the
|
||||
token is valid, `NIL` otherwise (never raises an error for an invalid token):
|
||||
|
||||
```lisp
|
||||
(let ((result (validate-token *client* some-token)))
|
||||
(if (getf result :valid)
|
||||
(format t "valid; sub=~A~%" (getf result :sub))
|
||||
(format t "invalid~%")))
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
```sh
|
||||
sbcl --non-interactive \
|
||||
--eval '(require :asdf)' \
|
||||
--eval "(push #P\"$(pwd)/\" asdf:*central-registry*)" \
|
||||
--eval '(ql:quickload :mcias-client/tests :silent t)' \
|
||||
--eval '(mcias-client-tests:run-all-tests)' \
|
||||
--eval '(uiop:quit)'
|
||||
```
|
||||
Reference in New Issue
Block a user