Files
authz_client/CLAUDE.md
T
argoyle 01f30cfd2b
authz_client / test (push) Successful in 58s
Unbound Release / Check Preconditions (push) Successful in 22s
Unbound Release / Create Tag (push) Skipped
authz_client / vulnerabilities (push) Successful in 47s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 32s
Unbound Release / Create Release (push) Successful in 24s
Release / release (push) Successful in 58s
pre-commit / pre-commit (push) Successful in 2m17s
fix: keep existing privileges when User.Added is processed late (#331)
2026-09-16 05:19:03 +00:00

2.0 KiB

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

import client "gitea.unbound.se/shiny/authz_client"

// Create handler with options
handler := client.New(client.WithBaseURL("http://authz-service"))

// Check user privileges
privileges := handler.Get(email, companyID)
if privileges.Invoicing {
    // User has invoicing privileges
}

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 update events from the authz-service (Setup()), keeping the local privilege cache up-to-date. Each routing key gets its own queue, so events for one company arrive in any order — Process must never discard state it didn't itself record. User.Added therefore creates the company entry only when it is missing: a Privilege.Added processed ahead of it would otherwise be overwritten, and nothing re-reads the privilege until the next Fetch(). The converse doesn't hold yet — setPrivileges still creates an entry for a company it never saw a User.Added for, so a Privilege.* delivered after User.Removed resurrects an all-false membership. 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.