Unbound Release / Check Preconditions (push) Successful in 26s
Unbound Release / Create Tag (push) Skipped
authz_client / test (push) Skipped
authz_client / vulnerabilities (push) Skipped
pre-commit / pre-commit (push) Skipped
Unbound Release / Generate Changelog and Handle PR (push) Successful in 50s
Unbound Release / Create Release (push) Successful in 29s
Release / release (push) Successful in 5m38s
52 lines
3.0 KiB
Markdown
52 lines
3.0 KiB
Markdown
# authz_client
|
|
|
|
Shared Go library for authorization service client integration.
|
|
|
|
## Shared Documentation
|
|
|
|
@../docs/claude/architecture.md
|
|
@../docs/claude/go-services.md
|
|
@../docs/claude/conventions.md
|
|
|
|
## Library Information
|
|
|
|
### Purpose
|
|
|
|
Provides a client for the authz-service, handling privilege management for users across companies. Used by all microservices that need to check user permissions.
|
|
|
|
### Usage
|
|
|
|
```go
|
|
import client "gitea.unbound.se/shiny/authz_client"
|
|
|
|
// Create handler with options
|
|
handler := client.New(client.WithBaseURL("http://authz-service"))
|
|
|
|
// Check user privileges
|
|
allowed := handler.IsAllowed(email, companyID, func(p client.CompanyPrivileges) bool {
|
|
return p.Invoicing
|
|
})
|
|
```
|
|
|
|
### Privileges
|
|
|
|
The `CompanyPrivileges` struct contains permission flags:
|
|
- `Admin` - Administrative access
|
|
- `Company` - Company management
|
|
- `Consumer` - Consumer/customer access
|
|
- `Time` - Time tracking
|
|
- `Invoicing` - Invoice management
|
|
- `Accounting` - Accounting access
|
|
- `Supplier` - Supplier management
|
|
- `Salary` - Salary/payroll access
|
|
|
|
### Event Handling
|
|
|
|
Registers per-replica (transient) go-messaging-amqp consumers for privilege events from authz-service (`Setup()`). Each routing key gets its own queue, so events arrive in any order. `Process` orders them by the `sequenceNo` authz-service's event store stamps on every event: all four events come from authz-service's `Company` aggregate, so the global sequence number orders them within a company. Each (email, company) keeps the sequence number of the last fact about membership and about each privilege; an event only overrides older facts. `User.Removed` stamps every privilege, so a late grant can't come back. An event without `sequenceNo` fails closed: additions are dropped, and removals apply and block every later event for those facts until the next snapshot. An event with a negative or implausibly large `sequenceNo` is dropped. Tests that seed the handler must set `SequenceNo`.
|
|
|
|
### Startup
|
|
|
|
Call `Fetch()` **after** `conn.Start` (consumers bound), never before: events published between the snapshot and the binding would otherwise be lost. authz-service only serves a snapshot whose read view has applied every stored event. It sends that position in the `X-Authz-Sequence` header and answers 503 while the read view lags (backlog, backfill, reset); `Fetch` retries 503 for about a minute. `Fetch` merges the snapshot as facts at that sequence number (newer event facts win, pairs missing from the snapshot are removed) and drops later events at or below it. A snapshot older than one already merged is ignored, so concurrent or repeated fetches are safe. A snapshot without the header merges at 0 and logs a warning (rollout window only). When the `/authz` contract changes, deploy authz-service before the services that use this library.
|
|
|
|
Don't combine `Setup()` with go-messaging-amqp's `WithReconnect`: a reconnect declares new per-replica queues, so revocations published during the outage are lost unless `Fetch()` runs again. Services exit on connection loss (`CloseListener`) and re-fetch on start.
|