api

package
v0.19.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: AGPL-3.0 Imports: 40 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// Credential failures
	FailureReasonInvalidUsername   = "invalid_username"         // Username not found
	FailureReasonInvalidPassword   = "invalid_password"         // Wrong password
	FailureReasonPasswordChangeReq = "password_change_required" // Initial password not changed

	// Token failures
	FailureReasonTokenInvalid = "token_invalid" // Malformed or unknown token
	FailureReasonTokenExpired = "token_expired" // Token past expiration
	FailureReasonTokenRevoked = "token_revoked" // Token was revoked

	// Account status
	FailureReasonUserDisabled = "user_disabled" // Account disabled by admin
	FailureReasonUserDeleted  = "user_deleted"  // Account was deleted
)

REST API failure reasons

Variables

View Source
var (
	ErrInvalidUID = errors.New("invalid UID")
)

API errors.

View Source
var ErrRequestOutOfScope = errors.New("grant request is out of the definition's scope")

ErrRequestOutOfScope is returned when a pending request no longer matches its definition's scope — typically because an admin tightened the scope after the request was filed. It hard-blocks the approval rather than silently granting access the current policy no longer allows; an admin who still wants to grant it can always create a direct grant.

Functions

This section is empty.

Types

type ChangePasswordRequest

type ChangePasswordRequest struct {
	Username        string `json:"username"`
	CurrentPassword string `json:"current_password" binding:"required"`
	NewPassword     string `json:"new_password" binding:"required"`
}

ChangePasswordRequest represents the request body for authenticated password change Requires re-authentication via username/password (not Bearer token) Username is optional when changing your own password (inferred from :uid param)

type ConnectionInfo added in v0.11.0

type ConnectionInfo struct {
	DatabaseUID  uuid.UUID `json:"database_uid"`
	DatabaseName string    `json:"database_name"`
	Protocol     string    `json:"protocol"`
	Format       string    `json:"format"` // "uri" or "ez-connect"
	URL          string    `json:"url"`
}

ConnectionInfo describes a ready-to-paste connection URL for a single database.

func BuildConnectionURL added in v0.11.0

func BuildConnectionURL(
	db *store.Server,
	user *store.User,
	endpoints store.ResolvedEndpoints,
	apiKey string,
) (ConnectionInfo, bool)

BuildConnectionURL builds a connection URL for the given database, user, and key. When apiKey is "", the placeholder "{DBBAT_KEY}" is substituted in the password slot. Returns (ConnectionInfo{}, false) when the protocol's resolved port is 0.

type ConnectionTestResponse added in v0.18.0

type ConnectionTestResponse struct {
	OK            bool   `json:"ok"`
	Stage         string `json:"stage"`
	Code          string `json:"code"`
	Message       string `json:"message"`
	HostKeyPinned bool   `json:"host_key_pinned,omitempty"`
	KnownHostKey  string `json:"ssh_known_host_key,omitempty"`
	DurationMs    int64  `json:"duration_ms"`
}

ConnectionTestResponse is the API shape of a connectivity check. It mirrors conncheck.Result and carries no secret material — only the stage reached, a machine-readable code, a human-readable message, and the bastion's public host key.

type CreateAPIKeyRequest

type CreateAPIKeyRequest struct {
	Name      string     `json:"name" binding:"required"`
	ExpiresAt *time.Time `json:"expires_at"`
}

CreateAPIKeyRequest represents the request to create an API key

type CreateAPIKeyResponse

type CreateAPIKeyResponse struct {
	ID                   uuid.UUID        `json:"id"`
	Name                 string           `json:"name"`
	Key                  string           `json:"key"` // Only returned once!
	KeyPrefix            string           `json:"key_prefix"`
	ExpiresAt            *time.Time       `json:"expires_at"`
	CreatedAt            time.Time        `json:"created_at"`
	Connections          []ConnectionInfo `json:"connections"`
	ConnectionsTruncated bool             `json:"connections_truncated"`
}

CreateAPIKeyResponse represents the response when creating an API key

type CreateDatabaseRequest

