Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pascal/mar 734 return information on missing configuration in feature #821

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -94,6 +94,7 @@ func RunServer(apiVersion string) 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 @@ -103,6 +104,7 @@ func RunServer(apiVersion string) 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 @@ -156,6 +158,9 @@ func RunServer(apiVersion string) 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 @@ -80,6 +80,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
Loading