auth

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 31, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package auth provides JWT authentication for the SOC HTTP API. Uses HMAC-SHA256 (HS256) with configurable secret. Zero external dependencies — pure Go stdlib.

Index

Constants

View Source
const DemoTenantID = "tnt-demo-000000"

DemoTenantID is the fixed ID for the demo tenant.

View Source
const DemoUserEmail = "demo@syntrex.pro"

DemoUserEmail is the login email for the demo account.

View Source
const DemoUserPassword = "demo"

DemoUserPassword is the demo account password (read-only viewer).

Variables

View Source
var (
	ErrInvalidToken   = errors.New("auth: invalid token")
	ErrExpiredToken   = errors.New("auth: token expired")
	ErrInvalidSecret  = errors.New("auth: secret too short (min 32 bytes)")
	ErrWrongTokenType = errors.New("auth: wrong token type")
)

Standard JWT errors.

View Source
var (
	ErrTenantNotFound = errors.New("auth: tenant not found")
	ErrTenantExists   = errors.New("auth: tenant already exists")
	ErrQuotaExceeded  = errors.New("auth: plan quota exceeded")
)

Standard tenant errors.

View Source
var (
	ErrUserNotFound      = errors.New("auth: user not found")
	ErrUserExists        = errors.New("auth: user already exists")
	ErrInvalidPassword   = errors.New("auth: invalid password")
	ErrUserDisabled      = errors.New("auth: account disabled")
	ErrEmailNotVerified  = errors.New("auth: email not verified")
	ErrInvalidVerifyCode = errors.New("auth: invalid or expired verification code")
)

Standard user errors.

View Source
var DefaultPlans = map[string]Plan{
	"free": {
		ID: "free", Name: "Free",
		Description: "Scanner API — 1 000 сканов/мес, все 66 движков, без SOC Dashboard",
		MaxUsers:    1, MaxEventsMonth: 1000, MaxIncidents: 100, MaxSensors: 1,
		MaxScansMonth: 1000,
		RetentionDays: 3,
		SOCEnabled:    false, SLAEnabled: false, SOAREnabled: false, ComplianceEnabled: false,
		PriceMonthCents: 0,
	},
	"demo": {
		ID: "demo", Name: "Demo Sandbox",
		Description: "Общая демо-песочница. Жёсткий лимит.",
		MaxUsers:    10, MaxEventsMonth: 1000, MaxIncidents: 100, MaxSensors: 5,
		MaxScansMonth: 1000,
		RetentionDays: 1,
		SOCEnabled:    true, SLAEnabled: false, SOAREnabled: false, ComplianceEnabled: false,
		PriceMonthCents: 0,
	},
	"starter": {
		ID: "starter", Name: "Starter",
		Description: "AI-мониторинг: до 5 сенсоров, базовая корреляция и алерты",
		MaxUsers:    10, MaxEventsMonth: 100000, MaxIncidents: 200, MaxSensors: 5,
		MaxScansMonth: 100000,
		RetentionDays: 30,
		SOCEnabled:    true, SLAEnabled: true, SOAREnabled: false, ComplianceEnabled: false,
		PriceMonthCents: 8990000,
	},
	"professional": {
		ID: "professional", Name: "Professional",
		Description: "Полный AI SOC: SOAR, compliance, расширенная аналитика",
		MaxUsers:    50, MaxEventsMonth: 500000, MaxIncidents: 1000, MaxSensors: 25,
		MaxScansMonth: 500000,
		RetentionDays: 90,
		SOCEnabled:    true, SLAEnabled: true, SOAREnabled: true, ComplianceEnabled: true,
		PriceMonthCents: 14990000,
	},
	"enterprise": {
		ID: "enterprise", Name: "Enterprise",
		Description: "On-premise / выделенный инстанс. Сертификация — на стороне заказчика",
		MaxUsers:    -1, MaxEventsMonth: -1, MaxIncidents: -1, MaxSensors: -1,
		MaxScansMonth: -1,
		RetentionDays: 365,
		SOCEnabled:    true, SLAEnabled: true, SOAREnabled: true, ComplianceEnabled: true,
		OnPremise:       true,
		PriceMonthCents: -1,
	},
}

DefaultPlans defines the standard pricing tiers (prices in RUB kopecks).

Functions

func APIKeyMiddleware

func APIKeyMiddleware(store *UserStore, next http.Handler) http.Handler

APIKeyMiddleware checks for API key authentication alongside JWT. If Authorization header starts with "stx_", validate as API key. SEC-CRIT3: Now resolves user from DB to inject correct TenantID.

