wg-portal/internal/app/api/v1/handlers/middleware_authentication.go
h44z d596f578f6
API - CRUD for peers, interfaces and users (#340)
Public REST API implementation to handle peers, interfaces and users. It also includes some simple provisioning endpoints.

The Swagger API documentation is available under /api/v1/doc.html
2025-01-11 18:44:55 +01:00

93 lines
2.3 KiB
Go

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
}