-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a publisher store to enable Archivista to publish information using different protocols and integrations * feat: add dapr publisher this publisher allows users to publish messages (gitoid/dsse payload) to a dapr HTTP pub/sub. * feat: add RSTUF publisher this publisher allows users to integrate Archivista with Repository Service for TUF, in order to secure the Archivista repository using TUF metadata signatures. --------- Signed-off-by: John Kjell <[email protected]> Signed-off-by: Kairo Araujo <[email protected]> Co-authored-by: John Kjell <[email protected]>
- Loading branch information
1 parent
d0e318e
commit ab2a157
Showing
8 changed files
with
362 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// Copyright 2024 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 dapr | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/in-toto/archivista/pkg/config" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
type DaprHttp struct { | ||
Client *http.Client | ||
Host string | ||
HttpPort string | ||
PubsubComponentName string | ||
PubsubTopic string | ||
Url string | ||
} | ||
|
||
type daprPayload struct { | ||
Gitoid string | ||
Payload []byte | ||
} | ||
|
||
type Publisher interface { | ||
Publish(ctx context.Context, gitoid string, payload []byte) error | ||
} | ||
|
||
func (d *DaprHttp) Publish(ctx context.Context, gitoid string, payload []byte) error { | ||
if d.Client == nil { | ||
d.Client = &http.Client{ | ||
Timeout: 15 * time.Second, | ||
} | ||
} | ||
|
||
if d.Url == "" { | ||
d.Url = d.Host + ":" + d.HttpPort + | ||
"/v1.0/publish/" + d.PubsubComponentName + "/" + d.PubsubTopic | ||
} | ||
|
||
dp := daprPayload{ | ||
Gitoid: gitoid, | ||
Payload: payload, | ||
} | ||
// Marshal the message to JSON | ||
msgBytes, err := json.Marshal(dp) | ||
if err != nil { | ||
logrus.Error(err.Error()) | ||
return err | ||
} | ||
|
||
res, err := d.Client.Post(d.Url, "application/json", bytes.NewReader(msgBytes)) | ||
if err != nil { | ||
logrus.Error(err.Error()) | ||
return err | ||
} | ||
if res.StatusCode != http.StatusNoContent { | ||
logrus.Printf("failed to publish message: %s", res.Body) | ||
return fmt.Errorf("failed to publish message: %s", res.Body) | ||
} | ||
defer res.Body.Close() | ||
|
||
return nil | ||
} | ||
|
||
func NewPublisher(config *config.Config) Publisher { | ||
daprPublisher := &DaprHttp{ | ||
Host: config.PublisherDaprHost, | ||
HttpPort: config.PublisherDaprPort, | ||
PubsubComponentName: config.PublisherDaprComponentName, | ||
PubsubTopic: config.PublisherDaprTopic, | ||
Url: config.PublisherDaprURL, | ||
} | ||
return daprPublisher | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Copyright 2024 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 publisherstore | ||
|
||
import ( | ||
"context" | ||
"strings" | ||
|
||
"github.com/in-toto/archivista/pkg/config" | ||
"github.com/in-toto/archivista/pkg/publisherstore/dapr" | ||
"github.com/in-toto/archivista/pkg/publisherstore/rstuf" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
type Publisher interface { | ||
Publish(ctx context.Context, gitoid string, payload []byte) error | ||
} | ||
|
||
func New(config *config.Config) []Publisher { | ||
var publisherStore []Publisher | ||
for _, pubType := range config.Publisher { | ||
pubType = strings.ToUpper(pubType) // Normalize the input | ||
switch pubType { | ||
case "DAPR": | ||
publisherStore = append(publisherStore, dapr.NewPublisher(config)) | ||
logrus.Info("Using publisher: DAPR") | ||
|
||
case "RSTUF": | ||
publisherStore = append(publisherStore, rstuf.NewPublisher(config)) | ||
logrus.Info("Using publisher: RSTUF") | ||
default: | ||
logrus.Errorf("unsupported publisher type: %s", pubType) | ||
} | ||
} | ||
return publisherStore | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
// Copyright 2024 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 rstuf | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"crypto/sha256" | ||
"encoding/hex" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/http/httputil" | ||
|
||
"github.com/in-toto/archivista/pkg/config" | ||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
type RSTUF struct { | ||
Host string | ||
} | ||
|
||
type Publisher interface { | ||
Publish(ctx context.Context, gitoid string, payload []byte) error | ||
} | ||
|
||
func (r *RSTUF) parseRSTUFPayload(gitoid string, payload []byte) ([]byte, error) { | ||
objHash := sha256.Sum256(payload) | ||
// custom := make(map[string]any) | ||
// custom["gitoid"] = gitoid | ||
artifacts := []Artifact{ | ||
{ | ||
Path: gitoid, | ||
Info: ArtifactInfo{ | ||
Length: len(payload), | ||
Hashes: Hashes{ | ||
Sha256: hex.EncodeToString(objHash[:]), | ||
}, | ||
// Custom: custom, | ||
}, | ||
}, | ||
} | ||
|
||
artifactPayload := ArtifactPayload{ | ||
Artifacts: artifacts, | ||
AddTaskIDToCustom: false, | ||
PublishTargets: true, | ||
} | ||
|
||
payloadBytes, err := json.Marshal(artifactPayload) | ||
if err != nil { | ||
return nil, fmt.Errorf("error marshaling payload: %v", err) | ||
} | ||
return payloadBytes, nil | ||
} | ||
|
||
func (r *RSTUF) Publish(ctx context.Context, gitoid string, payload []byte) error { | ||
// this publisher allows integration with the RSTUF project to store | ||
// the attestation and policy in the TUF metadata. | ||
// this TUF metadata can be used to build truste when distributing the | ||
// attestations and policies. | ||
// Convert payload to JSON | ||
url := r.Host + "/api/v1/artifacts" | ||
|
||
payloadBytes, err := r.parseRSTUFPayload(gitoid, payload) | ||
if err != nil { | ||
return fmt.Errorf("error parsing payload: %v", err) | ||
} | ||
|
||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(payloadBytes)) | ||
if err != nil { | ||
return fmt.Errorf("error creating request: %v", err) | ||
} | ||
|
||
req.Header.Set("Content-Type", "application/json") | ||
// Add any additional headers or authentication if needed | ||
|
||
client := &http.Client{} | ||
resp, err := client.Do(req) | ||
if err != nil { | ||
logrus.Errorf("error making request: %v", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != http.StatusAccepted { | ||
logb, _ := httputil.DumpResponse(resp, true) | ||
logrus.Errorf("error body from RSTUF: %v", string(logb)) | ||
return fmt.Errorf("error response from RSTUF: %v", err) | ||
} | ||
|
||
// Handle the response as needed | ||
body, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
logrus.Errorf("error reading response body: %v", err) | ||
} | ||
|
||
response := Response{} | ||
err = json.Unmarshal(body, &response) | ||
if err != nil { | ||
logrus.Errorf("error unmarshaling response: %v", err) | ||
} | ||
logrus.Debugf("RSTUF task id: %v", response.Data.TaskId) | ||
// TODO: monitor RSTUF task id for completion | ||
return nil | ||
} | ||
|
||
func NewPublisher(config *config.Config) Publisher { | ||
return &RSTUF{ | ||
Host: config.PublisherRstufHost, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// Copyright 2024 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 rstuf | ||
|
||
// Hashes represents the Hashes structure | ||
type Hashes struct { | ||
Sha256 string `json:"sha256"` | ||
} | ||
|
||
// ArtifactInfo represents the ArtifactInfo structure | ||
type ArtifactInfo struct { | ||
Length int `json:"length"` | ||
Hashes Hashes `json:"hashes"` | ||
Custom map[string]any `json:"custom,omitempty"` | ||
} | ||
|
||
// Artifact represents the Artifact structure | ||
type Artifact struct { | ||
Path string `json:"path"` | ||
Info ArtifactInfo `json:"info"` | ||
} | ||
|
||
// ArtifactPayload represents the payload structure | ||
type ArtifactPayload struct { | ||
Artifacts []Artifact `json:"artifacts"` | ||
AddTaskIDToCustom bool `json:"add_task_id_to_custom"` | ||
PublishTargets bool `json:"publish_targets"` | ||
} | ||
|
||
type ArtifactsResponse struct { | ||
Artifacts []string `json:"artifacts"` | ||
TaskId string `json:"task_id"` | ||
LastUpdate string `json:"last_update"` | ||
Message string `json:"message"` | ||
} | ||
|
||
type Response struct { | ||
Data ArtifactsResponse `json:"data"` | ||
} |
Oops, something went wrong.