Introduce middleware for parsing ID from URI

This commit is contained in:
eikendev 2020-08-02 17:06:11 +02:00
parent e1cd2d2f8e
commit 018ce2e537
No known key found for this signature in database
GPG key ID: A1BDB1B28C8EF694
8 changed files with 74 additions and 50 deletions

View file

@ -40,7 +40,7 @@ func (h *ApplicationHandler) applicationExists(token string) bool {
func (h *ApplicationHandler) CreateApplication(ctx *gin.Context) { func (h *ApplicationHandler) CreateApplication(ctx *gin.Context) {
var createApplication model.CreateApplication var createApplication model.CreateApplication
if success := successOrAbort(ctx, http.StatusBadRequest, ctx.Bind(&createApplication)); !success { if err := ctx.Bind(&createApplication); err != nil {
return return
} }
@ -69,13 +69,12 @@ func (h *ApplicationHandler) CreateApplication(ctx *gin.Context) {
// DeleteApplication deletes an application with a certain ID. // DeleteApplication deletes an application with a certain ID.
func (h *ApplicationHandler) DeleteApplication(ctx *gin.Context) { func (h *ApplicationHandler) DeleteApplication(ctx *gin.Context) {
var deleteApplication model.DeleteApplication id, err := getID(ctx)
if err != nil {
if success := successOrAbort(ctx, http.StatusBadRequest, ctx.BindUri(&deleteApplication)); !success {
return return
} }
application, err := h.DB.GetApplicationByID(deleteApplication.ID) application, err := h.DB.GetApplicationByID(id)
if success := successOrAbort(ctx, http.StatusNotFound, err); !success { if success := successOrAbort(ctx, http.StatusNotFound, err); !success {
return return
} }
@ -99,13 +98,12 @@ func (h *ApplicationHandler) DeleteApplication(ctx *gin.Context) {
// UpdateApplication updates an application with a certain ID. // UpdateApplication updates an application with a certain ID.
func (h *ApplicationHandler) UpdateApplication(ctx *gin.Context) { func (h *ApplicationHandler) UpdateApplication(ctx *gin.Context) {
var updateApplication model.UpdateApplication id, err := getID(ctx)
if err != nil {
if success := successOrAbort(ctx, http.StatusBadRequest, ctx.BindUri(&updateApplication)); !success {
return return
} }
application, err := h.DB.GetApplicationByID(updateApplication.ID) application, err := h.DB.GetApplicationByID(id)
if success := successOrAbort(ctx, http.StatusNotFound, err); !success { if success := successOrAbort(ctx, http.StatusNotFound, err); !success {
return return
} }
@ -114,6 +112,12 @@ func (h *ApplicationHandler) UpdateApplication(ctx *gin.Context) {
return return
} }
var updateApplication model.UpdateApplication
if err := ctx.BindUri(&updateApplication); err != nil {
return
}
log.Printf("Updating application %s.\n", application.Name) log.Printf("Updating application %s.\n", application.Name)
// TODO: Handle unbound members. // TODO: Handle unbound members.

36
api/middleware.go Normal file
View file

@ -0,0 +1,36 @@
package api
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
)
type idInURI struct {
ID uint `uri:"id" binding:"required"`
}
// RequireIDInURI returns a Gin middleware which requires an ID to be supplied in the URI of the request.
func RequireIDInURI() gin.HandlerFunc {
return func(ctx *gin.Context) {
var requestModel idInURI
if err := ctx.BindUri(&requestModel); err != nil {
return
}
ctx.Set("id", requestModel.ID)
}
}
func getID(ctx *gin.Context) (uint, error) {
id, ok := ctx.MustGet("user").(uint)
if !ok {
err := errors.New("an error occured while retrieving ID from context")
ctx.AbortWithError(http.StatusInternalServerError, err)
return 0, err
}
return id, nil
}

View file

@ -31,7 +31,7 @@ type NotificationHandler struct {
func (h *NotificationHandler) CreateNotification(ctx *gin.Context) { func (h *NotificationHandler) CreateNotification(ctx *gin.Context) {
var notification model.Notification var notification model.Notification
if success := successOrAbort(ctx, http.StatusBadRequest, ctx.Bind(&notification)); !success { if err := ctx.Bind(&notification); err != nil {
return return
} }

View file

@ -13,7 +13,7 @@ import (
// The UserDatabase interface for encapsulating database access. // The UserDatabase interface for encapsulating database access.
type UserDatabase interface { type UserDatabase interface {
CreateUser(user model.ExternalUserWithCredentials) (*model.User, error) CreateUser(user model.CreateUser) (*model.User, error)
DeleteUser(user *model.User) error DeleteUser(user *model.User) error
UpdateUser(user *model.User) error UpdateUser(user *model.User) error
GetUserByID(ID uint) (*model.User, error) GetUserByID(ID uint) (*model.User, error)
@ -57,9 +57,9 @@ func (h *UserHandler) ensureIsNotLastAdmin(ctx *gin.Context) (int, error) {
// CreateUser creates a new user. // CreateUser creates a new user.
// This method assumes that the requesting user has privileges. // This method assumes that the requesting user has privileges.
func (h *UserHandler) CreateUser(ctx *gin.Context) { func (h *UserHandler) CreateUser(ctx *gin.Context) {
var externalUser model.ExternalUserWithCredentials var externalUser model.CreateUser
if success := successOrAbort(ctx, http.StatusBadRequest, ctx.Bind(&externalUser)); !success { if err := ctx.Bind(&externalUser); err != nil {
return return
} }
@ -81,13 +81,12 @@ func (h *UserHandler) CreateUser(ctx *gin.Context) {
// //
// This method assumes that the requesting user has privileges. // This method assumes that the requesting user has privileges.
func (h *UserHandler) DeleteUser(ctx *gin.Context) { func (h *UserHandler) DeleteUser(ctx *gin.Context) {
var deleteUser model.DeleteUser id, err := getID(ctx)
if err != nil {
if success := successOrAbort(ctx, http.StatusBadRequest, ctx.BindUri(&deleteUser)); !success {
return return
} }
user, err := h.DB.GetUserByID(deleteUser.ID) user, err := h.DB.GetUserByID(id)
if success := successOrAbort(ctx, http.StatusNotFound, err); !success { if success := successOrAbort(ctx, http.StatusNotFound, err); !success {
return return
} }
@ -125,17 +124,22 @@ func (h *UserHandler) DeleteUser(ctx *gin.Context) {
// This method assumes that the requesting user has privileges. If users can later update their own user, make sure they // This method assumes that the requesting user has privileges. If users can later update their own user, make sure they
// cannot give themselves privileges. // cannot give themselves privileges.
func (h *UserHandler) UpdateUser(ctx *gin.Context) { func (h *UserHandler) UpdateUser(ctx *gin.Context) {
var updateUser model.UpdateUser id, err := getID(ctx)
if err != nil {
if success := successOrAbort(ctx, http.StatusBadRequest, ctx.BindUri(&updateUser)); !success {
return return
} }
user, err := h.DB.GetUserByID(updateUser.ID) user, err := h.DB.GetUserByID(id)
if success := successOrAbort(ctx, http.StatusNotFound, err); !success { if success := successOrAbort(ctx, http.StatusNotFound, err); !success {
return return
} }
var updateUser model.UpdateUser
if err := ctx.BindUri(&updateUser); err != nil {
return
}
currentUser := authentication.GetUser(ctx) currentUser := authentication.GetUser(ctx)
// Last privileged user must not be taken privileges. Assumes that the current user has privileges. // Last privileged user must not be taken privileges. Assumes that the current user has privileges.

View file

@ -10,7 +10,7 @@ import (
) )
// CreateUser creates a user. // CreateUser creates a user.
func (d *Database) CreateUser(externalUser model.ExternalUserWithCredentials) (*model.User, error) { func (d *Database) CreateUser(externalUser model.CreateUser) (*model.User, error) {
user := externalUser.IntoInternalUser(d.credentialsManager) user := externalUser.IntoInternalUser(d.credentialsManager)
return user, d.gormdb.Create(user).Error return user, d.gormdb.Create(user).Error

View file

@ -14,17 +14,7 @@ type CreateApplication struct {
Name string `form:"name" query:"name" json:"name" binding:"required"` Name string `form:"name" query:"name" json:"name" binding:"required"`
} }
type applicationIdentification struct {
ID uint `uri:"id" binding:"required"`
}
// DeleteApplication is used to process queries for deleting applications.
type DeleteApplication struct {
applicationIdentification
}
// UpdateApplication is used to process queries for updating applications. // UpdateApplication is used to process queries for updating applications.
type UpdateApplication struct { type UpdateApplication struct {
applicationIdentification
Name string `json:"name"` Name string `json:"name"`
} }

View file

@ -29,8 +29,8 @@ type UserCredentials struct {
Password string `json:"password,omitempty" form:"password" query:"password" binding:"required"` Password string `json:"password,omitempty" form:"password" query:"password" binding:"required"`
} }
// ExternalUserWithCredentials represents a user for external purposes and includes the user's credentials in plaintext. // CreateUser is used to process queries for creating users.
type ExternalUserWithCredentials struct { type CreateUser struct {
ExternalUser ExternalUser
UserCredentials UserCredentials
} }
@ -47,8 +47,8 @@ func NewUser(cm *credentials.Manager, name, password string, isAdmin bool, matri
} }
} }
// IntoInternalUser converts a ExternalUserWithCredentials into a User. // IntoInternalUser converts a CreateUser into a User.
func (u *ExternalUserWithCredentials) IntoInternalUser(cm *credentials.Manager) *User { func (u *CreateUser) IntoInternalUser(cm *credentials.Manager) *User {
return &User{ return &User{
Name: u.Name, Name: u.Name,
PasswordHash: cm.CreatePasswordHash(u.Password), PasswordHash: cm.CreatePasswordHash(u.Password),
@ -67,18 +67,8 @@ func (u *User) IntoExternalUser() *ExternalUser {
} }
} }
type userIdentification struct {
ID uint `uri:"id" binding:"required"`
}
// DeleteUser is used to process queries for deleting users.
type DeleteUser struct {
userIdentification
}
// UpdateUser is used to process queries for updating users. // UpdateUser is used to process queries for updating users.
type UpdateUser struct { type UpdateUser struct {
userIdentification
Name string `json:"name"` Name string `json:"name"`
Password string `json:"password"` Password string `json:"password"`
IsAdmin bool `json:"is_admin"` IsAdmin bool `json:"is_admin"`

View file

@ -35,8 +35,8 @@ func Create(debug bool, cm *credentials.Manager, db *database.Database, dp *disp
applicationGroup.Use(auth.RequireUser()) applicationGroup.Use(auth.RequireUser())
{ {
applicationGroup.POST("", applicationHandler.CreateApplication) applicationGroup.POST("", applicationHandler.CreateApplication)
applicationGroup.DELETE("/:id", applicationHandler.DeleteApplication) applicationGroup.DELETE("/:id", api.RequireIDInURI(), applicationHandler.DeleteApplication)
applicationGroup.PUT("/:id", applicationHandler.UpdateApplication) applicationGroup.PUT("/:id", api.RequireIDInURI(), applicationHandler.UpdateApplication)
} }
r.POST("/message", auth.RequireApplicationToken(), notificationHandler.CreateNotification) r.POST("/message", auth.RequireApplicationToken(), notificationHandler.CreateNotification)
@ -45,8 +45,8 @@ func Create(debug bool, cm *credentials.Manager, db *database.Database, dp *disp
userGroup.Use(auth.RequireAdmin()) userGroup.Use(auth.RequireAdmin())
{ {
userGroup.POST("", userHandler.CreateUser) userGroup.POST("", userHandler.CreateUser)
userGroup.DELETE("/:id", userHandler.DeleteUser) userGroup.DELETE("/:id", api.RequireIDInURI(), userHandler.DeleteUser)
userGroup.PUT("/:id", userHandler.UpdateUser) userGroup.PUT("/:id", api.RequireIDInURI(), userHandler.UpdateUser)
} }
return r return r