mirror of
https://github.com/andwati/zitadel.git
synced 2026-10-18 05:52:51 +00:00
# Which Problems Are Solved As part of #11035 , this PR implements the TOTP check logic for session validation # How the Problems Are Solved - Implement TOTP check logic and tests - The tarpit function has been moved to a common file so that it can be used by both TOTP and password checks - Manual transaction management to avoid stalling the DB while verifier function is executed - Update `database.Change` `Matches()` function to allow comparison of `time.Time` values - A converter package from GRPC to Domain model has been added # Additional Context This is a cherry-picked PR + changes, coming from https://github.com/zitadel/zitadel/pull/11164 - Relates to #11035
53 lines
2.2 KiB
Go
53 lines
2.2 KiB
Go
package domain
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/muhlemmer/gu"
|
|
|
|
"github.com/zitadel/zitadel/backend/v3/storage/database"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
type verifierFn func(encoded, password string) (updated string, err error)
|
|
|
|
// tarpitFn represents a tarpit function
|
|
//
|
|
// The input is the number of failed attempts after which the tarpit is started
|
|
type tarpitFn func(failedAttempts uint64)
|
|
|
|
// totpValidateFn represents a function to validate TOTP
|
|
type totpValidateFn func(toValidate, verifier string) bool
|
|
|
|
func GetLockoutPolicy(ctx context.Context, db database.QueryExecutor, lockoutSettingsRepo LockoutSettingsRepository, instanceID, orgID string) (*LockoutSettings, error) {
|
|
|
|
// We need the organization lockout policy first, and if not available, the instance (default) policy.
|
|
// So we retrieve all records with a matching instance ID and organization ID OR
|
|
// all records with a matching instance ID and NULL (or empty) organization ID.
|
|
// Then we assume NULLs are sorted as largest numbers (that's the case in Postgres),
|
|
// so we sort ascending by organization ID.
|
|
// We limit the result to 1 so that we get either the org policy or the instance one.
|
|
settings, err := lockoutSettingsRepo.List(ctx, db,
|
|
listLockoutSettingCondition(lockoutSettingsRepo, instanceID, orgID),
|
|
database.WithOrderByAscending(lockoutSettingsRepo.OrganizationIDColumn(), lockoutSettingsRepo.InstanceIDColumn()),
|
|
database.WithLimit(1),
|
|
)
|
|
if err != nil {
|
|
return nil, zerrors.ThrowInternal(err, "DOM-3B8Z6s", "failed fetching lockout settings")
|
|
}
|
|
|
|
if rowsReturned := len(settings); rowsReturned != 1 {
|
|
return nil, zerrors.ThrowInternal(NewRowsReturnedMismatchError(1, int64(rowsReturned)), "DOM-mmsrCt", "unexpected number of rows returned")
|
|
}
|
|
|
|
return settings[0], nil
|
|
}
|
|
|
|
func listLockoutSettingCondition(repo LockoutSettingsRepository, instanceID, orgID string) database.QueryOption {
|
|
instanceAndOrg := database.And(repo.InstanceIDCondition(instanceID), repo.OrganizationIDCondition(&orgID))
|
|
orgNullOrEmpty := database.Or(repo.OrganizationIDCondition(nil), repo.OrganizationIDCondition(gu.Ptr("")))
|
|
onlyInstance := database.And(repo.InstanceIDCondition(instanceID), orgNullOrEmpty)
|
|
|
|
return database.WithCondition(database.Or(instanceAndOrg, onlyInstance))
|
|
}
|