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

View file

@ -13,7 +13,7 @@ import (
// The UserDatabase interface for encapsulating database access.
type UserDatabase interface {
CreateUser(user model.ExternalUserWithCredentials) (*model.User, error)
CreateUser(user model.CreateUser) (*model.User, error)
DeleteUser(user *model.User) error
UpdateUser(user *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.
// This method assumes that the requesting user has privileges.
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
}
@ -81,13 +81,12 @@ func (h *UserHandler) CreateUser(ctx *gin.Context) {
//
// This method assumes that the requesting user has privileges.
func (h *UserHandler) DeleteUser(ctx *gin.Context) {
var deleteUser model.DeleteUser
if success := successOrAbort(ctx, http.StatusBadRequest, ctx.BindUri(&deleteUser)); !success {
id, err := getID(ctx)
if err != nil {
return
}
user, err := h.DB.GetUserByID(deleteUser.ID)
user, err := h.DB.GetUserByID(id)
if success := successOrAbort(ctx, http.StatusNotFound, err); !success {
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
// cannot give themselves privileges.
func (h *UserHandler) UpdateUser(ctx *gin.Context) {
var updateUser model.UpdateUser
if success := successOrAbort(ctx, http.StatusBadRequest, ctx.BindUri(&updateUser)); !success {
id, err := getID(ctx)
if err != nil {
return
}
user, err := h.DB.GetUserByID(updateUser.ID)
user, err := h.DB.GetUserByID(id)
if success := successOrAbort(ctx, http.StatusNotFound, err); !success {
return
}
var updateUser model.UpdateUser
if err := ctx.BindUri(&updateUser); err != nil {
return
}
currentUser := authentication.GetUser(ctx)
// 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.
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)
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"`
}
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.
type UpdateApplication struct {
applicationIdentification
Name string `json:"name"`
}

View file

@ -29,8 +29,8 @@ type UserCredentials struct {
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.
type ExternalUserWithCredentials struct {
// CreateUser is used to process queries for creating users.
type CreateUser struct {
ExternalUser
UserCredentials
}
@ -47,8 +47,8 @@ func NewUser(cm *credentials.Manager, name, password string, isAdmin bool, matri
}
}
// IntoInternalUser converts a ExternalUserWithCredentials into a User.
func (u *ExternalUserWithCredentials) IntoInternalUser(cm *credentials.Manager) *User {
// IntoInternalUser converts a CreateUser into a User.
func (u *CreateUser) IntoInternalUser(cm *credentials.Manager) *User {
return &User{
Name: u.Name,
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.
type UpdateUser struct {
userIdentification
Name string `json:"name"`
Password string `json:"password"`
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.POST("", applicationHandler.CreateApplication)
applicationGroup.DELETE("/:id", applicationHandler.DeleteApplication)
applicationGroup.PUT("/:id", applicationHandler.UpdateApplication)
applicationGroup.DELETE("/:id", api.RequireIDInURI(), applicationHandler.DeleteApplication)
applicationGroup.PUT("/:id", api.RequireIDInURI(), applicationHandler.UpdateApplication)
}
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.POST("", userHandler.CreateUser)
userGroup.DELETE("/:id", userHandler.DeleteUser)
userGroup.PUT("/:id", userHandler.UpdateUser)
userGroup.DELETE("/:id", api.RequireIDInURI(), userHandler.DeleteUser)
userGroup.PUT("/:id", api.RequireIDInURI(), userHandler.UpdateUser)
}
return r