Skip to content

Commit

Permalink
Merge pull request #12 from MGTheTrain/feature/secure-file-storage
Browse files Browse the repository at this point in the history
Feature/secure file storage
  • Loading branch information
MGTheTrain authored Nov 19, 2024
2 parents 76b3f5d + c119608 commit 7cb9a4b
Show file tree
Hide file tree
Showing 19 changed files with 201 additions and 202 deletions.
4 changes: 3 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"name": "Tools for building and running Go projects",
"image": "mcr.microsoft.com/vscode/devcontainers/go:1.21",
// Features to add to the dev container. More info: https://containers.dev/features
"features": {},
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
},
"postCreateCommand": "apt-get update && apt-get install -y openssl opensc softhsm libssl-dev libengine-pkcs11-openssl",
"remoteUser": "root"
}
6 changes: 6 additions & 0 deletions docs/architecture-decision-record/ddd.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@ Incorporating DDD as an architecture decision impacts the structure and developm
│ │ └── ...
│ ├── app
│ ├── domain
│ └── blob
│ └── key
│ └── permission
│ ├── infrastructure
│ └── persistence
└── test
├── data
├── integration
│ ├── domain
│ └── blob
│ └── key
│ └── permission
│ ├── infrastructure
│ └── persistence
└── unit
Expand Down
28 changes: 28 additions & 0 deletions internal/domain/blobs/contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package blobs

// BlobManagement defines methods for managing blob operations.
type BlobManagement interface {
// Upload handles the upload of blobs from file paths.
// Returns the created Blobs metadata and any error encountered.
Upload(filePath []string) ([]*Blob, error)

// Download retrieves a blob by its ID and name, returning the metadata and file data.
// Returns the Blob metadata, file data as a byte slice, and any error.
Download(blobId, blobName string) (*Blob, []byte, error)

// DeleteByID removes a blob by its ID.
// Returns any error encountered.
DeleteByID(blobId string) error
}

// BlobMetadataManagement defines the methods for managing Blob metadata
type BlobMetadataManagement interface {
// Create creates a new blob
Create(blob *Blob) (*Blob, error)
// GetByID retrieves blob by ID
GetByID(blobID string) (*Blob, error)
// UpdateByID updates a blob's metadata
UpdateByID(blobID string, updates *Blob) (*Blob, error)
// DeleteByID deletes a blob by ID
DeleteByID(blobID string) error
}
43 changes: 43 additions & 0 deletions internal/domain/blobs/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package blobs

import (
"crypto_vault_service/internal/domain/keys"
"fmt"
"time"

"github.com/go-playground/validator/v10"
)

// Blob represents metadata on the actual blob being stored
type Blob struct {
ID string `gorm:"primaryKey" validate:"required,uuid4"` // ID is required and must be a valid UUID
UploadTime time.Time `validate:"required"` // UploadTime is required
UserID string `validate:"required,uuid4"` // UserID is required and must be a valid UUID
Name string `validate:"required,min=1,max=255"` // Name is required, and its length must be between 1 and 255 characters
Size int64 `validate:"required,min=1"` // Size must be greater than 0
Type string `validate:"required,min=1,max=50"` // Type is required, and its length must be between 1 and 50 characters
EncryptionAlgorithm string `validate:"omitempty,oneof=AES RSA ECDSA"` // EncryptionAlgorithm is optional and must be one of the listed algorithms
HashAlgorithm string `validate:"omitempty,oneof=SHA256 SHA512 MD5"` // HashAlgorithm is optional and must be one of the listed algorithms
IsEncrypted bool `validate:"-"` // IsEncrypted is required (true/false)
IsSigned bool `validate:"-"` // IsSigned is required (true/false)
CryptographicKey keys.CryptographicKey `gorm:"foreignKey:KeyID" validate:"required"` // CryptographicKey is required
KeyID string `validate:"omitempty,uuid4"` // KeyID is optional and must be a valid UUID
}

// Validate for validating Blob struct
func (b *Blob) Validate() error {
// Initialize the validator
validate := validator.New()

// Validate the struct
err := validate.Struct(b)
if err != nil {
// If validation fails, return a formatted error
var validationErrors []string
for _, err := range err.(validator.ValidationErrors) {
validationErrors = append(validationErrors, fmt.Sprintf("Field: %s, Tag: %s", err.Field(), err.Tag()))
}
return fmt.Errorf("Validation failed: %v", validationErrors)
}
return nil // Return nil if validation passes
}
20 changes: 0 additions & 20 deletions internal/domain/contracts/blob_management.go

This file was deleted.

30 changes: 0 additions & 30 deletions internal/domain/contracts/key_management.go

This file was deleted.

29 changes: 0 additions & 29 deletions internal/domain/contracts/metadata_management.go

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,41 @@
package contracts
package keys

// Define KeyType as a custom type (based on int)
type KeyType int

// Enum-like values using iota
const (
AsymmetricPublic KeyType = iota
AsymmetricPrivate
Symmetric
)

// KeyManagement defines methods for managing cryptographic key operations.
type KeyManagement interface {
// Upload handles the upload of blobs from file paths.
// Returns the created Blobs metadata and any error encountered.
Upload(filePath []string) ([]*CryptographicKey, error)

// Download retrieves a cryptographic key by its ID and key type, returning the metadata and key data.
// Returns the key metadata, key data as a byte slice, and any error.
Download(keyId string, keyType KeyType) (*CryptographicKey, []byte, error)

// DeleteByID removes a cryptographic key by its ID.
// Returns any error encountered.
DeleteByID(keyId string) error
}

