Files
2025-10-24 17:18:29 +08:00

514 lines
15 KiB
Go

package application_authorization
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"github.com/APIParkLab/APIPark/service/subscribe"
application_authorization "github.com/APIParkLab/APIPark/service/application-authorization"
"github.com/eolinker/eosc/log"
authDriver "github.com/APIParkLab/APIPark/module/application-authorization/auth-driver"
"github.com/eolinker/go-common/utils"
"github.com/APIParkLab/APIPark/gateway"
"github.com/APIParkLab/APIPark/service/cluster"
"github.com/APIParkLab/APIPark/service/service"
"github.com/eolinker/go-common/auto"
"github.com/google/uuid"
"github.com/eolinker/go-common/store"
application_authorization_dto "github.com/APIParkLab/APIPark/module/application-authorization/dto"
)
var (
_ IAuthorizationModule = (*imlAuthorizationModule)(nil)
_ IExportAuthorizationModule = (*imlAuthorizationModule)(nil)
)
type imlAuthorizationModule struct {
serviceService service.IServiceService `autowired:""`
subscribeService subscribe.ISubscribeService `autowired:""`
authorizationService application_authorization.IAuthorizationService `autowired:""`
clusterService cluster.IClusterService `autowired:""`
transaction store.ITransaction `autowired:""`
}
func (i *imlAuthorizationModule) CheckAPIKeyAuthorizationByApp(ctx context.Context, appId string, apikey string) (bool, error) {
authorizations, err := i.authorizationService.ListByApp(ctx, appId)
if err != nil {
return false, err
}
for _, a := range authorizations {
if a.Type != "apikey" {
continue
}
cfg := make(map[string]interface{})
if a.Config != "" {
json.Unmarshal([]byte(a.Config), &cfg)
}
if cfg["apikey"] == apikey {
return true, nil
}
}
return false, nil
}
func (i *imlAuthorizationModule) CheckAPIKeyAuthorizationByService(ctx context.Context, serviceId string, apikey string) (bool, error) {
list, err := i.subscribeService.ListBySubscribeStatus(ctx, serviceId, subscribe.ApplyStatusSubscribe)
if err != nil {
return false, err
}
if len(list) < 1 {
return false, fmt.Errorf("no application found")
}
appIds := utils.SliceToSlice(list, func(s *subscribe.Subscribe) string {
return s.Application
})
authorizations, err := i.authorizationService.ListByApp(ctx, appIds...)
if err != nil {
return false, err
}
for _, a := range authorizations {
if a.Type != "apikey" {
continue
}
cfg := make(map[string]interface{})
if a.Config != "" {
json.Unmarshal([]byte(a.Config), &cfg)
}
if cfg["apikey"] == apikey {
return true, nil
}
}
return false, nil
}
func (i *imlAuthorizationModule) AuthorizationsByService(ctx context.Context, serviceId string, authorizationType string) ([]*application_authorization_dto.Authorization, error) {
list, err := i.subscribeService.ListBySubscribeStatus(ctx, serviceId, subscribe.ApplyStatusSubscribe)
if err != nil {
return nil, err
}
if len(list) < 1 {
return nil, fmt.Errorf("no application found")
}
appIds := utils.SliceToSlice(list, func(s *subscribe.Subscribe) string {
return s.Application
})
authorizations, err := i.authorizationService.ListByApp(ctx, appIds...)
if err != nil {
return nil, err
}
result := make([]*application_authorization_dto.Authorization, 0, len(authorizations))
for _, a := range authorizations {
if authorizationType != "" && a.Type != authorizationType {
continue
}
cfg := make(map[string]interface{})
if a.Config != "" {
json.Unmarshal([]byte(a.Config), &cfg)
}
result = append(result, &application_authorization_dto.Authorization{
UUID: a.UUID,
Name: a.Name,
Driver: a.Type,
Position: a.Position,
TokenName: a.TokenName,
Config: cfg,
ExpireTime: a.ExpireTime,
HideCredential: a.HideCredential,
})
}
return result, nil
}
func (i *imlAuthorizationModule) ExportAll(ctx context.Context) ([]*application_authorization_dto.ExportAuthorization, error) {
list, err := i.authorizationService.List(ctx)
if err != nil {
return nil, err
}
return utils.SliceToSlice(list, func(a *application_authorization.Authorization) *application_authorization_dto.ExportAuthorization {
cfg := make(map[string]interface{})
json.Unmarshal([]byte(a.Config), &cfg)
return &application_authorization_dto.ExportAuthorization{
Application: a.Application,
UUID: a.UUID,
Name: a.Name,
Driver: a.Type,
Position: a.Position,
TokenName: a.TokenName,
Config: cfg,
ExpireTime: a.ExpireTime,
HideCredential: a.HideCredential,
}
}), nil
}
func (i *imlAuthorizationModule) getApplications(ctx context.Context, appIds []string, appMap map[string]*service.Service) ([]*gateway.ApplicationRelease, error) {
authorizations, err := i.authorizationService.ListByApp(ctx, appIds...)
if err != nil {
return nil, err
}
authMap := utils.SliceToMapArray(authorizations, func(a *application_authorization.Authorization) string {
return a.Application
})
return utils.SliceToSlice(appIds, func(id string) *gateway.ApplicationRelease {
auths := authMap[id]
description := ""
info, ok := appMap[id]
if ok {
description = info.Description
}
return &gateway.ApplicationRelease{
BasicItem: &gateway.BasicItem{
ID: id,
Description: description,
Version: time.Now().Format("20060102150405"),
MatchLabels: map[string]string{
"service": id,
},
},
Authorizations: utils.SliceToSlice(auths, func(a *application_authorization.Authorization) *gateway.Authorization {
authCfg := make(map[string]interface{})
_ = json.Unmarshal([]byte(a.Config), &authCfg)
return &gateway.Authorization{
Type: a.Type,
Position: a.Position,
TokenName: a.TokenName,
Expire: a.ExpireTime,
Config: authCfg,
HideCredential: a.HideCredential,
Label: map[string]string{
"authorization": a.UUID,
"authorization_name": a.Name,
},
}
}),
}
}), nil
}
func (i *imlAuthorizationModule) initGateway(ctx context.Context, partitionId string, clientDriver gateway.IClientDriver) error {
services, err := i.serviceService.AppList(ctx)
if err != nil {
return err
}
serviceIds := make([]string, 0, len(services))
serviceMap := make(map[string]*service.Service)
for _, p := range services {
serviceIds = append(serviceIds, p.Id)
serviceMap[p.Id] = p
}
applications, err := i.getApplications(ctx, serviceIds, serviceMap)
if err != nil {
return err
}
return clientDriver.Application().Online(ctx, applications...)
}
func (i *imlAuthorizationModule) getApplicationRelease(ctx context.Context, s *service.Service) (*gateway.ApplicationRelease, error) {
authorizations, err := i.authorizationService.ListByApp(ctx, s.Id)
if err != nil {
return nil, err
}
if len(authorizations) < 1 {
return &gateway.ApplicationRelease{
BasicItem: &gateway.BasicItem{
ID: s.Id,
},
}, nil
}
return &gateway.ApplicationRelease{
BasicItem: &gateway.BasicItem{
ID: s.Id,
Description: s.Description,
Version: time.Now().Format("20060102150405"),
MatchLabels: map[string]string{
"service": s.Id,
},
},
Authorizations: utils.SliceToSlice(authorizations, func(a *application_authorization.Authorization) *gateway.Authorization {
authCfg := make(map[string]interface{})
_ = json.Unmarshal([]byte(a.Config), &authCfg)
return &gateway.Authorization{
Type: a.Type,
Position: a.Position,
TokenName: a.TokenName,
Expire: a.ExpireTime,
Config: authCfg,
HideCredential: a.HideCredential,
Label: map[string]string{
"authorization": a.UUID,
"authorization_name": a.Name,
},
}
}),
}, nil
}
func (i *imlAuthorizationModule) doOffline(ctx context.Context, clusterId string, app *gateway.ApplicationRelease) error {
client, err := i.clusterService.GatewayClient(ctx, clusterId)
if err != nil {
return err
}
defer func() {
_ = client.Close(ctx)
}()
return client.Application().Offline(ctx, app)
}
func (i *imlAuthorizationModule) online(ctx context.Context, s *service.Service) error {
clusters, err := i.clusterService.List(ctx)
if err != nil {
return err
}
if len(clusters) < 1 {
return nil
}
release, err := i.getApplicationRelease(ctx, s)
if err != nil {
return err
}
for _, c := range clusters {
if len(release.Authorizations) < 1 {
err = i.doOffline(ctx, c.Uuid, release)
if err != nil {
log.Warnf("service authorization offline for cluster[%s] %v", c.Name, err)
}
continue
}
err = i.doOnline(ctx, c.Uuid, release)
if err != nil {
log.Warnf("service authorization online for cluster[%s] %v", c.Name, err)
}
}
return nil
}
func (i *imlAuthorizationModule) doOnline(ctx context.Context, clusterId string, app *gateway.ApplicationRelease) error {
client, err := i.clusterService.GatewayClient(ctx, clusterId)
if err != nil {
return err
}
defer func() {
_ = client.Close(ctx)
}()
return client.Application().Online(ctx, app)
}
func (i *imlAuthorizationModule) AddAuthorization(ctx context.Context, appId string, info *application_authorization_dto.CreateAuthorization) (*application_authorization_dto.Authorization, error) {
authFactory, has := authDriver.GetAuthFactory(info.Driver)
if !has {
return nil, fmt.Errorf("unknown driver %s", info.Driver)
}
auth, err := authFactory.Create(info.Config)
if err != nil {
return nil, err
}
cfg, err := auth.AuthConfig().Valid()
if err != nil {
return nil, err
}
s, err := i.serviceService.Get(ctx, appId)
if err != nil {
return nil, err
}
if info.UUID == "" {
info.UUID = uuid.New().String()
}
// 缺少配置查重操作
err = i.transaction.Transaction(ctx, func(ctx context.Context) error {
err = i.authorizationService.Create(ctx, &application_authorization.Create{
UUID: info.UUID,
Application: appId,
Name: info.Name,
Type: info.Driver,
Position: info.Position,
TokenName: info.TokenName,
Config: string(cfg),
ExpireTime: info.ExpireTime,
HideCredential: info.HideCredential,
AuthID: auth.GenerateID(info.Position, info.TokenName),
})
if err != nil {
return err
}
return i.online(ctx, s)
})
if err != nil {
return nil, err
}
return i.Info(ctx, appId, info.UUID)
}
func (i *imlAuthorizationModule) EditAuthorization(ctx context.Context, appId string, aid string, info *application_authorization_dto.EditAuthorization) (*application_authorization_dto.Authorization, error) {
authInfo, err := i.authorizationService.Get(ctx, aid)
if err != nil {
return nil, err
}
authFactory, has := authDriver.GetAuthFactory(authInfo.Type)
if !has {
return nil, fmt.Errorf("unknown driver %s", authInfo.Type)
}
auth, err := authFactory.Create(info.Config)
if err != nil {
return nil, err
}
cfg, err := auth.AuthConfig().Valid()
if err != nil {
return nil, err
}
appInfo, err := i.serviceService.Get(ctx, appId)
if err != nil {
return nil, err
}
err = i.transaction.Transaction(ctx, func(ctx context.Context) error {
authId := auth.GenerateID(authInfo.Position, authInfo.TokenName)
cfgStr := string(cfg)
err = i.authorizationService.Save(ctx, aid, &application_authorization.Edit{
Name: info.Name,
Position: info.Position,
TokenName: info.TokenName,
ExpireTime: info.ExpireTime,
HideCredential: info.HideCredential,
AuthID: &authId,
Config: &cfgStr,
})
if err != nil {
return err
}
return i.online(ctx, appInfo)
})
if err != nil {
return nil, err
}
return i.Info(ctx, appId, aid)
}
func (i *imlAuthorizationModule) DeleteAuthorization(ctx context.Context, pid string, aid string) error {
s, err := i.serviceService.Get(ctx, pid)
if err != nil {
return err
}
return i.transaction.Transaction(ctx, func(ctx context.Context) error {
err = i.authorizationService.Delete(ctx, aid)
if err != nil {
return err
}
return i.online(ctx, s)
})
}
func (i *imlAuthorizationModule) Authorizations(ctx context.Context, pid string) ([]*application_authorization_dto.AuthorizationItem, error) {
_, err := i.serviceService.Get(ctx, pid)
if err != nil {
return nil, err
}
authorizations, err := i.authorizationService.ListByApp(ctx, pid)
if err != nil {
return nil, err
}
result := make([]*application_authorization_dto.AuthorizationItem, 0, len(authorizations))
for _, a := range authorizations {
result = append(result, &application_authorization_dto.AuthorizationItem{
Id: a.UUID,
Name: a.Name,
Driver: a.Type,
ExpireTime: a.ExpireTime,
Position: a.Position,
TokenName: a.TokenName,
Creator: auto.UUID(a.Creator),
Updater: auto.UUID(a.Updater),
CreateTime: auto.TimeLabel(a.CreateTime),
UpdateTime: auto.TimeLabel(a.UpdateTime),
HideCredential: a.HideCredential,
})
}
return result, nil
}
func (i *imlAuthorizationModule) Detail(ctx context.Context, pid string, aid string) ([]application_authorization_dto.DetailItem, error) {
_, err := i.serviceService.Get(ctx, pid)
if err != nil {
return nil, err
}
authInfo, err := i.authorizationService.Get(ctx, aid)
if err != nil {
return nil, err
}
authFactory, has := authDriver.GetAuthFactory(authInfo.Type)
if !has {
return nil, errors.New("unknown driver")
}
auth, err := authFactory.Create(authInfo.Config)
if err != nil {
return nil, err
}
cfgItems := auth.AuthConfig().Detail()
details := make([]application_authorization_dto.DetailItem, 0, 6+len(cfgItems))
details = append(details, application_authorization_dto.DetailItem{Key: "名称", Value: authInfo.Name})
details = append(details, application_authorization_dto.DetailItem{Key: "鉴权类型", Value: authInfo.Type})
details = append(details, application_authorization_dto.DetailItem{Key: "参数位置", Value: authInfo.Position})
details = append(details, application_authorization_dto.DetailItem{Key: "参数名", Value: authInfo.TokenName})
details = append(details, cfgItems...)
dateStr := "永久"
if authInfo.ExpireTime != 0 {
dateStr = time.Unix(authInfo.ExpireTime, 0).Format("2006-01-02")
}
details = append(details, application_authorization_dto.DetailItem{Key: "过期日期", Value: dateStr})
hideAuthStr := "是"
if !authInfo.HideCredential {
hideAuthStr = "否"
}
details = append(details, application_authorization_dto.DetailItem{Key: "隐藏鉴权信息", Value: hideAuthStr})
return details, nil
}
func (i *imlAuthorizationModule) Info(ctx context.Context, pid string, aid string) (*application_authorization_dto.Authorization, error) {
_, err := i.serviceService.Get(ctx, pid)
if err != nil {
return nil, err
}
auth, err := i.authorizationService.Get(ctx, aid)
if err != nil {
return nil, err
}
var cfg map[string]interface{}
if auth.Config != "" {
_ = json.Unmarshal([]byte(auth.Config), &cfg)
}
return &application_authorization_dto.Authorization{
UUID: auth.UUID,
Name: auth.Name,
Driver: auth.Type,
Position: auth.Position,
TokenName: auth.TokenName,
ExpireTime: auth.ExpireTime,
HideCredential: auth.HideCredential,
Config: cfg,
}, nil
}