add application scope

This commit is contained in:
ivamp 2024-11-07 20:00:06 +08:00
parent f2efea4ed1
commit 40074417c7
13 changed files with 325 additions and 45 deletions

View File

@ -22,6 +22,7 @@ import (
"leafdev.top/Ecosystem/recommender/internal/handler/grpc/documents"
"leafdev.top/Ecosystem/recommender/internal/handler/grpc/interceptor"
"leafdev.top/Ecosystem/recommender/internal/handler/http"
"leafdev.top/Ecosystem/recommender/internal/handler/http/controller/application_v1"
"leafdev.top/Ecosystem/recommender/internal/handler/http/controller/v1"
"leafdev.top/Ecosystem/recommender/internal/handler/http/middleware"
"leafdev.top/Ecosystem/recommender/internal/router"
@ -37,18 +38,20 @@ func CreateApp() (*base.Application, error) {
loggerLogger := logger.NewZapLogger()
config := conf.ProviderConfig(loggerLogger)
jwksJWKS := jwks.NewJWKS(config, loggerLogger)
authService := auth.NewAuthService(config, jwksJWKS, loggerLogger)
db := orm.NewGORM(config, loggerLogger)
query := dao.NewQuery(db)
authService := auth.NewAuthService(config, jwksJWKS, loggerLogger, query)
applicationService := application.NewService(query)
applicationController := v1.NewApplicationController(authService, applicationService)
handlers := http.NewHandler(applicationController)
application_v1ApplicationController := application_v1.NewApplicationController(authService, applicationService)
handlers := http.NewHandler(applicationController, application_v1ApplicationController)
api := router.NewApiRoute(handlers)
swaggerRouter := router.NewSwaggerRoute()
ginLoggerMiddleware := middleware.NewGinLoggerMiddleware(loggerLogger)
authMiddleware := middleware.NewAuthMiddleware(authService)
jsonResponseMiddleware := middleware.NewJSONResponseMiddleware()
httpMiddleware := http.NewMiddleware(ginLoggerMiddleware, authMiddleware, jsonResponseMiddleware)
applicationAuthMiddleware := middleware.NewApplicationAuthMiddleware(authService)
httpMiddleware := http.NewMiddleware(ginLoggerMiddleware, authMiddleware, jsonResponseMiddleware, applicationAuthMiddleware)
httpServer := server.NewHTTPServer(config, api, swaggerRouter, httpMiddleware)
documentService := documents.NewDocumentService(query)
interceptorAuth := interceptor.NewAuth(authService)

View File

@ -237,6 +237,52 @@ const docTemplate = `{
}
}
}
},
"/applications/v1/info": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "当前的应用程序信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"application_api"
],
"summary": "Current Application Info",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/schema.ResponseBody"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/entity.Application"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/schema.ResponseBody"
}
}
}
}
}
},
"definitions": {

View File

@ -228,6 +228,52 @@
}
}
}
},
"/applications/v1/info": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "当前的应用程序信息",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"application_api"
],
"summary": "Current Application Info",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/schema.ResponseBody"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/entity.Application"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/schema.ResponseBody"
}
}
}
}
}
},
"definitions": {

View File

@ -176,6 +176,32 @@ paths:
summary: 创建并保存一个应用程序
tags:
- applications
/applications/v1/info:
get:
consumes:
- application/json
description: 当前的应用程序信息
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/schema.ResponseBody'
- properties:
data:
$ref: '#/definitions/entity.Application'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/schema.ResponseBody'
security:
- ApiKeyAuth: []
summary: Current Application Info
tags:
- application_api
securityDefinitions:
ApiKeyAuth:
in: header

View File

@ -73,7 +73,6 @@ func (hs *HttpServer) BizRouter() *gin.Engine {
apiV1 := rootGroup.Group("/api/v1")
{
//apiV1.Use(corsMiddleWare)
apiV1.Use(hs.middleware.JSONResponse.ContentTypeJSON)
apiV1.Use(hs.middleware.Auth.RequireJWTIDToken)
hs.apiRouter.InitApiRouter(apiV1)
@ -85,6 +84,13 @@ func (hs *HttpServer) BizRouter() *gin.Engine {
hs.apiRouter.InitNoAuthApiRouter(apiV1NoAuth)
}
applicationApiV1 := rootGroup.Group("/applications/v1")
{
applicationApiV1.Use(hs.middleware.JSONResponse.ContentTypeJSON)
applicationApiV1.Use(hs.middleware.ApplicationAuth.RequireApplicationAuth)
hs.apiRouter.InitApplicationApi(applicationApiV1)
}
hs.Gin.NoRoute(func(ctx *gin.Context) {
response.Ctx(ctx).Status(http.StatusNotFound).Error(consts.ErrPageNotFound).Send()
})

View File

@ -0,0 +1,42 @@
package application_v1
import (
"github.com/gin-gonic/gin"
"leafdev.top/Ecosystem/recommender/internal/handler/http/response"
"leafdev.top/Ecosystem/recommender/internal/service/application"
"leafdev.top/Ecosystem/recommender/internal/service/auth"
"net/http"
)
type ApplicationController struct {
authService *auth.Service
applicationService *application.Service
}
func NewApplicationController(authService *auth.Service, applicationService *application.Service) *ApplicationController {
return &ApplicationController{
authService,
applicationService,
}
}
// Info godoc
// @Summary Current Application Info
// @Description 当前的应用程序信息
// @Tags application_api
// @Accept json
// @Produce json
// @Security ApiKeyAuth
// @Success 200 {object} schema.ResponseBody{data=entity.Application}
// @Failure 400 {object} schema.ResponseBody
// @Router /applications/v1/info [get]
func (ac *ApplicationController) Info(c *gin.Context) {
app, err := ac.authService.GetApplication(c)
if err != nil {
response.Ctx(c).Error(err).Status(http.StatusBadRequest).Send()
return
}
response.Ctx(c).Data(app).Send()
return
}

View File

@ -0,0 +1,37 @@
package middleware
import (
"github.com/gin-gonic/gin"
"leafdev.top/Ecosystem/recommender/internal/handler/http/response"
"leafdev.top/Ecosystem/recommender/internal/service/auth"
"net/http"
)
type ApplicationAuthMiddleware struct {
authService *auth.Service
}
func NewApplicationAuthMiddleware(authService *auth.Service) *ApplicationAuthMiddleware {
return &ApplicationAuthMiddleware{
authService,
}
}
func (a *ApplicationAuthMiddleware) RequireApplicationAuth(c *gin.Context) {
token, err := a.authService.GetBearerToken(c)
if err != nil {
response.Ctx(c).Status(http.StatusForbidden).Error(err).Send()
c.Abort()
return
}
applicationEntity, err := a.authService.GetAppByToken(c, token)
if err != nil {
response.Ctx(c).Status(http.StatusForbidden).Error(err).Send()
c.Abort()
return
}
a.authService.SetApplicationScope(c, applicationEntity)
c.Next()
}

View File

@ -29,7 +29,8 @@ func (a AuthMiddleware) RequireJWTIDToken(c *gin.Context) {
return
}
c.Set(consts.AuthMiddlewareKey, user)
c.Set(consts.AuthMiddlewareKeyUser.String(), user)
c.Next()
}
@ -41,6 +42,7 @@ func (a AuthMiddleware) RequireJWTAccessToken(c *gin.Context) {
return
}
c.Set(consts.AuthMiddlewareKey, user)
c.Set(consts.AuthMiddlewareKeyUser.String(), user)
c.Next()
}

View File

@ -2,6 +2,7 @@ package http
import (
"github.com/google/wire"
"leafdev.top/Ecosystem/recommender/internal/handler/http/controller/application_v1"
v1 "leafdev.top/Ecosystem/recommender/internal/handler/http/controller/v1"
"leafdev.top/Ecosystem/recommender/internal/handler/http/middleware"
)
@ -10,37 +11,45 @@ var ProviderSet = wire.NewSet(
middleware.NewAuthMiddleware,
middleware.NewGinLoggerMiddleware,
middleware.NewJSONResponseMiddleware,
middleware.NewApplicationAuthMiddleware,
NewMiddleware,
v1.NewApplicationController,
application_v1.NewApplicationController,
NewHandler,
)
type Middleware struct {
GinLogger *middleware.GinLoggerMiddleware
Auth *middleware.AuthMiddleware
JSONResponse *middleware.JSONResponseMiddleware
GinLogger *middleware.GinLoggerMiddleware
Auth *middleware.AuthMiddleware
ApplicationAuth *middleware.ApplicationAuthMiddleware
JSONResponse *middleware.JSONResponseMiddleware
}
func NewMiddleware(
GinLogger *middleware.GinLoggerMiddleware,
Auth *middleware.AuthMiddleware,
JSONResponse *middleware.JSONResponseMiddleware,
ApplicationAuth *middleware.ApplicationAuthMiddleware,
) *Middleware {
return &Middleware{
Auth: Auth,
GinLogger: GinLogger,
JSONResponse: JSONResponse,
Auth: Auth,
GinLogger: GinLogger,
JSONResponse: JSONResponse,
ApplicationAuth: ApplicationAuth,
}
}
type Handlers struct {
Application *v1.ApplicationController
ApplicationController *v1.ApplicationController
ApplicationApi *application_v1.ApplicationController
}
func NewHandler(
application *v1.ApplicationController,
applicationController *v1.ApplicationController,
applicationApiController *application_v1.ApplicationController,
) *Handlers {
return &Handlers{
Application: application,
ApplicationController: applicationController,
ApplicationApi: applicationApiController,
}
}

View File

@ -23,13 +23,17 @@ func NewApiRoute(
}
func (a *Api) InitApiRouter(r *gin.RouterGroup) {
r.GET("/applications", a.h.Application.List)
r.POST("/applications", a.h.Application.Save)
r.GET("/application/:application_id/tokens", a.h.Application.ListToken)
r.POST("/application/:application_id/tokens", a.h.Application.SaveToken)
r.GET("/applications", a.h.ApplicationController.List)
r.POST("/applications", a.h.ApplicationController.Save)
r.GET("/application/:application_id/tokens", a.h.ApplicationController.ListToken)
r.POST("/application/:application_id/tokens", a.h.ApplicationController.SaveToken)
}
func (a *Api) InitNoAuthApiRouter(r *gin.RouterGroup) {
}
func (a *Api) InitApplicationApi(r *gin.RouterGroup) {
r.GET("/info", a.h.ApplicationApi.Info)
}

View File

@ -4,6 +4,8 @@ import (
"context"
"leafdev.top/Ecosystem/recommender/internal/base/conf"
"leafdev.top/Ecosystem/recommender/internal/base/logger"
"leafdev.top/Ecosystem/recommender/internal/dao"
"leafdev.top/Ecosystem/recommender/internal/entity"
"leafdev.top/Ecosystem/recommender/internal/schema"
"leafdev.top/Ecosystem/recommender/internal/service/jwks"
"leafdev.top/Ecosystem/recommender/pkg/consts"
@ -17,37 +19,49 @@ type Service struct {
config *conf.Config
jwks *jwks.JWKS
logger *logger.Logger
q *dao.Query
}
func NewAuthService(config *conf.Config, jwks *jwks.JWKS, logger *logger.Logger) *Service {
func NewAuthService(config *conf.Config, jwks *jwks.JWKS, logger *logger.Logger, q *dao.Query) *Service {
return &Service{
config: config,
jwks: jwks,
logger: logger,
q: q,
}
}
func (a *Service) GetBearerToken(c *gin.Context) (string, error) {
authorization := c.Request.Header.Get(consts.AuthHeader)
if authorization == "" {
return "", consts.ErrBearerTokenNotFound
}
authSplit := strings.Split(authorization, " ")
if len(authSplit) != 2 {
return "", consts.ErrNotBearerType
}
if authSplit[0] != consts.AuthPrefix {
return "", consts.ErrNotBearerType
}
return authSplit[1], nil
}
func (a *Service) GinMiddlewareAuth(tokenType schema.JWTTokenTypes, c *gin.Context) (*schema.User, error) {
if a.config.Debug.Enabled {
return a.parseUserJWT(tokenType, "")
}
authorization := c.Request.Header.Get(consts.AuthHeader)
token, err := a.GetBearerToken(c)
if authorization == "" {
return nil, consts.ErrJWTFormatError
if err != nil {
return nil, err
}
authSplit := strings.Split(authorization, " ")
if len(authSplit) != 2 {
return nil, consts.ErrJWTFormatError
}
if authSplit[0] != consts.AuthPrefix {
return nil, consts.ErrNotBearerType
}
return a.parseUserJWT(tokenType, authSplit[1])
return a.parseUserJWT(tokenType, token)
}
func (a *Service) AuthFromToken(tokenType schema.JWTTokenTypes, token string) (*schema.User, error) {
@ -63,7 +77,7 @@ func (a *Service) GetUserFromIdToken(idToken string) (*schema.User, error) {
}
func (a *Service) GinUser(c *gin.Context) *schema.User {
user, _ := c.Get(consts.AuthMiddlewareKey)
user, _ := c.Get(consts.AuthMiddlewareKeyUser.String())
return user.(*schema.User)
}
@ -74,7 +88,7 @@ func (a *Service) GetUserId(ctx context.Context) schema.UserId {
}
func (a *Service) GetUser(ctx context.Context) *schema.User {
user := ctx.Value(consts.AuthMiddlewareKey)
user := ctx.Value(consts.AuthMiddlewareKeyUser.String())
user2 := user.(*schema.User)
user2.ID = user2.Token.Sub
@ -83,8 +97,7 @@ func (a *Service) GetUser(ctx context.Context) *schema.User {
}
func (a *Service) SetUser(ctx context.Context, user *schema.User) context.Context {
context.WithValue(ctx, consts.AuthMiddlewareKey, user)
return context.WithValue(ctx, consts.AuthMiddlewareKey, user)
return context.WithValue(ctx, consts.AuthMiddlewareKeyUser.String(), user)
}
func (a *Service) parseUserJWT(tokenType schema.JWTTokenTypes, jwtToken string) (*schema.User, error) {
@ -137,3 +150,35 @@ func (a *Service) parseUserJWT(tokenType schema.JWTTokenTypes, jwtToken string)
return jwtIdToken, nil
}
func (a *Service) GetAppByToken(ctx context.Context, token string) (*entity.Application, error) {
r, err := a.q.ApplicationToken.WithContext(ctx).Where(a.q.ApplicationToken.Token.Eq(token)).
Preload(a.q.ApplicationToken.Application).First()
if err != nil {
return nil, err
}
if r.Application == nil {
return nil, consts.ErrApplicationNotFound
}
return r.Application, nil
}
func (a *Service) GetApplication(ctx context.Context) (*entity.Application, error) {
app := ctx.Value(consts.AuthMiddlewareKeyApplication.String())
app2, ok := app.(*entity.Application)
if !ok {
return nil, consts.ErrApplicationNotFound
}
return app2, nil
}
func (a *Service) SetApplicationScope(c *gin.Context, application *entity.Application) context.Context {
c.Set(consts.AuthMiddlewareKeyApplication.String(), application)
return context.WithValue(c, consts.AuthMiddlewareKeyApplication, application)
}

View File

@ -0,0 +1,7 @@
package consts
import "errors"
var (
ErrApplicationNotFound = errors.New("application not found")
)

View File

@ -5,6 +5,8 @@ import (
"leafdev.top/Ecosystem/recommender/internal/schema"
)
type AuthMiddlewareKey string
const (
AuthHeader = "Authorization"
AuthPrefix = "Bearer"
@ -12,17 +14,22 @@ const (
//AnonymousUser schema.UserId = 1
AnonymousUser schema.UserId = "anonymous"
AuthMiddlewareKey = "auth.user"
AuthAssistantShareMiddlewareKey = "auth.assistant.share"
AuthMiddlewareKeyUser AuthMiddlewareKey = "auth.user"
AuthMiddlewareKeyApplication AuthMiddlewareKey = "auth.application"
)
func (a AuthMiddlewareKey) String() string {
return string(a)
}
var (
ErrNotValidToken = errors.New("无效的 JWT 令牌")
ErrJWTFormatError = errors.New("JWT 格式错误")
ErrNotBearerType = errors.New("不是 Bearer 类型")
ErrEmptyResponse = errors.New("我们的服务器返回了空请求,可能某些环节出了问题")
ErrTokenError = errors.New("token 类型错误")
ErrBearerToken = errors.New("无效的 Bearer 令牌")
ErrBearerTokenNotFound = errors.New("bearer 令牌未找到")
ErrNotValidToken = errors.New("无效的 JWT 令牌")
ErrJWTFormatError = errors.New("JWT 格式错误")
ErrNotBearerType = errors.New("不是 Bearer 类型")
ErrEmptyResponse = errors.New("我们的服务器返回了空请求,可能某些环节出了问题")
ErrTokenError = errors.New("token 类型错误")
ErrBearerToken = errors.New("无效的 Bearer 令牌")
ErrNotYourResource = errors.New("你不能修改这个资源,因为它不是你创建的。")
ErrPermissionDenied = errors.New("没有权限访问此资源")