mirror of
https://github.com/eolinker/apinto
synced 2025-09-26 21:01:19 +08:00
openid-connect鉴权完成
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/eolinker/apinto/drivers/plugins/acl"
|
||||
"github.com/eolinker/apinto/drivers/plugins/app"
|
||||
"github.com/eolinker/apinto/drivers/plugins/cors"
|
||||
data_transform "github.com/eolinker/apinto/drivers/plugins/data-transform"
|
||||
@@ -93,6 +94,7 @@ func pluginRegister(extenderRegister eosc.IExtenderDriverRegister) {
|
||||
app.Register(extenderRegister)
|
||||
rsa_filter.Register(extenderRegister)
|
||||
js_inject.Register(extenderRegister)
|
||||
acl.Register(extenderRegister)
|
||||
|
||||
// 可观测性(输出内容到第三方)
|
||||
access_log.Register(extenderRegister)
|
||||
|
@@ -20,7 +20,7 @@ type IClient interface {
|
||||
Expire() int64
|
||||
}
|
||||
|
||||
func registerClient(clientId string, client IClient) {
|
||||
func RegisterClient(clientId string, client IClient) {
|
||||
manager.clients.Set(clientId, client)
|
||||
}
|
||||
|
||||
@@ -32,15 +32,36 @@ func GetClient(clientId string) (IClient, bool) {
|
||||
return manager.clients.Get(clientId)
|
||||
}
|
||||
|
||||
func GetClientMap(id string) map[string]struct{} {
|
||||
result := map[string]struct{}{}
|
||||
apps, has := manager.apps.Get(id)
|
||||
if !has {
|
||||
return result
|
||||
}
|
||||
for key := range apps {
|
||||
result[key] = struct{}{}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func SetClientMap(id string, clientIds map[string]struct{}) {
|
||||
manager.apps.Set(id, clientIds)
|
||||
}
|
||||
|
||||
func DeleteClientMap(id string) (map[string]struct{}, bool) {
|
||||
return manager.apps.Del(id)
|
||||
}
|
||||
|
||||
var manager = NewManager()
|
||||
|
||||
// Manager 管理oauth2配置
|
||||
type Manager struct {
|
||||
clients eosc.Untyped[string, IClient]
|
||||
apps eosc.Untyped[string, map[string]struct{}]
|
||||
}
|
||||
|
||||
func NewManager() *Manager {
|
||||
return &Manager{clients: eosc.BuildUntyped[string, IClient]()}
|
||||
return &Manager{clients: eosc.BuildUntyped[string, IClient](), apps: eosc.BuildUntyped[string, map[string]struct{}]()}
|
||||
}
|
||||
|
||||
type client struct {
|
||||
|
@@ -74,6 +74,8 @@ func (o *oauth2) Check(appID string, users []application.ITransformConfig) error
|
||||
|
||||
func (o *oauth2) Set(app application.IApp, users []application.ITransformConfig) {
|
||||
infos := make([]*application.UserInfo, 0, len(users))
|
||||
clientIDMap := make(map[string]struct{})
|
||||
oldClientIDMap := GetClientMap(app.Id())
|
||||
for _, user := range users {
|
||||
v, _ := user.Config().(*User)
|
||||
c := &client{
|
||||
@@ -93,8 +95,9 @@ func (o *oauth2) Set(app application.IApp, users []application.ITransformConfig)
|
||||
log.Debug("hash rule: ", *hr)
|
||||
c.hashRule = hr
|
||||
}
|
||||
registerClient(v.Pattern.ClientId, c)
|
||||
|
||||
RegisterClient(v.Pattern.ClientId, c)
|
||||
clientIDMap[v.Pattern.ClientId] = struct{}{}
|
||||
delete(oldClientIDMap, v.Pattern.ClientId)
|
||||
infos = append(infos, &application.UserInfo{
|
||||
Name: v.Username(),
|
||||
Value: v.Pattern.ClientSecret,
|
||||
@@ -106,11 +109,20 @@ func (o *oauth2) Set(app application.IApp, users []application.ITransformConfig)
|
||||
App: app,
|
||||
})
|
||||
}
|
||||
for clientID := range oldClientIDMap {
|
||||
RemoveClient(clientID)
|
||||
}
|
||||
SetClientMap(app.Id(), clientIDMap)
|
||||
o.users.Set(app.Id(), infos)
|
||||
}
|
||||
|
||||
func (o *oauth2) Del(appID string) {
|
||||
o.users.DelByAppID(appID)
|
||||
oldClientIDMap, _ := DeleteClientMap(appID)
|
||||
for clientID := range oldClientIDMap {
|
||||
RemoveClient(clientID)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (o *oauth2) UserCount() int {
|
||||
|
26
application/auth/openid-connect-jwt/config.go
Normal file
26
application/auth/openid-connect-jwt/config.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package openid_connect_jwt
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/eolinker/apinto/application"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
application.Auth
|
||||
Users []*User `json:"users" label:"用户列表"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Pattern Pattern `json:"pattern" label:"用户信息"`
|
||||
application.User
|
||||
}
|
||||
|
||||
type Pattern struct {
|
||||
Issuer string `json:"issuer"`
|
||||
AuthenticatedGroupsClaim []string `json:"authenticated_groups_claim"`
|
||||
}
|
||||
|
||||
func (u *User) Username() string {
|
||||
return base64.RawStdEncoding.EncodeToString([]byte(u.Pattern.Issuer))
|
||||
}
|
131
application/auth/openid-connect-jwt/discovery.go
Normal file
131
application/auth/openid-connect-jwt/discovery.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package openid_connect_jwt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/eolinker/eosc/log"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
)
|
||||
|
||||
var client = http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
type IssuerConfig struct {
|
||||
ID string `json:"id"`
|
||||
Issuer string `json:"issuer"`
|
||||
Configuration *DiscoveryConfig `json:"configuration"`
|
||||
Keys []JWK `json:"keys"`
|
||||
UpdateTime time.Time `json:"update_time"`
|
||||
JWKKeys map[string]jwk.Key `json:"-"`
|
||||
}
|
||||
|
||||
type DiscoveryConfig struct {
|
||||
TokenEndpoint string `json:"token_endpoint"`
|
||||
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
|
||||
JwksUri string `json:"jwks_uri"`
|
||||
ResponseModesSupported []string `json:"response_modes_supported"`
|
||||
SubjectTypesSupported []string `json:"subject_types_supported"`
|
||||
IdTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
|
||||
ResponseTypesSupported []string `json:"response_types_supported"`
|
||||
ScopesSupported []string `json:"scopes_supported"`
|
||||
Issuer string `json:"issuer"`
|
||||
MicrosoftMultiRefreshToken bool `json:"microsoft_multi_refresh_token"`
|
||||
AuthorizationEndpoint string `json:"authorization_endpoint"`
|
||||
DeviceAuthorizationEndpoint string `json:"device_authorization_endpoint"`
|
||||
HttpLogoutSupported bool `json:"http_logout_supported"`
|
||||
FrontchannelLogoutSupported bool `json:"frontchannel_logout_supported"`
|
||||
EndSessionEndpoint string `json:"end_session_endpoint"`
|
||||
ClaimsSupported []string `json:"claims_supported"`
|
||||
CheckSessionIframe string `json:"check_session_iframe"`
|
||||
UserinfoEndpoint string `json:"userinfo_endpoint"`
|
||||
KerberosEndpoint string `json:"kerberos_endpoint"`
|
||||
TenantRegionScope string `json:"tenant_region_scope"`
|
||||
CloudInstanceName string `json:"cloud_instance_name"`
|
||||
CloudGraphHostName string `json:"cloud_graph_host_name"`
|
||||
MsgraphHost string `json:"msgraph_host"`
|
||||
RbacUrl string `json:"rbac_url"`
|
||||
}
|
||||
|
||||
type JWKs struct {
|
||||
Keys []JWK `json:"keys"`
|
||||
}
|
||||
|
||||
type JWK struct {
|
||||
Kid string `json:"kid"`
|
||||
Kty string `json:"kty"`
|
||||
Alg string `json:"alg"`
|
||||
Use string `json:"use"`
|
||||
N string `json:"n"`
|
||||
E string `json:"e"`
|
||||
X5C []string `json:"x5c"`
|
||||
X5T string `json:"x5t"`
|
||||
X5TS256 string `json:"x5t#S256"`
|
||||
}
|
||||
|
||||
func getIssuerConfig(issuer string) (*DiscoveryConfig, error) {
|
||||
resp, err := client.Get(issuer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get issuer config error: %w, issuer: %s", err, issuer)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read issuer config error: %w, issuer: %s", err, issuer)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("get issuer config error: %d, issuer: %s, body: %s", resp.StatusCode, issuer, string(body))
|
||||
}
|
||||
var config DiscoveryConfig
|
||||
err = json.Unmarshal(body, &config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unmarshal issuer config error: %w, issuer: %s, body: %s", err, issuer, string(body))
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func getJWKs(uri string) ([]JWK, map[string]jwk.Key, error) {
|
||||
resp, err := client.Get(uri)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("get issuer jwks error: %w, uri: %s", err, uri)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("read issuer jwks error: %w, uri: %s", err, uri)
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, nil, fmt.Errorf("get issuer jwks error: %d, uri: %s, body: %s", resp.StatusCode, uri, string(body))
|
||||
}
|
||||
var jwks JWKs
|
||||
err = json.Unmarshal(body, &jwks)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unmarshal issuer jwks error: %w, uri: %s, body: %s", err, uri, string(body))
|
||||
}
|
||||
set, err := jwk.Parse(body)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("parse issuer jwks error: %w, uri: %s, body: %s", err, uri, string(body))
|
||||
}
|
||||
keys := make(map[string]jwk.Key)
|
||||
l := set.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
key, success := set.Get(i)
|
||||
if !success {
|
||||
continue
|
||||
}
|
||||
if key.KeyUsage() != string(jwk.ForSignature) {
|
||||
continue
|
||||
}
|
||||
pubKey, err := key.PublicKey()
|
||||
if err != nil {
|
||||
log.Errorf("get public key error: %w, uri: %s, key: %s", err, uri, key.KeyID())
|
||||
}
|
||||
keys[key.KeyID()] = pubKey
|
||||
}
|
||||
return jwks.Keys, keys, nil
|
||||
}
|
79
application/auth/openid-connect-jwt/factory.go
Normal file
79
application/auth/openid-connect-jwt/factory.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package openid_connect_jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/eolinker/eosc/router"
|
||||
|
||||
"github.com/eolinker/eosc/utils/schema"
|
||||
|
||||
"github.com/eolinker/apinto/application"
|
||||
"github.com/eolinker/apinto/application/auth"
|
||||
)
|
||||
|
||||
var _ auth.IAuthFactory = (*factory)(nil)
|
||||
|
||||
var driverName = "openid-connect-jwt"
|
||||
|
||||
// Register 注册auth驱动工厂
|
||||
func Register() {
|
||||
auth.FactoryRegister(driverName, NewFactory())
|
||||
}
|
||||
|
||||
type factory struct {
|
||||
configType reflect.Type
|
||||
render *schema.Schema
|
||||
userType reflect.Type
|
||||
}
|
||||
|
||||
func (f *factory) Render() interface{} {
|
||||
return f.render
|
||||
}
|
||||
|
||||
func (f *factory) ConfigType() reflect.Type {
|
||||
return f.configType
|
||||
}
|
||||
|
||||
func (f *factory) UserType() reflect.Type {
|
||||
return f.userType
|
||||
}
|
||||
|
||||
func (f *factory) Alias() []string {
|
||||
return []string{
|
||||
"openid-connect-jwt",
|
||||
}
|
||||
}
|
||||
|
||||
func (f *factory) PreRouters() []*auth.PreRouter {
|
||||
return []*auth.PreRouter{}
|
||||
}
|
||||
|
||||
func (f *factory) Create(tokenName string, position string, rule interface{}) (application.IAuth, error) {
|
||||
a := &jwt{
|
||||
id: toId(tokenName, position),
|
||||
tokenName: tokenName,
|
||||
position: position,
|
||||
users: application.NewUserManager(),
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// NewFactory 生成一个 auth_apiKey工厂
|
||||
func NewFactory() auth.IAuthFactory {
|
||||
typ := reflect.TypeOf((*Config)(nil))
|
||||
render, _ := schema.Generate(typ, nil)
|
||||
h := newIssuerHandler("/openid-connect/issuers")
|
||||
j := newJwkHandler("/openid-connect/jwks")
|
||||
router.SetPath("openid-connect-jwt-issuer", h.prefix, h)
|
||||
router.SetPath("openid-connect-jwt-jwk", j.prefix, j)
|
||||
return &factory{
|
||||
configType: typ,
|
||||
render: render,
|
||||
userType: reflect.TypeOf((*User)(nil)),
|
||||
}
|
||||
}
|
||||
|
||||
func toId(tokenName, position string) string {
|
||||
return fmt.Sprintf("%s@%s@%s", tokenName, position, driverName)
|
||||
}
|
105
application/auth/openid-connect-jwt/handler.go
Normal file
105
application/auth/openid-connect-jwt/handler.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package openid_connect_jwt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type issuerHandler struct {
|
||||
prefix string
|
||||
}
|
||||
|
||||
func newIssuerHandler(prefix string) *issuerHandler {
|
||||
return &issuerHandler{prefix: strings.TrimPrefix(prefix, "/")}
|
||||
}
|
||||
|
||||
func (h *issuerHandler) PrefixPath() string {
|
||||
return h.prefix
|
||||
}
|
||||
|
||||
func (h *issuerHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
path := strings.TrimPrefix(request.URL.Path, fmt.Sprintf("/apinto/%s", h.prefix))
|
||||
switch request.Method {
|
||||
case http.MethodGet:
|
||||
if path == "" {
|
||||
h.list(writer, request)
|
||||
return
|
||||
}
|
||||
paths := strings.SplitN(path, "/", 2)
|
||||
if len(paths) == 2 {
|
||||
h.info(writer, request, paths[1])
|
||||
return
|
||||
}
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
writer.Write([]byte("not found"))
|
||||
return
|
||||
|
||||
default:
|
||||
writer.WriteHeader(http.StatusMethodNotAllowed)
|
||||
writer.Write([]byte("method not allowed"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *issuerHandler) info(writer http.ResponseWriter, request *http.Request, id string) {
|
||||
var body []byte
|
||||
info, has := manager.Issuers.Get(id)
|
||||
if !has {
|
||||
body = []byte("{\"message\":\"not found\"}")
|
||||
} else {
|
||||
body, _ = json.Marshal(info)
|
||||
}
|
||||
writer.Write(body)
|
||||
|
||||
}
|
||||
|
||||
func (h *issuerHandler) list(writer http.ResponseWriter, request *http.Request) {
|
||||
var data = map[string]interface{}{
|
||||
"data": manager.Issuers.List(),
|
||||
}
|
||||
body, _ := json.Marshal(data)
|
||||
writer.Write(body)
|
||||
}
|
||||
|
||||
type jwkHandler struct {
|
||||
prefix string
|
||||
}
|
||||
|
||||
func newJwkHandler(prefix string) *jwkHandler {
|
||||
return &jwkHandler{prefix: strings.TrimPrefix(prefix, "/")}
|
||||
}
|
||||
|
||||
func (j *jwkHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
|
||||
path := strings.TrimPrefix(request.URL.Path, fmt.Sprintf("/apinto/%s", j.prefix))
|
||||
switch request.Method {
|
||||
case http.MethodGet:
|
||||
if path == "" {
|
||||
j.list(writer, request)
|
||||
return
|
||||
}
|
||||
writer.WriteHeader(http.StatusNotFound)
|
||||
writer.Write([]byte("not found"))
|
||||
return
|
||||
|
||||
default:
|
||||
writer.WriteHeader(http.StatusMethodNotAllowed)
|
||||
writer.Write([]byte("method not allowed"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (j *jwkHandler) list(writer http.ResponseWriter, request *http.Request) {
|
||||
|
||||
all := manager.Issuers.List()
|
||||
keys := make([]JWK, 0, len(all))
|
||||
for _, issuer := range all {
|
||||
keys = append(keys, issuer.Keys...)
|
||||
}
|
||||
var data = map[string]interface{}{
|
||||
"keys": keys,
|
||||
}
|
||||
body, _ := json.Marshal(data)
|
||||
writer.Write(body)
|
||||
}
|
131
application/auth/openid-connect-jwt/jwt.go
Normal file
131
application/auth/openid-connect-jwt/jwt.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package openid_connect_jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/ohler55/ojg/jp"
|
||||
|
||||
"github.com/eolinker/apinto/resources"
|
||||
scope_manager "github.com/eolinker/apinto/scope-manager"
|
||||
|
||||
"github.com/eolinker/apinto/application"
|
||||
|
||||
http_service "github.com/eolinker/eosc/eocontext/http-context"
|
||||
"github.com/eolinker/eosc/log"
|
||||
)
|
||||
|
||||
var _ application.IAuth = (*jwt)(nil)
|
||||
|
||||
type jwt struct {
|
||||
id string
|
||||
tokenName string
|
||||
position string
|
||||
users application.IUserManager
|
||||
cache scope_manager.IProxyOutput[resources.ICache]
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func (o *jwt) GetUser(ctx http_service.IHttpContext) (*application.UserInfo, bool) {
|
||||
token, has := application.GetToken(ctx, o.tokenName, o.position)
|
||||
if !has || token == "" {
|
||||
return nil, false
|
||||
}
|
||||
id, obj, has := verify(token)
|
||||
if !has {
|
||||
return nil, false
|
||||
}
|
||||
info, has := o.users.Get(id)
|
||||
if !has {
|
||||
return nil, false
|
||||
}
|
||||
exprs, ok := info.Additional.([]jp.Expr)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
result := make([]interface{}, 0, len(exprs))
|
||||
for _, expr := range exprs {
|
||||
v := expr.Get(obj)
|
||||
if len(v) > 0 {
|
||||
result = append(result, v...)
|
||||
}
|
||||
}
|
||||
ctx.WithValue("acl_groups", result)
|
||||
return info, true
|
||||
}
|
||||
|
||||
func (o *jwt) ID() string {
|
||||
return o.id
|
||||
}
|
||||
|
||||
func (o *jwt) Driver() string {
|
||||
return driverName
|
||||
}
|
||||
|
||||
func (o *jwt) Check(appID string, users []application.ITransformConfig) error {
|
||||
us := make([]application.IUser, 0, len(users))
|
||||
for _, u := range users {
|
||||
v, ok := u.Config().(*User)
|
||||
if !ok {
|
||||
return fmt.Errorf("%s check error: invalid config type", driverName)
|
||||
}
|
||||
us = append(us, v)
|
||||
}
|
||||
return o.users.Check(appID, driverName, us)
|
||||
}
|
||||
|
||||
func (o *jwt) Set(app application.IApp, users []application.ITransformConfig) {
|
||||
infos := make([]*application.UserInfo, 0, len(users))
|
||||
idMap := make(map[string]struct{})
|
||||
oldIDMap := manager.GetIssuerIDMap(app.Id())
|
||||
for _, user := range users {
|
||||
v, _ := user.Config().(*User)
|
||||
exprs := make([]jp.Expr, 0, len(v.Pattern.AuthenticatedGroupsClaim))
|
||||
for _, key := range v.Pattern.AuthenticatedGroupsClaim {
|
||||
if !strings.HasPrefix(key, "$.") {
|
||||
key = fmt.Sprintf("$.%s", key)
|
||||
}
|
||||
expr, err := jp.ParseString(key)
|
||||
if err != nil {
|
||||
log.Errorf("parse key %w, key: %s", err, key)
|
||||
continue
|
||||
}
|
||||
exprs = append(exprs, expr)
|
||||
}
|
||||
manager.Set(v.Username(), &IssuerConfig{
|
||||
ID: v.Username(),
|
||||
Issuer: v.Pattern.Issuer,
|
||||
})
|
||||
delete(oldIDMap, v.Username())
|
||||
idMap[v.Username()] = struct{}{}
|
||||
infos = append(infos, &application.UserInfo{
|
||||
Name: v.Username(),
|
||||
Value: strings.Join(v.Pattern.AuthenticatedGroupsClaim, ","),
|
||||
Expire: v.Expire,
|
||||
Labels: v.Labels,
|
||||
HideCredential: v.HideCredential,
|
||||
TokenName: o.tokenName,
|
||||
Position: o.position,
|
||||
App: app,
|
||||
Additional: exprs,
|
||||
})
|
||||
}
|
||||
for id := range oldIDMap {
|
||||
manager.Del(id)
|
||||
}
|
||||
manager.SetIssuerIDMap(app.Id(), idMap)
|
||||
o.users.Set(app.Id(), infos)
|
||||
}
|
||||
|
||||
func (o *jwt) Del(appID string) {
|
||||
o.users.DelByAppID(appID)
|
||||
oldIDMap, _ := manager.DelIssuerIDMap(appID)
|
||||
for id := range oldIDMap {
|
||||
manager.Del(id)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *jwt) UserCount() int {
|
||||
return o.users.Count()
|
||||
}
|
77
application/auth/openid-connect-jwt/manager.go
Normal file
77
application/auth/openid-connect-jwt/manager.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package openid_connect_jwt
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/eolinker/eosc"
|
||||
"github.com/eolinker/eosc/log"
|
||||
)
|
||||
|
||||
var (
|
||||
manager = NewManager()
|
||||
)
|
||||
|
||||
type Manager struct {
|
||||
Issuers eosc.Untyped[string, *IssuerConfig]
|
||||
Apps eosc.Untyped[string, map[string]struct{}]
|
||||
}
|
||||
|
||||
func NewManager() *Manager {
|
||||
m := &Manager{Issuers: eosc.BuildUntyped[string, *IssuerConfig](), Apps: eosc.BuildUntyped[string, map[string]struct{}]()}
|
||||
go m.doLoop()
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Manager) doLoop() {
|
||||
ticket := time.NewTicker(10 * time.Second)
|
||||
defer ticket.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticket.C:
|
||||
for _, issuer := range m.Issuers.All() {
|
||||
config, err := getIssuerConfig(issuer.Issuer)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
}
|
||||
issuer.Configuration = config
|
||||
issuer.UpdateTime = time.Now()
|
||||
keys, jwks, err := getJWKs(config.JwksUri)
|
||||
if err != nil {
|
||||
log.Errorf("%w, issuer: %s", err, issuer.Issuer)
|
||||
continue
|
||||
}
|
||||
issuer.Keys = keys
|
||||
issuer.JWKKeys = jwks
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Manager) Set(id string, config *IssuerConfig) {
|
||||
m.Issuers.Set(id, config)
|
||||
}
|
||||
|
||||
func (m *Manager) Del(id string) {
|
||||
m.Issuers.Del(id)
|
||||
}
|
||||
|
||||
func (m *Manager) GetIssuerIDMap(appID string) map[string]struct{} {
|
||||
result := make(map[string]struct{})
|
||||
idMap, has := m.Apps.Get(appID)
|
||||
if !has {
|
||||
return result
|
||||
}
|
||||
for id := range idMap {
|
||||
result[id] = struct{}{}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *Manager) SetIssuerIDMap(appID string, issuerIDMap map[string]struct{}) {
|
||||
m.Apps.Set(appID, issuerIDMap)
|
||||
}
|
||||
|
||||
func (m *Manager) DelIssuerIDMap(appID string) (map[string]struct{}, bool) {
|
||||
return m.Apps.Del(appID)
|
||||
}
|
35
application/auth/openid-connect-jwt/token.go
Normal file
35
application/auth/openid-connect-jwt/token.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package openid_connect_jwt
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidToken = errors.New("invalid token")
|
||||
)
|
||||
|
||||
type tokenHeader struct {
|
||||
Alg string `json:"alg"`
|
||||
Kid string `json:"kid"`
|
||||
Typ string `json:"typ"`
|
||||
}
|
||||
|
||||
func extractTokenHeader(token string) (*tokenHeader, error) {
|
||||
ts := strings.Split(token, ".")
|
||||
if len(ts) != 3 {
|
||||
return nil, ErrInvalidToken
|
||||
}
|
||||
headerData, err := base64.RawStdEncoding.DecodeString(ts[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var th tokenHeader
|
||||
err = json.Unmarshal(headerData, &th)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &th, nil
|
||||
}
|
120
application/auth/openid-connect-jwt/verify.go
Normal file
120
application/auth/openid-connect-jwt/verify.go
Normal file
@@ -0,0 +1,120 @@
|
||||
package openid_connect_jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/lestrrat-go/jwx/jws"
|
||||
|
||||
"github.com/eolinker/eosc/log"
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/ohler55/ojg/jp"
|
||||
"github.com/ohler55/ojg/oj"
|
||||
)
|
||||
|
||||
type IVerifyClaim interface {
|
||||
Verify(obj interface{}) error
|
||||
}
|
||||
|
||||
var claims = []IVerifyClaim{
|
||||
newNbfClaim(),
|
||||
newExpClaim(),
|
||||
}
|
||||
|
||||
func newNbfClaim() IVerifyClaim {
|
||||
nbfExpr, _ := jp.ParseString("$.nbf")
|
||||
return &nbfClaim{nbfExpr}
|
||||
}
|
||||
|
||||
type nbfClaim struct {
|
||||
expr jp.Expr
|
||||
}
|
||||
|
||||
func (n *nbfClaim) Verify(obj interface{}) error {
|
||||
result := n.expr.Get(obj)
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
var nbf int64
|
||||
switch r := result[0].(type) {
|
||||
case float64:
|
||||
nbf = int64(r)
|
||||
case int64:
|
||||
nbf = r
|
||||
default:
|
||||
return fmt.Errorf("nbf claim type error")
|
||||
}
|
||||
if nbf > time.Now().Unix() {
|
||||
return fmt.Errorf("token not valid yet")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newExpClaim() IVerifyClaim {
|
||||
expExpr, _ := jp.ParseString("$.exp")
|
||||
return &expClaim{expExpr}
|
||||
}
|
||||
|
||||
type expClaim struct {
|
||||
expr jp.Expr
|
||||
}
|
||||
|
||||
func (e *expClaim) Verify(obj interface{}) error {
|
||||
result := e.expr.Get(obj)
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
var exp int64
|
||||
switch r := result[0].(type) {
|
||||
case float64:
|
||||
exp = int64(r)
|
||||
case int64:
|
||||
exp = r
|
||||
default:
|
||||
return fmt.Errorf("exp claim type error")
|
||||
}
|
||||
if exp < time.Now().Unix() {
|
||||
return fmt.Errorf("token expired")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verify(token string) (string, interface{}, bool) {
|
||||
id, payload, success := verifySign(token)
|
||||
if !success {
|
||||
return "", nil, false
|
||||
}
|
||||
obj, err := oj.Parse(payload)
|
||||
if err != nil {
|
||||
log.Errorf("%w, payload: %s", err, string(payload))
|
||||
return "", nil, false
|
||||
}
|
||||
for _, c := range claims {
|
||||
err = c.Verify(obj)
|
||||
if err != nil {
|
||||
log.Errorf("%w, payload: %s", err, string(payload))
|
||||
return "", nil, false
|
||||
}
|
||||
}
|
||||
return id, obj, true
|
||||
|
||||
}
|
||||
|
||||
func verifySign(token string) (string, []byte, bool) {
|
||||
header, err := extractTokenHeader(token)
|
||||
if err != nil {
|
||||
return "", nil, false
|
||||
}
|
||||
for _, issuer := range manager.Issuers.All() {
|
||||
if key, ok := issuer.JWKKeys[header.Kid]; ok {
|
||||
payload, err := jws.Verify([]byte(token), jwa.SignatureAlgorithm(key.Algorithm()), key)
|
||||
if err != nil {
|
||||
log.DebugF("%w, issuer: %s, key: %s", err, issuer.Issuer, key.KeyID())
|
||||
continue
|
||||
}
|
||||
log.DebugF("verify sign successful! payload: %s", string(payload))
|
||||
return issuer.ID, payload, true
|
||||
}
|
||||
}
|
||||
return "", nil, false
|
||||
}
|
@@ -25,6 +25,7 @@ type UserInfo struct {
|
||||
TokenName string
|
||||
Position string
|
||||
App IApp
|
||||
Additional interface{}
|
||||
}
|
||||
|
||||
var _ IUserManager = (*UserManager)(nil)
|
||||
|
@@ -3,6 +3,8 @@ package app
|
||||
import (
|
||||
"sync"
|
||||
|
||||
openid_connect_jwt "github.com/eolinker/apinto/application/auth/openid-connect-jwt"
|
||||
|
||||
"github.com/eolinker/apinto/application/auth"
|
||||
"github.com/eolinker/apinto/application/auth/aksk"
|
||||
"github.com/eolinker/apinto/application/auth/apikey"
|
||||
@@ -36,6 +38,7 @@ func NewFactory() eosc.IExtenderDriverFactory {
|
||||
aksk.Register()
|
||||
jwt.Register()
|
||||
oauth2.Register()
|
||||
openid_connect_jwt.Register()
|
||||
appManager = manager.NewManager(auth.Alias(), auth.Keys())
|
||||
bean.Injection(&appManager)
|
||||
})
|
||||
|
29
drivers/plugins/acl/config.go
Normal file
29
drivers/plugins/acl/config.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package acl
|
||||
|
||||
import (
|
||||
"github.com/eolinker/apinto/drivers"
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Allow []string `json:"allow"`
|
||||
Deny []string `json:"deny"`
|
||||
HideGroupsHeader bool `json:"hide_groups_header"`
|
||||
}
|
||||
|
||||
func Create(id, name string, conf *Config, workers map[eosc.RequireId]eosc.IWorker) (eosc.IWorker, error) {
|
||||
allow := make(map[string]struct{})
|
||||
deny := make(map[string]struct{})
|
||||
for _, a := range conf.Allow {
|
||||
allow[a] = struct{}{}
|
||||
}
|
||||
for _, d := range conf.Deny {
|
||||
deny[d] = struct{}{}
|
||||
}
|
||||
return &executor{
|
||||
WorkerBase: drivers.Worker(id, name),
|
||||
cfg: conf,
|
||||
allow: allow,
|
||||
deny: deny,
|
||||
}, nil
|
||||
}
|
124
drivers/plugins/acl/executor.go
Normal file
124
drivers/plugins/acl/executor.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package acl
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/eolinker/eosc/log"
|
||||
|
||||
"github.com/eolinker/apinto/drivers"
|
||||
"github.com/eolinker/eosc"
|
||||
"github.com/eolinker/eosc/eocontext"
|
||||
http_service "github.com/eolinker/eosc/eocontext/http-context"
|
||||
)
|
||||
|
||||
var _ eocontext.IFilter = (*executor)(nil)
|
||||
var _ http_service.HttpFilter = (*executor)(nil)
|
||||
var _ eosc.IWorker = (*executor)(nil)
|
||||
|
||||
type executor struct {
|
||||
drivers.WorkerBase
|
||||
cfg *Config
|
||||
allow map[string]struct{}
|
||||
deny map[string]struct{}
|
||||
}
|
||||
|
||||
func (e *executor) DoFilter(ctx eocontext.EoContext, next eocontext.IChain) (err error) {
|
||||
return http_service.DoHttpFilter(e, ctx, next)
|
||||
}
|
||||
|
||||
func (e *executor) DoHttpFilter(ctx http_service.IHttpContext, next eocontext.IChain) (err error) {
|
||||
groupMap := make(map[string]struct{})
|
||||
groups := ctx.Value("acl_groups")
|
||||
groupArr := make([]string, 0)
|
||||
switch gs := groups.(type) {
|
||||
case []interface{}:
|
||||
for _, g := range gs {
|
||||
switch t := g.(type) {
|
||||
case string:
|
||||
groupMap[t] = struct{}{}
|
||||
groupArr = append(groupArr, t)
|
||||
case []interface{}:
|
||||
for _, v := range t {
|
||||
if s, ok := v.(string); ok {
|
||||
groupMap[s] = struct{}{}
|
||||
groupArr = append(groupArr, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case []string:
|
||||
for _, g := range gs {
|
||||
groupMap[g] = struct{}{}
|
||||
groupArr = append(groupArr, g)
|
||||
}
|
||||
case string:
|
||||
groupMap[gs] = struct{}{}
|
||||
groupArr = append(groupArr, gs)
|
||||
}
|
||||
type Msg struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
for key := range e.deny {
|
||||
if _, ok := groupMap[key]; ok {
|
||||
// 拒绝访问
|
||||
err = fmt.Errorf("acl deny, group is %s", key)
|
||||
log.Error(err)
|
||||
msg := Msg{
|
||||
Message: "Unauthorized",
|
||||
}
|
||||
data, _ := json.Marshal(msg)
|
||||
ctx.Response().SetStatus(401, "Unauthorized")
|
||||
ctx.Response().SetBody([]byte(data))
|
||||
return
|
||||
}
|
||||
}
|
||||
allow := false
|
||||
for key := range e.allow {
|
||||
if _, ok := groupMap[key]; ok {
|
||||
allow = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !allow {
|
||||
err = fmt.Errorf("groups is not allow, groups is %v", groups)
|
||||
log.Error(err)
|
||||
msg := Msg{
|
||||
Message: "Unauthorized",
|
||||
}
|
||||
data, _ := json.Marshal(msg)
|
||||
ctx.Response().SetStatus(401, "Unauthorized")
|
||||
ctx.Response().SetBody([]byte(data))
|
||||
return
|
||||
}
|
||||
|
||||
if !e.cfg.HideGroupsHeader {
|
||||
ctx.Proxy().Header().SetHeader("X-Consumer-Groups", strings.Join(groupArr, ","))
|
||||
}
|
||||
if next != nil {
|
||||
return next.DoChain(ctx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (e *executor) Destroy() {
|
||||
return
|
||||
}
|
||||
|
||||
func (e *executor) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *executor) Reset(conf interface{}, workers map[eosc.RequireId]eosc.IWorker) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *executor) Stop() error {
|
||||
e.Destroy()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *executor) CheckSkill(skill string) bool {
|
||||
return http_service.FilterSkillName == skill
|
||||
}
|
18
drivers/plugins/acl/factory.go
Normal file
18
drivers/plugins/acl/factory.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package acl
|
||||
|
||||
import (
|
||||
"github.com/eolinker/apinto/drivers"
|
||||
"github.com/eolinker/eosc"
|
||||
)
|
||||
|
||||
const (
|
||||
Name = "acl"
|
||||
)
|
||||
|
||||
func Register(register eosc.IExtenderDriverRegister) {
|
||||
register.RegisterExtenderDriver(Name, NewFactory())
|
||||
}
|
||||
|
||||
func NewFactory() eosc.IExtenderDriverFactory {
|
||||
return drivers.NewFactory[Config](Create)
|
||||
}
|
@@ -30,7 +30,7 @@ func (a *App) DoHttpFilter(ctx http_service.IHttpContext, next eocontext.IChain)
|
||||
log.Debug("auth beginning")
|
||||
err := a.auth(ctx)
|
||||
if err != nil {
|
||||
ctx.Response().SetStatus(403, "403")
|
||||
ctx.Response().SetStatus(401, "Unauthorized")
|
||||
ctx.Response().SetBody([]byte(err.Error()))
|
||||
return err
|
||||
}
|
||||
|
@@ -1,208 +0,0 @@
|
||||
package openid_connect
|
||||
|
||||
type Config struct {
|
||||
Anonymous interface{} `json:"anonymous"`
|
||||
Audience []interface{} `json:"audience"`
|
||||
AudienceClaim []interface{} `json:"audience_claim"`
|
||||
AudienceRequired []interface{} `json:"audience_required"`
|
||||
AuthMethods []string `json:"auth_methods"`
|
||||
AuthenticatedGroupsClaim []string `json:"authenticated_groups_claim"`
|
||||
AuthorizationCookieDomain interface{} `json:"authorization_cookie_domain"`
|
||||
AuthorizationCookieHttponly bool `json:"authorization_cookie_httponly"`
|
||||
AuthorizationCookieLifetime int `json:"authorization_cookie_lifetime"`
|
||||
AuthorizationCookieName string `json:"authorization_cookie_name"`
|
||||
AuthorizationCookiePath string `json:"authorization_cookie_path"`
|
||||
AuthorizationCookieSamesite string `json:"authorization_cookie_samesite"`
|
||||
AuthorizationCookieSecure interface{} `json:"authorization_cookie_secure"`
|
||||
AuthorizationEndpoint interface{} `json:"authorization_endpoint"`
|
||||
AuthorizationQueryArgsClient interface{} `json:"authorization_query_args_client"`
|
||||
AuthorizationQueryArgsNames interface{} `json:"authorization_query_args_names"`
|
||||
AuthorizationQueryArgsValues interface{} `json:"authorization_query_args_values"`
|
||||
BearerTokenCookieName interface{} `json:"bearer_token_cookie_name"`
|
||||
BearerTokenParamType []string `json:"bearer_token_param_type"`
|
||||
CacheIntrospection bool `json:"cache_introspection"`
|
||||
CacheTokenExchange bool `json:"cache_token_exchange"`
|
||||
CacheTokens bool `json:"cache_tokens"`
|
||||
CacheTokensSalt interface{} `json:"cache_tokens_salt"`
|
||||
CacheTtl int `json:"cache_ttl"`
|
||||
CacheTtlMax interface{} `json:"cache_ttl_max"`
|
||||
CacheTtlMin interface{} `json:"cache_ttl_min"`
|
||||
CacheTtlNeg interface{} `json:"cache_ttl_neg"`
|
||||
CacheTtlResurrect interface{} `json:"cache_ttl_resurrect"`
|
||||
CacheUserInfo bool `json:"cache_user_info"`
|
||||
ClientAlg interface{} `json:"client_alg"`
|
||||
ClientArg string `json:"client_arg"`
|
||||
ClientAuth interface{} `json:"client_auth"`
|
||||
ClientCredentialsParamType []string `json:"client_credentials_param_type"`
|
||||
ClientId []string `json:"client_id"`
|
||||
ClientJwk interface{} `json:"client_jwk"`
|
||||
ClientSecret []string `json:"client_secret"`
|
||||
ConsumerBy []string `json:"consumer_by"`
|
||||
ConsumerClaim interface{} `json:"consumer_claim"`
|
||||
ConsumerOptional bool `json:"consumer_optional"`
|
||||
CredentialClaim []string `json:"credential_claim"`
|
||||
DiscoveryHeadersNames interface{} `json:"discovery_headers_names"`
|
||||
DiscoveryHeadersValues interface{} `json:"discovery_headers_values"`
|
||||
DisplayErrors bool `json:"display_errors"`
|
||||
Domains interface{} `json:"domains"`
|
||||
|
||||
EnableHsSignatures bool `json:"enable_hs_signatures"`
|
||||
EndSessionEndpoint interface{} `json:"end_session_endpoint"`
|
||||
ExtraJwksUris interface{} `json:"extra_jwks_uris"`
|
||||
ForbiddenDestroySession bool `json:"forbidden_destroy_session"`
|
||||
ForbiddenErrorMessage string `json:"forbidden_error_message"`
|
||||
ForbiddenRedirectUri interface{} `json:"forbidden_redirect_uri"`
|
||||
GroupsClaim []string `json:"groups_claim"`
|
||||
GroupsRequired interface{} `json:"groups_required"`
|
||||
HideCredentials bool `json:"hide_credentials"`
|
||||
HttpProxy interface{} `json:"http_proxy"`
|
||||
HttpProxyAuthorization interface{} `json:"http_proxy_authorization"`
|
||||
HttpVersion float64 `json:"http_version"`
|
||||
HttpsProxy interface{} `json:"https_proxy"`
|
||||
HttpsProxyAuthorization interface{} `json:"https_proxy_authorization"`
|
||||
IdTokenParamName interface{} `json:"id_token_param_name"`
|
||||
IdTokenParamType []string `json:"id_token_param_type"`
|
||||
IgnoreSignature []interface{} `json:"ignore_signature"`
|
||||
IntrospectJwtTokens bool `json:"introspect_jwt_tokens"`
|
||||
|
||||
Issuer string `json:"issuer"`
|
||||
IssuersAllowed interface{} `json:"issuers_allowed"`
|
||||
JwtSessionClaim string `json:"jwt_session_claim"`
|
||||
JwtSessionCookie interface{} `json:"jwt_session_cookie"`
|
||||
Keepalive bool `json:"keepalive"`
|
||||
Leeway int `json:"leeway"`
|
||||
LoginAction string `json:"login_action"`
|
||||
LoginMethods []string `json:"login_methods"`
|
||||
LoginRedirectMode string `json:"login_redirect_mode"`
|
||||
LoginRedirectUri interface{} `json:"login_redirect_uri"`
|
||||
LoginTokens []string `json:"login_tokens"`
|
||||
LogoutMethods []string `json:"logout_methods"`
|
||||
LogoutPostArg interface{} `json:"logout_post_arg"`
|
||||
LogoutQueryArg interface{} `json:"logout_query_arg"`
|
||||
LogoutRedirectUri interface{} `json:"logout_redirect_uri"`
|
||||
LogoutRevoke bool `json:"logout_revoke"`
|
||||
LogoutRevokeAccessToken bool `json:"logout_revoke_access_token"`
|
||||
LogoutRevokeRefreshToken bool `json:"logout_revoke_refresh_token"`
|
||||
LogoutUriSuffix interface{} `json:"logout_uri_suffix"`
|
||||
MaxAge interface{} `json:"max_age"`
|
||||
NoProxy interface{} `json:"no_proxy"`
|
||||
PasswordParamType []string `json:"password_param_type"`
|
||||
PreserveQueryArgs bool `json:"preserve_query_args"`
|
||||
RedirectUri interface{} `json:"redirect_uri"`
|
||||
RediscoveryLifetime int `json:"rediscovery_lifetime"`
|
||||
RefreshTokenParamName interface{} `json:"refresh_token_param_name"`
|
||||
RefreshTokenParamType []string `json:"refresh_token_param_type"`
|
||||
RefreshTokens bool `json:"refresh_tokens"`
|
||||
ResponseMode string `json:"response_mode"`
|
||||
ResponseType []string `json:"response_type"`
|
||||
Reverify bool `json:"reverify"`
|
||||
RevocationEndpoint interface{} `json:"revocation_endpoint"`
|
||||
RevocationEndpointAuthMethod interface{} `json:"revocation_endpoint_auth_method"`
|
||||
RolesClaim []string `json:"roles_claim"`
|
||||
RolesRequired interface{} `json:"roles_required"`
|
||||
RunOnPreflight bool `json:"run_on_preflight"`
|
||||
Scopes []string `json:"scopes"`
|
||||
ScopesClaim []interface{} `json:"scopes_claim"`
|
||||
ScopesRequired []interface{} `json:"scopes_required"`
|
||||
SearchUserInfo bool `json:"search_user_info"`
|
||||
SessionCompressor string `json:"session_compressor"`
|
||||
SessionCookieDomain interface{} `json:"session_cookie_domain"`
|
||||
|
||||
SessionMemcacheHost string `json:"session_memcache_host"`
|
||||
SessionMemcachePort int `json:"session_memcache_port"`
|
||||
SessionMemcachePrefix string `json:"session_memcache_prefix"`
|
||||
SessionMemcacheSocket interface{} `json:"session_memcache_socket"`
|
||||
|
||||
SessionSecret interface{} `json:"session_secret"`
|
||||
SessionStorage string `json:"session_storage"`
|
||||
SessionStrategy string `json:"session_strategy"`
|
||||
SslVerify bool `json:"ssl_verify"`
|
||||
Timeout int `json:"timeout"`
|
||||
TokenEndpoint interface{} `json:"token_endpoint"`
|
||||
TokenEndpointAuthMethod interface{} `json:"token_endpoint_auth_method"`
|
||||
TokenExchangeEndpoint interface{} `json:"token_exchange_endpoint"`
|
||||
TokenHeadersClient interface{} `json:"token_headers_client"`
|
||||
TokenHeadersGrants interface{} `json:"token_headers_grants"`
|
||||
TokenHeadersNames interface{} `json:"token_headers_names"`
|
||||
TokenHeadersPrefix interface{} `json:"token_headers_prefix"`
|
||||
TokenHeadersReplay interface{} `json:"token_headers_replay"`
|
||||
TokenHeadersValues interface{} `json:"token_headers_values"`
|
||||
TokenPostArgsClient interface{} `json:"token_post_args_client"`
|
||||
TokenPostArgsNames interface{} `json:"token_post_args_names"`
|
||||
TokenPostArgsValues interface{} `json:"token_post_args_values"`
|
||||
UnauthorizedErrorMessage string `json:"unauthorized_error_message"`
|
||||
UnauthorizedRedirectUri interface{} `json:"unauthorized_redirect_uri"`
|
||||
UnexpectedRedirectUri interface{} `json:"unexpected_redirect_uri"`
|
||||
UpstreamAccessTokenHeader string `json:"upstream_access_token_header"`
|
||||
UpstreamAccessTokenJwkHeader interface{} `json:"upstream_access_token_jwk_header"`
|
||||
UpstreamHeadersClaims interface{} `json:"upstream_headers_claims"`
|
||||
UpstreamHeadersNames interface{} `json:"upstream_headers_names"`
|
||||
UpstreamIdTokenHeader interface{} `json:"upstream_id_token_header"`
|
||||
UpstreamIdTokenJwkHeader interface{} `json:"upstream_id_token_jwk_header"`
|
||||
UpstreamIntrospectionHeader interface{} `json:"upstream_introspection_header"`
|
||||
UpstreamRefreshTokenHeader interface{} `json:"upstream_refresh_token_header"`
|
||||
UpstreamSessionIdHeader interface{} `json:"upstream_session_id_header"`
|
||||
UpstreamUserInfoHeader interface{} `json:"upstream_user_info_header"`
|
||||
UserinfoEndpoint interface{} `json:"userinfo_endpoint"`
|
||||
VerifyClaims bool `json:"verify_claims"`
|
||||
VerifyNonce bool `json:"verify_nonce"`
|
||||
VerifyParameters bool `json:"verify_parameters"`
|
||||
VerifySignature bool `json:"verify_signature"`
|
||||
SessionRedis SessionRedis `json:"session_redis"`
|
||||
}
|
||||
|
||||
type SessionRedis struct {
|
||||
SessionRedisAuth interface{} `json:"session_redis_auth"`
|
||||
SessionRedisClusterMaxredirections interface{} `json:"session_redis_cluster_maxredirections"`
|
||||
SessionRedisClusterNodes interface{} `json:"session_redis_cluster_nodes"`
|
||||
SessionRedisConnectTimeout interface{} `json:"session_redis_connect_timeout"`
|
||||
SessionRedisHost string `json:"session_redis_host"`
|
||||
SessionRedisPort int `json:"session_redis_port"`
|
||||
SessionRedisPrefix string `json:"session_redis_prefix"`
|
||||
SessionRedisReadTimeout interface{} `json:"session_redis_read_timeout"`
|
||||
SessionRedisSendTimeout interface{} `json:"session_redis_send_timeout"`
|
||||
SessionRedisServerName interface{} `json:"session_redis_server_name"`
|
||||
SessionRedisSocket interface{} `json:"session_redis_socket"`
|
||||
SessionRedisSsl bool `json:"session_redis_ssl"`
|
||||
SessionRedisSslVerify bool `json:"session_redis_ssl_verify"`
|
||||
}
|
||||
|
||||
type SessionCookie struct {
|
||||
SessionCookieHttponly bool `json:"session_cookie_httponly"`
|
||||
SessionCookieIdletime interface{} `json:"session_cookie_idletime"`
|
||||
SessionCookieLifetime int `json:"session_cookie_lifetime"`
|
||||
SessionCookieMaxsize int `json:"session_cookie_maxsize"`
|
||||
SessionCookieName string `json:"session_cookie_name"`
|
||||
SessionCookiePath string `json:"session_cookie_path"`
|
||||
SessionCookieRenew int `json:"session_cookie_renew"`
|
||||
SessionCookieSamesite string `json:"session_cookie_samesite"`
|
||||
SessionCookieSecure interface{} `json:"session_cookie_secure"`
|
||||
}
|
||||
|
||||
type Introspection struct {
|
||||
IntrospectionEndpoint interface{} `json:"introspection_endpoint"`
|
||||
IntrospectionEndpointAuthMethod interface{} `json:"introspection_endpoint_auth_method"`
|
||||
IntrospectionHeadersClient interface{} `json:"introspection_headers_client"`
|
||||
IntrospectionHeadersNames interface{} `json:"introspection_headers_names"`
|
||||
IntrospectionHeadersValues interface{} `json:"introspection_headers_values"`
|
||||
IntrospectionHint string `json:"introspection_hint"`
|
||||
IntrospectionPostArgsClient interface{} `json:"introspection_post_args_client"`
|
||||
IntrospectionPostArgsNames interface{} `json:"introspection_post_args_names"`
|
||||
IntrospectionPostArgsValues interface{} `json:"introspection_post_args_values"`
|
||||
}
|
||||
|
||||
type DownStream struct {
|
||||
}
|
||||
|
||||
type Upstream struct {
|
||||
DownstreamAccessTokenHeader interface{} `json:"downstream_access_token_header"`
|
||||
DownstreamAccessTokenJwkHeader interface{} `json:"downstream_access_token_jwk_header"`
|
||||
DownstreamHeadersClaims interface{} `json:"downstream_headers_claims"`
|
||||
DownstreamHeadersNames interface{} `json:"downstream_headers_names"`
|
||||
DownstreamIdTokenHeader interface{} `json:"downstream_id_token_header"`
|
||||
DownstreamIdTokenJwkHeader interface{} `json:"downstream_id_token_jwk_header"`
|
||||
DownstreamIntrospectionHeader interface{} `json:"downstream_introspection_header"`
|
||||
DownstreamRefreshTokenHeader interface{} `json:"downstream_refresh_token_header"`
|
||||
DownstreamSessionIdHeader interface{} `json:"downstream_session_id_header"`
|
||||
DownstreamUserInfoHeader interface{} `json:"downstream_user_info_header"`
|
||||
}
|
17
go.mod
17
go.mod
@@ -17,6 +17,7 @@ require (
|
||||
github.com/hashicorp/consul/api v1.9.1
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.12.1
|
||||
github.com/jhump/protoreflect v1.14.1
|
||||
github.com/lestrrat-go/jwx v1.2.28
|
||||
github.com/nacos-group/nacos-sdk-go/v2 v2.2.3
|
||||
github.com/nsqio/go-nsq v1.1.0
|
||||
github.com/ohler55/ojg v1.12.9
|
||||
@@ -26,8 +27,8 @@ require (
|
||||
github.com/soheilhy/cmux v0.1.5
|
||||
github.com/urfave/cli/v2 v2.23.4
|
||||
github.com/valyala/fasthttp v1.47.0
|
||||
golang.org/x/crypto v0.7.0
|
||||
golang.org/x/net v0.8.0
|
||||
golang.org/x/crypto v0.17.0
|
||||
golang.org/x/net v0.10.0
|
||||
google.golang.org/grpc v1.53.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
)
|
||||
@@ -41,6 +42,7 @@ require (
|
||||
github.com/bits-and-blooms/bitset v1.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/creasty/defaults v1.5.2 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/dubbogo/triple v1.1.8 // indirect
|
||||
github.com/go-kit/log v0.1.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||
@@ -50,6 +52,7 @@ require (
|
||||
github.com/go-playground/locales v0.14.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.11.0 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
@@ -60,6 +63,11 @@ require (
|
||||
github.com/k0kubun/pp v3.0.1+incompatible // indirect
|
||||
github.com/knadh/koanf v1.4.1 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
@@ -141,7 +149,6 @@ require (
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/savsgio/gotils v0.0.0-20211223103454-d0aaa54c5899 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
@@ -159,8 +166,8 @@ require (
|
||||
go.uber.org/atomic v1.9.0
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.23.0
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.8.0
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0
|
||||
golang.org/x/time v0.1.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
|
||||
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||
|
Reference in New Issue
Block a user