// CryptographicKeyMetadataManagement defines the methods for managing CryptographicKey metadata
type CryptographicKeyMetadataManagement interface {
// Create creates a new cryptographic key
Create(key *CryptographicKey) (*CryptographicKey, error)
// GetByID retrieves cryptographic key by ID
GetByID(keyID string) (*CryptographicKey, error)
// UpdateByID updates cryptographic key metadata
UpdateByID(keyID string, updates *CryptographicKey) (*CryptographicKey, error)
// DeleteByID deletes a cryptographic key by ID
DeleteByID(keyID string) error
}

// KeyOperations defines methods for key management, encryption, signing, and PKCS#11 operations.
type KeyOperations interface {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package model
package keys

import (
"fmt"
Expand Down
42 changes: 0 additions & 42 deletions internal/domain/model/blob.go

This file was deleted.

File renamed without changes.
39 changes: 21 additions & 18 deletions internal/infrastructure/connector/az_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ package connector
import (
"bytes"
"context"
"crypto_vault_service/internal/domain/model"
"crypto_vault_service/internal/domain/blobs"
"fmt"
"log"
"os"
"path/filepath"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/google/uuid"
Expand All @@ -16,7 +17,7 @@ import (
// AzureBlobConnector is an interface for interacting with Azure Blob storage
type AzureBlobConnector interface {
// Upload uploads multiple files to Azure Blob Storage and returns their metadata.
Upload(filePaths []string) ([]*model.Blob, error)
Upload(filePaths []string) ([]*blobs.Blob, error)
// Download retrieves a blob's content by its ID and name, and returns the data as a stream.
Download(blobId, blobName string) (*bytes.Buffer, error)
// Delete deletes a blob from Azure Blob Storage by its ID and Name, and returns any error encountered.
Expand Down Expand Up @@ -49,17 +50,17 @@ func NewAzureBlobConnector(connectionString string, containerName string) (*Azur
}

// Upload uploads multiple files to Azure Blob Storage and returns their metadata.
func (abc *AzureBlobConnectorImpl) Upload(filePaths []string) ([]*model.Blob, error) {
var blobs []*model.Blob
blobId := uuid.New().String()
func (abc *AzureBlobConnectorImpl) Upload(filePaths []string) ([]*blobs.Blob, error) {
var uploadedBlobs []*blobs.Blob
blobID := uuid.New().String()

// Iterate through all file paths and upload each file
for _, filePath := range filePaths {
// Open the file from the given filePath
file, err := os.Open(filePath)
if err != nil {
err = fmt.Errorf("failed to open file '%s': %w", filePath, err)
abc.rollbackUploadedBlobs(blobs) // Rollback previously uploaded blobs
abc.rollbackUploadedBlobs(uploadedBlobs) // Rollback previously uploaded blobs
return nil, err
}
// Ensure file is closed after processing
Expand All @@ -69,7 +70,7 @@ func (abc *AzureBlobConnectorImpl) Upload(filePaths []string) ([]*model.Blob, er
fileInfo, err := file.Stat()
if err != nil {
err = fmt.Errorf("failed to stat file '%s': %w", filePath, err)
abc.rollbackUploadedBlobs(blobs)
abc.rollbackUploadedBlobs(uploadedBlobs)
return nil, err
}

Expand All @@ -78,44 +79,46 @@ func (abc *AzureBlobConnectorImpl) Upload(filePaths []string) ([]*model.Blob, er
_, err = buf.ReadFrom(file)
if err != nil {
err = fmt.Errorf("failed to read file '%s': %w", filePath, err)
abc.rollbackUploadedBlobs(blobs)
abc.rollbackUploadedBlobs(uploadedBlobs)
return nil, err
}

// Extract the file extension (type)
fileExt := filepath.Ext(fileInfo.Name()) // Gets the file extension (e.g. ".txt", ".jpg")

// Create a Blob object for metadata
blob := &model.Blob{
ID: blobId,
Name: fileInfo.Name(),
Size: fileInfo.Size(),
Type: fileExt,
// Create a Blob object for metadata (Fill in missing fields)
blob := &blobs.Blob{
ID: blobID,
Name: fileInfo.Name(),
Size: fileInfo.Size(),
Type: fileExt,
UploadTime: time.Now(), // Set the current time
}

// Construct the full blob name (ID and Name)
fullBlobName := fmt.Sprintf("%s/%s", blob.ID, blob.Name) // Combine ID and name to form a full path
fullBlobName = filepath.ToSlash(fullBlobName) // Ensure consistent slash usage across platforms

// Upload the blob to Azure
_, err = abc.Client.UploadBuffer(context.Background(), abc.ContainerName, fullBlobName, buf.Bytes(), nil)
if err != nil {
err = fmt.Errorf("failed to upload blob '%s': %w", fullBlobName, err)
abc.rollbackUploadedBlobs(blobs)
abc.rollbackUploadedBlobs(uploadedBlobs)
return nil, err
}

log.Printf("Blob '%s' uploaded successfully.\n", blob.Name)

// Add the successfully uploaded blob to the list
blobs = append(blobs, blob)
uploadedBlobs = append(uploadedBlobs, blob)
}

// Return the list of blobs after successful upload.
return blobs, nil
return uploadedBlobs, nil
}

// rollbackUploadedBlobs deletes the blobs that were uploaded successfully before the error occurred
func (abc *AzureBlobConnectorImpl) rollbackUploadedBlobs(blobs []*model.Blob) {
func (abc *AzureBlobConnectorImpl) rollbackUploadedBlobs(blobs []*blobs.Blob) {
for _, blob := range blobs {
err := abc.Delete(blob.ID, blob.Name)
if err != nil {
Expand Down
Loading

0 comments on commit 7cb9a4b

Please sign in to comment.