feat: extend problem fields

This commit is contained in:
Vyacheslav1557 2024-10-17 00:34:43 +05:00
parent 3ed195bb58
commit 6dc8f05675
14 changed files with 70 additions and 362 deletions

View file

@ -1,44 +0,0 @@
package problem.rbac
import rego.v1
spectator := 0
participant := 1
moderator := 2
admin := 3
permissions := {
"read": is_spectator,
"participate": is_participant,
"update": is_moderator,
"create": is_moderator,
"delete": is_moderator,
}
default allow := false
allow if is_admin
allow if {
permissions[input.action]
}
default is_admin := false
is_admin if {
input.user.role == admin
}
default is_moderator := false
is_moderator if {
input.user.role >= moderator
}
default is_participant := false
is_participant if {
input.user.role >= participant
}
default is_spectator := true
is_spectator if {
input.user.role >= spectator
}

View file

@ -1,39 +0,0 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"github.com/open-policy-agent/opa/rego"
)
type PermissionService struct {
query *rego.PreparedEvalQuery
}
func NewPermissionService() *PermissionService {
query, err := rego.New(
rego.Query("allow = data.problem.rbac.allow"),
rego.Load([]string{"./opa/problem.rego"}, nil),
).PrepareForEval(context.TODO())
if err != nil {
panic(err)
}
return &PermissionService{
query: &query,
}
}
func (s *PermissionService) Allowed(ctx context.Context, user *models.User, action string) bool {
input := map[string]interface{}{
"user": user,
"action": action,
}
result, err := s.query.Eval(ctx, rego.EvalInput(input))
if err != nil {
panic(err)
}
return result[0].Bindings["allow"].(bool)
}

View file

@ -1,8 +1,12 @@
package models package models
import "time"
type Participant struct { type Participant struct {
Id *int32 `db:"id"` Id *int32 `db:"id"`
UserId *int32 `db:"user_id"` UserId *int32 `db:"user_id"`
ContestId *int32 `db:"contest_id"` ContestId *int32 `db:"contest_id"`
Name *string `db:"name"` Name *string `db:"name"`
CreatedAt *time.Time `db:"created_at"`
UpdatedAt *time.Time `db:"updated_at"`
} }

View file

@ -3,13 +3,16 @@ package models
import "time" import "time"
type Problem struct { type Problem struct {
Id *int32 `db:"id"` Id *int32 `db:"id"`
Title *string `db:"title"` Title *string `db:"title"`
Content *string `db:"content"` Legend *string `db:"legend"`
TimeLimit *int32 `db:"time_limit"` InputFormat *string `db:"input_format"`
MemoryLimit *int32 `db:"memory_limit"` OutputFormat *string `db:"output_format"`
TestingStrategy *int32 `db:"testing_strategy"` Notes *string `db:"notes"`
TestingOrder *string `db:"testing_order"` Tutorial *string `db:"tutorial"`
CreatedAt *time.Time `db:"created_at"` LatexSummary *string `db:"latex_summary"`
UpdatedAt *time.Time `db:"updated_at"` TimeLimit *int32 `db:"time_limit"`
MemoryLimit *int32 `db:"memory_limit"`
CreatedAt *time.Time `db:"created_at"`
UpdatedAt *time.Time `db:"updated_at"`
} }

View file

@ -4,11 +4,12 @@ import "time"
type Solution struct { type Solution struct {
Id *int32 `db:"id"` Id *int32 `db:"id"`
ParticipantId *int32 `db:"participant_id"`
TaskId *int32 `db:"task_id"` TaskId *int32 `db:"task_id"`
LanguageId *int32 `db:"language_id"` ParticipantId *int32 `db:"participant_id"`
SolutionHash *string `db:"solution_hash"` State *int32 `db:"state"`
Result *int32 `db:"result"`
Score *int32 `db:"score"` Score *int32 `db:"score"`
Penalty *int32 `db:"penalty"`
TotalScore *int32 `db:"total_score"`
Language *int32 `db:"language"`
CreatedAt *time.Time `db:"created_at"` CreatedAt *time.Time `db:"created_at"`
} }

