Skip to content

Commit

Permalink
chore: migration changes
Browse files Browse the repository at this point in the history
  • Loading branch information
mistahj67 committed Aug 21, 2024
1 parent 2cd4332 commit 2586311
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 224 deletions.
1 change: 1 addition & 0 deletions cmd/api/src/auth/permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func (s PermissionSet) All() model.Permissions {
}
}

// Permissions Note: Not the only source of truth, changes here must be added to a migration *.sql file to update the permissions table
func Permissions() PermissionSet {
return PermissionSet{
AppReadApplicationConfiguration: model.NewPermission("app", "ReadAppConfig"),
Expand Down
30 changes: 1 addition & 29 deletions cmd/api/src/auth/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
package auth

import (
"fmt"

"github.com/specterops/bloodhound/src/model"
)

Expand All @@ -36,33 +34,7 @@ type RoleTemplate struct {
Permissions model.Permissions
}

func (s RoleTemplate) Build(allPermissions model.Permissions) (model.Role, error) {
role := model.Role{
Name: s.Name,
Description: s.Description,
Permissions: make(model.Permissions, len(s.Permissions)),
}

for idx, requiredPermission := range s.Permissions {
found := false

for _, permission := range allPermissions {
if permission.Equals(requiredPermission) {
role.Permissions[idx] = permission
found = true

break
}
}

if !found {
return role, fmt.Errorf("unable to locate required permission %s for role template %s", requiredPermission, s.Name)
}
}

return role, nil
}

// Roles Note: Not the source of truth, changes here must be added to a migration *.sql file to update the roles & roles_permissions table
func Roles() map[string]RoleTemplate {
permissions := Permissions()

Expand Down
2 changes: 1 addition & 1 deletion cmd/api/src/database/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ func (s *BloodhoundDB) Wipe(ctx context.Context) error {

func (s *BloodhoundDB) Migrate(ctx context.Context) error {
// Run the migrator
if err := migration.NewMigrator(s.db.WithContext(ctx)).Migrate(); err != nil {
if err := migration.NewMigrator(s.db.WithContext(ctx)).ExecuteStepwiseMigrations(); err != nil {
log.Errorf("Error during SQL database migration phase: %v", err)
return err
}
Expand Down
29 changes: 0 additions & 29 deletions cmd/api/src/database/migration/agi_test.go

This file was deleted.

9 changes: 0 additions & 9 deletions cmd/api/src/database/migration/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package migration

import (
"embed"
"fmt"
"io/fs"

"github.com/specterops/bloodhound/src/version"
Expand Down Expand Up @@ -56,11 +55,3 @@ func NewMigrator(db *gorm.DB) *Migrator {
DB: db,
}
}

func (s *Migrator) Migrate() error {
if err := s.executeStepwiseMigrations(); err != nil {
return fmt.Errorf("failed to execute stepwise migrations: %w", err)
}

return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ func TestMigrator_Migrate(t *testing.T) {
manifest, err := migrator.GenerateManifest()
require.Nil(t, err)

assert.Nil(t, migrator.Migrate())
assert.Nil(t, migrator.ExecuteStepwiseMigrations())

lastVersionInManifest := manifest.VersionTable[len(manifest.VersionTable)-1]
latestMigration, err := migrator.LatestMigration()
Expand Down
94 changes: 33 additions & 61 deletions cmd/api/src/database/migration/migrations/schema.sql

Large diffs are not rendered by default.

63 changes: 63 additions & 0 deletions cmd/api/src/database/migration/migrations/v5.15.0.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
-- Copyright 2024 Specter Ops, Inc.
--
-- Licensed under the Apache License, Version 2.0
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://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.
--
-- SPDX-License-Identifier: Apache-2.0

-- Feature Flags
INSERT INTO feature_flags (created_at, updated_at, key, name, description, enabled, user_updatable) VALUES (current_timestamp, current_timestamp, 'adcs', 'Enable collection and processing of Active Directory Certificate Services Data', 'Enables the ability to collect, analyze, and explore Active Directory Certificate Services data and previews new attack paths.', false, true) ON CONFLICT DO NOTHING;
INSERT INTO feature_flags (created_at, updated_at, key, name, description, enabled, user_updatable) VALUES (current_timestamp, current_timestamp, 'clear_graph_data', 'Clear Graph Data', 'Enables the ability to delete all nodes and edges from the graph database.', true, false) ON CONFLICT DO NOTHING;
INSERT INTO feature_flags (created_at, updated_at, key, name, description, enabled, user_updatable) VALUES (current_timestamp, current_timestamp, 'risk_exposure_new_calculation', 'Use new tier zero risk exposure calculation', 'Enables the use of new tier zero risk exposure metatree metrics.', false, false) ON CONFLICT DO NOTHING;
INSERT INTO feature_flags (created_at, updated_at, key, name, description, enabled, user_updatable) VALUES (current_timestamp, current_timestamp, 'fedramp_eula', 'FedRAMP EULA', 'Enables showing the FedRAMP EULA on every login. (Enterprise only)', false, false) ON CONFLICT DO NOTHING;

-- Note - order matters permissions and roles ops must come before roles permissions ops
-- Permissions
INSERT INTO permissions (authority, name, created_at, updated_at) VALUES ('saved_queries', 'Read', current_timestamp, current_timestamp) ON CONFLICT DO NOTHING;
INSERT INTO permissions (authority, name, created_at, updated_at) VALUES ('saved_queries', 'Write', current_timestamp, current_timestamp) ON CONFLICT DO NOTHING;
INSERT INTO permissions (authority, name, created_at, updated_at) VALUES ('clients', 'Read', current_timestamp, current_timestamp) ON CONFLICT DO NOTHING;
INSERT INTO permissions (authority, name, created_at, updated_at) VALUES ('db', 'Wipe', current_timestamp, current_timestamp) ON CONFLICT DO NOTHING;
INSERT INTO permissions (authority, name, created_at, updated_at) VALUES ('graphdb', 'Mutate', current_timestamp, current_timestamp) ON CONFLICT DO NOTHING;

-- Roles
INSERT INTO roles (name, description, created_at, updated_at) VALUES ('Power User', 'Can upload data, manage clients, and perform any action a User can', current_timestamp, current_timestamp) ON CONFLICT DO NOTHING;

-- Roles Permissions
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Administrator'), (SELECT id FROM permissions WHERE permissions.authority = 'saved_queries' and permissions.name = 'Read')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Administrator'), (SELECT id FROM permissions WHERE permissions.authority = 'saved_queries' and permissions.name = 'Write')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Administrator'), (SELECT id FROM permissions WHERE permissions.authority = 'clients' and permissions.name = 'Read')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Administrator'), (SELECT id FROM permissions WHERE permissions.authority = 'db' and permissions.name = 'Wipe')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Administrator'), (SELECT id FROM permissions WHERE permissions.authority = 'graphdb' AND permissions.name = 'Mutate')) ON CONFLICT DO NOTHING;

INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'User'), (SELECT id FROM permissions WHERE permissions.authority = 'saved_queries' and permissions.name = 'Read')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'User'), (SELECT id FROM permissions WHERE permissions.authority = 'saved_queries' and permissions.name = 'Write')) ON CONFLICT DO NOTHING;
-- Swap user clients manage for clients read permission
DELETE FROM roles_permissions WHERE role_id = (SELECT id FROM roles WHERE roles.name = 'User') AND permission_id = (SELECT id FROM permissions WHERE permissions.authority = 'clients' and permissions.name = 'Manage');
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'User'), (SELECT id FROM permissions WHERE permissions.authority = 'clients' and permissions.name = 'Read')) ON CONFLICT DO NOTHING;

INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Read-Only'), (SELECT id FROM permissions WHERE permissions.authority = 'auth' and permissions.name = 'CreateToken')) ON CONFLICT DO NOTHING;

INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'app' and permissions.name = 'ReadAppConfig')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'app' and permissions.name = 'WriteAppConfig')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'risks' and permissions.name = 'GenerateReport')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'risks' and permissions.name = 'ManageRisks')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'auth' and permissions.name = 'CreateToken')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'auth' and permissions.name = 'ManageSelf')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'clients' and permissions.name = 'Manage')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'clients' and permissions.name = 'Read')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'clients' and permissions.name = 'Tasking')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'collection' and permissions.name = 'ManageJobs')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'graphdb' and permissions.name = 'Write')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'graphdb' and permissions.name = 'Read')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'saved_queries' and permissions.name = 'Read')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'saved_queries' and permissions.name = 'Write')) ON CONFLICT DO NOTHING;
INSERT INTO roles_permissions (role_id, permission_id) VALUES ((SELECT id FROM roles WHERE roles.name = 'Power User'), (SELECT id FROM permissions WHERE permissions.authority = 'graphdb' AND permissions.name = 'Mutate')) ON CONFLICT DO NOTHING;
2 changes: 1 addition & 1 deletion cmd/api/src/database/migration/migrations/v5.4.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ ADD COLUMN IF NOT EXISTS certtemplates BIGINT DEFAULT 0;
DELETE FROM
saved_queries
WHERE
user_id = '00000000-0000-0000-0000-000000000000'
user_id = '00000000-0000-0000-0000-000000000000';
4 changes: 2 additions & 2 deletions cmd/api/src/database/migration/stepwise.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (s *Migrator) RequiresMigration() (bool, error) {
}
}

