Files
sxchnge/backend/product/verification/store/consume_scope.go
T

113 lines
3.4 KiB
Go

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 := "<nil>"
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...)
}