View file

@ -1,9 +1,12 @@
package models package models
import "time"
type Task struct { type Task struct {
Id *int32 `db:"id"` Id *int32 `db:"id"`
ContestId *int32 `db:"contest_id"` ProblemId *int32 `db:"problem_id"`
ProblemId *int32 `db:"problem_id"` ContestId *int32 `db:"contest_id"`
Position *int32 `db:"position"` Position *int32 `db:"position"`
PositionName *string `db:"position_name"` CreatedAt *time.Time `db:"created_at"`
UpdatedAt *time.Time `db:"updated_at"`
} }

View file

@ -1,9 +0,0 @@
package models
import "time"
type User struct {
UserId *int32 `json:"user_id" db:"user_id"`
Role *Role `json:"role" db:"role"`
UpdatedAt *time.Time `json:"updated_at" db:"updated_at"`
}

View file

@ -36,15 +36,18 @@ func (h *problemHandlers) ReadProblem(ctx context.Context, req *problemv1.ReadPr
} }
return &problemv1.ReadProblemResponse{ return &problemv1.ReadProblemResponse{
Problem: &problemv1.ReadProblemResponse_Problem{ Problem: &problemv1.ReadProblemResponse_Problem{
Id: *problem.Id, Id: *problem.Id,
Title: *problem.Title, Title: *problem.Title,
Content: *problem.Content, Legend: *problem.Legend,
TimeLimit: *problem.TimeLimit, InputFormat: *problem.InputFormat,
MemoryLimit: *problem.MemoryLimit, OutputFormat: *problem.OutputFormat,
TestingStrategy: *problem.TestingStrategy, Notes: *problem.Notes,
TestingOrder: *problem.TestingOrder, Tutorial: *problem.Tutorial,
CreatedAt: utils.TimestampP(problem.CreatedAt), LatexSummary: *problem.LatexSummary,
UpdatedAt: utils.TimestampP(problem.UpdatedAt), TimeLimit: *problem.TimeLimit,
MemoryLimit: *problem.MemoryLimit,
CreatedAt: utils.TimestampP(problem.CreatedAt),
UpdatedAt: utils.TimestampP(problem.UpdatedAt),
}, },
}, nil }, nil
} }

View file

@ -1,67 +0,0 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"github.com/open-policy-agent/opa/rego"
)
type PermissionService struct {
query *rego.PreparedEvalQuery
}
func NewPermissionService() *PermissionService {
query, err := rego.New(
rego.Query("allow = data.problem.rbac.allow"),
rego.Load([]string{"./opa/problem.rego"}, nil),
).PrepareForEval(context.TODO())
if err != nil {
panic(err)
}
return &PermissionService{
query: &query,
}
}
func (s *PermissionService) Allowed(ctx context.Context, user *models.User, action string) bool {
input := map[string]interface{}{
"user": user,
"action": action,
}
result, err := s.query.Eval(ctx, rego.EvalInput(input))
if err != nil {
panic(err)
}
return result[0].Bindings["allow"].(bool)
}
//func (service *ProblemUseCase) CanCreateProblem(ctx context.Context) error {
// if !service.permissionService.Allowed(ctx, extractUser(ctx), "create") {
// return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
// }
// return nil
//}
//
//func (service *ProblemUseCase) CanReadProblemById(ctx context.Context) error {
// if !service.permissionService.Allowed(ctx, extractUser(ctx), "read") {
// return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
// }
// return nil
//}
//
//func (service *ProblemUseCase) CanUpdateProblem(ctx context.Context) error {
// if !service.permissionService.Allowed(ctx, extractUser(ctx), "update") {
// return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
// }
// return nil
//}
//
//func (service *ProblemUseCase) CanDeleteProblem(ctx context.Context) error {
// if !service.permissionService.Allowed(ctx, extractUser(ctx), "delete") {
// return utils.ServiceError(nil, utils.ErrNoPermission, "permission denied")
// }
// return nil
//}

View file