type CreateDatabaseRequest struct {
	Name              string     `json:"name" binding:"required"`
	Description       string     `json:"description"`
	Host              string     `json:"host" binding:"required"`
	Port              int        `json:"port"`
	DatabaseName      string     `json:"database_name"`
	Username          string     `json:"username" binding:"required"`
	Password          string     `json:"password"`
	SSLMode           string     `json:"ssl_mode"`
	Protocol          string     `json:"protocol"`
	OracleServiceName string     `json:"oracle_service_name"`
	MongoAuthSource   string     `json:"mongo_auth_source"`
	Listable          *bool      `json:"listable"`
	ViaUID            *uuid.UUID `json:"via_uid"`
	// SSH bastion secrets (write-only, never returned).
	SSHPrivateKey string `json:"ssh_private_key"`
	SSHPassphrase string `json:"ssh_passphrase"`
	// TestConnection asks the API to validate the row by actually dialing it
	// once created. Opt-in, and never fatal: the outcome comes back as a
	// connection_test object alongside the created server.
	TestConnection bool `json:"test_connection"`
}

CreateDatabaseRequest represents the request to create a database (or, when protocol is "ssh", an SSH bastion). Password is optional for SSH rows that authenticate with a private key.

type CreateGrantDefinitionRequest added in v0.10.0

type CreateGrantDefinitionRequest struct {
	Name                string   `json:"name" binding:"required"`
	Description         string   `json:"description"`
	DurationSeconds     int64    `json:"duration_seconds" binding:"required"`
	Controls            []string `json:"controls"`
	MaxQueryCounts      *int64   `json:"max_query_counts"`
	MaxBytesTransferred *int64   `json:"max_bytes_transferred"`
	// AutoApprove, when true, makes grant requests against this definition
	// skip the pending/admin-approval step and materialize the grant
	// instantly.
	AutoApprove bool `json:"auto_approve"`
	// GroupUIDs restricts the definition to members of these user groups.
	// Empty/omitted = every user, which is how every pre-scoping definition
	// keeps behaving.
	GroupUIDs []uuid.UUID `json:"group_uids"`
	// DatabaseUIDs restricts the definition to these databases.
	// Empty/omitted = every database.
	DatabaseUIDs []uuid.UUID `json:"database_uids"`
}

CreateGrantDefinitionRequest is the JSON body for POST /grant-definitions.

type CreateGrantRequest

type CreateGrantRequest struct {
	UserID              uuid.UUID `json:"user_id" binding:"required"`
	DatabaseID          uuid.UUID `json:"database_id" binding:"required"`
	Controls            []string  `json:"controls"` // Array of controls: read_only, block_copy, block_ddl
	StartsAt            time.Time `json:"starts_at" binding:"required"`
	ExpiresAt           time.Time `json:"expires_at" binding:"required"`
	MaxQueryCounts      *int64    `json:"max_query_counts"`
	MaxBytesTransferred *int64    `json:"max_bytes_transferred"`
}

CreateGrantRequest represents the request to create a grant

type CreateGrantRequestRequest added in v0.10.0

type CreateGrantRequestRequest struct {
	GrantDefinitionID uuid.UUID `json:"grant_definition_id" binding:"required"`
	DatabaseID        uuid.UUID `json:"database_id" binding:"required"`
	Justification     string    `json:"justification"`
}

CreateGrantRequestRequest is the body for POST /grant-requests.

type CreateUserGroupRequest added in v0.18.0

type CreateUserGroupRequest struct {
	Name        string `json:"name" binding:"required"`
	Description string `json:"description"`
	// MemberUIDs, when non-nil, replaces the group's membership. On create
	// it seeds it; on update a nil value leaves membership untouched.
	MemberUIDs []uuid.UUID `json:"member_uids"`
}

CreateUserGroupRequest is the body for POST /user-groups.

type CreateUserRequest

type CreateUserRequest struct {
	Username string   `json:"username" binding:"required"`
	Password string   `json:"password" binding:"required"`
	Roles    []string `json:"roles"`
}

CreateUserRequest represents the request to create a user

type DatabaseLimitedResponse

type DatabaseLimitedResponse struct {
	UID         uuid.UUID `json:"uid"`
	Name        string    `json:"name"`
	Description string    `json:"description"`
}

