-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathuser_back.go
More file actions
155 lines (140 loc) · 3.78 KB
/
user_back.go
File metadata and controls
155 lines (140 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//go:build !wasm
package user
import (
"sync"
"time"
"github.com/tinywasm/orm"
)
// Module is the user/auth/rbac handle. All backend operations are methods on this type.
// Created exclusively via New().
type Module struct {
db *orm.DB
cache *sessionCache
ucache *userCache
config Config
log func(...any)
providers map[string]OAuthProvider
providersMu sync.RWMutex
mu sync.RWMutex
}
// New initializes the user/rbac schema, warms the cache, and returns a Module handle.
// This is the ONLY entry point for this package on the backend.
func New(db *orm.DB, cfg Config) (*Module, error) {
if cfg.CookieName == "" {
cfg.CookieName = "session"
}
if cfg.TokenTTL == 0 {
cfg.TokenTTL = 86400
}
m := &Module{
db: db,
cache: newSessionCache(),
ucache: newUserCache(),
config: cfg,
providers: make(map[string]OAuthProvider),
}
if err := initSchema(db, cfg.AuthMode); err != nil {
return nil, err
}
for _, p := range cfg.OAuthProviders {
m.registerProvider(p)
}
if cfg.AuthMode == AuthModeCookie {
if err := m.cache.warmUp(db); err != nil {
return nil, err
}
}
return m, nil
}
// SetLog configures optional logging. Call immediately after New().
// Default: no-op. Follows the tinywasm ecosystem SetLog convention (same as rbac).
//
// Example:
//
// m.SetLog(func(msg ...any) { log.Println(msg...) })
func (m *Module) SetLog(fn func(...any)) {
m.log = fn
}
func (m *Module) notify(e SecurityEvent) {
if e.Timestamp == 0 {
e.Timestamp = time.Now().Unix()
}
if m.config.OnSecurityEvent != nil {
m.config.OnSecurityEvent(e)
return
}
if m.log != nil {
m.log("security_event", e.Type, e.IP, e.UserID)
}
}
// SuspendUser sets Status = "suspended". Evicts user from cache.
func (m *Module) SuspendUser(id string) error {
return suspendUser(m.db, m.ucache, id)
}
// ReactivateUser sets Status = "active". Evicts user from cache.
func (m *Module) ReactivateUser(id string) error {
return reactivateUser(m.db, m.ucache, id)
}
// PurgeSessionsByUser deletes all sessions belonging to userID from cache and DB.
func (m *Module) PurgeSessionsByUser(userID string) error {
// First from DB
qb := m.db.Query(&Session{}).Where(Session_.UserID).Eq(userID)
sessions, err := ReadAllSession(qb)
if err != nil {
return err
}
for _, s := range sessions {
m.db.Delete(s, orm.Eq(Session_.ID, s.ID))
m.cache.delete(s.ID)
}
return nil
}
func (m *Module) registerProvider(p OAuthProvider) {
m.providersMu.Lock()
defer m.providersMu.Unlock()
m.providers[p.Name()] = p
}
func (m *Module) getProvider(name string) OAuthProvider {
m.providersMu.RLock()
defer m.providersMu.RUnlock()
return m.providers[name]
}
func (m *Module) registeredProviders() []OAuthProvider {
m.providersMu.RLock()
defer m.providersMu.RUnlock()
var list []OAuthProvider
for _, p := range m.providers {
list = append(list, p)
}
return list
}
// Add returns all admin-managed CRUDP handlers for registration.
// The concrete types are private — pass directly to crudp.RegisterHandlers.
//
// Usage: cp.RegisterHandlers(m.Add()...)
func (m *Module) Add() []any {
return []any{
&userCRUD{db: m.db, cache: m.ucache},
&roleCRUD{m: m},
&permissionCRUD{m: m},
&lanipCRUD{m: m},
}
}
// UIModules returns all standard authentication UI flow handlers bound to this module.
// Isomorphic: The signature exists in both WASM and backend. On the backend, it links to the DB.
func (m *Module) UIModules() []any {
for _, mod := range uiModules {
if lm, ok := mod.(*loginModule); ok {
lm.m = m
} else if rm, ok := mod.(*registerModule); ok {
rm.m = m
} else if pm, ok := mod.(*profileModule); ok {
pm.m = m
} else if lam, ok := mod.(*lanModule); ok {
lam.m = m
} else if om, ok := mod.(*oauthModule); ok {
om.m = m
}
}
return uiModules
}