@ -1,44 +0,0 @@
package problem.rbac
import rego.v1
spectator := 0
participant := 1
moderator := 2
admin := 3
permissions := {
"read": is_spectator,
"participate": is_participant,
"update": is_moderator,
"create": is_moderator,
"delete": is_moderator,
}
default allow := false
allow if is_admin
allow if {
permissions[input.action]
}
default is_admin := false
is_admin if {
input.user.role == admin
}
default is_moderator := false
is_moderator if {
input.user.role >= moderator
}
default is_participant := false
is_participant if {
input.user.role >= participant
}
default is_spectator := true
is_spectator if {
input.user.role >= spectator
}

View file

@ -1,44 +0,0 @@
package problem.rbac
import rego.v1
spectator := 0
participant := 1
moderator := 2
admin := 3
permissions := {
"read": is_spectator,
"participate": is_participant,
"update": is_moderator,
"create": is_moderator,
"delete": is_moderator,
}
default allow := false
allow if is_admin
allow if {
permissions[input.action]
}
default is_admin := false
is_admin if {
input.user.role == admin
}
default is_moderator := false
is_moderator if {
input.user.role >= moderator
}
default is_participant := false
is_participant if {
input.user.role >= participant
}
default is_spectator := true
is_spectator if {
input.user.role >= spectator
}

View file

@ -1,39 +0,0 @@
package usecase
import (
"context"
"git.sch9.ru/new_gate/ms-tester/internal/models"
"github.com/open-policy-agent/opa/rego"
)
type PermissionService struct {
query *rego.PreparedEvalQuery
}
func NewPermissionService() *PermissionService {
query, err := rego.New(
rego.Query("allow = data.problem.rbac.allow"),
rego.Load([]string{"./opa/problem.rego"}, nil),
).PrepareForEval(context.TODO())
if err != nil {
panic(err)
}
return &PermissionService{
query: &query,
}
}
func (s *PermissionService) Allowed(ctx context.Context, user *models.User, action string) bool {
input := map[string]interface{}{
"user": user,
"action": action,
}
result, err := s.query.Eval(ctx, rego.EvalInput(input))
if err != nil {
panic(err)
}
return result[0].Bindings["allow"].(bool)
}

View file

