feat(cache): implement hashed API key storage and retrieval

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`.
This commit is contained in:
2025-11-20 22:11:17 +01:00
parent 1e2236dc9e
commit 4468903535
11 changed files with 357 additions and 69 deletions
+26 -16
View File
@@ -14,7 +14,7 @@ import (
type Cache struct {
organizations map[string]domain.Organization
users map[string][]string
apiKeys map[string]domain.APIKey
apiKeys map[string]domain.APIKey // keyed by organizationId-name
services map[string]map[string]map[string]struct{}
subGraphs map[string]string
lastUpdate map[string]string
@@ -22,15 +22,17 @@ type Cache struct {
}
func (c *Cache) OrganizationByAPIKey(apiKey string) *domain.Organization {
key, exists := c.apiKeys[apiKey]
if !exists {
return nil
// Find the API key by comparing hashes
for _, key := range c.apiKeys {
if hash.CompareAPIKey(key.Key, apiKey) {
org, exists := c.organizations[key.OrganizationId]
if !exists {
return nil
}
return &org
}
}
org, exists := c.organizations[key.OrganizationId]
if !exists {
return nil
}
return &org
return nil
}
func (c *Cache) OrganizationsByUser(sub string) []domain.Organization {
@@ -43,11 +45,13 @@ func (c *Cache) OrganizationsByUser(sub string) []domain.Organization {
}
func (c *Cache) ApiKeyByKey(key string) *domain.APIKey {
k, exists := c.apiKeys[hash.String(key)]
if !exists {
return nil
// Find the API key by comparing hashes
for _, apiKey := range c.apiKeys {
if hash.CompareAPIKey(apiKey.Key, key) {
return &apiKey
}
}
return &k
return nil
}
func (c *Cache) Services(orgId, ref, lastUpdate string) ([]string, string) {
@@ -76,14 +80,15 @@ func (c *Cache) Update(msg any, _ goamqp.Headers) (any, error) {
key := domain.APIKey{
Name: m.Name,
OrganizationId: m.OrganizationId,
Key: m.Key,
Key: m.Key, // This is now the hashed key
Refs: m.Refs,
Read: m.Read,
Publish: m.Publish,
CreatedBy: m.Initiator,
CreatedAt: m.When(),
}
c.apiKeys[m.Key] = key
// Use composite key: organizationId-name
c.apiKeys[apiKeyId(m.OrganizationId, m.Name)] = key
org := c.organizations[m.OrganizationId]
org.APIKeys = append(org.APIKeys, key)
c.organizations[m.OrganizationId] = org
@@ -93,7 +98,8 @@ func (c *Cache) Update(msg any, _ goamqp.Headers) (any, error) {
c.organizations[m.ID.String()] = *m
c.addUser(m.CreatedBy, *m)
for _, k := range m.APIKeys {
c.apiKeys[k.Key] = k
// Use composite key: organizationId-name
c.apiKeys[apiKeyId(k.OrganizationId, k.Name)] = k
}
case *domain.SubGraph:
c.updateSubGraph(m.OrganizationId, m.Ref, m.ID.String(), m.Service, m.ChangedAt)
@@ -143,3 +149,7 @@ func refKey(orgId string, ref string) string {
func subGraphKey(orgId string, ref string, service string) string {
return fmt.Sprintf("%s<->%s<->%s", orgId, ref, service)
}
func apiKeyId(orgId string, name string) string {
return fmt.Sprintf("%s<->%s", orgId, name)
}