DatabaseLimitedResponse represents a database with limited info (non-admin)

type DatabaseResponse

type DatabaseResponse struct {
	UID               uuid.UUID  `json:"uid"`
	Name              string     `json:"name"`
	Description       string     `json:"description"`
	Host              string     `json:"host,omitempty"`
	Port              int        `json:"port,omitempty"`
	DatabaseName      string     `json:"database_name,omitempty"`
	Username          string     `json:"username,omitempty"`
	SSLMode           string     `json:"ssl_mode,omitempty"`
	Protocol          string     `json:"protocol,omitempty"`
	OracleServiceName string     `json:"oracle_service_name,omitempty"`
	MongoAuthSource   string     `json:"mongo_auth_source,omitempty"`
	Listable          bool       `json:"listable"`
	CreatedBy         *uuid.UUID `json:"created_by,omitempty"`
	ViaUID            *uuid.UUID `json:"via_uid,omitempty"`
	// SSHKnownHostKey is the TOFU-pinned bastion host key (read-only). Secrets
	// (private key, passphrase) are never returned.
	SSHKnownHostKey string `json:"ssh_known_host_key,omitempty"`
	// ConnectionTest is present only when the request set test_connection.
	ConnectionTest *ConnectionTestResponse `json:"connection_test,omitempty"`
}

DatabaseResponse represents a database with full details (admin only)

type DenyGrantRequestRequest added in v0.10.0

type DenyGrantRequestRequest struct {
	Reason string `json:"reason"`
}

DenyGrantRequestRequest is the body for POST /grant-requests/:uid/deny.

type DeviceAuthorizationRequest added in v0.19.0

type DeviceAuthorizationRequest struct {
	ClientName string `json:"client_name"`
	ClientID   string `json:"client_id"`
}

DeviceAuthorizationRequest is the request body for POST /auth/device. client_name is a dbbat extension used for the consent-page label; client_id is accepted for OAuth compatibility but ignored (dbbat is not a multi-client authorization server).

type DeviceAuthorizationResponse added in v0.19.0

type DeviceAuthorizationResponse struct {
	DeviceCode              string `json:"device_code"`
	UserCode                string `json:"user_code"`
	VerificationURI         string `json:"verification_uri"`
	VerificationURIComplete string `json:"verification_uri_complete"`
	ExpiresIn               int    `json:"expires_in"`
	Interval                int    `json:"interval"`
}

DeviceAuthorizationResponse is the RFC 8628 device authorization response.

type DeviceConsentInfo added in v0.19.0

type DeviceConsentInfo struct {
	ClientName string    `json:"client_name"`
	UserCode   string    `json:"user_code"`
	Status     string    `json:"status"`
	ExpiresAt  time.Time `json:"expires_at"`
}

DeviceConsentInfo is the public detail of a device authorization request, safe to show on the (authenticated) consent page.

type DeviceConsentRequest added in v0.19.0

type DeviceConsentRequest struct {
	UserCode string `json:"user_code" binding:"required"`
	Approve  bool   `json:"approve"`
}

DeviceConsentRequest is the request body for POST /auth/device/consent.

type DeviceTokenRequest added in v0.19.0

type DeviceTokenRequest struct {
	GrantType  string `json:"grant_type" binding:"required"`
	DeviceCode string `json:"device_code" binding:"required"`
	ClientID   string `json:"client_id"`
}

DeviceTokenRequest is the request body for POST /auth/device/token.

type ErrorBody added in v0.4.0

type ErrorBody struct {
	Code       ErrorCode `json:"code"`
	Message    string    `json:"message"`
	Detail     string    `json:"detail,omitempty"`
	RetryAfter int       `json:"retry_after,omitempty"`
}

ErrorBody is the standard error response structure.

type ErrorCode added in v0.4.0

type ErrorCode string

ErrorCode is a machine-readable error code returned in API responses.

