diff --git a/auth/auth.go b/auth/auth.go index 489c0d1..013af4c 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -246,37 +246,6 @@ func (con Connection) verify(c *gin.Context) { c.Redirect(303, fmt.Sprintf("http://%s/login", DOMAIN)) } - -func (con Connection) ServerAuthorized(permissions models.Permission) func(*gin.Context) bool { - return func(ctx *gin.Context) bool { - claims, exists := ctx.Get("claims") - if !exists { - return false - } - - server_id := ctx.Param("server_id") - if server_id == "" { - return false - } - - var serverData models.ServerData - - con.DatabaseConnection.Database("Backend").Collection("Servers").FindOne(context.TODO(), bson.D{{Key: "Id", Value: server_id}}).Decode(&serverData) - - if serverData.OwnerId == claims.(*AuthClaims).Username { - return true - } - - userPermissions := serverData.UserPermissions[claims.(*AuthClaims).Username] - - if userPermissions&permissions == permissions || userPermissions&models.Admin == models.Admin { - return true - } - - return false - } -} - func LoadGroup(group *gin.RouterGroup, client *mongo.Client, config models.GlobalConfig) { connection := Connection{DatabaseConnection: client} diff --git a/db_handler/mongo/user_pass_authentication.go b/db_handler/mongo/user_pass_authentication.go index 7f9668b..63bfef7 100644 --- a/db_handler/mongo/user_pass_authentication.go +++ b/db_handler/mongo/user_pass_authentication.go @@ -7,6 +7,7 @@ import ( "git.acooldomain.co/server-manager/backend-kubernetes-go/dbhandler" "git.acooldomain.co/server-manager/backend-kubernetes-go/models" + "github.com/google/uuid" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" @@ -14,14 +15,11 @@ import ( type AuthUser struct { Username string `json:"username"` + Nickname string `json:"nickname"` HashedPassword string `json:"hashed_password"` Permissions models.Permission `json:"permissions"` -} - -type Invite struct { - Email string `json:"email"` - InvitingUser string `json:"inviting_user"` - Token string `json:"token"` + MaxOwnedSevers uint `json:"max_owned_severs"` + Email string `json:"email"` } type UserPassAuthenticationDbHandler struct { @@ -29,6 +27,29 @@ type UserPassAuthenticationDbHandler struct { collection *mongo.Collection } +func (self *UserPassAuthenticationDbHandler) ListUsers(ctx context.Context) ([]models.User, error) { + cursor, err := self.collection.Find(ctx, nil) + + if err != nil { + return nil, err + } + + var authUsers []AuthUser + + cursor.All(ctx, &authUsers) + + modelUsers := make([]models.User, len(authUsers)) + for i, authUser := range authUsers { + modelUsers[i] = models.User{ + Username: authUser.Username, + Nickname: authUser.Nickname, + Email: authUser.Email, + } + } + + return modelUsers, nil +} + func (self *UserPassAuthenticationDbHandler) AuthenticateUser(ctx context.Context, username string, password string) (models.Permission, error) { var user AuthUser err := self.collection.FindOne(ctx, bson.M{"username": username}).Decode(&user) @@ -55,6 +76,8 @@ func (self *UserPassAuthenticationDbHandler) CreateUser( username string, password string, permissions models.Permission, + email string, + maxOwnedServers uint, ) error { hashedPassword, err := dbhandler.HashPassword(password) @@ -66,6 +89,8 @@ func (self *UserPassAuthenticationDbHandler) CreateUser( Username: username, HashedPassword: hashedPassword, Permissions: permissions, + Email: email, + MaxOwnedSevers: maxOwnedServers, }) return err @@ -121,3 +146,63 @@ func NewUserPassAuthHandler(config models.MongoDBConfig) (*UserPassAuthenticatio collection: client.Database(config.Database).Collection(config.Collection), }, nil } + +type InviteToken struct { + Email string `json:"email"` + Token string `json:"token"` + Permissions models.Permission `json:"permissions"` +} + +type InviteTokenDbHandler struct { + dbhandler.InviteTokenDbHandler + collection *mongo.Collection +} + +func (self *ServersDbHandler) SaveInviteToken(ctx context.Context, email string, permissions models.Permission) (string, error) { + token := uuid.NewString() + + _, err := self.collection.InsertOne(ctx, &InviteToken{ + Permissions: permissions, + Email: email, + Token: token, + }) + + if err != nil { + return "", err + } + + return token, nil +} + +func (self *ServersDbHandler) GetInviteToken(ctx context.Context, token string) (*dbhandler.InviteToken, error) { + var inviteToken InviteToken + err := self.collection.FindOne(ctx, bson.M{"token": token}).Decode(&inviteToken) + if err != nil { + return nil, err + } + return &dbhandler.InviteToken{ + Email: inviteToken.Email, + Permissions: inviteToken.Permissions, + Token: inviteToken.Token, + }, nil +} + +func NewInviteTokenDbHandler(config models.MongoDBConfig) (*InviteTokenDbHandler, error) { + clientOptions := options.Client().ApplyURI(config.Url).SetAuth(options.Credential{ + Username: config.Username, + Password: config.Password, + }) + + ctx, cancel := context.WithTimeoutCause(context.Background(), 30*time.Second, fmt.Errorf("Timeout")) + defer cancel() + + client, err := mongo.Connect(ctx, clientOptions) + + if err != nil { + return nil, err + } + + return &InviteTokenDbHandler{ + collection: client.Database(config.Database).Collection(config.Collection), + }, nil +} diff --git a/db_handler/user_pass_auth.go b/db_handler/user_pass_auth.go index 1e4c7f0..570c677 100644 --- a/db_handler/user_pass_auth.go +++ b/db_handler/user_pass_auth.go @@ -1,6 +1,8 @@ package dbhandler import ( + "context" + "git.acooldomain.co/server-manager/backend-kubernetes-go/models" "golang.org/x/crypto/bcrypt" ) @@ -10,33 +12,31 @@ func HashPassword(password string) (string, error) { return string(bytes), err } -type InviteUserRequest struct { - Email string `json:"email"` - InvitingUser string `json:"inviting_user"` - Permissions models.Permission `json:"permissions"` -} - type InviteToken struct { - Email string `json:"email"` - Permissions models.Permission `json:"permissions"` - Token string `json:"token"` + Email string + Permissions models.Permission + Token string } type UserSignupRequest struct { - Token InviteToken `json:"token"` - Username string `json:"username"` - Password string `json:"password"` + Token InviteToken + Username string + Password string } type UserPassAuthanticationDbHandler interface { - AuthenticateUser(username string, password string) (models.Permission, error) - CreateUser(username string, password string, permissions models.Permission) error - RemoveUser(username string) error - SetPermissions(username string, permissions models.Permission) error - SetPassword(username string, password string) error + // Read Only + AuthenticateUser(ctx context.Context, username string, password string) (models.Permission, error) + ListUsers(ctx context.Context) ([]models.User, error) + + // Write + CreateUser(ctx context.Context, username string, password string, permissions models.Permission, email string, maxOwnedServers uint) error + RemoveUser(ctx context.Context, username string) error + SetPermissions(ctx context.Context, username string, permissions models.Permission) error + SetPassword(ctx context.Context, username string, password string) error } type InviteTokenDbHandler interface { - SaveInviteToken(token string, email string, permissions models.Permission) error - GetInviteToken(token string) (*InviteToken, error) + SaveInviteToken(ctx context.Context, email string, permissions models.Permission) (string, error) + GetInviteToken(ctx context.Context, token string) (*InviteToken, error) } diff --git a/go.work.sum b/go.work.sum index e429545..7cccae1 100644 --- a/go.work.sum +++ b/go.work.sum @@ -13,6 +13,8 @@ github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NB github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= diff --git a/models/config.go b/models/config.go index d54a60a..33de1b3 100644 --- a/models/config.go +++ b/models/config.go @@ -26,11 +26,17 @@ type OidcAuthConfig struct { ClientSecret string `yaml:"client_secret"` } -type UserPassAuthConfig struct { +type InviteTokenDatabaseConfig struct { Type DatabaseType `yaml:"type"` Mongo *MongoDBConfig `yaml:"mongo"` } +type UserPassAuthConfig struct { + Type DatabaseType `yaml:"type"` + Mongo *MongoDBConfig `yaml:"mongo"` + InviteTokenDatabase InviteTokenDatabaseConfig `yaml:"invite_token_database"` +} + type AuthenticationConfig struct { Type AuthMode `yaml:"type"` Oidc OidcAuthConfig `yaml:"oidc"` diff --git a/servers/browsers.go b/servers/browsers.go index 5f711e6..f30ee8b 100644 --- a/servers/browsers.go +++ b/servers/browsers.go @@ -1,152 +1,71 @@ package servers import ( - "context" - "encoding/json" - "fmt" - "git.acooldomain.co/server-manager/backend-kubernetes-go/auth" + "git.acooldomain.co/server-manager/backend-kubernetes-go/db_handler/mongo" + "git.acooldomain.co/server-manager/backend-kubernetes-go/dbhandler" + instancemanager "git.acooldomain.co/server-manager/backend-kubernetes-go/instance_manager" + "git.acooldomain.co/server-manager/backend-kubernetes-go/instance_manager/docker" "git.acooldomain.co/server-manager/backend-kubernetes-go/models" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types/volume" - "github.com/docker/docker/client" "github.com/gin-gonic/gin" - "go.mongodb.org/mongo-driver/mongo" ) -func (con Connection) getBrowserInfo(volume volume.Volume) (*models.FileBrowser, error) { - serverInfo, err := con.getServerInfo(volume) - if err != nil { - return nil, err - } - - containers, err := con.dockerClient.ContainerList(context.TODO(), container.ListOptions{ - All: true, - Filters: filters.NewArgs(filters.Arg("label", "type=FILE_BROWSER"), filters.Arg("label", fmt.Sprintf("volume_id=%s", volume.Name))), - }) - if err != nil || len(containers) == 0 { - return nil, nil - } - - container := containers[0] - jsonData, err := json.Marshal(container.Labels) - if err != nil { - return nil, err - } - - var browserInfo ContainerLabels - err = json.Unmarshal(jsonData, &browserInfo) - - if err != nil { - return nil, err - } - - return &models.FileBrowser{ - Id: serverInfo.Id, - OwnerId: browserInfo.OwnerId, - ConnectedTo: *serverInfo, - Url: serverInfo.Id[:12] + ".browsers." + DOMAIN, - }, nil - -} -func (con Connection) getBrowserInfoFromServerId(serverId string) (*models.FileBrowser, error) { - serverInfo, err := con.getServerInfoFromId(serverId) - if err != nil { - return nil, err - } - - containers, err := con.dockerClient.ContainerList(context.TODO(), container.ListOptions{ - All: true, - Filters: filters.NewArgs(filters.Arg("label", "type=FILE_BROWSER"), filters.Arg("label", fmt.Sprintf("volume_id=%s", serverInfo.Id))), - }) - if err != nil || len(containers) == 0 { - return nil, nil - } - - container := containers[0] - jsonData, err := json.Marshal(container.Labels) - if err != nil { - return nil, err - } - - var browserInfo ContainerLabels - err = json.Unmarshal(jsonData, &browserInfo) - - if err != nil { - return nil, err - } - - return &models.FileBrowser{ - Id: serverInfo.Id, - OwnerId: browserInfo.OwnerId, - ConnectedTo: *serverInfo, - Url: serverInfo.Id[:12] + ".browsers." + DOMAIN, - }, nil - -} - func (con Connection) GetBrowsers(ctx *gin.Context) { - volumes, err := con.dockerClient.VolumeList( - context.TODO(), - volume.ListOptions{ - Filters: filters.NewArgs(filters.Arg("label", "type=GAME")), - }, - ) - if err != nil { - ctx.AbortWithError(500, err) - } - - var servers []models.FileBrowser = make([]models.FileBrowser, 0, len(volumes.Volumes)) - - for _, volume := range volumes.Volumes { - browserInfo, err := con.getBrowserInfo(*volume) - if err != nil { - ctx.AbortWithError(500, err) - } - if browserInfo == nil { - continue - } - - servers = append(servers, *browserInfo) - } + fileBrowsers, err := con.InstanceManager.ListFileBrowsers(ctx) if err != nil { ctx.AbortWithError(500, err) } - ctx.JSON(200, servers) + ctx.JSON(200, fileBrowsers) } func (con Connection) StopBrowser(ctx *gin.Context) { serverId := ctx.Param("server_id") - containersList, err := con.dockerClient.ContainerList(context.TODO(), container.ListOptions{ - Filters: filters.NewArgs(filters.Arg("label", "volume_id="+serverId), filters.Arg("label", "type=FILE_BROWSER")), - }) + err := con.InstanceManager.StopFileBrowser(ctx, serverId) if err != nil { ctx.AbortWithError(500, err) return } - if len(containersList) == 0 { - ctx.Status(200) - return - } - for _, containerData := range containersList { - con.dockerClient.ContainerStop(context.TODO(), containerData.ID, container.StopOptions{}) - } ctx.Status(200) } -func LoadBrowsersGroup(group *gin.RouterGroup, mongo_client *mongo.Client, config models.GlobalConfig) { - apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - if err != nil { - panic(err) - } - defer apiClient.Close() +func LoadBrowsersGroup(group *gin.RouterGroup, config models.GlobalConfig) { + var instanceManager instancemanager.InstanceManager + var serversDbHandler dbhandler.ServersDbHandler + var serversAuthorizationHandler dbhandler.ServersAuthorizationDbHandler + + var err error + + if config.InstanceManager.Type == models.DOCKER { + instanceManager, err = docker.NewInstanceManager(config.InstanceManager.Docker) + if err != nil { + panic(err) + } + } + + if config.ServersDatabase.Type == models.MONGO { + serversDbHandler, err = mongo.NewServersDbHandler(*config.ServersDatabase.Mongo) + if err != nil { + panic(err) + } + } + + if config.ServersAuthorizationDatabase.Type == models.MONGO { + serversAuthorizationHandler, err = mongo.NewAuthorizationHandler(*config.ServersAuthorizationDatabase.Mongo) + if err != nil { + panic(err) + } + + } + + connection := Connection{ + ServersDbHandler: serversDbHandler, + ServerAuthorization: serversAuthorizationHandler, + InstanceManager: instanceManager, + } - connection := Connection{databaseConnection: mongo_client, dockerClient: apiClient} - authConnection := auth.Connection{DatabaseConnection: mongo_client} group.GET("", auth.AuthorizedTo(0), connection.GetBrowsers) - group.POST("/:server_id/stop", auth.AuthorizedTo(models.Browse, authConnection.ServerAuthorized(models.Browse)), connection.StopBrowser) + group.POST("/:server_id/stop", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Browse), connection.StopBrowser) } diff --git a/servers/images.go b/servers/images.go index 3a25a36..e43050a 100644 --- a/servers/images.go +++ b/servers/images.go @@ -1,46 +1,41 @@ package servers import ( - "context" "fmt" - "strings" "git.acooldomain.co/server-manager/backend-kubernetes-go/auth" + "git.acooldomain.co/server-manager/backend-kubernetes-go/db_handler/mongo" + "git.acooldomain.co/server-manager/backend-kubernetes-go/dbhandler" + instancemanager "git.acooldomain.co/server-manager/backend-kubernetes-go/instance_manager" + "git.acooldomain.co/server-manager/backend-kubernetes-go/instance_manager/docker" "git.acooldomain.co/server-manager/backend-kubernetes-go/models" - "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/client" "github.com/gin-gonic/gin" - "go.mongodb.org/mongo-driver/mongo" ) type ImageData struct { - Id string - Name string - Version string - DisplayName string + Id string `json:"Id"` + Name string `json:"Name"` + Version string `json:"Version"` + DisplayName string `json:"DisplayName"` } -func convertImageToImageData(imageSummary image.Summary) *ImageData { - if len(imageSummary.RepoTags) == 0 { - return nil - } - imageId := imageSummary.RepoTags[0] - splitImageId := strings.Split(imageId, ":") - imageName, imageVersion := splitImageId[0], splitImageId[1] +func convertImageToImageData(instanceImage instancemanager.Image) *ImageData { + imageId := instanceImage.Registry + ":" + instanceImage.Tag + return &ImageData{ Id: imageId, - Name: imageName, - Version: imageVersion, - DisplayName: fmt.Sprintf("%s %s", imageName, imageVersion), + Name: instanceImage.Registry, + Version: instanceImage.Tag, + DisplayName: fmt.Sprintf("%s %s", instanceImage.Registry, instanceImage.Tag), } } -func (con Connection) GetImages(c *gin.Context) { - images, err := con.dockerClient.ImageList(context.TODO(), image.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "type=GAME"))}) +func (con Connection) GetImages(ctx *gin.Context) { + images, err := con.InstanceManager.ListImages(ctx) if err != nil { - c.AbortWithError(500, err) + ctx.AbortWithError(500, err) return } + imagesData := make([]ImageData, 0, len(images)) for _, imageSummary := range images { @@ -52,16 +47,43 @@ func (con Connection) GetImages(c *gin.Context) { imagesData = append(imagesData, *imageData) } - c.JSON(200, imagesData) + ctx.JSON(200, imagesData) } -func LoadeImagesGroup(group *gin.RouterGroup, mongo_client *mongo.Client, config models.GlobalConfig) { - apiClient, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - if err != nil { - panic(err) - } - defer apiClient.Close() +func LoadeImagesGroup(group *gin.RouterGroup, config models.GlobalConfig) { + var instanceManager instancemanager.InstanceManager + var serversDbHandler dbhandler.ServersDbHandler + var serversAuthorizationHandler dbhandler.ServersAuthorizationDbHandler + + var err error + + if config.InstanceManager.Type == models.DOCKER { + instanceManager, err = docker.NewInstanceManager(config.InstanceManager.Docker) + if err != nil { + panic(err) + } + } + + if config.ServersDatabase.Type == models.MONGO { + serversDbHandler, err = mongo.NewServersDbHandler(*config.ServersDatabase.Mongo) + if err != nil { + panic(err) + } + } + + if config.ServersAuthorizationDatabase.Type == models.MONGO { + serversAuthorizationHandler, err = mongo.NewAuthorizationHandler(*config.ServersAuthorizationDatabase.Mongo) + if err != nil { + panic(err) + } + + } + + connection := Connection{ + ServersDbHandler: serversDbHandler, + ServerAuthorization: serversAuthorizationHandler, + InstanceManager: instanceManager, + } - connection := Connection{databaseConnection: mongo_client, dockerClient: apiClient} group.GET("", auth.AuthorizedTo(0), connection.GetImages) } diff --git a/servers/servers.go b/servers/servers.go index 83dd58d..0802f8b 100644 --- a/servers/servers.go +++ b/servers/servers.go @@ -1,7 +1,6 @@ package servers import ( - "context" "encoding/json" "errors" "fmt" @@ -19,7 +18,6 @@ import ( "git.acooldomain.co/server-manager/backend-kubernetes-go/models" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" - v1 "github.com/opencontainers/image-spec/specs-go/v1" ) var upgrader = websocket.Upgrader{ @@ -67,6 +65,37 @@ type CreateServerRequest struct { Nickname string `json:"Nickname"` } +func (con Connection) 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 Connection) CreateServer(ctx *gin.Context) { claims, exists := ctx.Get("claims") if !exists { @@ -109,6 +138,12 @@ func (con Connection) CreateServer(ctx *gin.Context) { return } + err = con.ServerAuthorization.AddPermissions(ctx, serverClaims.Username, instanceServer.Id, models.Admin) + if err != nil { + ctx.AbortWithError(500, err) + return + } + ctx.JSON(200, instanceServer.Id) } @@ -540,17 +575,16 @@ func LoadGroup(group *gin.RouterGroup, config models.GlobalConfig) { ServerAuthorization: serversAuthorizationHandler, InstanceManager: instanceManager, } - authConnection := auth.Connection{} - group.POST("/:server_id/start", auth.AuthorizedTo(models.Start, authConnection.ServerAuthorized(models.Start)), connection.StartServer) + group.POST("/:server_id/start", auth.AuthorizedTo(models.Start), connection.ServerAuthorized(models.Start), connection.StartServer) group.POST("", auth.AuthorizedTo(models.Create), connection.CreateServer) group.GET("", auth.AuthorizedTo(0), connection.GetServers) - group.POST("/:server_id/stop", auth.AuthorizedTo(models.Stop, authConnection.ServerAuthorized(models.Stop)), connection.StopServer) - group.DELETE("/:server_id", auth.AuthorizedTo(models.Delete, authConnection.ServerAuthorized(models.Delete)), connection.DeleteServer) - group.POST("/:server_id/run_command", auth.AuthorizedTo(models.RunCommand, authConnection.ServerAuthorized(models.RunCommand)), connection.RunCommand) - group.GET("/:server_id/attach", auth.AuthorizedTo(models.RunCommand, authConnection.ServerAuthorized(models.RunCommand)), connection.AttachServer) - group.PATCH("/:server_id", auth.AuthorizedTo(models.Admin, authConnection.ServerAuthorized(models.Admin)), connection.UpdateServer) - group.POST("/:server_id/browse", auth.AuthorizedTo(models.Browse, authConnection.ServerAuthorized(models.Admin)), connection.BrowseServer) - group.GET("/:server_id/permissions", auth.AuthorizedTo(models.Browse, authConnection.ServerAuthorized(models.Admin)), connection.GetServerUserPermissions) - group.POST("/:server_id/permissions", auth.AuthorizedTo(models.Browse, authConnection.ServerAuthorized(models.Admin)), connection.SetServerUserPermissions) + group.POST("/:server_id/stop", auth.AuthorizedTo(models.Stop), connection.ServerAuthorized(models.Stop), connection.StopServer) + group.DELETE("/:server_id", auth.AuthorizedTo(models.Delete), connection.ServerAuthorized(models.Delete), connection.DeleteServer) + group.POST("/:server_id/run_command", auth.AuthorizedTo(models.RunCommand), connection.ServerAuthorized(models.RunCommand), connection.RunCommand) + group.GET("/:server_id/attach", auth.AuthorizedTo(models.RunCommand), connection.ServerAuthorized(models.RunCommand), connection.AttachServer) + group.PATCH("/:server_id", auth.AuthorizedTo(models.Admin), connection.ServerAuthorized(models.Admin), connection.UpdateServer) + group.POST("/:server_id/browse", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Admin), connection.BrowseServer) + group.GET("/:server_id/permissions", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Admin), connection.GetServerUserPermissions) + group.POST("/:server_id/permissions", auth.AuthorizedTo(models.Browse), connection.ServerAuthorized(models.Admin), connection.SetServerUserPermissions) } diff --git a/users/users.go b/users/users.go index 5a64f21..ebd0ac2 100644 --- a/users/users.go +++ b/users/users.go @@ -1,49 +1,49 @@ package users import ( - "context" "encoding/json" "net/http" "git.acooldomain.co/server-manager/backend-kubernetes-go/auth" + "git.acooldomain.co/server-manager/backend-kubernetes-go/db_handler/mongo" "git.acooldomain.co/server-manager/backend-kubernetes-go/dbhandler" "git.acooldomain.co/server-manager/backend-kubernetes-go/mail" "git.acooldomain.co/server-manager/backend-kubernetes-go/models" "github.com/gin-gonic/gin" - "github.com/google/uuid" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo" ) type Connection struct { - connection *dbhandler.UsersDBHandler - config *models.GlobalConfig + userPassAuthHandler dbhandler.UserPassAuthanticationDbHandler + tokenHandler dbhandler.InviteTokenDbHandler + mailClient mail.MailClient + config *models.GlobalConfig } type UserResponse struct { - Username string - Email string - Permissions models.Permission + Username string `json:"Username"` + Email string `json:"Email"` + Permissions models.Permission `json:"Permissions"` } -func (con Connection) GetUsers(c *gin.Context) { - users, err := (*con.connection).ListUsers() +func (con Connection) GetUsers(ctx *gin.Context) { + users, err := con.userPassAuthHandler.ListUsers(ctx) if err != nil { - panic(err) - } - - c.JSON(http.StatusOK, users) -} - -func (con Connection) GetUser(c *gin.Context) { - claims, exists := c.Get("claims") - if !exists { - c.AbortWithStatus(505) + ctx.AbortWithError(500, err) return } - c.IndentedJSON(http.StatusOK, UserResponse{ + ctx.JSON(http.StatusOK, users) +} + +func (con Connection) GetUser(ctx *gin.Context) { + claims, exists := ctx.Get("claims") + if !exists { + ctx.AbortWithStatus(403) + return + } + + ctx.IndentedJSON(http.StatusOK, UserResponse{ Username: claims.(*auth.AuthClaims).Username, Permissions: claims.(*auth.AuthClaims).Permissions, }) @@ -54,63 +54,87 @@ type InviteUser struct { Permissions models.Permission `json:"Permissions"` } -func (con Connection) InviteUser(c *gin.Context) { +func (con Connection) InviteUser(ctx *gin.Context) { var request InviteUser - json.NewDecoder(c.Request.Body).Decode(&request) - token := uuid.NewString() + json.NewDecoder(ctx.Request.Body).Decode(&request) + + token, err := con.tokenHandler.SaveInviteToken(ctx, request.Email, request.Permissions) - err := mail.SendMail(request.Email, "You've been invited to join", "please open this link https://games.acooldomain.co/signup?token="+token) if err != nil { - c.AbortWithError(500, err) + ctx.AbortWithError(500, err) return } - con.connection.Database("Backend").Collection("Tokens").InsertOne(context.TODO(), auth.InviteToken{ - Email: request.Email, - Permissions: request.Permissions, - Token: token, - }) - c.JSON(200, "OK") + + err = con.mailClient.SendMail(request.Email, "You've been invited to join", "please open this link https://games.acooldomain.co/signup?token="+token) + if err != nil { + ctx.AbortWithError(500, err) + return + } + + ctx.JSON(200, "OK") } type SetUserPermissionsRequest struct { - Permissions models.Permission + Permissions models.Permission `json:"Permissions"` } -func (con Connection) SetUserPermissions(c *gin.Context) { +func (con Connection) SetUserPermissions(ctx *gin.Context) { var request SetUserPermissionsRequest - json.NewDecoder(c.Request.Body).Decode(&request) - username := c.Param("user_id") + json.NewDecoder(ctx.Request.Body).Decode(&request) + username := ctx.Param("user_id") - _, err := con.connection.Database("Backend").Collection("Users").UpdateOne( - context.TODO(), - bson.D{{Key: "Username", Value: username}}, - bson.D{{Key: "$set", Value: bson.D{{Key: "Permissions", Value: request.Permissions}}}}, - ) + err := con.userPassAuthHandler.SetPermissions(ctx, username, request.Permissions) if err != nil { - c.AbortWithError(500, err) + ctx.AbortWithError(500, err) return } - c.JSON(200, "OK") + ctx.JSON(200, "OK") } -func (con Connection) DeleteUser(c *gin.Context) { - username := c.Param("user_id") +func (con Connection) DeleteUser(ctx *gin.Context) { + username := ctx.Param("user_id") + + err := con.userPassAuthHandler.RemoveUser(ctx, username) - _, err := con.connection.Database("Backend").Collection("Users").DeleteOne( - context.TODO(), - bson.D{{Key: "Username", Value: username}}, - ) if err != nil { - c.AbortWithError(500, err) + ctx.AbortWithError(500, err) return } - c.JSON(200, "OK") + ctx.JSON(200, "OK") } -func LoadGroup(group *gin.RouterGroup, client *mongo.Client, config models.GlobalConfig) { - connection := Connection{connection: client} +func LoadGroup(group *gin.RouterGroup, config models.GlobalConfig) { + var userAuthHandler dbhandler.UserPassAuthanticationDbHandler + var inviteHandler dbhandler.InviteTokenDbHandler + var mailClient mail.MailClient + + var err error + + if config.Authentication.UserPass.Type == models.MONGO { + userAuthHandler, err = mongo.NewUserPassAuthHandler(*config.Authentication.UserPass.Mongo) + if err != nil { + panic(err) + } + } + + if config.Authentication.UserPass.InviteTokenDatabase.Type == models.MONGO { + inviteHandler, err = mongo.NewInviteTokenDbHandler(*config.Authentication.UserPass.Mongo) + if err != nil { + panic(err) + } + } + + mailClient = *mail.NewMailClient(config.Email) + + connection := Connection{ + userPassAuthHandler: userAuthHandler, + tokenHandler: inviteHandler, + mailClient: mailClient, + config: &config, + } + group.GET("", auth.AuthorizedTo(0), connection.GetUsers) group.GET("/@me", auth.AuthorizedTo(0), connection.GetUser) group.POST("", auth.AuthorizedTo(models.Admin), connection.InviteUser)