Skip to content

Commit

Permalink
Merge pull request #16 from MGTheTrain/opt/general
Browse files Browse the repository at this point in the history
Increase code coverage and refactoring
  • Loading branch information
MGTheTrain authored Nov 20, 2024
2 parents be33688 + 312520d commit ef0057c
Show file tree
Hide file tree
Showing 25 changed files with 973 additions and 385 deletions.
7 changes: 2 additions & 5 deletions docs/diagrams/erd.mmd
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,20 @@ erDiagram
CRYPTOGRAPHIC_KEY {
string id PK
string type
datetime created_at
datetime expires_at
datetime date_time_created
string user_id FK
}

BLOB {
string id PK
datetime upload_time
datetime date_time_created
string user_id FK
string key_id FK
string name
int size
string type
string encryption_algorithm
string hash_algorithm
bool is_encrypted
bool is_signed
}

CRYPTOGRAPHIC_KEY ||--o| BLOB : "associated with"
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ require (
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
golang.org/x/arch v0.12.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
Expand Down
124 changes: 124 additions & 0 deletions internal/app/services/blob.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,132 @@
package services

import (
"crypto_vault_service/internal/domain/blobs"
"crypto_vault_service/internal/infrastructure/connector"
"crypto_vault_service/internal/persistence/repository"
"fmt"
)

// BlobUploadService implements the BlobUploadService interface for handling blob uploads
type BlobUploadService struct {
BlobConnector connector.BlobConnector
BlobRepository repository.BlobRepository
}

// NewBlobUploadService creates a new instance of BlobUploadService
func NewBlobUploadService(blobConnector connector.BlobConnector, blobRepository repository.BlobRepository) *BlobUploadService {
return &BlobUploadService{
BlobConnector: blobConnector,
BlobRepository: blobRepository,
}
}

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

// Use the BlobConnector to upload the files to Azure Blob Storage
blobMeta, err := s.BlobConnector.Upload(filePaths)
if err != nil {
return nil, fmt.Errorf("failed to upload blobs: %w", err)
}

// If no blobs are uploaded, return early
if len(blobMeta) == 0 {
return nil, fmt.Errorf("no blobs uploaded")
}

// Store the metadata in the database using the BlobRepository
for _, blob := range blobMeta {
err := s.BlobRepository.Create(blob)
if err != nil {
// Rollback any previously uploaded blobs if the metadata fails to store
// (you can call delete method to handle this as needed)
return nil, fmt.Errorf("failed to store metadata for blob '%s': %w", blob.Name, err)
}
}

// Return the metadata of uploaded blobs
return blobMeta, nil
}

// BlobMetadataService implements the BlobMetadataService interface for retrieving and deleting blob metadata
type BlobMetadataService struct {
BlobConnector connector.BlobConnector
BlobRepository repository.BlobRepository
}

// NewBlobMetadataService creates a new instance of BlobMetadataService
func NewBlobMetadataService(blobRepository repository.BlobRepository, blobConnector connector.BlobConnector) *BlobMetadataService {
return &BlobMetadataService{
BlobConnector: blobConnector,
BlobRepository: blobRepository,
}
}

// List retrieves all blobs' metadata considering a query filter
func (s *BlobMetadataService) List(query *blobs.BlobMetaQuery) ([]*blobs.BlobMeta, error) {
// Assuming BlobRepository has a method to query metadata, you can adapt to GORM queries.
var blobsList []*blobs.BlobMeta

// TBD

return blobsList, nil
}

// GetByID retrieves a blob's metadata by its unique ID
func (s *BlobMetadataService) GetByID(blobID string) (*blobs.BlobMeta, error) {
// Retrieve the blob metadata using the BlobRepository
blob, err := s.BlobRepository.GetById(blobID)
if err != nil {
return nil, fmt.Errorf("failed to retrieve blob metadata by ID '%s': %w", blobID, err)
}
return blob, nil
}

// DeleteByID deletes a blob and its associated metadata by ID
func (s *BlobMetadataService) DeleteByID(blobID string) error {
// Retrieve the blob metadata to ensure it exists
blob, err := s.BlobRepository.GetById(blobID)
if err != nil {
return fmt.Errorf("failed to retrieve blob metadata by ID '%s' for deletion: %w", blobID, err)
}

// Delete the blob from Blob Storage using the BlobConnector
err = s.BlobRepository.DeleteById(blobID)
if err != nil {
return fmt.Errorf("failed to delete blob metadata by ID '%s': %w", blobID, err)
}

// Now, delete the actual blob from the Blob Storage
err = s.BlobConnector.Delete(blob.ID, blob.Name)
if err != nil {
return fmt.Errorf("failed to delete blob '%s' from Blob Storage: %w", blob.Name, err)
}

return nil
}