func HandleBillingStatus

func HandleBillingStatus(tenantStore *TenantStore) http.HandlerFunc

HandleBillingStatus returns the billing status for the tenant. GET /api/auth/billing

func HandleCreateAPIKey

func HandleCreateAPIKey(store *UserStore) http.HandlerFunc

HandleCreateAPIKey generates a new API key for the authenticated user. POST /api/auth/keys

func HandleCreateUser

func HandleCreateUser(store *UserStore) http.HandlerFunc

HandleCreateUser creates a new user (admin only). POST /api/auth/users

func HandleDeleteAPIKey

func HandleDeleteAPIKey(store *UserStore) http.HandlerFunc

HandleDeleteAPIKey revokes an API key. DELETE /api/auth/keys/{id}

func HandleDeleteUser

func HandleDeleteUser(store *UserStore) http.HandlerFunc

HandleDeleteUser deletes a user (admin only). DELETE /api/auth/users/{id}

func HandleDemo

func HandleDemo(userStore *UserStore, tenantStore *TenantStore, secret []byte) http.HandlerFunc

HandleDemo provisions a read-only demo session and logs the user in. GET /api/auth/demo

func HandleGetTenant

func HandleGetTenant(tenantStore *TenantStore) http.HandlerFunc

HandleGetTenant returns the current tenant info. GET /api/auth/tenant

func HandleImpersonateTenant

func HandleImpersonateTenant(tenantStore *TenantStore, jwtSecret []byte) http.HandlerFunc

HandleImpersonateTenant allows a superadmin to switch their working tenant context. POST /api/auth/impersonate

func HandleListAPIKeys

func HandleListAPIKeys(store *UserStore) http.HandlerFunc

HandleListAPIKeys returns API keys for the authenticated user, or all keys for superadmin. GET /api/auth/keys

func HandleListPlans

func HandleListPlans() http.HandlerFunc

HandleListPlans returns all available pricing plans. GET /api/auth/plans

func HandleListTenants

func HandleListTenants(tenantStore *TenantStore) http.HandlerFunc

HandleListTenants returns a list of all tenants for superadmin dashboard. GET /api/auth/tenants

func HandleListUsers

func HandleListUsers(store *UserStore) http.HandlerFunc

HandleListUsers returns users scoped to the caller's tenant (admin only). GET /api/auth/users SEC-HIGH1: Returns empty list when TenantID is empty to prevent cross-tenant leak.

func HandleLogin

func HandleLogin(store *UserStore, secret []byte) http.HandlerFunc

HandleLogin creates an HTTP handler for POST /api/auth/login.

func HandleLogout

func HandleLogout() http.HandlerFunc

HandleLogout clears the auth cookies. POST /api/auth/logout

func HandleMe

func HandleMe(store *UserStore) http.HandlerFunc

HandleMe returns the current authenticated user profile. GET /api/auth/me

func HandleRefresh

func HandleRefresh(secret []byte) http.HandlerFunc

HandleRefresh creates an HTTP handler for POST /api/auth/refresh.

func HandleRegister

func HandleRegister(userStore *UserStore, tenantStore *TenantStore, jwtSecret []byte, emailFn EmailSendFunc) http.HandlerFunc

HandleRegister processes new tenant + owner registration. POST /api/auth/register { email, password, name, org_name, org_slug } Returns verification_required — user must verify email before login. If emailFn is nil, verification code is returned in response (dev mode).

func HandleStripeWebhook

func HandleStripeWebhook(tenantStore *TenantStore) http.HandlerFunc

HandleStripeWebhook processes Stripe webhook events. POST /api/billing/webhook

func HandleUpdateTenantPlan

func HandleUpdateTenantPlan(tenantStore *TenantStore) http.HandlerFunc

HandleUpdateTenantPlan upgrades/downgrades the tenant plan. POST /api/auth/tenant/plan { plan_id } SEC: Only allows downgrade to 'free' without payment. Paid upgrades require Stripe webhook confirmation (HandleStripeWebhook). This prevents users from clicking "Перейти" on paid plans and getting access without payment.

func HandleUpdateUser

func HandleUpdateUser(store *UserStore) http.HandlerFunc

HandleUpdateUser updates a user's profile (admin only). PUT /api/auth/users/{id}

func HandleVerifyEmail

func HandleVerifyEmail(userStore *UserStore, tenantStore *TenantStore, jwtSecret []byte) http.HandlerFunc

