Help us shape the future of Go! Take the 2026 Go Developer Survey

adminapi

package
v0.16.0 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2026 License: MIT Imports: 59 Imported by: 0

Documentation

Overview

Package adminapi implements the panel JSON API mounted at /api/v1/admin: cookie sessions (auth_tokens kind=web) or personal API tokens (Bearer rdp_..., api_tokens.go), CSRF double-submit (X-CSRF-Token) for cookie sessions, TOTP, OIDC panel login and CRUD for users, groups, devices, address books, audit, identity providers, server control and settings. The contract is api/openapi.yaml; errors use httpx.WriteError ({"error":{"code","message"}}).

Index

Constants

View Source
const (
	// SessionCookie holds the opaque session token (HttpOnly, SameSite=Lax,
	// Secure when the public URL is https).
	SessionCookie = "rdpanel_session"
	// CSRFCookie holds the CSRF token readable by the SPA, echoed back in the
	// CSRFHeader on every non-GET request.
	CSRFCookie = "rdpanel_csrf"
	// CSRFHeader is the double-submit header.
	CSRFHeader = "X-CSRF-Token"
)

Session cookie names shared with the web client (web/src/api/client.ts).

View Source
const (
	ActionServerCommand = "server.command" // mutating typed call (reconciler, unblock, ...)
	ActionServerConsole = "server.console" // raw console line
)

Admin-audit actions written for admin-channel calls.

View Source
const (
	LogCaptureFiles = "files" // s6 image: hbbs, hbbr and panel from s6-log dirs
	LogCapturePanel = "panel" // plain `rdpanel serve`: the panel's own lines only
	LogCaptureOff   = "off"   // RDPANEL_LOGS_BUFFER=0
)

Log capture modes reported to the UI.

View Source
const (
	ActionShareLinkCreate = "share_link.create"
	ActionShareLinkRevoke = "share_link.revoke"
)

Admin-audit actions of share links.

View Source
const (
	// SettingShareLinksEnabled turns the feature on. Unset, the configured
	// RDPANEL_SHARE_LINKS_ENABLED decides, so an existing deployment keeps
	// whatever it had until someone touches the switch in the panel.
	SettingShareLinksEnabled = "share_links.enabled"
	SettingSharePageTitle    = "share.page_title"
	SettingShareDescription  = "share.description"
)

Panel settings of the public share page (plain text, rendered escaped).

View Source
const AppName = "RD Panel"

AppName is the product name shown on the About page.

View Source
const DefaultBrandingTitle = "RD Panel"

DefaultBrandingTitle is the panel title when none is configured.

View Source
const LogoPath = "/api/v1/admin/branding/logo"

LogoPath is the public URL of the branding logo (relative to the site root).

View Source
const MFAPath = "/login"

MFAPath is the SPA route that asks for the second factor after an OIDC login of a user with TOTP enabled ("/login?mfa=1&next=...").

View Source
const MinPasswordChars = 8

MinPasswordChars is the minimum length of a password set from the panel.

View Source
const PunchCollectorActor = "punch-collector"

PunchCollectorActor names the system actor of the punch-request collector, whose periodic "pr -" is housekeeping and is not audited.

View Source
const SharePath = "/s"

SharePath is where the public share pages are mounted (<base>/s/<token>).

Variables

This section is empty.

Functions

func ClientServerSettings added in v0.14.0

func ClientServerSettings(cfg *config.Config, r *http.Request) clientdl.ServerSettings