// BlobDownloadService implements the BlobDownloadService interface for downloading blobs
type BlobDownloadService struct {
BlobConnector connector.BlobConnector
}

// NewBlobDownloadService creates a new instance of BlobDownloadService
func NewBlobDownloadService(blobConnector connector.BlobConnector) *BlobDownloadService {
return &BlobDownloadService{
BlobConnector: blobConnector,
}
}

// Download retrieves a blob's content by its ID and name
func (s *BlobDownloadService) Download(blobID, blobName string) ([]byte, error) {
// Retrieve the blob metadata from the BlobRepository to ensure it exists
// Here you might want to consider validating the blob's existence.
blob, err := s.BlobConnector.Download(blobID, blobName)
if err != nil {
return nil, fmt.Errorf("failed to download blob '%s': %w", blobName, err)
}

// Return the metadata and content of the downloaded blob
return blob, nil
}
5 changes: 5 additions & 0 deletions internal/app/services/crypto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package services

// CryptoKeyOperationService implements the ICryptoKeyOperationService interface for local cryptographic key management, encryption, signing, and PKCS#11 operations.
type CryptoKeyOperationService struct {
}
2 changes: 0 additions & 2 deletions internal/app/services/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,3 @@ type CryptoKeyMetadataService struct {
}
type CryptoKeyDownloadService struct {
}
type CryptoKeyOperationService struct {
}
16 changes: 8 additions & 8 deletions internal/domain/blobs/contract.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package blobs

// BlobUploadService defines methods for uploading blobs.
type BlobUploadService interface {
// IBlobUploadService defines methods for uploading 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)
}

