Skip to content

Commit

Permalink
Merge pull request #29 from FIWARE/auth-flow-performance
Browse files Browse the repository at this point in the history
Added async caching
  • Loading branch information
Beknazar Esenbek authored Mar 19, 2024
2 parents a86d1ba + 24fadd1 commit b0f2321
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 59 deletions.
36 changes: 36 additions & 0 deletions common/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package common

import (
"time"

"github.com/patrickmn/go-cache"
)

const CacheExpiry = 60

type Cache interface {
Add(k string, x interface{}, d time.Duration) error
Get(k string) (interface{}, bool)
Delete(k string)
}

type AllCaches struct {
ServiceCache Cache
TirEndpoints Cache
IssuersCache Cache
IssuerCache Cache
}

func initCache() *AllCaches {
return &AllCaches{
ServiceCache: cache.New(CacheExpiry*time.Second, 2*CacheExpiry*time.Second),
TirEndpoints: cache.New(CacheExpiry*time.Second, 2*CacheExpiry*time.Second),
IssuersCache: cache.New(CacheExpiry*time.Second, 2*CacheExpiry*time.Second),
IssuerCache: cache.New(CacheExpiry*time.Second, 2*CacheExpiry*time.Second)}
}

var GlobalCache = initCache()

func ResetGlobalCache() {
GlobalCache = initCache()
}
19 changes: 19 additions & 0 deletions common/httpUtils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package common

import "strings"

func BuildUrlString(address string, path string) string {
if strings.HasSuffix(address, "/") {
if strings.HasPrefix(path, "/") {
return address + strings.TrimPrefix(path, "/")
} else {
return address + path
}
} else {
if strings.HasPrefix(path, "/") {
return address + path
} else {
return address + "/" + path
}
}
}
58 changes: 34 additions & 24 deletions tir/authorizationClient.go
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
package tir

import (
"context"
"encoding/json"
"errors"
"net/http"
"net/url"
"strings"

common "github.com/fiware/VCVerifier/common"
"github.com/fiware/VCVerifier/common"
"github.com/fiware/VCVerifier/logging"
"github.com/patrickmn/go-cache"
"golang.org/x/exp/slices"
)

const WELL_KNOWN_ENDPOINT = "/.well-known/openid-configuration"
const SCOPE_TIR_READ = "tir_read"
const TirEndpointsCache = "tirEndpoints"

// http client to be used
var ErrorTokenEndpointNoResponse = errors.New("no_response_from_token_endpoint")
var ErrorMetaDataNotOk = errors.New("no_metadata_available")
var ErrorGrantTypeNotSupported = errors.New("grant_type_not_supported")
var ErrorScopeNotSupported = errors.New("scope_not_supported")
var ErrorCachedOpenidMetadataNotFound = errors.New("cached_openid_metadata_not_found")