@ -2,20 +2,22 @@
-- +goose StatementBegin -- +goose StatementBegin
CREATE TABLE IF NOT EXISTS problems CREATE TABLE IF NOT EXISTS problems
( (
id serial NOT NULL, id serial NOT NULL,
title varchar(255) NOT NULL, title varchar(64) NOT NULL,
content varchar(65536) NOT NULL DEFAULT '', legend varchar(10240) NOT NULL DEFAULT '',
time_limit integer NOT NULL DEFAULT 1000, input_format varchar(10240) NOT NULL DEFAULT '',
memory_limit integer NOT NULL DEFAULT 65536, output_format varchar(10240) NOT NULL DEFAULT '',
testing_strategy integer NOT NULL DEFAULT 1, notes varchar(10240) NOT NULL DEFAULT '',
testing_order varchar(1024) NOT NULL DEFAULT '', tutorial varchar(10240) NOT NULL DEFAULT '',
created_at timestamptz NOT NULL DEFAULT now(), latex_summary varchar(10240) NOT NULL DEFAULT '',
updated_at timestamptz NOT NULL DEFAULT now(), time_limit integer NOT NULL DEFAULT 1000,
memory_limit integer NOT NULL DEFAULT 65536,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (id), PRIMARY KEY (id),
CHECK (length(title) != 0), CHECK (length(title) != 0),
CHECK (memory_limit > 0), CHECK (memory_limit > 0),
CHECK (time_limit > 0), CHECK (time_limit > 0)
CHECK (testing_strategy > 0)
); );
CREATE TRIGGER on_problems_update CREATE TRIGGER on_problems_update
@ -26,10 +28,10 @@ EXECUTE PROCEDURE updated_at_update();
CREATE TABLE IF NOT EXISTS contests CREATE TABLE IF NOT EXISTS contests
( (
id serial NOT NULL, id serial NOT NULL,
title varchar(255) NOT NULL, title varchar(64) NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(), created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (id), PRIMARY KEY (id),
CHECK (length(title) != 0) CHECK (length(title) != 0)
); );
@ -46,11 +48,11 @@ CREATE TABLE IF NOT EXISTS tasks
problem_id integer NOT NULL REFERENCES problems (id), problem_id integer NOT NULL REFERENCES problems (id),
contest_id integer NOT NULL REFERENCES contests (id), contest_id integer NOT NULL REFERENCES contests (id),
position integer NOT NULL, position integer NOT NULL,
prefix varchar(10) NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(), created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (id), PRIMARY KEY (id),
UNIQUE (problem_id, contest_id), UNIQUE (problem_id, contest_id),
UNIQUE (contest_id, position),
CHECK (position >= 0) CHECK (position >= 0)
); );
@ -62,12 +64,12 @@ EXECUTE PROCEDURE updated_at_update();
CREATE TABLE IF NOT EXISTS participants CREATE TABLE IF NOT EXISTS participants
( (
id serial NOT NULL, id serial NOT NULL,
user_id integer NOT NULL, user_id integer NOT NULL,
contest_id integer NOT NULL REFERENCES contests (id), contest_id integer NOT NULL REFERENCES contests (id),
name varchar(255) NOT NULL, name varchar(64) NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(), created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (id), PRIMARY KEY (id),
UNIQUE (user_id, contest_id), UNIQUE (user_id, contest_id),
CHECK (length(name) != 0) CHECK (length(name) != 0)
@ -86,7 +88,6 @@ CREATE TABLE IF NOT EXISTS solutions
participant_id integer NOT NULL REFERENCES participants (id), participant_id integer NOT NULL REFERENCES participants (id),
solution varchar(1048576) NOT NULL, solution varchar(1048576) NOT NULL,
state integer NOT NULL DEFAULT 1, state integer NOT NULL DEFAULT 1,
results varchar(1000) NOT NULL,
score integer NOT NULL, score integer NOT NULL,
penalty integer NOT NULL, penalty integer NOT NULL,
total_score integer NOT NULL, total_score integer NOT NULL,
@ -102,25 +103,6 @@ CREATE TRIGGER on_solutions_update
FOR EACH ROW FOR EACH ROW
EXECUTE PROCEDURE updated_at_update(); EXECUTE PROCEDURE updated_at_update();
CREATE TABLE IF NOT EXISTS best_solutions
(
id serial NOT NULL,
participant_id integer NOT NULL REFERENCES participants (id),
task_id integer NOT NULL REFERENCES tasks (id),
solution_id integer NOT NULL REFERENCES solutions (id),
best_total_score integer NOT NULL,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
PRIMARY KEY (id),
UNIQUE (participant_id, task_id)
);
CREATE TRIGGER on_best_solutions_update
BEFORE UPDATE
ON best_solutions
FOR EACH ROW
EXECUTE PROCEDURE updated_at_update();
CREATE FUNCTION updated_at_update() RETURNS TRIGGER CREATE FUNCTION updated_at_update() RETURNS TRIGGER
LANGUAGE plpgsql AS LANGUAGE plpgsql AS
$$ $$
@ -144,7 +126,5 @@ DROP TRIGGER IF EXISTS on_participants_update ON participants;
DROP TABLE IF EXISTS participants; DROP TABLE IF EXISTS participants;
DROP TRIGGER IF EXISTS on_solutions_update ON solutions; DROP TRIGGER IF EXISTS on_solutions_update ON solutions;
DROP TABLE IF EXISTS solutions; DROP TABLE IF EXISTS solutions;
DROP TRIGGER IF EXISTS on_best_solutions_update ON best_solutions;
DROP TABLE IF EXISTS best_solutions;
DROP FUNCTION updated_at_update(); DROP FUNCTION updated_at_update();
-- +goose StatementEnd -- +goose StatementEnd

2
proto

@ -1 +1 @@
Subproject commit e6de2af416b636ffc84d2bedccdc3c79592af1b9 Subproject commit c6824ea56aee42b5491fada04eb1f1b33162c1ea