mirror of
https://github.com/h44z/wg-portal.git
synced 2025-09-14 06:51:15 +00:00
chore: replace gin with standard lib net/http
This commit is contained in:
@@ -4,17 +4,19 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-pkgz/routegroup"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/app/api/core"
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/middleware/cors"
|
||||
"github.com/h44z/wg-portal/internal/app/api/v1/models"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
|
||||
type Handler interface {
|
||||
// GetName returns the name of the handler.
|
||||
GetName() string
|
||||
RegisterRoutes(g *gin.RouterGroup, authenticator *authenticationHandler)
|
||||
// RegisterRoutes registers the routes for the handler. The session manager is passed to the handler.
|
||||
RegisterRoutes(g *routegroup.Bundle)
|
||||
}
|
||||
|
||||
// To compile the API documentation use the
|
||||
@@ -38,18 +40,14 @@ type Handler interface {
|
||||
// @BasePath /api/v1
|
||||
// @query.collection.format multi
|
||||
|
||||
func NewRestApi(userSource UserSource, handlers ...Handler) core.ApiEndpointSetupFunc {
|
||||
authenticator := &authenticationHandler{
|
||||
userSource: userSource,
|
||||
}
|
||||
|
||||
func NewRestApi(handlers ...Handler) core.ApiEndpointSetupFunc {
|
||||
return func() (core.ApiVersion, core.GroupSetupFn) {
|
||||
return "v1", func(group *gin.RouterGroup) {
|
||||
group.Use(cors.Default())
|
||||
return "v1", func(group *routegroup.Bundle) {
|
||||
group.Use(cors.New().Handler)
|
||||
|
||||
// Handler functions
|
||||
for _, h := range handlers {
|
||||
h.RegisterRoutes(group, authenticator)
|
||||
h.RegisterRoutes(group)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,3 +78,12 @@ func ParseServiceError(err error) (int, models.Error) {
|
||||
Message: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
type Authenticator interface {
|
||||
// LoggedIn checks if a user is logged in. If scopes are given, they are validated as well.
|
||||
LoggedIn(scopes ...Scope) func(next http.Handler) http.Handler
|
||||
}
|
||||
|
||||
type Validator interface {
|
||||
Struct(s interface{}) error
|
||||
}
|
||||
|
@@ -4,8 +4,10 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-pkgz/routegroup"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/request"
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/respond"
|
||||
"github.com/h44z/wg-portal/internal/app/api/v1/models"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
@@ -19,12 +21,20 @@ type InterfaceEndpointInterfaceService interface {
|
||||
}
|
||||
|
||||
type InterfaceEndpoint struct {
|
||||
interfaces InterfaceEndpointInterfaceService
|
||||
interfaces InterfaceEndpointInterfaceService
|
||||
authenticator Authenticator
|
||||
validator Validator
|
||||
}
|
||||
|
||||
func NewInterfaceEndpoint(interfaceService InterfaceEndpointInterfaceService) *InterfaceEndpoint {
|
||||
func NewInterfaceEndpoint(
|
||||
authenticator Authenticator,
|
||||
validator Validator,
|
||||
interfaceService InterfaceEndpointInterfaceService,
|
||||
) *InterfaceEndpoint {
|
||||
return &InterfaceEndpoint{
|
||||
interfaces: interfaceService,
|
||||
authenticator: authenticator,
|
||||
validator: validator,
|
||||
interfaces: interfaceService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,15 +42,16 @@ func (e InterfaceEndpoint) GetName() string {
|
||||
return "InterfaceEndpoint"
|
||||
}
|
||||
|
||||
func (e InterfaceEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authenticationHandler) {
|
||||
apiGroup := g.Group("/interface", authenticator.LoggedIn())
|
||||
func (e InterfaceEndpoint) RegisterRoutes(g *routegroup.Bundle) {
|
||||
apiGroup := g.Mount("/interface")
|
||||
apiGroup.Use(e.authenticator.LoggedIn(ScopeAdmin))
|
||||
|
||||
apiGroup.GET("/all", authenticator.LoggedIn(ScopeAdmin), e.handleAllGet())
|
||||
apiGroup.GET("/by-id/:id", authenticator.LoggedIn(ScopeAdmin), e.handleByIdGet())
|
||||
apiGroup.HandleFunc("GET /all", e.handleAllGet())
|
||||
apiGroup.HandleFunc("GET /by-id/{id}", e.handleByIdGet())
|
||||
|
||||
apiGroup.POST("/new", authenticator.LoggedIn(ScopeAdmin), e.handleCreatePost())
|
||||
apiGroup.PUT("/by-id/:id", authenticator.LoggedIn(ScopeAdmin), e.handleUpdatePut())
|
||||
apiGroup.DELETE("/by-id/:id", authenticator.LoggedIn(ScopeAdmin), e.handleDelete())
|
||||
apiGroup.HandleFunc("POST /new", e.handleCreatePost())
|
||||
apiGroup.HandleFunc("PUT /by-id/{id}", e.handleUpdatePut())
|
||||
apiGroup.HandleFunc("DELETE /by-id/{id}", e.handleDelete())
|
||||
}
|
||||
|
||||
// handleAllGet returns a gorm Handler function.
|
||||
@@ -54,17 +65,16 @@ func (e InterfaceEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *aut
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /interface/all [get]
|
||||
// @Security BasicAuth
|
||||
func (e InterfaceEndpoint) handleAllGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
allInterfaces, allPeersPerInterface, err := e.interfaces.GetAll(ctx)
|
||||
func (e InterfaceEndpoint) handleAllGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
allInterfaces, allPeersPerInterface, err := e.interfaces.GetAll(r.Context())
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewInterfaces(allInterfaces, allPeersPerInterface))
|
||||
respond.JSON(w, http.StatusOK, models.NewInterfaces(allInterfaces, allPeersPerInterface))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,23 +92,23 @@ func (e InterfaceEndpoint) handleAllGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /interface/by-id/{id} [get]
|
||||
// @Security BasicAuth
|
||||
func (e InterfaceEndpoint) handleByIdGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e InterfaceEndpoint) handleByIdGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
return
|
||||
}
|
||||
|
||||
iface, interfacePeers, err := e.interfaces.GetById(ctx, domain.InterfaceIdentifier(id))
|
||||
iface, interfacePeers, err := e.interfaces.GetById(r.Context(), domain.InterfaceIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewInterface(iface, interfacePeers))
|
||||
respond.JSON(w, http.StatusOK, models.NewInterface(iface, interfacePeers))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,24 +127,26 @@ func (e InterfaceEndpoint) handleByIdGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /interface/new [post]
|
||||
// @Security BasicAuth
|
||||
func (e InterfaceEndpoint) handleCreatePost() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
func (e InterfaceEndpoint) handleCreatePost() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var iface models.Interface
|
||||
err := c.BindJSON(&iface)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
if err := request.BodyJson(r, &iface); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
if err := e.validator.Struct(iface); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
newInterface, err := e.interfaces.Create(ctx, models.NewDomainInterface(&iface))
|
||||
newInterface, err := e.interfaces.Create(r.Context(), models.NewDomainInterface(&iface))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewInterface(newInterface, nil))
|
||||
respond.JSON(w, http.StatusOK, models.NewInterface(newInterface, nil))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,34 +166,43 @@ func (e InterfaceEndpoint) handleCreatePost() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /interface/by-id/{id} [put]
|
||||
// @Security BasicAuth
|
||||
func (e InterfaceEndpoint) handleUpdatePut() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e InterfaceEndpoint) handleUpdatePut() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
return
|
||||
}
|
||||
|
||||
var iface models.Interface
|
||||
err := c.BindJSON(&iface)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
if err := request.BodyJson(r, &iface); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
if err := e.validator.Struct(iface); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if id != iface.Identifier {
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "interface id mismatch"})
|
||||
return
|
||||
}
|
||||
|
||||
updatedInterface, updatedInterfacePeers, err := e.interfaces.Update(
|
||||
ctx,
|
||||
r.Context(),
|
||||
domain.InterfaceIdentifier(id),
|
||||
models.NewDomainInterface(&iface),
|
||||
)
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewInterface(updatedInterface, updatedInterfacePeers))
|
||||
respond.JSON(w, http.StatusOK, models.NewInterface(updatedInterface, updatedInterfacePeers))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,22 +221,22 @@ func (e InterfaceEndpoint) handleUpdatePut() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /interface/by-id/{id} [delete]
|
||||
// @Security BasicAuth
|
||||
func (e InterfaceEndpoint) handleDelete() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e InterfaceEndpoint) handleDelete() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
return
|
||||
}
|
||||
|
||||
err := e.interfaces.Delete(ctx, domain.InterfaceIdentifier(id))
|
||||
err := e.interfaces.Delete(r.Context(), domain.InterfaceIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusNoContent)
|
||||
respond.Status(w, http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
@@ -4,8 +4,10 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-pkgz/routegroup"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/request"
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/respond"
|
||||
"github.com/h44z/wg-portal/internal/app/api/v1/models"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
@@ -17,12 +19,20 @@ type MetricsEndpointStatisticsService interface {
|
||||
}
|
||||
|
||||
type MetricsEndpoint struct {
|
||||
metrics MetricsEndpointStatisticsService
|
||||
metrics MetricsEndpointStatisticsService
|
||||
authenticator Authenticator
|
||||
validator Validator
|
||||
}
|
||||
|
||||
func NewMetricsEndpoint(metrics MetricsEndpointStatisticsService) *MetricsEndpoint {
|
||||
func NewMetricsEndpoint(
|
||||
authenticator Authenticator,
|
||||
validator Validator,
|
||||
metrics MetricsEndpointStatisticsService,
|
||||
) *MetricsEndpoint {
|
||||
return &MetricsEndpoint{
|
||||
metrics: metrics,
|
||||
authenticator: authenticator,
|
||||
validator: validator,
|
||||
metrics: metrics,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +40,14 @@ func (e MetricsEndpoint) GetName() string {
|
||||
return "MetricsEndpoint"
|
||||
}
|
||||
|
||||
func (e MetricsEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authenticationHandler) {
|
||||
apiGroup := g.Group("/metrics", authenticator.LoggedIn())
|
||||
func (e MetricsEndpoint) RegisterRoutes(g *routegroup.Bundle) {
|
||||
apiGroup := g.Mount("/metrics")
|
||||
apiGroup.Use(e.authenticator.LoggedIn())
|
||||
|
||||
apiGroup.GET("/by-interface/:id", authenticator.LoggedIn(ScopeAdmin), e.handleMetricsForInterfaceGet())
|
||||
apiGroup.GET("/by-user/:id", authenticator.LoggedIn(), e.handleMetricsForUserGet())
|
||||
apiGroup.GET("/by-peer/:id", authenticator.LoggedIn(), e.handleMetricsForPeerGet())
|
||||
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("GET /by-interface/{id}",
|
||||
e.handleMetricsForInterfaceGet())
|
||||
apiGroup.HandleFunc("GET /by-user/{id}", e.handleMetricsForUserGet())
|
||||
apiGroup.HandleFunc("GET /by-peer/{id}", e.handleMetricsForPeerGet())
|
||||
}
|
||||
|
||||
// handleMetricsForInterfaceGet returns a gorm Handler function.
|
||||
@@ -52,23 +64,23 @@ func (e MetricsEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authe
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /metrics/by-interface/{id} [get]
|
||||
// @Security BasicAuth
|
||||
func (e MetricsEndpoint) handleMetricsForInterfaceGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e MetricsEndpoint) handleMetricsForInterfaceGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
return
|
||||
}
|
||||
|
||||
interfaceMetrics, err := e.metrics.GetForInterface(ctx, domain.InterfaceIdentifier(id))
|
||||
interfaceMetrics, err := e.metrics.GetForInterface(r.Context(), domain.InterfaceIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewInterfaceMetrics(interfaceMetrics))
|
||||
respond.JSON(w, http.StatusOK, models.NewInterfaceMetrics(interfaceMetrics))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,23 +98,23 @@ func (e MetricsEndpoint) handleMetricsForInterfaceGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /metrics/by-user/{id} [get]
|
||||
// @Security BasicAuth
|
||||
func (e MetricsEndpoint) handleMetricsForUserGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e MetricsEndpoint) handleMetricsForUserGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
return
|
||||
}
|
||||
|
||||
user, userMetrics, err := e.metrics.GetForUser(ctx, domain.UserIdentifier(id))
|
||||
user, userMetrics, err := e.metrics.GetForUser(r.Context(), domain.UserIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewUserMetrics(user, userMetrics))
|
||||
respond.JSON(w, http.StatusOK, models.NewUserMetrics(user, userMetrics))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,22 +132,22 @@ func (e MetricsEndpoint) handleMetricsForUserGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /metrics/by-peer/{id} [get]
|
||||
// @Security BasicAuth
|
||||
func (e MetricsEndpoint) handleMetricsForPeerGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e MetricsEndpoint) handleMetricsForPeerGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
return
|
||||
}
|
||||
|
||||
peerMetrics, err := e.metrics.GetForPeer(ctx, domain.PeerIdentifier(id))
|
||||
peerMetrics, err := e.metrics.GetForPeer(r.Context(), domain.PeerIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewPeerMetrics(peerMetrics))
|
||||
respond.JSON(w, http.StatusOK, models.NewPeerMetrics(peerMetrics))
|
||||
}
|
||||
}
|
||||
|
@@ -4,8 +4,10 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-pkgz/routegroup"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/request"
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/respond"
|
||||
"github.com/h44z/wg-portal/internal/app/api/v1/models"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
@@ -20,12 +22,19 @@ type PeerService interface {
|
||||
}
|
||||
|
||||
type PeerEndpoint struct {
|
||||
peers PeerService
|
||||
peers PeerService
|
||||
authenticator Authenticator
|
||||
validator Validator
|
||||
}
|
||||
|
||||
func NewPeerEndpoint(peerService PeerService) *PeerEndpoint {
|
||||
func NewPeerEndpoint(
|
||||
authenticator Authenticator,
|
||||
validator Validator, peerService PeerService,
|
||||
) *PeerEndpoint {
|
||||
return &PeerEndpoint{
|
||||
peers: peerService,
|
||||
authenticator: authenticator,
|
||||
validator: validator,
|
||||
peers: peerService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,16 +42,18 @@ func (e PeerEndpoint) GetName() string {
|
||||
return "PeerEndpoint"
|
||||
}
|
||||
|
||||
func (e PeerEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authenticationHandler) {
|
||||
apiGroup := g.Group("/peer", authenticator.LoggedIn())
|
||||
func (e PeerEndpoint) RegisterRoutes(g *routegroup.Bundle) {
|
||||
apiGroup := g.Mount("/peer")
|
||||
apiGroup.Use(e.authenticator.LoggedIn())
|
||||
|
||||
apiGroup.GET("/by-interface/:id", authenticator.LoggedIn(ScopeAdmin), e.handleAllForInterfaceGet())
|
||||
apiGroup.GET("/by-user/:id", authenticator.LoggedIn(), e.handleAllForUserGet())
|
||||
apiGroup.GET("/by-id/:id", authenticator.LoggedIn(), e.handleByIdGet())
|
||||
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("GET /by-interface/{id}",
|
||||
e.handleAllForInterfaceGet())
|
||||
apiGroup.HandleFunc("GET /by-user/{id}", e.handleAllForUserGet())
|
||||
apiGroup.HandleFunc("GET /by-id/{id}", e.handleByIdGet())
|
||||
|
||||
apiGroup.POST("/new", authenticator.LoggedIn(ScopeAdmin), e.handleCreatePost())
|
||||
apiGroup.PUT("/by-id/:id", authenticator.LoggedIn(ScopeAdmin), e.handleUpdatePut())
|
||||
apiGroup.DELETE("/by-id/:id", authenticator.LoggedIn(ScopeAdmin), e.handleDelete())
|
||||
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("POST /new", e.handleCreatePost())
|
||||
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("PUT /by-id/{id}", e.handleUpdatePut())
|
||||
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("DELETE /by-id/{id}", e.handleDelete())
|
||||
}
|
||||
|
||||
// handleAllForInterfaceGet returns a gorm Handler function.
|
||||
@@ -57,23 +68,23 @@ func (e PeerEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authenti
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /peer/by-interface/{id} [get]
|
||||
// @Security BasicAuth
|
||||
func (e PeerEndpoint) handleAllForInterfaceGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e PeerEndpoint) handleAllForInterfaceGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing interface id"})
|
||||
return
|
||||
}
|
||||
|
||||
interfacePeers, err := e.peers.GetForInterface(ctx, domain.InterfaceIdentifier(id))
|
||||
interfacePeers, err := e.peers.GetForInterface(r.Context(), domain.InterfaceIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewPeers(interfacePeers))
|
||||
respond.JSON(w, http.StatusOK, models.NewPeers(interfacePeers))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,23 +101,23 @@ func (e PeerEndpoint) handleAllForInterfaceGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /peer/by-user/{id} [get]
|
||||
// @Security BasicAuth
|
||||
func (e PeerEndpoint) handleAllForUserGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e PeerEndpoint) handleAllForUserGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||
return
|
||||
}
|
||||
|
||||
interfacePeers, err := e.peers.GetForUser(ctx, domain.UserIdentifier(id))
|
||||
interfacePeers, err := e.peers.GetForUser(r.Context(), domain.UserIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewPeers(interfacePeers))
|
||||
respond.JSON(w, http.StatusOK, models.NewPeers(interfacePeers))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,23 +136,23 @@ func (e PeerEndpoint) handleAllForUserGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /peer/by-id/{id} [get]
|
||||
// @Security BasicAuth
|
||||
func (e PeerEndpoint) handleByIdGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e PeerEndpoint) handleByIdGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
return
|
||||
}
|
||||
|
||||
peer, err := e.peers.GetById(ctx, domain.PeerIdentifier(id))
|
||||
peer, err := e.peers.GetById(r.Context(), domain.PeerIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewPeer(peer))
|
||||
respond.JSON(w, http.StatusOK, models.NewPeer(peer))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,24 +172,26 @@ func (e PeerEndpoint) handleByIdGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /peer/new [post]
|
||||
// @Security BasicAuth
|
||||
func (e PeerEndpoint) handleCreatePost() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
func (e PeerEndpoint) handleCreatePost() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var peer models.Peer
|
||||
err := c.BindJSON(&peer)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
if err := request.BodyJson(r, &peer); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
if err := e.validator.Struct(peer); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
newPeer, err := e.peers.Create(ctx, models.NewDomainPeer(&peer))
|
||||
newPeer, err := e.peers.Create(r.Context(), models.NewDomainPeer(&peer))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewPeer(newPeer))
|
||||
respond.JSON(w, http.StatusOK, models.NewPeer(newPeer))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,30 +212,33 @@ func (e PeerEndpoint) handleCreatePost() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /peer/by-id/{id} [put]
|
||||
// @Security BasicAuth
|
||||
func (e PeerEndpoint) handleUpdatePut() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e PeerEndpoint) handleUpdatePut() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
return
|
||||
}
|
||||
|
||||
var peer models.Peer
|
||||
err := c.BindJSON(&peer)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
if err := request.BodyJson(r, &peer); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
if err := e.validator.Struct(peer); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
updatedPeer, err := e.peers.Update(ctx, domain.PeerIdentifier(id), models.NewDomainPeer(&peer))
|
||||
updatedPeer, err := e.peers.Update(r.Context(), domain.PeerIdentifier(id), models.NewDomainPeer(&peer))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewPeer(updatedPeer))
|
||||
respond.JSON(w, http.StatusOK, models.NewPeer(updatedPeer))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,22 +257,22 @@ func (e PeerEndpoint) handleUpdatePut() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /peer/by-id/{id} [delete]
|
||||
// @Security BasicAuth
|
||||
func (e PeerEndpoint) handleDelete() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e PeerEndpoint) handleDelete() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
return
|
||||
}
|
||||
|
||||
err := e.peers.Delete(ctx, domain.PeerIdentifier(id))
|
||||
err := e.peers.Delete(r.Context(), domain.PeerIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusNoContent)
|
||||
respond.Status(w, http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
@@ -5,8 +5,10 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-pkgz/routegroup"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/request"
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/respond"
|
||||
"github.com/h44z/wg-portal/internal/app/api/v1/models"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
@@ -23,12 +25,20 @@ type ProvisioningEndpointProvisioningService interface {
|
||||
}
|
||||
|
||||
type ProvisioningEndpoint struct {
|
||||
provisioning ProvisioningEndpointProvisioningService
|
||||
provisioning ProvisioningEndpointProvisioningService
|
||||
authenticator Authenticator
|
||||
validator Validator
|
||||
}
|
||||
|
||||
func NewProvisioningEndpoint(provisioning ProvisioningEndpointProvisioningService) *ProvisioningEndpoint {
|
||||
func NewProvisioningEndpoint(
|
||||
authenticator Authenticator,
|
||||
validator Validator,
|
||||
provisioning ProvisioningEndpointProvisioningService,
|
||||
) *ProvisioningEndpoint {
|
||||
return &ProvisioningEndpoint{
|
||||
provisioning: provisioning,
|
||||
authenticator: authenticator,
|
||||
validator: validator,
|
||||
provisioning: provisioning,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,14 +46,15 @@ func (e ProvisioningEndpoint) GetName() string {
|
||||
return "ProvisioningEndpoint"
|
||||
}
|
||||
|
||||
func (e ProvisioningEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authenticationHandler) {
|
||||
apiGroup := g.Group("/provisioning", authenticator.LoggedIn())
|
||||
func (e ProvisioningEndpoint) RegisterRoutes(g *routegroup.Bundle) {
|
||||
apiGroup := g.Mount("/provisioning")
|
||||
apiGroup.Use(e.authenticator.LoggedIn())
|
||||
|
||||
apiGroup.GET("/data/user-info", authenticator.LoggedIn(), e.handleUserInfoGet())
|
||||
apiGroup.GET("/data/peer-config", authenticator.LoggedIn(), e.handlePeerConfigGet())
|
||||
apiGroup.GET("/data/peer-qr", authenticator.LoggedIn(), e.handlePeerQrGet())
|
||||
apiGroup.HandleFunc("GET /data/user-info", e.handleUserInfoGet())
|
||||
apiGroup.HandleFunc("GET /data/peer-config", e.handlePeerConfigGet())
|
||||
apiGroup.HandleFunc("GET /data/peer-qr", e.handlePeerQrGet())
|
||||
|
||||
apiGroup.POST("/new-peer", authenticator.LoggedIn(), e.handleNewPeerPost())
|
||||
apiGroup.HandleFunc("POST /new-peer", e.handleNewPeerPost())
|
||||
}
|
||||
|
||||
// handleUserInfoGet returns a gorm Handler function.
|
||||
@@ -63,24 +74,23 @@ func (e ProvisioningEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /provisioning/data/user-info [get]
|
||||
// @Security BasicAuth
|
||||
func (e ProvisioningEndpoint) handleUserInfoGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := strings.TrimSpace(c.Query("UserId"))
|
||||
email := strings.TrimSpace(c.Query("Email"))
|
||||
func (e ProvisioningEndpoint) handleUserInfoGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := strings.TrimSpace(request.Query(r, "UserId"))
|
||||
email := strings.TrimSpace(request.Query(r, "Email"))
|
||||
|
||||
if id == "" && email == "" {
|
||||
id = string(domain.GetUserInfo(ctx).Id)
|
||||
id = string(domain.GetUserInfo(r.Context()).Id)
|
||||
}
|
||||
|
||||
user, peers, err := e.provisioning.GetUserAndPeers(ctx, domain.UserIdentifier(id), email)
|
||||
user, peers, err := e.provisioning.GetUserAndPeers(r.Context(), domain.UserIdentifier(id), email)
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewUserInformation(user, peers))
|
||||
respond.JSON(w, http.StatusOK, models.NewUserInformation(user, peers))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,23 +111,23 @@ func (e ProvisioningEndpoint) handleUserInfoGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /provisioning/data/peer-config [get]
|
||||
// @Security BasicAuth
|
||||
func (e ProvisioningEndpoint) handlePeerConfigGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := strings.TrimSpace(c.Query("PeerId"))
|
||||
func (e ProvisioningEndpoint) handlePeerConfigGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := strings.TrimSpace(request.Query(r, "PeerId"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
return
|
||||
}
|
||||
|
||||
peerConfig, err := e.provisioning.GetPeerConfig(ctx, domain.PeerIdentifier(id))
|
||||
peerConfig, err := e.provisioning.GetPeerConfig(r.Context(), domain.PeerIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.Data(http.StatusOK, "text/plain", peerConfig)
|
||||
respond.Data(w, http.StatusOK, "text/plain", peerConfig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,23 +148,23 @@ func (e ProvisioningEndpoint) handlePeerConfigGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /provisioning/data/peer-qr [get]
|
||||
// @Security BasicAuth
|
||||
func (e ProvisioningEndpoint) handlePeerQrGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := strings.TrimSpace(c.Query("PeerId"))
|
||||
func (e ProvisioningEndpoint) handlePeerQrGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := strings.TrimSpace(request.Query(r, "PeerId"))
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing peer id"})
|
||||
return
|
||||
}
|
||||
|
||||
peerConfigQrCode, err := e.provisioning.GetPeerQrPng(ctx, domain.PeerIdentifier(id))
|
||||
peerConfigQrCode, err := e.provisioning.GetPeerQrPng(r.Context(), domain.PeerIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.Data(http.StatusOK, "image/png", peerConfigQrCode)
|
||||
respond.Data(w, http.StatusOK, "image/png", peerConfigQrCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,23 +184,25 @@ func (e ProvisioningEndpoint) handlePeerQrGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /provisioning/new-peer [post]
|
||||
// @Security BasicAuth
|
||||
func (e ProvisioningEndpoint) handleNewPeerPost() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
func (e ProvisioningEndpoint) handleNewPeerPost() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req models.ProvisioningRequest
|
||||
err := c.BindJSON(&req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
if err := request.BodyJson(r, &req); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
if err := e.validator.Struct(req); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
peer, err := e.provisioning.NewPeer(ctx, req)
|
||||
peer, err := e.provisioning.NewPeer(r.Context(), req)
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewPeer(peer))
|
||||
respond.JSON(w, http.StatusOK, models.NewPeer(peer))
|
||||
}
|
||||
}
|
||||
|
@@ -4,8 +4,10 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-pkgz/routegroup"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/request"
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/respond"
|
||||
"github.com/h44z/wg-portal/internal/app/api/v1/models"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
@@ -19,12 +21,20 @@ type UserService interface {
|
||||
}
|
||||
|
||||
type UserEndpoint struct {
|
||||
users UserService
|
||||
users UserService
|
||||
authenticator Authenticator
|
||||
validator Validator
|
||||
}
|
||||
|
||||
func NewUserEndpoint(userService UserService) *UserEndpoint {
|
||||
func NewUserEndpoint(
|
||||
authenticator Authenticator,
|
||||
validator Validator,
|
||||
userService UserService,
|
||||
) *UserEndpoint {
|
||||
return &UserEndpoint{
|
||||
users: userService,
|
||||
authenticator: authenticator,
|
||||
validator: validator,
|
||||
users: userService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,14 +42,15 @@ func (e UserEndpoint) GetName() string {
|
||||
return "UserEndpoint"
|
||||
}
|
||||
|
||||
func (e UserEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authenticationHandler) {
|
||||
apiGroup := g.Group("/user", authenticator.LoggedIn())
|
||||
func (e UserEndpoint) RegisterRoutes(g *routegroup.Bundle) {
|
||||
apiGroup := g.Mount("/user")
|
||||
apiGroup.Use(e.authenticator.LoggedIn())
|
||||
|
||||
apiGroup.GET("/all", authenticator.LoggedIn(ScopeAdmin), e.handleAllGet())
|
||||
apiGroup.GET("/by-id/:id", authenticator.LoggedIn(), e.handleByIdGet())
|
||||
apiGroup.POST("/new", authenticator.LoggedIn(ScopeAdmin), e.handleCreatePost())
|
||||
apiGroup.PUT("/by-id/:id", authenticator.LoggedIn(ScopeAdmin), e.handleUpdatePut())
|
||||
apiGroup.DELETE("/by-id/:id", authenticator.LoggedIn(ScopeAdmin), e.handleDelete())
|
||||
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("GET /all", e.handleAllGet())
|
||||
apiGroup.HandleFunc("GET /by-id/{id}", e.handleByIdGet())
|
||||
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("POST /new", e.handleCreatePost())
|
||||
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("PUT /by-id/{id}", e.handleUpdatePut())
|
||||
apiGroup.With(e.authenticator.LoggedIn(ScopeAdmin)).HandleFunc("DELETE /by-id/{id}", e.handleDelete())
|
||||
}
|
||||
|
||||
// handleAllGet returns a gorm Handler function.
|
||||
@@ -53,17 +64,16 @@ func (e UserEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authenti
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /user/all [get]
|
||||
// @Security BasicAuth
|
||||
func (e UserEndpoint) handleAllGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
users, err := e.users.GetAll(ctx)
|
||||
func (e UserEndpoint) handleAllGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
users, err := e.users.GetAll(r.Context())
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewUsers(users))
|
||||
respond.JSON(w, http.StatusOK, models.NewUsers(users))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,23 +92,23 @@ func (e UserEndpoint) handleAllGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /user/by-id/{id} [get]
|
||||
// @Security BasicAuth
|
||||
func (e UserEndpoint) handleByIdGet() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e UserEndpoint) handleByIdGet() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||
return
|
||||
}
|
||||
|
||||
user, err := e.users.GetById(ctx, domain.UserIdentifier(id))
|
||||
user, err := e.users.GetById(r.Context(), domain.UserIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewUser(user, true))
|
||||
respond.JSON(w, http.StatusOK, models.NewUser(user, true))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,24 +128,26 @@ func (e UserEndpoint) handleByIdGet() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /user/new [post]
|
||||
// @Security BasicAuth
|
||||
func (e UserEndpoint) handleCreatePost() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
func (e UserEndpoint) handleCreatePost() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var user models.User
|
||||
err := c.BindJSON(&user)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
if err := request.BodyJson(r, &user); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
if err := e.validator.Struct(user); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
newUser, err := e.users.Create(ctx, models.NewDomainUser(&user))
|
||||
newUser, err := e.users.Create(r.Context(), models.NewDomainUser(&user))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewUser(newUser, true))
|
||||
respond.JSON(w, http.StatusOK, models.NewUser(newUser, true))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,30 +168,33 @@ func (e UserEndpoint) handleCreatePost() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /user/by-id/{id} [put]
|
||||
// @Security BasicAuth
|
||||
func (e UserEndpoint) handleUpdatePut() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e UserEndpoint) handleUpdatePut() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||
return
|
||||
}
|
||||
|
||||
var user models.User
|
||||
err := c.BindJSON(&user)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
if err := request.BodyJson(r, &user); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
if err := e.validator.Struct(user); err != nil {
|
||||
respond.JSON(w, http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
updateUser, err := e.users.Update(ctx, domain.UserIdentifier(id), models.NewDomainUser(&user))
|
||||
updateUser, err := e.users.Update(r.Context(), domain.UserIdentifier(id), models.NewDomainUser(&user))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, models.NewUser(updateUser, true))
|
||||
respond.JSON(w, http.StatusOK, models.NewUser(updateUser, true))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,22 +213,22 @@ func (e UserEndpoint) handleUpdatePut() gin.HandlerFunc {
|
||||
// @Failure 500 {object} models.Error
|
||||
// @Router /user/by-id/{id} [delete]
|
||||
// @Security BasicAuth
|
||||
func (e UserEndpoint) handleDelete() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx := domain.SetUserInfoFromGin(c)
|
||||
|
||||
id := c.Param("id")
|
||||
func (e UserEndpoint) handleDelete() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id := request.Path(r, "id")
|
||||
if id == "" {
|
||||
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||
respond.JSON(w, http.StatusBadRequest,
|
||||
models.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||
return
|
||||
}
|
||||
|
||||
err := e.users.Delete(ctx, domain.UserIdentifier(id))
|
||||
err := e.users.Delete(r.Context(), domain.UserIdentifier(id))
|
||||
if err != nil {
|
||||
c.JSON(ParseServiceError(err))
|
||||
status, model := ParseServiceError(err)
|
||||
respond.JSON(w, status, model)
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusNoContent)
|
||||
respond.Status(w, http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
@@ -1,93 +0,0 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/app/api/v0/model"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
|
||||
type Scope string
|
||||
|
||||
const (
|
||||
ScopeAdmin Scope = "ADMIN" // Admin scope contains all other scopes
|
||||
)
|
||||
|
||||
type UserSource interface {
|
||||
GetUser(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
|
||||
}
|
||||
|
||||
type authenticationHandler struct {
|
||||
userSource UserSource
|
||||
}
|
||||
|
||||
// LoggedIn checks if a user is logged in. If scopes are given, they are validated as well.
|
||||
func (h authenticationHandler) LoggedIn(scopes ...Scope) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
username, password, ok := c.Request.BasicAuth()
|
||||
if !ok || username == "" || password == "" {
|
||||
// Abort the request with the appropriate error code
|
||||
c.Abort()
|
||||
c.JSON(http.StatusUnauthorized, model.Error{Code: http.StatusUnauthorized, Message: "missing credentials"})
|
||||
return
|
||||
}
|
||||
|
||||
// check if user exists in DB
|
||||
|
||||
ctx := domain.SetUserInfo(c.Request.Context(), domain.SystemAdminContextUserInfo())
|
||||
user, err := h.userSource.GetUser(ctx, domain.UserIdentifier(username))
|
||||
if err != nil {
|
||||
// Abort the request with the appropriate error code
|
||||
c.Abort()
|
||||
c.JSON(http.StatusUnauthorized, model.Error{Code: http.StatusUnauthorized, Message: "invalid credentials"})
|
||||
return
|
||||
}
|
||||
|
||||
// validate API token
|
||||
if err := user.CheckApiToken(password); err != nil {
|
||||
// Abort the request with the appropriate error code
|
||||
c.Abort()
|
||||
c.JSON(http.StatusUnauthorized, model.Error{Code: http.StatusUnauthorized, Message: "invalid credentials"})
|
||||
return
|
||||
}
|
||||
|
||||
if !UserHasScopes(user, scopes...) {
|
||||
// Abort the request with the appropriate error code
|
||||
c.Abort()
|
||||
c.JSON(http.StatusForbidden, model.Error{Code: http.StatusForbidden, Message: "not enough permissions"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Set(domain.CtxUserInfo, &domain.ContextUserInfo{
|
||||
Id: user.Identifier,
|
||||
IsAdmin: user.IsAdmin,
|
||||
})
|
||||
|
||||
// Continue down the chain to Handler etc
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func UserHasScopes(user *domain.User, scopes ...Scope) bool {
|
||||
// No scopes give, so the check should succeed
|
||||
if len(scopes) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// check if user has admin scope
|
||||
if user.IsAdmin {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if admin scope is required
|
||||
for _, scope := range scopes {
|
||||
if scope == ScopeAdmin {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
101
internal/app/api/v1/handlers/web_authentication.go
Normal file
101
internal/app/api/v1/handlers/web_authentication.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/app/api/core/respond"
|
||||
"github.com/h44z/wg-portal/internal/app/api/v0/model"
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
|
||||
type Scope string
|
||||
|
||||
const (
|
||||
ScopeAdmin Scope = "ADMIN" // Admin scope contains all other scopes
|
||||
)
|
||||
|
||||
type UserAuthenticator interface {
|
||||
GetUser(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
|
||||
}
|
||||
|
||||
type AuthenticationHandler struct {
|
||||
authenticator UserAuthenticator
|
||||
}
|
||||
|
||||
func NewAuthenticationHandler(authenticator UserAuthenticator) AuthenticationHandler {
|
||||
return AuthenticationHandler{
|
||||
authenticator: authenticator,
|
||||
}
|
||||
}
|
||||
|
||||
// LoggedIn checks if a user is logged in. If scopes are given, they are validated as well.
|
||||
func (h AuthenticationHandler) LoggedIn(scopes ...Scope) func(next http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok || username == "" || password == "" {
|
||||
// Abort the request with the appropriate error code
|
||||
respond.JSON(w, http.StatusUnauthorized,
|
||||
model.Error{Code: http.StatusUnauthorized, Message: "missing credentials"})
|
||||
return
|
||||
}
|
||||
|
||||
// check if user exists in DB
|
||||
|
||||
ctx := domain.SetUserInfo(r.Context(), domain.SystemAdminContextUserInfo())
|
||||
user, err := h.authenticator.GetUser(ctx, domain.UserIdentifier(username))
|
||||
if err != nil {
|
||||
// Abort the request with the appropriate error code
|
||||
respond.JSON(w, http.StatusUnauthorized,
|
||||
model.Error{Code: http.StatusUnauthorized, Message: "invalid credentials"})
|
||||
return
|
||||
}
|
||||
|
||||
// validate API token
|
||||
if err := user.CheckApiToken(password); err != nil {
|
||||
// Abort the request with the appropriate error code
|
||||
respond.JSON(w, http.StatusUnauthorized,
|
||||
model.Error{Code: http.StatusUnauthorized, Message: "invalid credentials"})
|
||||
return
|
||||
}
|
||||
|
||||
if !UserHasScopes(user, scopes...) {
|
||||
// Abort the request with the appropriate error code
|
||||
respond.JSON(w, http.StatusForbidden,
|
||||
model.Error{Code: http.StatusForbidden, Message: "not enough permissions"})
|
||||
return
|
||||
}
|
||||
|
||||
ctx = context.WithValue(r.Context(), domain.CtxUserInfo, &domain.ContextUserInfo{
|
||||
Id: user.Identifier,
|
||||
IsAdmin: user.IsAdmin,
|
||||
})
|
||||
r = r.WithContext(ctx)
|
||||
|
||||
// Continue down the chain to Handler etc
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func UserHasScopes(user *domain.User, scopes ...Scope) bool {
|
||||
// No scopes give, so the check should succeed
|
||||
if len(scopes) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// check if user has admin scope
|
||||
if user.IsAdmin {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if admin scope is required
|
||||
for _, scope := range scopes {
|
||||
if scope == ScopeAdmin {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
Reference in New Issue
Block a user