// executeStepwiseMigrations will run all necessary migrations for a deployment.
// ExecuteStepwiseMigrations will run all necessary migrations for a deployment.
// It begins by checking if migration schema exists. If it does not, we assume the
// deployment is a new installation, otherwise we assume it may have migration updates.
//
Expand All @@ -156,7 +156,7 @@ func (s *Migrator) RequiresMigration() (bool, error) {
// and then build a manifest starting after the last successful version
//
// Once schema is verified and a manifest is created, we run ExecuteMigrations.
func (s *Migrator) executeStepwiseMigrations() error {
func (s *Migrator) ExecuteStepwiseMigrations() error {
var (
manifest Manifest
lastMigration model.Migration
Expand Down
92 changes: 1 addition & 91 deletions cmd/api/src/model/appcfg/flag.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/specterops/bloodhound/src/model"
)

// AvailableFlags has been removed and the db feature_flags table is the source of truth. Feature flag defaults should be added via migration *.sql files.
const (
FeatureButterflyAnalysis = "butterfly_analysis"
FeatureEnableSAMLSSO = "enable_saml_sso"
Expand All @@ -37,97 +38,6 @@ const (
FeatureDarkMode = "dark_mode"
)

// AvailableFlags returns a FeatureFlagSet of expected feature flags. Feature flag defaults introduced here will become the initial
// default value of the feature flag once it is inserted into the database.
func AvailableFlags() FeatureFlagSet {
return FeatureFlagSet{
FeatureButterflyAnalysis: {
Key: FeatureButterflyAnalysis,
Name: "Enhanced Asset Inbound-Outbound Exposure Analysis",
Description: "Enables more extensive analysis of attack path findings that allows BloodHound to help the user prioritize remediation of the most exposed assets.",
Enabled: false,
UserUpdatable: false,
},
FeaturePGMigrationDualIngest: {
Key: FeaturePGMigrationDualIngest,
Name: "PostgreSQL Migration Dual Ingest",
Description: "Enables dual ingest pathing for both Neo4j and PostgreSQL.",
Enabled: false,
UserUpdatable: false,
},
FeatureEnableSAMLSSO: {
Key: FeatureEnableSAMLSSO,
Name: "SAML Single Sign-On Support",
Description: "Enables SSO authentication flows and administration panels to third party SAML identity providers.",
Enabled: true,
UserUpdatable: false,
},
FeatureScopeCollectionByOU: {
Key: FeatureScopeCollectionByOU,
Name: "Enable SharpHound OU Scoped Collections",
Description: "Enables scoping SharpHound collections to specific lists of OUs.",
Enabled: true,
UserUpdatable: false,
},
FeatureAzureSupport: {
Key: FeatureAzureSupport,
Name: "Enable Azure Support",
Description: "Enables Azure support.",
Enabled: true,
UserUpdatable: false,
},
FeatureReconciliation: {
Key: FeatureReconciliation,
Name: "Reconciliation",
Description: "Enables Reconciliation",
Enabled: true,
UserUpdatable: false,
},
FeatureEntityPanelCaching: {
Key: FeatureEntityPanelCaching,
Name: "Enable application level caching",
Description: "Enables the use of application level caching for entity panel queries",
Enabled: true,
UserUpdatable: false,
},
FeatureAdcs: {
Key: FeatureAdcs,
Name: "Enable collection and processing of Active Directory Certificate Services Data",
Description: "Enables the ability to collect, analyze, and explore Active Directory Certificate Services data and previews new attack paths.",
Enabled: false,
UserUpdatable: false,
},
FeatureClearGraphData: {
Key: FeatureClearGraphData,
Name: "Clear Graph Data",
Description: "Enables the ability to delete all nodes and edges from the graph database.",
Enabled: true,
UserUpdatable: false,
},
FeatureRiskExposureNewCalculation: {
Key: FeatureRiskExposureNewCalculation,
Name: "Use new tier zero risk exposure calculation",
Description: "Enables the use of new tier zero risk exposure metatree metrics.",
Enabled: false,
UserUpdatable: false,
},
FeatureFedRAMPEULA: {
Key: FeatureFedRAMPEULA,
Name: "FedRAMP EULA",
Description: "Enables showing the FedRAMP EULA on every login. (Enterprise only)",
Enabled: false,
UserUpdatable: false,
},
FeatureDarkMode: {
Key: FeatureDarkMode,
Name: "Dark Mode",
Description: "Allows users to enable or disable dark mode via a toggle in the settings menu",
Enabled: false,
UserUpdatable: true,
},
}
}

// FeatureFlag defines the most basic details of what a feature flag must contain to be actionable. Feature flags should be
// self-descriptive as many use-cases will involve iterating over all available flags to display them back to the
// end-user.
Expand Down

0 comments on commit 2586311

Please sign in to comment.