Files
wehub-resource-sync 5357c39144
Fuzzer / Run Fuzzer (push) Has been cancelled
Race tests / Go race tests (ubuntu-22.04) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:01:40 +08:00

131 lines
3.5 KiB
Go

// Copyright 2022 Dolthub, Inc.
//
// 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 engine
import (
"errors"
"fmt"
"strings"
"time"
"github.com/dolthub/go-mysql-server/sql/mysql_db"
"github.com/sirupsen/logrus"
"github.com/dolthub/dolt/go/libraries/doltcore/servercfg"
"github.com/dolthub/dolt/go/libraries/utils/jwtauth"
)
// authenticateDoltJWTPlugin is used to authenticate plaintext user plugins
type authenticateDoltJWTPlugin struct {
jwksConfig []servercfg.JwksConfig
}
func NewAuthenticateDoltJWTPlugin(jwksConfig []servercfg.JwksConfig) mysql_db.PlaintextAuthPlugin {
return &authenticateDoltJWTPlugin{jwksConfig: jwksConfig}
}
func (p *authenticateDoltJWTPlugin) Authenticate(db *mysql_db.MySQLDb, user string, userEntry *mysql_db.User, pass string) (bool, error) {
return validateJWT(p.jwksConfig, user, userEntry.Identity, pass, time.Now())
}
func validateJWT(config []servercfg.JwksConfig, username, identity, token string, reqTime time.Time) (bool, error) {
if len(config) == 0 {
return false, errors.New("ValidateJWT: JWKS server config not found")
}
expectedClaimsMap := parseUserIdentity(identity)
sub, ok := expectedClaimsMap["sub"]
if ok && sub != username {
return false, errors.New("ValidateJWT: Subjects do not match")
}
jwksConfig, err := getMatchingJwksConfig(config, expectedClaimsMap["jwks"])
if err != nil {
return false, err
}
pr, err := getJWTProvider(expectedClaimsMap, jwksConfig.LocationUrl)
if err != nil {
return false, err
}
vd, err := jwtauth.NewJWTValidator(pr)
if err != nil {
return false, err
}
claims, err := vd.ValidateJWT(token, reqTime)
if err != nil {
return false, err
}
logString := "Authenticating with JWT: "
for _, field := range jwksConfig.FieldsToLog {
logString += fmt.Sprintf("%s: %s,", field, getClaimFromKey(claims, field))
}
logrus.Info(logString)
return true, nil
}
func getJWTProvider(expectedClaimsMap map[string]string, url string) (jwtauth.JWTProvider, error) {
pr := jwtauth.JWTProvider{URL: url}
for name, claim := range expectedClaimsMap {
switch name {
case "iss":
pr.Issuer = claim
case "aud":
pr.Audience = claim
case "sub":
pr.Subject = claim
case "jwks":
continue
default:
return pr, errors.New("ValidateJWT: Unexpected expected claim found in user identity")
}
}
return pr, nil
}
func getClaimFromKey(claims *jwtauth.Claims, field string) string {
switch field {
case "id":
return claims.ID
case "iss":
return claims.Issuer
case "sub":
return claims.Subject
case "on_behalf_of":
return claims.OnBehalfOf
}
return ""
}
func getMatchingJwksConfig(config []servercfg.JwksConfig, name string) (*servercfg.JwksConfig, error) {
for _, item := range config {
if item.Name == name {
return &item, nil
}
}
return nil, errors.New("ValidateJWT: Matching JWKS config not found")
}
func parseUserIdentity(identity string) map[string]string {
idMap := make(map[string]string)
items := strings.Split(identity, ",")
for _, item := range items {
tup := strings.Split(item, "=")
idMap[tup[0]] = tup[1]
}
return idMap
}