Let a signed-in user turn their own machine into a worker without the shared token. The coordinator already binds a JWT-authenticated registration to owner_id as untrusted; this adds the missing pieces. userservice: long-lived worker keys (scimesh_wk_live_*, hash-at-rest) with create/list/revoke and a public /worker-tokens/exchange that trades a key for a short-lived JWT carrying the owner current role/verified. python worker: SCIMESH_WORKER_KEY + SCIMESH_USERSERVICE_URL; a token provider exchanges the key and refreshes the JWT proactively and on 401, so a long-running worker survives token expiry. Static bearer token path is unchanged. coordinator UI: an "add your machine" page that mints a key and shows a ready-to-run command, proxying key management to the userservice; the dashboard gains an owner-scoped "my machines" section. docs: how to run a worker from your account, plus the untrusted/quorum/ verified trust model.
68 lines
2.8 KiB
Go
68 lines
2.8 KiB
Go
// Package usecase holds the application logic — registration and login — plus
|
|
// the ports (interfaces) it depends on. The concrete adapters (PostgreSQL,
|
|
// bcrypt, JWT) are injected from cmd, so this package never imports them.
|
|
package usecase
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/emil28092005/SciMesh/users/internal/domain"
|
|
)
|
|
|
|
// UserRepository persists and looks up users. Implementations return the
|
|
// sentinel errors in errors.go so the use cases can react without knowing about
|
|
// SQL or driver types.
|
|
type UserRepository interface {
|
|
// Insert stores a new user, returning ErrEmailExists if the email is taken.
|
|
Insert(ctx context.Context, u *domain.User) error
|
|
// GetByEmail returns the user with the (normalised) email, or ErrUserNotFound.
|
|
GetByEmail(ctx context.Context, email string) (*domain.User, error)
|
|
// GetByID returns the user with id, or ErrUserNotFound.
|
|
GetByID(ctx context.Context, id uuid.UUID) (*domain.User, error)
|
|
// SetVerified toggles the verified flag, returning ErrUserNotFound if no
|
|
// such user exists.
|
|
SetVerified(ctx context.Context, id uuid.UUID, verified bool) error
|
|
// SetRole changes a user's role, returning ErrUserNotFound if no such user
|
|
// exists.
|
|
SetRole(ctx context.Context, id uuid.UUID, role domain.Role) error
|
|
}
|
|
|
|
// WorkerKeyRepository persists and looks up the long-lived worker keys a user
|
|
// creates to run a worker bound to their account. Implementations return the
|
|
// sentinel errors in errors.go so the use cases stay free of SQL types.
|
|
type WorkerKeyRepository interface {
|
|
// Insert stores a freshly minted key.
|
|
Insert(ctx context.Context, k *domain.WorkerKey) error
|
|
// ListByUser returns a user's live (non-revoked) keys, newest first.
|
|
ListByUser(ctx context.Context, userID uuid.UUID) ([]*domain.WorkerKey, error)
|
|
// GetActiveByHash returns the non-revoked key with the given hash, or
|
|
// ErrWorkerKeyNotFound.
|
|
GetActiveByHash(ctx context.Context, tokenHash string) (*domain.WorkerKey, error)
|
|
// Revoke retires a key the user owns, returning ErrWorkerKeyNotFound when no
|
|
// live key with that id belongs to the user.
|
|
Revoke(ctx context.Context, id, userID uuid.UUID) error
|
|
// TouchLastUsed records a successful exchange. Best-effort: a failure here
|
|
// must not fail the exchange itself.
|
|
TouchLastUsed(ctx context.Context, id uuid.UUID) error
|
|
}
|
|
|
|
// PasswordHasher hashes and verifies passwords. The bcrypt adapter satisfies it.
|
|
type PasswordHasher interface {
|
|
Hash(password string) (string, error)
|
|
Compare(hash, password string) error
|
|
}
|
|
|
|
// TokenIssuer mints a signed access token for an authenticated user. It takes
|
|
// the whole user so trust-bearing claims (role, verified) travel in the token.
|
|
type TokenIssuer interface {
|
|
Issue(u *domain.User) (string, error)
|
|
}
|
|
|
|
// Clock reads the current time; a fake one makes tests deterministic.
|
|
type Clock interface {
|
|
Now() time.Time
|
|
}
|