diff --git a/pkg/datastore/datastore.go b/pkg/datastore/datastore.go index d199ed7..a58cb32 100644 --- a/pkg/datastore/datastore.go +++ b/pkg/datastore/datastore.go @@ -12,6 +12,8 @@ var ( type Datastore interface { Submissions() Submissions + Scripts() Scripts + ScriptPolicy() ScriptPolicy } type Submissions interface { @@ -23,3 +25,17 @@ type Submissions interface { Delete(ctx context.Context, id int64) error List(ctx context.Context, filters OptionalMap, page model.Page) ([]model.Submission, error) } + +type Scripts interface { + Get(ctx context.Context, id int64) (model.Script, error) + Create(ctx context.Context, smap model.Script) (model.Script, error) + Update(ctx context.Context, id int64, values OptionalMap) error + Delete(ctx context.Context, id int64) error +} + +type ScriptPolicy interface { + Get(ctx context.Context, id int64) (model.ScriptPolicy, error) + Create(ctx context.Context, smap model.ScriptPolicy) (model.ScriptPolicy, error) + Update(ctx context.Context, id int64, values OptionalMap) error + Delete(ctx context.Context, id int64) error +} diff --git a/pkg/datastore/gormstore/db.go b/pkg/datastore/gormstore/db.go index 48aaa31..938fec0 100644 --- a/pkg/datastore/gormstore/db.go +++ b/pkg/datastore/gormstore/db.go @@ -30,7 +30,11 @@ func New(ctx *cli.Context) (datastore.Datastore, error) { } if ctx.Bool("migrate") { - if err := db.AutoMigrate(&model.Submission{}); err != nil { + if err := db.AutoMigrate( + &model.Submission{}, + &model.Script{}, + &model.ScriptPolicy{}, + ); err != nil { log.WithError(err).Errorln("database migration failed") return nil, err } diff --git a/pkg/datastore/gormstore/gormstore.go b/pkg/datastore/gormstore/gormstore.go index 3edc246..6b1f9d9 100644 --- a/pkg/datastore/gormstore/gormstore.go +++ b/pkg/datastore/gormstore/gormstore.go @@ -12,3 +12,11 @@ type Gormstore struct { func (g Gormstore) Submissions() datastore.Submissions { return &Submissions{db: g.db} } + +func (g Gormstore) Scripts() datastore.Scripts { + return &Scripts{db: g.db} +} + +func (g Gormstore) ScriptPolicy() datastore.ScriptPolicy { + return &ScriptPolicy{db: g.db} +} diff --git a/pkg/datastore/gormstore/script_policy.go b/pkg/datastore/gormstore/script_policy.go new file mode 100644 index 0000000..d2fa1ba --- /dev/null +++ b/pkg/datastore/gormstore/script_policy.go @@ -0,0 +1,54 @@ +package gormstore + +import ( + "context" + "errors" + + "git.itzana.me/strafesnet/maps-service/pkg/datastore" + "git.itzana.me/strafesnet/maps-service/pkg/model" + "gorm.io/gorm" +) + +type ScriptPolicy struct { + db *gorm.DB +} + +func (env *ScriptPolicy) Get(ctx context.Context, id int64) (model.ScriptPolicy, error) { + var mdl model.ScriptPolicy + if err := env.db.First(&mdl, id).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return mdl, datastore.ErrNotExist + } + } + return mdl, nil +} + +func (env *ScriptPolicy) Create(ctx context.Context, smap model.ScriptPolicy) (model.ScriptPolicy, error) { + if err := env.db.Create(&smap).Error; err != nil { + return smap, err + } + + return smap, nil +} + +func (env *ScriptPolicy) Update(ctx context.Context, id int64, values datastore.OptionalMap) error { + if err := env.db.Model(&model.ScriptPolicy{}).Where("id = ?", id).Updates(values.Map()).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return datastore.ErrNotExist + } + return err + } + + return nil +} + +func (env *ScriptPolicy) Delete(ctx context.Context, id int64) error { + if err := env.db.Delete(&model.ScriptPolicy{}, id).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return datastore.ErrNotExist + } + return err + } + + return nil +} diff --git a/pkg/datastore/gormstore/scripts.go b/pkg/datastore/gormstore/scripts.go new file mode 100644 index 0000000..135c3de --- /dev/null +++ b/pkg/datastore/gormstore/scripts.go @@ -0,0 +1,84 @@ +package gormstore + +import ( + "context" + "errors" + + "git.itzana.me/strafesnet/maps-service/pkg/datastore" + "git.itzana.me/strafesnet/maps-service/pkg/model" + "gorm.io/gorm" +) + +type Scripts struct { + db *gorm.DB +} + +func (env *Scripts) Get(ctx context.Context, id int64) (model.Script, error) { + var mdl model.Script + if err := env.db.First(&mdl, id).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return mdl, datastore.ErrNotExist + } + } + return mdl, nil +} + +func (env *Scripts) GetList(ctx context.Context, id []int64) ([]model.Script, error) { + var mapList []model.Script + if err := env.db.Find(&mapList, "id IN ?", id).Error; err != nil { + return mapList, err + } + + return mapList, nil +} + +func (env *Scripts) Create(ctx context.Context, smap model.Script) (model.Script, error) { + if err := env.db.Create(&smap).Error; err != nil { + return smap, err + } + + return smap, nil +} + +func (env *Scripts) Update(ctx context.Context, id int64, values datastore.OptionalMap) error { + if err := env.db.Model(&model.Script{}).Where("id = ?", id).Updates(values.Map()).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return datastore.ErrNotExist + } + return err + } + + return nil +} + +// the update can only occur if the status matches one of the provided values. +func (env *Scripts) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error { + if err := env.db.Model(&model.Script{}).Where("id = ?", id).Where("status IN ?",statuses).Updates(values.Map()).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return datastore.ErrNotExist + } + return err + } + + return nil +} + +func (env *Scripts) Delete(ctx context.Context, id int64) error { + if err := env.db.Delete(&model.Script{}, id).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return datastore.ErrNotExist + } + return err + } + + return nil +} + +func (env *Scripts) List(ctx context.Context, filters datastore.OptionalMap, page model.Page) ([]model.Script, error) { + var maps []model.Script + if err := env.db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&maps).Error; err != nil { + return nil, err + } + + return maps, nil +} diff --git a/pkg/model/policy.go b/pkg/model/policy.go new file mode 100644 index 0000000..026d441 --- /dev/null +++ b/pkg/model/policy.go @@ -0,0 +1,21 @@ +package model + +type Policy int32 + +const( + ScriptPolicyAllowed Policy=0 + ScriptPolicyBlocked Policy=1 + ScriptPolicyDelete Policy=2 + ScriptPolicyReplace Policy=3 +) + +type ScriptPolicy struct { + ID int64 + // Hash of the source code that leads to this policy. + // If this is a replacement mapping, the original source may not be pointed to by any policy. + // The original source should still exist in the scripts table, which can be located by the same hash. + Hash uint64 + // The ID of the replacement source (ScriptPolicyReplace) or verbatim source (other) + ScriptID int64 + Policy Policy +} diff --git a/pkg/model/script.go b/pkg/model/script.go new file mode 100644 index 0000000..6d1b83f --- /dev/null +++ b/pkg/model/script.go @@ -0,0 +1,8 @@ +package model + +type Script struct { + ID int64 + Hash uint64 + Source string + SubmissionID int64 // which submission did this script first appear in +}