Import path:
gitlab.soludian.com/soludian/fountain/biz/core
core
import "gitlab.soludian.com/soludian/fountain/biz/core"Index
- func DeregisterCoreController(ctr CoreController)
- func NotifyObservers[T any](event EventType, params ...T)
- func RegisterCoreController(ctr CoreController)
- type CoreController
- type CoreControllerAfterInstalled
- type CoreControllerObserver
- type DeviceCallback
- type EventType
- type FraudGraphCallback
- type SecurityEvaluatorCallback
- type SecurityStoreCallback
- type TwoFACallback
- type UserOAuthCallback
- type UsersCallback
func DeregisterCoreController
func DeregisterCoreController(ctr CoreController)func NotifyObservers
func NotifyObservers[T any](event EventType, params ...T)func RegisterCoreController
func RegisterCoreController(ctr CoreController)RegisterCoreController func;
type CoreController
CoreModel interface
type CoreController interface {
InstallController()
RegisterCallback(cb any)
}func InstallCoreControllers
func InstallCoreControllers(ctrl ...CoreController) []CoreControllerInstallCoreControllers func must be executed after the sql/redis and other dependencies are installed
type CoreControllerAfterInstalled
type CoreControllerAfterInstalled interface {
// For test or auto run
AfterInstalledDone()
}type CoreControllerObserver
type CoreControllerObserver[T any] interface {
Update(event EventType, params ...T)
}type DeviceCallback
DeviceCallback là callback cho domain quản lý device registry của user — lưu trữ thiết bị (DeviceID/DeviceName/Kind/UA/Location) gắn với user.
Tách riêng khỏi UsersCallback vì device registry thường:
- đặt ở bảng/store riêng (vd: Postgres "user_devices" + Redis cache),
- có vòng đời độc lập (user xoá tài khoản chưa chắc lập tức xoá device log; revoke device không revoke user),
- được nhiều domain khác query (audit, anti-fraud, push notification).
Auth controller chỉ gọi qua DeviceCallback khi callback được đăng ký — nếu không, các flow vẫn chạy bình thường, chỉ không có device registry. Đây là callback OPTIONAL (khác UsersCallback bắt buộc cho login flow).
type DeviceCallback interface {
// RegisterDevice ghi nhận thiết bị thuộc về user. Phải IDEMPOTENT: gọi
// lại với cùng (userID, device.ID) chỉ cập nhật metadata
// (LastSeen, UserAgent, Location, ...), không tạo bản ghi trùng. Nếu
// device.ID == "" thì bên triển khai có thể tự sinh hoặc trả
// REQUIRED_FIELD — auth controller không tự sinh DeviceID.
RegisterDevice(userID string, device do.AuthDeviceInfoReq) *froto.RpcError
// UnregisterDevice xoá thiết bị khỏi registry của user. Idempotent:
// thiết bị không tồn tại → no-op (không trả lỗi).
UnregisterDevice(userID, deviceID string) *froto.RpcError
// IsKnownDevice trả về true nếu (userID, deviceID) đã từng được register
// thành công trong registry. Dùng bởi built-in fraud detector trong
// core/auth để phát hiện "new device challenge". userID hoặc deviceID
// rỗng → trả false.
IsKnownDevice(userID, deviceID string) (bool, *froto.RpcError)
}type EventType
-- Observer
type EventType stringtype FraudGraphCallback
FraudGraphCallback là DATA-ACCESS callback cho fraud detection dựa trên graph database (Neo4j, Dgraph, ArangoDB, Memgraph, Apache AGE / Postgres pgRouting, ...). Idea: lưu mỗi login như một edge giữa node User / Device / IP — query fan-out để phát hiện account farming, credential stuffing, NAT abuse, mà các heuristic single-IP / single-device không bắt được.
Schema gợi ý (Cypher-like):
CREATE (u:User {id: $userID})
CREATE (d:Device {id: $deviceID})
CREATE (i:IP {addr: $ip})
CREATE (u)-[:LOGIN {at: $occurredAt, outcome: $outcome}]->(d)
CREATE (u)-[:LOGIN {at: $occurredAt, outcome: $outcome}]->(i)Auth controller ingest qua UpsertLoginEdge sau mỗi login (mọi outcome), built-in detector dùng các Count* để chấm rủi ro fan-out:
┌─────────────────────────────────────┐ Auth Login Flow
│ UpsertLoginEdge(event) │←── ingest async (mọi outcome)
│ CountOtherUsersOnDevice(uid,d,t) │←── built-in: device farm
│ CountOtherUsersFromIP(uid,ip,t) │←── built-in: IP fan-out (NAT abuse)
│ CountDevicesUsedByUser(uid,t) │←── built-in: user device sprawl
└─────────────────────────────────────┘Callback OPTIONAL — không register thì 3 heuristic graph bị bỏ qua, các heuristic khác (brute-force / travel / new-device) vẫn chạy. Các phép đếm CHỈ tính các edge có outcome=success — failed attempts có thể tới từ bot probing và sẽ làm nhiễu signal "user X dùng device Y".
type FraudGraphCallback interface {
// UpsertLoginEdge ghi event lên graph. Auth controller gọi trong goroutine
// riêng (non-blocking trên path login). userID có thể "" khi user không
// tồn tại — implementer vẫn nên tạo node IP/Device để track bot probing.
// Trả lỗi không ảnh hưởng kết quả login đã quyết định — implementer tự
// log lỗi nội bộ.
UpsertLoginEdge(event do.AuthLoginEventDO) *froto.RpcError
// CountOtherUsersOnDevice trả về số userID DISTINCT (KHÁC excludeUserID)
// đã login THÀNH CÔNG trên deviceID trong cửa sổ [since, now].
// excludeUserID="" → đếm tất cả userID. deviceID="" → trả 0.
//
// Threshold cao chỉ ra "device farm" — 1 thiết bị nuôi nhiều account.
CountOtherUsersOnDevice(excludeUserID, deviceID string, since int32) (int, *froto.RpcError)
// CountOtherUsersFromIP tương tự CountOtherUsersOnDevice nhưng theo IP.
// Cần đặt ngưỡng cao hơn nhiều so với device do NAT / proxy / WiFi
// chung dorm gộp hợp lệ rất nhiều user.
CountOtherUsersFromIP(excludeUserID, ip string, since int32) (int, *froto.RpcError)
// CountDevicesUsedByUser trả số device DISTINCT mà userID đã login THÀNH
// CÔNG trong cửa sổ. Cao bất thường = credential stuffing / takeover.
// userID="" → trả 0.
CountDevicesUsedByUser(userID string, since int32) (int, *froto.RpcError)
}type SecurityEvaluatorCallback
SecurityEvaluatorCallback (OPTIONAL) — domain implement khi muốn THAY THẾ logic chấm rủi ro mặc định của core/auth bằng risk engine riêng (rule engine, ML model, external service như reCAPTCHA Enterprise / Auth0 Detection / Castle / Sift).
Khi callback này register, built-in evaluator (kể cả SecurityStoreCallback) bị BỎ QUA cho phần evaluate — nhưng RecordLoginEvent của SecurityStoreCallback vẫn được gọi để duy trì audit log nếu có.
type SecurityEvaluatorCallback interface {
// EvaluateLoginRisk: chấm rủi ro sau khi credential verify nhưng trước
// khi issue token. Trả lỗi → fail-closed (auth deny). Trả Allow → tiếp
// tục issue token. Trả Challenge → auth pass-through (log, caller
// policy tự xử). Trả Deny → auth trả FORBIDDEN với Reason.
EvaluateLoginRisk(attempt do.AuthLoginAttemptDO) (*do.AuthRiskDecisionDO, *froto.RpcError)
}type SecurityStoreCallback
SecurityStoreCallback là DATA-ACCESS callback cho built-in fraud detector trong core/auth. Implementer KHÔNG cần viết logic chấm rủi ro — chỉ cần expose 3 thao tác cơ bản với event store:
┌──────────────────────────────┐ Auth Login Flow
│ RecordLoginEvent(event) │←── append (mọi outcome, sau khi quyết định)
│ CountFailsByIPSince(ip,t) │←── built-in evaluator: brute-force
│ GetLastSuccessByUser(uid) │←── built-in evaluator: impossible travel
└──────────────────────────────┘Callback OPTIONAL — không register thì built-in evaluator được bỏ qua, auth flow chạy mặc định Allow. Nếu cũng KHÔNG có SecurityEvaluatorCallback, auth không block bất cứ login nào dựa trên risk.
type SecurityStoreCallback interface {
// RecordLoginEvent append event vào store. Auth controller gọi trong
// goroutine riêng (non-blocking trên path login). Trả lỗi không ảnh
// hưởng kết quả login — implementer tự log lỗi.
//
// TTL: implementer tự quản lý retention. Tối thiểu phải đủ cho cửa sổ
// brute-force (SecurityFailWindow) + cửa sổ impossible travel
// (SecurityImpossibleTravelGap). Vd: TTL >= 30 phút cho fail event,
// TTL >= 30 ngày cho success event nếu muốn so sánh "last success".
RecordLoginEvent(event do.AuthLoginEventDO) *froto.RpcError
// CountFailsByIPSince trả về số lần fail ghi nhận từ IP này có
// OccurredAt >= since (unix seconds). Built-in evaluator dùng để chặn
// brute-force. ip rỗng → trả 0.
CountFailsByIPSince(ip string, since int32) (int, *froto.RpcError)
// OldestFailByIPSince trả về OccurredAt (unix seconds) của fail event
// CŨ NHẤT trong cửa sổ [since, now] của IP, hoặc 0 nếu không có / ip
// rỗng. Built-in evaluator dùng để tính RetryAfter chính xác cho
// rate-limit response: retry_after = SecurityFailWindow - (now - oldest).
//
// Implementer có thể trả 0 nếu không tracking timestamp riêng — auth
// controller fallback retry-after = SecurityFailWindow (conservative).
OldestFailByIPSince(ip string, since int32) (int32, *froto.RpcError)
// GetLastSuccessByUser trả về event login THÀNH CÔNG gần nhất của user,
// hoặc nil nếu user chưa từng login thành công (bootstrap). Dùng cho
// kiểm tra impossible travel.
GetLastSuccessByUser(userID string) (*do.AuthLoginEventDO, *froto.RpcError)
}type TwoFACallback
TwoFACallback là hợp đồng cho domain phụ trách lưu trữ trạng thái 2FA của user. Tách khỏi UsersCallback để triển khai có thể đặt ở module riêng (ví dụ "users_2fa") mà không buộc UsersCallback phải biết về 2FA.
type TwoFACallback interface {
// Set2FA bật/tắt 2FA và lưu secret. Khi enabled = false, bên triển khai
// nên xoá secret để tránh rò rỉ khi user tắt 2FA.
Set2FA(userID string, enabled bool, secret string) *froto.RpcError
}type UserOAuthCallback
UserOAuthCallback là hợp đồng cho domain quản lý các bản ghi liên kết OAuth (Google/Facebook/...). Tách riêng để cho phép lưu OAuth link ở bảng khác với bảng users chính.
type UserOAuthCallback interface {
LinkOAuth(userID string, link do.OAuthLinkDataInf) *froto.RpcError
UnlinkOAuth(userID, provider string) *froto.RpcError
}type UsersCallback
UsersCallback là hợp đồng giữa auth controller và domain users. Domain users chịu trách nhiệm về persistence, cache và các validation đặc thù (auth chỉ lo định dạng credential, hash password, phát hành token).
Mọi method đều trả về *froto.RpcError nhằm đảm bảo lỗi truyền tải đồng nhất, không phải remap từ error stdlib.
type UsersCallback interface {
GetUserByID(userID string) (do.AuthUserDataInf, *froto.RpcError)
GetUserByEmail(email string) (do.AuthUserDataInf, *froto.RpcError)
GetUserByUsername(username string) (do.AuthUserDataInf, *froto.RpcError)
GetUserByPhone(phone string) (do.AuthUserDataInf, *froto.RpcError)
GetUserByOAuth(provider, providerUserID string) (do.AuthUserDataInf, *froto.RpcError)
CreateUser(data do.AuthUserCreateInf) (do.AuthUserDataInf, *froto.RpcError)
UpdatePassword(userID, hashedPassword string) *froto.RpcError
UpdateLastLogin(userID, deviceIP string, lastTime int32) *froto.RpcError
SetEmailVerified(userID string) *froto.RpcError
SetPhoneVerified(userID string) *froto.RpcError
}Generated by gomarkdoc