mirror of
				https://github.com/h44z/wg-portal.git
				synced 2025-10-31 14:36:18 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			111 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			111 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package handlers
 | |
| 
 | |
| import (
 | |
| 	"github.com/gin-gonic/gin"
 | |
| 	"github.com/h44z/wg-portal/internal/app"
 | |
| 	"github.com/h44z/wg-portal/internal/app/api/v0/model"
 | |
| 	"github.com/h44z/wg-portal/internal/domain"
 | |
| 	"net/http"
 | |
| )
 | |
| 
 | |
| type Scope string
 | |
| 
 | |
| const (
 | |
| 	ScopeAdmin   Scope = "ADMIN" // Admin scope contains all other scopes
 | |
| 	ScopeSwagger Scope = "SWAGGER"
 | |
| 	ScopeUser    Scope = "USER"
 | |
| )
 | |
| 
 | |
| type authenticationHandler struct {
 | |
| 	app     *app.App
 | |
| 	Session SessionStore
 | |
| }
 | |
| 
 | |
| // 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) {
 | |
| 		session := h.Session.GetData(c)
 | |
| 
 | |
| 		if !session.LoggedIn {
 | |
| 			// Abort the request with the appropriate error code
 | |
| 			c.Abort()
 | |
| 			c.JSON(http.StatusUnauthorized, model.Error{Code: http.StatusUnauthorized, Message: "not logged in"})
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		if !UserHasScopes(session, 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
 | |
| 		}
 | |
| 
 | |
| 		// Check if logged-in user is still valid
 | |
| 		if !h.app.Authenticator.IsUserValid(c.Request.Context(), domain.UserIdentifier(session.UserIdentifier)) {
 | |
| 			h.Session.DestroyData(c)
 | |
| 			c.Abort()
 | |
| 			c.JSON(http.StatusUnauthorized, model.Error{Code: http.StatusUnauthorized, Message: "session no longer available"})
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		c.Set(domain.CtxUserInfo, &domain.ContextUserInfo{
 | |
| 			Id:      domain.UserIdentifier(session.UserIdentifier),
 | |
| 			IsAdmin: session.IsAdmin,
 | |
| 		})
 | |
| 
 | |
| 		// Continue down the chain to handler etc
 | |
| 		c.Next()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // UserIdMatch checks if the user id in the session matches the user id in the request. If not, the request is aborted.
 | |
| func (h authenticationHandler) UserIdMatch(idParameter string) gin.HandlerFunc {
 | |
| 	return func(c *gin.Context) {
 | |
| 		session := h.Session.GetData(c)
 | |
| 
 | |
| 		if session.IsAdmin {
 | |
| 			c.Next() // Admins can do everything
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		sessionUserId := domain.UserIdentifier(session.UserIdentifier)
 | |
| 		requestUserId := domain.UserIdentifier(Base64UrlDecode(c.Param(idParameter)))
 | |
| 
 | |
| 		if sessionUserId != requestUserId {
 | |
| 			// Abort the request with the appropriate error code
 | |
| 			c.Abort()
 | |
| 			c.JSON(http.StatusForbidden, model.Error{Code: http.StatusForbidden, Message: "not enough permissions"})
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 		// Continue down the chain to handler etc
 | |
| 		c.Next()
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func UserHasScopes(session SessionData, scopes ...Scope) bool {
 | |
| 	// No scopes give, so the check should succeed
 | |
| 	if len(scopes) == 0 {
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	// check if user has admin scope
 | |
| 	if session.IsAdmin {
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	// Check if admin scope is required
 | |
| 	for _, scope := range scopes {
 | |
| 		if scope == ScopeAdmin {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// For all other scopes, a logged-in user is sufficient (for now)
 | |
| 	if session.LoggedIn {
 | |
| 		return true
 | |
| 	}
 | |
| 
 | |
| 	return false
 | |
| }
 |