Skip to content

Commit

Permalink
Merge pull request #17 from MGTheTrain/opt/general
Browse files Browse the repository at this point in the history
Descriptive file names, VaultConnector implementation and tests, Key service implementation
  • Loading branch information
MGTheTrain authored Nov 20, 2024
2 parents ef0057c + 24d0c7d commit 515ac3c
Show file tree
Hide file tree
Showing 27 changed files with 566 additions and 198 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ func NewBlobUploadService(blobConnector connector.BlobConnector, blobRepository
}

// Upload handles the upload of blobs and stores their metadata in the database.
func (s *BlobUploadService) Upload(filePaths []string) ([]*blobs.BlobMeta, error) {
func (s *BlobUploadService) Upload(filePaths []string, userId string) ([]*blobs.BlobMeta, error) {

// Use the BlobConnector to upload the files to Azure Blob Storage
blobMeta, err := s.BlobConnector.Upload(filePaths)
blobMeta, err := s.BlobConnector.Upload(filePaths, userId)
if err != nil {
return nil, fmt.Errorf("failed to upload blobs: %w", err)
}
Expand Down
File renamed without changes.
8 changes: 0 additions & 8 deletions internal/app/services/key.go

This file was deleted.

116 changes: 116 additions & 0 deletions internal/app/services/key_services.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package services

import (
"crypto_vault_service/internal/domain/keys"
"crypto_vault_service/internal/infrastructure/connector"
"crypto_vault_service/internal/persistence/repository"
"fmt"
"time"

"github.com/google/uuid"
)

type CryptKeyUploadService struct {
VaultConnector connector.VaultConnector
CryptoKeyRepo repository.CryptoKeyRepository
}

func (s *CryptKeyUploadService) Upload(filePaths []string) ([]*keys.CryptoKeyMeta, error) {
// Step 1: Upload files to blob storage
userId := uuid.New().String()
blobMeta, err := s.VaultConnector.Upload(filePaths, userId)
if err != nil {
return nil, fmt.Errorf("failed to upload files: %w", err)
}

// Step 2: Store the metadata in the database
var keyMetas []*keys.CryptoKeyMeta
for _, blob := range blobMeta {
// Map Blob metadata to CryptoKey metadata
keyMeta := &keys.CryptoKeyMeta{
ID: uuid.New().String(), // Generate valid UUID for ID
Type: "RSA", // Example key type
DateTimeCreated: time.Now(), // Valid DateTimeCreated time
UserID: uuid.New().String(), // Generate valid UUID for UserID
}

// Validate CryptoKeyMeta
if err := keyMeta.Validate(); err != nil {
return nil, fmt.Errorf("invalid key metadata: %w", err)
}

// Save metadata to DB
if err := s.CryptoKeyRepo.Create(blob); err != nil {
return nil, fmt.Errorf("failed to create metadata for key of type %s: %w", keyMeta.Type, err)
}

// Append to list
keyMetas = append(keyMetas, keyMeta)
}

// Return metadata
return keyMetas, nil
}

// CryptoKeyMetadataService manages cryptographic key metadata.
type CryptoKeyMetadataService struct {
CryptoKeyRepo repository.CryptoKeyRepository
}

// List retrieves all cryptographic key metadata based on a query.
func (s *CryptoKeyMetadataService) List(query *keys.CryptoKeyQuery) ([]*keys.CryptoKeyMeta, error) {
// For now, let's just retrieve all metadata from the database
var keyMetas []*keys.CryptoKeyMeta
// TBD

return keyMetas, nil
}

// GetByID retrieves the metadata of a cryptographic key by its ID.
func (s *CryptoKeyMetadataService) GetByID(keyID string) (*keys.CryptoKeyMeta, error) {
// Retrieve the metadata from the database
keyMeta, err := s.CryptoKeyRepo.GetByID(keyID)
if err != nil {
return nil, fmt.Errorf("failed to retrieve key metadata: %w", err)
}

return keyMeta, nil
}

// DeleteByID deletes a cryptographic key's metadata by its ID.
func (s *CryptoKeyMetadataService) DeleteByID(keyID string) error {
// Delete the metadata from the database
err := s.CryptoKeyRepo.DeleteByID(keyID)
if err != nil {
return fmt.Errorf("failed to delete key metadata: %w", err)
}
return nil
}

// CryptoKeyDownloadService handles the download of cryptographic keys.
type CryptoKeyDownloadService struct {
VaultConnector connector.VaultConnector
}

// Download retrieves a cryptographic key by its ID and type.
func (s *CryptoKeyDownloadService) Download(keyID string, keyType keys.KeyType) ([]byte, error) {
blobName := "" // Declare the variable outside the blocks

if keyType == keys.AsymmetricPublic {
blobName = "asymmetric-public-key" // Assign to the already declared variable
} else if keyType == keys.AsymmetricPrivate {
blobName = "asymmetric-private-key" // Assign to the already declared variable
} else if keyType == keys.Symmetric {
blobName = "symmetric-key" // Assign to the already declared variable
} else {
return nil, fmt.Errorf("unsupported key type: %v", keyType)
}

blobData, err := s.VaultConnector.Download(keyID, blobName)
if err != nil {
return nil, fmt.Errorf("failed to download key from blob storage: %w", err)
}

// Return the metadata and the downloaded content (as a byte slice)
return blobData, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package blobs
type IBlobUploadService interface {
// Upload handles the upload of blobs from the specified file paths.
// It returns a slice of Blob for the uploaded blobs and any error encountered during the upload process.
Upload(filePaths []string) ([]*BlobMeta, error)
Upload(filePaths []string, userId string) ([]*BlobMeta, error)
}

// IBlobMetadataService defines methods for retrieving Blob and deleting a blob along with its metadata.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ type BlobMeta struct {
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
CryptoKey keys.CryptoKeyMeta `gorm:"foreignKey:KeyID" validate:"required"` // CryptoKey is required
KeyID string `validate:"omitempty,uuid4"` // KeyID is optional and must be a valid UUID
EncryptionAlgorithm string `validate:"omitempty,oneof=AES RSA ECDSA"` // EncryptionAlgorithm is optional and if set must be one of the listed algorithms
HashAlgorithm string `validate:"omitempty,oneof=SHA256 SHA512 MD5"` // HashAlgorithm is optional and if set must be one of the listed algorithms
CryptoKey keys.CryptoKeyMeta `gorm:"foreignKey:KeyID" validate:"omitempty"` // CryptoKey is optional
KeyID string `validate:"omitempty,uuid4"` // KeyID is optional and if set must be a valid UUID
}

// Validate for validating BlobMeta struct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import (

// BlobMetaQuery represents metadata on the actual blob being stored.
type BlobMetaQuery struct {
DateTimeCreated time.Time `validate:"required"` // DateTimeCreated is required
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
DateTimeCreated time.Time `validate:"omitempty"` // DateTimeCreated is optional
Name string `validate:"omitempty,min=1,max=255"` // Name is optional and its length must be between 1 and 255 characters
Size int64 `validate:"omitempty,min=1"` // Size is optional and if set must be greater than 0
Type string `validate:"omitempty,min=1,max=50"` // Type is optional, and its length must be between 1 and 50 characters

// Pagination properties
Limit int `validate:"omitempty,min=1"` // Limit is optional but if provided, should be at least 1
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
// BlobConnector is an interface for interacting with Blob storage
type BlobConnector interface {
// Upload uploads multiple files to Blob Storage and returns their metadata.
Upload(filePaths []string) ([]*blobs.BlobMeta, error)
Upload(filePaths []string, userId string) ([]*blobs.BlobMeta, error)
// Download retrieves a blob's content by its ID and name, and returns the data as a stream.
Download(blobId, blobName string) ([]byte, error)
// Delete deletes a blob from Blob Storage by its ID and Name, and returns any error encountered.
Expand Down Expand Up @@ -50,7 +50,7 @@ func NewAzureBlobConnector(connectionString string, containerName string) (*Azur
}

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

Expand Down Expand Up @@ -92,7 +92,13 @@ func (abc *AzureBlobConnector) Upload(filePaths []string) ([]*blobs.BlobMeta, er
Name: fileInfo.Name(),
Size: fileInfo.Size(),
Type: fileExt,
DateTimeCreated: time.Now(), // Set the current time
DateTimeCreated: time.Now(),
UserID: userId,
// Size int64
// EncryptionAlgorithm string
// HashAlgorithm string
// CryptoKey keys.CryptoKeyMeta
// KeyID string
}

// Construct the full blob name (ID and Name)
Expand Down
20 changes: 0 additions & 20 deletions internal/infrastructure/connector/key.go

This file was deleted.

153 changes: 153 additions & 0 deletions internal/infrastructure/connector/key_connectors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package connector

import (
"bytes"
"context"
"crypto_vault_service/internal/domain/keys"
"fmt"
"log"
"os"
"time"

"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob"
"github.com/google/uuid"
)

// VaultConnector is an interface for interacting with custom key storage.
// The current implementation uses Azure Blob Storage, but this may be replaced
// with Azure Key Vault, AWS KMS, or any other cloud-based key management system in the future.
type VaultConnector interface {
// Upload uploads multiple files to the Vault Storage and returns their metadata.
Upload(filePaths []string, userId string) ([]*keys.CryptoKeyMeta, error)

// Download retrieves a key's content by its ID and name, and returns the data as a byte slice.
Download(blobId, blobName string) ([]byte, error)

// Delete deletes a key from Vault Storage by its ID and Name, and returns any error encountered.
Delete(blobId, blobName string) error
}

// AzureVaultConnector is a struct that implements the VaultConnector interface using Azure Blob Storage.
// This is a temporary implementation and may later be replaced with more specialized external key management systems
// like Azure Key Vault or AWS KMS.
type AzureVaultConnector struct {
Client *azblob.Client
ContainerName string
}

// NewAzureVaultConnector creates a new instance of AzureVaultConnector, which connects to Azure Blob Storage.
// This method can be updated in the future to support a more sophisticated key management system like Azure Key Vault.
func NewAzureVaultConnector(connectionString string, containerName string) (*AzureVaultConnector, error) {
// Create a new Azure Blob client using the provided connection string
client, err := azblob.NewClientFromConnectionString(connectionString, nil)
if err != nil {
return nil, fmt.Errorf("failed to create Azure Blob client: %w", err)
}

// Ensure the container exists (this could be a simple placeholder, or it could be updated later to use a dedicated container for keys)
_, err = client.CreateContainer(context.Background(), containerName, nil)
if err != nil {
// Log container creation failure, but do not fail if container already exists
log.Printf("Failed to create Azure container: %v\n", err)
}

return &AzureVaultConnector{
Client: client,
ContainerName: containerName,
}, nil
}

// Upload uploads multiple files to Azure Blob Storage and returns their metadata.
// In the future, this may be refactored to integrate with more advanced key storage systems like Azure Key Vault.
func (vc *AzureVaultConnector) Upload(filePaths []string, userId string) ([]*keys.CryptoKeyMeta, error) {
var keyMetas []*keys.CryptoKeyMeta

// Iterate through all file paths and upload each file
for _, filePath := range filePaths {
// Open the file
file, err := os.Open(filePath)
if err != nil {
return nil, fmt.Errorf("failed to open file '%s': %w", filePath, err)
}
defer file.Close()

// Get file information (name, size, etc.)
fileInfo, err := file.Stat()
if err != nil {
return nil, fmt.Errorf("failed to stat file '%s': %w", filePath, err)
}

// Read the file content into a buffer
buf := new(bytes.Buffer)
_, err = buf.ReadFrom(file)
if err != nil {
return nil, fmt.Errorf("failed to read file '%s': %w", filePath, err)
}

// Generate a unique ID for the key
keyID := uuid.New().String()

// Create metadata for the uploaded key
keyMeta := &keys.CryptoKeyMeta{
ID: keyID,
Type: fileInfo.Name(), // one of asymmetric-public-key, asymmetric-private-key, symmetric-key
DateTimeCreated: time.Now(),
UserID: userId,
}

// Construct the full blob name (ID and Name)
fullBlobName := fmt.Sprintf("%s/%s", keyID, fileInfo.Name())

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

// Add the metadata to the list
keyMetas = append(keyMetas, keyMeta)
}

// Return the metadata of the uploaded keys
return keyMetas, nil
}

// Download retrieves a key's content by its ID and name, and returns the data as a byte slice.
func (vc *AzureVaultConnector) Download(blobId, blobName string) ([]byte, error) {
// Construct the full blob path by combining blob ID and name
fullBlobName := fmt.Sprintf("%s/%s", blobId, blobName)

// Download the blob from Azure Blob Storage
ctx := context.Background()
get, err := vc.Client.DownloadStream(ctx, vc.ContainerName, fullBlobName, nil)
if err != nil {
return nil, fmt.Errorf("failed to download blob '%s': %w", fullBlobName, err)
}

// Read the content into a buffer
downloadedData := bytes.Buffer{}
_, err = downloadedData.ReadFrom(get.Body)
if err != nil {
return nil, fmt.Errorf("failed to read data from blob '%s': %w", fullBlobName, err)
}

// Return the downloaded data
return downloadedData.Bytes(), nil
}

// Delete deletes a key from Azure Blob Storage by its ID and Name.
func (vc *AzureVaultConnector) Delete(blobId, blobName string) error {
// Construct the full blob path by combining blob ID and name
fullBlobName := fmt.Sprintf("%s/%s", blobId, blobName)

// Delete the blob from Azure Blob Storage
ctx := context.Background()
_, err := vc.Client.DeleteBlob(ctx, vc.ContainerName, fullBlobName, nil)
if err != nil {
return fmt.Errorf("failed to delete blob '%s': %w", fullBlobName, err)
}

// Log the successful deletion
log.Printf("Deleted blob '%s' from storage.\n", fullBlobName)
return nil
}
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit 515ac3c

Please sign in to comment.