From d6b32f70f83452c13a9b8c320c15c688d04f204d Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Wed, 21 Jun 2023 22:31:36 +0200 Subject: [PATCH] wip: peer handlers --- .../app/api/v0/handlers/endpoint_peers.go | 208 +++++++++++++++++- internal/app/api/v0/model/models_peer.go | 35 ++- 2 files changed, 234 insertions(+), 9 deletions(-) diff --git a/internal/app/api/v0/handlers/endpoint_peers.go b/internal/app/api/v0/handlers/endpoint_peers.go index dd11b08..57a6ef0 100644 --- a/internal/app/api/v0/handlers/endpoint_peers.go +++ b/internal/app/api/v0/handlers/endpoint_peers.go @@ -20,27 +20,36 @@ func (e peerEndpoint) GetName() string { func (e peerEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authenticationHandler) { apiGroup := g.Group("/peer", e.authenticator.LoggedIn()) - apiGroup.GET("/all/:id", e.handlePeersGet()) + apiGroup.GET("/iface/:iface/all", e.handleAllGet()) + apiGroup.GET("/iface/:iface/prepare", e.handlePrepareGet()) + apiGroup.POST("/iface/:iface/new", e.handleCreatePost()) + apiGroup.GET("/:id", e.handleSingleGet()) + apiGroup.PUT("/:id", e.handleUpdatePut()) + apiGroup.DELETE("/:id", e.handleDelete()) } -// handlePeersGet returns a gorm handler function. +// handleAllGet returns a gorm handler function. // -// @ID peers_handlePeersGet +// @ID peers_handleAllGet // @Tags Peer // @Summary Get peers for the given interface. // @Produce json +// @Param iface path string true "The interface identifier" // @Success 200 {object} []model.Peer +// @Failure 400 {object} model.Error // @Failure 500 {object} model.Error -// @Router /peer/all/{id} [get] -func (e peerEndpoint) handlePeersGet() gin.HandlerFunc { +// @Router /peer/iface/{iface}/all [get] +func (e peerEndpoint) handleAllGet() gin.HandlerFunc { return func(c *gin.Context) { - interfaceId := c.Param("id") + ctx := domain.SetUserInfoFromGin(c) + + interfaceId := c.Param("iface") if interfaceId == "" { - c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusInternalServerError, Message: "missing id parameter"}) + c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing iface parameter"}) return } - _, peers, err := e.app.GetInterfaceAndPeers(c.Request.Context(), domain.InterfaceIdentifier(interfaceId)) + _, peers, err := e.app.GetInterfaceAndPeers(ctx, domain.InterfaceIdentifier(interfaceId)) if err != nil { c.JSON(http.StatusInternalServerError, model.Error{Code: http.StatusInternalServerError, Message: err.Error()}) return @@ -49,3 +58,186 @@ func (e peerEndpoint) handlePeersGet() gin.HandlerFunc { c.JSON(http.StatusOK, model.NewPeers(peers)) } } + +// handleSingleGet returns a gorm handler function. +// +// @ID peers_handleSingleGet +// @Tags Peer +// @Summary Get peer for the given identifier. +// @Produce json +// @Param id path string true "The peer identifier" +// @Success 200 {object} model.Peer +// @Failure 400 {object} model.Error +// @Failure 500 {object} model.Error +// @Router /peer/{id} [get] +func (e peerEndpoint) handleSingleGet() gin.HandlerFunc { + return func(c *gin.Context) { + ctx := domain.SetUserInfoFromGin(c) + + peerId := c.Param("id") + if peerId == "" { + c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing id parameter"}) + return + } + + peer, err := e.app.GetPeer(ctx, domain.PeerIdentifier(peerId)) + if err != nil { + c.JSON(http.StatusInternalServerError, model.Error{Code: http.StatusInternalServerError, Message: err.Error()}) + return + } + + c.JSON(http.StatusOK, model.NewPeer(peer)) + } +} + +// handlePrepareGet returns a gorm handler function. +// +// @ID peers_handlePrepareGet +// @Tags Peer +// @Summary Prepare a new peer for the given interface. +// @Produce json +// @Param iface path string true "The interface identifier" +// @Success 200 {object} model.Peer +// @Failure 400 {object} model.Error +// @Failure 500 {object} model.Error +// @Router /peer/iface/{iface}/prepare [get] +func (e peerEndpoint) handlePrepareGet() gin.HandlerFunc { + return func(c *gin.Context) { + ctx := domain.SetUserInfoFromGin(c) + + interfaceId := c.Param("iface") + if interfaceId == "" { + c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing iface parameter"}) + return + } + + peer, err := e.app.PreparePeer(ctx, domain.InterfaceIdentifier(interfaceId)) + if err != nil { + c.JSON(http.StatusInternalServerError, model.Error{Code: http.StatusInternalServerError, Message: err.Error()}) + return + } + + c.JSON(http.StatusOK, model.NewPeer(peer)) + } +} + +// handleCreatePost returns a gorm handler function. +// +// @ID peers_handleCreatePost +// @Tags Peer +// @Summary Prepare a new peer for the given interface. +// @Produce json +// @Param iface path string true "The interface identifier" +// @Param request body model.Peer true "The peer data" +// @Success 200 {object} model.Peer +// @Failure 400 {object} model.Error +// @Failure 500 {object} model.Error +// @Router /peer/iface/{iface}/new [post] +func (e peerEndpoint) handleCreatePost() gin.HandlerFunc { + return func(c *gin.Context) { + ctx := domain.SetUserInfoFromGin(c) + + interfaceId := c.Param("iface") + if interfaceId == "" { + c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing iface parameter"}) + return + } + + var p model.Peer + err := c.BindJSON(&p) + if err != nil { + c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: err.Error()}) + return + } + + if p.InterfaceIdentifier != interfaceId { + c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "interface id mismatch"}) + return + } + + newPeer, err := e.app.CreatePeer(ctx, model.NewDomainPeer(&p)) + if err != nil { + c.JSON(http.StatusInternalServerError, model.Error{Code: http.StatusInternalServerError, Message: err.Error()}) + return + } + + c.JSON(http.StatusOK, model.NewPeer(newPeer)) + } +} + +// handleUpdatePut returns a gorm handler function. +// +// @ID peers_handleUpdatePut +// @Tags Peer +// @Summary Update the given peer record. +// @Produce json +// @Param id path string true "The peer identifier" +// @Param request body model.Peer true "The peer data" +// @Success 200 {object} model.Peer +// @Failure 400 {object} model.Error +// @Failure 500 {object} model.Error +// @Router /peer/{id} [post] +func (e peerEndpoint) handleUpdatePut() gin.HandlerFunc { + return func(c *gin.Context) { + ctx := domain.SetUserInfoFromGin(c) + + peerId := c.Param("id") + if peerId == "" { + c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing id parameter"}) + return + } + + var p model.Peer + err := c.BindJSON(&p) + if err != nil { + c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: err.Error()}) + return + } + + if p.InterfaceIdentifier != peerId { + c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "peer id mismatch"}) + return + } + + updatedPeer, err := e.app.UpdateInterface(ctx, model.NewDomainPeer(&p)) + if err != nil { + c.JSON(http.StatusInternalServerError, model.Error{Code: http.StatusInternalServerError, Message: err.Error()}) + return + } + + c.JSON(http.StatusOK, model.NewPeer(updatedPeer)) + } +} + +// handleDelete returns a gorm handler function. +// +// @ID peers_handleDelete +// @Tags Peer +// @Summary Delete the peer record. +// @Produce json +// @Param id path string true "The peer identifier" +// @Success 204 "No content if deletion was successful" +// @Failure 400 {object} model.Error +// @Failure 500 {object} model.Error +// @Router /peer/{id} [delete] +func (e peerEndpoint) handleDelete() gin.HandlerFunc { + return func(c *gin.Context) { + ctx := domain.SetUserInfoFromGin(c) + + id := c.Param("id") + if id == "" { + c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing peer id"}) + return + } + + err := e.app.DeletePeer(ctx, domain.PeerIdentifier(id)) + if err != nil { + c.JSON(http.StatusInternalServerError, model.Error{ + Code: http.StatusInternalServerError, Message: err.Error(), + }) + return + } + + c.Status(http.StatusNoContent) + } +} diff --git a/internal/app/api/v0/model/models_peer.go b/internal/app/api/v0/model/models_peer.go index 9a35195..baa2300 100644 --- a/internal/app/api/v0/model/models_peer.go +++ b/internal/app/api/v0/model/models_peer.go @@ -1,6 +1,9 @@ package model -import "github.com/h44z/wg-portal/internal/domain" +import ( + "github.com/h44z/wg-portal/internal/domain" + "time" +) type Peer struct { Identifier string `json:"Identifier" example:"super_nice_peer"` // peer unique identifier @@ -73,3 +76,33 @@ func NewPeers(src []domain.Peer) []Peer { return results } + +func NewDomainPeer(src *Peer) *domain.Peer { + now := time.Now() + + res := &domain.Peer{ + BaseModel: domain.BaseModel{}, + Endpoint: domain.StringConfigOption{}, + EndpointPublicKey: src.EndpointPublicKey, + AllowedIPsStr: domain.StringConfigOption{}, + ExtraAllowedIPsStr: "", + PresharedKey: "", + PersistentKeepalive: domain.IntConfigOption{}, + DisplayName: "", + Identifier: "", + UserIdentifier: "", + InterfaceIdentifier: "", + Temporary: nil, + Disabled: nil, + DisabledReason: "", + ExpiresAt: nil, + Notes: "", + Interface: domain.PeerInterfaceConfig{}, + } + + if src.Disabled { + res.Disabled = &now + } + + return res +}