mirror of https://github.com/Xhofe/alist
Merge branch 'main' into feat/allow-edit-role-guest
commit
d5df6fa4cf
|
@ -1,4 +1,4 @@
|
||||||
FROM alpine:edge
|
FROM alpine:3.20.7
|
||||||
|
|
||||||
ARG TARGETPLATFORM
|
ARG TARGETPLATFORM
|
||||||
ARG INSTALL_FFMPEG=false
|
ARG INSTALL_FFMPEG=false
|
||||||
|
|
|
@ -101,6 +101,10 @@ English | [中文](./README_cn.md) | [日本語](./README_ja.md) | [Contributing
|
||||||
|
|
||||||
<https://alistgo.com/>
|
<https://alistgo.com/>
|
||||||
|
|
||||||
|
## API Documentation (via Apifox):
|
||||||
|
|
||||||
|
<https://alist-public.apifox.cn/>
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
<https://al.nn.ci>
|
<https://al.nn.ci>
|
||||||
|
|
|
@ -99,6 +99,10 @@
|
||||||
|
|
||||||
<https://alistgo.com/zh/>
|
<https://alistgo.com/zh/>
|
||||||
|
|
||||||
|
## API 文档(通过 Apifox 提供)
|
||||||
|
|
||||||
|
<https://alist-public.apifox.cn/>
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
<https://al.nn.ci>
|
<https://al.nn.ci>
|
||||||
|
|
|
@ -100,6 +100,10 @@
|
||||||
|
|
||||||
<https://alistgo.com/>
|
<https://alistgo.com/>
|
||||||
|
|
||||||
|
## APIドキュメント(Apifox 提供)
|
||||||
|
|
||||||
|
<https://alist-public.apifox.cn/>
|
||||||
|
|
||||||
## デモ
|
## デモ
|
||||||
|
|
||||||
<https://al.nn.ci>
|
<https://al.nn.ci>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/bootstrap/patch/v3_46_0"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -16,6 +17,12 @@ func Init() {
|
||||||
bootstrap.InitConfig()
|
bootstrap.InitConfig()
|
||||||
bootstrap.Log()
|
bootstrap.Log()
|
||||||
bootstrap.InitDB()
|
bootstrap.InitDB()
|
||||||
|
|
||||||
|
if v3_46_0.IsLegacyRoleDetected() {
|
||||||
|
utils.Log.Warnf("Detected legacy role format, executing ConvertLegacyRoles patch early...")
|
||||||
|
v3_46_0.ConvertLegacyRoles()
|
||||||
|
}
|
||||||
|
|
||||||
data.InitData()
|
data.InitData()
|
||||||
bootstrap.InitStreamLimit()
|
bootstrap.InitStreamLimit()
|
||||||
bootstrap.InitIndex()
|
bootstrap.InitIndex()
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package v3_46_0
|
package v3_46_0
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
"github.com/alist-org/alist/v3/internal/db"
|
"github.com/alist-org/alist/v3/internal/db"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/internal/op"
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
@ -83,47 +86,101 @@ func ConvertLegacyRoles() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
users, _, err := op.GetUsers(1, -1)
|
rawDb := db.GetDb()
|
||||||
|
table := conf.Conf.Database.TablePrefix + "users"
|
||||||
|
rows, err := rawDb.Table(table).Select("id, username, role").Rows()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log.Errorf("[convert roles] failed to get users: %v", err)
|
utils.Log.Errorf("[convert roles] failed to get users: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
for i := range users {
|
var updatedCount int
|
||||||
user := users[i]
|
for rows.Next() {
|
||||||
if user.Role == nil {
|
var id uint
|
||||||
|
var username string
|
||||||
|
var rawRole []byte
|
||||||
|
|
||||||
|
if err := rows.Scan(&id, &username, &rawRole); err != nil {
|
||||||
|
utils.Log.Warnf("[convert roles] skip user scan err: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
changed := false
|
|
||||||
var roles model.Roles
|
utils.Log.Debugf("[convert roles] user: %s raw role: %s", username, string(rawRole))
|
||||||
for _, r := range user.Role {
|
|
||||||
|
if len(rawRole) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldRoles []int
|
||||||
|
wasSingleInt := false
|
||||||
|
if err := json.Unmarshal(rawRole, &oldRoles); err != nil {
|
||||||
|
var single int
|
||||||
|
if err := json.Unmarshal(rawRole, &single); err != nil {
|
||||||
|
utils.Log.Warnf("[convert roles] user %s has invalid role: %s", username, string(rawRole))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
oldRoles = []int{single}
|
||||||
|
wasSingleInt = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var newRoles model.Roles
|
||||||
|
for _, r := range oldRoles {
|
||||||
switch r {
|
switch r {
|
||||||
case model.ADMIN:
|
case model.ADMIN:
|
||||||
roles = append(roles, int(adminRole.ID))
|
newRoles = append(newRoles, int(adminRole.ID))
|
||||||
if int(adminRole.ID) != r {
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
case model.GUEST:
|
case model.GUEST:
|
||||||
roles = append(roles, int(guestRole.ID))
|
newRoles = append(newRoles, int(guestRole.ID))
|
||||||
if int(guestRole.ID) != r {
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
case model.GENERAL:
|
case model.GENERAL:
|
||||||
roles = append(roles, int(generalRole.ID))
|
newRoles = append(newRoles, int(generalRole.ID))
|
||||||
if int(generalRole.ID) != r {
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
roles = append(roles, r)
|
newRoles = append(newRoles, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if changed {
|
|
||||||
user.Role = roles
|
if wasSingleInt {
|
||||||
if err := db.UpdateUser(&user); err != nil {
|
err := rawDb.Table(table).Where("id = ?", id).Update("role", newRoles).Error
|
||||||
utils.Log.Errorf("[convert roles] failed to update user %s: %v", user.Username, err)
|
if err != nil {
|
||||||
|
utils.Log.Errorf("[convert roles] failed to update user %s: %v", username, err)
|
||||||
|
} else {
|
||||||
|
updatedCount++
|
||||||
|
utils.Log.Infof("[convert roles] updated user %s: %v → %v", username, oldRoles, newRoles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.Log.Infof("[convert roles] completed role conversion for %d users", len(users))
|
utils.Log.Infof("[convert roles] completed role conversion for %d users", updatedCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsLegacyRoleDetected() bool {
|
||||||
|
rawDb := db.GetDb()
|
||||||
|
table := conf.Conf.Database.TablePrefix + "users"
|
||||||
|
rows, err := rawDb.Table(table).Select("role").Rows()
|
||||||
|
if err != nil {
|
||||||
|
utils.Log.Errorf("[role check] failed to scan user roles: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var raw sql.RawBytes
|
||||||
|
if err := rows.Scan(&raw); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(raw) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var roles []int
|
||||||
|
if err := json.Unmarshal(raw, &roles); err == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var single int
|
||||||
|
if err := json.Unmarshal(raw, &single); err == nil {
|
||||||
|
utils.Log.Infof("[role check] detected legacy int role: %d", single)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package op
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Xhofe/go-cache"
|
"github.com/Xhofe/go-cache"
|
||||||
|
@ -106,6 +107,20 @@ 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 &&
|
||||||
|
old.PermissionScopes[0].Path != r.PermissionScopes[0].Path {
|
||||||
|
|
||||||
|
oldPath := old.PermissionScopes[0].Path
|
||||||
|
newPath := r.PermissionScopes[0].Path
|
||||||
|
modifiedUsernames, err := db.UpdateUserBasePathPrefix(oldPath, newPath)
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, "failed to update user base path when role updated")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range modifiedUsernames {
|
||||||
|
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)
|
||||||
|
|
|
@ -46,6 +46,11 @@ func GetStorageByMountPath(mountPath string) (driver.Driver, error) {
|
||||||
func CreateStorage(ctx context.Context, storage model.Storage) (uint, error) {
|
func CreateStorage(ctx context.Context, storage model.Storage) (uint, error) {
|
||||||
storage.Modified = time.Now()
|
storage.Modified = time.Now()
|
||||||
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
|
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
|
||||||
|
|
||||||
|
if storage.MountPath == "/" {
|
||||||
|
return 0, errors.New("Mount path cannot be '/'")
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
// check driver first
|
// check driver first
|
||||||
driverName := storage.Driver
|
driverName := storage.Driver
|
||||||
|
@ -205,6 +210,9 @@ func UpdateStorage(ctx context.Context, storage model.Storage) error {
|
||||||
}
|
}
|
||||||
storage.Modified = time.Now()
|
storage.Modified = time.Now()
|
||||||
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
|
storage.MountPath = utils.FixAndCleanPath(storage.MountPath)
|
||||||
|
if storage.MountPath == "/" {
|
||||||
|
return errors.New("Mount path cannot be '/'")
|
||||||
|
}
|
||||||
err = db.UpdateStorage(&storage)
|
err = db.UpdateStorage(&storage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessage(err, "failed update storage in database")
|
return errors.WithMessage(err, "failed update storage in database")
|
||||||
|
|
|
@ -78,7 +78,25 @@ func GetUsers(pageIndex, pageSize int) (users []model.User, count int64, err err
|
||||||
|
|
||||||
func CreateUser(u *model.User) error {
|
func CreateUser(u *model.User) error {
|
||||||
u.BasePath = utils.FixAndCleanPath(u.BasePath)
|
u.BasePath = utils.FixAndCleanPath(u.BasePath)
|
||||||
return db.CreateUser(u)
|
|
||||||
|
err := db.CreateUser(u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := GetRolesByUserID(u.ID)
|
||||||
|
if err == nil {
|
||||||
|
for _, role := range roles {
|
||||||
|
if len(role.PermissionScopes) > 0 {
|
||||||
|
u.BasePath = utils.FixAndCleanPath(role.PermissionScopes[0].Path)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = db.UpdateUser(u)
|
||||||
|
userCache.Del(u.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteUserById(id uint) error {
|
func DeleteUserById(id uint) error {
|
||||||
|
@ -106,6 +124,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 {
|
||||||
|
roles, err := GetRolesByUserID(u.ID)
|
||||||
|
if err == nil {
|
||||||
|
for _, role := range roles {
|
||||||
|
if len(role.PermissionScopes) > 0 {
|
||||||
|
u.BasePath = utils.FixAndCleanPath(role.PermissionScopes[0].Path)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return db.UpdateUser(u)
|
return db.UpdateUser(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package handles
|
package handles
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
@ -60,10 +61,18 @@ func UpdateUser(c *gin.Context) {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//if !utils.SliceEqual(user.Role, req.Role) {
|
|
||||||
// common.ErrorStrResp(c, "role can not be changed", 400)
|
if user.Username == "admin" {
|
||||||
// return
|
if !utils.SliceEqual(user.Role, req.Role) {
|
||||||
//}
|
common.ErrorStrResp(c, "cannot change role of admin user", 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if user.Username != req.Username {
|
||||||
|
common.ErrorStrResp(c, "cannot change username of admin user", 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if req.Password == "" {
|
if req.Password == "" {
|
||||||
req.PwdHash = user.PwdHash
|
req.PwdHash = user.PwdHash
|
||||||
req.Salt = user.Salt
|
req.Salt = user.Salt
|
||||||
|
|
Loading…
Reference in New Issue