From 8baac029f4c717ea149029baffc8a2622af3e13a Mon Sep 17 00:00:00 2001 From: Mykhailo Sizov Date: Mon, 8 Apr 2024 13:07:55 +0300 Subject: [PATCH] feat: check if profile is active on a profile reader level Signed-off-by: Mykhailo Sizov --- component/profile/reader/file/reader.go | 49 ++++++++++++++++-- component/profile/reader/file/version.go | 8 +-- pkg/restapi/resterr/error.go | 1 + pkg/restapi/v1/verifier/controller.go | 4 -- pkg/restapi/v1/verifier/controller_test.go | 18 ------- .../oidc4ci_service_initiate_issuance.go | 4 -- .../oidc4ci_service_initiate_issuance_test.go | 25 --------- .../oidc4ci_service_store_auth_code_test.go | 51 ------------------- 8 files changed, 50 insertions(+), 110 deletions(-) diff --git a/component/profile/reader/file/reader.go b/component/profile/reader/file/reader.go index da80421e9..195d48c14 100644 --- a/component/profile/reader/file/reader.go +++ b/component/profile/reader/file/reader.go @@ -28,6 +28,7 @@ import ( "github.com/trustbloc/vcs/internal/logfields" vcskms "github.com/trustbloc/vcs/pkg/kms" profileapi "github.com/trustbloc/vcs/pkg/profile" + "github.com/trustbloc/vcs/pkg/restapi/resterr" ) const ( @@ -62,7 +63,7 @@ type VerifierReader struct { verifiers map[string]*profileapi.Verifier } -type profile struct { +type profileData struct { IssuersData []*issuerProfile `json:"issuers"` VerifiersData []*verifierProfile `json:"verifiers"` } @@ -96,7 +97,7 @@ func NewIssuerReader(config *Config) (*IssuerReader, error) { return nil, err } - var p profile + var p profileData if err = json.Unmarshal(jsonBytes, &p); err != nil { return nil, err } @@ -142,7 +143,26 @@ func NewIssuerReader(config *Config) (*IssuerReader, error) { // GetProfile returns profile with given id. func (p *IssuerReader) GetProfile( profileID profileapi.ID, profileVersion profileapi.Version) (*profileapi.Issuer, error) { - return p.issuers[fmt.Sprintf("%s_%s", profileID, profileVersion)], nil + profile, ok := p.issuers[fmt.Sprintf("%s_%s", profileID, profileVersion)] + if !ok { + return nil, resterr.ErrProfileNotFound + } + + if !profile.Active { + return nil, resterr.ErrProfileInactive + } + + // Check latest version of given profileID if it is inactive. + latestProfileVersion, ok := p.issuers[fmt.Sprintf("%s_%s", profileID, latest)] + if !ok { + return nil, resterr.ErrProfileNotFound + } + + if !latestProfileVersion.Active { + return nil, resterr.ErrProfileInactive + } + + return profile, nil } // GetAllProfiles returns all profiles with given organization id. @@ -167,7 +187,7 @@ func NewVerifierReader(config *Config) (*VerifierReader, error) { return nil, err } - var p profile + var p profileData if err = json.Unmarshal(jsonBytes, &p); err != nil { return nil, err } @@ -225,7 +245,26 @@ func (p *VerifierReader) setTrustList( // GetProfile returns profile with given id. func (p *VerifierReader) GetProfile( profileID profileapi.ID, profileVersion profileapi.Version) (*profileapi.Verifier, error) { - return p.verifiers[fmt.Sprintf("%s_%s", profileID, profileVersion)], nil + profile, ok := p.verifiers[fmt.Sprintf("%s_%s", profileID, profileVersion)] + if !ok { + return nil, resterr.ErrProfileNotFound + } + + if !profile.Active { + return nil, resterr.ErrProfileInactive + } + + // Check latest version of given profileID if it is inactive. + latestProfileVersion, ok := p.verifiers[fmt.Sprintf("%s_%s", profileID, latest)] + if !ok { + return nil, resterr.ErrProfileNotFound + } + + if !latestProfileVersion.Active { + return nil, resterr.ErrProfileInactive + } + + return profile, nil } // GetAllProfiles returns all profiles with given organization id. diff --git a/component/profile/reader/file/version.go b/component/profile/reader/file/version.go index fded36fa2..ab597972f 100644 --- a/component/profile/reader/file/version.go +++ b/component/profile/reader/file/version.go @@ -13,6 +13,8 @@ import ( "github.com/hashicorp/go-version" ) +const latest = "latest" + type profileVersionKey string func getProfileVersionKey(profileID string, profileVersion *version.Version) profileVersionKey { @@ -31,10 +33,10 @@ func populateLatestTag[Profile any]( latestVersion := versions[len(versions)-1] latestMajorVersion := latestVersion.Segments()[0] // Set latest tag. - store[fmt.Sprintf("%s_latest", profileID)] = + store[fmt.Sprintf("%s_%s", profileID, latest)] = profileData[getProfileVersionKey(profileID, latestVersion)] // Set v.latest tag for the latest version. - store[fmt.Sprintf("%s_v%d.latest", profileID, latestMajorVersion)] = + store[fmt.Sprintf("%s_v%d.%s", profileID, latestMajorVersion, latest)] = profileData[getProfileVersionKey(profileID, latestVersion)] for i := versions.Len() - 1; i >= 0; i-- { @@ -45,7 +47,7 @@ func populateLatestTag[Profile any]( latestMajorVersion = currentMajorVersion // Set v.latest tag points to the most recent version of the current version number. - store[fmt.Sprintf("%s_v%d.latest", profileID, currentMajorVersion)] = + store[fmt.Sprintf("%s_v%d.%s", profileID, currentMajorVersion, latest)] = profileData[getProfileVersionKey(profileID, currentVersion)] } } diff --git a/pkg/restapi/resterr/error.go b/pkg/restapi/resterr/error.go index 2d645d75e..373b909d9 100644 --- a/pkg/restapi/resterr/error.go +++ b/pkg/restapi/resterr/error.go @@ -93,6 +93,7 @@ var ( ErrDataNotFound = NewCustomError(DataNotFound, errors.New("data not found")) ErrOpStateKeyDuplication = NewCustomError(OpStateKeyDuplication, errors.New("op state key duplication")) ErrProfileInactive = NewCustomError(ProfileInactive, errors.New("profile not active")) + ErrProfileNotFound = NewCustomError(ProfileNotFound, errors.New("profile doesn't exist")) ErrCredentialTemplateNotFound = NewCustomError(CredentialTemplateNotFound, errors.New("credential template not found")) //nolint:lll ErrCredentialTemplateNotConfigured = NewCustomError(CredentialTemplateNotConfigured, errors.New("credential template not configured")) //nolint:lll ErrCredentialTemplateIDRequired = NewCustomError(CredentialTemplateIDRequired, errors.New("credential template ID is required")) //nolint:lll diff --git a/pkg/restapi/v1/verifier/controller.go b/pkg/restapi/v1/verifier/controller.go index 12f718343..010c9cb7d 100644 --- a/pkg/restapi/v1/verifier/controller.go +++ b/pkg/restapi/v1/verifier/controller.go @@ -340,10 +340,6 @@ func (c *Controller) initiateOidcInteraction( data *InitiateOIDC4VPData, profile *profileapi.Verifier, ) (*InitiateOIDC4VPResponse, error) { - if !profile.Active { - return nil, resterr.ErrProfileInactive - } - if profile.OIDCConfig == nil { return nil, resterr.NewValidationError(resterr.ConditionNotMet, "profile.OIDCConfig", errors.New("OIDC not configured")) diff --git a/pkg/restapi/v1/verifier/controller_test.go b/pkg/restapi/v1/verifier/controller_test.go index 882ae8f45..5c8f9795c 100644 --- a/pkg/restapi/v1/verifier/controller_test.go +++ b/pkg/restapi/v1/verifier/controller_test.go @@ -2077,24 +2077,6 @@ func TestController_initiateOidcInteraction(t *testing.T) { "invalid-value[presentationDefinitionID]: presentation definition id= not found for profile with id=profile-id") }) - t.Run("Should be active", func(t *testing.T) { - controller := NewController(&Config{ - ProfileSvc: mockProfileSvc, - KMSRegistry: kmsRegistry, - OIDCVPService: oidc4VPSvc, - }) - - _, err := controller.initiateOidcInteraction(context.TODO(), &InitiateOIDC4VPData{}, - &profileapi.Verifier{ - OrganizationID: tenantID, - Active: false, - OIDCConfig: &profileapi.OIDC4VPConfig{}, - SigningDID: &profileapi.SigningDID{}, - }) - - requireCustomError(t, resterr.ProfileInactive, err) - }) - t.Run("Error - With Presentation Definition and PD filters", func(t *testing.T) { controller := NewController(&Config{ ProfileSvc: mockProfileSvc, diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go index 8ac9a7d2d..455a2babc 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance.go @@ -44,10 +44,6 @@ func (s *Service) InitiateIssuance( // nolint:funlen,gocyclo,gocognit req.OpState = uuid.NewString() } - if !profile.Active { - return nil, resterr.ErrProfileInactive - } - if profile.VCConfig == nil { return nil, resterr.ErrVCOptionsNotConfigured } diff --git a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go index a082a6bea..07d14ab3d 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go +++ b/pkg/service/oidc4ci/oidc4ci_service_initiate_issuance_test.go @@ -1200,31 +1200,6 @@ func TestService_InitiateIssuance(t *testing.T) { require.Nil(t, resp) }, }, - { - name: "Profile is not active", - setup: func(mocks *mocks) { - issuanceReq = &oidc4ci.InitiateIssuanceRequest{ - ClientInitiateIssuanceURL: "https://wallet.example.com/initiate_issuance", - OpState: "eyJhbGciOiJSU0Et", - CredentialConfiguration: []oidc4ci.InitiateIssuanceCredentialConfiguration{ - { - ClaimEndpoint: "https://vcs.pb.example.com/claim", - CredentialTemplateID: "templateID", - }, - }, - } - - profile = &profileapi.Issuer{ - Active: false, - OIDCConfig: &profileapi.OIDCConfig{}, - VCConfig: &profileapi.VCConfig{}, - } - }, - check: func(t *testing.T, resp *oidc4ci.InitiateIssuanceResponse, err error) { - require.Nil(t, resp) - require.ErrorIs(t, err, resterr.ErrProfileInactive) - }, - }, { name: "VC options not configured", setup: func(mocks *mocks) { diff --git a/pkg/service/oidc4ci/oidc4ci_service_store_auth_code_test.go b/pkg/service/oidc4ci/oidc4ci_service_store_auth_code_test.go index 805de4d7b..15038a199 100644 --- a/pkg/service/oidc4ci/oidc4ci_service_store_auth_code_test.go +++ b/pkg/service/oidc4ci/oidc4ci_service_store_auth_code_test.go @@ -166,57 +166,6 @@ func TestInitiateWalletFlowFromStoreCode(t *testing.T) { assert.ErrorContains(t, err, "issuer not found") }) - t.Run("error init", func(t *testing.T) { - store := NewMockTransactionStore(gomock.NewController(t)) - eventMock := NewMockEventService(gomock.NewController(t)) - profileSvc := NewMockProfileService(gomock.NewController(t)) - wellKnown := NewMockWellKnownService(gomock.NewController(t)) - - srv, err := oidc4ci.NewService(&oidc4ci.Config{ - TransactionStore: store, - EventService: eventMock, - EventTopic: spi.IssuerEventTopic, - ProfileService: profileSvc, - WellKnownService: wellKnown, - }) - assert.NoError(t, err) - - profileSvc.EXPECT().GetProfile(profileapi.ID("bank_issuer1"), "v111.0"). - Return(&profileapi.Issuer{ - CredentialTemplates: []*profileapi.CredentialTemplate{ - { - ID: "some-template", - }, - }, - Active: false, - VCConfig: &profileapi.VCConfig{}, - SigningDID: &profileapi.SigningDID{}, - OIDCConfig: &profileapi.OIDCConfig{ - WalletInitiatedAuthFlowSupported: true, - IssuerWellKnownURL: "https://awesome.local", - ClaimsEndpoint: "https://awesome.claims.local", - GrantTypesSupported: []string{ - "authorization_code", - }, - ScopesSupported: []string{ - "scope1", - "scope2", - "scope3", - }, - }, - }, nil) - - resp, err := srv.StoreAuthorizationCode(context.TODO(), "random-op-state", "code123", - &common.WalletInitiatedFlowData{ - OpState: "random-op-state", - ProfileId: "bank_issuer1", - ProfileVersion: "v111.0", - }, - ) - assert.Empty(t, resp) - assert.ErrorContains(t, err, - "can not initiate issuance for wallet-initiated flow: profile-inactive") - }) t.Run("success", func(t *testing.T) { store := NewMockTransactionStore(gomock.NewController(t)) eventMock := NewMockEventService(gomock.NewController(t))