4468903535
Adds a new hashed key storage mechanism for API keys in the cache. Replaces direct mapping to API keys with composite keys based on organizationId and name. Implements searching of API keys using hash comparisons for improved security. Updates related tests to ensure correct functionality and validate the hashing. Also, adds support for a new dependency `golang.org/x/crypto`.
126 lines
2.9 KiB
Go
126 lines
2.9 KiB
Go
package domain
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
|
|
|
|
"gitlab.com/unboundsoftware/schemas/hash"
|
|
)
|
|
|
|
type AddOrganization struct {
|
|
Name string
|
|
Initiator string
|
|
}
|
|
|
|
func (a AddOrganization) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
|
|
if aggregate.Identity() != nil {
|
|
return fmt.Errorf("organization already exists")
|
|
}
|
|
if len(a.Name) == 0 {
|
|
return fmt.Errorf("name is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a AddOrganization) Event(context.Context) eventsourced.Event {
|
|
return &OrganizationAdded{
|
|
Name: a.Name,
|
|
Initiator: a.Initiator,
|
|
}
|
|
}
|
|
|
|
var _ eventsourced.Command = AddOrganization{}
|
|
|
|
type AddAPIKey struct {
|
|
Name string
|
|
Key string
|
|
Refs []string
|
|
Read bool
|
|
Publish bool
|
|
Initiator string
|
|
}
|
|
|
|
func (a AddAPIKey) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
|
|
if aggregate.Identity() == nil {
|
|
return fmt.Errorf("organization does not exist")
|
|
}
|
|
for _, k := range aggregate.(*Organization).APIKeys {
|
|
if k.Name == a.Name {
|
|
return fmt.Errorf("a key named '%s' already exist", a.Name)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (a AddAPIKey) Event(context.Context) eventsourced.Event {
|
|
// Hash the API key using bcrypt for secure storage
|
|
// Note: We can't return an error here, but bcrypt errors are extremely rare
|
|
// (only if system runs out of memory or bcrypt cost is invalid)
|
|
// We use a fixed cost of 12 which is always valid
|
|
hashedKey, err := hash.APIKey(a.Key)
|
|
if err != nil {
|
|
// This should never happen with bcrypt cost 12, but if it does,
|
|
// we'll store an empty hash which will fail validation later
|
|
hashedKey = ""
|
|
}
|
|
|
|
return &APIKeyAdded{
|
|
Name: a.Name,
|
|
Key: hashedKey,
|
|
Refs: a.Refs,
|
|
Read: a.Read,
|
|
Publish: a.Publish,
|
|
Initiator: a.Initiator,
|
|
}
|
|
}
|
|
|
|
var _ eventsourced.Command = AddAPIKey{}
|
|
|
|
type UpdateSubGraph struct {
|
|
OrganizationId string
|
|
Ref string
|
|
Service string
|
|
Url *string
|
|
WSUrl *string
|
|
Sdl string
|
|
Initiator string
|
|
}
|
|
|
|
func (u UpdateSubGraph) Validate(_ context.Context, aggregate eventsourced.Aggregate) error {
|
|
switch a := aggregate.(type) {
|
|
case *SubGraph:
|
|
if strings.TrimSpace(u.Ref) == "" {
|
|
return fmt.Errorf("ref is missing")
|
|
}
|
|
if strings.TrimSpace(u.Service) == "" {
|
|
return fmt.Errorf("service is missing")
|
|
}
|
|
if strings.TrimSpace(u.Sdl) == "" {
|
|
return fmt.Errorf("SDL is missing")
|
|
}
|
|
if (u.Url == nil || strings.TrimSpace(*u.Url) == "") && a.Url == nil {
|
|
return fmt.Errorf("url is missing")
|
|
}
|
|
default:
|
|
return fmt.Errorf("aggregate is not a SubGraph")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (u UpdateSubGraph) Event(context.Context) eventsourced.Event {
|
|
return &SubGraphUpdated{
|
|
OrganizationId: u.OrganizationId,
|
|
Ref: u.Ref,
|
|
Service: u.Service,
|
|
Url: u.Url,
|
|
WSUrl: u.WSUrl,
|
|
Sdl: u.Sdl,
|
|
Initiator: u.Initiator,
|
|
}
|
|
}
|
|
|
|
var _ eventsourced.Command = UpdateSubGraph{}
|