go-loose/Makefile
Daniel Noulet c7c81fb65c feat: account menu, database roles, and single logout
Bundles the console work from the current branch: the header account
control, database-backed roles and permissions, and signing out across
the applications that share this sign-in.

The account control replaces a separate identity chip and a separate
sign-out button, which meant the header carried two controls describing
one fact. web/account.js is mounted into every page by a placeholder and
owns the keyboard handling, the outside-click behaviour and the sign-out
form once. It also fills the trigger's name, which the previous markup
left empty.

Signing out revokes the hosted-login bearer sessions this installation
issued, then redirects to the provider's end_session_endpoint when the
discovery document advertises one. Applications that authenticate
against this installation therefore lose their session too; one pointed
straight at an identity provider is out of its reach and is unchanged.

Roles and permissions become rows rather than a closed enum, so a tenant
can extend the catalog without a migration, and a role can no longer be
granted a capability that is not in it. requireSameOrigin now compares
against the origin the request arrived on instead of the configured base
URL, which defaulted to http://localhost:8080 and so rejected every
write in a deployment that never set it.

Two bugs found while verifying in a browser:

  - A profile picture never rendered. avatarURL built an absolute URL
    from the configured base URL, and the page's own img-src 'self'
    blocked it. The console's own pages now take a root-relative path;
    only /connect/userinfo, read from another origin, needs an absolute
    one, and it follows the request.
  - absoluteURL preferred the configuration over the request while its
    comment claimed the opposite, which also misdirected the logout
    redirect.

Card layout: the footer was one two-column row shared between a label
and a forty-character identifier, so the identifier took the width and
crushed its label into three stacked words. Each pair is a line now, the
value truncates with the full text in the title, and cards align their
footers across a row.
2026-10-05 19:18:07 +02:00

79 lines
3.1 KiB
Makefile

SHELL := /bin/sh
GO_LOOSE_ADDR ?= :8081
GO_LOOSE_PORT ?= 8081
GO_LOOSE_BASE_URL ?= http://localhost:$(GO_LOOSE_PORT)
GO_LOOSE_DATABASE_URL ?= postgres://goloose:goloose@localhost:5433/goloose?sslmode=disable
GO_LOOSE_SESSION_SECRET ?= local-development-secret-change-me-now
GO_LOOSE_DEV_LOGIN ?= true
GO_LOOSE_DEV_USER ?= admin@local.test
GO_LOOSE_BOOTSTRAP_TENANT ?= nmbs
GO_LOOSE_BOOTSTRAP_APP ?= guess
GO_LOOSE_ALLOW_PRIVATE_CONTRACT_URLS ?= true
GO_LOOSE_DEV_PORT ?= 8080
export
.PHONY: build test integration-db integration-test run compose-up compose-down compose-dev-up compose-dev-down compose-dev-logs fmt vet
build:
go build ./...
test:
go test -race ./...
# Integration tests drop every table they touch, so they only run against scratch
# databases. Both test helpers refuse any database whose name lacks "test", and
# `make integration-db` creates the two they expect. Override the check with
# GO_LOOSE_TEST_DATABASE_DESTRUCTIVE=1 only when you mean it.
#
# The store-backed tests in internal/server need a database of their own: Go runs
# package tests in parallel and both packages drop every table, so sharing one
# database makes them fight over the schema.
GO_LOOSE_TEST_DATABASE_URL ?= postgres://goloose:goloose@localhost:5433/goloose_test?sslmode=disable
GO_LOOSE_TEST_SERVER_DATABASE_URL ?= postgres://goloose:goloose@localhost:5433/goloose_server_test?sslmode=disable
# createdb is not idempotent, so ignore a database that is already there.
integration-db:
-docker compose exec -T postgres createdb -U goloose goloose_test
-docker compose exec -T postgres createdb -U goloose goloose_server_test
integration-test:
@test -n "$(GO_LOOSE_TEST_DATABASE_URL)" || { echo "set GO_LOOSE_TEST_DATABASE_URL to a scratch database"; exit 1; }
go test -race ./internal/store/ ./internal/server/ -count=1
run:
go run ./cmd/go-loose
# The variables above are exported for `make run`, but compose reads
# GO_LOOSE_BASE_URL too and would inherit the local http://localhost:8081 value,
# which then overrides the https:// defaults in docker-compose.yaml. Clearing it
# for the compose targets lets compose apply its own defaults.
compose-up:
GO_LOOSE_BASE_URL= docker compose up --build
compose-down:
docker compose down
# Air rebuilds into ./dist on every change.
compose-dev-up:
mkdir -p dist
docker compose -f docker-compose.dev.yaml up --build
compose-dev-down:
docker compose -f docker-compose.dev.yaml down --remove-orphans
# Air writes dist/go-loose as root inside the container, so the host user
# cannot delete it directly. Empty the directory through a container instead.
-docker run --rm -v "$(CURDIR)/dist:/dist" alpine sh -c 'rm -rf /dist/*' 2>/dev/null || true
compose-dev-logs:
docker compose -f docker-compose.dev.yaml logs -f go-loose
fmt:
# Prune directories that are not source and may be unreadable: vendored
# code, build output, the PostgreSQL volume, and anything hidden or
# underscore prefixed, which is how Go itself skips package trees.
gofmt -w $$(find . -mindepth 1 -type d \( -name vendor -o -name dist -o -name '.*' -o -name '_*' \) -prune -o -name '*.go' -print)
vet:
go vet ./...