Skip to content

Commit

Permalink
return "missing config" on feature access + test mode handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Pascal-Delange committed Feb 4, 2025
1 parent 727ec60 commit ea1d052
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 10 deletions.
6 changes: 3 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ TRANSFER_CHECK_ENRICHMENT_BUCKET_URL="file://./tempFiles/transfercheck-bucket?cr

# Configure the connection details to your Metabase instance.
# - To retrieve the JWT signing key, go to your Metabase admin panel, in 'Settings', then 'Embedding', and click 'Manage' under 'Static embedding'
METABASE_SITE_URL="https://your_subdomain.metabaseapp.com"
METABASE_JWT_SIGNING_KEY="dummy"
METABASE_GLOBAL_DASHBOARD_ID=123
METABASE_SITE_URL=
METABASE_JWT_SIGNING_KEY=
METABASE_GLOBAL_DASHBOARD_ID=

# Set up connection details to Convoy to enable webhooks sending.
# You can get your project ID and API key from your project settings page in Convoy's dashboard, in the "Secrets" section.
Expand Down
5 changes: 5 additions & 0 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func RunServer() error {
loggingFormat string
sentryDsn string
transferCheckEnrichmentBucketUrl string
firebaseEmulatorHost string
}{
batchIngestionMaxSize: utils.GetEnv("BATCH_INGESTION_MAX_SIZE", 0),
caseManagerBucket: utils.GetEnv("CASE_MANAGER_BUCKET_URL", ""),
Expand All @@ -99,6 +100,7 @@ func RunServer() error {
loggingFormat: utils.GetEnv("LOGGING_FORMAT", "text"),
sentryDsn: utils.GetEnv("SENTRY_DSN", ""),
transferCheckEnrichmentBucketUrl: utils.GetEnv("TRANSFER_CHECK_ENRICHMENT_BUCKET_URL", ""), // required for transfercheck
firebaseEmulatorHost: utils.GetEnv("FIREBASE_AUTH_EMULATOR_HOST", ""),
}

logger := utils.NewLogger(serverConfig.loggingFormat)
Expand Down Expand Up @@ -151,6 +153,9 @@ func RunServer() error {
usecases.WithCaseManagerBucketUrl(serverConfig.caseManagerBucket),
usecases.WithLicense(license),
usecases.WithConvoyServer(convoyConfiguration.APIUrl),
usecases.WithMetabase(metabaseConfig.SiteUrl),
usecases.WithOpensanctions(openSanctionsConfig.IsSet()),
usecases.WithTestMode(serverConfig.firebaseEmulatorHost != ""),
)

////////////////////////////////////////////////////////////
Expand Down
4 changes: 4 additions & 0 deletions infra/opensanctions.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ func (os OpenSanctions) Host() string {
return OPEN_SANCTIONS_API_HOST
}

func (os OpenSanctions) IsSet() bool {
return os.IsSelfHosted() || os.Credentials() != ""
}

func (os OpenSanctions) AuthMethod() OpenSanctionsAuthMethod {
return os.authMethod
}
Expand Down
5 changes: 5 additions & 0 deletions models/feature_access.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const (
Restricted FeatureAccess = iota
Allowed
Test
MissingConfiguration
UnknownFeatureAccess
)

Expand All @@ -20,6 +21,8 @@ func (f FeatureAccess) String() string {
return "restricted"
case Test:
return "test"
case MissingConfiguration:
return "missing_configuration"
}
return "unknown"
}
Expand All @@ -33,6 +36,8 @@ func FeatureAccessFrom(s string) FeatureAccess {
return Restricted
case "test":
return Test
case "missing_configuration":
return MissingConfiguration
}
return UnknownFeatureAccess
}
Expand Down
54 changes: 53 additions & 1 deletion models/organization_feature_access.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,31 @@ type OrganizationFeatureAccess struct {
UpdatedAt time.Time
}

