Files
QSfera/vendor/github.com/libregraph/lico/identity/authorities/registry.go
T
2023-04-19 20:24:34 +02:00

226 lines
6.6 KiB
Go

/*
* Copyright 2017-2019 Kopano and its licensors
*
* 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 authorities
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/url"
"sync"
"github.com/sirupsen/logrus"
"sigs.k8s.io/yaml"
)
// Registry implements the registry for registered authorities.
type Registry struct {
mutex sync.RWMutex
baseURI *url.URL
defaultID string
authorities map[string]AuthorityRegistration
logger logrus.FieldLogger
}
// NewRegistry creates a new authorizations Registry with the provided parameters.
func NewRegistry(ctx context.Context, baseURI *url.URL, registrationConfFilepath string, logger logrus.FieldLogger) (*Registry, error) {
registryData := &authorityRegistryData{}
if registrationConfFilepath != "" {
logger.Debugf("parsing authorities registration conf from %v", registrationConfFilepath)
registryFile, err := ioutil.ReadFile(registrationConfFilepath)
if err != nil {
return nil, err
}
// NOTE(longsleep): Convert YAML to JSON for parsing. This is done to be
// able to use jose.JSONWebKeySet custom JSON unmarshaller which is not
// available for YAML. We could use sigs.k8s.io/yaml directly as it has
// the same behavior, but we do it explicitly to be clear.
j, err := yaml.YAMLToJSON(registryFile)
if err != nil {
return nil, err
}
err = json.Unmarshal(j, registryData)
if err != nil {
return nil, err
}
}
r := &Registry{
baseURI: baseURI,
authorities: make(map[string]AuthorityRegistration),
logger: logger,
}
var defaultAuthorityRegistrationData *authorityRegistrationData
var defaultAuthority AuthorityRegistration
for _, registrationData := range registryData.Authorities {
var authority AuthorityRegistration
var validateErr error
if registrationData.ID == "" {
registrationData.ID = registrationData.Name
r.logger.WithField("id", registrationData.ID).Warnln("authority has no id, using name")
}
switch registrationData.AuthorityType {
case AuthorityTypeOIDC:
authority, validateErr = newOIDCAuthorityRegistration(r, registrationData)
case AuthorityTypeSAML2:
authority, validateErr = newSAML2AuthorityRegistration(r, registrationData)
}
fields := logrus.Fields{
"id": registrationData.ID,
"authority_type": registrationData.AuthorityType,
"insecure": registrationData.Insecure,
"trusted": registrationData.Trusted,
"default": registrationData.Default,
"alias_required": registrationData.IdentityAliasRequired,
}
if validateErr != nil {
logger.WithError(validateErr).WithFields(fields).Warnln("skipped registration of invalid authority entry")
continue
}
if authority == nil {
logger.WithFields(fields).Warnln("skipped registration of authority of unknown type")
continue
}
if registerErr := r.Register(authority); registerErr != nil {
logger.WithError(registerErr).WithFields(fields).Warnln("skipped registration of invalid authority")
continue
}
if registrationData.Default || defaultAuthorityRegistrationData == nil {
if defaultAuthorityRegistrationData == nil || !defaultAuthorityRegistrationData.Default {
defaultAuthorityRegistrationData = registrationData
defaultAuthority = authority
} else {
logger.Warnln("ignored default authority flag since already have a default")
}
} else {
// TODO(longsleep): Implement authority selection.
logger.Warnln("non-default additional authorities are not supported yet")
}
go func() {
if initializeErr := authority.Initialize(ctx, r); initializeErr != nil {
logger.WithError(initializeErr).WithFields(fields).Warnln("failed to initialize authority")
}
}()
logger.WithFields(fields).Debugln("registered authority")
}
if defaultAuthority != nil {
if defaultAuthorityRegistrationData.Default {
r.defaultID = defaultAuthorityRegistrationData.ID
logger.WithField("id", defaultAuthorityRegistrationData.ID).Infoln("using external default authority")
} else {
logger.Warnln("non-default authorities are not supported yet")
}
}
return r, nil
}
// Register validates the provided authority registration and adds the authority
// to the accociated registry if valid. Returns error otherwise.
func (r *Registry) Register(authority AuthorityRegistration) error {
id := authority.ID()
if id == "" {
return errors.New("no authority id")
}
if err := authority.Validate(); err != nil {
return fmt.Errorf("authority data validation error: %w", err)
}
switch authority.AuthorityType() {
case AuthorityTypeOIDC:
// breaks
case AuthorityTypeSAML2:
// breaks
default:
return fmt.Errorf("unknown authority type: %v", authority.AuthorityType())
}
r.mutex.Lock()
defer r.mutex.Unlock()
r.authorities[id] = authority
return nil
}
// Lookup returns and validates the authority Detail information for the provided
// parameters from the accociated authority registry.
func (r *Registry) Lookup(ctx context.Context, authorityID string) (*Details, error) {
registration, ok := r.Get(ctx, authorityID)
if !ok {
return nil, fmt.Errorf("unknown authority id: %v", authorityID)
}
details := registration.Authority()
return details, nil
}
// Get returns the registered authorities registration for the provided client ID.
func (r *Registry) Get(ctx context.Context, authorityID string) (AuthorityRegistration, bool) {
if authorityID == "" {
return nil, false
}
// Lookup authority registration.
r.mutex.RLock()
registration, ok := r.authorities[authorityID]
r.mutex.RUnlock()
return registration, ok
}
// Find returns the first registered authority that satisfies the provided
// selector function.
func (r *Registry) Find(ctx context.Context, selector func(authority AuthorityRegistration) bool) (AuthorityRegistration, bool) {
r.mutex.RLock()
defer r.mutex.RUnlock()
for _, authority := range r.authorities {
if selector(authority) {
return authority, true
}
}
return nil, false
}
// Default returns the default authority from the associated registry if any.
func (r *Registry) Default(ctx context.Context) *Details {
authority, _ := r.Lookup(ctx, r.defaultID)
return authority
}