-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #26 from napptive/feature/PG-1070_multi_secret_int…
…erceptor PG-1070 Add multi secret zone aware interceptor
- Loading branch information
Showing
14 changed files
with
709 additions
and
1,067 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,40 @@ | ||
module github.com/napptive/njwt | ||
|
||
go 1.16 | ||
go 1.19 | ||
|
||
require ( | ||
github.com/fsnotify/fsnotify v1.6.0 // indirect | ||
github.com/golang-jwt/jwt v3.2.2+incompatible | ||
github.com/kr/pretty v0.3.0 // indirect | ||
github.com/mattn/go-colorable v0.1.13 // indirect | ||
github.com/mattn/go-isatty v0.0.17 // indirect | ||
github.com/napptive/grpc-common-go v0.8.0 // indirect | ||
github.com/golang/mock v1.6.0 | ||
github.com/napptive/go-utils v0.0.0-20230302113223-949e47c65976 | ||
github.com/napptive/grpc-jwt-go v0.1.0 | ||
github.com/napptive/grpc-ping-go v0.1.0 | ||
github.com/napptive/nerrors v1.1.0 | ||
github.com/onsi/ginkgo v1.16.5 | ||
github.com/onsi/gomega v1.27.1 | ||
github.com/onsi/gomega v1.27.2 | ||
github.com/rs/xid v1.4.0 | ||
github.com/rs/zerolog v1.29.0 | ||
google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44 // indirect | ||
google.golang.org/grpc v1.53.0 | ||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect | ||
syreclabs.com/go/faker v1.2.3 | ||
) | ||
|
||
require ( | ||
github.com/envoyproxy/protoc-gen-validate v0.9.1 // indirect | ||
github.com/fsnotify/fsnotify v1.6.0 // indirect | ||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect | ||
github.com/golang/protobuf v1.5.2 // indirect | ||
github.com/google/go-cmp v0.5.9 // indirect | ||
github.com/kr/pretty v0.3.0 // indirect | ||
github.com/mattn/go-colorable v0.1.13 // indirect | ||
github.com/mattn/go-isatty v0.0.17 // indirect | ||
github.com/napptive/grpc-common-go v0.8.0 // indirect | ||
github.com/nxadm/tail v1.4.8 // indirect | ||
golang.org/x/net v0.7.0 // indirect | ||
golang.org/x/sys v0.5.0 // indirect | ||
golang.org/x/text v0.7.0 // indirect | ||
golang.org/x/tools v0.6.0 // indirect | ||
google.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5 // indirect | ||
google.golang.org/protobuf v1.28.1 // indirect | ||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect | ||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/** | ||
* Copyright 2023 Napptive | ||
* | ||
* 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 | ||
* | ||
* https://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. | ||
*/ | ||
|
||
//go:generate mockgen -destination secret_provider_mock_test.go -package=interceptors github.com/napptive/njwt/pkg/interceptors SecretProvider | ||
//go:generate mockgen -destination secret_client_mock_test.go -package=interceptors github.com/napptive/grpc-jwt-go SecretsClient | ||
|
||
package interceptors |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
/** | ||
* Copyright 2023 Napptive | ||
* | ||
* 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 | ||
* | ||
* https://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 interceptors | ||
|
||
import ( | ||
"context" | ||
"sync" | ||
"time" | ||
|
||
"github.com/napptive/grpc-jwt-go" | ||
"github.com/napptive/nerrors/pkg/nerrors" | ||
"github.com/napptive/njwt/pkg/config" | ||
"github.com/rs/zerolog/log" | ||
) | ||
|
||
const ClientTimeout = 30 * time.Second | ||
|
||
// CachedSecret stores a JWT secret with the timestamp in which it has been retrieved. | ||
type CachedSecret struct { | ||
timestamp time.Time | ||
secret string | ||
} | ||
|
||
// Clone a cached secret. | ||
func (cs *CachedSecret) Clone() *CachedSecret { | ||
return &CachedSecret{ | ||
timestamp: cs.timestamp, | ||
secret: cs.secret, | ||
} | ||
} | ||
|
||
// InterceptorZoneSecretManager offers a cached zone JWT signing secret retrieval interface. Elements | ||
// retrieved from the SecretsClient are stored in an internal cache for a period of time before being evicted. | ||
type InterceptorZoneSecretManager struct { | ||
sync.RWMutex | ||
// Config with the default signing secret. This is used for backward compatibility with old versions, | ||
// and it will be returned if the zone found in the JWT is empty. | ||
config config.JWTConfig | ||
secretsClient grpc_jwt_go.SecretsClient | ||
zoneCacheTTL time.Duration | ||
SecretCache map[string]*CachedSecret | ||
} | ||
|
||
// NewInterceptorZoneSecretManager creates a zone manager that communicates with the secrets service | ||
// to retrieve zone signing secrets. | ||
func NewInterceptorZoneSecretManager(config config.JWTConfig, secretsClient grpc_jwt_go.SecretsClient, zoneCacheTTL time.Duration) SecretProvider { | ||
manager := &InterceptorZoneSecretManager{ | ||
config: config, | ||
secretsClient: secretsClient, | ||
zoneCacheTTL: zoneCacheTTL, | ||
SecretCache: make(map[string]*CachedSecret), | ||
} | ||
go manager.evictLoop() | ||
return manager | ||
} | ||
|
||
// evictLoop cleans the cache triggering the eviction method. | ||
func (izsm *InterceptorZoneSecretManager) evictLoop() { | ||
ticker := time.NewTicker(izsm.zoneCacheTTL / 2) | ||
for range ticker.C { | ||
izsm.Evict() | ||
} | ||
} | ||
|
||
// Evict old entries of the cache attending to the creation timestamp. | ||
func (izsm *InterceptorZoneSecretManager) Evict() { | ||
timeLimit := time.Now().Add(-1 * izsm.zoneCacheTTL) | ||
izsm.Lock() | ||
defer izsm.Unlock() | ||
for zoneID, secret := range izsm.SecretCache { | ||
if secret.timestamp.Before(timeLimit) { | ||
delete(izsm.SecretCache, zoneID) | ||
} | ||
} | ||
} | ||
|
||
// GetZoneSecret retrieves JWT signing secret associated with a given zone identifier. | ||
func (izsm *InterceptorZoneSecretManager) GetZoneSecret(zoneID string) (*string, error) { | ||
izsm.RLock() | ||
secret, exists := izsm.SecretCache[zoneID] | ||
izsm.RUnlock() | ||
|
||
if exists { | ||
return &secret.secret, nil | ||
} | ||
|
||
if zoneID == "" && izsm.config.Secret != "" { | ||
// Preload the cache with the default secret | ||
izsm.Lock() | ||
izsm.SecretCache[""] = &CachedSecret{ | ||
timestamp: time.Now(), | ||
secret: izsm.config.Secret, | ||
} | ||
izsm.Unlock() | ||
return &izsm.config.Secret, nil | ||
} | ||
// Retrieve it from the secret provider | ||
ctx, cancel := context.WithTimeout(context.Background(), ClientTimeout) | ||
defer cancel() | ||
|
||
zoneSigningSecret, err := izsm.secretsClient.Get(ctx, &grpc_jwt_go.GetSecretRequest{SecretId: zoneID}) | ||
if err != nil { | ||
log.Error().Err(err).Str("zone_id", zoneID).Msg("unable to retrieve zone signing secret") | ||
return nil, nerrors.NewInternalError("cannot verify token") | ||
} | ||
izsm.Lock() | ||
izsm.SecretCache[zoneID] = &CachedSecret{ | ||
timestamp: time.Now(), | ||
secret: zoneSigningSecret.JwtSecret, | ||
} | ||
izsm.Unlock() | ||
return &zoneSigningSecret.JwtSecret, nil | ||
} |
Oops, something went wrong.