type HttpGetClient interface {
Get(tirAddress string, tirPath string) (resp *http.Response, err error)
Expand All @@ -35,13 +39,33 @@ type NoAuthHttpClient struct {
httpClient HttpClient
}

func (ac AuthorizingHttpClient) FillMetadataCache(context.Context) {
tirEndpointsInterface, hit := common.GlobalCache.TirEndpoints.Get(TirEndpointsCache)
if !hit {
logging.Log().Info("issuers list not found in cache")
return
}

tirEndpoints := tirEndpointsInterface.([]string)
for _, tirEndpoint := range tirEndpoints {
metaData, err := ac.getMetaData(tirEndpoint)
if err != nil {
logging.Log().Errorf("Was not able to get the openid metadata from endpoint %s. Err: %v", tirEndpoint, err)
}
err = common.GlobalCache.IssuersCache.Add(tirEndpoint, metaData, cache.NoExpiration)
if err != nil {
logging.Log().Errorf("failed caching issuer metadata in FillMetadataCache(): %v", err)
}
}
}

func (nac NoAuthHttpClient) Get(tirAddress string, tirPath string) (resp *http.Response, err error) {
urlString := buildUrlString(tirAddress, tirPath)
urlString := common.BuildUrlString(tirAddress, tirPath)
return nac.httpClient.Get(urlString)
}

func (ac AuthorizingHttpClient) Get(tirAddress string, tirPath string) (resp *http.Response, err error) {
urlString := buildUrlString(tirAddress, tirPath)
urlString := common.BuildUrlString(tirAddress, tirPath)
resp, err = ac.httpClient.Get(urlString)
if err != nil {
logging.Log().Infof("Was not able to get a response. Err: %v", err)
Expand All @@ -67,22 +91,6 @@ func (ac AuthorizingHttpClient) Get(tirAddress string, tirPath string) (resp *ht
return ac.httpClient.Do(authenticatedRequest)
}

func buildUrlString(address string, path string) string {
if strings.HasSuffix(address, "/") {
if strings.HasPrefix(path, "/") {
return address + strings.TrimPrefix(path, "/")
} else {
return address + path
}
} else {
if strings.HasPrefix(path, "/") {
return address + path
} else {
return address + "/" + path
}
}
}

func (ac AuthorizingHttpClient) handleAuthorization(tirAddress string) (bearerToken string, err error) {
logging.Log().Debugf("Handle authorization for %s", tirAddress)
vc, err := ac.tokenProvider.GetAuthCredential()
Expand All @@ -97,11 +105,13 @@ func (ac AuthorizingHttpClient) handleAuthorization(tirAddress string) (bearerTo
return bearerToken, err
}

metaData, err := ac.getMetaData(tirAddress)
if err != nil {
logging.Log().Warnf("Was not able to get the openid metadata. Err: %v", err)
return bearerToken, err
metaDataInterface, hit := common.GlobalCache.IssuersCache.Get(tirAddress)
if !hit {
logging.Log().Warnf("Was not able to get the openid metadata from address %s. Err: %v", tirAddress, err)
return bearerToken, ErrorCachedOpenidMetadataNotFound
}

metaData := metaDataInterface.(common.OpenIDProviderMetadata)
if !slices.Contains(metaData.GrantTypesSupported, common.TYPE_VP_TOKEN) {
logging.Log().Warnf("The server does not support grant type vp_token. Config: %v", logging.PrettyPrintObject(metaData))
return bearerToken, ErrorGrantTypeNotSupported
Expand All @@ -121,7 +131,7 @@ func (ac AuthorizingHttpClient) handleAuthorization(tirAddress string) (bearerTo

func (ac AuthorizingHttpClient) getMetaData(tokenHost string) (metadata common.OpenIDProviderMetadata, err error) {
logging.Log().Debugf("Retrieve openid-metadata from %s", tokenHost)
resp, err := ac.httpClient.Get(buildUrlString(tokenHost, "/.well-known/openid-configuration"))
resp, err := ac.httpClient.Get(common.BuildUrlString(tokenHost, WELL_KNOWN_ENDPOINT))
if err != nil {
logging.Log().Warnf("Was not able to get openid metadata from %s. Err: %v", tokenHost, err)
return metadata, err
Expand Down
26 changes: 24 additions & 2 deletions tir/tirClient.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ package tir
import (
"encoding/json"
"errors"
"github.com/procyon-projects/chrono"
"net/http"
"time"

"github.com/bxcodec/httpcache"
"github.com/fiware/VCVerifier/common"
"github.com/fiware/VCVerifier/config"
"github.com/fiware/VCVerifier/logging"
"github.com/patrickmn/go-cache"
)

const ISSUERS_V4_PATH = "v4/issuers"
Expand Down Expand Up @@ -84,8 +87,16 @@ func NewTirHttpClient(tokenProvider TokenProvider, config config.M2M) (client Ti
}
var httpGetClient HttpGetClient
if config.AuthEnabled {
logging.Log().Debug("Authorization for the tursted-issuers-registry is enabled.")
httpGetClient = AuthorizingHttpClient{httpClient: httpClient, tokenProvider: tokenProvider, clientId: config.ClientId}
logging.Log().Debug("Authorization for the trusted-issuers-registry is enabled.")
authorizingHttpClient := AuthorizingHttpClient{httpClient: httpClient, tokenProvider: tokenProvider, clientId: config.ClientId}

_, err := chrono.NewDefaultTaskScheduler().ScheduleAtFixedRate(authorizingHttpClient.FillMetadataCache, time.Duration(30)*time.Second)
if err != nil {
logging.Log().Errorf("failed scheduling task: %v", err)
return nil, err
}

httpGetClient = authorizingHttpClient
} else {
httpGetClient = NoAuthHttpClient{httpClient: httpClient}
}
Expand Down Expand Up @@ -167,6 +178,12 @@ func (tc TirHttpClient) requestIssuer(tirEndpoint string, did string) (response

func (tc TirHttpClient) requestIssuerWithVersion(tirEndpoint string, didPath string) (response *http.Response, err error) {
logging.Log().Debugf("Get issuer %s/%s.", tirEndpoint, didPath)
cacheKey := common.BuildUrlString(tirEndpoint, didPath)
responseInterface, hit := common.GlobalCache.IssuerCache.Get(cacheKey)
if hit {
return responseInterface.(*http.Response), nil
}

resp, err := tc.client.Get(tirEndpoint, didPath)
if err != nil {
logging.Log().Warnf("Was not able to get the issuer %s from %s. Err: %v", didPath, tirEndpoint, err)
Expand All @@ -176,6 +193,11 @@ func (tc TirHttpClient) requestIssuerWithVersion(tirEndpoint string, didPath str
logging.Log().Warnf("Was not able to get any response for issuer %s from %s.", didPath, tirEndpoint)
return nil, ErrorTirNoResponse
}

err = common.GlobalCache.IssuerCache.Add(cacheKey, resp, cache.DefaultExpiration)
if err != nil {
logging.Log().Errorf("Was not able to cache the response for issuer %s from %s.", didPath, tirEndpoint)
}
return resp, err
}

Expand Down
3 changes: 3 additions & 0 deletions tir/tirClient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tir
import (
"errors"
"fmt"
"github.com/fiware/VCVerifier/common"
"io"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -58,6 +59,7 @@ func TestIsTrustedParticipant(t *testing.T) {
}

for _, tc := range tests {
common.ResetGlobalCache()
t.Run(tc.testName, func(t *testing.T) {
tirClient := TirHttpClient{mockClient{responses: tc.mockResponses, errors: tc.mockErrors}}
isTrusted := tirClient.IsTrustedParticipant(tc.testEndpoints, tc.testIssuer)
Expand Down Expand Up @@ -99,6 +101,7 @@ func TestGetTrustedIssuer(t *testing.T) {
}

for _, tc := range tests {
common.ResetGlobalCache()
t.Run(tc.testName, func(t *testing.T) {
tirClient := TirHttpClient{mockClient{responses: tc.mockResponses, errors: tc.mockErrors}}
exists, issuer, err := tirClient.GetTrustedIssuer(tc.testEndpoints, tc.testIssuer)
Expand Down
9 changes: 0 additions & 9 deletions verifier/cache.go

This file was deleted.

Loading

0 comments on commit b0f2321

Please sign in to comment.