func (o OrganizationFeatureAccess) WithTestMode() OrganizationFeatureAccess {
if o.TestRun == Restricted {
o.TestRun = Test
}
if o.Workflows == Restricted {
o.Workflows = Test
}
if o.Webhooks == Restricted {
o.Webhooks = Test
}
if o.RuleSnoozes == Restricted {
o.RuleSnoozes = Test
}
if o.Roles == Restricted {
o.Roles = Test
}
if o.Analytics == Restricted {
o.Analytics = Test
}
if o.Sanctions == Restricted {
o.Sanctions = Test
}
return o
}

type DbStoredOrganizationFeatureAccess struct {
Id string
OrganizationId string
Expand All @@ -31,7 +56,17 @@ type UpdateOrganizationFeatureAccessInput struct {
Sanctions *FeatureAccess
}

func (f DbStoredOrganizationFeatureAccess) MergeWithLicenseEntitlement(l *LicenseEntitlements) OrganizationFeatureAccess {
type FeaturesConfiguration struct {
Webhooks bool
Sanctions bool
Analytics bool
}

func (f DbStoredOrganizationFeatureAccess) MergeWithLicenseEntitlement(
l LicenseEntitlements,
c FeaturesConfiguration,
hasTestMode bool,
) OrganizationFeatureAccess {
o := OrganizationFeatureAccess{
Id: f.Id,
OrganizationId: f.OrganizationId,
Expand All @@ -40,6 +75,7 @@ func (f DbStoredOrganizationFeatureAccess) MergeWithLicenseEntitlement(l *Licens
CreatedAt: f.CreatedAt,
UpdatedAt: f.UpdatedAt,
}

// First, set the feature accesses to "allowed" if the license allows it
if l.Analytics {
o.Analytics = Allowed
Expand All @@ -65,5 +101,21 @@ func (f DbStoredOrganizationFeatureAccess) MergeWithLicenseEntitlement(l *Licens
o.Sanctions = Restricted
}

// as an exception, if test mode is enambled (if the app is running with the firebase auth emulator), set all the features to "test"
if hasTestMode {
o = o.WithTestMode()
}

// remove the feature accesses that are not allowed by the configuration
if o.Analytics.IsAllowed() && !c.Analytics {
o.Analytics = MissingConfiguration
}
if o.Webhooks.IsAllowed() && !c.Webhooks {
o.Webhooks = MissingConfiguration
}
if o.Sanctions.IsAllowed() && !c.Sanctions {
o.Sanctions = MissingConfiguration
}

return o
}
163 changes: 163 additions & 0 deletions models/organization_feature_access_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package models

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestMergeWithLicenseEntitlement(t *testing.T) {
tests := []struct {
name string
dbFeatureAccess DbStoredOrganizationFeatureAccess
license LicenseEntitlements
config FeaturesConfiguration
testMode bool
expected OrganizationFeatureAccess
}{
{
name: "All features allowed by license and configuration",
dbFeatureAccess: DbStoredOrganizationFeatureAccess{
Id: "1",
OrganizationId: "org1",
TestRun: Allowed,
Sanctions: Allowed,
},
license: LicenseEntitlements{
Analytics: true,
Webhooks: true,
Workflows: true,
RuleSnoozes: true,
UserRoles: true,
TestRun: true,
Sanctions: true,
},
config: FeaturesConfiguration{
Webhooks: true,
Sanctions: true,
Analytics: true,
},
expected: OrganizationFeatureAccess{
Id: "1",
OrganizationId: "org1",
TestRun: Allowed,
Sanctions: Allowed,
Analytics: Allowed,
Webhooks: Allowed,
Workflows: Allowed,
RuleSnoozes: Allowed,
Roles: Allowed,
},
},
{
name: "Some features restricted by license",
dbFeatureAccess: DbStoredOrganizationFeatureAccess{
Id: "2",
OrganizationId: "org2",
TestRun: Allowed,
Sanctions: Allowed,
},
license: LicenseEntitlements{
Analytics: false,
Webhooks: true,
Workflows: true,
RuleSnoozes: true,
UserRoles: true,
TestRun: false,
Sanctions: false,
},
config: FeaturesConfiguration{
Webhooks: true,
Sanctions: true,
Analytics: true,
},
expected: OrganizationFeatureAccess{
Id: "2",
OrganizationId: "org2",
TestRun: Restricted,
Sanctions: Restricted,
Analytics: Restricted,
Webhooks: Allowed,
Workflows: Allowed,
RuleSnoozes: Allowed,
Roles: Allowed,
},
},
{
name: "Some features restricted by configuration",
dbFeatureAccess: DbStoredOrganizationFeatureAccess{
Id: "3",
OrganizationId: "org3",
TestRun: Allowed,
Sanctions: Allowed,
},
license: LicenseEntitlements{
Analytics: true,
Webhooks: true,
Workflows: true,
RuleSnoozes: true,
UserRoles: true,
TestRun: true,
Sanctions: true,
},
config: FeaturesConfiguration{
Webhooks: false,
Sanctions: false,
Analytics: false,
},
expected: OrganizationFeatureAccess{
Id: "3",
OrganizationId: "org3",
TestRun: Allowed,
Sanctions: MissingConfiguration,
Analytics: MissingConfiguration,
Webhooks: MissingConfiguration,
Workflows: Allowed,
RuleSnoozes: Allowed,
Roles: Allowed,
},
},
{
name: "Test mode enabled",
dbFeatureAccess: DbStoredOrganizationFeatureAccess{
Id: "4",
OrganizationId: "org4",
TestRun: Restricted,
Sanctions: Allowed,
},
license: LicenseEntitlements{
Analytics: false,
Webhooks: false,
Workflows: false,
RuleSnoozes: true,
UserRoles: true,
TestRun: false,
Sanctions: true,
},
config: FeaturesConfiguration{
Webhooks: false,
Sanctions: true,
Analytics: false,
},
testMode: true,
expected: OrganizationFeatureAccess{
Id: "4",
OrganizationId: "org4",
TestRun: Test,
Sanctions: Allowed,
Analytics: MissingConfiguration,
Webhooks: MissingConfiguration,
Workflows: Test,
RuleSnoozes: Allowed,
Roles: Allowed,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.dbFeatureAccess.MergeWithLicenseEntitlement(tt.license, tt.config, tt.testMode)
assert.Equal(t, tt.expected, result, tt.name)
})
}
}
24 changes: 18 additions & 6 deletions usecases/feature_access/feature_access_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,35 @@ type FeatureAccessReaderOrgRepository interface {
}

type FeatureAccessReader struct {
enforceSecurity security.EnforceSecurityOrganization
orgRepo FeatureAccessReaderOrgRepository
executorFactory executor_factory.ExecutorFactory
license models.LicenseValidation
enforceSecurity security.EnforceSecurityOrganization
orgRepo FeatureAccessReaderOrgRepository
executorFactory executor_factory.ExecutorFactory
license models.LicenseValidation
featuresConfiguration models.FeaturesConfiguration
hasTestMode bool
}

func NewFeatureAccessReader(
enforceSecurity security.EnforceSecurityOrganization,
orgRepo FeatureAccessReaderOrgRepository,
executorFactory executor_factory.ExecutorFactory,
license models.LicenseValidation,
hasConvoyServerSetup bool,
hasMetabaseSetup bool,
hasOpensanctionsSetup bool,
hasTestMode bool,
) FeatureAccessReader {
return FeatureAccessReader{
enforceSecurity: enforceSecurity,
orgRepo: orgRepo,
executorFactory: executorFactory,
license: license,
featuresConfiguration: models.FeaturesConfiguration{
Webhooks: hasConvoyServerSetup,
Sanctions: hasOpensanctionsSetup,
Analytics: hasMetabaseSetup,
},
hasTestMode: hasTestMode,
}
}

Expand All @@ -52,6 +64,6 @@ func (f FeatureAccessReader) GetOrganizationFeatureAccess(
return models.OrganizationFeatureAccess{}, err
}

return dbStoredFeatureAccess.MergeWithLicenseEntitlement(
&f.license.LicenseEntitlements), nil
return dbStoredFeatureAccess.MergeWithLicenseEntitlement(f.license.LicenseEntitlements,
f.featuresConfiguration, f.hasTestMode), nil
}
Loading

0 comments on commit ea1d052

Please sign in to comment.