feat: migrate IM identity and snowflake primary keys

This commit is contained in:
richjohnrambo
2026-08-18 01:15:21 +08:00
parent db322d80dd
commit bf562c7d56
27 changed files with 1071 additions and 482 deletions
+40 -85
View File
@@ -73,31 +73,47 @@ func replyCreateUser(s *Session, msg *ClientComMessage, rec *auth.Rec) {
user.State = state
}
// Ensure tags are unique and not restricted.
if tags := normalizeTags(msg.Acc.Tags, globals.maxTagCount); tags != nil {
if !restrictedTagsEqual(tags, nil, globals.immutableTagNS) {
logs.Warn.Println("create user: attempt to directly assign restricted tags, sid=", s.sid)
msg := ErrPermissionDenied(msg.Id, "", msg.Timestamp)
msg.Ctrl.Params = map[string]any{"what": "tags"}
s.queueOut(msg)
return
}
user.Tags = tags
if msg.Acc.Tags != nil {
logs.Warn.Println("create user: user tags are no longer supported, sid=", s.sid)
msg := ErrPermissionDenied(msg.Id, "", msg.Timestamp)
msg.Ctrl.Params = map[string]any{"what": "tags"}
s.queueOut(msg)
return
}
// Pre-check credentials for validity. We don't know user's access level
// consequently cannot check presence of required credentials. Must do that later.
creds := normalizeCredentials(msg.Acc.Cred, true)
var registrationPhone string
for i := range creds {
cr := &creds[i]
vld := store.Store.GetValidator(cr.Method)
if _, err := vld.PreCheck(cr.Value, cr.Params); err != nil {
normalized, err := vld.PreCheck(cr.Value, cr.Params)
if err != nil {
logs.Warn.Println("create user: failed credential pre-check", cr, err, "sid=", s.sid)
s.queueOut(decodeStoreError(err, msg.Id, msg.Timestamp,
map[string]any{"what": cr.Method}))
return
}
if cr.Method == "tel" {
phone := strings.TrimSpace(strings.TrimPrefix(normalized, "tel:"))
challengeID, _ := cr.Params["challenge_id"].(string)
if phone == "" || strings.TrimSpace(challengeID) == "" || strings.TrimSpace(cr.Response) == "" ||
(registrationPhone != "" && registrationPhone != phone) {
s.queueOut(decodeStoreError(types.ErrCredentials, msg.Id, msg.Timestamp,
map[string]any{"creds": []string{"tel"}}))
return
}
registrationPhone = phone
}
}
if registrationPhone == "" && store.Verifications != nil {
logs.Warn.Println("create user: verified phone is required, sid=", s.sid)
s.queueOut(decodeStoreError(types.ErrCredentials, msg.Id, msg.Timestamp,
map[string]any{"creds": []string{"tel"}}))
return
}
user.Phone = registrationPhone
// Assign default access values in case the acc creator has not provided them
user.Access.Auth = getDefaultAccess(types.TopicCatP2P, true, false) |
@@ -138,9 +154,9 @@ func replyCreateUser(s *Session, msg *ClientComMessage, rec *auth.Rec) {
return
}
// Add authentication record. The authhdl.AddRecord may change tags.
// Add authentication record.
rec, err := authhdl.AddRecord(authCtx,
&auth.Rec{TenantID: s.tenantID, Uid: user.Uid(), Tags: user.Tags}, msg.Acc.Secret)
&auth.Rec{TenantID: s.tenantID, Uid: user.Uid()}, msg.Acc.Secret)
if err != nil {
logs.Warn.Println("create user: add auth record failed", err, "sid=", s.sid)
s.queueOut(decodeStoreError(err, msg.Id, msg.Timestamp, nil))
@@ -167,14 +183,14 @@ func replyCreateUser(s *Session, msg *ClientComMessage, rec *auth.Rec) {
return
}
// Save credentials, update tags if necessary.
// Save and validate credentials.
tmpToken, _, _ := store.Store.GetLogicalAuthHandler("token").GenSecret(&auth.Rec{
TenantID: s.tenantID,
Uid: user.Uid(),
AuthLevel: auth.LevelAuth,
Lifetime: auth.Duration(time.Hour * 24),
})
validated, _, err := addCreds(s.tenantID, user.Uid(), creds, rec.Tags, s.lang, tmpToken)
validated, _, err := addCreds(s.tenantID, user.Uid(), creds, nil, s.lang, tmpToken)
if err != nil {
logs.Warn.Println("create user: failed to save or validate credential", err, "sid=", s.sid)
s.queueOut(decodeStoreError(err, msg.Id, msg.Timestamp, nil))
@@ -351,17 +367,11 @@ func updateUserAuth(tenantID types.TenantID, msg *ClientComMessage, user *types.
// TODO(gene): support adding new auth schemes
rec, err := authhdl.UpdateRecord(auth.AuthContext{TenantID: tenantID, RemoteAddr: remoteAddr},
&auth.Rec{TenantID: tenantID, Uid: user.Uid(), Tags: user.Tags}, msg.Acc.Secret)
_, err := authhdl.UpdateRecord(auth.AuthContext{TenantID: tenantID, RemoteAddr: remoteAddr},
&auth.Rec{TenantID: tenantID, Uid: user.Uid()}, msg.Acc.Secret)
if err != nil {
return err
}
// Tags may have been changed by authhdl.UpdateRecord, reset them.
// Can't do much with the error here, logging it but not returning.
if _, err = store.Users.UpdateTags(tenantID, user.Uid(), nil, nil, rec.Tags); err != nil {
logs.Warn.Println("updateUserAuth tags update failed:", err)
}
return nil
}
@@ -369,11 +379,8 @@ func updateUserAuth(tenantID types.TenantID, msg *ClientComMessage, user *types.
return types.ErrMalformed
}
// addCreds adds new credentials and re-send validation request for existing ones.
// It also adds credential-defined tags if necessary.
// Returns methods validated in this call only. Returns either a full set of tags
// or nil for tags when tags are unchanged.
func addCreds(tenantID types.TenantID, uid types.Uid, creds []MsgCredClient, extraTags []string,
// addCreds adds credentials and re-sends validation requests for existing ones.
func addCreds(tenantID types.TenantID, uid types.Uid, creds []MsgCredClient, _ []string,
lang string, tmpToken []byte) ([]string, []string, error) {
var validated []string
for i := range creds {
@@ -385,14 +392,11 @@ func addCreds(tenantID types.TenantID, uid types.Uid, creds []MsgCredClient, ext
}
if cr.Response != "" {
if value, ok, err := consumeProductVerificationCred(tenantID, uid, cr, vld.PreCheck); ok || err != nil {
if _, ok, err := consumeProductVerificationCred(tenantID, uid, cr, vld.PreCheck); ok || err != nil {
if err != nil {
return nil, nil, err
}
validated = append(validated, cr.Method)
if globals.validators[cr.Method].addToTags {
extraTags = append(extraTags, cr.Method+":"+value)
}
continue
}
}
@@ -407,24 +411,9 @@ func addCreds(tenantID types.TenantID, uid types.Uid, creds []MsgCredClient, ext
// successfully validated.
validated = append(validated, cr.Method)
// Generate tags for these confirmed credentials.
if globals.validators[cr.Method].addToTags {
extraTags = append(extraTags, cr.Method+":"+cr.Value)
}
}
}
// Save tags potentially changed by the validator.
if len(extraTags) > 0 {
if utags, err := store.Users.UpdateTags(tenantID, uid, extraTags, nil, nil); err == nil {
extraTags = utags
} else {
logs.Warn.Println("add cred tags update failed:", err)
}
} else {
extraTags = nil
}
return validated, extraTags, nil
return validated, nil, nil
}
func consumeProductVerificationCred(tenantID types.TenantID, uid types.Uid, cr *MsgCredClient,
@@ -475,7 +464,6 @@ func consumeProductVerificationCred(tenantID types.TenantID, uid types.Uid, cr *
// validatedCreds returns the list of validated credentials including those validated in this call.
// Returns all validated methods including those validated earlier and now.
// Returns either a full set of tags or nil for tags if tags are unchanged.
func validatedCreds(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, creds []MsgCredClient,
errorOnFail bool) ([]string, []string, error) {
// Check if credential validation is required.
@@ -497,7 +485,6 @@ func validatedCreds(tenantID types.TenantID, uid types.Uid, authLvl auth.Level,
// Add credentials which are validated in this call.
// Unknown validators are removed.
creds = normalizeCredentials(creds, false)
var tagsToAdd []string
for i := range creds {
cr := &creds[i]
if cr.Response == "" {
@@ -506,7 +493,7 @@ func validatedCreds(tenantID types.TenantID, uid types.Uid, authLvl auth.Level,
}
vld := store.Store.GetValidator(cr.Method) // No need to check for nil, unknown methods are removed earlier.
value, err := vld.Check(tenantID, uid, cr.Response)
_, err := vld.Check(tenantID, uid, cr.Response)
if err != nil {
// Check failed.
@@ -525,23 +512,6 @@ func validatedCreds(tenantID types.TenantID, uid types.Uid, authLvl auth.Level,
// Check did not return an error: the request was successfully validated.
methods[cr.Method] = struct{}{}
// Add validated credential to user's tags.
if globals.validators[cr.Method].addToTags {
tagsToAdd = append(tagsToAdd, cr.Method+":"+value)
}
}
var tags []string
if len(tagsToAdd) > 0 {
// Save update to tags
if utags, err := store.Users.UpdateTags(tenantID, uid, tagsToAdd, nil, nil); err == nil {
tags = utags
} else {
logs.Warn.Println("validated creds tags update failed:", err)
tags = nil
}
} else {
tags = nil
}
validated := make([]string, 0, len(methods))
@@ -549,11 +519,10 @@ func validatedCreds(tenantID types.TenantID, uid types.Uid, authLvl auth.Level,
validated = append(validated, method)
}
return validated, tags, nil
return validated, nil, nil
}
// deleteCred deletes user's credential.
// Returns full set of remaining tags or nil if tags are unchanged.
func deleteCred(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, cred *MsgCredClient) ([]string, error) {
vld := store.Store.GetValidator(cred.Method)
if vld == nil || cred.Value == "" {
@@ -601,21 +570,7 @@ func deleteCred(tenantID types.TenantID, uid types.Uid, authLvl auth.Level, cred
return nil, err
}
// Remove generated tags for the deleted credential.
var tags []string
if globals.validators[cred.Method].addToTags {
// This error should not be returned to user.
if utags, err := store.Users.UpdateTags(tenantID, uid, nil, []string{cred.Method + ":" + cred.Value}, nil); err == nil {
tags = utags
} else {
logs.Warn.Println("delete cred: failed to update tags:", err)
tags = nil
}
} else {
tags = nil
}
return tags, nil
return nil, nil
}
// Change user state: suspended/normal (ok).