HandleVerifyEmail validates the verification code and issues JWT. POST /api/auth/verify { email, code }

func NewAccessToken

func NewAccessToken(subject, role string, secret []byte, ttl time.Duration) (string, error)

NewAccessToken creates a short-lived access token (15 min default).

func NewRefreshToken

func NewRefreshToken(subject, role string, secret []byte, ttl time.Duration) (string, error)

NewRefreshToken creates a long-lived refresh token (7 days default).

func RateLimitMiddleware

func RateLimitMiddleware(rl *RateLimiter, next http.HandlerFunc) http.HandlerFunc

RateLimitMiddleware wraps an http.HandlerFunc with rate limiting.

func SeedDemoTenant

func SeedDemoTenant(userStore *UserStore, tenantStore *TenantStore, socRepo domsoc.SOCRepository)

SeedDemoTenant creates an isolated demo tenant with pre-seeded SOC data. Idempotent: skips if demo user already exists. The demo user has role "viewer" (read-only) and is pre-verified.

func SetClaimsContext

func SetClaimsContext(ctx context.Context, claims *Claims) context.Context

SetClaimsContext injects claims into a context (used by API key auth).

func Sign

func Sign(claims Claims, secret []byte) (string, error)

Sign creates a JWT token string from claims.

Types

type APIKey

type APIKey struct {
	ID        string     `json:"id"`
	UserID    string     `json:"user_id"`
	Name      string     `json:"name"`
	Role      string     `json:"role"`
	KeyPrefix string     `json:"key_prefix"` // first 8 chars for display
	CreatedAt time.Time  `json:"created_at"`
	LastUsed  *time.Time `json:"last_used,omitempty"`
}

APIKey represents an API key for programmatic access.

type Claims

type Claims struct {
	Sub       string `json:"sub"`                  // Subject (username or user ID)
	Role      string `json:"role"`                 // RBAC role: admin, operator, analyst, viewer
	TenantID  string `json:"tenant_id,omitempty"`  // Multi-tenant isolation
	TokenType string `json:"token_type,omitempty"` // "access" or "refresh"
	Exp       int64  `json:"exp"`                  // Expiration (Unix timestamp)
	Iat       int64  `json:"iat"`                  // Issued at
	Iss       string `json:"iss,omitempty"`        // Issuer
}

Claims represents JWT payload.

func GetClaims

func GetClaims(ctx context.Context) *Claims

GetClaims extracts JWT claims from request context.

func Verify

func Verify(tokenStr string, secret []byte) (*Claims, error)

Verify validates a JWT token string and returns the claims.

func (Claims) IsExpired

func (c Claims) IsExpired() bool

IsExpired returns true if the token has expired.

type EmailSendFunc

type EmailSendFunc func(toEmail, userName, code string) error

EmailSendFunc is a callback for sending verification emails. Signature: func(toEmail, userName, code string) error

type JWTMiddleware

type JWTMiddleware struct {

	// PublicPaths are exempt from auth (e.g., /health, /api/auth/login).
	PublicPaths map[string]bool
	// contains filtered or unexported fields
}

JWTMiddleware validates Bearer tokens on protected routes.

func NewJWTMiddleware

func NewJWTMiddleware(secret []byte) *JWTMiddleware

NewJWTMiddleware creates JWT middleware with the given secret.

func (*JWTMiddleware) Middleware

func (m *JWTMiddleware) Middleware(next http.Handler) http.Handler

Middleware wraps an http.Handler with JWT validation.

type LoginRequest

type LoginRequest struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

LoginRequest is the POST /api/auth/login body.

type Plan

type Plan struct {
	ID                string `json:"id"`
	Name              string `json:"name"`
	Description       string `json:"description,omitempty"`
	MaxUsers          int    `json:"max_users"`
	MaxEventsMonth    int    `json:"max_events_month"` // SOC event ingestion quota (-1=unlimited)
	MaxIncidents      int    `json:"max_incidents"`
	MaxSensors        int    `json:"max_sensors"`
	MaxScansMonth     int    `json:"max_scans_month"` // /api/v1/scan quota (-1=unlimited, 0=none)
	RetentionDays     int    `json:"retention_days"`
	SOCEnabled        bool   `json:"soc_enabled"` // SOC Dashboard access
	SLAEnabled        bool   `json:"sla_enabled"`
	SOAREnabled       bool   `json:"soar_enabled"`
	ComplianceEnabled bool   `json:"compliance_enabled"`
	OnPremise         bool   `json:"on_premise"`        // Enterprise: on-premise deployment
	PriceMonthCents   int    `json:"price_month_cents"` // 0 = free, -1 = custom pricing
}