// BlobMetadataService defines methods for retrieving Blob and deleting a blob along with its metadata.
type BlobMetadataService interface {
// IBlobMetadataService defines methods for retrieving Blob and deleting a blob along with its metadata.
type IBlobMetadataService interface {
// List retrieves all blobs' metadata considering a query filter when set.
// It returns a slice of Blob and any error encountered during the retrieval.
List(query *BlobMetaQuery) ([]*BlobMeta, error)
Expand All @@ -22,9 +22,9 @@ type BlobMetadataService interface {
DeleteByID(blobID string) error
}

// BlobDownloadService defines methods for downloading blobs.
type BlobDownloadService interface {
// IBlobDownloadService defines methods for downloading blobs.
type IBlobDownloadService interface {
// Download retrieves a blob by its ID and name.
// It returns the Blob, the file data as a byte slice, and any error encountered during the download process.
Download(blobID, blobName string) (*BlobMeta, []byte, error)
// It returns the file data as a byte slice, and any error encountered during the download process.
Download(blobID, blobName string) ([]byte, error)
}
4 changes: 1 addition & 3 deletions internal/domain/blobs/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@ import (
// BlobMeta represents metadata on the actual blob metadata being stored
type BlobMeta 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
DateTimeCreated time.Time `validate:"required"` // DateTimeCreated 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)
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
}
Expand Down
25 changes: 10 additions & 15 deletions internal/domain/blobs/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,27 @@ import (

// BlobMetaQuery represents metadata on the actual blob being stored.
type BlobMetaQuery struct {
UploadTime time.Time `validate:"required"` // UploadTime 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:"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

// Pagination properties
Limit int `validate:"omitempty,min=1"` // Limit is optional but if provided, should be at least 1
Offset int `validate:"omitempty,min=0"` // Offset is optional but should be 0 or greater for pagination

// Sorting properties
SortBy string `validate:"omitempty,oneof=ID Type CreatedAt ExpiresAt"` // SortBy is optional but can be one of the fields to sort by
SortOrder string `validate:"omitempty,oneof=asc desc"` // SortOrder is optional, default is ascending ('asc'), can also be 'desc'
SortBy string `validate:"omitempty,oneof=ID Type DateTimeCreated"` // SortBy is optional but can be one of the fields to sort by
SortOrder string `validate:"omitempty,oneof=asc desc"` // SortOrder is optional, default is ascending ('asc'), can also be 'desc'
}

// NewBlobMetaQuery creates a BlobMetaQuery with default values.
func NewBlobMetaQuery() *BlobMetaQuery {
return &BlobMetaQuery{
Limit: 10, // Default limit to 10 results per page
Offset: 0, // Default offset to 0 for pagination
SortBy: "CreatedAt", // Default sort by CreatedAt
SortOrder: "asc", // Default sort order ascending
Limit: 10, // Default limit to 10 results per page
Offset: 0, // Default offset to 0 for pagination
SortBy: "DateTimeCreated", // Default sort by DateTimeCreated
SortOrder: "asc", // Default sort order ascending
}
}

Expand All @@ -49,11 +49,6 @@ func (b *BlobMetaQuery) Validate() error {
return fmt.Errorf("Validation failed: %v", validationErrors)
}

// Custom validation logic: Check that ExpiresAt (if exists) is after CreatedAt
if !b.UploadTime.IsZero() && b.UploadTime.After(time.Now()) {
return fmt.Errorf("UploadTime cannot be in the future")
}

// Return nil if no validation errors are found
return nil
}
69 changes: 69 additions & 0 deletions internal/domain/crypto/contracts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package crypto

// ICryptoKeyOperationService defines methods for local cryptographic key management, encryption, signing, and PKCS#11 operations.
type ICryptoKeyOperationService interface {

// --- Key Generation ---

// GenerateKey generates a cryptographic key of the specified type and size (e.g., AES, RSA, ECDSA).
// It returns the generated key as a byte slice and any error encountered during the key generation.
GenerateKey(keyType string, keySize int) ([]byte, error)

// --- Key Storage and Retrieval ---

// SaveKey saves a cryptographic key to a specified file.
// It returns any error encountered during the saving process.
SaveKey(key []byte, filename string) error

// LoadKey loads a cryptographic key from a specified file.
// It returns the loaded key as a byte slice and any error encountered during the loading process.
LoadKey(filename string) ([]byte, error)

// --- Encryption and Decryption (Symmetric algorithms like AES) ---

// EncryptWithSymmetricKey encrypts data using a symmetric key (e.g., AES).
// It returns the encrypted data as a byte slice and any error encountered during encryption.
EncryptWithSymmetricKey(plainText []byte, key []byte) ([]byte, error)

// DecryptWithSymmetricKey decrypts data using a symmetric key (e.g., AES).
// It returns the decrypted data as a byte slice and any error encountered during decryption.
DecryptWithSymmetricKey(cipherText []byte, key []byte) ([]byte, error)

// --- Asymmetric Encryption (RSA, ECDSA, PKCS#11) ---

// EncryptWithPublicKey encrypts data with a public key using asymmetric encryption algorithms (e.g., RSA, ECDSA).
// It optionally supports PKCS#11 hardware tokens for key storage.
// It returns the encrypted data as a byte slice and any error encountered during encryption.
EncryptWithPublicKey(plainText []byte, publicKey interface{}) ([]byte, error)

// DecryptWithPrivateKey decrypts data with a private key using asymmetric encryption algorithms (e.g., RSA, ECDSA).
// It optionally supports PKCS#11 hardware tokens for key storage.
// It returns the decrypted data as a byte slice and any error encountered during decryption.
DecryptWithPrivateKey(cipherText []byte, privateKey interface{}) ([]byte, error)

// --- Signing and Verification (For RSA, ECDSA) ---

// SignWithPrivateKey signs a message using a private key with asymmetric algorithms (e.g., RSA, ECDSA).
// It optionally supports PKCS#11 hardware tokens for key storage.
// It returns the signature and any error encountered during the signing process.
SignWithPrivateKey(message []byte, privateKey interface{}) ([]byte, error)

// VerifyWithPublicKey verifies a signature using a public key with asymmetric algorithms (e.g., RSA, ECDSA).
// It optionally supports PKCS#11 hardware tokens for key storage.
// It returns true if the signature is valid, false otherwise, and any error encountered during the verification process.
VerifyWithPublicKey(message []byte, signature []byte, publicKey interface{}) (bool, error)

// --- PKCS#11 Operations ---

// InitializeToken initializes a PKCS#11 token in the specified hardware slot.
// It returns any error encountered during the initialization.
InitializeToken(slot string) error

// AddKeyToToken adds a cryptographic key to a PKCS#11 token.
// It returns any error encountered during the addition of the key.
AddKeyToToken() error

// DeleteKeyFromToken deletes a cryptographic key from a PKCS#11 token by its type and label.
// It returns any error encountered during the deletion of the key.
DeleteKeyFromToken(objectType, objectLabel string) error
}
Loading

0 comments on commit ef0057c

Please sign in to comment.