2023-03-28 18:39:22 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
2020-05-12 01:59:29 +00:00
|
|
|
package ssoauth
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/hashicorp/consul/agent/consul/authmethod"
|
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
|
|
"github.com/hashicorp/consul/internal/go-sso/oidcauth"
|
|
|
|
"github.com/hashicorp/go-hclog"
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
authmethod.Register("jwt", func(logger hclog.Logger, method *structs.ACLAuthMethod) (authmethod.Validator, error) {
|
|
|
|
v, err := NewValidator(logger, method)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return v, nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validator is the wrapper around the go-sso library that also conforms to the
|
|
|
|
// authmethod.Validator interface.
|
|
|
|
type Validator struct {
|
|
|
|
name string
|
|
|
|
methodType string
|
|
|
|
config *oidcauth.Config
|
|
|
|
logger hclog.Logger
|
|
|
|
oa *oidcauth.Authenticator
|
|
|
|
}
|
|
|
|
|
|
|
|
var _ authmethod.Validator = (*Validator)(nil)
|
|
|
|
|
|
|
|
func NewValidator(logger hclog.Logger, method *structs.ACLAuthMethod) (*Validator, error) {
|
|
|
|
if err := validateType(method.Type); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var config Config
|
|
|
|
if err := authmethod.ParseConfig(method.Config, &config); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
ssoConfig := config.convertForLibrary(method.Type)
|
|
|
|
|
|
|
|
oa, err := oidcauth.New(ssoConfig, logger)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
v := &Validator{
|
|
|
|
name: method.Name,
|
|
|
|
methodType: method.Type,
|
|
|
|
config: ssoConfig,
|
|
|
|
logger: logger,
|
|
|
|
oa: oa,
|
|
|
|
}
|
|
|
|
|
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Name implements authmethod.Validator.
|
|
|
|
func (v *Validator) Name() string { return v.name }
|
|
|
|
|
|
|
|
// Stop implements authmethod.Validator.
|
|
|
|
func (v *Validator) Stop() { v.oa.Stop() }
|
|
|
|
|
|
|
|
// ValidateLogin implements authmethod.Validator.
|
|
|
|
func (v *Validator) ValidateLogin(ctx context.Context, loginToken string) (*authmethod.Identity, error) {
|
|
|
|
c, err := v.oa.ClaimsFromJWT(ctx, loginToken)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return v.identityFromClaims(c), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *Validator) identityFromClaims(c *oidcauth.Claims) *authmethod.Identity {
|
|
|
|
id := v.NewIdentity()
|
|
|
|
id.SelectableFields = &fieldDetails{
|
|
|
|
Values: c.Values,
|
|
|
|
Lists: c.Lists,
|
|
|
|
}
|
|
|
|
for k, val := range c.Values {
|
|
|
|
id.ProjectedVars["value."+k] = val
|
|
|
|
}
|
|
|
|
id.EnterpriseMeta = v.ssoEntMetaFromClaims(c)
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewIdentity implements authmethod.Validator.
|
|
|
|
func (v *Validator) NewIdentity() *authmethod.Identity {
|
|
|
|
// Populate selectable fields with empty values so emptystring filters
|
|
|
|
// works. Populate projectable vars with empty values so HIL works.
|
|
|
|
fd := &fieldDetails{
|
|
|
|
Values: make(map[string]string),
|
|
|
|
Lists: make(map[string][]string),
|
|
|
|
}
|
|
|
|
projectedVars := make(map[string]string)
|
|
|
|
for _, k := range v.config.ClaimMappings {
|
|
|
|
fd.Values[k] = ""
|
|
|
|
projectedVars["value."+k] = ""
|
|
|
|
}
|
|
|
|
for _, k := range v.config.ListClaimMappings {
|
|
|
|
fd.Lists[k] = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return &authmethod.Identity{
|
|
|
|
SelectableFields: fd,
|
|
|
|
ProjectedVars: projectedVars,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type fieldDetails struct {
|
|
|
|
Values map[string]string `bexpr:"value"`
|
|
|
|
Lists map[string][]string `bexpr:"list"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Config is the collection of all settings that pertain to doing OIDC-based
|
|
|
|
// authentication and direct JWT-based authentication processes.
|
|
|
|
type Config struct {
|
|
|
|
// common for type=oidc and type=jwt
|
|
|
|
JWTSupportedAlgs []string `json:",omitempty"`
|
|
|
|
BoundAudiences []string `json:",omitempty"`
|
|
|
|
ClaimMappings map[string]string `json:",omitempty"`
|
|
|
|
ListClaimMappings map[string]string `json:",omitempty"`
|
|
|
|
OIDCDiscoveryURL string `json:",omitempty"`
|
|
|
|
OIDCDiscoveryCACert string `json:",omitempty"`
|
|
|
|
|
|
|
|
// just for type=jwt
|
|
|
|
JWKSURL string `json:",omitempty"`
|
|
|
|
JWKSCACert string `json:",omitempty"`
|
|
|
|
JWTValidationPubKeys []string `json:",omitempty"`
|
|
|
|
BoundIssuer string `json:",omitempty"`
|
|
|
|
ExpirationLeeway time.Duration `json:",omitempty"`
|
|
|
|
NotBeforeLeeway time.Duration `json:",omitempty"`
|
|
|
|
ClockSkewLeeway time.Duration `json:",omitempty"`
|
|
|
|
|
|
|
|
enterpriseConfig `mapstructure:",squash"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Config) convertForLibrary(methodType string) *oidcauth.Config {
|
|
|
|
ssoConfig := &oidcauth.Config{
|
|
|
|
Type: methodType,
|
|
|
|
|
|
|
|
// common for type=oidc and type=jwt
|
|
|
|
JWTSupportedAlgs: c.JWTSupportedAlgs,
|
|
|
|
BoundAudiences: c.BoundAudiences,
|
|
|
|
ClaimMappings: c.ClaimMappings,
|
|
|
|
ListClaimMappings: c.ListClaimMappings,
|
|
|
|
OIDCDiscoveryURL: c.OIDCDiscoveryURL,
|
|
|
|
OIDCDiscoveryCACert: c.OIDCDiscoveryCACert,
|
|
|
|
|
|
|
|
// just for type=jwt
|
|
|
|
JWKSURL: c.JWKSURL,
|
|
|
|
JWKSCACert: c.JWKSCACert,
|
|
|
|
JWTValidationPubKeys: c.JWTValidationPubKeys,
|
|
|
|
BoundIssuer: c.BoundIssuer,
|
|
|
|
ExpirationLeeway: c.ExpirationLeeway,
|
|
|
|
NotBeforeLeeway: c.NotBeforeLeeway,
|
|
|
|
ClockSkewLeeway: c.ClockSkewLeeway,
|
|
|
|
}
|
|
|
|
c.enterpriseConvertForLibrary(ssoConfig)
|
|
|
|
return ssoConfig
|
|
|
|
}
|