Skip to content

Commit

Permalink
Subjects initial code
Browse files Browse the repository at this point in the history
  • Loading branch information
PacoNelos committed Dec 4, 2023
1 parent 1b01303 commit bab0a55
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 7 deletions.
6 changes: 6 additions & 0 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@ func SetupApi(core *Core) *fiber.App {
tokensGroup.Get(":tokenId", core.GetTokenById)
tokensGroup.Post("", core.CreateToken)

subjectsGroup := app.Group("/subjects")
subjectsGroup.Use(authGuard(core))
subjectsGroup.Get(":subjectId", core.GetSubject)
subjectsGroup.Post("", core.CreateSubject)
subjectsGroup.Delete(":subjectId", core.DeleteSubject)

app.Use(func(c *fiber.Ctx) error {
return c.SendStatus(404)
})
Expand Down
7 changes: 0 additions & 7 deletions api/principals.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,6 @@ import (
_vault "github.com/subrose/vault"
)

// type NewPrincipal struct {
// Username string `json:"username" validate:"required,min=1,max=32"`
// Password string `json:"password" validate:"required,min=4,max=32"` // This is to limit the size of the password hash.
// Description string `json:"description"`
// Policies []string `json:"policies"`
// }

type PrincipalResponse struct {
Id string `json:"id"`
Username string `json:"username" validate:"required,min=3,max=32"`
Expand Down
43 changes: 43 additions & 0 deletions api/subjects.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"net/http"

"github.com/gofiber/fiber/v2"
_vault "github.com/subrose/vault"
)

func (core *Core) CreateSubject(c *fiber.Ctx) error {
var subject *_vault.Subject
if err := core.ParseJsonBody(c.Body(), subject); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(err)
}

sessionPrincipal := GetSessionPrincipal(c)
err := core.vault.CreateSubject(c.Context(), sessionPrincipal, subject)
if err != nil {
return err
}
return c.Status(http.StatusCreated).JSON(subject)
}

func (core *Core) DeleteSubject(c *fiber.Ctx) error {
sid := c.Params("subjectId")
sessionPrincipal := GetSessionPrincipal(c)
err := core.vault.DeleteSubject(c.Context(), sessionPrincipal, sid)
if err != nil {
return err
}
return c.SendStatus(http.StatusNoContent)
}

func (core *Core) GetSubject(c *fiber.Ctx) error {
sid := c.Params("subjectId")
sessionPrincipal := GetSessionPrincipal(c)
subject, err := core.vault.GetSubject(c.Context(), sessionPrincipal, sid)

if err != nil {
return err
}
return c.Status(http.StatusOK).JSON(subject)
}
17 changes: 17 additions & 0 deletions simulator/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,20 @@ def detokenise(
)
check_expected_status(response, expected_statuses)
return response.json()

def create_subject(self, eid: str, expected_statuses: Optional[list[int]] = None):
response = requests.post(
f"{self.vault_url}/subjects",
auth=(self.username, self.password),
json={"eid": eid},
)
check_expected_status(response, expected_statuses)
return response.json()

def get_subject(self, sid: str, expected_statuses: Optional[list[int]] = None):
response = requests.get(
f"{self.vault_url}/subjects/{sid}",
auth=(self.username, self.password),
)
check_expected_status(response, expected_statuses)
return response.json()
31 changes: 31 additions & 0 deletions vault/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func (st *SqlStore) CreateSchemas() error {
"principal_policies": "CREATE TABLE IF NOT EXISTS principal_policies (principal_id TEXT, policy_id TEXT, UNIQUE(principal_id, policy_id))",
"tokens": "CREATE TABLE IF NOT EXISTS tokens (id TEXT PRIMARY KEY, value TEXT)",
"collection_metadata": "CREATE TABLE IF NOT EXISTS collection_metadata (id TEXT PRIMARY KEY, name TEXT UNIQUE, field_schema JSON)",
"subjects": "CREATE TABLE IF NOT EXISTS subjects (id TEXT PRIMARY KEY, sid TEXT UNIQUE, metadata JSON)",
}

