feat: add personal access token middleware to open api (#2590)
Signed-off-by: Gaius <gaius.qi@gmail.com>
This commit is contained in:
parent
58f486a4ed
commit
b7231818e9
|
|
@ -148,7 +148,7 @@ func New(cfg *config.Config, d dfpath.Dfpath) (*Server, error) {
|
|||
|
||||
// Initialize REST server
|
||||
restService := service.New(cfg, db, cache, job, enforcer, objectStorage)
|
||||
router, err := router.Init(cfg, d.LogDir(), restService, enforcer, EmbedFolder(assets, assetsTargetPath))
|
||||
router, err := router.Init(cfg, d.LogDir(), restService, db, enforcer, EmbedFolder(assets, assetsTargetPath))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2023 The Dragonfly Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-http-utils/headers"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"d7y.io/dragonfly/v2/manager/models"
|
||||
)
|
||||
|
||||
func PersonalAccessToken(gdb *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// Get bearer token from Authorization header.
|
||||
authorization := c.GetHeader(headers.Authorization)
|
||||
tokenFields := strings.Fields(authorization)
|
||||
if len(tokenFields) != 2 || tokenFields[0] != "Bearer" {
|
||||
c.JSON(http.StatusUnauthorized, ErrorResponse{
|
||||
Message: http.StatusText(http.StatusUnauthorized),
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the personal access token is valid.
|
||||
personalAccessToken := tokenFields[1]
|
||||
if err := gdb.WithContext(c).Where("token = ?", personalAccessToken).First(&models.PersonalAccessToken{}).Error; err != nil {
|
||||
c.JSON(http.StatusUnauthorized, ErrorResponse{
|
||||
Message: http.StatusText(http.StatusUnauthorized),
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ type PersonalAccessToken struct {
|
|||
BaseModel
|
||||
Name string `gorm:"column:name;type:varchar(256);index:uk_personal_access_token_name,unique;not null;comment:name" json:"name"`
|
||||
BIO string `gorm:"column:bio;type:varchar(1024);comment:biography" json:"bio"`
|
||||
Token string `gorm:"column:token;type:varchar(256);not null;comment:access token" json:"token"`
|
||||
Token string `gorm:"column:token;type:varchar(256);index:uk_personal_access_token,unique;not null;comment:access token" json:"token"`
|
||||
Scopes Array `gorm:"column:scopes;not null;comment:scopes flags" json:"scopes"`
|
||||
State string `gorm:"column:state;type:varchar(256);default:'inactive';comment:service state" json:"state"`
|
||||
ExpiredAt time.Time `gorm:"column:expired_at;type:timestamp;default:current_timestamp;not null;comment:expired at" json:"expired_at"`
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import (
|
|||
|
||||
logger "d7y.io/dragonfly/v2/internal/dflog"
|
||||
"d7y.io/dragonfly/v2/manager/config"
|
||||
"d7y.io/dragonfly/v2/manager/database"
|
||||
"d7y.io/dragonfly/v2/manager/handlers"
|
||||
"d7y.io/dragonfly/v2/manager/middlewares"
|
||||
"d7y.io/dragonfly/v2/manager/service"
|
||||
|
|
@ -41,7 +42,7 @@ const (
|
|||
OtelServiceName = "dragonfly-manager"
|
||||
)
|
||||
|
||||
func Init(cfg *config.Config, logDir string, service service.Service, enforcer *casbin.Enforcer, assets static.ServeFileSystem) (*gin.Engine, error) {
|
||||
func Init(cfg *config.Config, logDir string, service service.Service, database *database.Database, enforcer *casbin.Enforcer, assets static.ServeFileSystem) (*gin.Engine, error) {
|
||||
// Set mode.
|
||||
if !cfg.Verbose {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
|
|
@ -65,23 +66,31 @@ func Init(cfg *config.Config, logDir string, service service.Service, enforcer *
|
|||
r.Use(otelgin.Middleware(OtelServiceName))
|
||||
}
|
||||
|
||||
// Middleware.
|
||||
// Gin middleware.
|
||||
r.Use(gin.Recovery())
|
||||
r.Use(ginzap.Ginzap(logger.GinLogger.Desugar(), time.RFC3339, true))
|
||||
r.Use(ginzap.RecoveryWithZap(logger.GinLogger.Desugar(), true))
|
||||
|
||||
// Error middleware.
|
||||
r.Use(middlewares.Error())
|
||||
|
||||
// CORS middleware.
|
||||
r.Use(middlewares.CORS())
|
||||
|
||||
// RBAC middleware.
|
||||
rbac := middlewares.RBAC(enforcer)
|
||||
jwt, err := middlewares.Jwt(cfg.Auth.JWT, service)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Personal access token middleware.
|
||||
personalAccessToken := middlewares.PersonalAccessToken(database.DB)
|
||||
|
||||
// Manager view.
|
||||
r.Use(static.Serve("/", assets))
|
||||
|
||||
// Router.
|
||||
// API router.
|
||||
apiv1 := r.Group("/api/v1")
|
||||
|
||||
// User.
|
||||
|
|
@ -179,6 +188,7 @@ func Init(cfg *config.Config, logDir string, service service.Service, enforcer *
|
|||
config.GET(":id", jwt.MiddlewareFunc(), rbac, h.GetConfig)
|
||||
config.GET("", h.GetConfigs)
|
||||
|
||||
// TODO Add auth to the following routes and fix the tests.
|
||||
// Job.
|
||||
job := apiv1.Group("/jobs")
|
||||
job.POST("", h.CreateJob)
|
||||
|
|
@ -187,12 +197,6 @@ func Init(cfg *config.Config, logDir string, service service.Service, enforcer *
|
|||
job.GET(":id", h.GetJob)
|
||||
job.GET("", h.GetJobs)
|
||||
|
||||
// Compatible with the V1 preheat.
|
||||
pv1 := r.Group("/preheats")
|
||||
r.GET("_ping", h.GetHealth)
|
||||
pv1.POST("", h.CreateV1Preheat)
|
||||
pv1.GET(":id", h.GetV1Preheat)
|
||||
|
||||
// Application.
|
||||
cs := apiv1.Group("/applications", jwt.MiddlewareFunc(), rbac)
|
||||
cs.POST("", h.CreateApplication)
|
||||
|
|
@ -216,6 +220,24 @@ func Init(cfg *config.Config, logDir string, service service.Service, enforcer *
|
|||
pat.GET(":id", h.GetPersonalAccessToken)
|
||||
pat.GET("", h.GetPersonalAccessTokens)
|
||||
|
||||
// Open API router.
|
||||
oapiv1 := r.Group("/oapi/v1")
|
||||
|
||||
// Job.
|
||||
ojob := oapiv1.Group("/jobs", personalAccessToken)
|
||||
ojob.POST("", h.CreateJob)
|
||||
ojob.DELETE(":id", h.DestroyJob)
|
||||
ojob.PATCH(":id", h.UpdateJob)
|
||||
ojob.GET(":id", h.GetJob)
|
||||
ojob.GET("", h.GetJobs)
|
||||
|
||||
// TODO Remove this api.
|
||||
// Compatible with the V1 preheat.
|
||||
pv1 := r.Group("/preheats")
|
||||
r.GET("_ping", h.GetHealth)
|
||||
pv1.POST("", h.CreateV1Preheat)
|
||||
pv1.GET(":id", h.GetV1Preheat)
|
||||
|
||||
// Health Check.
|
||||
r.GET("/healthy", h.GetHealth)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue