Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Implement authorization #17

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion database/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Function struct {
Language string `json:"language"`
Built bool `json:"built"` // The builder has built the image for this function

OwnerID int `json:"owner_id"`
OwnerID uint `json:"owner_id"` // todo: change userID to uuid
Owner User `json:"-"`

}
2 changes: 1 addition & 1 deletion database/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

// put
var (
models = []interface{}{&User{}, Function{}, &FunctionState{}}
models = []interface{}{&User{}, Function{}}
)

func Init(dsn string) *gorm.DB {
Expand Down
137 changes: 123 additions & 14 deletions routes/function/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,44 @@ import (
"gorm.io/gorm"
)

const (
NotOwner = "You are not the owner of this function"
NotFound = "Function not found!"
DBError = "Database error!"
)

type Controller struct {
CodeStorageService *objectStorage.CodeStorageService
DB *gorm.DB
BuilderEndpoint string
Scheduler *scheduler.Scheduler
CodeStorageService *objectStorage.CodeStorageService
DB *gorm.DB
BuilderEndpoint string
Scheduler *scheduler.Scheduler
}

// Checks if said user owns the function in order to perform CRUD operations
func (c *Controller) IsOwner(userId uint, fnId string) (isOwner bool, err error) {
var res database.Function
isOwner = false

tx := c.DB.Where(
"ID = ?",
fnId,
).Select(
"OwnerID", // because we don't need the whole object
).First(&res)

err = tx.Error

if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
log.Println(err.Error())

return
}

log.Println("OwnerID: ", res.OwnerID, "UserID: ", userId)

isOwner = tx.RowsAffected > 0 && res.OwnerID == userId

return
}

