wg-portal/internal/server/routes.go
2021-04-29 11:23:32 +02:00

212 lines
6.4 KiB
Go

package server
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
wgportal "github.com/h44z/wg-portal"
"github.com/h44z/wg-portal/internal/authentication"
_ "github.com/h44z/wg-portal/internal/server/docs" // docs is generated by Swag CLI, you have to import it.
ginSwagger "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
csrf "github.com/utrack/gin-csrf"
)
func SetupRoutes(s *Server) {
csrfMiddleware := csrf.Middleware(csrf.Options{
Secret: s.config.Core.SessionSecret,
ErrorFunc: func(c *gin.Context) {
c.String(400, "CSRF token mismatch")
c.Abort()
},
})
// Startpage
s.server.GET("/", s.GetIndex)
s.server.GET("/favicon.ico", func(c *gin.Context) {
file, _ := wgportal.Statics.ReadFile("assets/img/favicon.ico")
c.Data(
http.StatusOK,
"image/x-icon",
file,
)
})
// Auth routes
auth := s.server.Group("/auth")
auth.Use(csrfMiddleware)
auth.GET("/login", s.GetLogin)
auth.POST("/login", s.PostLogin)
auth.GET("/logout", s.GetLogout)
// Admin routes
admin := s.server.Group("/admin")
admin.Use(csrfMiddleware)
admin.Use(s.RequireAuthentication("admin"))
admin.GET("/", s.GetAdminIndex)
admin.GET("/device/edit", s.GetAdminEditInterface)
admin.POST("/device/edit", s.PostAdminEditInterface)
admin.GET("/device/download", s.GetInterfaceConfig)
admin.GET("/device/write", s.GetSaveConfig)
admin.GET("/device/applyglobals", s.GetApplyGlobalConfig)
admin.GET("/peer/edit", s.GetAdminEditPeer)
admin.POST("/peer/edit", s.PostAdminEditPeer)
admin.GET("/peer/create", s.GetAdminCreatePeer)
admin.POST("/peer/create", s.PostAdminCreatePeer)
admin.GET("/peer/createldap", s.GetAdminCreateLdapPeers)
admin.POST("/peer/createldap", s.PostAdminCreateLdapPeers)
admin.GET("/peer/delete", s.GetAdminDeletePeer)
admin.GET("/peer/download", s.GetPeerConfig)
admin.GET("/peer/email", s.GetPeerConfigMail)
admin.GET("/users/", s.GetAdminUsersIndex)
admin.GET("/users/create", s.GetAdminUsersCreate)
admin.POST("/users/create", s.PostAdminUsersCreate)
admin.GET("/users/edit", s.GetAdminUsersEdit)
admin.POST("/users/edit", s.PostAdminUsersEdit)
// User routes
user := s.server.Group("/user")
user.Use(csrfMiddleware)
user.Use(s.RequireAuthentication("")) // empty scope = all logged in users
user.GET("/qrcode", s.GetPeerQRCode)
user.GET("/profile", s.GetUserIndex)
user.GET("/download", s.GetPeerConfig)
user.GET("/email", s.GetPeerConfigMail)
user.GET("/status", s.GetPeerStatus)
}
func SetupApiRoutes(s *Server) {
api := ApiServer{s: s}
// Admin authenticated routes
apiV1Backend := s.server.Group("/api/v1/backend")
apiV1Backend.Use(s.RequireApiAuthentication("admin"))
apiV1Backend.GET("/users", api.GetUsers)
apiV1Backend.POST("/users", api.PostUser)
apiV1Backend.GET("/user/:email", api.GetUser)
apiV1Backend.PUT("/user/:email", api.PutUser)
apiV1Backend.PATCH("/user/:email", api.PatchUser)
apiV1Backend.DELETE("/user/:email", api.DeleteUser)
apiV1Backend.GET("/peers/:device", api.GetPeers)
apiV1Backend.POST("/peers/:device", api.PostPeer)
apiV1Backend.GET("/peer/:pkey", api.GetPeer)
apiV1Backend.PUT("/peer/:pkey", api.PutPeer)
apiV1Backend.PATCH("/peer/:pkey", api.PatchPeer)
apiV1Backend.DELETE("/peer/:pkey", api.DeletePeer)
apiV1Backend.GET("/devices", api.GetDevices)
apiV1Backend.GET("/device/:device", api.GetDevice)
apiV1Backend.PUT("/device/:device", api.PutDevice)
apiV1Backend.PATCH("/device/:device", api.PatchDevice)
// Simple authenticated routes
apiV1Deployment := s.server.Group("/api/v1/provisioning")
apiV1Deployment.Use(s.RequireApiAuthentication(""))
apiV1Deployment.GET("/peers/:email", api.GetPeerDeploymentInformation)
apiV1Deployment.GET("/peer/:pkey", api.GetPeerDeploymentConfig)
apiV1Deployment.POST("/peers", api.PostPeerDeploymentConfig)
// Swagger doc/ui
s.server.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
func (s *Server) RequireAuthentication(scope string) gin.HandlerFunc {
return func(c *gin.Context) {
session := GetSessionData(c)
if !session.LoggedIn {
// Abort the request with the appropriate error code
c.Abort()
c.Redirect(http.StatusSeeOther, "/auth/login?err=loginreq")
return
}
if scope == "admin" && !session.IsAdmin {
// Abort the request with the appropriate error code
c.Abort()
s.GetHandleError(c, http.StatusUnauthorized, "unauthorized", "not enough permissions")
return
}
// default case if some random scope was set...
if scope != "" && !session.IsAdmin {
// Abort the request with the appropriate error code
c.Abort()
s.GetHandleError(c, http.StatusUnauthorized, "unauthorized", "not enough permissions")
return
}
// Continue down the chain to handler etc
c.Next()
}
}
func (s *Server) RequireApiAuthentication(scope string) gin.HandlerFunc {
return func(c *gin.Context) {
username, password, hasAuth := c.Request.BasicAuth()
if !hasAuth {
c.Abort()
c.JSON(http.StatusUnauthorized, ApiError{Message: "unauthorized"})
return
}
// Validate form input
if strings.Trim(username, " ") == "" || strings.Trim(password, " ") == "" {
c.Abort()
c.JSON(http.StatusUnauthorized, ApiError{Message: "unauthorized"})
return
}
// Check user database for an matching entry
var loginProvider authentication.AuthProvider
user := s.users.GetUser(username) // retrieve active candidate user from db
if user == nil || user.Email == "" {
c.Abort()
c.JSON(http.StatusUnauthorized, ApiError{Message: "unauthorized"})
return
}
loginProvider = s.auth.GetProvider(string(user.Source))
if loginProvider == nil {
c.Abort()
c.JSON(http.StatusUnauthorized, ApiError{Message: "unauthorized"})
return
}
authEmail, err := loginProvider.Login(&authentication.AuthContext{
Username: username,
Password: password,
})
// Test if authentication succeeded
if err != nil || authEmail == "" {
c.Abort()
c.JSON(http.StatusUnauthorized, ApiError{Message: "unauthorized"})
return
}
// Check admin scope
if scope == "admin" && !user.IsAdmin {
// Abort the request with the appropriate error code
c.Abort()
c.JSON(http.StatusForbidden, ApiError{Message: "unauthorized"})
return
}
// default case if some random scope was set...
if scope != "" && !user.IsAdmin {
// Abort the request with the appropriate error code
c.Abort()
c.JSON(http.StatusForbidden, ApiError{Message: "unauthorized"})
return
}
// Continue down the chain to handler etc
c.Next()
}
}