mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* AuthN: Implement identity.Requester interface for authn.Identity * AuthN: Replace OrgRole with GetOrgRole * IDForwarding: skip converting to SignedInUser * Pass identity directly in permission sync hook
99 lines
2.6 KiB
Go
99 lines
2.6 KiB
Go
package idimpl
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/go-jose/go-jose/v3/jwt"
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"github.com/grafana/grafana/pkg/infra/remotecache"
|
|
"github.com/grafana/grafana/pkg/services/auth"
|
|
"github.com/grafana/grafana/pkg/services/auth/identity"
|
|
"github.com/grafana/grafana/pkg/services/authn"
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
const (
|
|
cachePrefix = "id-token"
|
|
tokenTTL = 1 * time.Hour
|
|
cacheTTL = 58 * time.Minute
|
|
)
|
|
|
|
var _ auth.IDService = (*Service)(nil)
|
|
|
|
func ProvideService(
|
|
cfg *setting.Cfg, signer auth.IDSigner, cache remotecache.CacheStorage,
|
|
features featuremgmt.FeatureToggles, authnService authn.Service,
|
|
) *Service {
|
|
s := &Service{cfg, log.New("id-service"), signer, cache}
|
|
|
|
if features.IsEnabled(featuremgmt.FlagIdForwarding) {
|
|
authnService.RegisterPostAuthHook(s.hook, 140)
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
type Service struct {
|
|
cfg *setting.Cfg
|
|
logger log.Logger
|
|
signer auth.IDSigner
|
|
cache remotecache.CacheStorage
|
|
}
|
|
|
|
func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (string, error) {
|
|
namespace, identifier := id.GetNamespacedID()
|
|
|
|
cacheKey := prefixCacheKey(id.GetCacheKey())
|
|
cachedToken, err := s.cache.Get(ctx, cacheKey)
|
|
if err == nil {
|
|
s.logger.Debug("Cached token found", "namespace", namespace, "id", identifier)
|
|
return string(cachedToken), nil
|
|
}
|
|
|
|
s.logger.Debug("Sign new id token", "namespace", namespace, "id", identifier)
|
|
|
|
now := time.Now()
|
|
token, err := s.signer.SignIDToken(ctx, &auth.IDClaims{
|
|
Claims: jwt.Claims{
|
|
ID: identifier,
|
|
Issuer: s.cfg.AppURL,
|
|
Audience: jwt.Audience{strconv.FormatInt(id.GetOrgID(), 10)},
|
|
Subject: fmt.Sprintf("%s:%s", namespace, identifier),
|
|
Expiry: jwt.NewNumericDate(now.Add(tokenTTL)),
|
|
IssuedAt: jwt.NewNumericDate(now),
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if err := s.cache.Set(ctx, cacheKey, []byte(token), cacheTTL); err != nil {
|
|
s.logger.Error("failed to set cache", "error", err)
|
|
}
|
|
|
|
return token, nil
|
|
}
|
|
|
|
func (s *Service) hook(ctx context.Context, identity *authn.Identity, _ *authn.Request) error {
|
|
// FIXME(kalleep): we should probably lazy load this
|
|
token, err := s.SignIdentity(ctx, identity)
|
|
if err != nil {
|
|
namespace, id := identity.NamespacedID()
|
|
s.logger.Error("Failed to sign id token", "err", err, "namespace", namespace, "id", id)
|
|
// for now don't return error so we don't break authentication from this hook
|
|
return nil
|
|
}
|
|
|
|
identity.IDToken = token
|
|
return nil
|
|
}
|
|
|
|
func prefixCacheKey(key string) string {
|
|
return fmt.Sprintf("%s-%s", cachePrefix, key)
|
|
}
|