feat(user): enhance path management and role handling (#9249)

- Add `GetUsersByRole` function for fetching users by role.
- Introduce `GetAllBasePathsFromRoles` to aggregate paths from roles.
- Refine path handling in `pkg/utils/path.go` for normalization.
- Comment out base path prefix updates to simplify role operations.
pull/9265/head v3.49.0
千石 2025-08-06 16:31:36 +08:00 committed by GitHub
parent 85fe4e5bb3
commit 6b2d81eede
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 89 additions and 40 deletions

View File

@ -149,9 +149,21 @@ func (u *User) JoinPath(reqPath string) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
if u.CheckPathLimit() && !utils.IsSubPath(u.BasePath, path) {
if u.CheckPathLimit() {
basePaths := GetAllBasePathsFromRoles(u)
match := false
for _, base := range basePaths {
if utils.IsSubPath(base, path) {
match = true
break
}
}
if !match {
return "", errs.PermissionDenied return "", errs.PermissionDenied
} }
}
return path, nil return path, nil
} }
@ -193,3 +205,22 @@ func (u *User) WebAuthnCredentials() []webauthn.Credential {
func (u *User) WebAuthnIcon() string { func (u *User) WebAuthnIcon() string {
return "https://alistgo.com/logo.svg" return "https://alistgo.com/logo.svg"
} }
// GetAllBasePathsFromRoles returns all permission paths from user's roles
func GetAllBasePathsFromRoles(u *User) []string {
basePaths := make([]string, 0)
seen := make(map[string]struct{})
for _, role := range u.RolesDetail {
for _, entry := range role.PermissionScopes {
if entry.Path == "" {
continue
}
if _, ok := seen[entry.Path]; !ok {
basePaths = append(basePaths, entry.Path)
seen[entry.Path] = struct{}{}
}
}
}
return basePaths
}

View File

@ -2,7 +2,6 @@ package op
import ( import (
"fmt" "fmt"
"github.com/pkg/errors"
"time" "time"
"github.com/Xhofe/go-cache" "github.com/Xhofe/go-cache"
@ -106,26 +105,26 @@ func UpdateRole(r *model.Role) error {
for i := range r.PermissionScopes { for i := range r.PermissionScopes {
r.PermissionScopes[i].Path = utils.FixAndCleanPath(r.PermissionScopes[i].Path) r.PermissionScopes[i].Path = utils.FixAndCleanPath(r.PermissionScopes[i].Path)
} }
if len(old.PermissionScopes) > 0 && len(r.PermissionScopes) > 0 && //if len(old.PermissionScopes) > 0 && len(r.PermissionScopes) > 0 &&
old.PermissionScopes[0].Path != r.PermissionScopes[0].Path { // old.PermissionScopes[0].Path != r.PermissionScopes[0].Path {
//
oldPath := old.PermissionScopes[0].Path // oldPath := old.PermissionScopes[0].Path
newPath := r.PermissionScopes[0].Path // newPath := r.PermissionScopes[0].Path
//
users, err := db.GetUsersByRole(int(r.ID)) // users, err := db.GetUsersByRole(int(r.ID))
if err != nil { // if err != nil {
return errors.WithMessage(err, "failed to get users by role") // return errors.WithMessage(err, "failed to get users by role")
} // }
//
modifiedUsernames, err := db.UpdateUserBasePathPrefix(oldPath, newPath, users) // modifiedUsernames, err := db.UpdateUserBasePathPrefix(oldPath, newPath, users)
if err != nil { // if err != nil {
return errors.WithMessage(err, "failed to update user base path when role updated") // return errors.WithMessage(err, "failed to update user base path when role updated")
} // }
//
for _, name := range modifiedUsernames { // for _, name := range modifiedUsernames {
userCache.Del(name) // userCache.Del(name)
} // }
} //}
roleCache.Del(fmt.Sprint(r.ID)) roleCache.Del(fmt.Sprint(r.ID))
roleCache.Del(r.Name) roleCache.Del(r.Name)
return db.UpdateRole(r) return db.UpdateRole(r)

View File

@ -232,12 +232,20 @@ func UpdateStorage(ctx context.Context, storage model.Storage) error {
roleCache.Del(fmt.Sprint(id)) roleCache.Del(fmt.Sprint(id))
} }
modifiedUsernames, err := db.UpdateUserBasePathPrefix(oldStorage.MountPath, storage.MountPath) //modifiedUsernames, err := db.UpdateUserBasePathPrefix(oldStorage.MountPath, storage.MountPath)
//if err != nil {
// return errors.WithMessage(err, "failed to update user base path")
//}
for _, id := range modifiedRoleIDs {
roleCache.Del(fmt.Sprint(id))
users, err := db.GetUsersByRole(int(id))
if err != nil { if err != nil {
return errors.WithMessage(err, "failed to update user base path") return errors.WithMessage(err, "failed to get users by role")
}
for _, user := range users {
userCache.Del(user.Username)
} }
for _, name := range modifiedUsernames {
userCache.Del(name)
} }
} }
if err != nil { if err != nil {

View File

@ -50,6 +50,10 @@ func GetUserByRole(role int) (*model.User, error) {
return db.GetUserByRole(role) return db.GetUserByRole(role)
} }
func GetUsersByRole(role int) ([]model.User, error) {
return db.GetUsersByRole(role)
}
func GetUserByName(username string) (*model.User, error) { func GetUserByName(username string) (*model.User, error) {
if username == "" { if username == "" {
return nil, errs.EmptyUsername return nil, errs.EmptyUsername
@ -124,17 +128,17 @@ func UpdateUser(u *model.User) error {
} }
userCache.Del(old.Username) userCache.Del(old.Username)
u.BasePath = utils.FixAndCleanPath(u.BasePath) u.BasePath = utils.FixAndCleanPath(u.BasePath)
if len(u.Role) > 0 { //if len(u.Role) > 0 {
roles, err := GetRolesByUserID(u.ID) // roles, err := GetRolesByUserID(u.ID)
if err == nil { // if err == nil {
for _, role := range roles { // for _, role := range roles {
if len(role.PermissionScopes) > 0 { // if len(role.PermissionScopes) > 0 {
u.BasePath = utils.FixAndCleanPath(role.PermissionScopes[0].Path) // u.BasePath = utils.FixAndCleanPath(role.PermissionScopes[0].Path)
break // break
} // }
} // }
} // }
} //}
return db.UpdateUser(u) return db.UpdateUser(u)
} }

View File

@ -88,6 +88,13 @@ func JoinBasePath(basePath, reqPath string) (string, error) {
strings.Contains(reqPath, "/../") { strings.Contains(reqPath, "/../") {
return "", errs.RelativePath return "", errs.RelativePath
} }
reqPath = FixAndCleanPath(reqPath)
if strings.HasPrefix(reqPath, "/") {
return reqPath, nil
}
return stdpath.Join(FixAndCleanPath(basePath), FixAndCleanPath(reqPath)), nil return stdpath.Join(FixAndCleanPath(basePath), FixAndCleanPath(reqPath)), nil
} }