Files
grafana/pkg/services/signingkeys/signingkeysimpl/service.go

88 lines
2.9 KiB
Go
Raw Normal View History

package signingkeysimpl
import (
"context"
"crypto"
AuthN: Embed an OAuth2 server for external service authentication (#68086) * Moving POC files from #64283 to a new branch Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Adding missing permission definition Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Force the service instantiation while client isn't merged Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Merge conf with main Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Leave go-sqlite3 version unchanged Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * tidy Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * User SearchUserPermissions instead of SearchUsersPermissions * Replace DummyKeyService with signingkeys.Service * Use user:id:<id> as subject * Fix introspection endpoint issue * Add X-Grafana-Org-Id to get_resources.bash script * Regenerate toggles_gen.go * Fix basic.go * Add GetExternalService tests * Add GetPublicKeyScopes tests * Add GetScopesOnUser tests * Add GetScopes tests * Add ParsePublicKeyPem tests * Add database test for GetByName * re-add comments * client tests added * Add GetExternalServicePublicKey tests * Add other test case to GetExternalServicePublicKey * client_credentials grant test * Add test to jwtbearer grant * Test Comments * Add handleKeyOptions tests * Add RSA key generation test * Add ECDSA by default to EmbeddedSigningKeysService * Clean up org id scope and audiences * Add audiences to the DB * Fix check on Audience * Fix double import * Add AC Store mock and align oauthserver tests * Fix test after rebase * Adding missing store function to mock * Fix double import * Add CODEOWNER * Fix some linting errors * errors don't need type assertion * Typo codeowners * use mockery for oauthserver store * Add feature toggle check * Fix db tests to handle the feature flag * Adding call to DeleteExternalServiceRole * Fix flaky test * Re-organize routes comments and plan futur work * Add client_id check to Extended JWT client * Clean up * Fix * Remove background service registry instantiation of the OAuth server * Comment cleanup * Remove unused client function * Update go.mod to use the latest ory/fosite commit * Remove oauth2_server related configs from defaults.ini * Add audiences to DTO * Fix flaky test * Remove registration endpoint and demo scripts. Document code * Rename packages * Remove the OAuthService vs OAuthServer confusion * fix incorrect import ext_jwt_test * Comments and order * Comment basic auth * Remove unecessary todo * Clean api * Moving ParsePublicKeyPem to utils * re ordering functions in service.go * Fix comment * comment on the redirect uri * Add RBAC actions, not only scopes * Fix tests * re-import featuremgmt in migrations * Fix wire * Fix scopes in test * Fix flaky test * Remove todo, the intersection should always return the minimal set * Remove unecessary check from intersection code * Allow env overrides on settings * remove the term app name * Remove app keyword for client instead and use Name instead of ExternalServiceName * LogID remove ExternalService ref * Use Name instead of ExternalServiceName * Imports order * Inline * Using ExternalService and ExternalServiceDTO * Remove xorm tags * comment * Rename client files * client -> external service * comments * Move test to correct package * slimmer test * cachedUser -> cachedExternalService * Fix aggregate store test * PluginAuthSession -> AuthSession * Revert the nil cehcks * Remove unecessary extra * Removing custom session * fix typo in test * Use constants for tests * Simplify HandleToken tests * Refactor the HandleTokenRequest test * test message * Review test * Prevent flacky test on client as well * go imports * Revert changes from 526e48ad4550fed7e2b753b9d0a0cc6097155f58 * AuthN: Change the External Service registration form (#68649) * AuthN: change the External Service registration form * Gen default permissions * Change demo script registration form * Remove unecessary comment * Nit. * Reduce cyclomatic complexity * Remove demo_scripts * Handle case with no service account * Comments * Group key gen * Nit. * Check the SaveExternalService test * Rename cachedUser to cachedClient in test * One more test case to database test * Comments * Remove last org scope Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Update pkg/services/oauthserver/utils/utils_test.go * Update pkg/services/sqlstore/migrations/oauthserver/migrations.go Remove comment * Update pkg/setting/setting.go Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> --------- Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com>
2023-05-25 15:38:30 +02:00
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"errors"
"strings"
"time"
"github.com/go-jose/go-jose/v3"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/signingkeys"
"github.com/grafana/grafana/pkg/services/signingkeys/signingkeystore"
)
var _ signingkeys.Service = new(Service)
func ProvideEmbeddedSigningKeysService(dbStore db.DB, secretsService secrets.Service,
remoteCache remotecache.CacheStorage,
) (*Service, error) {
s := &Service{
log: log.New("auth.key_service"),
store: signingkeystore.NewSigningKeyStore(dbStore, secretsService),
remoteCache: remoteCache,
}
return s, nil
}
// Service provides functionality for managing signing keys used to sign and verify JWT tokens for
// the OSS version of Grafana.
//
// The service is under active development and is not yet ready for production use.
type Service struct {
log log.Logger
store signingkeystore.SigningStore
remoteCache remotecache.CacheStorage
}
// GetJWKS returns the JSON Web Key Set (JWKS) with all the keys that can be used to verify tokens (public keys)
func (s *Service) GetJWKS(ctx context.Context) (jose.JSONWebKeySet, error) {
jwks, err := s.store.GetJWKS(ctx)
return jwks, err
}
// GetOrCreatePrivateKey returns the private key with the specified key ID. If the key does not exist, it will be
// created with the specified algorithm.
// The key will be automatically rotated at the beginning of each month. The previous key will be kept for 30 days.
func (s *Service) GetOrCreatePrivateKey(ctx context.Context,
keyPrefix string, alg jose.SignatureAlgorithm) (string, crypto.Signer, error) {
if alg != jose.ES256 {
s.log.Error("Only ES256 is supported", "alg", alg)
return "", nil, signingkeys.ErrKeyGenerationFailed.Errorf("Only ES256 is supported: %v", alg)
}
keyID := keyMonthScopedID(keyPrefix, alg)
signer, err := s.store.GetPrivateKey(ctx, keyID)
if err == nil {
return keyID, signer, nil
}
s.log.Debug("Private key not found, generating new key", "keyID", keyID, "err", err)
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
s.log.Error("Error generating private key", "err", err)
return "", nil, signingkeys.ErrKeyGenerationFailed.Errorf("Error generating private key: %v", err)
}
expiry := time.Now().Add(30 * 24 * time.Hour)
if signer, err = s.store.AddPrivateKey(ctx, keyID, alg, privateKey, &expiry, false); err != nil && !errors.Is(err, signingkeys.ErrSigningKeyAlreadyExists) {
return "", nil, err
}
return keyID, signer, nil
}
func keyMonthScopedID(keyPrefix string, alg jose.SignatureAlgorithm) string {
keyID := keyPrefix + "-" + time.Now().UTC().Format("2006-01") + "-" + strings.ToLower(string(alg))
return keyID
}