Skip to content

Commit

Permalink
feat: add personal access token middleware to open api (#2590)
Browse files Browse the repository at this point in the history
Signed-off-by: Gaius <[email protected]>
  • Loading branch information
gaius-qi authored Aug 1, 2023
1 parent 58f486a commit b723181
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 11 deletions.
2 changes: 1 addition & 1 deletion manager/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
55 changes: 55 additions & 0 deletions manager/middlewares/personal_access_token.go
Original file line number Diff line number Diff line change
@@ -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()
}
}
2 changes: 1 addition & 1 deletion manager/models/personal_access_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down
40 changes: 31 additions & 9 deletions manager/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand All @@ -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.
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)

Expand Down

0 comments on commit b723181

Please sign in to comment.