Skip to content

Commit

Permalink
feat: collect subjects about in toto statements that contain other pr…
Browse files Browse the repository at this point in the history
…edicates (in-toto#49)

To date Archivista has been limited to archive DSSE envelopes that
contain in-toto statements whose predicate is a Witness attestation
collection.

This commit introduces a change that allows Archivista to collect
information about DSSE envelopes that contain in-toto statements with
any arbitrary predicate.

Parsers and Storers can be added to collect more refined information
about these other predicates as we seek to add more support for them.
  • Loading branch information
mikhailswift authored Mar 28, 2023
1 parent 1f459fa commit 21ab99d
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 28 deletions.
69 changes: 69 additions & 0 deletions internal/metadatastorage/attestationcollection/parserstorer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2023 The Archivista Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package attestationcollection

import (
"context"
"encoding/json"

"github.com/testifysec/archivista/ent"
"github.com/testifysec/archivista/internal/metadatastorage"
"github.com/testifysec/go-witness/attestation"
)

const (
Predicate = "https://witness.testifysec.com/attestation-collection/v0.1"
)

// attestation.Collection from go-witness will try to parse each of the attestations by calling their factory functions,
// which require the attestations to be registered in the go-witness library. We don't really care about the actual attestation
// data for the purposes here, so just leave it as a raw message.
type ParsedCollection struct {
attestation.Collection
Attestations []struct {
Type string `json:"type"`
Attestation json.RawMessage `json:"attestation"`
} `json:"attestations"`
}

func Parse(data []byte) (metadatastorage.Storer, error) {
parsedCollection := ParsedCollection{}
if err := json.Unmarshal(data, &parsedCollection); err != nil {
return parsedCollection, err
}

return parsedCollection, nil
}

func (parsedCollection ParsedCollection) Store(ctx context.Context, tx *ent.Tx, stmtID int) error {
collection, err := tx.AttestationCollection.Create().
SetStatementID(stmtID).
SetName(parsedCollection.Name).
Save(ctx)
if err != nil {
return err
}

for _, a := range parsedCollection.Attestations {
if err := tx.Attestation.Create().
SetAttestationCollectionID(collection.ID).
SetType(a.Type).
Exec(ctx); err != nil {
return err
}
}

return nil
}
39 changes: 11 additions & 28 deletions internal/metadatastorage/mysqlstore/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import (
"github.com/go-sql-driver/mysql"
"github.com/networkservicemesh/sdk/pkg/tools/log"
"github.com/testifysec/archivista/ent"
"github.com/testifysec/go-witness/attestation"
"github.com/testifysec/archivista/internal/metadatastorage"
"github.com/testifysec/archivista/internal/metadatastorage/parserregistry"
"github.com/testifysec/go-witness/cryptoutil"
"github.com/testifysec/go-witness/dsse"
"github.com/testifysec/go-witness/intoto"
Expand Down Expand Up @@ -115,17 +116,6 @@ func (s *Store) withTx(ctx context.Context, fn func(tx *ent.Tx) error) error {
return nil
}

// attestation.Collection from go-witness will try to parse each of the attestations by calling their factory functions,
// which require the attestations to be registered in the go-witness library. We don't really care about the actual attestation
// data for the purposes here, so just leave it as a raw message.
type parsedCollection struct {
attestation.Collection
Attestations []struct {
Type string `json:"type"`
Attestation json.RawMessage `json:"attestation"`
} `json:"attestations"`
}

func (s *Store) Store(ctx context.Context, gitoid string, obj []byte) error {
envelope := &dsse.Envelope{}
if err := json.Unmarshal(obj, envelope); err != nil {
Expand All @@ -142,9 +132,13 @@ func (s *Store) Store(ctx context.Context, gitoid string, obj []byte) error {
return err
}

parsedCollection := &parsedCollection{}
if err := json.Unmarshal(payload.Predicate, parsedCollection); err != nil {
return err
predicateParser, ok := parserregistry.ParserForPredicate(payload.PredicateType)
var predicateStorer metadatastorage.Storer
if ok {
predicateStorer, err = predicateParser(payload.Predicate)
if err != nil {
return fmt.Errorf("unable to parse intoto statements predicate: %w", err)
}
}

err = s.withTx(ctx, func(tx *ent.Tx) error {
Expand Down Expand Up @@ -240,19 +234,8 @@ func (s *Store) Store(ctx context.Context, gitoid string, obj []byte) error {
return err
}

collection, err := tx.AttestationCollection.Create().
SetStatementID(stmt.ID).
SetName(parsedCollection.Name).
Save(ctx)
if err != nil {
return err
}

for _, a := range parsedCollection.Attestations {
if err := tx.Attestation.Create().
SetAttestationCollectionID(collection.ID).
SetType(a.Type).
Exec(ctx); err != nil {
if predicateStorer != nil {
if err := predicateStorer.Store(ctx, tx, stmt.ID); err != nil {
return err
}
}
Expand Down
27 changes: 27 additions & 0 deletions internal/metadatastorage/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2023 The Archivista Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package metadatastorage

import (
"context"

"github.com/testifysec/archivista/ent"
)

type ParserFunc func([]byte) (Storer, error)

type Storer interface {
Store(ctx context.Context, tx *ent.Tx, stmtID int) error
}
31 changes: 31 additions & 0 deletions internal/metadatastorage/parserregistry/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2023 The Archivista Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package parserregistry

import (
"github.com/testifysec/archivista/internal/metadatastorage"
"github.com/testifysec/archivista/internal/metadatastorage/attestationcollection"
)

var (
parsersByPredicate = map[string]metadatastorage.ParserFunc{
attestationcollection.Predicate: attestationcollection.Parse,
}
)

func ParserForPredicate(predicate string) (metadatastorage.ParserFunc, bool) {
pf, ok := parsersByPredicate[predicate]
return pf, ok
}

0 comments on commit 21ab99d

Please sign in to comment.