ClientServerSettings returns the ID server, relay, API URL and public key that RustDesk clients use for this server: RDPANEL_PUBLIC_URL (or, unset, the request's host), the relay public host and hbbs' public key file.

func CurrentUser

func CurrentUser(ctx context.Context) *store.User

CurrentUser returns the fully authenticated user of the request, or nil (anonymous or second factor pending).

func DecodeRustdeskConfig added in v0.5.0

func DecodeRustdeskConfig(s string) (host, relay, api, key string, err error)

DecodeRustdeskConfig decodes a RustDesk config string back into its parameters.

func EncodeRustdeskConfig added in v0.5.0

func EncodeRustdeskConfig(host, relay, api, key string) (string, error)

EncodeRustdeskConfig serializes the connection parameters into RustDesk's URL-safe base64 reversed JSON config string format.

func OIDCWebLogin

func OIDCWebLogin(d Deps) oidc.WebLoginFunc

OIDCWebLogin returns the oidc.WebLoginFunc that turns a successful panel OIDC login into a session (pending second factor when TOTP is enabled). Wire it into oidc.Options.WebLogin.

func Register

func Register(r chi.Router, d Deps)

Register mounts the admin API on r (already scoped to /api/v1/admin).

Every route gets the CSRF double-submit check (unsafe methods) and the session loader. Resource routes of other streams must be wrapped in RequireUser or RequireAdmin (see session.go).

func RegisterSharePublic added in v0.14.0

func RegisterSharePublic(r chi.Router, d Deps, webFS fs.FS)

RegisterSharePublic mounts the public share pages at /s on r (the root router, next to /api). webFS is the built SPA (web/dist).

func RequireAdmin

func RequireAdmin(next http.Handler) http.Handler

RequireAdmin is RequireUser plus the admin role.

func RequireUser

func RequireUser(next http.Handler) http.Handler

RequireUser admits fully authenticated users (no pending second factor, no pending forced 2FA enrolment). Mount it on every panel route except the auth and "my security" endpoints.

func SampleOnline

func SampleOnline(ctx context.Context, st *store.Store, p PresenceReader) error

SampleOnline records the current number of online devices into the hourly presence samples (dashboard "online" series). Call it periodically.

func SessionMiddleware

func SessionMiddleware(d Deps) func(http.Handler) http.Handler

SessionMiddleware returns the session loader for routers mounted outside Register (e.g. /api/v1/events). Combine it with RequireUser/RequireAdmin.

func WithSession

func WithSession(ctx context.Context, s *SessionInfo) context.Context

WithSession returns ctx carrying s (tests of other handler packages).

Types

type ChannelStatus

type ChannelStatus struct {
	// Known is false until the channel was probed once.
	Known     bool
	Reachable bool
	Since     time.Time
	RTT       time.Duration
	LastError string
}

ChannelStatus is the state of one hbbs/hbbr loopback admin channel.

type ClientDownloads added in v0.13.0

type ClientDownloads interface {
	Release(ctx context.Context) clientdl.Result
	Open(ctx context.Context, id string) (*os.File, clientdl.Asset, error)
}

ClientDownloads looks up RustDesk releases and keeps verified installer copies (*clientdl.Service).

type CommandAudit

type CommandAudit struct {
	Store *store.Store
	Log   *slog.Logger
}

CommandAudit implements control.AuditSink on admin_audit: mutating typed calls become "server.command" rows and raw console lines "server.console" rows (the console handler writes those itself, with an output excerpt). Read-only typed calls are not persisted (they are logged by control).

func NewCommandAudit

func NewCommandAudit(st *store.Store, log *slog.Logger) *CommandAudit

NewCommandAudit returns the sink to install with control.Client.SetAudit.

func (*CommandAudit) RecordCommand

func (a *CommandAudit) RecordCommand(ctx context.Context, rec control.CommandRecord)

RecordCommand implements control.AuditSink.

type Deps

type Deps struct {
	Log     *slog.Logger
	Config  *config.Config
	Store   *store.Store
	AEAD    *crypto.AEAD
	JWT     *auth.ClientJWT
	Hasher  *auth.Hasher
	Limiter auth.Limiter
	Metrics prometheus.Registerer

	// LDAP manages directory synchronization and corporate authentication (nil: disabled).
	LDAP *ldap.Service
	// WebSessions issues web client session ids bound to panel users (nil: none).
	WebSessions *websessions.Registry
	// OIDC runs provider flows (nil disables OIDC login/linking).
	OIDC *oidc.Service
	// TOTP verifies second factors (default: NewTOTPService(Store, AEAD)).
	TOTP *TOTPService
	// Mail sends e-mail codes and reset links (default: mailauth.New over
	// Store, AEAD and Config).
	Mail *mailauth.Service
	// LoginEvents records login attempts (default: loginlog.DB on Store).
	LoginEvents loginlog.Recorder
	// Telemetry counts login attempts (nil: no metrics).
	Telemetry *metrics.Panel

	// Presence reads live device presence (nil: hbbs integration disabled,
	// presence falls back to "heartbeat within 45 s").
	Presence PresenceReader
	// Servers reports the hbbs/hbbr admin channels (nil: unknown).
	Servers ServerStatusReader
	// Kicks queues heartbeat "disconnect" requests (clientapi.KickQueue).
	Kicks DeviceKicker
	// Events publishes live events such as device.updated (nil: none).
	Events EventPublisher
	// DeviceRate is the effective per-IP limit of the unauthenticated device
	// endpoints (shown read-only in /settings).
	DeviceRate DeviceRateInfo
	// ---- stream B2b: address books, audit, server (hbbs/hbbr) ----
	// PresenceSrc is the live device presence (nil: devices report offline and
	// /server/health reports the heartbeat fallback).
	PresenceSrc PresenceSource
	// Control is the hbbs/hbbr admin channel (nil: hbbs-backed /server/*
	// endpoints answer 503 server_unavailable).
	Control ServerControl
	// Reconciler owns the desired hbbs/hbbr settings (nil: /server/settings 503).
	Reconciler SettingsReconciler
	// Relays manages relays and relay agents (nil: /relays/* answer 503).
	Relays RelayManager
	// Federation manages peering and federated relays (nil: /federation/* answer 503).
	Federation *federation.Service
	// ClientOptions are the client options pushed in heartbeats (default:
	// clientopts.New over Store and AEAD; the app shares one with clientapi).
	ClientOptions *clientopts.Service
	// ConsoleLimit caps raw console commands per admin per minute (default 30).
	ConsoleLimit int
	// Logs is the recent log lines buffer behind /server/logs* (nil or
	// capacity 0: the endpoints report the buffer as disabled).
	Logs *logs.Buffer
	// LogSources are the sources the Logs page always lists (with 0 lines).
	LogSources []string
	// LogCapture is LogCaptureFiles or LogCapturePanel (default).
	LogCapture string

	// ---- About page (GET /about) ----
	// StartedAt is the process start (uptime); zero = when Register ran.
	StartedAt time.Time
	// Schema reports the applied and the newest embedded migration (nil: unknown).
	Schema SchemaInfo
	// Updates looks up the latest release (nil: the update check is disabled).
	Updates UpdateChecker

	// Clients backs the "Install client" page (nil: client downloads are
	// disabled; the page then offers only the settings).
	Clients ClientDownloads
}

Deps are the collaborators the admin API needs.

type DeviceKicker

type DeviceKicker interface {
	Kick(rustdeskID string, connIDs ...int32)
}

DeviceKicker queues a heartbeat "disconnect" for a device (clientapi.KickQueue).

type DeviceRateInfo

type DeviceRateInfo struct {
	Rate  float64
	Burst int
}

DeviceRateInfo is a token bucket: Rate requests per second, bursts of Burst.

type EventPublisher

type EventPublisher interface {
	Publish(t events.Type, data any, aud events.Audience)
}

EventPublisher publishes live events (*events.Bus).

type IdentityDTO

type IdentityDTO struct {
	ID            int64      `json:"id"`
	ProviderID    int64      `json:"provider_id"`
	ProviderSlug  string     `json:"provider_slug"`
	ProviderKind  string     `json:"provider_kind"`
	ProviderName  string     `json:"provider_name"`
	Subject       string     `json:"subject"`
	Email         *string    `json:"email"`
	EmailVerified bool       `json:"email_verified"`
	Username      *string    `json:"username"`
	DisplayName   string     `json:"display_name"`
	AvatarURL     string     `json:"avatar_url"`
	LastLoginAt   *time.Time `json:"last_login_at"`
	CreatedAt     time.Time  `json:"created_at"`
}

IdentityDTO is components.schemas.UserIdentity (a linked IdP identity), used by /me/security/identities and the admin user detail.

func IdentitiesOf

func IdentitiesOf(ctx context.Context, st *store.Store, userID int64) ([]IdentityDTO, error)

IdentitiesOf lists a user's linked identities as API objects (also for the admin users page).

type PasskeyDTO

type PasskeyDTO struct {
	ID             int64      `json:"id"`
	Name           string     `json:"name"`
	CreatedAt      time.Time  `json:"created_at"`
	LastUsedAt     *time.Time `json:"last_used_at"`
	BackupEligible bool       `json:"backup_eligible"`
	Synced         bool       `json:"synced"`
	CloneWarning   bool       `json:"clone_warning"`
	Transports     []string   `json:"transports"`
}

PasskeyDTO is components.schemas.Passkey.

type PresenceHealth

type PresenceHealth struct {
	// Polls counts poll cycles since start (0 = no observation yet).
	Polls     uint64
	Reachable bool
	// Since is when Reachable last changed.
	Since time.Time
	// LastOK is the last successful poll.
	LastOK    time.Time
	RTT       time.Duration
	LastError string
}

PresenceHealth is the presence poller's view of hbbs (rendezvous port).

type PresenceReader

type PresenceReader interface {
	// OnlineIDs returns the RustDesk IDs currently online.
	OnlineIDs() []string
	// DevicePresence returns the state of one device; known is false when the
	// poller has not observed it yet.
	DevicePresence(rustdeskID string) (online bool, since time.Time, known bool)
	PresenceHealth() PresenceHealth
}

PresenceReader exposes live device presence (implemented over hbbs/presence.Poller in internal/app).

type PresenceSource

type PresenceSource interface {
	IsOnline(rustdeskID string) bool
	Health() presence.Health
}

PresenceSource is the live device presence (implemented by *presence.Poller).

type ProviderDTO

type ProviderDTO struct {
	ID                  int64     `json:"id"`
	Slug                string    `json:"slug"`
	Kind                string    `json:"kind"`
	DisplayName         string    `json:"display_name"`
	ClientID            string    `json:"client_id"`
	HasClientSecret     bool      `json:"has_client_secret"`
	Issuer              *string   `json:"issuer"`
	Scopes              []string  `json:"scopes"`
	PKCEEnabled         bool      `json:"pkce_enabled"`
	PKCEMethod          string    `json:"pkce_method"`
	AutoRegister        bool      `json:"auto_register"`
	LinkByVerifiedEmail bool      `json:"link_by_verified_email"`
	Enabled             bool      `json:"enabled"`
	SortOrder           int       `json:"sort_order"`
	CallbackURL         string    `json:"callback_url"`
	CreatedAt           time.Time `json:"created_at"`
	UpdatedAt           time.Time `json:"updated_at"`
}

ProviderDTO is components.schemas.IdentityProvider. The client secret is write-only: only has_client_secret is returned.

type RelayManager added in v0.3.0

type RelayManager interface {
	List(ctx context.Context) ([]relays.View, relays.Rotation, error)
	Get(ctx context.Context, id int64) (relays.View, error)
	Observed(ctx context.Context) ([]string, error)
	Create(ctx context.Context, in relays.CreateInput) (relays.View, *relays.Enrollment, error)
	Update(ctx context.Context, id int64, p relays.Patch) (relays.View, error)
	Delete(ctx context.Context, id int64) error
	ResetEnrollment(ctx context.Context, id int64) (*relays.Enrollment, error)
	Approve(ctx context.Context, id int64, by *int64) (relays.View, error)
	Revoke(ctx context.Context, id int64) (relays.View, error)
	ProbeNow(ctx context.Context, id int64) (relays.View, error)
	Settings(ctx context.Context, id int64) (relays.SettingsView, error)
	SaveSettings(ctx context.Context, id int64, ov reconcile.Settings, by *int64) (relays.SettingsView, error)
	Usage(ctx context.Context, id int64) ([]control.RelayUsage, error)
	Traffic(ctx context.Context, id int64, since time.Time) ([]relays.TrafficSample, error)
	GetOptions(ctx context.Context) (relays.Options, error)
	SetOptions(ctx context.Context, o relays.Options, by *int64) error
	HBBSKeyFingerprint() string
	ListPrefixRules(ctx context.Context) ([]relays.PrefixRule, error)
	CreatePrefixRule(ctx context.Context, rule *relays.PrefixRule) error
	DeletePrefixRule(ctx context.Context, id int64) error
	ImportCountryRules(ctx context.Context, countryCode, relayHost, customComment string) (*relays.ImportCountryRulesResult, error)
	GeoIP() *relays.GeoIPEngine
}

RelayManager is implemented by *relays.Service.

type SchemaInfo

type SchemaInfo interface {
	SchemaVersion(ctx context.Context) (int64, error)
	LatestSchemaVersion() (int64, error)
}

SchemaInfo reports the database schema version (implemented by *db.DB).

type ServerControl

type ServerControl interface {
	Addr(t control.Target) string
	ExecLine(ctx context.Context, t control.Target, line string) (string, error)
	IPBlocker(ctx context.Context, start int) (control.IPBlockerPage, error)
	IPBlockerLookup(ctx context.Context, ip string, remove bool) (*control.IPBlockerEntry, error)
	IPChanges(ctx context.Context, start int) (control.IPChangesPage, error)
	IPChangesLookup(ctx context.Context, id string, remove bool) (*control.IPChange, error)
	Usage(ctx context.Context) ([]control.RelayUsage, error)
}

ServerControl is the hbbs/hbbr admin channel (implemented by *control.Client).

type ServerHealthDTO

type ServerHealthDTO struct {
	HBBS           componentHealthDTO `json:"hbbs"`
	HBBR           componentHealthDTO `json:"hbbr"`
	Presence       presenceHealthDTO  `json:"presence"`
	SettingsInSync *bool              `json:"settings_in_sync,omitempty"`
}

ServerHealthDTO is components.schemas.ServerHealth.

type ServerStatusReader

type ServerStatusReader interface {
	ChannelStatus() (hbbs, hbbr ChannelStatus)
	// SettingsInSync reports whether every desired setting was applied.
	SettingsInSync(ctx context.Context) (bool, error)
}

ServerStatusReader exposes the reconciler's view of hbbs/hbbr.

type SessionInfo

type SessionInfo struct {
	Token *store.AuthToken
	User  *store.User
	// MFAPending: password accepted, TOTP/recovery code still owed.
	MFAPending bool
	// TOTPEnabled: the user has confirmed TOTP.
	TOTPEnabled bool
	// MustEnrollTOTP: policy requires 2FA but the user has not enrolled; only
	// the auth and "my security" endpoints are reachable.
	MustEnrollTOTP bool
	// APIToken is set when the request authenticated with a personal API
	// token (Authorization: Bearer rdp_...) instead of the session cookie.
	// Token is then a placeholder with ID 0 that matches no auth_tokens row.
	APIToken *store.APIToken
}

SessionInfo describes the authenticated panel session of a request.

func CurrentSession

func CurrentSession(ctx context.Context) *SessionInfo

CurrentSession returns the request's session (nil when anonymous).

type SettingsReconciler

type SettingsReconciler interface {
	Status() reconcile.Status
	Desired(ctx context.Context) (reconcile.Settings, error)
	KeyStatus(ctx context.Context) ([]reconcile.KeyStatus, error)
	Current(ctx context.Context) (reconcile.Observed, error)
	Apply(ctx context.Context, s reconcile.Settings, updatedBy *int64) error
	Reconcile(ctx context.Context) error
}

SettingsReconciler owns the desired hbbs/hbbr settings (implemented by *reconcile.Reconciler).

type TOTPService

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

TOTPService verifies and manages TOTP second factors and recovery codes. It satisfies the client API's TOTPVerifier (POST /api/login tfa step):

Enrolled(ctx, userID uint32) (bool, error)
Verify(ctx, userID uint32, code string) (bool, error)

func NewTOTPService

func NewTOTPService(st *store.Store, aead *crypto.AEAD) *TOTPService

NewTOTPService returns a TOTPService.

func (*TOTPService) Enrolled

func (t *TOTPService) Enrolled(ctx context.Context, userID uint32) (bool, error)

Enrolled reports whether the user has confirmed TOTP (2FA active).

func (*TOTPService) ResetUser

func (t *TOTPService) ResetUser(ctx context.Context, userID int64) error

ResetUser removes a user's 2FA (admin "force 2FA reset"; for stream B2/CLI).

func (*TOTPService) Verify

func (t *TOTPService) Verify(ctx context.Context, userID uint32, code string) (bool, error)

Verify checks a 6-digit TOTP code (with replay protection) or, for any other input, a one-time recovery code (consumed on success). It returns false without error for wrong codes and for users without 2FA.

type UpdateChecker

type UpdateChecker interface {
	Latest(ctx context.Context) updatecheck.Result
}

UpdateChecker returns the cached latest release (*updatecheck.Checker).

type UserDTO

type UserDTO struct {
	ID              int64      `json:"id"`
	Username        string     `json:"username"`
	Email           *string    `json:"email"`
	DisplayName     string     `json:"display_name"`
	AvatarURL       string     `json:"avatar_url"`
	Note            string     `json:"note"`
	Role            store.Role `json:"role"`
	Status          string     `json:"status"`
	UserGroupID     *int64     `json:"user_group_id"`
	HasPassword     bool       `json:"has_password"`
	TOTPEnabled     bool       `json:"totp_enabled"`
	TOTPRequired    bool       `json:"totp_required"`
	EmailVerified   bool       `json:"email_verified"`
	Email2FAEnabled bool       `json:"email_2fa_enabled"`
	IsLDAP          bool       `json:"is_ldap"`
	LastLoginAt     *time.Time `json:"last_login_at"`
	CreatedAt       time.Time  `json:"created_at"`
	UpdatedAt       time.Time  `json:"updated_at"`
}

UserDTO is the API representation of a user (components.schemas.User).

func NewUserDTO

func NewUserDTO(u *store.User, totpEnabled bool) UserDTO

NewUserDTO converts a user row.

Jump to

Keyboard shortcuts

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