Feat: support guess user

pull/134/head
zijiren233 2 years ago
parent cc14366af1
commit 01c087e60e

@ -25,6 +25,10 @@ func Init(d *gorm.DB, t conf.DatabaseType) error {
if err != nil { if err != nil {
return err return err
} }
err = initGuestUser()
if err != nil {
return err
}
return initRootUser() return initRootUser()
} }
@ -39,6 +43,22 @@ func initRootUser() error {
return err return err
} }
const (
GuestUsername = "guest"
GuestUserID = "00000000000000000000000000000001"
)
func initGuestUser() error {
user := model.User{}
err := db.Where("id = ?", GuestUserID).First(&user).Error
if err == nil || !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
u, err := CreateUser("guest", utils.RandString(32), WithRole(model.RoleUser), WithID(GuestUserID))
log.Infof("init guest user:\nid: %s\nusername: %s", u.ID, u.Username)
return err
}
func DB() *gorm.DB { func DB() *gorm.DB {
return db return db
} }

@ -50,7 +50,7 @@ func FirstOrCreateRoomMemberRelation(roomID, userID string, conf ...CreateRoomMe
return roomMemberRelation, err return roomMemberRelation, err
} }
func GetRoomMemberRelation(roomID, userID string) (*model.RoomMember, error) { func GetRoomMember(roomID, userID string) (*model.RoomMember, error) {
roomMemberRelation := &model.RoomMember{} roomMemberRelation := &model.RoomMember{}
err := db.Where("room_id = ? AND user_id = ?", roomID, userID).First(roomMemberRelation).Error err := db.Where("room_id = ? AND user_id = ?", roomID, userID).First(roomMemberRelation).Error
return roomMemberRelation, HandleNotFound(err, "room or user") return roomMemberRelation, HandleNotFound(err, "room or user")

@ -15,6 +15,12 @@ import (
type CreateUserConfig func(u *model.User) type CreateUserConfig func(u *model.User)
func WithID(id string) CreateUserConfig {
return func(u *model.User) {
u.ID = id
}
}
func WithRole(role model.Role) CreateUserConfig { func WithRole(role model.Role) CreateUserConfig {
return func(u *model.User) { return func(u *model.User) {
u.Role = role u.Role = role

@ -77,6 +77,7 @@ type RoomSettings struct {
DisableJoinNewUser bool `gorm:"default:false" json:"disable_join_new_user"` DisableJoinNewUser bool `gorm:"default:false" json:"disable_join_new_user"`
JoinNeedReview bool `gorm:"default:false" json:"join_need_review"` JoinNeedReview bool `gorm:"default:false" json:"join_need_review"`
UserDefaultPermissions RoomMemberPermission `json:"user_default_permissions"` UserDefaultPermissions RoomMemberPermission `json:"user_default_permissions"`
DisableGuest bool `gorm:"default:false" json:"disable_guest"`
CanGetMovieList bool `gorm:"default:true" json:"can_get_movie_list"` CanGetMovieList bool `gorm:"default:true" json:"can_get_movie_list"`
CanAddMovie bool `gorm:"default:true" json:"can_add_movie"` CanAddMovie bool `gorm:"default:true" json:"can_add_movie"`
@ -93,6 +94,7 @@ func DefaultRoomSettings() *RoomSettings {
DisableJoinNewUser: false, DisableJoinNewUser: false,
JoinNeedReview: false, JoinNeedReview: false,
UserDefaultPermissions: DefaultPermissions, UserDefaultPermissions: DefaultPermissions,
DisableGuest: false,
CanGetMovieList: true, CanGetMovieList: true,
CanAddMovie: true, CanAddMovie: true,

@ -7,9 +7,9 @@ import (
"sync/atomic" "sync/atomic"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
log "github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/internal/db" "github.com/synctv-org/synctv/internal/db"
"github.com/synctv-org/synctv/internal/model" "github.com/synctv-org/synctv/internal/model"
"github.com/synctv-org/synctv/internal/settings"
"github.com/synctv-org/synctv/utils" "github.com/synctv-org/synctv/utils"
"github.com/zijiren233/gencontainer/rwmap" "github.com/zijiren233/gencontainer/rwmap"
rtmps "github.com/zijiren233/livelib/server" rtmps "github.com/zijiren233/livelib/server"
@ -93,10 +93,10 @@ func (r *Room) AddMovies(movies []*model.Movie) error {
} }
func (r *Room) UserRole(userID string) (model.RoomMemberRole, error) { func (r *Room) UserRole(userID string) (model.RoomMemberRole, error) {
if r.CreatorID == userID { if r.IsCreator(userID) {
return model.RoomMemberRoleCreator, nil return model.RoomMemberRoleCreator, nil
} }
rur, err := r.LoadOrCreateRoomMember(userID) rur, err := r.LoadRoomMember(userID)
if err != nil { if err != nil {
return model.RoomMemberRoleUnknown, err return model.RoomMemberRoleUnknown, err
} }
@ -105,12 +105,8 @@ func (r *Room) UserRole(userID string) (model.RoomMemberRole, error) {
// do not use this value for permission determination // do not use this value for permission determination
func (r *Room) IsAdmin(userID string) bool { func (r *Room) IsAdmin(userID string) bool {
if r.IsCreator(userID) {
return true
}
role, err := r.UserRole(userID) role, err := r.UserRole(userID)
if err != nil { if err != nil {
log.Errorf("get user role failed: %s", err.Error())
return false return false
} }
return role.IsAdmin() return role.IsAdmin()
@ -120,6 +116,10 @@ func (r *Room) IsCreator(userID string) bool {
return r.CreatorID == userID return r.CreatorID == userID
} }
func (r *Room) IsGuest(userID string) bool {
return userID == db.GuestUserID
}
func (r *Room) HasPermission(userID string, permission model.RoomMemberPermission) bool { func (r *Room) HasPermission(userID string, permission model.RoomMemberPermission) bool {
if r.IsCreator(userID) { if r.IsCreator(userID) {
return true return true
@ -162,7 +162,7 @@ func (r *Room) HasAdminPermission(userID string, permission model.RoomAdminPermi
} }
func (r *Room) LoadOrCreateMemberStatus(userID string) (model.RoomMemberStatus, error) { func (r *Room) LoadOrCreateMemberStatus(userID string) (model.RoomMemberStatus, error) {
if r.CreatorID == userID { if r.IsCreator(userID) {
return model.RoomMemberStatusActive, nil return model.RoomMemberStatusActive, nil
} }
rur, err := r.LoadOrCreateRoomMember(userID) rur, err := r.LoadOrCreateRoomMember(userID)
@ -173,7 +173,7 @@ func (r *Room) LoadOrCreateMemberStatus(userID string) (model.RoomMemberStatus,
} }
func (r *Room) LoadMemberStatus(userID string) (model.RoomMemberStatus, error) { func (r *Room) LoadMemberStatus(userID string) (model.RoomMemberStatus, error) {
if r.CreatorID == userID { if r.IsCreator(userID) {
return model.RoomMemberStatusActive, nil return model.RoomMemberStatusActive, nil
} }
rur, err := r.LoadRoomMember(userID) rur, err := r.LoadRoomMember(userID)
@ -187,12 +187,15 @@ func (r *Room) LoadOrCreateRoomMember(userID string) (*model.RoomMember, error)
if r.Settings.DisableJoinNewUser { if r.Settings.DisableJoinNewUser {
return r.LoadRoomMember(userID) return r.LoadRoomMember(userID)
} }
if r.IsGuest(userID) && (r.Settings.DisableGuest || !settings.EnableGuest.Get()) {
return nil, errors.New("guest is disabled")
}
member, ok := r.members.Load(userID) member, ok := r.members.Load(userID)
if ok { if ok {
return member, nil return member, nil
} }
var conf []db.CreateRoomMemberRelationConfig var conf []db.CreateRoomMemberRelationConfig
if r.CreatorID == userID { if r.IsCreator(userID) {
conf = append( conf = append(
conf, conf,
db.WithRoomMemberStatus(model.RoomMemberStatusActive), db.WithRoomMemberStatus(model.RoomMemberStatusActive),
@ -200,6 +203,14 @@ func (r *Room) LoadOrCreateRoomMember(userID string) (*model.RoomMember, error)
db.WithRoomMemberRole(model.RoomMemberRoleCreator), db.WithRoomMemberRole(model.RoomMemberRoleCreator),
db.WithRoomMemberAdminPermissions(model.AllAdminPermissions), db.WithRoomMemberAdminPermissions(model.AllAdminPermissions),
) )
} else if r.IsGuest(userID) {
conf = append(
conf,
db.WithRoomMemberStatus(model.RoomMemberStatusActive),
db.WithRoomMemberRelationPermissions(model.NoPermission),
db.WithRoomMemberRole(model.RoomMemberRoleMember),
db.WithRoomMemberAdminPermissions(model.NoAdminPermission),
)
} else { } else {
conf = append( conf = append(
conf, conf,
@ -220,6 +231,11 @@ func (r *Room) LoadOrCreateRoomMember(userID string) (*model.RoomMember, error)
member.Permissions = model.AllPermissions member.Permissions = model.AllPermissions
member.AdminPermissions = model.AllAdminPermissions member.AdminPermissions = model.AllAdminPermissions
member.Status = model.RoomMemberStatusActive member.Status = model.RoomMemberStatusActive
} else if r.IsGuest(userID) {
member.Role = model.RoomMemberRoleMember
member.Permissions = model.NoPermission
member.AdminPermissions = model.NoAdminPermission
member.Status = model.RoomMemberStatusActive
} else if member.Role.IsAdmin() { } else if member.Role.IsAdmin() {
member.Permissions = model.AllPermissions member.Permissions = model.AllPermissions
} }
@ -228,11 +244,14 @@ func (r *Room) LoadOrCreateRoomMember(userID string) (*model.RoomMember, error)
} }
func (r *Room) LoadRoomMember(userID string) (*model.RoomMember, error) { func (r *Room) LoadRoomMember(userID string) (*model.RoomMember, error) {
if r.IsGuest(userID) && (r.Settings.DisableGuest || !settings.EnableGuest.Get()) {
return nil, errors.New("guest is disabled")
}
member, ok := r.members.Load(userID) member, ok := r.members.Load(userID)
if ok { if ok {
return member, nil return member, nil
} }
member, err := db.GetRoomMemberRelation(r.ID, userID) member, err := db.GetRoomMember(r.ID, userID)
if err != nil { if err != nil {
return nil, fmt.Errorf("get room member failed: %w", err) return nil, fmt.Errorf("get room member failed: %w", err)
} }
@ -241,6 +260,11 @@ func (r *Room) LoadRoomMember(userID string) (*model.RoomMember, error) {
member.Permissions = model.AllPermissions member.Permissions = model.AllPermissions
member.AdminPermissions = model.AllAdminPermissions member.AdminPermissions = model.AllAdminPermissions
member.Status = model.RoomMemberStatusActive member.Status = model.RoomMemberStatusActive
} else if r.IsGuest(userID) {
member.Role = model.RoomMemberRoleMember
member.Permissions = model.NoPermission
member.AdminPermissions = model.NoAdminPermission
member.Status = model.RoomMemberStatusActive
} else if member.Role.IsAdmin() { } else if member.Role.IsAdmin() {
member.Permissions = model.AllPermissions member.Permissions = model.AllPermissions
} }

@ -193,6 +193,10 @@ func (u *User) IsPending() bool {
return u.Role == model.RolePending return u.Role == model.RolePending
} }
func (u *User) IsGuest() bool {
return u.ID == db.GuestUserID
}
func (u *User) HasRoomPermission(room *Room, permission model.RoomMemberPermission) bool { func (u *User) HasRoomPermission(room *Room, permission model.RoomMemberPermission) bool {
if u.IsAdmin() { if u.IsAdmin() {
return true return true

@ -19,6 +19,7 @@ var (
DisableUserSignup = NewBoolSetting("disable_user_signup", false, model.SettingGroupUser) DisableUserSignup = NewBoolSetting("disable_user_signup", false, model.SettingGroupUser)
SignupNeedReview = NewBoolSetting("signup_need_review", false, model.SettingGroupUser) SignupNeedReview = NewBoolSetting("signup_need_review", false, model.SettingGroupUser)
UserMaxRoomCount = NewInt64Setting("user_max_room_count", 3, model.SettingGroupUser) UserMaxRoomCount = NewInt64Setting("user_max_room_count", 3, model.SettingGroupUser)
EnableGuest = NewBoolSetting("enable_guest", true, model.SettingGroupUser)
) )
var ( var (

@ -167,6 +167,8 @@ func initRoom(room *gin.RouterGroup, needAuthUser *gin.RouterGroup, needAuthRoom
room.GET("/list", RoomList) room.GET("/list", RoomList)
room.POST("/guest", GuestJoinRoom)
needAuthUser.POST("/create", CreateRoom) needAuthUser.POST("/create", CreateRoom)
needAuthUser.POST("/login", LoginRoom) needAuthUser.POST("/login", LoginRoom)

@ -276,20 +276,27 @@ func CheckRoom(ctx *gin.Context) {
})) }))
} }
func LoginRoom(ctx *gin.Context) { func GuestJoinRoom(ctx *gin.Context) {
user := ctx.MustGet("user").(*op.UserEntry).Value()
log := ctx.MustGet("log").(*logrus.Entry) log := ctx.MustGet("log").(*logrus.Entry)
req := model.LoginRoomReq{} req := model.LoginRoomReq{}
if err := model.Decode(ctx, &req); err != nil { if err := model.Decode(ctx, &req); err != nil {
log.Errorf("login room failed: %v", err) log.Errorf("guest join room failed: %v", err)
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err)) ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return return
} }
userE, err := op.LoadOrInitUserByID(db.GuestUserID)
if err != nil {
log.Errorf("guest join room failed: %v", err)
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return
}
user := userE.Value()
roomE, err := op.LoadOrInitRoomByID(req.RoomId) roomE, err := op.LoadOrInitRoomByID(req.RoomId)
if err != nil { if err != nil {
log.Errorf("login room failed: %v", err) log.Errorf("guest join room failed: %v", err)
if err == op.ErrRoomBanned || err == op.ErrRoomPending { if err == op.ErrRoomBanned || err == op.ErrRoomPending {
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err)) ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err))
return return
@ -299,20 +306,49 @@ func LoginRoom(ctx *gin.Context) {
} }
room := roomE.Value() room := roomE.Value()
member, err := room.LoadOrCreateRoomMember(user.ID) if !room.CheckPassword(req.Password) {
log.Warn("guest join room failed: password error")
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("password error"))
return
}
token, err := middlewares.NewAuthRoomToken(user, room)
if err != nil { if err != nil {
log.Errorf("login room failed: %v", err) log.Errorf("guest join room failed: %v", err)
ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err)) ctx.AbortWithStatusJSON(http.StatusInternalServerError, model.NewApiErrorResp(err))
return return
} }
if member.Status.IsNotActive() { ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
log.Warn("login room failed: member status not active") "roomId": room.ID,
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("member status not active")) "token": token,
}))
}
func LoginRoom(ctx *gin.Context) {
user := ctx.MustGet("user").(*op.UserEntry).Value()
log := ctx.MustGet("log").(*logrus.Entry)
req := model.LoginRoomReq{}
if err := model.Decode(ctx, &req); err != nil {
log.Errorf("login room failed: %v", err)
ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
return
}
roomE, err := op.LoadOrInitRoomByID(req.RoomId)
if err != nil {
log.Errorf("login room failed: %v", err)
if err == op.ErrRoomBanned || err == op.ErrRoomPending {
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err))
return
}
ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err))
return return
} }
room := roomE.Value()
if !member.Role.IsAdmin() && !room.CheckPassword(req.Password) { if !user.IsAdmin() && !user.IsRoomAdmin(room) && !room.CheckPassword(req.Password) {
log.Warn("login room failed: password error") log.Warn("login room failed: password error")
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("password error")) ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("password error"))
return return

@ -121,16 +121,21 @@ func AuthUser(Authorization string) (*op.UserEntry, error) {
return nil, ErrAuthFailed return nil, ErrAuthFailed
} }
u, err := op.LoadOrInitUserByID(claims.UserId) userE, err := op.LoadOrInitUserByID(claims.UserId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
user := userE.Value()
if user.IsGuest() {
return nil, errors.New("user is guest, can not login")
}
if !u.Value().CheckVersion(claims.UserVersion) { if !user.CheckVersion(claims.UserVersion) {
return nil, ErrAuthExpired return nil, ErrAuthExpired
} }
return u, nil return userE, nil
} }
func NewAuthUserToken(user *op.User) (string, error) { func NewAuthUserToken(user *op.User) (string, error) {
@ -140,6 +145,9 @@ func NewAuthUserToken(user *op.User) (string, error) {
if user.IsPending() { if user.IsPending() {
return "", errors.New("user is pending, need admin to approve") return "", errors.New("user is pending, need admin to approve")
} }
if user.IsGuest() {
return "", errors.New("user is guest, can not login")
}
t, err := time.ParseDuration(conf.Conf.Jwt.Expire) t, err := time.ParseDuration(conf.Conf.Jwt.Expire)
if err != nil { if err != nil {
return "", err return "", err

Loading…
Cancel
Save