func (cont *Controller) GetAllFunction(c *gin.Context) {
Expand All @@ -34,16 +67,33 @@ func (cont *Controller) GetAllFunction(c *gin.Context) {

func (cont *Controller) GetOneFunction(c *gin.Context) {
id := c.Param("id")
userID := c.GetUint("userID")

var function database.Function
result := cont.DB.Find(&function, "ID = ?", id)

if result.Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Database error!"})
c.JSON(http.StatusNotFound, gin.H{"error": DBError})
return
}
if result.RowsAffected == 0 {
c.JSON(http.StatusNotFound, gin.H{"error": "Function not found!"})
c.JSON(http.StatusNotFound, gin.H{"error": NotFound})
return
}

isOwner, err := cont.IsOwner(userID, id)

if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
log.Println(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": DBError})
} else if err != nil {
// if the function does not exist, we return a 404
c.JSON(http.StatusNotFound, gin.H{"error": NotFound})
return
}

if !isOwner {
c.JSON(http.StatusForbidden, gin.H{"error": NotOwner})
return
}

Expand All @@ -65,22 +115,40 @@ func (cont *Controller) GetOneFunction(c *gin.Context) {

func (cont *Controller) PostFunction(c *gin.Context) {
id := uuid.New()
userID := c.GetUint("userID")

// does the user exist?
var user database.User
err := cont.DB.First(&user, userID).Error

if errors.Is(err, gorm.ErrRecordNotFound) {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found!"})
return
}

if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": DBError})
return
}

var dto FunctionDTO
if err := c.ShouldBindJSON(&dto); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

var function = database.Function{
ID: id,
Name: dto.Name,
Description: dto.Description,
Language: dto.Language,
ID: id,
Name: dto.Name,
Description: dto.Description,
Language: dto.Language,
OwnerID: userID,
Built: false,
}
cont.DB.Create(&function)

cont.CodeStorageService.PutCode(id, dto.Files)
err := cont.buildImage(id.String(), dto.Language, dto.Files)
err = cont.buildImage(id.String(), dto.Language, dto.Files)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Unable to build image!"})
return
Expand All @@ -90,13 +158,32 @@ func (cont *Controller) PostFunction(c *gin.Context) {
}

func (cont *Controller) PutFunction(c *gin.Context) {
userID := c.GetUint("userID")

var json FunctionDTO
if err := c.ShouldBindJSON(&json); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}

var uuid uuid.UUID = uuid.MustParse(c.Param("id"))

isOwner, err := cont.IsOwner(userID, uuid.String())

if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
log.Println(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": DBError})
} else if err != nil {
// if the function does not exist, we return a 404
c.JSON(http.StatusNotFound, gin.H{"error": NotFound})
return
}

if !isOwner {
c.JSON(http.StatusForbidden, gin.H{"error": NotOwner})
return
}

var function = database.Function{
ID: uuid,
Name: json.Name,
Expand All @@ -107,7 +194,7 @@ func (cont *Controller) PutFunction(c *gin.Context) {
cont.DB.Save(function)

cont.CodeStorageService.PutCode(uuid, json.Files)
err := cont.buildImage(uuid.String(), json.Language, json.Files)
err = cont.buildImage(uuid.String(), json.Language, json.Files)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Unable to build image!"})
return
Expand All @@ -118,9 +205,31 @@ func (cont *Controller) PutFunction(c *gin.Context) {

func (cont *Controller) DeleteFunction(c *gin.Context) {
var uuid uuid.UUID = uuid.MustParse(c.Param("id"))
userID := c.GetUint("userID")

isOwner, err := cont.IsOwner(userID, uuid.String())

if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
log.Println(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": DBError})
} else if err != nil {
// if the function does not exist, we return a 404
c.JSON(http.StatusNotFound, gin.H{"error": NotFound})
return
}

if !isOwner {
c.JSON(http.StatusForbidden, gin.H{"error": NotOwner})
return
}

if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
log.Println(err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"error": DBError})
}

cont.DB.Delete(&database.Function{ID: uuid})
err := cont.CodeStorageService.DeleteCode(uuid)
err = cont.CodeStorageService.DeleteCode(uuid)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Unable to delete code!"})
return
Expand Down Expand Up @@ -187,7 +296,7 @@ func (c *Controller) RunFunction(ctx *gin.Context) {
err = c.DB.Where(&database.Function{ID: fnID}).First(&fn).Error

if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
ctx.AbortWithStatusJSON(404, gin.H{"error": "Function not found"})
ctx.AbortWithStatusJSON(404, gin.H{"error": NotFound})
return
}

Expand Down
10 changes: 8 additions & 2 deletions routes/function/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ package function

import (
"github.com/do4-2022/grobuzin/objectStorage"
"github.com/do4-2022/grobuzin/routes/user"
"github.com/do4-2022/grobuzin/scheduler"
"github.com/gin-gonic/gin"
"github.com/minio/minio-go/v7"
"gorm.io/gorm"
)

func ConfigureRoutes(router *gin.Engine, db *gorm.DB, minioClient *minio.Client, builderEndpoint string, scheduler scheduler.Scheduler) {
func ConfigureRoutes(router *gin.Engine, db *gorm.DB, minioClient *minio.Client, builderEndpoint string, scheduler scheduler.Scheduler, jwtSecret string) {

group := router.Group("/function")

group.Use(user.RequireAuth(jwtSecret))

codeStorageService := objectStorage.CodeStorageService{MinioClient: minioClient}
codeStorageService.Init()

Expand All @@ -22,5 +25,8 @@ func ConfigureRoutes(router *gin.Engine, db *gorm.DB, minioClient *minio.Client,
group.GET("/:id", controller.GetOneFunction)
group.PUT("/:id", controller.PutFunction)
group.DELETE("/:id", controller.DeleteFunction)
group.POST("/:id/run", controller.RunFunction)

execGroup := router.Group("/function")

execGroup.POST("/:id/run", controller.RunFunction)
}
2 changes: 1 addition & 1 deletion routes/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func GetRoutes(db *gorm.DB, JWTSecret string, BuilderEndpoint string, minioClien

log.Println("Setting up routes", requireAuthMiddleware)

function.ConfigureRoutes(router, db, minioClient, BuilderEndpoint, scheduler)
function.ConfigureRoutes(router, db, minioClient, BuilderEndpoint, scheduler, JWTSecret)
user.ConfigureRoutes(router, db, JWTSecret)

return router
Expand Down
2 changes: 0 additions & 2 deletions routes/user/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ func ConfigureRoutes(router *gin.Engine, db *gorm.DB, jwtSecret string) {
controller := Controller{DB: db, JWTSecret: jwtSecret}
group := router.Group("/user")

group.Use(RequireAuth(jwtSecret))

group.POST("/", controller.createUser)
group.POST("/login", controller.login)
}
2 changes: 1 addition & 1 deletion routes/user/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (

type Claims struct {
jwt.RegisteredClaims
ID uint `json:"id"`
ID uint `json:"id"` // User's id
}

func (c *Controller) createJWT(user database.User) (string, error) {
Expand Down
Loading