const (
	// ErrCodeInternalError indicates an unexpected server error.
	ErrCodeInternalError ErrorCode = "INTERNAL_ERROR"
	// ErrCodeValidationError indicates invalid input.
	ErrCodeValidationError ErrorCode = "VALIDATION_ERROR"
	// ErrCodeNotFound indicates the requested resource was not found.
	ErrCodeNotFound ErrorCode = "NOT_FOUND"
	// ErrCodeUnauthorized indicates authentication is required.
	ErrCodeUnauthorized ErrorCode = "UNAUTHORIZED"
	// ErrCodeForbidden indicates insufficient permissions.
	ErrCodeForbidden ErrorCode = "FORBIDDEN"
	// ErrCodeInvalidCredentials indicates wrong username or password.
	ErrCodeInvalidCredentials ErrorCode = "INVALID_CREDENTIALS"
	// ErrCodePasswordChangeRequired indicates the user must change their password.
	ErrCodePasswordChangeRequired ErrorCode = "PASSWORD_CHANGE_REQUIRED"
	// ErrCodeWeakPassword indicates the password does not meet requirements.
	ErrCodeWeakPassword ErrorCode = "WEAK_PASSWORD"
	// ErrCodeRateLimited indicates too many requests.
	ErrCodeRateLimited ErrorCode = "RATE_LIMITED"
	// ErrCodeConflict indicates a state conflict (e.g. trying to transition
	// a non-pending grant request, or duplicating a unique resource).
	ErrCodeConflict ErrorCode = "CONFLICT"
	// ErrCodeOAuthFailed indicates an OAuth authentication failure.
	ErrCodeOAuthFailed ErrorCode = "OAUTH_FAILED"
	// ErrCodeOAuthStateMismatch indicates an invalid or expired OAuth state.
	ErrCodeOAuthStateMismatch ErrorCode = "OAUTH_STATE_MISMATCH"
	// ErrCodeOAuthProviderError indicates the OAuth provider returned an error.
	ErrCodeOAuthProviderError ErrorCode = "OAUTH_PROVIDER_ERROR"
	// ErrCodeOAuthUserNotLinked indicates no account is linked to the OAuth identity.
	ErrCodeOAuthUserNotLinked ErrorCode = "OAUTH_USER_NOT_LINKED"
	// ErrCodeOAuthWrongWorkspace indicates the wrong OAuth workspace was used.
	ErrCodeOAuthWrongWorkspace ErrorCode = "OAUTH_WRONG_WORKSPACE"
	// ErrCodeDuplicateName indicates a resource with that name already exists.
	ErrCodeDuplicateName ErrorCode = "DUPLICATE_NAME"
	// ErrCodeTargetMatchesSelf indicates the target matches the storage database.
	ErrCodeTargetMatchesSelf ErrorCode = "TARGET_MATCHES_SELF"
	// ErrCodeGrantExpired indicates the access grant has expired.
	ErrCodeGrantExpired ErrorCode = "GRANT_EXPIRED"
	// ErrCodeQuotaExceeded indicates a usage quota was exceeded.
	ErrCodeQuotaExceeded ErrorCode = "QUOTA_EXCEEDED"
)

type LoginRequest

type LoginRequest struct {
	Username string `json:"username" binding:"required"`
	Password string `json:"password" binding:"required"`
}

LoginRequest represents the request body for login

type LoginResponse

type LoginResponse struct {
	Token     string       `json:"token"`
	ExpiresAt string       `json:"expires_at"`
	User      UserResponse `json:"user"`
}

LoginResponse represents the response for a successful login

type MeResponse

type MeResponse struct {
	UID                    string          `json:"uid"`
	Username               string          `json:"username"`
	Roles                  []string        `json:"roles"`
	PasswordChangeRequired bool            `json:"password_change_required"`
	Session                SessionResponse `json:"session"`
}

MeResponse represents the response for /auth/me

type PreLoginPasswordChangeRequest

type PreLoginPasswordChangeRequest struct {
	Username        string `json:"username" binding:"required"`
	CurrentPassword string `json:"current_password" binding:"required"`
	NewPassword     string `json:"new_password" binding:"required"`
}

PreLoginPasswordChangeRequest represents the request body for pre-login password change

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter implements a sliding window rate limiter

func NewRateLimiter

func NewRateLimiter(cfg config.RateLimitConfig) *RateLimiter

