package verification import ( verificationmodel "git.sendico.io/tech/sxchnge/product/verification/model" "strings" "time" "git.sendico.io/tech/sxchnge/shared/database/repository" "git.sendico.io/tech/sxchnge/shared/database/repository/builder" "go.mongodb.org/mongo-driver/v2/bson" "go.uber.org/zap" ) type consumeScope struct { now time.Time accountRef bson.ObjectID purpose verificationmodel.VerificationPurpose contextRef string rawToken string trimmedRawToken string accountScoped bool accountRefHex string } func newConsumeScope( accountRef bson.ObjectID, purpose verificationmodel.VerificationPurpose, contextRef string, rawToken string, ) (consumeScope, error) { normalizedContextRef, err := normalizeContextRef(purpose, contextRef) if err != nil { return consumeScope{}, err } accountScoped := accountRef != bson.NilObjectID accountRefHex := "" if accountScoped { accountRefHex = accountRef.Hex() } return consumeScope{ now: time.Now().UTC(), accountRef: accountRef, purpose: purpose, contextRef: normalizedContextRef, rawToken: rawToken, trimmedRawToken: strings.TrimSpace(rawToken), accountScoped: accountScoped, accountRefHex: accountRefHex, }, nil } func (scope consumeScope) logFields(extra ...zap.Field) []zap.Field { fields := []zap.Field{ zap.String("purpose", string(scope.purpose)), zap.Bool("account_scoped", scope.accountScoped), zap.String("account_ref", scope.accountRefHex), } return append(fields, extra...) } func (scope consumeScope) tokenLogFields(token *verificationmodel.VerificationToken, extra ...zap.Field) []zap.Field { fields := scope.logFields(extra...) return append(fields, tokenStateFields(token)...) } func (scope consumeScope) startFields() []zap.Field { return scope.logFields( zap.Time("now_utc", scope.now), zap.Int("raw_token_len", len(scope.rawToken)), zap.Int("raw_token_trimmed_len", len(scope.trimmedRawToken)), zap.Bool("raw_token_digits_only", tokenIsDigitsOnly(scope.rawToken)), zap.Bool("raw_token_has_edge_whitespace", scope.rawToken != scope.trimmedRawToken), zap.String("raw_token_hash_prefix", hashPreview(tokenHash(scope.rawToken))), ) } func (scope consumeScope) scopeFilters() []builder.Query { filters := []builder.Query{ repository.Filter("purpose", scope.purpose), } if scope.accountScoped { filters = append(filters, repository.Filter("accountRef", scope.accountRef)) } filters = appendContextRefFilter(filters, scope.purpose, scope.contextRef) return filters } func (scope consumeScope) scopeFilter() builder.Query { return repository.Query().And(scope.scopeFilters()...) } func (scope consumeScope) consumeFilter(token *verificationmodel.VerificationToken) builder.Query { filters := []builder.Query{ repository.IDFilter(token.ID), repository.Filter("purpose", scope.purpose), repository.Filter("usedAt", nil), repository.Query().Comparison(repository.Field("expiresAt"), builder.Gt, scope.now), } filters = appendContextRefFilter(filters, scope.purpose, scope.contextRef) if scope.accountScoped { filters = append(filters, repository.Filter("accountRef", scope.accountRef)) } if token.MaxRetries != nil { filters = append( filters, repository.Query().Comparison(repository.Field("attempts"), builder.Lt, *token.MaxRetries), ) } return repository.Query().And(filters...) }