Plan represents a subscription tier with resource limits.

type RateLimiter

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

RateLimiter tracks login attempts per IP using a sliding window.

func NewRateLimiter

func NewRateLimiter(maxHits int, window time.Duration) *RateLimiter

NewRateLimiter creates a rate limiter. maxHits: max attempts per window per IP. window: sliding window duration.

func (*RateLimiter) Allow

func (rl *RateLimiter) Allow(ip string) bool

Allow checks if the IP is within the rate limit. Returns true if allowed, false if rate-limited.

func (*RateLimiter) Reset

func (rl *RateLimiter) Reset(ip string)

Reset clears attempts for an IP (e.g., on successful login).

type Tenant

type Tenant struct {
	ID                string    `json:"id"`
	Name              string    `json:"name"`
	Slug              string    `json:"slug"`
	PlanID            string    `json:"plan_id"`
	PaymentCustomerID string    `json:"payment_customer_id,omitempty"`
	PaymentSubID      string    `json:"payment_sub_id,omitempty"`
	OwnerUserID       string    `json:"owner_user_id"`
	Active            bool      `json:"active"`
	CreatedAt         time.Time `json:"created_at"`
	EventsThisMonth   int       `json:"events_this_month"`
	MonthResetAt      time.Time `json:"month_reset_at"`
}

Tenant represents an isolated organization in the multi-tenant system.

func (*Tenant) CanAccessSOC

func (t *Tenant) CanAccessSOC() bool

CanAccessSOC returns true if the tenant's plan includes SOC Dashboard access.

func (*Tenant) CanIngestEvent

func (t *Tenant) CanIngestEvent() bool

CanIngestEvent checks if the tenant can still ingest events this month.

func (*Tenant) GetPlan

func (t *Tenant) GetPlan() Plan

GetPlan returns the tenant's plan configuration.

func (*Tenant) ScanLimit

func (t *Tenant) ScanLimit() int

ScanLimit returns the monthly scan quota for this tenant (-1=unlimited).

type TenantStore

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

TenantStore manages tenant records backed by SQLite.

func NewTenantStore

func NewTenantStore(db *sql.DB) *TenantStore

NewTenantStore creates a tenant store.

func (*TenantStore) CreateTenant

func (s *TenantStore) CreateTenant(name, slug, ownerUserID, planID string) (*Tenant, error)

CreateTenant creates a new tenant and assigns an owner.

func (*TenantStore) DeactivateTenant

func (s *TenantStore) DeactivateTenant(tenantID string) error

DeactivateTenant marks a tenant as inactive (subscription cancelled).

func (*TenantStore) GetTenant

func (s *TenantStore) GetTenant(id string) (*Tenant, error)

GetTenant returns a tenant by ID.

func (*TenantStore) GetTenantBySlug

func (s *TenantStore) GetTenantBySlug(slug string) (*Tenant, error)

GetTenantBySlug returns a tenant by slug.

func (*TenantStore) IncrementEvents

func (s *TenantStore) IncrementEvents(tenantID string, count int) error

IncrementEvents increments the monthly event counter. Returns error if quota exceeded.

func (*TenantStore) ListTenants

func (s *TenantStore) ListTenants() []*Tenant

ListTenants returns all tenants.

func (*TenantStore) SetStripeIDs

func (s *TenantStore) SetStripeIDs(tenantID, customerID, subID string) error

SetStripeIDs saves Stripe customer + subscription IDs.

func (*TenantStore) UpdatePlan

func (s *TenantStore) UpdatePlan(tenantID, planID string) error

UpdatePlan changes a tenant's plan.

type TokenResponse

type TokenResponse struct {
	CSRFToken string `json:"csrf_token"`
	User      *User  `json:"user"`
}

type UsageInfo

type UsageInfo struct {
	Plan        string    `json:"plan"`
	ScansUsed   int       `json:"scans_used"`
	ScansLimit  int       `json:"scans_limit"`
	Remaining   int       `json:"remaining"`
	PeriodStart time.Time `json:"period_start"`
	PeriodEnd   time.Time `json:"period_end"`
	Unlimited   bool      `json:"unlimited"`
}

UsageInfo represents current usage state for a caller.

type UsageTracker

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

UsageTracker tracks scan usage per user/IP with monthly quotas.

func NewUsageTracker