for _, query := range tables {
Expand Down Expand Up @@ -93,6 +94,11 @@ func (st *SqlStore) CreateCollection(ctx context.Context, c *Collection) error {
for fieldName := range c.Fields {
queryBuilder.WriteString(", " + fieldName + " TEXT")
}
queryBuilder.WriteString(", sid TEXT")
queryBuilder.WriteString(", CONSTRAINT fk_sid FOREIGN KEY (sid) REFERENCES subjects(id)")

// TODO: Add if cascade subject deletes...
queryBuilder.WriteString("ON DELETE CASCADE")
queryBuilder.WriteString(")")
_, err = tx.ExecContext(ctx, queryBuilder.String())
if err != nil {
Expand Down Expand Up @@ -634,6 +640,9 @@ func (st SqlStore) Flush(ctx context.Context) error {
return err
}
for _, table := range tables {
if table == "subjects" {
continue
}
_, err = st.db.ExecContext(ctx, "DROP TABLE IF EXISTS "+table)
if err != nil {
return err
Expand All @@ -645,3 +654,25 @@ func (st SqlStore) Flush(ctx context.Context) error {
}
return nil
}

func (st SqlStore) CreateSubject(ctx context.Context, subject *Subject) error {
_, err := st.db.ExecContext(ctx, "INSERT INTO subjects (id, eid, metadata) VALUES ($1, $2, $3)", subject.Id, subject.Eid, subject.Metadata)
return err
}

func (st SqlStore) GetSubject(ctx context.Context, id string) (*Subject, error) {
var subject Subject
err := st.db.GetContext(ctx, &subject, "SELECT * FROM subjects WHERE id = $1", id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, &NotFoundError{"subject", id}
}
return nil, err
}
return &subject, nil
}

func (st SqlStore) DeleteSubject(ctx context.Context, id string) error {
_, err := st.db.ExecContext(ctx, "DELETE FROM subjects WHERE id = $1", id)
return err
}
65 changes: 65 additions & 0 deletions vault/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ type Collection struct {
Description string `json:"description"`
}

type Subject struct {
Id string `json:"id"`
Eid string `json:"eid" validate:"required"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
Metadata string `json:"metadata"`
}

type Record map[string]string // field name -> value

type Privatiser interface {
Expand Down Expand Up @@ -98,6 +106,7 @@ const (
PRINCIPALS_PPATH = "/principals"
RECORDS_PPATH = "/records"
POLICIES_PPATH = "/policies"
SUBJECTS_PPATH = "/subjects"
)

type VaultDB interface {
Expand All @@ -120,6 +129,9 @@ type VaultDB interface {
CreateToken(ctx context.Context, tokenId string, value string) error
DeleteToken(ctx context.Context, tokenId string) error
GetTokenValue(ctx context.Context, tokenId string) (string, error)
CreateSubject(ctx context.Context, subject *Subject) error
GetSubject(ctx context.Context, subjectId string) (*Subject, error)
DeleteSubject(ctx context.Context, subjectId string) error
Flush(ctx context.Context) error
}

Expand Down Expand Up @@ -669,3 +681,56 @@ func (vault *Vault) Validate(payload interface{}) error {
}
return ValidationErrors{errors}
}

func (vault *Vault) CreateSubject(ctx context.Context, actor Principal, subject *Subject) error {
request := Request{actor, PolicyActionWrite, SUBJECTS_PPATH}
allowed, err := vault.ValidateAction(ctx, request)
if err != nil {
return err
}
if !allowed {
return &ForbiddenError{request}
}
subject.Id = GenerateId("sub")
subject.CreatedAt = time.Now().Format(time.RFC3339)

if err := vault.Validate(subject); err != nil {
return err
}

err = vault.Db.CreateSubject(ctx, subject)
if err != nil {
return err
}
return nil
}

func (vault *Vault) GetSubject(ctx context.Context, actor Principal, subjectId string) (*Subject, error) {
request := Request{actor, PolicyActionRead, fmt.Sprintf("%s/%s", SUBJECTS_PPATH, subjectId)}
allowed, err := vault.ValidateAction(ctx, request)
if err != nil {
return nil, err
}
if !allowed {
return nil, &ForbiddenError{request}
}

return vault.Db.GetSubject(ctx, subjectId)
}

func (vault *Vault) DeleteSubject(ctx context.Context, actor Principal, subjectId string) error {
request := Request{actor, PolicyActionWrite, fmt.Sprintf("%s/%s", SUBJECTS_PPATH, subjectId)}
allowed, err := vault.ValidateAction(ctx, request)
if err != nil {
return err
}
if !allowed {
return &ForbiddenError{request}
}

err = vault.Db.DeleteSubject(ctx, subjectId)
if err != nil {
return err
}
return nil
}

0 comments on commit bab0a55

Please sign in to comment.