fixed bugs

This commit is contained in:
ACoolName 2025-03-19 19:56:58 +02:00
parent 1488d7db16
commit 32d64f3637
14 changed files with 173 additions and 98 deletions

View File

@ -48,28 +48,6 @@ func (con *AuthApi) signToken(token Claims) (string, error) {
return t.SignedString([]byte(con.config.Signing.Key)) return t.SignedString([]byte(con.config.Signing.Key))
} }
func AuthorizedTo(requiredPermissions models.Permission) gin.HandlerFunc {
return func(ctx *gin.Context) {
claimsPointer, exists := ctx.Get("claims")
if !exists {
log.Printf("LoggedIn was not called first")
ctx.AbortWithError(500, fmt.Errorf("Misconfigured method"))
return
}
claims, ok := claimsPointer.(*AuthClaims)
if !ok {
ctx.AbortWithStatus(500)
return
}
if (requiredPermissions&claims.Permissions != requiredPermissions) && (models.Admin&claims.Permissions != models.Admin) {
ctx.AbortWithStatusJSON(403, "matching permissions were not found")
return
}
}
}
func (con *AuthApi) LoggedIn(ctx *gin.Context) { func (con *AuthApi) LoggedIn(ctx *gin.Context) {
authCookie, err := ctx.Request.Cookie("auth") authCookie, err := ctx.Request.Cookie("auth")
if err != nil { if err != nil {
@ -77,7 +55,7 @@ func (con *AuthApi) LoggedIn(ctx *gin.Context) {
return return
} }
token, err := jwt.ParseWithClaims(authCookie.Value, &AuthClaims{}, func(token *jwt.Token) (interface{}, error) { token, err := jwt.ParseWithClaims(authCookie.Value, &AuthClaims{}, func(token *jwt.Token) (any, error) {
// Don't forget to validate the alg is what you expect: // Don't forget to validate the alg is what you expect:
if token.Method.Alg() != con.config.Signing.Algorithm { if token.Method.Alg() != con.config.Signing.Algorithm {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
@ -216,6 +194,7 @@ func (con AuthApi) Verify(ctx *gin.Context) {
ctx.Redirect(303, fmt.Sprintf("http://%s/login", con.config.Domain)) ctx.Redirect(303, fmt.Sprintf("http://%s/login", con.config.Domain))
} }
func LoadGroup(group *gin.RouterGroup, config models.GlobalConfig) gin.HandlerFunc { func LoadGroup(group *gin.RouterGroup, config models.GlobalConfig) gin.HandlerFunc {
userAuthHandler, err := factories.GetUserPassAuthDbHandler(config.Authentication.UserPass) userAuthHandler, err := factories.GetUserPassAuthDbHandler(config.Authentication.UserPass)
if err != nil { if err != nil {
@ -234,7 +213,7 @@ func LoadGroup(group *gin.RouterGroup, config models.GlobalConfig) gin.HandlerFu
} }
group.POST("/signin", connection.signIn) group.POST("/signin", connection.signIn)
group.POST("/signup", connection.LoggedIn, AuthorizedTo(models.Admin), connection.signUp) group.POST("/signup", connection.signUp)
group.Any("/verify", connection.Verify) group.Any("/verify", connection.Verify)
return connection.LoggedIn return connection.LoggedIn

46
auth/utils.go Normal file
View File

@ -0,0 +1,46 @@
package auth
import (
"fmt"
"git.acooldomain.co/server-manager/backend/models"
"github.com/gin-gonic/gin"
)
const AuthorizedParam string = "authorized"
func AuthorizedTo(requiredPermissions models.Permission) gin.HandlerFunc {
return func(ctx *gin.Context) {
claimsPointer, exists := ctx.Get("claims")
if !exists {
ctx.AbortWithError(500, fmt.Errorf("Did not call LoggedIn first"))
return
}
claims, ok := claimsPointer.(*AuthClaims)
if !ok {
return
}
if (requiredPermissions&claims.Permissions != requiredPermissions) && (models.Admin&claims.Permissions != models.Admin) {
return
}
ctx.Set(AuthorizedParam, true)
}
}
func AuthorizationEnforcer() gin.HandlerFunc {
return func(ctx *gin.Context) {
authorized, exists := ctx.Get(AuthorizedParam)
if !exists {
ctx.AbortWithStatus(403)
return
}
if !authorized.(bool) {
ctx.AbortWithStatus(403)
}
}
}

View File

@ -106,7 +106,7 @@ func (self *ServersDbHandler) ListServers(ctx context.Context) ([]dbhandler.Serv
func (self *ServersDbHandler) GetServer(ctx context.Context, serverId string) (*dbhandler.Server, error) { func (self *ServersDbHandler) GetServer(ctx context.Context, serverId string) (*dbhandler.Server, error) {
var server Server var server Server
err := self.collection.FindOne(ctx, bson.M{"server_id": serverId}).Decode(&server) err := self.collection.FindOne(ctx, bson.M{"id": serverId}).Decode(&server)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -125,7 +125,7 @@ func (self *ServersDbHandler) CreateServer(ctx context.Context, server dbhandler
func (self *ServersDbHandler) DeleteServer(ctx context.Context, serverId string) error { func (self *ServersDbHandler) DeleteServer(ctx context.Context, serverId string) error {
_, err := self.collection.DeleteOne(ctx, bson.M{ _, err := self.collection.DeleteOne(ctx, bson.M{
"server_id": serverId, "id": serverId,
}) })
return err return err
@ -163,7 +163,7 @@ func (self *ServersDbHandler) UpdateServer(ctx context.Context, serverId string,
updateServerRequest["command"] = updateParams.Command updateServerRequest["command"] = updateParams.Command
} }
_, err := self.collection.UpdateOne(ctx, bson.M{"server_id": serverId}, bson.M{"$set": updateServerRequest}) _, err := self.collection.UpdateOne(ctx, bson.M{"id": serverId}, bson.M{"$set": updateServerRequest})
return err return err
} }

View File

@ -98,6 +98,14 @@ func (self *UserPassAuthenticationDbHandler) CreateUser(
return err return err
} }
func (self *UserPassAuthenticationDbHandler) CountUsers(ctx context.Context) (uint, error) {
count, err := self.collection.CountDocuments(ctx, bson.M{})
if err != nil {
return 0, err
}
return uint(count), nil
}
func (self *UserPassAuthenticationDbHandler) RemoveUser(ctx context.Context, username string) error { func (self *UserPassAuthenticationDbHandler) RemoveUser(ctx context.Context, username string) error {
_, err := self.collection.DeleteOne( _, err := self.collection.DeleteOne(
ctx, ctx,

View File

@ -28,6 +28,7 @@ type UserPassAuthanticationDbHandler interface {
// Read Only // Read Only
AuthenticateUser(ctx context.Context, username string, password string) (*models.User, error) AuthenticateUser(ctx context.Context, username string, password string) (*models.User, error)
ListUsers(ctx context.Context) ([]models.User, error) ListUsers(ctx context.Context) ([]models.User, error)
CountUsers(ctx context.Context) (uint, error)
// Write // Write
CreateUser(ctx context.Context, username string, password string, permissions models.Permission, email string, maxOwnedServers uint) error CreateUser(ctx context.Context, username string, password string, permissions models.Permission, email string, maxOwnedServers uint) error

View File

@ -1,11 +1,14 @@
package factories package factories
import ( import (
"context"
"errors" "errors"
"log"
"sync" "sync"
"time"
"git.acooldomain.co/server-manager/backend/dbhandler/mongo"
"git.acooldomain.co/server-manager/backend/dbhandler" "git.acooldomain.co/server-manager/backend/dbhandler"
"git.acooldomain.co/server-manager/backend/dbhandler/mongo"
"git.acooldomain.co/server-manager/backend/models" "git.acooldomain.co/server-manager/backend/models"
) )
@ -133,6 +136,21 @@ func GetUserPassAuthDbHandler(config models.UserPassAuthConfig) (dbhandler.UserP
} }
userPassAuthDbHandlers[key] = handler userPassAuthDbHandlers[key] = handler
ctx, cancel := context.WithTimeoutCause(context.Background(), 5*time.Second, errors.New("Timeout"))
defer cancel()
if config.InitialUser == nil {
return handler, nil
}
count, _ := handler.CountUsers(ctx)
if count == 0 {
log.Printf("Trying to create user %#v\n", config.InitialUser)
err := handler.CreateUser(ctx, config.InitialUser.Username, config.InitialUser.Password, models.Admin, config.InitialUser.Email, 10)
if err != nil {
log.Printf("Failed to create initial user %e\n", err)
}
}
return handler, nil return handler, nil
} }

View File

@ -4,12 +4,12 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"maps"
"net" "net"
instancemanager "git.acooldomain.co/server-manager/backend/instancemanager" instancemanager "git.acooldomain.co/server-manager/backend/instancemanager"
"git.acooldomain.co/server-manager/backend/models" "git.acooldomain.co/server-manager/backend/models"
"github.com/buildkite/shellwords" "github.com/buildkite/shellwords"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/image"
"github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/mount"
@ -22,11 +22,11 @@ import (
type InstanceManager struct { type InstanceManager struct {
instancemanager.InstanceManager instancemanager.InstanceManager
client client.Client client *client.Client
config models.DockerInstanceManagerConfig config models.DockerInstanceManagerConfig
} }
func (self *InstanceManager) containerList(ctx context.Context, labels ContainerLabels, all bool) ([]types.Container, error) { func (self *InstanceManager) containerList(ctx context.Context, labels ContainerLabels, all bool) ([]container.Summary, error) {
filters, err := convertLabelsToFilter(labels) filters, err := convertLabelsToFilter(labels)
if err != nil { if err != nil {
return nil, err return nil, err
@ -61,12 +61,17 @@ func (self *InstanceManager) getVolume(ctx context.Context, serverId string) (*v
// General // General
// Read Only // Read Only
func (self *InstanceManager) GetImage(ctx context.Context, imageId string) (*instancemanager.Image, error) { func (self *InstanceManager) GetImage(ctx context.Context, imageId string) (*instancemanager.Image, error) {
imageInspect, _, err := self.client.ImageInspectWithRaw(ctx, imageId) imageInspect, err := self.client.ImageInspect(ctx, imageId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if imageInspect.Config.Labels["type"] != "game" { imageLabels, err := convertImageLabelsToStruct(imageInspect.Config.Labels)
if err != nil {
return nil, err
}
if imageLabels.Type != Game {
return nil, fmt.Errorf("Image not found") return nil, fmt.Errorf("Image not found")
} }
@ -89,7 +94,7 @@ func (self *InstanceManager) ListImages(ctx context.Context) ([]instancemanager.
images := make([]instancemanager.Image, len(rawImages)) images := make([]instancemanager.Image, len(rawImages))
for i, rawImage := range rawImages { for i, rawImage := range rawImages {
imageInspect, _, err := self.client.ImageInspectWithRaw(ctx, rawImage.ID) imageInspect, err := self.client.ImageInspect(ctx, rawImage.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -502,9 +507,7 @@ func (self *InstanceManager) StartFileBrowser(ctx context.Context, serverId stri
return nil, err return nil, err
} }
for key, value := range *containerConfig { maps.Copy(browserLabels, *containerConfig)
browserLabels[key] = value
}
command := self.config.FileBrowser.Command command := self.config.FileBrowser.Command
@ -579,6 +582,6 @@ func NewInstanceManager(config models.DockerInstanceManagerConfig) (*InstanceMan
return &InstanceManager{ return &InstanceManager{
config: config, config: config,
client: *apiClient, client: apiClient,
}, nil }, nil
} }

View File

@ -8,8 +8,9 @@ import (
instancemanager "git.acooldomain.co/server-manager/backend/instancemanager" instancemanager "git.acooldomain.co/server-manager/backend/instancemanager"
"git.acooldomain.co/server-manager/backend/models" "git.acooldomain.co/server-manager/backend/models"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/image"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
) )
@ -54,15 +55,15 @@ func stringifyMap(m map[string]any) map[string]string {
return stringifiedMap return stringifiedMap
} }
func convertContainerPortsToPorts(ports []types.Port) []models.Port { func convertContainerPortsToPorts(ports []container.Port) []models.Port {
containerPorts := make([]models.Port, len(ports)) containerPorts := make([]models.Port, len(ports))
logger := log.Default() logger := log.Default()
for i, port := range ports { for i, port := range ports {
var portProtocol models.PortProtocol var portProtocol models.PortProtocol
switch port.Type { switch port.Type {
case "TCP": case "tcp":
portProtocol = models.TCP portProtocol = models.TCP
case "UDP": case "udp":
portProtocol = models.UDP portProtocol = models.UDP
default: default:
logger.Println(fmt.Sprintf("Unkown Port Protocol %s assuming TCP", port.Type)) logger.Println(fmt.Sprintf("Unkown Port Protocol %s assuming TCP", port.Type))
@ -90,10 +91,11 @@ func convertImageStringToModelsImage(image string) models.Image {
} }
} }
func convertImageInspectToInstanceImage(image types.ImageInspect) instancemanager.Image { func convertImageInspectToInstanceImage(image image.InspectResponse) instancemanager.Image {
modelsImage := convertImageStringToModelsImage(image.RepoTags[0]) modelsImage := convertImageStringToModelsImage(image.RepoTags[0])
ports := convertImagePortsToPorts(image.Config.ExposedPorts) ports := convertImagePortsToPorts(image.Config.ExposedPorts)
fmt.Printf("image: %#v\nconfig: %#v\nports: %#v\n", image, image.Config, ports)
return instancemanager.Image{ return instancemanager.Image{
Registry: modelsImage.Registry, Registry: modelsImage.Registry,
@ -112,7 +114,7 @@ func convertContainerLabelsToStruct(labels map[string]string) (*ContainerLabels,
return nil, err return nil, err
} }
err = json.Unmarshal(rawLabels, &labels) err = json.Unmarshal(rawLabels, &containerLabels)
if err != nil { if err != nil {
return nil, err return nil, err
@ -129,7 +131,7 @@ func convertVolumeLabelsToStruct(labels map[string]string) (*VolumeLabels, error
return nil, err return nil, err
} }
err = json.Unmarshal(rawLabels, &labels) err = json.Unmarshal(rawLabels, &volumeLabels)
if err != nil { if err != nil {
return nil, err return nil, err
@ -146,7 +148,7 @@ func convertImageLabelsToStruct(labels map[string]string) (*ImageLabels, error)
return nil, err return nil, err
} }
err = json.Unmarshal(rawLabels, &labels) err = json.Unmarshal(rawLabels, &imageLabels)
if err != nil { if err != nil {
return nil, err return nil, err
@ -161,9 +163,9 @@ func convertImagePortsToPorts(rawPorts nat.PortSet) []instancemanager.Port {
portNumber := imagePort.Int() portNumber := imagePort.Int()
var protocol models.PortProtocol var protocol models.PortProtocol
switch imagePort.Proto() { switch imagePort.Proto() {
case "TCP": case "tcp":
protocol = models.TCP protocol = models.TCP
case "UDP": case "udp":
protocol = models.UDP protocol = models.UDP
default: default:
log.Default().Println(fmt.Sprintf("Unknown port protocol %s using TCP", imagePort.Proto())) log.Default().Println(fmt.Sprintf("Unknown port protocol %s using TCP", imagePort.Proto()))

View File

@ -1,5 +1,11 @@
package models package models
type InitialUserConfig struct {
Email string `yaml:"email"`
Username string `yaml:"username"`
Password string `yaml:"password"`
}
type EmailConfig struct { type EmailConfig struct {
FromEmail string `yaml:"from_email"` FromEmail string `yaml:"from_email"`
Username string `yaml:"username"` Username string `yaml:"username"`
@ -35,6 +41,7 @@ type UserPassAuthConfig struct {
Type DatabaseType `yaml:"type"` Type DatabaseType `yaml:"type"`
Mongo *MongoDBConfig `yaml:"mongo"` Mongo *MongoDBConfig `yaml:"mongo"`
InviteTokenDatabase InviteTokenDatabaseConfig `yaml:"invite_token_database"` InviteTokenDatabase InviteTokenDatabaseConfig `yaml:"invite_token_database"`
InitialUser *InitialUserConfig `yaml:"initial_user"`
} }
type AuthenticationConfig struct { type AuthenticationConfig struct {

36
servers/auth_utils.go Normal file
View File

@ -0,0 +1,36 @@
package servers
import (
"git.acooldomain.co/server-manager/backend/auth"
"git.acooldomain.co/server-manager/backend/models"
"github.com/gin-gonic/gin"
)
func (con ServersApi) ServerAuthorized(permissions models.Permission) func(*gin.Context) {
return func(ctx *gin.Context) {
claimsPointer, exists := ctx.Get("claims")
if !exists {
ctx.AbortWithStatus(403)
return
}
claims := claimsPointer.(*auth.AuthClaims)
serverId := ctx.Param("server_id")
if serverId == "" {
return
}
userPermissions, err := con.ServerAuthorization.GetPermissions(ctx, claims.Username, serverId)
if err != nil {
return
}
if userPermissions&permissions == permissions || userPermissions&models.Admin == models.Admin {
ctx.Set(auth.AuthorizedParam, true)
return
}
return
}
}

View File

@ -51,6 +51,6 @@ func LoadBrowsersGroup(group *gin.RouterGroup, config models.GlobalConfig) {
InstanceManager: instanceManager, InstanceManager: instanceManager,
} }
group.GET("", auth.AuthorizedTo(0), connection.GetBrowsers) group.GET("", auth.AuthorizedTo(0), auth.AuthorizationEnforcer(), connection.GetBrowsers)
group.POST("/:server_id/stop", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Browse), connection.StopBrowser) group.POST("/:server_id/stop", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Browse), auth.AuthorizationEnforcer(), connection.StopBrowser)
} }

View File

@ -70,5 +70,5 @@ func LoadeImagesGroup(group *gin.RouterGroup, config models.GlobalConfig) {
InstanceManager: instanceManager, InstanceManager: instanceManager,
} }
group.GET("", auth.AuthorizedTo(0), connection.GetImages) group.GET("", auth.AuthorizedTo(0), auth.AuthorizationEnforcer(), connection.GetImages)
} }

View File

@ -64,37 +64,6 @@ type CreateServerRequest struct {
Nickname string `json:"Nickname"` Nickname string `json:"Nickname"`
} }
func (con ServersApi) ServerAuthorized(permissions models.Permission) func(*gin.Context) {
return func(ctx *gin.Context) {
claimsPointer, exists := ctx.Get("claims")
if !exists {
ctx.AbortWithStatus(403)
return
}
claims := claimsPointer.(*auth.AuthClaims)
serverId := ctx.Param("server_id")
if serverId == "" {
ctx.AbortWithStatus(403)
return
}
userPermissions, err := con.ServerAuthorization.GetPermissions(ctx, claims.Username, serverId)
if err != nil {
ctx.AbortWithError(500, err)
return
}
if userPermissions&permissions == permissions || userPermissions&models.Admin == models.Admin {
return
}
ctx.AbortWithStatus(403)
return
}
}
func (con ServersApi) CreateServer(ctx *gin.Context) { func (con ServersApi) CreateServer(ctx *gin.Context) {
claims, exists := ctx.Get("claims") claims, exists := ctx.Get("claims")
if !exists { if !exists {
@ -168,6 +137,11 @@ func (con ServersApi) StartServer(ctx *gin.Context) {
} }
instanceServer, err := con.InstanceManager.GetServer(ctx, serverId) instanceServer, err := con.InstanceManager.GetServer(ctx, serverId)
if err != nil {
ctx.AbortWithError(500, err)
return
}
if instanceServer.Running { if instanceServer.Running {
ctx.Status(200) ctx.Status(200)
return return
@ -176,6 +150,7 @@ func (con ServersApi) StartServer(ctx *gin.Context) {
server, err := con.ServersDbHandler.GetServer(ctx, serverId) server, err := con.ServersDbHandler.GetServer(ctx, serverId)
if err != nil { if err != nil {
ctx.AbortWithError(500, err) ctx.AbortWithError(500, err)
return
} }
err = con.InstanceManager.StartServer( err = con.InstanceManager.StartServer(
@ -558,15 +533,15 @@ func LoadGroup(group *gin.RouterGroup, config models.GlobalConfig) {
InstanceManager: instanceManager, InstanceManager: instanceManager,
} }
group.POST("/:server_id/start", auth.AuthorizedTo(models.Start), connection.ServerAuthorized(models.Start), connection.StartServer) group.POST("/:server_id/start", auth.AuthorizedTo(models.Start), connection.ServerAuthorized(models.Start), auth.AuthorizationEnforcer(), connection.StartServer)
group.POST("", auth.AuthorizedTo(models.Create), connection.CreateServer) group.POST("", auth.AuthorizedTo(models.Create), auth.AuthorizationEnforcer(), connection.CreateServer)
group.GET("", auth.AuthorizedTo(0), connection.GetServers) group.GET("", connection.GetServers)
group.POST("/:server_id/stop", auth.AuthorizedTo(models.Stop), connection.ServerAuthorized(models.Stop), connection.StopServer) group.POST("/:server_id/stop", auth.AuthorizedTo(models.Stop), connection.ServerAuthorized(models.Stop), auth.AuthorizationEnforcer(), connection.StopServer)
group.DELETE("/:server_id", auth.AuthorizedTo(models.Delete), connection.ServerAuthorized(models.Delete), connection.DeleteServer) group.DELETE("/:server_id", auth.AuthorizedTo(models.Delete), connection.ServerAuthorized(models.Delete), auth.AuthorizationEnforcer(), connection.DeleteServer)
group.POST("/:server_id/run_command", auth.AuthorizedTo(models.RunCommand), connection.ServerAuthorized(models.RunCommand), connection.RunCommand) group.POST("/:server_id/run_command", auth.AuthorizedTo(models.RunCommand), connection.ServerAuthorized(models.RunCommand), auth.AuthorizationEnforcer(), connection.RunCommand)
group.GET("/:server_id/attach", auth.AuthorizedTo(models.RunCommand), connection.ServerAuthorized(models.RunCommand), connection.AttachServer) group.GET("/:server_id/attach", auth.AuthorizedTo(models.RunCommand), connection.ServerAuthorized(models.RunCommand), auth.AuthorizationEnforcer(), connection.AttachServer)
group.PATCH("/:server_id", auth.AuthorizedTo(models.Admin), connection.ServerAuthorized(models.Admin), connection.UpdateServer) group.PATCH("/:server_id", auth.AuthorizedTo(models.Admin), connection.ServerAuthorized(models.Admin), auth.AuthorizationEnforcer(), connection.UpdateServer)
group.POST("/:server_id/browse", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Admin), connection.BrowseServer) group.POST("/:server_id/browse", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Admin), auth.AuthorizationEnforcer(), connection.BrowseServer)
group.GET("/:server_id/permissions", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Admin), connection.GetServerUserPermissions) group.GET("/:server_id/permissions", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Admin), auth.AuthorizationEnforcer(), connection.GetServerUserPermissions)
group.POST("/:server_id/permissions", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Admin), connection.SetServerUserPermissions) group.POST("/:server_id/permissions", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Admin), auth.AuthorizationEnforcer(), connection.SetServerUserPermissions)
} }

View File

@ -122,9 +122,9 @@ func LoadGroup(group *gin.RouterGroup, config models.GlobalConfig) {
config: &config, config: &config,
} }
group.GET("", auth.AuthorizedTo(0), connection.GetUsers) group.GET("", auth.AuthorizedTo(0), auth.AuthorizationEnforcer(), connection.GetUsers)
group.GET("/@me", auth.AuthorizedTo(0), connection.GetUser) group.GET("/@me", auth.AuthorizedTo(0), auth.AuthorizationEnforcer(), connection.GetUser)
group.POST("", auth.AuthorizedTo(models.Admin), connection.InviteUser) group.POST("", auth.AuthorizedTo(models.Admin), auth.AuthorizationEnforcer(), connection.InviteUser)
group.DELETE("/:user_id", auth.AuthorizedTo(models.Admin), connection.DeleteUser) group.DELETE("/:user_id", auth.AuthorizedTo(models.Admin), auth.AuthorizationEnforcer(), connection.DeleteUser)
group.PATCH("/:user_id/permissions", auth.AuthorizedTo(models.Admin), connection.SetUserPermissions) group.PATCH("/:user_id/permissions", auth.AuthorizedTo(models.Admin), auth.AuthorizationEnforcer(), connection.SetUserPermissions)
} }