func NewUsageTracker(db *sql.DB) *UsageTracker

NewUsageTracker creates a usage tracker backed by PostgreSQL.

func (*UsageTracker) GetUsage

func (t *UsageTracker) GetUsage(userID, ip string) *UsageInfo

GetUsage returns current usage for a user or IP.

func (*UsageTracker) RecordScan

func (t *UsageTracker) RecordScan(userID, ip string) (int, error)

RecordScan atomically increments the scan counter and checks quota. Uses the free tier default limit (1000). For plan-aware quotas, use RecordScanWithLimit.

func (*UsageTracker) RecordScanWithLimit

func (t *UsageTracker) RecordScanWithLimit(userID, ip string, planLimit int) (int, error)

RecordScanWithLimit atomically increments the scan counter and checks against planLimit. planLimit: -1=unlimited, 0=no scans allowed, >0=monthly cap. Returns remaining scans. Returns error if quota exceeded.

func (*UsageTracker) ResetExpired

func (t *UsageTracker) ResetExpired()

ResetExpired cleans up old quota records from previous periods.

type User

type User struct {
	ID            string     `json:"id"`
	Email         string     `json:"email"`
	DisplayName   string     `json:"display_name"`
	Role          string     `json:"role"` // admin, analyst, viewer
	TenantID      string     `json:"tenant_id,omitempty"`
	Active        bool       `json:"active"`
	EmailVerified bool       `json:"email_verified"`
	PasswordHash  string     `json:"-"` // never serialized
	VerifyToken   string     `json:"-"` // never serialized
	VerifyExpiry  *time.Time `json:"-"` // never serialized
	CreatedAt     time.Time  `json:"created_at"`
	LastLoginAt   *time.Time `json:"last_login_at,omitempty"`
}

User represents an authenticated user in the system.

type UserStore

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

UserStore manages user credentials backed by SQLite. Falls back to in-memory store if no DB is provided.

func NewUserStore

func NewUserStore(db ...*sql.DB) *UserStore

NewUserStore creates a user store. If db is nil, uses in-memory only.

func (*UserStore) Authenticate

func (s *UserStore) Authenticate(email, password string) (*User, error)

Authenticate validates email/password and returns the user.

func (*UserStore) ChangePassword

func (s *UserStore) ChangePassword(id, newPassword string) error

ChangePassword updates a user's password.

func (*UserStore) CreateAPIKey

func (s *UserStore) CreateAPIKey(userID, name, role string) (string, *APIKey, error)

CreateAPIKey generates a new API key for a user. Returns the full key (only shown once).

func (*UserStore) CreateUser

func (s *UserStore) CreateUser(email, displayName, password, role string) (*User, error)

CreateUser creates a new user with a hashed password.

func (*UserStore) DeleteAPIKey

func (s *UserStore) DeleteAPIKey(keyID, userID string) error

DeleteAPIKey revokes an API key.

func (*UserStore) DeleteUser

func (s *UserStore) DeleteUser(id string) error

DeleteUser permanently removes a user.

func (*UserStore) GetByEmail

func (s *UserStore) GetByEmail(email string) (*User, error)

GetByEmail returns a user by email.

func (*UserStore) GetByID

func (s *UserStore) GetByID(id string) (*User, error)

GetByID returns a user by ID.

func (*UserStore) ListAPIKeys

func (s *UserStore) ListAPIKeys(userID string) ([]APIKey, error)

ListAPIKeys returns all API keys for a user.

func (*UserStore) ListAllAPIKeys

func (s *UserStore) ListAllAPIKeys() ([]APIKey, error)

ListAllAPIKeys returns all API keys across all users (for superadmin).

func (*UserStore) ListUsers

func (s *UserStore) ListUsers() []*User

ListUsers returns all users.

func (*UserStore) SetVerifyToken

func (s *UserStore) SetVerifyToken(email string) (string, error)

SetVerifyToken generates a 6-digit verification code for a user.

func (*UserStore) UpdateUser

func (s *UserStore) UpdateUser(id, displayName, role string, active bool) error

UpdateUser updates a user's display name, role, and active status.

func (*UserStore) ValidateAPIKey

func (s *UserStore) ValidateAPIKey(key string) (string, string, error)

ValidateAPIKey checks an API key and returns the associated role.

func (*UserStore) VerifyEmail

func (s *UserStore) VerifyEmail(email, code string) error

VerifyEmail checks the verification code and marks email as verified.

Jump to

Keyboard shortcuts

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