diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6059f5e0..b6acbacd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -235,3 +235,28 @@ curl -H "Authorization: Bearer ${JWT_TOKEN_HERE}" http://localhost:5984/_session ```bash ng run fastenhealth:storybook ``` + + +# Access Encrypted SQLite Database with IntelliJ + +- Download the latest `sqlite-jdbc-crypt` jar from https://github.com/Willena/sqlite-jdbc-crypt/releases +- Open IntelliJ -> Data Source Properties -> Driver Tab +- Find & Select `Sqlite` -> Right Click -> Duplicate +- Rename to `Sqlite (Encrypted)` +- Find `Driver Files` -> Select `sqlite-jdbc-crypt` jar that you downloaded previously +- Remove `Xerial Sqlite JDBC` jar +- Click `Apply` -> Click `OK` +- Create New Data Source -> Select `Sqlite (Encrypted)` -> Change Connection Type to `Url only` +- Specify the following connection url: `jdbc:sqlite:fasten.db?cipher=sqlcipher&legacy=3&hmac_use=0&kdf_iter=4000&legacy_page_size=1024&key=123456789012345678901234567890` +- Replace `key` with the encryption key specified in your config file (`database.encryption_key`) +- Click `Test Connection` -> Should be successful +- Click `Apply` -> Click `OK` + +# Flush SQLite Write-Ahead-Log (WAL) to Database + +```sqlite +PRAGMA wal_checkpoint(TRUNCATE); +``` + +See: https://sqlite.org/forum/info/fefd56014e2135589ea57825b0e2aa3e2df5daf53b5e41aa6a9d8f0c29d0b8e5 +TODO: check if https://www.sqlite.org/pragma.html#pragma_wal_checkpoint can be used to do this automatically. diff --git a/backend/pkg/config/config.go b/backend/pkg/config/config.go index 76b5e514..802c1463 100644 --- a/backend/pkg/config/config.go +++ b/backend/pkg/config/config.go @@ -32,6 +32,7 @@ func (c *configuration) Init() error { c.SetDefault("web.src.frontend.path", "/opt/fasten/web") c.SetDefault("database.type", "sqlite") c.SetDefault("database.location", "/opt/fasten/db/fasten.db") + //c.SetDefault("database.encryption.key", "") //encryption key must be set by the user. c.SetDefault("cache.location", "/opt/fasten/cache/") c.SetDefault("jwt.issuer.key", "thisismysupersecuressessionsecretlength") @@ -80,6 +81,14 @@ func (c *configuration) ReadConfig(configFilePath string) error { // This function ensures that required configuration keys (that must be manually set) are present func (c *configuration) ValidateConfig() error { - + if c.IsSet("database.encryption.key") { + key := c.GetString("database.encryption.key") + if key == "" { + return errors.ConfigValidationError("database.encryption.key cannot be empty") + } + if len(key) < 10 { + return errors.ConfigValidationError("database.encryption.key must be at least 10 characters") + } + } return nil } diff --git a/backend/pkg/config/config_test.go b/backend/pkg/config/config_test.go new file mode 100644 index 00000000..53eb3ad9 --- /dev/null +++ b/backend/pkg/config/config_test.go @@ -0,0 +1,25 @@ +package config + +import ( + "github.com/fastenhealth/fasten-onprem/backend/pkg/errors" + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + "testing" +) + +func Test_ValidateConfig(t *testing.T) { + //setup + testConfig := configuration{ + Viper: viper.New(), + } + + //test & verify + testConfig.Set("database.encryption.key", "tooshort") + err := testConfig.ValidateConfig() + require.ErrorIs(t, err, errors.ConfigValidationError("database.encryption.key must be at least 10 characters")) + + testConfig.Set("database.encryption.key", "") + err = testConfig.ValidateConfig() + require.ErrorIs(t, err, errors.ConfigValidationError("database.encryption.key cannot be empty")) + +} diff --git a/backend/pkg/database/gorm_common.go b/backend/pkg/database/gorm_common.go index 3863da82..0c6d9ca1 100644 --- a/backend/pkg/database/gorm_common.go +++ b/backend/pkg/database/gorm_common.go @@ -5,7 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "net/url" "strings" "time" @@ -485,7 +484,7 @@ func (gr *GormRepository) AddResourceAssociation(ctx context.Context, source *mo "related_resource_source_resource_type": relatedResourceType, "related_resource_source_resource_id": relatedResourceId, }).Error - uniqueConstraintError := errors.New("constraint failed: UNIQUE constraint failed") + uniqueConstraintError := errors.New("UNIQUE constraint failed") if err != nil { if strings.HasPrefix(err.Error(), uniqueConstraintError.Error()) { gr.Logger.Warnf("Ignoring an error when creating a related_resource association for %s/%s: %v", resourceType, resourceId, err) @@ -1002,8 +1001,11 @@ func (gr *GormRepository) ListBackgroundJobs(ctx context.Context, queryOptions m var backgroundJobs []models.BackgroundJob query := gr.GormClient.WithContext(ctx). //Group("source_id"). //broken in Postgres. - Where(queryParam).Limit(queryOptions.Limit).Order("locked_time DESC") + Where(queryParam).Order("locked_time DESC") + if queryOptions.Limit > 0 { + query = query.Limit(queryOptions.Limit) + } if queryOptions.Offset > 0 { query = query.Offset(queryOptions.Offset) } @@ -1119,19 +1121,6 @@ func (gr *GormRepository) CancelAllLockedBackgroundJobsAndFail() error { // Utilities //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -func sqlitePragmaString(pragmas map[string]string) string { - q := url.Values{} - for key, val := range pragmas { - q.Add("_pragma", fmt.Sprintf("%s=%s", key, val)) - } - - queryStr := q.Encode() - if len(queryStr) > 0 { - return "?" + queryStr - } - return "" -} - // Internal function // This function will return a list of resources from all FHIR tables in the database // The query allows us to set the source id, source resource id, source resource type diff --git a/backend/pkg/database/gorm_repository_query_sql_test.go b/backend/pkg/database/gorm_repository_query_sql_test.go index 0913d410..c146be9c 100644 --- a/backend/pkg/database/gorm_repository_query_sql_test.go +++ b/backend/pkg/database/gorm_repository_query_sql_test.go @@ -44,6 +44,7 @@ func (suite *RepositorySqlTestSuite) BeforeTest(suiteName, testName string) { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -62,6 +63,8 @@ func (suite *RepositorySqlTestSuite) BeforeTest(suiteName, testName string) { func (suite *RepositorySqlTestSuite) AfterTest(suiteName, testName string) { suite.MockCtrl.Finish() os.Remove(suite.TestDatabase.Name()) + os.Remove(suite.TestDatabase.Name() + "-shm") + os.Remove(suite.TestDatabase.Name() + "-wal") } // In order for 'go test' to run this suite, we need to create diff --git a/backend/pkg/database/gorm_repository_query_test.go b/backend/pkg/database/gorm_repository_query_test.go index 1495606b..53cb50b2 100644 --- a/backend/pkg/database/gorm_repository_query_test.go +++ b/backend/pkg/database/gorm_repository_query_test.go @@ -261,6 +261,7 @@ func (suite *RepositoryTestSuite) TestQueryResources_SQL() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) diff --git a/backend/pkg/database/gorm_repository_test.go b/backend/pkg/database/gorm_repository_test.go index 53d0816d..deb177ba 100644 --- a/backend/pkg/database/gorm_repository_test.go +++ b/backend/pkg/database/gorm_repository_test.go @@ -61,7 +61,9 @@ func (suite *RepositoryTestSuite) BeforeTest(suiteName, testName string) { // AfterTest has a function to be executed right after the test finishes and receives the suite and test names as input func (suite *RepositoryTestSuite) AfterTest(suiteName, testName string) { suite.MockCtrl.Finish() - os.Remove(suite.TestDatabase.Name()) + //os.Remove(suite.TestDatabase.Name()) + //os.Remove(suite.TestDatabase.Name() + "-shm") + //os.Remove(suite.TestDatabase.Name() + "-wal") } // In order for 'go test' to run this suite, we need to create @@ -76,6 +78,7 @@ func (suite *RepositoryTestSuite) TestNewRepository() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() //test @@ -90,6 +93,7 @@ func (suite *RepositoryTestSuite) TestCreateUser() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -111,6 +115,7 @@ func (suite *RepositoryTestSuite) TestCreateUser_WithExitingUser_ShouldFail() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -140,6 +145,7 @@ func (suite *RepositoryTestSuite) TestCreateUser_WithUserProvidedId_ShouldBeRepl fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -166,6 +172,7 @@ func (suite *RepositoryTestSuite) TestGetUserByUsername() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -190,6 +197,7 @@ func (suite *RepositoryTestSuite) TestGetUserByUsername_WithInvalidUsername() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -213,6 +221,7 @@ func (suite *RepositoryTestSuite) TestGetCurrentUser_WithContextBackgroundAuthUs fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -238,6 +247,7 @@ func (suite *RepositoryTestSuite) TestGetCurrentUser_WithGinContextBackgroundAut fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -267,6 +277,7 @@ func (suite *RepositoryTestSuite) TestGetCurrentUser_WithContextBackgroundAuthUs fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -284,6 +295,7 @@ func (suite *RepositoryTestSuite) TestCreateGlossaryEntry() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -320,6 +332,7 @@ func (suite *RepositoryTestSuite) TestUpsertRawResource() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -377,6 +390,7 @@ func (suite *RepositoryTestSuite) TestUpsertRawResource_WithRelatedResourceAndDu fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -425,6 +439,7 @@ func (suite *RepositoryTestSuite) TestListResources() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -528,6 +543,7 @@ func (suite *RepositoryTestSuite) TestGetResourceByResourceTypeAndId() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -582,6 +598,7 @@ func (suite *RepositoryTestSuite) TestGetResourceBySourceId() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -636,6 +653,7 @@ func (suite *RepositoryTestSuite) TestGetPatientForSources() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -693,6 +711,7 @@ func (suite *RepositoryTestSuite) TestAddResourceAssociation() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -730,6 +749,7 @@ func (suite *RepositoryTestSuite) TestAddResourceAssociation_WithMismatchingSour fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -780,6 +800,7 @@ func (suite *RepositoryTestSuite) TestRemoveResourceAssociation() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -822,6 +843,7 @@ func (suite *RepositoryTestSuite) TestGetSourceSummary() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -903,6 +925,7 @@ func (suite *RepositoryTestSuite) TestGetSummary() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -1007,6 +1030,7 @@ func (suite *RepositoryTestSuite) TestAddResourceComposition() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -1093,6 +1117,7 @@ func (suite *RepositoryTestSuite) TestAddResourceComposition_WithExistingComposi fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -1254,6 +1279,7 @@ func (suite *RepositoryTestSuite) TestCreateBackgroundJob_Sync() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -1289,7 +1315,8 @@ func (suite *RepositoryTestSuite) TestListBackgroundJobs() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() - fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() + fakeConfig.EXPECT().GetString("log.level").Return("DEBUG").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) @@ -1300,7 +1327,7 @@ func (suite *RepositoryTestSuite) TestListBackgroundJobs() { } err = dbRepo.CreateUser(context.Background(), userModel) require.NoError(suite.T(), err) - authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, "test_username") + authContext := context.WithValue(context.Background(), pkg.ContextKeyTypeAuthUsername, userModel.Username) otherUserModel := &models.User{ Username: "test_other_username", @@ -1319,24 +1346,25 @@ func (suite *RepositoryTestSuite) TestListBackgroundJobs() { backgroundJob := models.NewSyncBackgroundJob(testSourceCredential) err = dbRepo.CreateBackgroundJob( - context.WithValue(authContext, pkg.ContextKeyTypeAuthUsername, "test_username"), + authContext, backgroundJob, ) + require.NoError(suite.T(), err) backgroundJob2 := models.NewSyncBackgroundJob(testSourceCredential) backgroundJob2.JobType = pkg.BackgroundJobTypeScheduledSync err = dbRepo.CreateBackgroundJob( - context.WithValue(authContext, pkg.ContextKeyTypeAuthUsername, "test_username"), + authContext, backgroundJob2, ) + require.NoError(suite.T(), err) backgroundJob3 := models.NewSyncBackgroundJob(testSourceCredential) backgroundJob3.JobStatus = pkg.BackgroundJobStatusFailed err = dbRepo.CreateBackgroundJob( - context.WithValue(authContext, pkg.ContextKeyTypeAuthUsername, "test_username"), + authContext, backgroundJob3, ) - require.NoError(suite.T(), err) //test @@ -1356,9 +1384,9 @@ func (suite *RepositoryTestSuite) TestListBackgroundJobs() { require.NoError(suite.T(), err) //assert - require.Equal(suite.T(), len(foundAllBackgroundJobs), 3) - require.Equal(suite.T(), len(foundBackgroundJobsByType), 2) - require.Equal(suite.T(), len(foundBackgroundJobsByStatus), 1) + require.Equal(suite.T(), 3, len(foundAllBackgroundJobs)) + require.Equal(suite.T(), 2, len(foundBackgroundJobsByType)) + require.Equal(suite.T(), 1, len(foundBackgroundJobsByStatus)) } func (suite *RepositoryTestSuite) TestUpdateBackgroundJob() { @@ -1366,6 +1394,7 @@ func (suite *RepositoryTestSuite) TestUpdateBackgroundJob() { fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() dbRepo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) require.NoError(suite.T(), err) diff --git a/backend/pkg/database/sqlite_repository.go b/backend/pkg/database/sqlite_repository.go index b71be1f7..54ef5aa9 100644 --- a/backend/pkg/database/sqlite_repository.go +++ b/backend/pkg/database/sqlite_repository.go @@ -2,16 +2,19 @@ package database import ( "fmt" - "strings" - "github.com/fastenhealth/fasten-onprem/backend/pkg/config" "github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus" "github.com/fastenhealth/fasten-onprem/backend/pkg/models" - "github.com/glebarez/sqlite" "github.com/sirupsen/logrus" + "net/url" + "strings" + + //"github.com/glebarez/sqlite" + "gorm.io/driver/sqlite" "gorm.io/gorm" ) +// uses github.com/mattn/go-sqlite3 driver (warning, uses CGO) func newSqliteRepository(appConfig config.Interface, globalLogger logrus.FieldLogger, eventBus event_bus.Interface) (DatabaseRepository, error) { //backgroundContext := context.Background() @@ -37,11 +40,30 @@ func newSqliteRepository(appConfig config.Interface, globalLogger logrus.FieldLo // In this case all writes are appended to a temporary file (write-ahead log) and this file is periodically merged with the original database. When SQLite is searching for something it would first check this temporary file and if nothing is found proceed with the main database file. // As a result, readers don’t compete with writers and performance is much better compared to the Old Way. // https://stackoverflow.com/questions/4060772/sqlite-concurrent-access - pragmaStr := sqlitePragmaString(map[string]string{ - "busy_timeout": "5000", - "foreign_keys": "ON", - "journal_mode": "wal", - }) + // + // NOTE: this schema is driver specific, and may not work with other drivers. + // eg.https://github.com/mattn/go-sqlite3 uses `?_journal_mode=WAL` prefixes + // https://github.com/glebarez/sqlite uses `?_pragma=journal_mode(WAL)` + // see https://github.com/mattn/go-sqlite3/compare/master...jgiannuzzi:go-sqlite3:sqlite3mc + // see https://github.com/mattn/go-sqlite3/pull/1109 + pragmaOpts := map[string]string{ + "_busy_timeout": "5000", + "_foreign_keys": "on", + "_journal_mode": "WAL", + } + + //validation of encryption key happens in ValidateConfig method + if appConfig.IsSet("database.encryption.key") { + pragmaOpts["_cipher"] = "sqlcipher" + pragmaOpts["_legacy"] = "3" + pragmaOpts["_hmac_use"] = "off" + pragmaOpts["_kdf_iter"] = "4000" + pragmaOpts["_legacy_page_size"] = "1024" + pragmaOpts["_key"] = appConfig.GetString("database.encryption.key") + } + + pragmaStr := sqlitePragmaString(pragmaOpts) + dsn := "file:" + appConfig.GetString("database.location") + pragmaStr database, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{ //TODO: figure out how to log database queries again. @@ -49,13 +71,16 @@ func newSqliteRepository(appConfig config.Interface, globalLogger logrus.FieldLo DisableForeignKeyConstraintWhenMigrating: true, }) + if err != nil { + if strings.Contains(err.Error(), "file is not a database") { + return nil, fmt.Errorf("failed to connect to database! encryption key may be incorrect - %w", err) + } + + return nil, fmt.Errorf("failed to connect to database! - %w", err) + } if strings.ToUpper(appConfig.GetString("log.level")) == "DEBUG" { database = database.Debug() //set debug globally } - - if err != nil { - return nil, fmt.Errorf("Failed to connect to database! - %v", err) - } globalLogger.Infof("Successfully connected to fasten sqlite db: %s\n", dsn) ////verify journal mode @@ -96,3 +121,17 @@ func newSqliteRepository(appConfig config.Interface, globalLogger logrus.FieldLo return &fastenRepo, nil } + +func sqlitePragmaString(pragmas map[string]string) string { + q := url.Values{} + for key, val := range pragmas { + //q.Add("_pragma", fmt.Sprintf("%s=%s", key, val)) + q.Add(key, val) + } + + queryStr := q.Encode() + if len(queryStr) > 0 { + return "?" + queryStr + } + return "" +} diff --git a/backend/pkg/database/sqlite_repository_test.go b/backend/pkg/database/sqlite_repository_test.go new file mode 100644 index 00000000..6648c585 --- /dev/null +++ b/backend/pkg/database/sqlite_repository_test.go @@ -0,0 +1,264 @@ +package database + +import ( + "fmt" + mock_config "github.com/fastenhealth/fasten-onprem/backend/pkg/config/mock" + "github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus" + "github.com/golang/mock/gomock" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "io/ioutil" + "log" + "os" + "testing" +) + +// Define the suite, and absorb the built-in basic suite +// functionality from testify - including a T() method which +// returns the current testing context +type SqliteRepositoryTestSuite struct { + suite.Suite + MockCtrl *gomock.Controller + TestDatabase *os.File +} + +// BeforeTest has a function to be executed right before the test starts and receives the suite and test names as input +func (suite *SqliteRepositoryTestSuite) BeforeTest(suiteName, testName string) { + suite.MockCtrl = gomock.NewController(suite.T()) + + dbFile, err := ioutil.TempFile("", fmt.Sprintf("%s.*.db", testName)) + if err != nil { + log.Fatal(err) + } + suite.TestDatabase = dbFile + +} + +// AfterTest has a function to be executed right after the test finishes and receives the suite and test names as input +func (suite *SqliteRepositoryTestSuite) AfterTest(suiteName, testName string) { + suite.MockCtrl.Finish() + os.Remove(suite.TestDatabase.Name()) + os.Remove(suite.TestDatabase.Name() + "-shm") + os.Remove(suite.TestDatabase.Name() + "-wal") +} + +// In order for 'go test' to run this suite, we need to create +// a normal test function and pass our suite to suite.Run +func TestSqliteRepositoryTestSuite(t *testing.T) { + suite.Run(t, new(SqliteRepositoryTestSuite)) + +} + +// Scenario 0: repository creation with default settings (no encryption) +func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_DefaultPragmaSettings() { + //setup + fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() + fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + repo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + require.NoError(suite.T(), err) + sqliteRepo, sqliteRepoOk := repo.(*GormRepository) + require.True(suite.T(), sqliteRepoOk) + + //test + var journalMode string + resp := sqliteRepo.GormClient.Raw("PRAGMA journal_mode;").Scan(&journalMode) + require.NoError(suite.T(), resp.Error) + + var busyTimeout int + resp = sqliteRepo.GormClient.Raw("PRAGMA busy_timeout;").Scan(&busyTimeout) + require.NoError(suite.T(), resp.Error) + + var foreignKeys bool + resp = sqliteRepo.GormClient.Raw("PRAGMA foreign_keys;").Scan(&foreignKeys) + require.NoError(suite.T(), resp.Error) + + //assert + require.Equal(suite.T(), "wal", journalMode) + require.Equal(suite.T(), 5000, busyTimeout) + require.Equal(suite.T(), true, foreignKeys) +} + +// Scenario 1: repository creation with encryption +func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey() { + //setup + fakeConfig := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes() + fakeConfig.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes() + fakeConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + repo, err := NewRepository(fakeConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + require.NoError(suite.T(), err) + sqliteRepo, sqliteRepoOk := repo.(*GormRepository) + require.True(suite.T(), sqliteRepoOk) + + //test + var cipher string + resp := sqliteRepo.GormClient.Raw("PRAGMA cipher;").Scan(&cipher) + require.NoError(suite.T(), resp.Error) + + var legacy int + resp = sqliteRepo.GormClient.Raw("PRAGMA legacy;").Scan(&legacy) + require.NoError(suite.T(), resp.Error) + + var hmacUse bool + resp = sqliteRepo.GormClient.Raw("PRAGMA hmac_use;").Scan(&hmacUse) + require.NoError(suite.T(), resp.Error) + + var kdfIter int + resp = sqliteRepo.GormClient.Raw("PRAGMA kdf_iter;").Scan(&kdfIter) + require.NoError(suite.T(), resp.Error) + + var legacyPageSize int + resp = sqliteRepo.GormClient.Raw("PRAGMA legacy_page_size;").Scan(&legacyPageSize) + require.NoError(suite.T(), resp.Error) + + //assert + require.Equal(suite.T(), "sqlcipher", cipher) + require.Equal(suite.T(), 3, legacy) + require.Equal(suite.T(), 1024, legacyPageSize) + require.Equal(suite.T(), false, hmacUse) + require.Equal(suite.T(), 4000, kdfIter) +} + +// Scenario 2: repository creation with encryption key, closing the app, and then reopening with a changed/incorrect key +func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey_WhenReopenWithIncorrectKeyShouldFail() { + //setup + fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes() + fakeConfig1.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes() + fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + //intialize the database with a key + _, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + require.NoError(suite.T(), err) + + fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes() + fakeConfig2.EXPECT().GetString("database.encryption.key").Return("incorrect_key_here").AnyTimes() + fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + //test + _, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + + //assert + require.Equal(suite.T(), "failed to connect to database! encryption key may be incorrect - file is not a database", err2.Error()) +} + +// Scenario 3: repository creation with encryption key, closing the app, and then reopening with the correct key +func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey_WhenReopenWithCorrectKeyShouldPass() { + //setup + fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes() + fakeConfig1.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes() + fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + //intialize the database with a key + _, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + require.NoError(suite.T(), err) + + fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes() + fakeConfig2.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes() + fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + //test + _, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + + //assert + require.NoError(suite.T(), err2) +} + +// Scenario 4: repository creation with encryption key, closing the app, and then reopening with deaults (no encryption) +func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithEncryptionKey_WhenReopenWithNoEncryptionKeyShouldFail() { + //setup + fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes() + fakeConfig1.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes() + fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + //intialize the database with a key + _, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + require.NoError(suite.T(), err) + + fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() + fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + //test + _, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + + //assert + require.Equal(suite.T(), "failed to connect to database! encryption key may be incorrect - file is not a database", err2.Error()) +} + +// Scenario 5: repository creation without encryption key (defaults), closing the app, and then reopening with an encryption key +func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithoutEncryption_WhenReopenWithEncryptionKeyShouldFail() { + //setup + fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() + fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + //intialize the database with a key + _, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + require.NoError(suite.T(), err) + + fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(true).AnyTimes() + fakeConfig2.EXPECT().GetString("database.encryption.key").Return("012345678901234567890").AnyTimes() + fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + //test + _, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + + //assert + require.Equal(suite.T(), "failed to connect to database! encryption key may be incorrect - file is not a database", err2.Error()) +} + +// Scenario 6: repository creation without encryption key (defaults), closing the app, and then reopening without an encryption key +func (suite *SqliteRepositoryTestSuite) TestNewSqliteRepository_WithoutEncryption_WhenReopenWithoutEncryptionKeyShouldPass() { + //setup + fakeConfig1 := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig1.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig1.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig1.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() + fakeConfig1.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + //intialize the database with a key + _, err := NewRepository(fakeConfig1, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + require.NoError(suite.T(), err) + + fakeConfig2 := mock_config.NewMockInterface(suite.MockCtrl) + fakeConfig2.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() + fakeConfig2.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + fakeConfig2.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() + fakeConfig2.EXPECT().GetString("log.level").Return("INFO").AnyTimes() + + //test + _, err2 := NewRepository(fakeConfig2, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) + + //assert + require.NoError(suite.T(), err2) +} diff --git a/backend/pkg/web/handler/resource_fhir_test.go b/backend/pkg/web/handler/resource_fhir_test.go index 9b319fb6..4e3f2982 100644 --- a/backend/pkg/web/handler/resource_fhir_test.go +++ b/backend/pkg/web/handler/resource_fhir_test.go @@ -33,35 +33,35 @@ type ResourcFhirHandlerTestSuite struct { AppConfig *mock_config.MockInterface AppRepository database.DatabaseRepository AppEventBus event_bus.Interface - SourceId uuid.UUID + SourceId uuid.UUID } - -func (suite *ResourcFhirHandlerTestSuite) BeforeTest(suiteName string, testName string){ +func (suite *ResourcFhirHandlerTestSuite) BeforeTest(suiteName string, testName string) { suite.MockCtrl = gomock.NewController(suite.T()) - + // ioutils is deprecated, used os.CreateTemp - dbFile, err := os.CreateTemp("",fmt.Sprintf("%s.*.db",suiteName)) - if err!=nil { + dbFile, err := os.CreateTemp("", fmt.Sprintf("%s.*.db", suiteName)) + if err != nil { log.Fatal(err) } suite.TestDatabase = dbFile - appConfig := mock_config.NewMockInterface(suite.MockCtrl) + appConfig := mock_config.NewMockInterface(suite.MockCtrl) appConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() appConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + appConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() appConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() suite.AppConfig = appConfig - appRepo, err := database.NewRepository(suite.AppConfig,logrus.WithField("test",suite.T().Name()),event_bus.NewNoopEventBusServer()) + appRepo, err := database.NewRepository(suite.AppConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer()) suite.AppRepository = appRepo suite.AppEventBus = event_bus.NewNoopEventBusServer() - appRepo.CreateUser(context.Background(),&models.User{ + appRepo.CreateUser(context.Background(), &models.User{ Username: "test_user", Password: "test", }) - + //Pre adding the source data w := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(w) @@ -76,8 +76,8 @@ func (suite *ResourcFhirHandlerTestSuite) BeforeTest(suiteName string, testName ctx.Request = req CreateManualSource(ctx) - //assert - require.Equal(suite.T(), http.StatusOK, w.Code) + //assert + require.Equal(suite.T(), http.StatusOK, w.Code) type ResponseWrapper struct { Success bool `json:"success"` Source models.SourceCredential `json:"source"` @@ -86,11 +86,10 @@ func (suite *ResourcFhirHandlerTestSuite) BeforeTest(suiteName string, testName var respWrapper ResponseWrapper err = json.Unmarshal(w.Body.Bytes(), &respWrapper) require.NoError(suite.T(), err) - + suite.SourceId = respWrapper.Source.ID } - // AfterTest has a function to be executed right after the test finishes and receives the suite and test names as input func (suite *ResourcFhirHandlerTestSuite) AfterTest(suiteName, testName string) { suite.MockCtrl.Finish() @@ -112,21 +111,21 @@ func (suite *ResourcFhirHandlerTestSuite) TestGetResourceFhirHandler() { ctx.AddParam("sourceId", suite.SourceId.String()) ctx.AddParam("resourceId", "57959813-8cd2-4e3c-8970-e4364b74980a") - + GetResourceFhir(ctx) type ResponseWrapper struct { - Data *models.ResourceBase `json:"data"` - Success bool `json:"success"` + Data *models.ResourceBase `json:"data"` + Success bool `json:"success"` } var respWrapper ResponseWrapper err := json.Unmarshal(w.Body.Bytes(), &respWrapper) - require.NoError(suite.T(),err) - require.Equal(suite.T(),true,respWrapper.Success) - require.Equal(suite.T(),"Patient",respWrapper.Data.SourceResourceType) - require.Equal(suite.T(),suite.SourceId, respWrapper.Data.SourceID) - require.Equal(suite.T(),"57959813-8cd2-4e3c-8970-e4364b74980a", respWrapper.Data.SourceResourceID) - + require.NoError(suite.T(), err) + require.Equal(suite.T(), true, respWrapper.Success) + require.Equal(suite.T(), "Patient", respWrapper.Data.SourceResourceType) + require.Equal(suite.T(), suite.SourceId, respWrapper.Data.SourceID) + require.Equal(suite.T(), "57959813-8cd2-4e3c-8970-e4364b74980a", respWrapper.Data.SourceResourceID) + } func (suite *ResourcFhirHandlerTestSuite) TestGetResourceFhirHandler_WithInvalidSourceResourceId() { @@ -140,18 +139,18 @@ func (suite *ResourcFhirHandlerTestSuite) TestGetResourceFhirHandler_WithInvalid ctx.AddParam("sourceId", "-1") ctx.AddParam("resourceId", "57959813-9999-4e3c-8970-e4364b74980a") - + GetResourceFhir(ctx) type ResponseWrapper struct { - Data *models.ResourceBase `json:"data"` - Success bool `json:"success"` + Data *models.ResourceBase `json:"data"` + Success bool `json:"success"` } var respWrapper ResponseWrapper err := json.Unmarshal(w.Body.Bytes(), &respWrapper) - require.NoError(suite.T(),err) - - require.Equal(suite.T(),false,respWrapper.Success) - require.Nil(suite.T(),respWrapper.Data) - -} \ No newline at end of file + require.NoError(suite.T(), err) + + require.Equal(suite.T(), false, respWrapper.Success) + require.Nil(suite.T(), respWrapper.Data) + +} diff --git a/backend/pkg/web/handler/source_test.go b/backend/pkg/web/handler/source_test.go index 1d114fbc..4b36b0a3 100644 --- a/backend/pkg/web/handler/source_test.go +++ b/backend/pkg/web/handler/source_test.go @@ -55,6 +55,7 @@ func (suite *SourceHandlerTestSuite) BeforeTest(suiteName, testName string) { appConfig := mock_config.NewMockInterface(suite.MockCtrl) appConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes() appConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes() + appConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes() appConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes() suite.AppConfig = appConfig diff --git a/go.mod b/go.mod index a47d0a63..fa2bd613 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,13 @@ module github.com/fastenhealth/fasten-onprem +go 1.18 + //replace github.com/fastenhealth/fasten-sources => ../fasten-sources //replace github.com/fastenhealth/gofhir-models => ../gofhir-models -go 1.18 +replace github.com/mattn/go-sqlite3 v1.14.17 => github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20230719111531-6e53453ccbd3 + +//replace gorm.io/driver/sqlite v1.5.4 => github.com/jgiannuzzi/gorm-sqlite v1.4.4-0.20221215225833-42389ad31305 require ( github.com/analogj/go-util v0.0.0-20210417161720-39b497cca03b @@ -13,7 +17,6 @@ require ( github.com/fastenhealth/fasten-sources v0.4.9 github.com/fastenhealth/gofhir-models v0.0.6 github.com/gin-gonic/gin v1.9.0 - github.com/glebarez/sqlite v1.5.0 github.com/go-gormigrate/gormigrate/v2 v2.1.1 github.com/golang-jwt/jwt/v4 v4.4.2 github.com/golang/mock v1.6.0 @@ -30,6 +33,7 @@ require ( golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 golang.org/x/net v0.14.0 gorm.io/datatypes v1.0.7 + gorm.io/driver/sqlite v1.5.4 gorm.io/gorm v1.25.4 ) @@ -37,6 +41,7 @@ require ( github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.4.3 // indirect + github.com/mattn/go-sqlite3 v1.14.17 // indirect ) require ( @@ -51,7 +56,6 @@ require ( github.com/fatih/color v1.13.0 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/glebarez/go-sqlite v1.19.1 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.13.0 // indirect @@ -83,7 +87,6 @@ require ( github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.7 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/seborama/govcr v4.5.0+incompatible // indirect github.com/segmentio/asm v1.2.0 // indirect @@ -107,8 +110,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/mysql v1.3.2 // indirect gorm.io/driver/postgres v1.5.3 - modernc.org/libc v1.19.0 // indirect - modernc.org/mathutil v1.5.0 // indirect - modernc.org/memory v1.4.0 // indirect - modernc.org/sqlite v1.19.1 // indirect ) diff --git a/go.sum b/go.sum index 1f4b0dea..1a82e893 100644 --- a/go.sum +++ b/go.sum @@ -95,7 +95,6 @@ github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3 h1:+3HCtB74++ClLy8GgjU github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -116,10 +115,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= -github.com/glebarez/go-sqlite v1.19.1 h1:o2XhjyR8CQ2m84+bVz10G0cabmG0tY4sIMiCbrcUTrY= -github.com/glebarez/go-sqlite v1.19.1/go.mod h1:9AykawGIyIcxoSfpYWiX1SgTNHTNsa/FVc75cDkbp4M= -github.com/glebarez/sqlite v1.5.0 h1:+8LAEpmywqresSoGlqjjT+I9m4PseIM3NcerIJ/V7mk= -github.com/glebarez/sqlite v1.5.0/go.mod h1:0wzXzTvfVJIN2GqRhCdMbnYd+m+aH5/QV7B30rM6NgY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -189,7 +184,6 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= @@ -272,6 +266,8 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20230719111531-6e53453ccbd3 h1:Iqr8ZWwijosAcawoD9IZ7cwj61WH50Rysm+RQ+ZIB4I= +github.com/jgiannuzzi/go-sqlite3 v1.14.17-0.20230719111531-6e53453ccbd3/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= @@ -281,7 +277,6 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= @@ -332,13 +327,10 @@ github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= -github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -360,8 +352,6 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -623,7 +613,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -699,7 +688,6 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -830,13 +818,13 @@ gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2 gorm.io/driver/postgres v1.3.4/go.mod h1:y0vEuInFKJtijuSGu9e5bs5hzzSzPK+LancpKpvbRBw= gorm.io/driver/postgres v1.5.3 h1:qKGY5CPHOuj47K/VxbCXJfFvIUeqMSXXadqdCY+MbBU= gorm.io/driver/postgres v1.5.3/go.mod h1:F+LtvlFhZT7UBiA81mC9W6Su3D4WUhSboc/36QZU0gk= -gorm.io/driver/sqlite v1.3.1 h1:bwfE+zTEWklBYoEodIOIBwuWHpnx52Z9zJFW5F33WLk= gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg= +gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0= +gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4= gorm.io/driver/sqlserver v1.3.1 h1:F5t6ScMzOgy1zukRTIZgLZwKahgt3q1woAILVolKpOI= gorm.io/driver/sqlserver v1.3.1/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ= gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= -gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw= gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -846,38 +834,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.37.0/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= -modernc.org/cc/v3 v3.38.1/go.mod h1:vtL+3mdHx/wcj3iEGz84rQa8vEqR6XM84v5Lcvfph20= -modernc.org/ccgo/v3 v3.0.0-20220904174949-82d86e1b6d56/go.mod h1:YSXjPL62P2AMSxBphRHPn7IkzhVHqkvOnRKAKh+W6ZI= -modernc.org/ccgo/v3 v3.0.0-20220910160915-348f15de615a/go.mod h1:8p47QxPkdugex9J4n9P2tLZ9bK01yngIVp00g4nomW0= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.4/go.mod h1:WNg2ZH56rDEwdropAJeZPQkXmDwh+JCA1s/htl6r2fA= -modernc.org/libc v1.18.0/go.mod h1:vj6zehR5bfc98ipowQOM2nIDUZnVew/wNC/2tOGS+q0= -modernc.org/libc v1.19.0 h1:bXyVhGQg6KIClTr8FMVIDPl7jtbcs7aS5WP7vLDaxPs= -modernc.org/libc v1.19.0/go.mod h1:ZRfIaEkgrYgZDl6pa4W39HgN5G/yDW+NRmNKZBDFrk0= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.3.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk= -modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.19.1 h1:8xmS5oLnZtAK//vnd4aTVj8VOeTAccEFOtUnIzfSw+4= -modernc.org/sqlite v1.19.1/go.mod h1:UfQ83woKMaPW/ZBruK0T7YaFCrI+IE0LeWVY6pmnVms= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.14.0/go.mod h1:gQ7c1YPMvryCHCcmf8acB6VPabE59QBeuRQLL7cTUlM= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.6.0/go.mod h1:hVdgNMh8ggTuRG1rGU8x+xGRFfiQUIAw0ZqlPy8+HyQ= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=