feat: check joined and err handler

pull/242/head
zijiren233 1 year ago
parent 03a88d3aea
commit 2b5fb20675

@ -584,7 +584,7 @@ func (r *Room) RemoveMemberPermissions(userID string, permissions model.RoomMemb
func (r *Room) ApprovePendingMember(userID string) error { func (r *Room) ApprovePendingMember(userID string) error {
if r.IsCreator(userID) { if r.IsCreator(userID) {
return errors.New("you are creator, cannot approve") return errors.New("creator cannot be approved as a pending member")
} }
defer r.members.Delete(userID) defer r.members.Delete(userID)
return db.RoomApprovePendingMember(r.ID, userID) return db.RoomApprovePendingMember(r.ID, userID)
@ -592,7 +592,7 @@ func (r *Room) ApprovePendingMember(userID string) error {
func (r *Room) BanMember(userID string) error { func (r *Room) BanMember(userID string) error {
if r.IsCreator(userID) { if r.IsCreator(userID) {
return errors.New("you are creator, cannot ban") return errors.New("creator cannot be banned")
} }
if r.IsGuest(userID) { if r.IsGuest(userID) {
return errors.New("please set whether to disable guest users in the room settings") return errors.New("please set whether to disable guest users in the room settings")
@ -606,7 +606,7 @@ func (r *Room) BanMember(userID string) error {
func (r *Room) UnbanMember(userID string) error { func (r *Room) UnbanMember(userID string) error {
if r.IsCreator(userID) { if r.IsCreator(userID) {
return errors.New("you are creator, cannot unban") return errors.New("creator cannot be unbanned")
} }
if r.IsGuest(userID) { if r.IsGuest(userID) {
return errors.New("please set whether to enable guest users in the room settings") return errors.New("please set whether to enable guest users in the room settings")
@ -617,7 +617,7 @@ func (r *Room) UnbanMember(userID string) error {
func (r *Room) DeleteMember(userID string) error { func (r *Room) DeleteMember(userID string) error {
if r.IsCreator(userID) { if r.IsCreator(userID) {
return errors.New("you are creator, cannot delete") return errors.New("creator cannot be deleted")
} }
defer func() { defer func() {
r.members.Delete(userID) r.members.Delete(userID)
@ -632,7 +632,7 @@ func (r *Room) ResetAdminPermissions(userID string) error {
func (r *Room) SetAdminPermissions(userID string, permissions model.RoomAdminPermission) error { func (r *Room) SetAdminPermissions(userID string, permissions model.RoomAdminPermission) error {
if r.IsCreator(userID) { if r.IsCreator(userID) {
return errors.New("you are creator, cannot set admin permissions") return errors.New("creator cannot set admin permissions")
} }
if r.IsGuest(userID) { if r.IsGuest(userID) {
return errors.New("cannot set admin permissions to guest") return errors.New("cannot set admin permissions to guest")
@ -648,7 +648,7 @@ func (r *Room) SetAdminPermissions(userID string, permissions model.RoomAdminPer
func (r *Room) AddAdminPermissions(userID string, permissions model.RoomAdminPermission) error { func (r *Room) AddAdminPermissions(userID string, permissions model.RoomAdminPermission) error {
if r.IsCreator(userID) { if r.IsCreator(userID) {
return errors.New("you are creator, cannot add admin permissions") return errors.New("creator cannot add admin permissions")
} }
if r.IsGuest(userID) { if r.IsGuest(userID) {
return errors.New("cannot add admin permissions to guest") return errors.New("cannot add admin permissions to guest")
@ -664,7 +664,7 @@ func (r *Room) AddAdminPermissions(userID string, permissions model.RoomAdminPer
func (r *Room) RemoveAdminPermissions(userID string, permissions model.RoomAdminPermission) error { func (r *Room) RemoveAdminPermissions(userID string, permissions model.RoomAdminPermission) error {
if r.IsCreator(userID) { if r.IsCreator(userID) {
return errors.New("you are creator, cannot remove admin permissions") return errors.New("creator cannot remove admin permissions")
} }
if r.IsGuest(userID) { if r.IsGuest(userID) {
return errors.New("cannot remove admin permissions from guest") return errors.New("cannot remove admin permissions from guest")
@ -680,7 +680,7 @@ func (r *Room) RemoveAdminPermissions(userID string, permissions model.RoomAdmin
func (r *Room) SetAdmin(userID string, permissions model.RoomAdminPermission) error { func (r *Room) SetAdmin(userID string, permissions model.RoomAdminPermission) error {
if r.IsCreator(userID) { if r.IsCreator(userID) {
return errors.New("you are creator, cannot set admin") return errors.New("creator cannot set admin")
} }
if r.IsGuest(userID) { if r.IsGuest(userID) {
return errors.New("cannot set guest as admin") return errors.New("cannot set guest as admin")
@ -691,7 +691,7 @@ func (r *Room) SetAdmin(userID string, permissions model.RoomAdminPermission) er
func (r *Room) SetMember(userID string, permissions model.RoomMemberPermission) error { func (r *Room) SetMember(userID string, permissions model.RoomMemberPermission) error {
if r.IsCreator(userID) { if r.IsCreator(userID) {
return errors.New("you are creator, cannot set member") return errors.New("creator cannot set member")
} }
defer r.members.Delete(userID) defer r.members.Delete(userID)
return db.RoomSetMember(r.ID, userID, permissions) return db.RoomSetMember(r.ID, userID, permissions)

@ -13,12 +13,10 @@ import (
var ( var (
roomCache *synccache.SyncCache[string, *Room] roomCache *synccache.SyncCache[string, *Room]
ErrRoomPending = errors.New("room pending, please wait for admin to approve") ErrRoomCreatorBanned = errors.New("room creator is banned")
ErrRoomBanned = errors.New("room banned") ErrRoomCreatorPending = errors.New("room creator is pending approval, please wait for admin to review")
ErrRoomCreatorBanned = errors.New("room creator banned") ErrInvalidRoomID = errors.New("invalid room ID: must be 32 characters long")
ErrRoomCreatorPending = errors.New("room creator pending, please wait for admin to approve") ErrRoomNotInCache = errors.New("room not found in cache")
ErrInvalidRoomID = errors.New("room id is not 32 bit")
ErrRoomNotInCache = errors.New("room is not in cache")
) )
type RoomEntry = synccache.Entry[*Room] type RoomEntry = synccache.Entry[*Room]

@ -16,8 +16,8 @@ var userCache *synccache.SyncCache[string, *User]
type UserEntry = synccache.Entry[*User] type UserEntry = synccache.Entry[*User]
var ( var (
ErrUserBanned = errors.New("user banned") ErrUserBanned = errors.New("user account has been banned")
ErrUserPending = errors.New("user pending, please wait for admin to approve") ErrUserPending = errors.New("user account is pending approval, please wait for administrator review")
) )
func LoadOrInitUser(u *model.User) (*UserEntry, error) { func LoadOrInitUser(u *model.User) (*UserEntry, error) {

@ -69,6 +69,7 @@ func RoomInfo(ctx *gin.Context) {
"name": room.Name, "name": room.Name,
"needPassword": room.NeedPassword(), "needPassword": room.NeedPassword(),
"creator": op.GetUserName(room.CreatorID), "creator": op.GetUserName(room.CreatorID),
"creatorId": room.CreatorID,
"createdAt": room.CreatedAt.UnixMilli(), "createdAt": room.CreatedAt.UnixMilli(),
"status": room.Status, "status": room.Status,
"enabledGuest": room.EnabledGuest(), "enabledGuest": room.EnabledGuest(),
@ -128,9 +129,10 @@ var roomHotCache = refreshcache0.NewRefreshCache[[]*model.RoomListResp](func(con
rooms = append(rooms, &model.RoomListResp{ rooms = append(rooms, &model.RoomListResp{
RoomId: v.ID, RoomId: v.ID,
RoomName: v.Name, RoomName: v.Name,
PeopleNum: v.ViewerCount(), ViewerCount: v.ViewerCount(),
NeedPassword: v.NeedPassword(), NeedPassword: v.NeedPassword(),
Creator: op.GetUserName(v.CreatorID), Creator: op.GetUserName(v.CreatorID),
CreatorID: v.CreatorID,
CreatedAt: v.CreatedAt.UnixMilli(), CreatedAt: v.CreatedAt.UnixMilli(),
}) })
} }
@ -138,7 +140,7 @@ var roomHotCache = refreshcache0.NewRefreshCache[[]*model.RoomListResp](func(con
}) })
slices.SortStableFunc(rooms, func(a, b *model.RoomListResp) int { slices.SortStableFunc(rooms, func(a, b *model.RoomListResp) int {
if a.PeopleNum == b.PeopleNum { if a.ViewerCount == b.ViewerCount {
if a.RoomName == b.RoomName { if a.RoomName == b.RoomName {
return 0 return 0
} }
@ -147,7 +149,7 @@ var roomHotCache = refreshcache0.NewRefreshCache[[]*model.RoomListResp](func(con
} else { } else {
return 1 return 1
} }
} else if a.PeopleNum > b.PeopleNum { } else if a.ViewerCount > b.ViewerCount {
return -1 return -1
} else { } else {
return 1 return 1
@ -274,7 +276,7 @@ func genRoomListResp(scopes ...func(db *gorm.DB) *gorm.DB) ([]*model.RoomListRes
resp[i] = &model.RoomListResp{ resp[i] = &model.RoomListResp{
RoomId: r.ID, RoomId: r.ID,
RoomName: r.Name, RoomName: r.Name,
PeopleNum: op.ViewerCount(r.ID), ViewerCount: op.ViewerCount(r.ID),
NeedPassword: len(r.HashedPassword) != 0, NeedPassword: len(r.HashedPassword) != 0,
CreatorID: r.CreatorID, CreatorID: r.CreatorID,
Creator: op.GetUserName(r.CreatorID), Creator: op.GetUserName(r.CreatorID),
@ -299,7 +301,7 @@ func genJoinedRoomListResp(scopes ...func(db *gorm.DB) *gorm.DB) ([]*model.Joine
RoomListResp: model.RoomListResp{ RoomListResp: model.RoomListResp{
RoomId: r.ID, RoomId: r.ID,
RoomName: r.Name, RoomName: r.Name,
PeopleNum: op.ViewerCount(r.ID), ViewerCount: op.ViewerCount(r.ID),
NeedPassword: len(r.HashedPassword) != 0, NeedPassword: len(r.HashedPassword) != 0,
CreatorID: r.CreatorID, CreatorID: r.CreatorID,
Creator: op.GetUserName(r.CreatorID), Creator: op.GetUserName(r.CreatorID),
@ -330,14 +332,14 @@ func CheckRoom(ctx *gin.Context) {
} }
room := roomE.Value() room := roomE.Value()
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ ctx.JSON(http.StatusOK, model.NewApiDataResp(&model.CheckRoomResp{
"name": room.Name, Name: room.Name,
"status": room.Status, Status: room.Status,
"peopleNum": op.ViewerCount(room.ID), CreatorID: room.CreatorID,
"needPassword": room.NeedPassword(), Creator: op.GetUserName(room.CreatorID),
"creatorId": room.CreatorID, NeedPassword: room.NeedPassword(),
"creator": op.GetUserName(room.CreatorID), ViewerCount: op.ViewerCount(room.ID),
"enabledGuest": room.EnabledGuest(), EnabledGuest: room.EnabledGuest(),
})) }))
} }
@ -355,15 +357,23 @@ func LoginRoom(ctx *gin.Context) {
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("login room failed: %v", err)
if err == op.ErrRoomBanned || err == op.ErrRoomPending { ctx.AbortWithStatusJSON(http.StatusBadRequest, model.NewApiErrorResp(err))
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(err))
return
}
ctx.AbortWithStatusJSON(http.StatusNotFound, model.NewApiErrorResp(err))
return return
} }
room := roomE.Value() room := roomE.Value()
if room.IsBanned() {
log.Warn("login room failed: room is banned")
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("room is banned"))
return
}
if room.IsPending() {
log.Warn("login room failed: room is pending, please wait for admin to approve")
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("room is pending, please wait for admin to approve"))
return
}
if member, err := room.LoadMember(user.ID); err == nil { if member, err := room.LoadMember(user.ID); err == nil {
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"status": member.Status, "status": member.Status,

@ -305,6 +305,15 @@ func UserCheckJoinedRoom(ctx *gin.Context) {
ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{ ctx.JSON(http.StatusOK, model.NewApiDataResp(gin.H{
"joined": status != dbModel.RoomMemberStatusNotJoined, "joined": status != dbModel.RoomMemberStatusNotJoined,
"status": status, "status": status,
"room": &model.CheckRoomResp{
Name: room.Name,
Status: room.Status,
CreatorID: room.CreatorID,
Creator: op.GetUserName(room.CreatorID),
NeedPassword: room.NeedPassword(),
ViewerCount: op.ViewerCount(room.ID),
EnabledGuest: room.EnabledGuest(),
},
})) }))
} }

@ -2,7 +2,6 @@ package middlewares
import ( import (
"errors" "errors"
"fmt"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@ -20,8 +19,20 @@ import (
) )
var ( var (
ErrAuthFailed = errors.New("auth failed") ErrAuthFailed = errors.New("authentication failed")
ErrAuthExpired = errors.New("auth expired") ErrAuthExpired = errors.New("authentication token expired")
ErrUserBanned = errors.New("user account has been banned")
ErrUserPending = errors.New("user account is pending approval")
ErrUserGuest = errors.New("guests are not allowed to perform this action")
ErrRoomBanned = errors.New("room has been banned")
ErrRoomPending = errors.New("room is pending approval")
ErrUserBannedFromRoom = errors.New("user has been banned from this room")
ErrInvalidRoomID = errors.New("invalid room ID")
ErrEmptyToken = errors.New("authentication token is empty")
ErrNotRoomAdmin = errors.New("user is not a room administrator")
ErrNotRoomCreator = errors.New("user is not the room creator")
ErrNotAdmin = errors.New("user is not an administrator")
ErrNotRoot = errors.New("user is not a root user")
) )
type AuthClaims struct { type AuthClaims struct {
@ -46,7 +57,7 @@ func authUser(authorization string) (*AuthClaims, error) {
func AuthRoom(authorization, roomId string) (*op.UserEntry, *op.RoomEntry, error) { func AuthRoom(authorization, roomId string) (*op.UserEntry, *op.RoomEntry, error) {
if len(roomId) != 32 { if len(roomId) != 32 {
return nil, nil, ErrAuthFailed return nil, nil, ErrInvalidRoomID
} }
userE, err := authenticateUserOrGuest(authorization) userE, err := authenticateUserOrGuest(authorization)
@ -95,23 +106,23 @@ func authenticateUser(Authorization string) (*op.UserEntry, error) {
func authenticateGuest() (*op.UserEntry, error) { func authenticateGuest() (*op.UserEntry, error) {
if !settings.EnableGuest.Get() { if !settings.EnableGuest.Get() {
return nil, fmt.Errorf("guests are disabled") return nil, ErrUserGuest
} }
return op.LoadOrInitGuestUser() return op.LoadOrInitGuestUser()
} }
func validateUser(user *op.User, userVersion uint32) error { func validateUser(user *op.User, userVersion uint32) error {
if user.IsGuest() { if user.IsGuest() {
return fmt.Errorf("guests are not allowed to join rooms by token") return ErrUserGuest
} }
if !user.CheckVersion(userVersion) { if !user.CheckVersion(userVersion) {
return ErrAuthExpired return ErrAuthExpired
} }
if user.IsBanned() { if user.IsBanned() {
return fmt.Errorf("user is banned") return ErrUserBanned
} }
if user.IsPending() { if user.IsPending() {
return fmt.Errorf("user is pending, need admin to approve") return ErrUserPending
} }
return nil return nil
} }
@ -133,18 +144,18 @@ func authenticateRoomAccess(roomId string, user *op.User) (*op.RoomEntry, error)
func validateRoomAccess(room *op.Room, user *op.User) error { func validateRoomAccess(room *op.Room, user *op.User) error {
if room.IsGuest(user.ID) { if room.IsGuest(user.ID) {
if room.Settings.DisableGuest { if room.Settings.DisableGuest {
return fmt.Errorf("guests are not allowed to join rooms") return ErrUserGuest
} }
if room.NeedPassword() { if room.NeedPassword() {
return fmt.Errorf("guests are not allowed to join rooms that require a password") return ErrUserGuest
} }
} }
if room.IsBanned() { if room.IsBanned() {
return fmt.Errorf("room is banned") return ErrRoomBanned
} }
if room.IsPending() { if room.IsPending() {
return fmt.Errorf("room is pending, need admin to approve") return ErrRoomPending
} }
var status dbModel.RoomMemberStatus var status dbModel.RoomMemberStatus
@ -159,10 +170,10 @@ func validateRoomAccess(room *op.Room, user *op.User) error {
} }
if status.IsBanned() { if status.IsBanned() {
return fmt.Errorf("user is banned from room") return ErrUserBannedFromRoom
} }
if status.IsPending() { if status.IsPending() {
return fmt.Errorf("user is pending, need admin to approve") return ErrUserPending
} }
return nil return nil
@ -193,16 +204,16 @@ func AuthUser(authorization string) (*op.UserEntry, error) {
func validateAuthUser(user *op.User, userVersion uint32) error { func validateAuthUser(user *op.User, userVersion uint32) error {
if user.IsGuest() { if user.IsGuest() {
return errors.New("user is guest, cannot login") return ErrUserGuest
} }
if !user.CheckVersion(userVersion) { if !user.CheckVersion(userVersion) {
return ErrAuthExpired return ErrAuthExpired
} }
if user.IsBanned() { if user.IsBanned() {
return errors.New("user is banned") return ErrUserBanned
} }
if user.IsPending() { if user.IsPending() {
return errors.New("user is pending, need admin to approve") return ErrUserPending
} }
return nil return nil
} }
@ -228,12 +239,6 @@ func NewAuthUserToken(user *op.User) (string, error) {
return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret)) return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(stream.StringToBytes(conf.Conf.Jwt.Secret))
} }
var (
ErrUserBanned = errors.New("user banned")
ErrUserPending = errors.New("user is pending, need admin to approve")
ErrUserGuest = errors.New("user is guest, cannot login")
)
func validateNewAuthUserToken(user *op.User) error { func validateNewAuthUserToken(user *op.User) error {
if user.IsBanned() { if user.IsBanned() {
return ErrUserBanned return ErrUserBanned
@ -250,7 +255,7 @@ func validateNewAuthUserToken(user *op.User) error {
func AuthUserMiddleware(ctx *gin.Context) { func AuthUserMiddleware(ctx *gin.Context) {
token := GetAuthorizationTokenFromContext(ctx) token := GetAuthorizationTokenFromContext(ctx)
if token == "" { if token == "" {
ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorStringResp("token is empty")) ctx.AbortWithStatusJSON(http.StatusUnauthorized, model.NewApiErrorResp(ErrEmptyToken))
return return
} }
userE, err := AuthUser(token) userE, err := AuthUser(token)
@ -291,7 +296,7 @@ func AuthRoomWithoutGuestMiddleware(ctx *gin.Context) {
user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value() user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value()
if user.IsGuest() { if user.IsGuest() {
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("guest has no permission")) ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(ErrUserGuest))
return return
} }
} }
@ -306,7 +311,7 @@ func AuthRoomAdminMiddleware(ctx *gin.Context) {
user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value() user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value()
if !user.IsRoomAdmin(room) { if !user.IsRoomAdmin(room) {
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user has no permission")) ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(ErrNotRoomAdmin))
return return
} }
} }
@ -321,7 +326,7 @@ func AuthRoomCreatorMiddleware(ctx *gin.Context) {
user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value() user := ctx.MustGet("user").(*synccache.Entry[*op.User]).Value()
if room.CreatorID != user.ID { if room.CreatorID != user.ID {
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user is not creator")) ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(ErrNotRoomCreator))
return return
} }
} }
@ -334,7 +339,7 @@ func AuthAdminMiddleware(ctx *gin.Context) {
userE := ctx.MustGet("user").(*synccache.Entry[*op.User]) userE := ctx.MustGet("user").(*synccache.Entry[*op.User])
if !userE.Value().IsAdmin() { if !userE.Value().IsAdmin() {
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user is not admin")) ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(ErrNotAdmin))
return return
} }
} }
@ -347,7 +352,7 @@ func AuthRootMiddleware(ctx *gin.Context) {
userE := ctx.MustGet("user").(*synccache.Entry[*op.User]) userE := ctx.MustGet("user").(*synccache.Entry[*op.User])
if !userE.Value().IsRoot() { if !userE.Value().IsRoot() {
ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorStringResp("user is not root")) ctx.AbortWithStatusJSON(http.StatusForbidden, model.NewApiErrorResp(ErrNotRoot))
return return
} }
} }
@ -392,11 +397,11 @@ func GetRoomIdFromContext(ctx *gin.Context) (string, error) {
return roomId, nil return roomId, nil
} }
ctx.Set("roomId", "") ctx.Set("roomId", "")
return "", errors.New("room id length is not 32") return "", ErrInvalidRoomID
} }
ctx.Set("roomId", "") ctx.Set("roomId", "")
return "", errors.New("room id is empty") return "", ErrInvalidRoomID
} }
func setLogFields(ctx *gin.Context, user *op.User, room *op.Room) { func setLogFields(ctx *gin.Context, user *op.User, room *op.Room) {

@ -61,7 +61,7 @@ func (c *CreateRoomReq) Validate() error {
type RoomListResp struct { type RoomListResp struct {
RoomId string `json:"roomId"` RoomId string `json:"roomId"`
RoomName string `json:"roomName"` RoomName string `json:"roomName"`
PeopleNum int64 `json:"peopleNum"` ViewerCount int64 `json:"viewerCount"`
NeedPassword bool `json:"needPassword"` NeedPassword bool `json:"needPassword"`
CreatorID string `json:"creatorId"` CreatorID string `json:"creatorId"`
Creator string `json:"creator"` Creator string `json:"creator"`
@ -150,3 +150,13 @@ func (c *CheckRoomPasswordReq) Decode(ctx *gin.Context) error {
func (c *CheckRoomPasswordReq) Validate() error { func (c *CheckRoomPasswordReq) Validate() error {
return nil return nil
} }
type CheckRoomResp struct {
Name string `json:"name"`
Status model.RoomStatus `json:"status"`
CreatorID string `json:"creatorId"`
Creator string `json:"creator"`
NeedPassword bool `json:"needPassword"`
ViewerCount int64 `json:"viewerCount"`
EnabledGuest bool `json:"enabledGuest"`
}

Loading…
Cancel
Save