Skip to content

Commit b71e538

Browse files
committed
refactor: add more logic to user fsm
1 parent 3b102af commit b71e538

File tree

8 files changed

+125
-27
lines changed

8 files changed

+125
-27
lines changed

cmd/onex-nightwatch/app/options/options.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ var _ app.CliOptions = (*Options)(nil)
3434
// Options contains everything necessary to create and run a nightwatch server.
3535
type Options struct {
3636
HealthOptions *genericoptions.HealthOptions `json:"health" mapstructure:"health"`
37-
MySQLOptions *genericoptions.MySQLOptions `json:"db" mapstructure:"db"`
37+
MySQLOptions *genericoptions.MySQLOptions `json:"mysql" mapstructure:"mysql"`
3838
RedisOptions *genericoptions.RedisOptions `json:"redis" mapstructure:"redis"`
3939
UserWatcherMaxWorkers int64 `json:"user-watcher-max-workers" mapstructure:"user-watcher-max-workers"`
4040
DisableWatchers []string `json:"disable-watchers" mapstructure:"disable-watchers"`
@@ -63,7 +63,7 @@ func NewOptions() *Options {
6363
// Flags returns flags for a specific server by section name.
6464
func (o *Options) Flags() (fss cliflag.NamedFlagSets) {
6565
o.HealthOptions.AddFlags(fss.FlagSet("health"))
66-
o.MySQLOptions.AddFlags(fss.FlagSet("db"))
66+
o.MySQLOptions.AddFlags(fss.FlagSet("mysql"))
6767
o.RedisOptions.AddFlags(fss.FlagSet("redis"))
6868
o.Metrics.AddFlags(fss.FlagSet("metrics"))
6969
o.Log.AddFlags(fss.FlagSet("log"))

internal/nightwatch/watcher/user/event.go

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/superproj/onex/internal/pkg/client/store"
1010
known "github.com/superproj/onex/internal/pkg/known/usercenter"
1111
"github.com/superproj/onex/internal/pkg/onexx"
12+
"github.com/superproj/onex/internal/usercenter/model"
1213
"github.com/superproj/onex/pkg/log"
1314
)
1415

@@ -21,8 +22,13 @@ func NewActiveUserCallback(store store.Interface) fsm.Callback {
2122
return func(ctx context.Context, event *fsm.Event) {
2223
userM := onexx.FromUserM(ctx)
2324
log.Infow("Now active user", "event", event.Event, "username", userM.Username)
24-
// Fake active user operations.
25-
time.Sleep(5 * time.Second)
25+
26+
// Active secrets if needed.
27+
if err := iterateSecrets(ctx, store, userM.UserID, activeSecret); err != nil {
28+
event.Err = err
29+
return
30+
}
31+
2632
log.Infow("Success to active user", "event", event.Event, "username", userM.Username)
2733
}
2834
}
@@ -32,8 +38,13 @@ func NewDisableUserCallback(store store.Interface) fsm.Callback {
3238
return func(ctx context.Context, event *fsm.Event) {
3339
userM := onexx.FromUserM(ctx)
3440
log.Infow("Now disable user", "event", event.Event, "username", userM.Username)
35-
// Fake disable user operations.
36-
time.Sleep(5 * time.Second)
41+
42+
// Disable secrets if needed.
43+
if err := iterateSecrets(ctx, store, userM.UserID, disableSecret); err != nil {
44+
event.Err = err
45+
return
46+
}
47+
3748
log.Infow("Success to disable user", "event", event.Event, "username", userM.Username)
3849
}
3950
}
@@ -42,9 +53,22 @@ func NewDisableUserCallback(store store.Interface) fsm.Callback {
4253
func NewDeleteUserCallback(store store.Interface) fsm.Callback {
4354
return func(ctx context.Context, event *fsm.Event) {
4455
userM := onexx.FromUserM(ctx)
45-
log.Infow("Now delete user", "event", event.Event, "username", userM.Username)
46-
// Fake delete user operations.
47-
time.Sleep(5 * time.Second)
56+
log.Infow("Now delete user if needed", "event", event.Event, "username", userM.Username)
57+
58+
// If a user remains in an disalbed state for more than 5 years,
59+
// the user should be deleted.
60+
duration := time.Since(userM.UpdatedAt)
61+
if duration.Hours() < 24*365*5 {
62+
return
63+
}
64+
65+
// Delete secrets if needed.
66+
if err := iterateSecrets(ctx, store, userM.UserID, deleteSecret); err != nil {
67+
event.Err = err
68+
return
69+
}
70+
71+
// Save user data for archiving purposes.
4872
log.Infow("Success to delete user", "event", event.Event, "username", userM.Username)
4973
}
5074
}
@@ -79,3 +103,57 @@ func NewUserEventAfterEvent(store store.Interface) fsm.Callback {
79103
}
80104
}
81105
}
106+
107+
// activeSecret used to active user secret.
108+
func activeSecret(ctx context.Context, store store.Interface, secret *model.SecretM) error {
109+
log.Infow("Now actice user secret", "userID", secret.UserID, "secretID", secret.SecretID)
110+
// To avoid unnecessary database update operations, we first check
111+
// whether updating the database is required.
112+
if secret.Status == known.SecretStatusNormal {
113+
return nil
114+
}
115+
secret.Status = known.SecretStatusNormal
116+
return store.UserCenter().Secrets().Update(ctx, secret)
117+
}
118+
119+
// disableSecret used to disable user secret.
120+
func disableSecret(ctx context.Context, store store.Interface, secret *model.SecretM) error {
121+
log.Infow("Now disable user secret", "userID", secret.UserID, "secretID", secret.SecretID)
122+
// To avoid unnecessary database update operations, we first check
123+
// whether updating the database is required.
124+
if secret.Status == known.SecretStatusDisabled {
125+
return nil
126+
}
127+
secret.Status = known.SecretStatusDisabled
128+
return store.UserCenter().Secrets().Update(ctx, secret)
129+
}
130+
131+
// deleteSecret used to delete user secret.
132+
func deleteSecret(ctx context.Context, store store.Interface, secret *model.SecretM) error {
133+
log.Infow("Now delete user secret", "userID", secret.UserID, "secretID", secret.SecretID)
134+
return store.UserCenter().Secrets().Delete(ctx, secret.UserID, secret.Name)
135+
}
136+
137+
// iterateSecrets iterates through the secrets of a user specified by userID
138+
// and calls the action function on each secret.
139+
func iterateSecrets(
140+
ctx context.Context,
141+
store store.Interface,
142+
userID string,
143+
action func(ctx context.Context, store store.Interface, secret *model.SecretM) error,
144+
) error {
145+
// Retrieve the list of secrets for the specified user.
146+
_, secrets, err := store.UserCenter().Secrets().List(ctx, userID)
147+
if err != nil {
148+
return err
149+
}
150+
151+
// Iterate through each secret and perform the action function.
152+
for i := range secrets {
153+
if err := action(ctx, store, secrets[i]); err != nil {
154+
return err
155+
}
156+
}
157+
158+
return nil
159+
}

internal/nightwatch/watcher/user/fsm.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,15 @@ func NewFSM(initial string, w *userWatcher) *fsm.FSM {
2626
return fsm.NewFSM(
2727
initial,
2828
fsm.Events{
29+
// Define status events.
2930
{Name: known.UserStatusRegistered, Src: []string{known.UserStatusRegistered}, Dst: known.UserStatusActive},
3031
{Name: known.UserStatusBlacklisted, Src: []string{known.UserStatusBlacklisted}, Dst: known.UserStatusDisabled},
31-
{Name: known.UserStatusDisabled, Src: []string{known.UserStatusDisabled}, Dst: known.UserStatusDeleted},
32+
// Define need events.
33+
{Name: known.UserStatusNeedActive, Src: []string{known.UserStatusNeedActive}, Dst: known.UserStatusActive},
34+
{Name: known.UserStatusNeedDisable, Src: []string{known.UserStatusNeedDisable}, Dst: known.UserStatusDisabled},
35+
// After disabling the user, they can be deleted, and the FSM will automatically transition to the next deleted state.
36+
// I have decided not to delete the user in the code, so the state transition here is commented out.
37+
// {Name: known.UserStatusDisabled, Src: []string{known.UserStatusDisabled}, Dst: known.UserStatusDeleted},
3238
},
3339
fsm.Callbacks{
3440
known.UserStatusActive: NewActiveUserCallback(w.store),

internal/nightwatch/watcher/user/watcher.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,12 @@ func (w *userWatcher) Run() {
4545
}
4646

4747
allowOperations := []string{
48+
// Need active user.
4849
known.UserStatusRegistered,
50+
// Need disable user.
4951
known.UserStatusBlacklisted,
50-
known.UserStatusDisabled,
52+
known.UserStatusNeedActive,
53+
known.UserStatusNeedDisable,
5154
}
5255

5356
wp := workerpool.New(int(w.maxWorkers))

internal/pkg/known/usercenter/status.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package usercenter
22

3+
// Define user status.
34
const (
45
// User has submitted registration information, the account is in a pending.
56
// The user needs to complete email/phone number verification steps to transition to an active state.
@@ -21,3 +22,19 @@ const (
2122
// The deleted account can be chosen to be soft-deleted (with some data retained) or completely deleted.
2223
UserStatusDeleted = "deleted"
2324
)
25+
26+
// Define need status.
27+
// We can directly update the database to the "Need" state to inform
28+
// onex-nightwatch of what needs to be done.
29+
const (
30+
// UserStatusNeedActive informs onex-nightwatch that the user needs to be activated.
31+
UserStatusNeedActive = "need_active"
32+
// UserStatusNeedDisable informs onex-nightwatch that the user needs to be disabled.
33+
UserStatusNeedDisable = "need_disable"
34+
)
35+
36+
// Define secret status.
37+
const (
38+
SecretStatusDisabled = iota // Status used for disabling a secret.
39+
SecretStatusNormal // Status used for enabling a secret.
40+
)

internal/usercenter/auth/authn.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (a *authnImpl) Verify(accessToken string) (string, error) {
119119
return "", err
120120
}
121121

122-
if secret.Status == model.StatusSecretDisabled {
122+
if secret.Status == known.SecretStatusDisabled {
123123
return "", ErrSecretDisabled
124124
}
125125

internal/usercenter/model/hook.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,6 @@ import (
1515
"github.com/superproj/onex/pkg/authn"
1616
)
1717

18-
// Secret status constants.
19-
const (
20-
StatusSecretDisabled = iota // Status used for disabling a secret.
21-
StatusSecretNormal // Status used for enabling a secret.
22-
)
23-
2418
// BeforeCreate runs before creating a SecretM database record and initializes various fields.
2519
func (s *SecretM) BeforeCreate(tx *gorm.DB) (err error) {
2620
// Supports custom SecretKey, but users need to ensure the uniqueness of the SecretKey themselves.
@@ -34,7 +28,7 @@ func (s *SecretM) BeforeCreate(tx *gorm.DB) (err error) {
3428
s.SecretKey = uuid.New().String()
3529

3630
// Set the default status for the secret as normal.
37-
s.Status = StatusSecretNormal
31+
s.Status = known.SecretStatusNormal
3832

3933
return nil
4034
}

pkg/options/mysql_options.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,20 @@ func (o *MySQLOptions) Validate() []error {
5454

5555
// AddFlags adds flags related to mysql storage for a specific APIServer to the specified FlagSet.
5656
func (o *MySQLOptions) AddFlags(fs *pflag.FlagSet, prefixes ...string) {
57-
fs.StringVar(&o.Host, join(prefixes...)+"db.host", o.Host, ""+
57+
fs.StringVar(&o.Host, join(prefixes...)+"mysql.host", o.Host, ""+
5858
"MySQL service host address. If left blank, the following related mysql options will be ignored.")
59-
fs.StringVar(&o.Username, join(prefixes...)+"db.username", o.Username, "Username for access to mysql service.")
60-
fs.StringVar(&o.Password, join(prefixes...)+"db.password", o.Password, ""+
59+
fs.StringVar(&o.Username, join(prefixes...)+"mysql.username", o.Username, "Username for access to mysql service.")
60+
fs.StringVar(&o.Password, join(prefixes...)+"mysql.password", o.Password, ""+
6161
"Password for access to mysql, should be used pair with password.")
62-
fs.StringVar(&o.Database, join(prefixes...)+"db.database", o.Database, ""+
62+
fs.StringVar(&o.Database, join(prefixes...)+"mysql.database", o.Database, ""+
6363
"Database name for the server to use.")
64-
fs.IntVar(&o.MaxIdleConnections, join(prefixes...)+"db.max-idle-connections", o.MaxOpenConnections, ""+
64+
fs.IntVar(&o.MaxIdleConnections, join(prefixes...)+"mysql.max-idle-connections", o.MaxOpenConnections, ""+
6565
"Maximum idle connections allowed to connect to mysql.")
66-
fs.IntVar(&o.MaxOpenConnections, join(prefixes...)+"db.max-open-connections", o.MaxOpenConnections, ""+
66+
fs.IntVar(&o.MaxOpenConnections, join(prefixes...)+"mysql.max-open-connections", o.MaxOpenConnections, ""+
6767
"Maximum open connections allowed to connect to mysql.")
68-
fs.DurationVar(&o.MaxConnectionLifeTime, join(prefixes...)+"db.max-connection-life-time", o.MaxConnectionLifeTime, ""+
68+
fs.DurationVar(&o.MaxConnectionLifeTime, join(prefixes...)+"mysql.max-connection-life-time", o.MaxConnectionLifeTime, ""+
6969
"Maximum connection life time allowed to connect to mysql.")
70-
fs.IntVar(&o.LogLevel, join(prefixes...)+"db.log-mode", o.LogLevel, ""+
70+
fs.IntVar(&o.LogLevel, join(prefixes...)+"mysql.log-mode", o.LogLevel, ""+
7171
"Specify gorm log level.")
7272
}
7373

0 commit comments

Comments
 (0)