NewRateLimiter creates a new rate limiter with the given configuration

func (*RateLimiter) GetStats

func (rl *RateLimiter) GetStats(userID *uuid.UUID, ip string) (int, time.Time)

GetStats returns statistics for a given key (for testing/debugging)

func (*RateLimiter) Middleware

func (rl *RateLimiter) Middleware() gin.HandlerFunc

Middleware returns a Gin middleware for rate limiting

func (*RateLimiter) PostAuthMiddleware

func (rl *RateLimiter) PostAuthMiddleware() gin.HandlerFunc

PostAuthMiddleware is a rate limiter middleware that runs after authentication It uses the authenticated user ID for rate limiting

func (*RateLimiter) PreAuthMiddleware

func (rl *RateLimiter) PreAuthMiddleware() gin.HandlerFunc

PreAuthMiddleware is a rate limiter middleware that runs before authentication It rate limits by IP for unauthenticated requests

type ResetPasswordRequest added in v0.3.0

type ResetPasswordRequest struct {
	NewPassword string `json:"new_password" binding:"required"`
}

ResetPasswordRequest represents the request body for admin password reset

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server represents the REST API server.

func NewServer

func NewServer(dataStore *store.Store, encryptionKey []byte, logger *slog.Logger, cfg *config.Config) *Server

NewServer creates a new API server.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the server.

func (*Server) Start

func (s *Server) Start(addr string) error

Start starts the API server.

type SessionResponse

type SessionResponse struct {
	ExpiresAt string `json:"expires_at"`
	CreatedAt string `json:"created_at"`
}

SessionResponse represents session info in me response

type UpdateDatabaseRequest

type UpdateDatabaseRequest struct {
	Description       *string    `json:"description"`
	Host              *string    `json:"host"`
	Port              *int       `json:"port"`
	DatabaseName      *string    `json:"database_name"`
	Username          *string    `json:"username"`
	Password          *string    `json:"password"`
	SSLMode           *string    `json:"ssl_mode"`
	Protocol          *string    `json:"protocol"`
	OracleServiceName *string    `json:"oracle_service_name"`
	MongoAuthSource   *string    `json:"mongo_auth_source"`
	Listable          *bool      `json:"listable"`
	ViaUID            *uuid.UUID `json:"via_uid"`
	// ClearViaUID, when true, removes the SSH tunnel (direct dial). Distinct
	// from an omitted via_uid, which leaves the tunnel unchanged.
	ClearViaUID bool `json:"clear_via_uid"`
	// SSH bastion secrets (write-only, never returned).
	SSHPrivateKey *string `json:"ssh_private_key"`
	SSHPassphrase *string `json:"ssh_passphrase"`
	// TestConnection asks the API to validate the row by actually dialing it
	// once updated. Opt-in, and never fatal.
	TestConnection bool `json:"test_connection"`
}

UpdateDatabaseRequest represents the request to update a database

type UpdateGrantDefinitionRequest added in v0.10.0

type UpdateGrantDefinitionRequest = CreateGrantDefinitionRequest

UpdateGrantDefinitionRequest is the JSON body for PATCH /grant-definitions/:uid. The shape mirrors the create request — partial updates aren't worth the extra complexity for this small surface.

type UpdateUserGroupRequest added in v0.18.0

type UpdateUserGroupRequest = CreateUserGroupRequest

UpdateUserGroupRequest is the body for PATCH /user-groups/:uid. Same shape as create — this surface is too small to warrant a separate partial type.

type UpdateUserRequest

type UpdateUserRequest struct {
	Password *string  `json:"password"`
	Roles    []string `json:"roles"`
	// GroupUIDs, when non-nil, replaces the user's group memberships
	// wholesale. Admin-only, like Roles.
	GroupUIDs []uuid.UUID `json:"group_uids"`
}

UpdateUserRequest represents the request to update a user

type UserResponse

type UserResponse struct {
	UID                    string   `json:"uid"`
	Username               string   `json:"username"`
	Roles                  []string `json:"roles"`
	PasswordChangeRequired bool     `json:"password_change_required"`
}

UserResponse represents user info in login/me responses

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL