feat: Add the api_tokens collection and its indexes

The unique index on token_hash is what makes authentication an indexed
lookup rather than a scan, so this builder is fatal on failure like
EnsureAuthIndexes rather than warning like the secrets one.

Registered in ScopedCollections so instance purge reaches it.
This commit is contained in:
2026-08-12 14:16:12 +00:00
parent a41f2b26cc
commit 6ad65a1242
4 changed files with 96 additions and 0 deletions
@@ -43,6 +43,7 @@ var ScopedCollections = []string{
"server_packages",
"vuln_findings",
"vuln_alert_rules",
"api_tokens",
"server_workloads",
}
+39
View File
@@ -0,0 +1,39 @@
package services
import (
"context"
"time"
"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/db"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
)
// EnsureAPITokenIndexes declares the indexes the token path depends on.
//
// The unique index on token_hash is a security property, not an optimisation:
// it is what makes authentication a single indexed lookup rather than a scan,
// and what makes two tokens hashing to one value impossible to store.
//
// Fatal on failure, like EnsureAuthIndexes and unlike the secrets and workflow
// builders: without the unique index the auth path would still answer, which is
// exactly the wrong kind of degradation.
func EnsureAPITokenIndexes() error {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if _, err := db.Col("api_tokens").Indexes().CreateOne(ctx, mongo.IndexModel{
Keys: bson.D{{Key: "token_hash", Value: 1}},
Options: options.Index().SetUnique(true),
}); err != nil {
return err
}
if _, err := db.Col("api_tokens").Indexes().CreateOne(ctx, mongo.IndexModel{
Keys: bson.D{{Key: "instance_id", Value: 1}, {Key: "user_id", Value: 1}},
}); err != nil {
return err
}
return nil
}