diff options
| author | James Elliott <james-d-elliott@users.noreply.github.com> | 2023-11-30 19:45:24 +1100 | 
|---|---|---|
| committer | James Elliott <james-d-elliott@users.noreply.github.com> | 2024-03-04 20:29:12 +1100 | 
| commit | e4e878f05f8ae1e1784b3ac190459b2d506f796c (patch) | |
| tree | ed8f5b927156300dddff33f3e14bc732803ea405 /internal | |
| parent | 61c30b373f8c5ee14321e82c8d7210aae7d260c3 (diff) | |
build(deps): use go.uber.org/mock
Use the new go.uber.org/mock which is currently maintained.
Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
Diffstat (limited to 'internal')
73 files changed, 1447 insertions, 618 deletions
diff --git a/internal/authentication/file_user_provider_database_mock_test.go b/internal/authentication/file_user_provider_database_mock_test.go index fde875135..dfdda0c63 100644 --- a/internal/authentication/file_user_provider_database_mock_test.go +++ b/internal/authentication/file_user_provider_database_mock_test.go @@ -7,7 +7,7 @@ package authentication  import (  	reflect "reflect" -	gomock "github.com/golang/mock/gomock" +	gomock "go.uber.org/mock/gomock"  )  // MockFileUserDatabase is a mock of FileUserProviderDatabase interface. diff --git a/internal/authentication/file_user_provider_hash_mock_test.go b/internal/authentication/file_user_provider_hash_mock_test.go index fb78a32c4..915cf3cf7 100644 --- a/internal/authentication/file_user_provider_hash_mock_test.go +++ b/internal/authentication/file_user_provider_hash_mock_test.go @@ -8,7 +8,7 @@ import (  	reflect "reflect"  	algorithm "github.com/go-crypt/crypt/algorithm" -	gomock "github.com/golang/mock/gomock" +	gomock "go.uber.org/mock/gomock"  )  // MockHash is a mock of Hash interface. diff --git a/internal/authentication/file_user_provider_test.go b/internal/authentication/file_user_provider_test.go index 0e8d1f8e9..f91501719 100644 --- a/internal/authentication/file_user_provider_test.go +++ b/internal/authentication/file_user_provider_test.go @@ -14,9 +14,9 @@ import (  	"github.com/go-crypt/crypt/algorithm/bcrypt"  	"github.com/go-crypt/crypt/algorithm/pbkdf2"  	"github.com/go-crypt/crypt/algorithm/scrypt" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/configuration/schema"  ) diff --git a/internal/authentication/ldap_client_factory_mock_test.go b/internal/authentication/ldap_client_factory_mock_test.go index d6cb41226..48fb070af 100644 --- a/internal/authentication/ldap_client_factory_mock_test.go +++ b/internal/authentication/ldap_client_factory_mock_test.go @@ -8,7 +8,7 @@ import (  	reflect "reflect"  	v3 "github.com/go-ldap/ldap/v3" -	gomock "github.com/golang/mock/gomock" +	gomock "go.uber.org/mock/gomock"  )  // MockLDAPClientFactory is a mock of LDAPClientFactory interface. diff --git a/internal/authentication/ldap_client_mock_test.go b/internal/authentication/ldap_client_mock_test.go index 5c996114d..6c8346f73 100644 --- a/internal/authentication/ldap_client_mock_test.go +++ b/internal/authentication/ldap_client_mock_test.go @@ -10,7 +10,7 @@ import (  	time "time"  	ldap "github.com/go-ldap/ldap/v3" -	gomock "github.com/golang/mock/gomock" +	gomock "go.uber.org/mock/gomock"  )  // MockLDAPClient is a mock of LDAPClient interface. diff --git a/internal/authentication/ldap_user_provider_test.go b/internal/authentication/ldap_user_provider_test.go index 96c06b31f..0918aee5a 100644 --- a/internal/authentication/ldap_user_provider_test.go +++ b/internal/authentication/ldap_user_provider_test.go @@ -7,9 +7,9 @@ import (  	"time"  	"github.com/go-ldap/ldap/v3" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require" +	"go.uber.org/mock/gomock"  	"golang.org/x/text/encoding/unicode"  	"github.com/authelia/authelia/v4/internal/clock" diff --git a/internal/handlers/const.go b/internal/handlers/const.go index 75e6066a5..c6725fc14 100644 --- a/internal/handlers/const.go +++ b/internal/handlers/const.go @@ -1,6 +1,8 @@  package handlers  import ( +	"errors" +  	"github.com/valyala/fasthttp"  ) @@ -58,15 +60,17 @@ var (  )  const ( -	messageOperationFailed                 = "Operation failed." -	messageAuthenticationFailed            = "Authentication failed. Check your credentials." -	messageUnableToRegisterOneTimePassword = "Unable to set up one-time password." //nolint:gosec -	messageUnableToDeleteOneTimePassword   = "Unable to delete one-time password." //nolint:gosec -	messageUnableToRegisterSecurityKey     = "Unable to register your security key." -	messageSecurityKeyDuplicateName        = "Another one of your security keys is already registered with that display name." -	messageUnableToResetPassword           = "Unable to reset your password." -	messageMFAValidationFailed             = "Authentication failed, please retry later." -	messagePasswordWeak                    = "Your supplied password does not meet the password policy requirements" +	messageOperationFailed                       = "Operation failed." +	messageAuthenticationFailed                  = "Authentication failed. Check your credentials." +	messageUnableToOptionsOneTimePassword        = "Unable to retrieve TOTP registration options."            //nolint:gosec +	messageUnableToRegisterOneTimePassword       = "Unable to set up one-time password."                      //nolint:gosec +	messageUnableToDeleteRegisterOneTimePassword = "Unable to delete one-time password registration session." //nolint:gosec +	messageUnableToDeleteOneTimePassword         = "Unable to delete one-time password." +	messageUnableToRegisterSecurityKey           = "Unable to register your security key." +	messageSecurityKeyDuplicateName              = "Another one of your security keys is already registered with that display name." +	messageUnableToResetPassword                 = "Unable to reset your password." +	messageMFAValidationFailed                   = "Authentication failed, please retry later." +	messagePasswordWeak                          = "Your supplied password does not meet the password policy requirements"  )  const ( @@ -78,7 +82,6 @@ const (  	logFmtActionRegistration   = "registration"  	logFmtErrParseRequestBody     = "Failed to parse %s request body" -	logFmtErrWriteResponseBody    = "Failed to write %s response body for user '%s'"  	logFmtErrRegulationFail       = "Failed to perform %s authentication regulation for user '%s'"  	logFmtErrSessionRegenerate    = "Could not regenerate session during %s authentication for user '%s'"  	logFmtErrSessionReset         = "Could not reset session during %s authentication for user '%s'" @@ -139,3 +142,14 @@ var ldapPasswordComplexityErrors = []string{  	"LDAP Result Code 19 \"Constraint Violation\": Password fails quality checking policy",  	"LDAP Result Code 19 \"Constraint Violation\": Password is too young to change",  } + +const ( +	errStrReqBodyParse        = "error parsing the request body" +	errStrRespBody            = "error occurred writing the response body" +	errStrUserSessionData     = "error occurred retrieving the user session data" +	errStrUserSessionDataSave = "error occurred saving the user session data" +) + +var ( +	errUserAnonymous = errors.New("user is anonymous") +) diff --git a/internal/handlers/func_test.go b/internal/handlers/func_test.go new file mode 100644 index 000000000..9aa8a24de --- /dev/null +++ b/internal/handlers/func_test.go @@ -0,0 +1,51 @@ +package handlers + +import ( +	"testing" + +	"github.com/sirupsen/logrus" +	"github.com/sirupsen/logrus/hooks/test" +	"github.com/stretchr/testify/assert" +	"github.com/stretchr/testify/require" + +	"github.com/authelia/authelia/v4/internal/middlewares" +) + +func AssertLogEntryMessageAndError(t *testing.T, entry *logrus.Entry, message, err string) { +	require.NotNil(t, entry) + +	assert.Equal(t, message, entry.Message) + +	v, ok := entry.Data["error"] + +	if err == "" { +		assert.False(t, ok) +		assert.Nil(t, v) +	} else { +		assert.True(t, ok) +		require.NotNil(t, v) + +		theErr, ok := v.(error) +		assert.True(t, ok) +		require.NotNil(t, theErr) + +		assert.EqualError(t, theErr, err) +	} +} + +func MustGetLogLastSeq(t *testing.T, hook *test.Hook, seq int) *logrus.Entry { +	require.Greater(t, len(hook.Entries), seq) + +	return &hook.Entries[len(hook.Entries)-1-seq] +} + +//nolint:unparam +func getStepTOTP(ctx *middlewares.AutheliaCtx, period int) uint64 { +	step := ctx.Clock.Now().Unix() + +	if period < 0 { +		period = ctx.Configuration.TOTP.DefaultPeriod +	} + +	return uint64(int(step) / period) +} diff --git a/internal/handlers/handler_authz_impl_legacy_test.go b/internal/handlers/handler_authz_impl_legacy_test.go index 30949e387..f7adfbff7 100644 --- a/internal/handlers/handler_authz_impl_legacy_test.go +++ b/internal/handlers/handler_authz_impl_legacy_test.go @@ -7,10 +7,10 @@ import (  	"strings"  	"testing" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/suite"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/authentication"  	"github.com/authelia/authelia/v4/internal/authorization" diff --git a/internal/handlers/handler_authz_test.go b/internal/handlers/handler_authz_test.go index 19d670400..368091977 100644 --- a/internal/handlers/handler_authz_test.go +++ b/internal/handlers/handler_authz_test.go @@ -6,10 +6,10 @@ import (  	"testing"  	"time" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/suite"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/authentication"  	"github.com/authelia/authelia/v4/internal/configuration/schema" diff --git a/internal/handlers/handler_firstfactor_test.go b/internal/handlers/handler_firstfactor_test.go index 3c046ea4a..6d17a71b9 100644 --- a/internal/handlers/handler_firstfactor_test.go +++ b/internal/handlers/handler_firstfactor_test.go @@ -5,12 +5,10 @@ import (  	"net/url"  	"testing" -	"github.com/golang/mock/gomock" -	"github.com/sirupsen/logrus"  	"github.com/stretchr/testify/assert" -	"github.com/stretchr/testify/require"  	"github.com/stretchr/testify/suite"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/authentication"  	"github.com/authelia/authelia/v4/internal/authorization" @@ -464,25 +462,3 @@ func TestFirstFactorSuite(t *testing.T) {  	suite.Run(t, new(FirstFactorSuite))  	suite.Run(t, new(FirstFactorRedirectionSuite))  } - -func AssertLogEntryMessageAndError(t *testing.T, entry *logrus.Entry, message, err string) { -	require.NotNil(t, entry) - -	assert.Equal(t, message, entry.Message) - -	v, ok := entry.Data["error"] - -	if err == "" { -		assert.False(t, ok) -		assert.Nil(t, v) -	} else { -		assert.True(t, ok) -		require.NotNil(t, v) - -		theErr, ok := v.(error) -		assert.True(t, ok) -		require.NotNil(t, theErr) - -		assert.EqualError(t, theErr, err) -	} -} diff --git a/internal/handlers/handler_register_duo_device_test.go b/internal/handlers/handler_register_duo_device_test.go index 84988c602..ee26511df 100644 --- a/internal/handlers/handler_register_duo_device_test.go +++ b/internal/handlers/handler_register_duo_device_test.go @@ -5,11 +5,11 @@ import (  	"net/url"  	"testing" -	"github.com/golang/mock/gomock"  	"github.com/sirupsen/logrus"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/suite"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/duo"  	"github.com/authelia/authelia/v4/internal/mocks" diff --git a/internal/handlers/handler_register_totp.go b/internal/handlers/handler_register_totp.go index 9eee6b219..d5830e010 100644 --- a/internal/handlers/handler_register_totp.go +++ b/internal/handlers/handler_register_totp.go @@ -2,6 +2,7 @@ package handlers  import (  	"encoding/json" +	"fmt"  	"time"  	"github.com/valyala/fasthttp" @@ -15,8 +16,34 @@ import (  // TOTPRegisterGET returns the registration specific options.  func TOTPRegisterGET(ctx *middlewares.AutheliaCtx) { -	if err := ctx.SetJSONBody(ctx.Providers.TOTP.Options()); err != nil { -		ctx.Logger.Errorf("Unable to set TOTP options response in body: %s", err) +	var ( +		userSession session.UserSession +		err         error +	) + +	if userSession, err = ctx.GetSession(); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred retrieving TOTP registration options: %s", errStrUserSessionData) + +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToOptionsOneTimePassword) + +		return +	} + +	if userSession.IsAnonymous() { +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred retrieving TOTP registration options") + +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToOptionsOneTimePassword) + +		return +	} + +	if err = ctx.SetJSONBody(ctx.Providers.TOTP.Options()); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred retrieving TOTP registration options for user '%s': %s", userSession.Username, errStrRespBody) + +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToOptionsOneTimePassword)  	}  } @@ -29,28 +56,28 @@ func TOTPRegisterPUT(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred retrieving session for %s registration", regulation.AuthTypeTOTP) +		ctx.Logger.WithError(err).Errorf("Error occurred generating a TOTP registration session: %s", errStrUserSessionData) -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return  	}  	if userSession.IsAnonymous() { -		ctx.Logger.Errorf("Error occurred handling request: anonymous user attempted %s registration", regulation.AuthTypeTOTP) +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred generating a TOTP registration session") -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return  	}  	if err = json.Unmarshal(ctx.PostBody(), &bodyJSON); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred unmarshaling body %s registration", regulation.AuthTypeTOTP) +		ctx.Logger.WithError(err).Errorf("Error occurred generating a TOTP registration session for user '%s': %s", userSession.Username, errStrReqBodyParse) -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusBadRequest) +		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return  	} @@ -60,10 +87,10 @@ func TOTPRegisterPUT(ctx *middlewares.AutheliaCtx) {  	if !utils.IsStringInSlice(bodyJSON.Algorithm, opts.Algorithms) ||  		!utils.IsIntegerInSlice(bodyJSON.Period, opts.Periods) ||  		!utils.IsIntegerInSlice(bodyJSON.Length, opts.Lengths) { -		ctx.Logger.Errorf("Validation failed for %s registration because the input options were not permitted by the configuration", regulation.AuthTypeTOTP) +		ctx.Logger.WithError(fmt.Errorf("the algorithm '%s', period '%d', or length '%d' was not permitted by configured policy", bodyJSON.Algorithm, bodyJSON.Period, bodyJSON.Length)).Errorf("Error occurred generating a TOTP registration session for user '%s': error occurred validating registration options selection", userSession.Username) -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusBadRequest) +		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return  	} @@ -71,8 +98,9 @@ func TOTPRegisterPUT(ctx *middlewares.AutheliaCtx) {  	var config *model.TOTPConfiguration  	if config, err = ctx.Providers.TOTP.GenerateCustom(userSession.Username, bodyJSON.Algorithm, "", uint(bodyJSON.Length), uint(bodyJSON.Period), 0); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred generating TOTP configuration") +		ctx.Logger.WithError(err).Errorf("Error occurred generating a TOTP registration session for user '%s': error generating TOTP configuration", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return @@ -88,8 +116,9 @@ func TOTPRegisterPUT(ctx *middlewares.AutheliaCtx) {  	}  	if err = ctx.SaveSession(userSession); err != nil { -		ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "pending TOTP configuration", regulation.AuthTypeTOTP, logFmtActionRegistration, userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred generating a TOTP registration session for user '%s': %s", userSession.Username, errStrUserSessionDataSave) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return @@ -101,7 +130,10 @@ func TOTPRegisterPUT(ctx *middlewares.AutheliaCtx) {  	}  	if err = ctx.SetJSONBody(response); err != nil { -		ctx.Logger.WithError(err).Errorf("Unable to set TOTP key response in body") +		ctx.Logger.WithError(err).Errorf("Error occurred generating a TOTP registration session for user '%s': %s", userSession.Username, errStrRespBody) + +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  	}  } @@ -111,50 +143,51 @@ func TOTPRegisterPOST(ctx *middlewares.AutheliaCtx) {  		userSession session.UserSession  		bodyJSON    bodyRegisterFinishTOTP  		valid       bool +		step        uint64  		err         error  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred retrieving session for %s registration", regulation.AuthTypeTOTP) +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP registration session: %s", errStrUserSessionData) -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return  	}  	if userSession.IsAnonymous() { -		ctx.Logger.Errorf("Error occurred handling request: anonymous user attempted %s registration", regulation.AuthTypeTOTP) +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred validating a TOTP registration session") -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return  	} -	if userSession.TOTP == nil { -		ctx.Logger.Errorf("Error occurred during %s registration: the user did not initiate a registration on their current session", regulation.AuthTypeTOTP) +	if err = json.Unmarshal(ctx.PostBody(), &bodyJSON); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP registration session for user '%s': %s", userSession.Username, errStrReqBodyParse) +		ctx.SetStatusCode(fasthttp.StatusBadRequest)  		ctx.SetJSONError(messageUnableToRegisterOneTimePassword) -		ctx.SetStatusCode(fasthttp.StatusForbidden)  		return  	} -	if ctx.Clock.Now().After(userSession.TOTP.Expires) { -		ctx.Logger.Errorf("Error occurred during %s registration: the registration is expired", regulation.AuthTypeTOTP) +	if userSession.TOTP == nil { +		ctx.Logger.Errorf("Error occurred validating a TOTP registration session for user '%s': the user did not initiate a registration session on their current session", userSession.Username) -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return  	} -	if err = json.Unmarshal(ctx.PostBody(), &bodyJSON); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred unmarshaling body %s registration", regulation.AuthTypeTOTP) +	if ctx.Clock.Now().After(userSession.TOTP.Expires) { +		ctx.Logger.WithError(fmt.Errorf("the registration session is expired")).Errorf("Error occurred validating a TOTP registration session for user '%s': error occurred validating the session", userSession.Username) -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return  	} @@ -169,25 +202,37 @@ func TOTPRegisterPOST(ctx *middlewares.AutheliaCtx) {  		Secret:    []byte(userSession.TOTP.Secret),  	} -	if valid, err = ctx.Providers.TOTP.Validate(bodyJSON.Token, &config); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred validating %s registration", regulation.AuthTypeTOTP) +	if valid, step, err = ctx.Providers.TOTP.Validate(bodyJSON.Token, &config); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP registration session for user '%s': error occurred validating the user input against the session", userSession.Username) -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return -	} else if !valid { -		ctx.Logger.Errorf("Error occurred validating %s registration", regulation.AuthTypeTOTP) +	} + +	if !valid { +		ctx.Logger.WithError(fmt.Errorf("user input did not match any expected value")).Errorf("Error occurred validating a TOTP registration session for user '%s'", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterOneTimePassword) + +		return +	} + +	if err = ctx.Providers.StorageProvider.SaveTOTPHistory(ctx, userSession.Username, step*uint64(config.Period)); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP registration session for user '%s': error occurred saving the TOTP history to the storage backend", userSession.Username) +  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return  	}  	if err = ctx.Providers.StorageProvider.SaveTOTPConfiguration(ctx, config); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred saving %s registration", regulation.AuthTypeTOTP) +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP registration session for user '%s': error occurred saving the TOTP configuration to the storage backend", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return @@ -196,8 +241,9 @@ func TOTPRegisterPOST(ctx *middlewares.AutheliaCtx) {  	userSession.TOTP = nil  	if err = ctx.SaveSession(userSession); err != nil { -		ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "completed TOTP configuration", regulation.AuthTypeTOTP, logFmtActionRegistration, userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP registration session for user '%s': %s", userSession.Username, errStrUserSessionDataSave) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		return @@ -216,19 +262,19 @@ func TOTPRegisterDELETE(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred retrieving session for %s registration cancel", regulation.AuthTypeTOTP) +		ctx.Logger.WithError(err).Errorf("Error occurred deleting a TOTP registration session: %s", errStrUserSessionData) -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToDeleteRegisterOneTimePassword)  		return  	}  	if userSession.IsAnonymous() { -		ctx.Logger.Errorf("Error occurred handling request: anonymous user attempted %s registration", regulation.AuthTypeTOTP) +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred deleting a TOTP registration session") -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToDeleteRegisterOneTimePassword)  		return  	} @@ -244,7 +290,8 @@ func TOTPRegisterDELETE(ctx *middlewares.AutheliaCtx) {  	if err = ctx.SaveSession(userSession); err != nil {  		ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "deleted pending TOTP configuration", regulation.AuthTypeTOTP, logFmtActionRegistration, userSession.Username) -		ctx.SetJSONError(messageUnableToRegisterOneTimePassword) +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToDeleteRegisterOneTimePassword)  		return  	} @@ -260,35 +307,36 @@ func TOTPConfigurationDELETE(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred retrieving session for %s configuration delete operation", regulation.AuthTypeTOTP) +		ctx.Logger.WithError(err).Errorf("Error occurred deleting a TOTP configuration: %s", errStrUserSessionData) -		ctx.SetJSONError(messageUnableToDeleteOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToDeleteOneTimePassword)  		return  	}  	if userSession.IsAnonymous() { -		ctx.Logger.Errorf("Error occurred handling request: anonymous user attempted %s removal", regulation.AuthTypeTOTP) +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred deleting a TOTP configuration") -		ctx.SetJSONError(messageUnableToDeleteOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToDeleteOneTimePassword)  		return  	}  	if _, err = ctx.Providers.StorageProvider.LoadTOTPConfiguration(ctx, userSession.Username); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred loading from storage for %s configuration delete operation for user '%s'", regulation.AuthTypeTOTP, userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred deleting a TOTP configuration for user '%s': error occurred loading configuration from the storage backend", userSession.Username) -		ctx.SetJSONError(messageUnableToDeleteOneTimePassword)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageUnableToDeleteOneTimePassword)  		return  	}  	if err = ctx.Providers.StorageProvider.DeleteTOTPConfiguration(ctx, userSession.Username); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred deleting from storage for %s configuration delete operation for user '%s'", regulation.AuthTypeTOTP, userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred deleting a TOTP configuration for user '%s': error occurred deleting configuration from the storage backend", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToDeleteOneTimePassword)  		return diff --git a/internal/handlers/handler_register_totp_test.go b/internal/handlers/handler_register_totp_test.go index ffd7d1c79..c5ec482ac 100644 --- a/internal/handlers/handler_register_totp_test.go +++ b/internal/handlers/handler_register_totp_test.go @@ -6,10 +6,10 @@ import (  	"testing"  	"time" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/authentication"  	"github.com/authelia/authelia/v4/internal/configuration/schema" @@ -23,20 +23,58 @@ func TestShouldReturnTOTPRegisterOptions(t *testing.T) {  	testCases := []struct {  		name           string  		config         schema.TOTP +		setup          func(t *testing.T, mock *mocks.MockAutheliaCtx)  		expected       string  		expectedStatus int +		expectedf      func(t *testing.T, mock *mocks.MockAutheliaCtx)  	}{  		{  			"ShouldHandleDefaults",  			schema.DefaultTOTPConfiguration, -			"{\"status\":\"OK\",\"data\":{\"algorithm\":\"SHA1\",\"algorithms\":[\"SHA1\"],\"length\":6,\"lengths\":[6],\"period\":30,\"periods\":[30]}}", +			func(t *testing.T, mock *mocks.MockAutheliaCtx) { +				us, err := mock.Ctx.GetSession() + +				require.NoError(t, err) + +				us.Username = testUsername +				us.AuthenticationLevel = authentication.OneFactor + +				require.NoError(t, mock.Ctx.SaveSession(us)) + +				mock.TOTPMock.EXPECT().Options().Return(*totp.NewTOTPOptionsFromSchema(mock.Ctx.Configuration.TOTP)) +			}, +			`{"status":"OK","data":{"algorithm":"SHA1","algorithms":["SHA1"],"length":6,"lengths":[6],"period":30,"periods":[30]}}`,  			fasthttp.StatusOK, +			nil,  		},  		{  			"ShouldHandleCustom",  			schema.TOTP{DefaultAlgorithm: "SHA256", AllowedAlgorithms: []string{"SHA1", "SHA256"}, DefaultDigits: 6, AllowedDigits: []int{6, 8}, DefaultPeriod: 30, AllowedPeriods: []int{30, 60, 90}}, -			"{\"status\":\"OK\",\"data\":{\"algorithm\":\"SHA256\",\"algorithms\":[\"SHA1\",\"SHA256\"],\"length\":6,\"lengths\":[6,8],\"period\":30,\"periods\":[30,60,90]}}", +			func(t *testing.T, mock *mocks.MockAutheliaCtx) { +				us, err := mock.Ctx.GetSession() + +				require.NoError(t, err) + +				us.Username = testUsername +				us.AuthenticationLevel = authentication.OneFactor + +				require.NoError(t, mock.Ctx.SaveSession(us)) + +				mock.TOTPMock.EXPECT().Options().Return(*totp.NewTOTPOptionsFromSchema(mock.Ctx.Configuration.TOTP)) +			}, +			`{"status":"OK","data":{"algorithm":"SHA256","algorithms":["SHA1","SHA256"],"length":6,"lengths":[6,8],"period":30,"periods":[30,60,90]}}`,  			fasthttp.StatusOK, +			nil, +		}, +		{ +			"ShouldHandleAnonymous", +			schema.TOTP{DefaultAlgorithm: "SHA256", AllowedAlgorithms: []string{"SHA1", "SHA256"}, DefaultDigits: 6, AllowedDigits: []int{6, 8}, DefaultPeriod: 30, AllowedPeriods: []int{30, 60, 90}}, +			nil, +			`{"status":"KO","message":"Unable to retrieve TOTP registration options."}`, +			fasthttp.StatusForbidden, +			func(t *testing.T, mock *mocks.MockAutheliaCtx) { +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred retrieving TOTP registration options", "user is anonymous") +			},  		},  	} @@ -48,12 +86,18 @@ func TestShouldReturnTOTPRegisterOptions(t *testing.T) {  			mock.Ctx.Configuration.TOTP = tc.config -			mock.TOTPMock.EXPECT().Options().Return(*totp.NewTOTPOptionsFromSchema(mock.Ctx.Configuration.TOTP)) +			if tc.setup != nil { +				tc.setup(t, mock) +			}  			TOTPRegisterGET(mock.Ctx)  			assert.Equal(t, tc.expectedStatus, mock.Ctx.Response.StatusCode())  			assert.Equal(t, tc.expected, string(mock.Ctx.Response.Body())) + +			if tc.expectedf != nil { +				tc.expectedf(t, mock) +			}  		})  	}  } @@ -114,7 +158,7 @@ func TestTOTPRegisterPUT(t *testing.T) {  			`{"status":"KO","message":"Unable to set up one-time password."}`,  			fasthttp.StatusBadRequest,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Validation failed for TOTP registration because the input options were not permitted by the configuration", "") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred generating a TOTP registration session for user 'john': error occurred validating registration options selection", "the algorithm 'SHA1', period '30', or length '20' was not permitted by configured policy")  			},  		},  		{ @@ -125,7 +169,7 @@ func TestTOTPRegisterPUT(t *testing.T) {  			`{"status":"KO","message":"Unable to set up one-time password."}`,  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred handling request: anonymous user attempted TOTP registration", "") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred generating a TOTP registration session", "user is anonymous")  			},  		},  		{ @@ -145,7 +189,7 @@ func TestTOTPRegisterPUT(t *testing.T) {  			`{"status":"KO","message":"Unable to set up one-time password."}`,  			fasthttp.StatusBadRequest,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred unmarshaling body TOTP registration", "invalid character 'S' after object key") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred generating a TOTP registration session for user 'john': error parsing the request body", "invalid character 'S' after object key")  			},  		},  		{ @@ -179,9 +223,9 @@ func TestTOTPRegisterPUT(t *testing.T) {  				)  			},  			`{"status":"KO","message":"Unable to set up one-time password."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred generating TOTP configuration", "no issuer") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred generating a TOTP registration session for user 'john': error generating TOTP configuration", "no issuer")  			},  		},  	} @@ -222,7 +266,7 @@ func TestTOTPRegisterDELETE(t *testing.T) {  		{  			"ShouldFailAnonymous",  			nil, -			`{"status":"KO","message":"Unable to set up one-time password."}`, +			`{"status":"KO","message":"Unable to delete one-time password registration session."}`,  			fasthttp.StatusForbidden,  			nil,  		}, @@ -317,7 +361,7 @@ func TestTOTPRegisterPOST(t *testing.T) {  			`{"status":"KO","message":"Unable to set up one-time password."}`,  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred during TOTP registration: the user did not initiate a registration on their current session", "") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a TOTP registration session for user 'john': the user did not initiate a registration session on their current session", "")  			},  		},  		{ @@ -343,9 +387,9 @@ func TestTOTPRegisterPOST(t *testing.T) {  				require.NoError(t, mock.Ctx.SaveSession(us))  			},  			`{"status":"KO","message":"Unable to set up one-time password."}`, -			fasthttp.StatusForbidden, +			fasthttp.StatusBadRequest,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred unmarshaling body TOTP registration", "invalid character '1' after object key:value pair") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a TOTP registration session for user 'john': error parsing the request body", "invalid character '1' after object key:value pair")  			},  		},  		{ @@ -373,7 +417,7 @@ func TestTOTPRegisterPOST(t *testing.T) {  			`{"status":"KO","message":"Unable to set up one-time password."}`,  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred during TOTP registration: the registration is expired", "") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a TOTP registration session for user 'john': error occurred validating the session", "the registration session is expired")  			},  		},  		{ @@ -399,7 +443,7 @@ func TestTOTPRegisterPOST(t *testing.T) {  			`{"status":"KO","message":"Unable to set up one-time password."}`,  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred handling request: anonymous user attempted TOTP registration", "") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a TOTP registration session", "user is anonymous")  			},  		},  		{ @@ -429,13 +473,13 @@ func TestTOTPRegisterPOST(t *testing.T) {  						Validate(  							"012345",  							&model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}, -						).Return(false, nil), +						).Return(false, uint64(0), nil),  				)  			},  			`{"status":"KO","message":"Unable to set up one-time password."}`,  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating TOTP registration", "") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a TOTP registration session for user 'john'", "user input did not match any expected value")  			},  		},  		{ @@ -465,13 +509,13 @@ func TestTOTPRegisterPOST(t *testing.T) {  						Validate(  							"012345",  							&model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}, -						).Return(false, fmt.Errorf("pink staple")), +						).Return(false, uint64(0), fmt.Errorf("pink staple")),  				)  			},  			`{"status":"KO","message":"Unable to set up one-time password."}`,  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating TOTP registration", "pink staple") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a TOTP registration session for user 'john': error occurred validating the user input against the session", "pink staple")  			},  		},  		{ @@ -501,7 +545,11 @@ func TestTOTPRegisterPOST(t *testing.T) {  						Validate(  							"012345",  							&model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}, -						).Return(true, nil), +						).Return(true, getStepTOTP(mock.Ctx, -1), nil), +					mock.StorageMock. +						EXPECT(). +						SaveTOTPHistory(mock.Ctx, "john", uint64(1701295890)). +						Return(nil),  					mock.StorageMock.EXPECT().  						SaveTOTPConfiguration(mock.Ctx, model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}).Return(nil),  					mock.UserProviderMock.EXPECT().GetDetails(testUsername).Return(&authentication.UserDetails{Username: testUsername, DisplayName: testDisplayName, Emails: []string{"john@example.com"}}, nil), @@ -539,7 +587,8 @@ func TestTOTPRegisterPOST(t *testing.T) {  						Validate(  							"012345",  							&model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}, -						).Return(true, nil), +						).Return(true, getStepTOTP(mock.Ctx, -1), nil), +					mock.StorageMock.EXPECT().SaveTOTPHistory(mock.Ctx, "john", uint64(1701295890)).Return(nil),  					mock.StorageMock.EXPECT().  						SaveTOTPConfiguration(mock.Ctx, model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}).Return(nil),  					mock.UserProviderMock.EXPECT().GetDetails(testUsername).Return(&authentication.UserDetails{Username: testUsername, DisplayName: testDisplayName, Emails: []string{"john@example.com"}}, nil), @@ -578,8 +627,9 @@ func TestTOTPRegisterPOST(t *testing.T) {  					mock.TOTPMock.EXPECT().  						Validate(  							"012345", -							&model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}, -						).Return(true, nil), +							&model.TOTPConfiguration{CreatedAt: mock.Ctx.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}, +						).Return(true, getStepTOTP(mock.Ctx, -1), nil), +					mock.StorageMock.EXPECT().SaveTOTPHistory(mock.Ctx, "john", uint64(1701295890)).Return(nil),  					mock.StorageMock.EXPECT().  						SaveTOTPConfiguration(mock.Ctx, model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}).Return(nil),  					mock.UserProviderMock.EXPECT().GetDetails(testUsername).Return(&authentication.UserDetails{Username: testUsername, DisplayName: testDisplayName}, nil), @@ -617,8 +667,9 @@ func TestTOTPRegisterPOST(t *testing.T) {  					mock.TOTPMock.EXPECT().  						Validate(  							"012345", -							&model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}, -						).Return(true, nil), +							&model.TOTPConfiguration{CreatedAt: mock.Ctx.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}, +						).Return(true, getStepTOTP(mock.Ctx, -1), nil), +					mock.StorageMock.EXPECT().SaveTOTPHistory(mock.Ctx, "john", uint64(1701295890)).Return(nil),  					mock.StorageMock.EXPECT().  						SaveTOTPConfiguration(mock.Ctx, model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}).Return(nil),  					mock.UserProviderMock.EXPECT().GetDetails(testUsername).Return(nil, fmt.Errorf("lookup failure")), @@ -657,15 +708,16 @@ func TestTOTPRegisterPOST(t *testing.T) {  						Validate(  							"012345",  							&model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}, -						).Return(true, nil), +						).Return(true, getStepTOTP(mock.Ctx, -1), nil), +					mock.StorageMock.EXPECT().SaveTOTPHistory(mock.Ctx, "john", uint64(1701295890)).Return(nil),  					mock.StorageMock.EXPECT().  						SaveTOTPConfiguration(mock.Ctx, model.TOTPConfiguration{CreatedAt: mock.Clock.Now(), Username: testUsername, Issuer: "abc", Algorithm: "SHA1", Period: 30, Digits: 6, Secret: []byte(testBASE32TOTPSecret)}).Return(fmt.Errorf("failed to connect")),  				)  			},  			`{"status":"KO","message":"Unable to set up one-time password."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred saving TOTP registration", "failed to connect") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a TOTP registration session for user 'john': error occurred saving the TOTP configuration to the storage backend", "failed to connect")  			},  		},  	} @@ -678,7 +730,7 @@ func TestTOTPRegisterPOST(t *testing.T) {  			mock.Ctx.Configuration.TOTP = tc.config  			mock.Ctx.Request.SetBodyString(tc.have) -			mock.Clock.Set(time.Unix(0, 0)) +			mock.Clock.Set(time.Unix(1701295903, 0))  			mock.Ctx.Clock = &mock.Clock  			if tc.setup != nil { @@ -814,9 +866,9 @@ func TestTOTPConfigurationDELETE(t *testing.T) {  				)  			},  			`{"status":"KO","message":"Unable to delete one-time password."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred deleting from storage for TOTP configuration delete operation for user 'john'", "not a sql") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred deleting a TOTP configuration for user 'john': error occurred deleting configuration from the storage backend", "not a sql")  			},  		},  		{ @@ -835,13 +887,16 @@ func TestTOTPConfigurationDELETE(t *testing.T) {  				require.NoError(t, mock.Ctx.SaveSession(us))  				gomock.InOrder( -					mock.StorageMock.EXPECT().LoadTOTPConfiguration(mock.Ctx, testUsername).Return(nil, fmt.Errorf("not found")), +					mock.StorageMock. +						EXPECT(). +						LoadTOTPConfiguration(mock.Ctx, testUsername). +						Return(nil, fmt.Errorf("not found")),  				)  			},  			`{"status":"KO","message":"Unable to delete one-time password."}`,  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred loading from storage for TOTP configuration delete operation for user 'john'", "not found") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred deleting a TOTP configuration for user 'john': error occurred loading configuration from the storage backend", "not found")  			},  		},  	} diff --git a/internal/handlers/handler_register_webauthn.go b/internal/handlers/handler_register_webauthn.go index 21c94090d..84c187384 100644 --- a/internal/handlers/handler_register_webauthn.go +++ b/internal/handlers/handler_register_webauthn.go @@ -27,7 +27,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred generating a WebAuthn registration challenge: error occurred retrieving the user session data") +		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn registration challenge: %s", errStrUserSessionData)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterSecurityKey) @@ -36,7 +36,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {  	}  	if userSession.IsAnonymous() { -		ctx.Logger.WithError(fmt.Errorf("user is anonymous")).Error("Error occurred generating a WebAuthn registration challenge") +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred generating a WebAuthn registration challenge")  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterSecurityKey) @@ -45,7 +45,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {  	}  	if err = json.Unmarshal(ctx.PostBody(), &bodyJSON); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn registration challenge for user '%s': error parsing the request body", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn registration challenge for user '%s': %s", userSession.Username, errStrReqBodyParse)  		ctx.SetStatusCode(fasthttp.StatusBadRequest)  		ctx.SetJSONError(messageUnableToRegisterSecurityKey) @@ -72,7 +72,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {  	}  	if user, err = handleGetWebAuthnUserByRPID(ctx, userSession.Username, userSession.DisplayName, w.Config.RPID); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn registration challenge for user '%s': error occurred retrieving the WebAuthn user configuration from storage", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn registration challenge for user '%s': error occurred retrieving the WebAuthn user configuration from the storage backend", userSession.Username)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterSecurityKey) @@ -117,7 +117,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {  	userSession.WebAuthn = &data  	if err = ctx.SaveSession(userSession); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred generating a WebAuthn registration challenge: error occurred saving the user session data") +		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn registration challenge for user '%s': %s", userSession.Username, errStrUserSessionDataSave)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterSecurityKey) @@ -126,7 +126,7 @@ func WebAuthnRegistrationPUT(ctx *middlewares.AutheliaCtx) {  	}  	if err = ctx.SetJSONBody(creation); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn registration challenge for user '%s': error occurred writing the response body", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn registration challenge for user '%s': %s", userSession.Username, errStrRespBody)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterSecurityKey) @@ -150,7 +150,7 @@ func WebAuthnRegistrationPOST(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred validating a WebAuthn registration challenge: error occurred retrieving the user session data") +		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn registration challenge: %s", errStrUserSessionData)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterSecurityKey) @@ -159,7 +159,7 @@ func WebAuthnRegistrationPOST(ctx *middlewares.AutheliaCtx) {  	}  	if userSession.IsAnonymous() { -		ctx.Logger.WithError(fmt.Errorf("user is anonymous")).Error("Error occurred validating a WebAuthn registration challenge") +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred validating a WebAuthn registration challenge")  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterSecurityKey) @@ -180,7 +180,7 @@ func WebAuthnRegistrationPOST(ctx *middlewares.AutheliaCtx) {  		userSession.WebAuthn = nil  		if err = ctx.SaveSession(userSession); err != nil { -			ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn registration challenge for user '%s': error occurred saving the user session data", userSession.Username) +			ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn registration challenge for user '%s': %s", userSession.Username, errStrUserSessionDataSave)  		}  	}() @@ -189,9 +189,9 @@ func WebAuthnRegistrationPOST(ctx *middlewares.AutheliaCtx) {  		switch {  		case errors.As(err, &e): -			ctx.Logger.WithError(fmt.Errorf("%w: %s", e, e.DevInfo)).Errorf("Error occurred validating a WebAuthn registration challenge for user '%s': error parsing the request body", userSession.Username) +			ctx.Logger.WithError(fmt.Errorf("%w: %s", e, e.DevInfo)).Errorf("Error occurred validating a WebAuthn registration challenge for user '%s': %s", userSession.Username, errStrReqBodyParse)  		default: -			ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn registration challenge for user '%s': error parsing the request body", userSession.Username) +			ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn registration challenge for user '%s': %s", userSession.Username, errStrReqBodyParse)  		}  		ctx.SetStatusCode(fasthttp.StatusBadRequest) @@ -210,7 +210,7 @@ func WebAuthnRegistrationPOST(ctx *middlewares.AutheliaCtx) {  	}  	if user, err = handleGetWebAuthnUserByRPID(ctx, userSession.Username, userSession.DisplayName, w.Config.RPID); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn registration challenge for user '%s': error occurred retrieving the WebAuthn user configuration from storage", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn registration challenge for user '%s': error occurred retrieving the WebAuthn user configuration from the storage backend", userSession.Username)  		ctx.SetStatusCode(fasthttp.StatusBadRequest)  		ctx.SetJSONError(messageUnableToRegisterSecurityKey) @@ -239,7 +239,7 @@ func WebAuthnRegistrationPOST(ctx *middlewares.AutheliaCtx) {  	credential.Discoverable = handleWebAuthnCredentialCreationIsDiscoverable(ctx, response)  	if err = ctx.Providers.StorageProvider.SaveWebAuthnCredential(ctx, credential); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn registration challenge for user '%s': error occurred saving the registration to storage", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn registration challenge for user '%s': error occurred saving the credential to the storage backend", userSession.Username)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterSecurityKey) @@ -261,7 +261,7 @@ func WebAuthnRegistrationDELETE(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred deleting a WebAuthn registration challenge: error occurred retrieving the user session data") +		ctx.Logger.WithError(err).Errorf("Error occurred deleting a WebAuthn registration challenge: %s", errStrUserSessionData)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed) @@ -270,7 +270,7 @@ func WebAuthnRegistrationDELETE(ctx *middlewares.AutheliaCtx) {  	}  	if userSession.IsAnonymous() { -		ctx.Logger.WithError(fmt.Errorf("user is anonymous")).Error("Error occurred deleting a WebAuthn registration challenge") +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred deleting a WebAuthn registration challenge")  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed) @@ -282,7 +282,7 @@ func WebAuthnRegistrationDELETE(ctx *middlewares.AutheliaCtx) {  		userSession.WebAuthn = nil  		if err = ctx.SaveSession(userSession); err != nil { -			ctx.Logger.WithError(err).Error("Error occurred deleting a WebAuthn registration challenge: error occurred saving the user session data") +			ctx.Logger.WithError(err).Errorf("Error occurred deleting a WebAuthn registration challenge for user '%s': %s", userSession.Username, errStrUserSessionDataSave)  			ctx.SetStatusCode(fasthttp.StatusForbidden)  			ctx.SetJSONError(messageOperationFailed) diff --git a/internal/handlers/handler_register_webauthn_test.go b/internal/handlers/handler_register_webauthn_test.go index e29432497..0ceb796d5 100644 --- a/internal/handlers/handler_register_webauthn_test.go +++ b/internal/handlers/handler_register_webauthn_test.go @@ -10,10 +10,10 @@ import (  	"time"  	"github.com/go-webauthn/webauthn/webauthn" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/authentication"  	"github.com/authelia/authelia/v4/internal/configuration/schema" @@ -162,7 +162,7 @@ func TestWebAuthnRegistrationPUT(t *testing.T) {  			regexp.MustCompile(`^\{"status":"KO","message":"Unable to register your security key."}$`),  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred generating a WebAuthn registration challenge for user 'john': error occurred retrieving the WebAuthn user configuration from storage", "database closed") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred generating a WebAuthn registration challenge for user 'john': error occurred retrieving the WebAuthn user configuration from the storage backend", "database closed")  			},  		},  		{ @@ -189,7 +189,7 @@ func TestWebAuthnRegistrationPUT(t *testing.T) {  			regexp.MustCompile(`^\{"status":"KO","message":"Unable to register your security key."}$`),  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred generating a WebAuthn registration challenge for user 'john': error occurred retrieving the WebAuthn user configuration from storage", "no user x") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred generating a WebAuthn registration challenge for user 'john': error occurred retrieving the WebAuthn user configuration from the storage backend", "no user x")  			},  		},  		{ @@ -574,7 +574,7 @@ func TestWebAuthnRegistrationPOST(t *testing.T) {  				assert.Nil(t, us.WebAuthn) -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn registration challenge for user 'john': error occurred saving the registration to storage", "disk full") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn registration challenge for user 'john': error occurred saving the credential to the storage backend", "disk full")  			},  		},  		{ @@ -620,7 +620,7 @@ func TestWebAuthnRegistrationPOST(t *testing.T) {  				assert.Nil(t, us.WebAuthn) -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn registration challenge for user 'john': error occurred retrieving the WebAuthn user configuration from storage", "no dice") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn registration challenge for user 'john': error occurred retrieving the WebAuthn user configuration from the storage backend", "no dice")  			},  		},  		{ @@ -662,7 +662,7 @@ func TestWebAuthnRegistrationPOST(t *testing.T) {  				assert.Nil(t, us.WebAuthn) -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn registration challenge for user 'john': error occurred retrieving the WebAuthn user configuration from storage", "not enough cowbell") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn registration challenge for user 'john': error occurred retrieving the WebAuthn user configuration from the storage backend", "not enough cowbell")  			},  		},  		{ diff --git a/internal/handlers/handler_session_elevation.go b/internal/handlers/handler_session_elevation.go index 63b2dd5ad..e58338167 100755 --- a/internal/handlers/handler_session_elevation.go +++ b/internal/handlers/handler_session_elevation.go @@ -29,7 +29,7 @@ func UserSessionElevationGET(ctx *middlewares.AutheliaCtx) {  	response := &bodyGETUserSessionElevate{}  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred retrieving user session elevation state: error occurred retrieving the user session data") +		ctx.Logger.WithError(err).Errorf("Error occurred retrieving user session elevation state: %s", errStrUserSessionData)  		ctx.SetJSONError(messageOperationFailed)  		ctx.SetStatusCode(fasthttp.StatusForbidden) @@ -38,7 +38,7 @@ func UserSessionElevationGET(ctx *middlewares.AutheliaCtx) {  	}  	if userSession.IsAnonymous() { -		ctx.Logger.WithError(fmt.Errorf("user is anonymous")).Error("Error occurred retrieving user session elevation state") +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred retrieving user session elevation state")  		ctx.SetJSONError(messageOperationFailed)  		ctx.SetStatusCode(fasthttp.StatusForbidden) @@ -105,7 +105,7 @@ func UserSessionElevationGET(ctx *middlewares.AutheliaCtx) {  	}  	if err = ctx.ReplyJSON(middlewares.OKResponse{Status: "OK", Data: response}, fasthttp.StatusOK); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred retrieving the user session elevation state: error occurred writing the response body") +		ctx.Logger.WithError(err).Errorf("Error occurred retrieving the user session elevation state for user '%s': %s", userSession.Username, errStrRespBody)  		ctx.SetJSONError(messageOperationFailed) @@ -123,7 +123,7 @@ func UserSessionElevationPOST(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred creating user session elevation One-Time Code challenge: error occurred retrieving the user session data") +		ctx.Logger.WithError(err).Errorf("Error occurred creating user session elevation One-Time Code challenge: %s", errStrUserSessionData)  		ctx.SetJSONError(messageOperationFailed)  		ctx.SetStatusCode(fasthttp.StatusForbidden) @@ -132,7 +132,7 @@ func UserSessionElevationPOST(ctx *middlewares.AutheliaCtx) {  	}  	if userSession.IsAnonymous() { -		ctx.Logger.WithError(fmt.Errorf("user is anonymous")).Error("Error occurred creating user session elevation One-Time Code challenge") +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred creating user session elevation One-Time Code challenge")  		ctx.SetJSONError(messageOperationFailed)  		ctx.SetStatusCode(fasthttp.StatusForbidden) @@ -156,7 +156,7 @@ func UserSessionElevationPOST(ctx *middlewares.AutheliaCtx) {  	var signature string  	if signature, err = ctx.Providers.StorageProvider.SaveOneTimeCode(ctx, *otp); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred creating user session elevation One-Time Code challenge for user '%s': error occurred saving the challenge to storage", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred creating user session elevation One-Time Code challenge for user '%s': error occurred saving the challenge to the storage backend", userSession.Username)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed) @@ -201,7 +201,7 @@ func UserSessionElevationPOST(ctx *middlewares.AutheliaCtx) {  	if err = ctx.SetJSONBody(&bodyPOSTUserSessionElevate{  		DeleteID: deleteID,  	}); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred creating user session elevation One-Time Code challenge: error occurred writing the response body") +		ctx.Logger.WithError(err).Errorf("Error occurred creating user session elevation One-Time Code challenge for user '%s': %s", userSession.Username, errStrRespBody)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed) @@ -221,7 +221,7 @@ func UserSessionElevationPUT(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred validating user session elevation One-Time Code challenge: error occurred retrieving the user session data") +		ctx.Logger.WithError(err).Errorf("Error occurred validating user session elevation One-Time Code challenge: %s", errStrUserSessionData)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed) @@ -230,7 +230,7 @@ func UserSessionElevationPUT(ctx *middlewares.AutheliaCtx) {  	}  	if userSession.IsAnonymous() { -		ctx.Logger.WithError(fmt.Errorf("user is anonymous")).Error("Error occurred validating user session elevation One-Time Code challenge") +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred validating user session elevation One-Time Code challenge")  		ctx.SetJSONError(messageOperationFailed)  		ctx.SetStatusCode(fasthttp.StatusForbidden) @@ -239,7 +239,7 @@ func UserSessionElevationPUT(ctx *middlewares.AutheliaCtx) {  	}  	if err = ctx.ParseBody(&bodyJSON); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred validating user session elevation One-Time Code challenge: error parsing the request body") +		ctx.Logger.WithError(err).Errorf("Error occurred validating user session elevation One-Time Code challenge for user '%s': %s", userSession.Username, errStrReqBodyParse)  		ctx.SetStatusCode(fasthttp.StatusBadRequest)  		ctx.SetJSONError(messageOperationFailed) @@ -251,7 +251,7 @@ func UserSessionElevationPUT(ctx *middlewares.AutheliaCtx) {  	if code, err = ctx.Providers.StorageProvider.LoadOneTimeCode(ctx, userSession.Username, model.OTCIntentUserSessionElevation, bodyJSON.OneTimeCode); err != nil {  		ctx.Logger.WithError(err). -			Errorf("Error occurred validating user session elevation One-Time Code challenge for user '%s': error occurred retrieving the code challenge from storage", userSession.Username) +			Errorf("Error occurred validating user session elevation One-Time Code challenge for user '%s': error occurred retrieving the code challenge from the storage backend", userSession.Username)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed) @@ -259,7 +259,7 @@ func UserSessionElevationPUT(ctx *middlewares.AutheliaCtx) {  		return  	} else if code == nil {  		ctx.Logger.WithError(fmt.Errorf("the code didn't match any recorded code challenges")). -			Errorf("Error occurred validating user session elevation One-Time Code challenge for user '%s': error occurred retrieving the code challenge from storage", userSession.Username) +			Errorf("Error occurred validating user session elevation One-Time Code challenge for user '%s': error occurred retrieving the code challenge from the storage backend", userSession.Username)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed) @@ -330,7 +330,7 @@ func UserSessionElevationPUT(ctx *middlewares.AutheliaCtx) {  	}  	if err = ctx.SaveSession(userSession); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred validating user session elevation One-Time Code challenge: error occurred saving the user session data") +		ctx.Logger.WithError(err).Errorf("Error occurred validating user session elevation One-Time Code challenge for user '%s': %s", userSession.Username, errStrUserSessionDataSave)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed) @@ -373,7 +373,7 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {  	if code, err = ctx.Providers.StorageProvider.LoadOneTimeCodeByPublicID(ctx, id); err != nil {  		ctx.Logger.WithError(err). -			Error("Error occurred revoking user session elevation One-Time Code challenge: error occurred retrieving the code challenge from storage") +			Error("Error occurred revoking user session elevation One-Time Code challenge: error occurred retrieving the code challenge from the storage backend")  		ctx.SetJSONError(messageOperationFailed) @@ -381,7 +381,7 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {  	}  	if code.RevokedAt.Valid { -		ctx.Logger.WithError(fmt.Errorf("the code challenge has already been revoked")).Errorf("Error occurred validating user session elevation One-Time Code challenge") +		ctx.Logger.WithError(fmt.Errorf("the code challenge has already been revoked")).Errorf("Error occurred revoking user session elevation One-Time Code challenge")  		ctx.SetJSONError(messageOperationFailed) @@ -389,7 +389,7 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {  	}  	if code.ConsumedAt.Valid { -		ctx.Logger.WithError(fmt.Errorf("the code challenge has already been consumed")).Errorf("Error occurred validating user session elevation One-Time Code challenge") +		ctx.Logger.WithError(fmt.Errorf("the code challenge has already been consumed")).Errorf("Error occurred revoking user session elevation One-Time Code challenge")  		ctx.SetJSONError(messageOperationFailed) @@ -397,7 +397,7 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {  	}  	if code.Intent != model.OTCIntentUserSessionElevation { -		ctx.Logger.WithError(fmt.Errorf("the code challenge has the '%s' intent but the '%s' intent is required", code.Intent, model.OTCIntentUserSessionElevation)).Errorf("Error occurred revoking user session elevation One-Time Code challenge for user") +		ctx.Logger.WithError(fmt.Errorf("the code challenge has the '%s' intent but the '%s' intent is required", code.Intent, model.OTCIntentUserSessionElevation)).Errorf("Error occurred revoking user session elevation One-Time Code challenge")  		ctx.SetJSONError(messageOperationFailed) @@ -405,7 +405,7 @@ func UserSessionElevateDELETE(ctx *middlewares.AutheliaCtx) {  	}  	if err = ctx.Providers.StorageProvider.RevokeOneTimeCode(ctx, id, model.NewIP(ctx.RemoteIP())); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred revoking user session elevation One-Time Code challenge: error occurred saving the revocation of the code being saved to storage") +		ctx.Logger.WithError(err).Errorf("Error occurred revoking user session elevation One-Time Code challenge: error occurred saving the revocation to the storage backend")  		ctx.SetJSONError(messageOperationFailed) diff --git a/internal/handlers/handler_session_elevation_test.go b/internal/handlers/handler_session_elevation_test.go index 0b256b7b1..3cb2e5a4c 100644 --- a/internal/handlers/handler_session_elevation_test.go +++ b/internal/handlers/handler_session_elevation_test.go @@ -9,11 +9,11 @@ import (  	"testing"  	"time" -	"github.com/golang/mock/gomock"  	"github.com/google/uuid"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/authentication"  	"github.com/authelia/authelia/v4/internal/mocks" @@ -449,7 +449,7 @@ func TestUserSessionElevationPOST(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred creating user session elevation One-Time Code challenge for user 'john': error occurred saving the challenge to storage", "failed to insert") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred creating user session elevation One-Time Code challenge for user 'john': error occurred saving the challenge to the storage backend", "failed to insert")  			},  		},  		{ @@ -670,7 +670,7 @@ func TestUserSessionElevationPUT(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusBadRequest,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating user session elevation One-Time Code challenge: error parsing the request body", "unable to parse body: invalid character 'A' looking for beginning of value") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating user session elevation One-Time Code challenge for user 'john': error parsing the request body", "unable to parse body: invalid character 'A' looking for beginning of value")  			},  		},  		{ @@ -905,7 +905,7 @@ func TestUserSessionElevationPUT(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating user session elevation One-Time Code challenge for user 'john': error occurred retrieving the code challenge from storage", "the code didn't match any recorded code challenges") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating user session elevation One-Time Code challenge for user 'john': error occurred retrieving the code challenge from the storage backend", "the code didn't match any recorded code challenges")  			},  		},  		{ @@ -934,7 +934,7 @@ func TestUserSessionElevationPUT(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating user session elevation One-Time Code challenge for user 'john': error occurred retrieving the code challenge from storage", "not found") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating user session elevation One-Time Code challenge for user 'john': error occurred retrieving the code challenge from the storage backend", "not found")  			},  		},  	} @@ -1068,7 +1068,7 @@ func TestUserSessionElevationDELETE(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusOK,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred revoking user session elevation One-Time Code challenge: error occurred saving the revocation of the code being saved to storage", "failed to update") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred revoking user session elevation One-Time Code challenge: error occurred saving the revocation to the storage backend", "failed to update")  			},  		},  		{ @@ -1108,7 +1108,7 @@ func TestUserSessionElevationDELETE(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusOK,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred revoking user session elevation One-Time Code challenge for user", "the code challenge has the 'abc' intent but the 'use' intent is required") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred revoking user session elevation One-Time Code challenge", "the code challenge has the 'abc' intent but the 'use' intent is required")  			},  		},  		{ @@ -1149,7 +1149,7 @@ func TestUserSessionElevationDELETE(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusOK,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating user session elevation One-Time Code challenge", "the code challenge has already been consumed") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred revoking user session elevation One-Time Code challenge", "the code challenge has already been consumed")  			},  		},  		{ @@ -1190,7 +1190,7 @@ func TestUserSessionElevationDELETE(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusOK,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating user session elevation One-Time Code challenge", "the code challenge has already been revoked") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred revoking user session elevation One-Time Code challenge", "the code challenge has already been revoked")  			},  		},  		{ @@ -1219,7 +1219,7 @@ func TestUserSessionElevationDELETE(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusOK,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred revoking user session elevation One-Time Code challenge: error occurred retrieving the code challenge from storage", "invalid user") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred revoking user session elevation One-Time Code challenge: error occurred retrieving the code challenge from the storage backend", "invalid user")  			},  		},  		{ diff --git a/internal/handlers/handler_sign_duo_test.go b/internal/handlers/handler_sign_duo_test.go index bd68d93ba..bc2130561 100644 --- a/internal/handlers/handler_sign_duo_test.go +++ b/internal/handlers/handler_sign_duo_test.go @@ -8,10 +8,10 @@ import (  	"regexp"  	"testing" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/suite"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/configuration/schema"  	"github.com/authelia/authelia/v4/internal/duo" diff --git a/internal/handlers/handler_sign_totp.go b/internal/handlers/handler_sign_totp.go index e6a95ec5e..a4416e532 100644 --- a/internal/handlers/handler_sign_totp.go +++ b/internal/handlers/handler_sign_totp.go @@ -2,6 +2,7 @@ package handlers  import (  	"errors" +	"fmt"  	"github.com/valyala/fasthttp" @@ -20,9 +21,19 @@ func TimeBasedOneTimePasswordGET(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred retrieving user session") +		ctx.Logger.WithError(err).Errorf("Error occurred retrieving TOTP configuration: %s", errStrUserSessionData) -		ctx.ReplyForbidden() +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed) + +		return +	} + +	if userSession.IsAnonymous() { +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred retrieving TOTP configuration") + +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed)  		return  	} @@ -30,24 +41,27 @@ func TimeBasedOneTimePasswordGET(ctx *middlewares.AutheliaCtx) {  	var config *model.TOTPConfiguration  	if config, err = ctx.Providers.StorageProvider.LoadTOTPConfiguration(ctx, userSession.Username); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred retrieving TOTP configuration for user '%s': error occurred retrieving the configuration from the storage backend", userSession.Username) +  		if errors.Is(err, storage.ErrNoTOTPConfiguration) {  			ctx.SetStatusCode(fasthttp.StatusNotFound)  			ctx.SetJSONError("Could not find TOTP Configuration for user.") -			ctx.Logger.WithError(err).Errorf("Failed to lookup TOTP configuration for user '%s'", userSession.Username)  		} else {  			ctx.SetStatusCode(fasthttp.StatusInternalServerError)  			ctx.SetJSONError("Could not find TOTP Configuration for user.") -			ctx.Logger.WithError(err).Errorf("Failed to lookup TOTP configuration for user '%s' with unknown error", userSession.Username)  		}  		return  	}  	if err = ctx.SetJSONBody(config); err != nil { -		ctx.Logger.Errorf("Unable to perform TOTP configuration response: %s", err) -	} +		ctx.Logger.WithError(err).Errorf("Error occurred retrieving TOTP configuration for user '%s': %s", userSession.Username, errStrRespBody) + +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed) -	ctx.SetStatusCode(fasthttp.StatusOK) +		return +	}  }  // TimeBasedOneTimePasswordPOST validate the TOTP passcode provided by the user. @@ -55,60 +69,108 @@ func TimeBasedOneTimePasswordPOST(ctx *middlewares.AutheliaCtx) {  	bodyJSON := bodySignTOTPRequest{}  	var ( -		userSession session.UserSession -		err         error +		userSession   session.UserSession +		config        *model.TOTPConfiguration +		valid, exists bool +		step          uint64 +		err           error  	) -	if err = ctx.ParseBody(&bodyJSON); err != nil { -		ctx.Logger.WithError(err).Errorf(logFmtErrParseRequestBody, regulation.AuthTypeTOTP) +	if userSession, err = ctx.GetSession(); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP authentication: %s", errStrUserSessionData) -		respondUnauthorized(ctx, messageMFAValidationFailed) +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed)  		return  	} -	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred retrieving user session") +	if userSession.IsAnonymous() { +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred validating a TOTP authentication") -		respondUnauthorized(ctx, messageMFAValidationFailed) +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed)  		return  	} -	config, err := ctx.Providers.StorageProvider.LoadTOTPConfiguration(ctx, userSession.Username) -	if err != nil { -		ctx.Logger.WithError(err).Errorf("Failed to load TOTP configuration") +	if err = ctx.ParseBody(&bodyJSON); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP authentication for user '%s': %s", userSession.Username, errStrReqBodyParse) -		respondUnauthorized(ctx, messageMFAValidationFailed) +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed)  		return  	} -	var valid bool +	if config, err = ctx.Providers.StorageProvider.LoadTOTPConfiguration(ctx, userSession.Username); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP authentication for user '%s': error occurred retreiving the configuration from the storage backend", userSession.Username) + +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed) + +		return +	} -	if valid, err = ctx.Providers.TOTP.Validate(bodyJSON.Token, config); err != nil { -		ctx.Logger.WithError(err).Errorf("Failed to perform TOTP verification") +	if valid, step, err = ctx.Providers.TOTP.Validate(bodyJSON.Token, config); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP authentication for user '%s': error occurred validating the user input", userSession.Username) -		respondUnauthorized(ctx, messageMFAValidationFailed) +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed)  		return -	} else if !valid { +	} + +	if !valid { +		ctx.Logger.WithError(fmt.Errorf("the user input wasn't valid")).Errorf("Error occurred validating a TOTP authentication for user '%s': error occurred validating the user input", userSession.Username) +  		_ = markAuthenticationAttempt(ctx, false, nil, userSession.Username, regulation.AuthTypeTOTP, nil) -		respondUnauthorized(ctx, messageMFAValidationFailed) +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed) + +		return +	} + +	if exists, err = ctx.Providers.StorageProvider.ExistsTOTPHistory(ctx, userSession.Username, step*uint64(config.Period), config.HistorySince(ctx.GetClock().Now(), ctx.Configuration.TOTP.Skew)); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP authentication for user '%s': error occurred checking the TOTP history", userSession.Username) + +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed) + +		return +	} + +	if exists { +		ctx.Logger.WithError(fmt.Errorf("the user has already used this code recently and will not be permitted to reuse it")).Errorf("Error occurred validating a TOTP authentication for user '%s': error occurred satisfying security policies", userSession.Username) + +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed) + +		return +	} + +	if err = ctx.Providers.StorageProvider.SaveTOTPHistory(ctx, userSession.Username, step*uint64(config.Period)); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP authentication for user '%s': error occurred saving the TOTP history to the storage backend", userSession.Username) + +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed)  		return  	}  	if err = markAuthenticationAttempt(ctx, true, nil, userSession.Username, regulation.AuthTypeTOTP, nil); err != nil { -		respondUnauthorized(ctx, messageMFAValidationFailed) +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed) +  		return  	}  	if err = ctx.RegenerateSession(); err != nil { -		ctx.Logger.WithError(err).Errorf(logFmtErrSessionRegenerate, regulation.AuthTypeTOTP, userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP authentication for user '%s': error regenerating the user session", userSession.Username) -		respondUnauthorized(ctx, messageMFAValidationFailed) +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed)  		return  	} @@ -116,9 +178,10 @@ func TimeBasedOneTimePasswordPOST(ctx *middlewares.AutheliaCtx) {  	config.UpdateSignInInfo(ctx.Clock.Now())  	if err = ctx.Providers.StorageProvider.UpdateTOTPConfigurationSignIn(ctx, config.ID, config.LastUsedAt); err != nil { -		ctx.Logger.WithError(err).Errorf("Unable to save %s device sign in metadata for user '%s'", regulation.AuthTypeTOTP, userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP authentication for user '%s': error occurred saving the credential sign-in information to the storage backend", userSession.Username) -		respondUnauthorized(ctx, messageMFAValidationFailed) +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed)  		return  	} @@ -126,9 +189,10 @@ func TimeBasedOneTimePasswordPOST(ctx *middlewares.AutheliaCtx) {  	userSession.SetTwoFactorTOTP(ctx.Clock.Now())  	if err = ctx.SaveSession(userSession); err != nil { -		ctx.Logger.WithError(err).Errorf(logFmtErrSessionSave, "authentication time", regulation.AuthTypeTOTP, logFmtActionAuthentication, userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred validating a TOTP authentication for user '%s': %s", userSession.Username, errStrUserSessionDataSave) -		respondUnauthorized(ctx, messageMFAValidationFailed) +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed)  		return  	} diff --git a/internal/handlers/handler_sign_totp_test.go b/internal/handlers/handler_sign_totp_test.go index 9e57951c1..aa73c190e 100644 --- a/internal/handlers/handler_sign_totp_test.go +++ b/internal/handlers/handler_sign_totp_test.go @@ -6,14 +6,17 @@ import (  	"fmt"  	"regexp"  	"testing" +	"time" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/suite" +	"go.uber.org/mock/gomock" +	"github.com/authelia/authelia/v4/internal/authentication"  	"github.com/authelia/authelia/v4/internal/configuration/schema"  	"github.com/authelia/authelia/v4/internal/mocks"  	"github.com/authelia/authelia/v4/internal/model"  	"github.com/authelia/authelia/v4/internal/regulation" +	"github.com/authelia/authelia/v4/internal/storage"  )  type HandlerSignTOTPSuite struct { @@ -28,6 +31,12 @@ func (s *HandlerSignTOTPSuite) SetupTest() {  	s.Assert().NoError(err)  	userSession.Username = testUsername +	userSession.AuthenticationLevel = authentication.OneFactor + +	s.mock.Clock.Set(time.Unix(1701295903, 0)) +	s.mock.Ctx.Clock = &s.mock.Clock +	s.mock.Ctx.Configuration.TOTP = schema.DefaultTOTPConfiguration +  	s.Assert().NoError(s.mock.Ctx.SaveSession(userSession))  } @@ -35,29 +44,46 @@ func (s *HandlerSignTOTPSuite) TearDownTest() {  	s.mock.Close()  } -func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToDefaultURL() { -	config := model.TOTPConfiguration{ID: 1, Username: "john", Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} - -	s.mock.StorageMock.EXPECT(). -		LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). -		Return(&config, nil) - -	s.mock.StorageMock. -		EXPECT(). -		AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ -			Username:   "john", -			Successful: true, -			Banned:     false, -			Time:       s.mock.Clock.Now(), -			Type:       regulation.AuthTypeTOTP, -			RemoteIP:   model.NewNullIPFromString("0.0.0.0"), -		})) +func (s *HandlerSignTOTPSuite) AssertLastLogMessage(message string, err string) { +	AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), message, err) +} -	s.mock.TOTPMock.EXPECT().Validate(gomock.Eq("abc"), gomock.Eq(&config)).Return(true, nil) +func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToDefaultURL() { +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} -	s.mock.StorageMock. -		EXPECT(). -		UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()) +	gomock.InOrder( +		s.mock.StorageMock. +			EXPECT(). +			LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). +			Return(&config, nil), +		s.mock.TOTPMock. +			EXPECT(). +			Validate(gomock.Eq("abc"), gomock.Eq(&config)). +			Return(true, getStepTOTP(s.mock.Ctx, -1), nil), +		s.mock.StorageMock. +			EXPECT(). +			ExistsTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890), config.HistorySince(s.mock.Clock.Now(), s.mock.Ctx.Configuration.TOTP.Skew)). +			Return(false, nil), +		s.mock.StorageMock. +			EXPECT(). +			SaveTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890)). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ +				Username:   testUsername, +				Successful: true, +				Banned:     false, +				Time:       s.mock.Clock.Now(), +				Type:       regulation.AuthTypeTOTP, +				RemoteIP:   model.NewNullIPFromString("0.0.0.0"), +			})). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()). +			Return(nil), +	)  	s.mock.Ctx.Configuration.Session.Cookies[0].DefaultRedirectionURL = testRedirectionURL @@ -74,28 +100,40 @@ func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToDefaultURL() {  }  func (s *HandlerSignTOTPSuite) TestShouldFailWhenTOTPSignInInfoFailsToUpdate() { -	config := model.TOTPConfiguration{ID: 1, Username: "john", Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} - -	s.mock.StorageMock.EXPECT(). -		LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). -		Return(&config, nil) +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} -	s.mock.StorageMock. -		EXPECT(). -		AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ -			Username:   "john", -			Successful: true, -			Banned:     false, -			Time:       s.mock.Clock.Now(), -			Type:       regulation.AuthTypeTOTP, -			RemoteIP:   model.NewNullIPFromString("0.0.0.0"), -		})) - -	s.mock.TOTPMock.EXPECT().Validate(gomock.Eq("abc"), gomock.Eq(&config)).Return(true, nil) - -	s.mock.StorageMock. -		EXPECT(). -		UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()).Return(errors.New("failed to perform update")) +	gomock.InOrder( +		s.mock.StorageMock. +			EXPECT(). +			LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). +			Return(&config, nil), +		s.mock.TOTPMock. +			EXPECT(). +			Validate(gomock.Eq("abc"), gomock.Eq(&config)). +			Return(true, getStepTOTP(s.mock.Ctx, -1), nil), +		s.mock.StorageMock. +			EXPECT(). +			ExistsTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890), config.HistorySince(s.mock.Clock.Now(), s.mock.Ctx.Configuration.TOTP.Skew)). +			Return(false, nil), +		s.mock.StorageMock. +			EXPECT(). +			SaveTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890)). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ +				Username:   testUsername, +				Successful: true, +				Banned:     false, +				Time:       s.mock.Clock.Now(), +				Type:       regulation.AuthTypeTOTP, +				RemoteIP:   model.NewNullIPFromString("0.0.0.0"), +			})), +		s.mock.StorageMock. +			EXPECT(). +			UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()). +			Return(errors.New("failed to perform update")), +	)  	s.mock.Ctx.Configuration.Session.Cookies[0].DefaultRedirectionURL = testRedirectionURL @@ -106,32 +144,45 @@ func (s *HandlerSignTOTPSuite) TestShouldFailWhenTOTPSignInInfoFailsToUpdate() {  	s.mock.Ctx.Request.SetBody(bodyBytes)  	TimeBasedOneTimePasswordPOST(s.mock.Ctx) -	s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.") +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.")  }  func (s *HandlerSignTOTPSuite) TestShouldNotReturnRedirectURL() { -	config := model.TOTPConfiguration{ID: 1, Username: "john", Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} - -	s.mock.StorageMock.EXPECT(). -		LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). -		Return(&config, nil) +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} -	s.mock.StorageMock. -		EXPECT(). -		AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ -			Username:   "john", -			Successful: true, -			Banned:     false, -			Time:       s.mock.Clock.Now(), -			Type:       regulation.AuthTypeTOTP, -			RemoteIP:   model.NewNullIPFromString("0.0.0.0"), -		})) - -	s.mock.TOTPMock.EXPECT().Validate(gomock.Eq("abc"), gomock.Eq(&config)).Return(true, nil) - -	s.mock.StorageMock. -		EXPECT(). -		UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()) +	gomock.InOrder( +		s.mock.StorageMock. +			EXPECT(). +			LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). +			Return(&config, nil), +		s.mock.TOTPMock. +			EXPECT(). +			Validate(gomock.Eq("abc"), gomock.Eq(&config)). +			Return(true, getStepTOTP(s.mock.Ctx, -1), nil), +		s.mock.StorageMock. +			EXPECT(). +			ExistsTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890), config.HistorySince(s.mock.Clock.Now(), s.mock.Ctx.Configuration.TOTP.Skew)). +			Return(false, nil), +		s.mock.StorageMock. +			EXPECT(). +			SaveTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890)). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ +				Username:   testUsername, +				Successful: true, +				Banned:     false, +				Time:       s.mock.Clock.Now(), +				Type:       regulation.AuthTypeTOTP, +				RemoteIP:   model.NewNullIPFromString("0.0.0.0"), +			})). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()). +			Return(nil), +	)  	bodyBytes, err := json.Marshal(bodySignTOTPRequest{  		Token: "abc", @@ -144,7 +195,7 @@ func (s *HandlerSignTOTPSuite) TestShouldNotReturnRedirectURL() {  }  func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToSafeTargetURL() { -	config := model.TOTPConfiguration{ID: 1, Username: "john", Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"}  	s.mock.Ctx.Configuration.Session.Cookies = []schema.SessionCookie{  		{  			Domain: "example.com", @@ -154,26 +205,39 @@ func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToSafeTargetURL() {  		},  	} -	s.mock.StorageMock.EXPECT(). -		LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). -		Return(&config, nil) - -	s.mock.StorageMock. -		EXPECT(). -		AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ -			Username:   "john", -			Successful: true, -			Banned:     false, -			Time:       s.mock.Clock.Now(), -			Type:       regulation.AuthTypeTOTP, -			RemoteIP:   model.NewNullIPFromString("0.0.0.0"), -		})) - -	s.mock.TOTPMock.EXPECT().Validate(gomock.Eq("abc"), gomock.Eq(&config)).Return(true, nil) - -	s.mock.StorageMock. -		EXPECT(). -		UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()) +	gomock.InOrder( +		s.mock.StorageMock. +			EXPECT(). +			LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). +			Return(&config, nil), +		s.mock.TOTPMock. +			EXPECT(). +			Validate(gomock.Eq("abc"), gomock.Eq(&config)). +			Return(true, getStepTOTP(s.mock.Ctx, -1), nil), +		s.mock.StorageMock. +			EXPECT(). +			ExistsTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890), config.HistorySince(s.mock.Clock.Now(), s.mock.Ctx.Configuration.TOTP.Skew)). +			Return(false, nil), +		s.mock.StorageMock. +			EXPECT(). +			SaveTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890)). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ +				Username:   testUsername, +				Successful: true, +				Banned:     false, +				Time:       s.mock.Clock.Now(), +				Type:       regulation.AuthTypeTOTP, +				RemoteIP:   model.NewNullIPFromString("0.0.0.0"), +			})). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()). +			Return(nil), +	)  	bodyBytes, err := json.Marshal(bodySignTOTPRequest{  		Token:     "abc", @@ -190,28 +254,38 @@ func (s *HandlerSignTOTPSuite) TestShouldRedirectUserToSafeTargetURL() {  }  func (s *HandlerSignTOTPSuite) TestShouldNotRedirectToUnsafeURL() { -	s.mock.StorageMock.EXPECT(). -		LoadTOTPConfiguration(s.mock.Ctx, "john"). -		Return(&model.TOTPConfiguration{Secret: []byte("secret")}, nil) - -	s.mock.StorageMock. -		EXPECT(). -		AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ -			Username:   "john", -			Successful: true, -			Banned:     false, -			Time:       s.mock.Clock.Now(), -			Type:       regulation.AuthTypeTOTP, -			RemoteIP:   model.NewNullIPFromString("0.0.0.0"), -		})) - -	s.mock.StorageMock. -		EXPECT(). -		UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()) - -	s.mock.TOTPMock.EXPECT(). -		Validate(gomock.Eq("abc"), gomock.Eq(&model.TOTPConfiguration{Secret: []byte("secret")})). -		Return(true, nil) +	gomock.InOrder( +		s.mock.StorageMock. +			EXPECT(). +			LoadTOTPConfiguration(s.mock.Ctx, testUsername). +			Return(&model.TOTPConfiguration{Secret: []byte("secret"), Period: 30}, nil), +		s.mock.TOTPMock.EXPECT(). +			Validate(gomock.Eq("abc"), gomock.Eq(&model.TOTPConfiguration{Secret: []byte("secret"), Period: 30})). +			Return(true, getStepTOTP(s.mock.Ctx, -1), nil), +		s.mock.StorageMock. +			EXPECT(). +			ExistsTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890), gomock.Any()). +			Return(false, nil), +		s.mock.StorageMock. +			EXPECT(). +			SaveTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890)). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ +				Username:   testUsername, +				Successful: true, +				Banned:     false, +				Time:       s.mock.Clock.Now(), +				Type:       regulation.AuthTypeTOTP, +				RemoteIP:   model.NewNullIPFromString("0.0.0.0"), +			})). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()). +			Return(nil), +	)  	bodyBytes, err := json.Marshal(bodySignTOTPRequest{  		Token:     "abc", @@ -226,30 +300,41 @@ func (s *HandlerSignTOTPSuite) TestShouldNotRedirectToUnsafeURL() {  }  func (s *HandlerSignTOTPSuite) TestShouldRegenerateSessionForPreventingSessionFixation() { -	config := model.TOTPConfiguration{ID: 1, Username: "john", Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} - -	s.mock.StorageMock.EXPECT(). -		LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). -		Return(&config, nil) - -	s.mock.StorageMock. -		EXPECT(). -		AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ -			Username:   "john", -			Successful: true, -			Banned:     false, -			Time:       s.mock.Clock.Now(), -			Type:       regulation.AuthTypeTOTP, -			RemoteIP:   model.NewNullIPFromString("0.0.0.0"), -		})) - -	s.mock.TOTPMock.EXPECT(). -		Validate(gomock.Eq("abc"), gomock.Eq(&config)). -		Return(true, nil) - -	s.mock.StorageMock. -		EXPECT(). -		UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()) +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} + +	gomock.InOrder( +		s.mock.StorageMock. +			EXPECT(). +			LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). +			Return(&config, nil), +		s.mock.TOTPMock. +			EXPECT(). +			Validate(gomock.Eq("abc"), gomock.Eq(&config)). +			Return(true, getStepTOTP(s.mock.Ctx, -1), nil), +		s.mock.StorageMock. +			EXPECT(). +			ExistsTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890), config.HistorySince(s.mock.Clock.Now(), s.mock.Ctx.Configuration.TOTP.Skew)). +			Return(false, nil), +		s.mock.StorageMock. +			EXPECT(). +			SaveTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890)). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ +				Username:   testUsername, +				Successful: true, +				Banned:     false, +				Time:       s.mock.Clock.Now(), +				Type:       regulation.AuthTypeTOTP, +				RemoteIP:   model.NewNullIPFromString("0.0.0.0"), +			})). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			UpdateTOTPConfigurationSignIn(s.mock.Ctx, gomock.Any(), gomock.Any()). +			Return(nil), +	)  	bodyBytes, err := json.Marshal(bodySignTOTPRequest{  		Token: "abc", @@ -268,8 +353,168 @@ func (s *HandlerSignTOTPSuite) TestShouldRegenerateSessionForPreventingSessionFi  		string(s.mock.Ctx.Request.Header.Cookie("authelia_session")))  } +func (s *HandlerSignTOTPSuite) TestShouldHandleErrorSaveHistory() { +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} + +	gomock.InOrder( +		s.mock.StorageMock. +			EXPECT(). +			LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). +			Return(&config, nil), +		s.mock.TOTPMock. +			EXPECT(). +			Validate(gomock.Eq("abc"), gomock.Eq(&config)). +			Return(true, getStepTOTP(s.mock.Ctx, -1), nil), +		s.mock.StorageMock. +			EXPECT(). +			ExistsTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890), config.HistorySince(s.mock.Clock.Now(), s.mock.Ctx.Configuration.TOTP.Skew)). +			Return(false, nil), +		s.mock.StorageMock. +			EXPECT(). +			SaveTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890)). +			Return(fmt.Errorf("bad stuff")), +	) + +	bodyBytes, err := json.Marshal(bodySignTOTPRequest{ +		Token: "abc", +	}) +	s.Require().NoError(err) +	s.mock.Ctx.Request.SetBody(bodyBytes) + +	TimeBasedOneTimePasswordPOST(s.mock.Ctx) +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.") + +	s.AssertLastLogMessage("Error occurred validating a TOTP authentication for user 'john': error occurred saving the TOTP history to the storage backend", "bad stuff") +} + +func (s *HandlerSignTOTPSuite) TestShouldHandleErrorExistsHistory() { +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} + +	gomock.InOrder( +		s.mock.StorageMock. +			EXPECT(). +			LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). +			Return(&config, nil), +		s.mock.TOTPMock. +			EXPECT(). +			Validate(gomock.Eq("abc"), gomock.Eq(&config)). +			Return(true, getStepTOTP(s.mock.Ctx, -1), nil), +		s.mock.StorageMock. +			EXPECT(). +			ExistsTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890), config.HistorySince(s.mock.Clock.Now(), s.mock.Ctx.Configuration.TOTP.Skew)). +			Return(false, fmt.Errorf("oh my")), +	) + +	bodyBytes, err := json.Marshal(bodySignTOTPRequest{ +		Token: "abc", +	}) +	s.Require().NoError(err) +	s.mock.Ctx.Request.SetBody(bodyBytes) + +	TimeBasedOneTimePasswordPOST(s.mock.Ctx) +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.") + +	s.AssertLastLogMessage("Error occurred validating a TOTP authentication for user 'john': error occurred checking the TOTP history", "oh my") +} + +func (s *HandlerSignTOTPSuite) TestShouldHandleExistsHistory() { +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} + +	gomock.InOrder( +		s.mock.StorageMock. +			EXPECT(). +			LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()). +			Return(&config, nil), +		s.mock.TOTPMock. +			EXPECT(). +			Validate(gomock.Eq("abc"), gomock.Eq(&config)). +			Return(true, getStepTOTP(s.mock.Ctx, -1), nil), +		s.mock.StorageMock. +			EXPECT(). +			ExistsTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890), config.HistorySince(s.mock.Clock.Now(), s.mock.Ctx.Configuration.TOTP.Skew)). +			Return(true, nil), +	) + +	bodyBytes, err := json.Marshal(bodySignTOTPRequest{ +		Token: "abc", +	}) +	s.Require().NoError(err) +	s.mock.Ctx.Request.SetBody(bodyBytes) + +	TimeBasedOneTimePasswordPOST(s.mock.Ctx) +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.") + +	s.AssertLastLogMessage("Error occurred validating a TOTP authentication for user 'john': error occurred satisfying security policies", "the user has already used this code recently and will not be permitted to reuse it") +} + +func (s *HandlerSignTOTPSuite) TestShouldHandleAnonymous() { +	us, err := s.mock.Ctx.GetSession() + +	s.Require().NoError(err) + +	us.Username = "" +	us.AuthenticationLevel = authentication.NotAuthenticated + +	s.Require().NoError(s.mock.Ctx.SaveSession(us)) + +	bodyBytes, err := json.Marshal(bodySignTOTPRequest{ +		Token: "abc", +	}) +	s.Require().NoError(err) +	s.mock.Ctx.Request.SetBody(bodyBytes) + +	TimeBasedOneTimePasswordPOST(s.mock.Ctx) +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.") + +	s.AssertLastLogMessage("Error occurred validating a TOTP authentication", "user is anonymous") +} + +func (s *HandlerSignTOTPSuite) TestShouldHandleGETAnonymous() { +	us, err := s.mock.Ctx.GetSession() + +	s.Require().NoError(err) + +	us.Username = "" +	us.AuthenticationLevel = authentication.NotAuthenticated + +	s.Require().NoError(s.mock.Ctx.SaveSession(us)) + +	TimeBasedOneTimePasswordGET(s.mock.Ctx) +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.") + +	s.AssertLastLogMessage("Error occurred retrieving TOTP configuration", "user is anonymous") +} + +func (s *HandlerSignTOTPSuite) TestShouldHandleGETErrorLoadConfiguration() { +	gomock.InOrder( +		s.mock.StorageMock. +			EXPECT(). +			LoadTOTPConfiguration(s.mock.Ctx, testUsername). +			Return(nil, fmt.Errorf("nah")), +	) + +	TimeBasedOneTimePasswordGET(s.mock.Ctx) +	s.mock.Assert500KO(s.T(), "Could not find TOTP Configuration for user.") + +	s.AssertLastLogMessage("Error occurred retrieving TOTP configuration for user 'john': error occurred retrieving the configuration from the storage backend", "nah") +} + +func (s *HandlerSignTOTPSuite) TestShouldHandleGETErrorLoadConfigurationNotFound() { +	gomock.InOrder( +		s.mock.StorageMock. +			EXPECT(). +			LoadTOTPConfiguration(s.mock.Ctx, testUsername). +			Return(nil, storage.ErrNoTOTPConfiguration), +	) + +	TimeBasedOneTimePasswordGET(s.mock.Ctx) +	s.mock.Assert404KO(s.T(), "Could not find TOTP Configuration for user.") + +	s.AssertLastLogMessage("Error occurred retrieving TOTP configuration for user 'john': error occurred retrieving the configuration from the storage backend", "no TOTP configuration for user") +} +  func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidValue() { -	config := model.TOTPConfiguration{ID: 1, Username: "john", Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"}  	gomock.InOrder(  		s.mock.StorageMock.EXPECT(). @@ -277,7 +522,7 @@ func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidValue() {  			Return(&config, nil),  		s.mock.TOTPMock.EXPECT().  			Validate(gomock.Eq("abc"), gomock.Eq(&config)). -			Return(false, fmt.Errorf("invalid")), +			Return(false, uint64(0), fmt.Errorf("invalid")),  	)  	bodyBytes, err := json.Marshal(bodySignTOTPRequest{ @@ -290,35 +535,38 @@ func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidValue() {  	res := r.FindAllStringSubmatch(string(s.mock.Ctx.Response.Header.PeekCookie("authelia_session")), -1)  	TimeBasedOneTimePasswordPOST(s.mock.Ctx) -	s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.") +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.")  	s.NotEqual(  		res[0][1],  		string(s.mock.Ctx.Request.Header.Cookie("authelia_session"))) -	AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Failed to perform TOTP verification", "invalid") +	AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Error occurred validating a TOTP authentication for user 'john': error occurred validating the user input", "invalid")  }  func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidBoolean() { -	config := model.TOTPConfiguration{ID: 1, Username: "john", Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"}  	gomock.InOrder( -		s.mock.StorageMock.EXPECT(). +		s.mock.StorageMock. +			EXPECT().  			LoadTOTPConfiguration(s.mock.Ctx, gomock.Any()).  			Return(&config, nil), -		s.mock.TOTPMock.EXPECT(). +		s.mock.TOTPMock. +			EXPECT().  			Validate(gomock.Eq("abc"), gomock.Eq(&config)). -			Return(false, nil), +			Return(false, uint64(0), nil),  		s.mock.StorageMock.  			EXPECT().  			AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ -				Username:   "john", +				Username:   testUsername,  				Successful: false,  				Banned:     false,  				Time:       s.mock.Clock.Now(),  				Type:       regulation.AuthTypeTOTP,  				RemoteIP:   model.NewNullIPFromString("0.0.0.0"), -			})), +			})). +			Return(nil),  	)  	bodyBytes, err := json.Marshal(bodySignTOTPRequest{ @@ -331,17 +579,17 @@ func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidBoolean() {  	res := r.FindAllStringSubmatch(string(s.mock.Ctx.Response.Header.PeekCookie("authelia_session")), -1)  	TimeBasedOneTimePasswordPOST(s.mock.Ctx) -	s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.") +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.")  	s.NotEqual(  		res[0][1],  		string(s.mock.Ctx.Request.Header.Cookie("authelia_session"))) -	AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Unsuccessful TOTP authentication attempt by user 'john'", "") +	AssertLogEntryMessageAndError(s.T(), MustGetLogLastSeq(s.T(), s.mock.Hook, 1), "Error occurred validating a TOTP authentication for user 'john': error occurred validating the user input", "the user input wasn't valid")  }  func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidBooleanMarkErr() { -	config := model.TOTPConfiguration{ID: 1, Username: "john", Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"}  	gomock.InOrder(  		s.mock.StorageMock.EXPECT(). @@ -349,11 +597,11 @@ func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidBooleanMarkErr() {  			Return(&config, nil),  		s.mock.TOTPMock.EXPECT().  			Validate(gomock.Eq("abc"), gomock.Eq(&config)). -			Return(false, nil), +			Return(false, uint64(0), nil),  		s.mock.StorageMock.  			EXPECT().  			AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ -				Username:   "john", +				Username:   testUsername,  				Successful: false,  				Banned:     false,  				Time:       s.mock.Clock.Now(), @@ -372,7 +620,7 @@ func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidBooleanMarkErr() {  	res := r.FindAllStringSubmatch(string(s.mock.Ctx.Response.Header.PeekCookie("authelia_session")), -1)  	TimeBasedOneTimePasswordPOST(s.mock.Ctx) -	s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.") +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.")  	s.NotEqual(  		res[0][1], @@ -388,17 +636,17 @@ func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidJSON() {  	res := r.FindAllStringSubmatch(string(s.mock.Ctx.Response.Header.PeekCookie("authelia_session")), -1)  	TimeBasedOneTimePasswordPOST(s.mock.Ctx) -	s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.") +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.")  	s.NotEqual(  		res[0][1],  		string(s.mock.Ctx.Request.Header.Cookie("authelia_session"))) -	AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Failed to parse TOTP request body", "unable to parse body: invalid character '1' after object key") +	AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Error occurred validating a TOTP authentication for user 'john': error parsing the request body", "unable to parse body: invalid character '1' after object key")  }  func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidBooleanMarkErrSuccess() { -	config := model.TOTPConfiguration{ID: 1, Username: "john", Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"} +	config := model.TOTPConfiguration{ID: 1, Username: testUsername, Digits: 6, Secret: []byte("secret"), Period: 30, Algorithm: "SHA1"}  	gomock.InOrder(  		s.mock.StorageMock.EXPECT(). @@ -406,17 +654,25 @@ func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidBooleanMarkErrSucce  			Return(&config, nil),  		s.mock.TOTPMock.EXPECT().  			Validate(gomock.Eq("abc"), gomock.Eq(&config)). -			Return(true, nil), +			Return(true, getStepTOTP(s.mock.Ctx, -1), nil),  		s.mock.StorageMock.  			EXPECT(). -			AppendAuthenticationLog(s.mock.Ctx, gomock.Eq(model.AuthenticationAttempt{ -				Username:   "john", +			ExistsTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890), config.HistorySince(s.mock.Clock.Now(), s.mock.Ctx.Configuration.TOTP.Skew)). +			Return(false, nil), +		s.mock.StorageMock. +			EXPECT(). +			SaveTOTPHistory(s.mock.Ctx, testUsername, uint64(1701295890)). +			Return(nil), +		s.mock.StorageMock. +			EXPECT(). +			AppendAuthenticationLog(s.mock.Ctx, model.AuthenticationAttempt{ +				Username:   testUsername,  				Successful: true,  				Banned:     false,  				Time:       s.mock.Clock.Now(),  				Type:       regulation.AuthTypeTOTP,  				RemoteIP:   model.NewNullIPFromString("0.0.0.0"), -			})). +			}).  			Return(fmt.Errorf("failed to insert")),  	) @@ -430,7 +686,7 @@ func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidBooleanMarkErrSucce  	res := r.FindAllStringSubmatch(string(s.mock.Ctx.Response.Header.PeekCookie("authelia_session")), -1)  	TimeBasedOneTimePasswordPOST(s.mock.Ctx) -	s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.") +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.")  	s.NotEqual(  		res[0][1], @@ -456,13 +712,13 @@ func (s *HandlerSignTOTPSuite) TestShouldReturnErrorOnInvalidConfig() {  	res := r.FindAllStringSubmatch(string(s.mock.Ctx.Response.Header.PeekCookie("authelia_session")), -1)  	TimeBasedOneTimePasswordPOST(s.mock.Ctx) -	s.mock.Assert401KO(s.T(), "Authentication failed, please retry later.") +	s.mock.Assert403KO(s.T(), "Authentication failed, please retry later.")  	s.NotEqual(  		res[0][1],  		string(s.mock.Ctx.Request.Header.Cookie("authelia_session"))) -	AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Failed to load TOTP configuration", "not found") +	AssertLogEntryMessageAndError(s.T(), s.mock.Hook.LastEntry(), "Error occurred validating a TOTP authentication for user 'john': error occurred retreiving the configuration from the storage backend", "not found")  }  func TestRunHandlerSignTOTPSuite(t *testing.T) { diff --git a/internal/handlers/handler_sign_webauthn.go b/internal/handlers/handler_sign_webauthn.go index e8c8bd387..dfa21eb59 100644 --- a/internal/handlers/handler_sign_webauthn.go +++ b/internal/handlers/handler_sign_webauthn.go @@ -24,7 +24,7 @@ func WebAuthnAssertionGET(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred generating a WebAuthn authentication challenge: error occurred retrieving the user session data") +		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn authentication challenge: %s", errStrUserSessionData)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageMFAValidationFailed) @@ -33,7 +33,7 @@ func WebAuthnAssertionGET(ctx *middlewares.AutheliaCtx) {  	}  	if userSession.IsAnonymous() { -		ctx.Logger.WithError(fmt.Errorf("user is anonymous")).Error("Error occurred generating a WebAuthn authentication challenge") +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred generating a WebAuthn authentication challenge")  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageMFAValidationFailed) @@ -51,7 +51,7 @@ func WebAuthnAssertionGET(ctx *middlewares.AutheliaCtx) {  	}  	if user, err = handleGetWebAuthnUserByRPID(ctx, userSession.Username, userSession.DisplayName, w.Config.RPID); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn authentication challenge for user '%s': error occurred retrieving the WebAuthn user configuration from storage", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn authentication challenge for user '%s': error occurred retrieving the WebAuthn user configuration from the storage backend", userSession.Username)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageMFAValidationFailed) @@ -90,7 +90,7 @@ func WebAuthnAssertionGET(ctx *middlewares.AutheliaCtx) {  	userSession.WebAuthn = &data  	if err = ctx.SaveSession(userSession); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred generating a WebAuthn authentication challenge: error occurred saving the user session data") +		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn authentication challenge for user '%s': %s", userSession.Username, errStrUserSessionDataSave)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageMFAValidationFailed) @@ -99,7 +99,7 @@ func WebAuthnAssertionGET(ctx *middlewares.AutheliaCtx) {  	}  	if err = ctx.SetJSONBody(assertion); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn authentication challenge for user '%s': error occurred writing the response body", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred generating a WebAuthn authentication challenge for user '%s': %s", userSession.Username, errStrRespBody)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageUnableToRegisterSecurityKey) @@ -127,7 +127,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred validating a WebAuthn authentication challenge: error occurred retrieving the user session data") +		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn authentication challenge: %s", errStrUserSessionData)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageMFAValidationFailed) @@ -136,7 +136,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {  	}  	if userSession.IsAnonymous() { -		ctx.Logger.WithError(fmt.Errorf("user is anonymous")).Error("Error occurred validating a WebAuthn authentication challenge") +		ctx.Logger.WithError(errUserAnonymous).Error("Error occurred validating a WebAuthn authentication challenge")  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageMFAValidationFailed) @@ -144,26 +144,26 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {  		return  	} -	if userSession.WebAuthn == nil || userSession.WebAuthn.SessionData == nil { -		ctx.Logger.WithError(fmt.Errorf("challenge session data is not present")).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': error occurred retrieving the user session data", userSession.Username) +	if err = ctx.ParseBody(&bodyJSON); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': %s", userSession.Username, errStrReqBodyParse) -		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetStatusCode(fasthttp.StatusBadRequest)  		ctx.SetJSONError(messageMFAValidationFailed)  		return  	} -	if err = ctx.ParseBody(&bodyJSON); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': error parsing the request body", userSession.Username) +	if assertionResponse, err = protocol.ParseCredentialRequestResponseBody(bytes.NewReader(bodyJSON.Response)); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': %s", userSession.Username, errStrReqBodyParse) -		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetStatusCode(fasthttp.StatusBadRequest)  		ctx.SetJSONError(messageMFAValidationFailed)  		return  	} -	if assertionResponse, err = protocol.ParseCredentialRequestResponseBody(bytes.NewReader(bodyJSON.Response)); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': error parsing the request body", userSession.Username) +	if userSession.WebAuthn == nil || userSession.WebAuthn.SessionData == nil { +		ctx.Logger.WithError(fmt.Errorf("challenge session data is not present")).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': %s", userSession.Username, errStrUserSessionData)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageMFAValidationFailed) @@ -181,7 +181,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {  	}  	if user, err = handleGetWebAuthnUserByRPID(ctx, userSession.Username, userSession.DisplayName, w.Config.RPID); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': error occurred retrieving the WebAuthn user configuration from storage", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': error occurred retrieving the WebAuthn user configuration from the storage backend", userSession.Username)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageMFAValidationFailed) @@ -193,6 +193,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {  		_ = markAuthenticationAttempt(ctx, false, nil, userSession.Username, regulation.AuthTypeWebAuthn, err)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageMFAValidationFailed)  		return  	} @@ -201,7 +202,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {  		userSession.WebAuthn = nil  		if err = ctx.SaveSession(userSession); err != nil { -			ctx.Logger.WithError(err).Error("Error occurred validating a WebAuthn authentication challenge: error occurred saving the user session data") +			ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': %s", userSession.Username, errStrUserSessionDataSave)  		}  	}() @@ -214,7 +215,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {  			found = true  			if err = ctx.Providers.StorageProvider.UpdateWebAuthnCredentialSignIn(ctx, credential); err != nil { -				ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': error occurred saving the credential sign-in information to storage", userSession.Username) +				ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': error occurred saving the credential sign-in information to the storage backend", userSession.Username)  				ctx.SetStatusCode(fasthttp.StatusForbidden)  				ctx.SetJSONError(messageMFAValidationFailed) @@ -236,7 +237,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {  	}  	if c.Authenticator.CloneWarning { -		ctx.Logger.WithError(fmt.Errorf("authenticator sign count indicates that it is cloned")).Error("Error occurred validating a WebAuthn authentication challenge: error occurred validating the authenticator response") +		ctx.Logger.WithError(fmt.Errorf("authenticator sign count indicates that it is cloned")).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': error occurred validating the authenticator response", userSession.Username)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageMFAValidationFailed) @@ -245,7 +246,7 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {  	}  	if err = ctx.RegenerateSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred validating a WebAuthn authentication challenge: error occurred regenerating the user session cookie value") +		ctx.Logger.WithError(err).Errorf("Error occurred validating a WebAuthn authentication challenge for user '%s': error regenerating the user session", userSession.Username)  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageMFAValidationFailed) @@ -254,8 +255,6 @@ func WebAuthnAssertionPOST(ctx *middlewares.AutheliaCtx) {  	}  	if err = markAuthenticationAttempt(ctx, true, nil, userSession.Username, regulation.AuthTypeWebAuthn, nil); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred validating a WebAuthn authentication challenge: error occurred recording the authentication result") -  		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageMFAValidationFailed) diff --git a/internal/handlers/handler_sign_webauthn_test.go b/internal/handlers/handler_sign_webauthn_test.go index bc9f452ab..c87ad079b 100644 --- a/internal/handlers/handler_sign_webauthn_test.go +++ b/internal/handlers/handler_sign_webauthn_test.go @@ -9,11 +9,11 @@ import (  	"time"  	"github.com/go-webauthn/webauthn/webauthn" -	"github.com/golang/mock/gomock"  	"github.com/google/uuid"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/authentication"  	"github.com/authelia/authelia/v4/internal/configuration/schema" @@ -133,7 +133,7 @@ func TestWebAuthnAssertionGET(t *testing.T) {  				assert.Nil(t, us.WebAuthn) -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred generating a WebAuthn authentication challenge for user 'john': error occurred retrieving the WebAuthn user configuration from storage", "failed") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred generating a WebAuthn authentication challenge for user 'john': error occurred retrieving the WebAuthn user configuration from the storage backend", "failed")  			},  		},  		{ @@ -379,7 +379,7 @@ func TestWebAuthnAssertionPOST(t *testing.T) {  				assert.Nil(t, us.WebAuthn) -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn authentication challenge: error occurred validating the authenticator response", "authenticator sign count indicates that it is cloned") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn authentication challenge for user 'john': error occurred validating the authenticator response", "authenticator sign count indicates that it is cloned")  			},  		},  		{ @@ -450,7 +450,7 @@ func TestWebAuthnAssertionPOST(t *testing.T) {  				assert.Nil(t, us.WebAuthn) -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn authentication challenge for user 'john': error occurred saving the credential sign-in information to storage", "failed to update") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn authentication challenge for user 'john': error occurred saving the credential sign-in information to the storage backend", "failed to update")  			},  		},  		{ @@ -521,7 +521,7 @@ func TestWebAuthnAssertionPOST(t *testing.T) {  			"",  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn authentication challenge: error occurred recording the authentication result", "bad record") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Unable to mark WebAuthn authentication attempt by user 'john'", "bad record")  			},  		},  		{ @@ -616,7 +616,7 @@ func TestWebAuthnAssertionPOST(t *testing.T) {  			},  			`{"response":{"id":true,"rawId":"rwOwV8WCh1hrE0M6mvaoRGpGHidqK6IlhkDJ2xERhPU","response":{"authenticatorData":"DGygg5w6VoNVeDP2GKJVZmXfKgiJZHh9U4ULStTTvtwFAAAAAw","clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaW4xY0wtb1dmU2pTZDd1dXdVdnYybmRPQW1SWGIwY09BYlVvVHRBcXZHRSIsIm9yaWdpbiI6Imh0dHBzOi8vbG9naW4uZXhhbXBsZS5jb206ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9","signature":"MEQCIBlJ2Fxf6ZwLNTCQglz0AW0pD4HlU8W5Yk696jjfxVxhAiAhAMkLh8iKyhW6zSmzwfQDjMF2nKjVHzEs7jLHRPDZ2A"},"type":"public-key","clientExtensionResults":{},"authenticatorAttachment":"cross-platform"},"targetURL":null}`,  			"", -			fasthttp.StatusForbidden, +			fasthttp.StatusBadRequest,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) {  				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn authentication challenge for user 'john': error parsing the request body", "Parse error for Assertion")  			}, @@ -646,7 +646,7 @@ func TestWebAuthnAssertionPOST(t *testing.T) {  			},  			`{"response:{"id":true,"rawId":"rwOwV8WCh1hrE0M6mvaoRGpGHidqK6IlhkDJ2xERhPU","response":{"authenticatorData":"DGygg5w6VoNVeDP2GKJVZmXfKgiJZHh9U4ULStTTvtwFAAAAAw","clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaW4xY0wtb1dmU2pTZDd1dXdVdnYybmRPQW1SWGIwY09BYlVvVHRBcXZHRSIsIm9yaWdpbiI6Imh0dHBzOi8vbG9naW4uZXhhbXBsZS5jb206ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9","signature":"MEQCIBlJ2Fxf6ZwLNTCQglz0AW0pD4HlU8W5Yk696jjfxVxhAiAhAMkLh8iKyhW6zSmzwfQDjMF2nKjVHzEs7jLHRPDZ2A"},"type":"public-key","clientExtensionResults":{},"authenticatorAttachment":"cross-platform"},"targetURL":null}`,  			"", -			fasthttp.StatusForbidden, +			fasthttp.StatusBadRequest,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) {  				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn authentication challenge for user 'john': error parsing the request body", "unable to parse body: invalid character 'i' after object key")  			}, @@ -696,7 +696,7 @@ func TestWebAuthnAssertionPOST(t *testing.T) {  				require.NoError(t, mock.Ctx.SaveSession(us))  			}, -			`{"response:{"id":true,"rawId":"rwOwV8WCh1hrE0M6mvaoRGpGHidqK6IlhkDJ2xERhPU","response":{"authenticatorData":"DGygg5w6VoNVeDP2GKJVZmXfKgiJZHh9U4ULStTTvtwFAAAAAw","clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiaW4xY0wtb1dmU2pTZDd1dXdVdnYybmRPQW1SWGIwY09BYlVvVHRBcXZHRSIsIm9yaWdpbiI6Imh0dHBzOi8vbG9naW4uZXhhbXBsZS5jb206ODA4MCIsImNyb3NzT3JpZ2luIjpmYWxzZSwib3RoZXJfa2V5c19jYW5fYmVfYWRkZWRfaGVyZSI6ImRvIG5vdCBjb21wYXJlIGNsaWVudERhdGFKU09OIGFnYWluc3QgYSB0ZW1wbGF0ZS4gU2VlIGh0dHBzOi8vZ29vLmdsL3lhYlBleCJ9","signature":"MEQCIBlJ2Fxf6ZwLNTCQglz0AW0pD4HlU8W5Yk696jjfxVxhAiAhAMkLh8iKyhW6zSmzwfQDjMF2nKjVHzEs7jLHRPDZ2A"},"type":"public-key","clientExtensionResults":{},"authenticatorAttachment":"cross-platform"},"targetURL":null}`, +			dataReqGood,  			"",  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { @@ -715,7 +715,7 @@ func TestWebAuthnAssertionPOST(t *testing.T) {  			},  		},  		{ -			"ShouldFailUpdateAuthLog", +			"ShouldFailLoadWebAuthnUser",  			&schema.DefaultWebAuthnConfiguration,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) {  				us, err := mock.Ctx.GetSession() @@ -750,7 +750,7 @@ func TestWebAuthnAssertionPOST(t *testing.T) {  			"",  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn authentication challenge for user 'john': error occurred retrieving the WebAuthn user configuration from storage", "failed to load credentials") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn authentication challenge for user 'john': error occurred retrieving the WebAuthn user configuration from the storage backend", "failed to load credentials")  			},  		},  		{ @@ -785,7 +785,7 @@ func TestWebAuthnAssertionPOST(t *testing.T) {  			"",  			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn authentication challenge for user 'john': error occurred retrieving the WebAuthn user configuration from storage", "failed load user") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred validating a WebAuthn authentication challenge for user 'john': error occurred retrieving the WebAuthn user configuration from the storage backend", "failed load user")  			},  		},  	} diff --git a/internal/handlers/handler_user_info_test.go b/internal/handlers/handler_user_info_test.go index dc10ad861..64f86d6af 100644 --- a/internal/handlers/handler_user_info_test.go +++ b/internal/handlers/handler_user_info_test.go @@ -6,11 +6,11 @@ import (  	"fmt"  	"testing" -	"github.com/golang/mock/gomock"  	"github.com/sirupsen/logrus"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/suite"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/configuration/schema"  	"github.com/authelia/authelia/v4/internal/mocks" diff --git a/internal/handlers/handler_webauthn_credentials.go b/internal/handlers/handler_webauthn_credentials.go index 52e0b9e36..7e4f6d391 100644 --- a/internal/handlers/handler_webauthn_credentials.go +++ b/internal/handlers/handler_webauthn_credentials.go @@ -42,7 +42,7 @@ func WebAuthnCredentialsGET(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred loading WebAuthn credentials: error occurred loading session data") +		ctx.Logger.WithError(err).Errorf("Error occurred loading WebAuthn credentials: %s", errStrUserSessionData)  		ctx.SetJSONError(messageOperationFailed)  		ctx.SetStatusCode(fasthttp.StatusForbidden) @@ -51,7 +51,7 @@ func WebAuthnCredentialsGET(ctx *middlewares.AutheliaCtx) {  	}  	if userSession.IsAnonymous() { -		ctx.Logger.WithError(fmt.Errorf("user is anonymous")).Errorf("Error occurred loading WebAuthn credentials") +		ctx.Logger.WithError(errUserAnonymous).Errorf("Error occurred loading WebAuthn credentials")  		ctx.SetJSONError(messageOperationFailed) @@ -69,7 +69,7 @@ func WebAuthnCredentialsGET(ctx *middlewares.AutheliaCtx) {  	var credentials []model.WebAuthnCredential  	if credentials, err = ctx.Providers.StorageProvider.LoadWebAuthnCredentialsByUsername(ctx, origin.Hostname(), userSession.Username); err != nil && err != storage.ErrNoWebAuthnCredential { -		ctx.Logger.WithError(err).Errorf("Error occurred loading WebAuthn credentials for user '%s'", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred loading WebAuthn credentials for user '%s': error occurred loading credentials from the storage backend", userSession.Username)  		ctx.SetJSONError(messageOperationFailed) @@ -77,7 +77,7 @@ func WebAuthnCredentialsGET(ctx *middlewares.AutheliaCtx) {  	}  	if err = ctx.SetJSONBody(credentials); err != nil { -		ctx.Logger.WithError(err).Errorf("Error ccurred loading WebAuthn credentials for user '%s': error occurred attempting to write the response body", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error ccurred loading WebAuthn credentials for user '%s': %s", userSession.Username, errStrRespBody)  	}  } @@ -90,31 +90,35 @@ func WebAuthnCredentialPUT(ctx *middlewares.AutheliaCtx) {  		credential  *model.WebAuthnCredential  		userSession session.UserSession +		origin      *url.URL +		credentials []model.WebAuthnCredential +  		err error  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred modifying WebAuthn credential: error occurred loading session data") +		ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential: %s", errStrUserSessionData) -		ctx.SetJSONError(messageOperationFailed)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageOperationFailed)  		return  	}  	if userSession.IsAnonymous() { -		ctx.Logger.WithError(fmt.Errorf("user is anonymous")).Errorf("Error occurred modifying WebAuthn credential") +		ctx.Logger.WithError(errUserAnonymous).Errorf("Error occurred modifying WebAuthn credential") +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed)  		return  	}  	if err = json.Unmarshal(ctx.PostBody(), &bodyJSON); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred modifying WebAuthn credential: error occurred parsing the form data") +		ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': %s", userSession.Username, errStrReqBodyParse) -		ctx.SetJSONError(messageOperationFailed)  		ctx.SetStatusCode(fasthttp.StatusBadRequest) +		ctx.SetJSONError(messageOperationFailed)  		return  	} @@ -122,6 +126,7 @@ func WebAuthnCredentialPUT(ctx *middlewares.AutheliaCtx) {  	if id, err = getWebAuthnCredentialIDFromContext(ctx); err != nil {  		ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred trying to determine the credential ID", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusBadRequest)  		ctx.SetJSONError(messageOperationFailed)  		return @@ -130,14 +135,16 @@ func WebAuthnCredentialPUT(ctx *middlewares.AutheliaCtx) {  	if len(bodyJSON.Description) == 0 {  		ctx.Logger.WithError(fmt.Errorf("description is empty")).Errorf("Error occurred modifying WebAuthn credential for user '%s", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed)  		return  	}  	if credential, err = ctx.Providers.StorageProvider.LoadWebAuthnCredentialByID(ctx, id); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred trying to load the credential", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred loading the credential from the storage backend", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed)  		return @@ -146,14 +153,49 @@ func WebAuthnCredentialPUT(ctx *middlewares.AutheliaCtx) {  	if credential.Username != userSession.Username {  		ctx.Logger.WithError(fmt.Errorf("user '%s' owns the credential with id '%d'", credential.Username, credential.ID)).Errorf("Error occurred modifying WebAuthn credential for user '%s'", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageOperationFailed) + +		return +	} + +	if origin, err = ctx.GetOrigin(); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred determining the origin for the request", userSession.Username) + +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed)  		return  	} +	if credentials, err = ctx.Providers.StorageProvider.LoadWebAuthnCredentialsByUsername(ctx, origin.Hostname(), userSession.Username); err != nil { +		ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred looking up existing credentials", userSession.Username) + +		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageOperationFailed) + +		return +	} + +	for _, c := range credentials { +		if c.ID == id { +			continue +		} + +		if c.Description == bodyJSON.Description { +			ctx.Logger.WithError(fmt.Errorf("credential with id '%d' also has the description '%s'", c.ID, bodyJSON.Description)).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred ensuring the credentials had unique descriptions", userSession.Username) + +			ctx.SetStatusCode(fasthttp.StatusConflict) +			ctx.SetJSONError(messageOperationFailed) + +			return +		} +	} +  	if err = ctx.Providers.StorageProvider.UpdateWebAuthnCredentialDescription(ctx, userSession.Username, id, bodyJSON.Description); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred while attempting to save the modified credential in storage", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred modifying WebAuthn credential for user '%s': error occurred while attempting to update the modified credential in the storage backend", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed)  		return @@ -172,33 +214,36 @@ func WebAuthnCredentialDELETE(ctx *middlewares.AutheliaCtx) {  	)  	if userSession, err = ctx.GetSession(); err != nil { -		ctx.Logger.WithError(err).Error("Error occurred deleting WebAuthn credential: error occurred loading session data") +		ctx.Logger.WithError(err).Errorf("Error occurred deleting WebAuthn credential: %s", errStrUserSessionData) -		ctx.SetJSONError(messageOperationFailed)  		ctx.SetStatusCode(fasthttp.StatusForbidden) +		ctx.SetJSONError(messageOperationFailed)  		return  	}  	if userSession.IsAnonymous() { -		ctx.Logger.WithError(fmt.Errorf("user is anonymous")).Errorf("Error occurred modifying WebAuthn credential") +		ctx.Logger.WithError(errUserAnonymous).Errorf("Error occurred modifying WebAuthn credential") +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed)  		return  	}  	if id, err = getWebAuthnCredentialIDFromContext(ctx); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred deleting WebAuthn credential: error occurred trying to determine the credential ID") +		ctx.Logger.WithError(err).Errorf("Error occurred deleting WebAuthn credential for user '%s': error occurred trying to determine the credential ID", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusBadRequest)  		ctx.SetJSONError(messageOperationFailed)  		return  	}  	if credential, err = ctx.Providers.StorageProvider.LoadWebAuthnCredentialByID(ctx, id); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred deleting WebAuthn credential for user '%s': error occurred trying to load the credential", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred deleting WebAuthn credential for user '%s': error occurred trying to load the credential from the storage backend", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed)  		return @@ -207,14 +252,16 @@ func WebAuthnCredentialDELETE(ctx *middlewares.AutheliaCtx) {  	if credential.Username != userSession.Username {  		ctx.Logger.WithError(fmt.Errorf("user '%s' owns the credential with id '%d'", credential.Username, credential.ID)).Errorf("Error occurred deleting WebAuthn credential for user '%s'", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed)  		return  	}  	if err = ctx.Providers.StorageProvider.DeleteWebAuthnCredential(ctx, credential.KID.String()); err != nil { -		ctx.Logger.WithError(err).Errorf("Error occurred delete WebAuthn credential for user '%s': error occurred while attempting to delete the credential from storage", userSession.Username) +		ctx.Logger.WithError(err).Errorf("Error occurred delete WebAuthn credential for user '%s': error occurred while attempting to delete the credential from the storage backend", userSession.Username) +		ctx.SetStatusCode(fasthttp.StatusForbidden)  		ctx.SetJSONError(messageOperationFailed)  		return diff --git a/internal/handlers/handler_webauthn_credentials_test.go b/internal/handlers/handler_webauthn_credentials_test.go index d95195f48..374088693 100644 --- a/internal/handlers/handler_webauthn_credentials_test.go +++ b/internal/handlers/handler_webauthn_credentials_test.go @@ -5,10 +5,10 @@ import (  	"net/mail"  	"testing" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/authentication"  	"github.com/authelia/authelia/v4/internal/mocks" @@ -138,7 +138,7 @@ func TestWebAuthnCredentialsGET(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusOK,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred loading WebAuthn credentials for user 'john'", "bad block") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred loading WebAuthn credentials for user 'john': error occurred loading credentials from the storage backend", "bad block")  			},  		},  		{ @@ -204,8 +204,20 @@ func TestWebAuthnCredentialsPUT(t *testing.T) {  				require.NoError(t, mock.Ctx.SaveSession(us)) -				mock.StorageMock.EXPECT().LoadWebAuthnCredentialByID(mock.Ctx, 1).Return(&model.WebAuthnCredential{ID: 1, Username: testUsername}, nil) -				mock.StorageMock.EXPECT().UpdateWebAuthnCredentialDescription(mock.Ctx, testUsername, 1, "abc").Return(nil) +				gomock.InOrder( +					mock.StorageMock. +						EXPECT(). +						LoadWebAuthnCredentialByID(mock.Ctx, 1). +						Return(&model.WebAuthnCredential{ID: 1, Username: testUsername}, nil), +					mock.StorageMock. +						EXPECT(). +						LoadWebAuthnCredentialsByUsername(mock.Ctx, exampleDotCom, testUsername). +						Return([]model.WebAuthnCredential{{ID: 1, Username: testUsername}}, nil), +					mock.StorageMock. +						EXPECT(). +						UpdateWebAuthnCredentialDescription(mock.Ctx, testUsername, 1, "abc"). +						Return(nil), +				)  			},  			`{"description":"abc"}`,  			`{"status":"OK"}`, @@ -213,6 +225,94 @@ func TestWebAuthnCredentialsPUT(t *testing.T) {  			nil,  		},  		{ +			"ShouldHandleDuplicateNames", +			func(t *testing.T, mock *mocks.MockAutheliaCtx) { +				us, err := mock.Ctx.GetSession() + +				require.NoError(t, err) + +				us.Username = testUsername +				us.AuthenticationLevel = authentication.OneFactor + +				require.NoError(t, mock.Ctx.SaveSession(us)) + +				gomock.InOrder( +					mock.StorageMock. +						EXPECT(). +						LoadWebAuthnCredentialByID(mock.Ctx, 1). +						Return(&model.WebAuthnCredential{ID: 1, Username: testUsername}, nil), +					mock.StorageMock. +						EXPECT(). +						LoadWebAuthnCredentialsByUsername(mock.Ctx, exampleDotCom, testUsername). +						Return([]model.WebAuthnCredential{{ID: 1, Username: testUsername}, {ID: 2, Description: "abc", Username: testUsername}}, nil), +				) +			}, +			`{"description":"abc"}`, +			`{"status":"KO","message":"Operation failed."}`, +			fasthttp.StatusConflict, +			func(t *testing.T, mock *mocks.MockAutheliaCtx) { +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john': error occurred ensuring the credentials had unique descriptions", "credential with id '2' also has the description 'abc'") +			}, +		}, +		{ +			"ShouldHandleDuplicateFail", +			func(t *testing.T, mock *mocks.MockAutheliaCtx) { +				us, err := mock.Ctx.GetSession() + +				require.NoError(t, err) + +				us.Username = testUsername +				us.AuthenticationLevel = authentication.OneFactor + +				require.NoError(t, mock.Ctx.SaveSession(us)) + +				gomock.InOrder( +					mock.StorageMock. +						EXPECT(). +						LoadWebAuthnCredentialByID(mock.Ctx, 1). +						Return(&model.WebAuthnCredential{ID: 1, Username: testUsername}, nil), +					mock.StorageMock. +						EXPECT(). +						LoadWebAuthnCredentialsByUsername(mock.Ctx, exampleDotCom, testUsername). +						Return(nil, fmt.Errorf("oops")), +				) +			}, +			`{"description":"abc"}`, +			`{"status":"KO","message":"Operation failed."}`, +			fasthttp.StatusForbidden, +			func(t *testing.T, mock *mocks.MockAutheliaCtx) { +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john': error occurred looking up existing credentials", "oops") +			}, +		}, +		{ +			"ShouldHandleBadOrigin", +			func(t *testing.T, mock *mocks.MockAutheliaCtx) { +				us, err := mock.Ctx.GetSession() + +				require.NoError(t, err) + +				us.Username = testUsername +				us.AuthenticationLevel = authentication.OneFactor + +				require.NoError(t, mock.Ctx.SaveSession(us)) + +				gomock.InOrder( +					mock.StorageMock. +						EXPECT(). +						LoadWebAuthnCredentialByID(mock.Ctx, 1). +						Return(&model.WebAuthnCredential{ID: 1, Username: testUsername}, nil), +				) + +				mock.Ctx.Request.Header.Set("X-Original-URL", "##!@#!@") +			}, +			`{"description":"abc"}`, +			`{"status":"KO","message":"Operation failed."}`, +			fasthttp.StatusForbidden, +			func(t *testing.T, mock *mocks.MockAutheliaCtx) { +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john': error occurred determining the origin for the request", "failed to parse X-Original-URL header: parse \"##!@#!@\": invalid URI for request") +			}, +		}, +		{  			"ShouldHandleAnotherUser",  			func(t *testing.T, mock *mocks.MockAutheliaCtx) {  				us, err := mock.Ctx.GetSession() @@ -224,11 +324,16 @@ func TestWebAuthnCredentialsPUT(t *testing.T) {  				require.NoError(t, mock.Ctx.SaveSession(us)) -				mock.StorageMock.EXPECT().LoadWebAuthnCredentialByID(mock.Ctx, 1).Return(&model.WebAuthnCredential{ID: 1, Username: "anotheruser"}, nil) +				gomock.InOrder( +					mock.StorageMock. +						EXPECT(). +						LoadWebAuthnCredentialByID(mock.Ctx, 1). +						Return(&model.WebAuthnCredential{ID: 1, Username: "anotheruser"}, nil), +				)  			},  			`{"description":"abc"}`,  			`{"status":"KO","message":"Operation failed."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) {  				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john'", "user 'anotheruser' owns the credential with id '1'")  			}, @@ -245,14 +350,26 @@ func TestWebAuthnCredentialsPUT(t *testing.T) {  				require.NoError(t, mock.Ctx.SaveSession(us)) -				mock.StorageMock.EXPECT().LoadWebAuthnCredentialByID(mock.Ctx, 1).Return(&model.WebAuthnCredential{ID: 1, Username: testUsername}, nil) -				mock.StorageMock.EXPECT().UpdateWebAuthnCredentialDescription(mock.Ctx, testUsername, 1, "abc").Return(fmt.Errorf("gremlin")) +				gomock.InOrder( +					mock.StorageMock. +						EXPECT(). +						LoadWebAuthnCredentialByID(mock.Ctx, 1). +						Return(&model.WebAuthnCredential{ID: 1, Username: testUsername}, nil), +					mock.StorageMock. +						EXPECT(). +						LoadWebAuthnCredentialsByUsername(mock.Ctx, exampleDotCom, testUsername). +						Return([]model.WebAuthnCredential{{ID: 1, Username: testUsername}}, nil), +					mock.StorageMock. +						EXPECT(). +						UpdateWebAuthnCredentialDescription(mock.Ctx, testUsername, 1, "abc"). +						Return(fmt.Errorf("gremlin")), +				)  			},  			`{"description":"abc"}`,  			`{"status":"KO","message":"Operation failed."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john': error occurred while attempting to save the modified credential in storage", "gremlin") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john': error occurred while attempting to update the modified credential in the storage backend", "gremlin")  			},  		},  		{ @@ -271,9 +388,9 @@ func TestWebAuthnCredentialsPUT(t *testing.T) {  			},  			`{"description":"abc"}`,  			`{"status":"KO","message":"Operation failed."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john': error occurred trying to load the credential", "deleted") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john': error occurred loading the credential from the storage backend", "deleted")  			},  		},  		{ @@ -292,7 +409,7 @@ func TestWebAuthnCredentialsPUT(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusBadRequest,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential: error occurred parsing the form data", "invalid character 'a' after object key") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john': error parsing the request body", "invalid character 'a' after object key")  			},  		},  		{ @@ -309,7 +426,7 @@ func TestWebAuthnCredentialsPUT(t *testing.T) {  			},  			`{"description":""}`,  			`{"status":"KO","message":"Operation failed."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) {  				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john", "description is empty")  			}, @@ -319,7 +436,7 @@ func TestWebAuthnCredentialsPUT(t *testing.T) {  			nil,  			`{"description":"abc"}`,  			`{"status":"KO","message":"Operation failed."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) {  				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential", "user is anonymous")  			}, @@ -496,9 +613,9 @@ func TestWebAuthnCredentialsDELETE(t *testing.T) {  				)  			},  			`{"status":"KO","message":"Operation failed."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred delete WebAuthn credential for user 'john': error occurred while attempting to delete the credential from storage", "bad pipe") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred delete WebAuthn credential for user 'john': error occurred while attempting to delete the credential from the storage backend", "bad pipe")  			},  		},  		{ @@ -520,9 +637,9 @@ func TestWebAuthnCredentialsDELETE(t *testing.T) {  				)  			},  			`{"status":"KO","message":"Operation failed."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred deleting WebAuthn credential for user 'john': error occurred trying to load the credential", "bad sql password") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred deleting WebAuthn credential for user 'john': error occurred trying to load the credential from the storage backend", "bad sql password")  			},  		},  		{ @@ -544,7 +661,7 @@ func TestWebAuthnCredentialsDELETE(t *testing.T) {  				)  			},  			`{"status":"KO","message":"Operation failed."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) {  				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred deleting WebAuthn credential for user 'john'", "user 'baduser' owns the credential with id '1'")  			}, @@ -566,14 +683,14 @@ func TestWebAuthnCredentialsDELETE(t *testing.T) {  			`{"status":"KO","message":"Operation failed."}`,  			fasthttp.StatusBadRequest,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) { -				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred deleting WebAuthn credential: error occurred trying to determine the credential ID", "strconv.Atoi: parsing \"a\": invalid syntax") +				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred deleting WebAuthn credential for user 'john': error occurred trying to determine the credential ID", "strconv.Atoi: parsing \"a\": invalid syntax")  			},  		},  		{  			"ShouldHandleAnonymous",  			nil,  			`{"status":"KO","message":"Operation failed."}`, -			fasthttp.StatusOK, +			fasthttp.StatusForbidden,  			func(t *testing.T, mock *mocks.MockAutheliaCtx) {  				AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential", "user is anonymous")  			}, diff --git a/internal/handlers/webauthn_test.go b/internal/handlers/webauthn_test.go index 100db5ab8..480affd34 100644 --- a/internal/handlers/webauthn_test.go +++ b/internal/handlers/webauthn_test.go @@ -6,10 +6,10 @@ import (  	"testing"  	"github.com/go-webauthn/webauthn/protocol" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/mocks"  	"github.com/authelia/authelia/v4/internal/model" diff --git a/internal/middlewares/authelia_context_test.go b/internal/middlewares/authelia_context_test.go index 2430d846c..c20b9df88 100644 --- a/internal/middlewares/authelia_context_test.go +++ b/internal/middlewares/authelia_context_test.go @@ -5,10 +5,10 @@ import (  	"net/url"  	"testing" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/configuration/schema"  	"github.com/authelia/authelia/v4/internal/middlewares" diff --git a/internal/middlewares/identity_verification_test.go b/internal/middlewares/identity_verification_test.go index 011eb3274..54e81bb5d 100644 --- a/internal/middlewares/identity_verification_test.go +++ b/internal/middlewares/identity_verification_test.go @@ -7,11 +7,11 @@ import (  	"time"  	"github.com/golang-jwt/jwt/v5" -	"github.com/golang/mock/gomock"  	"github.com/google/uuid"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/suite"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/middlewares"  	"github.com/authelia/authelia/v4/internal/mocks" diff --git a/internal/mocks/authelia_ctx.go b/internal/mocks/authelia_ctx.go index 906a3a72c..b51207112 100644 --- a/internal/mocks/authelia_ctx.go +++ b/internal/mocks/authelia_ctx.go @@ -7,12 +7,12 @@ import (  	"testing"  	"time" -	"github.com/golang/mock/gomock"  	"github.com/sirupsen/logrus"  	"github.com/sirupsen/logrus/hooks/test"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/authorization"  	"github.com/authelia/authelia/v4/internal/clock" @@ -263,6 +263,24 @@ func (m *MockAutheliaCtx) Assert401KO(t *testing.T, message string) {  	assert.Equal(t, fmt.Sprintf("{\"status\":\"KO\",\"message\":\"%s\"}", message), string(m.Ctx.Response.Body()))  } +// Assert403KO assert an error response from the service. +func (m *MockAutheliaCtx) Assert403KO(t *testing.T, message string) { +	assert.Equal(t, fasthttp.StatusForbidden, m.Ctx.Response.StatusCode()) +	assert.Equal(t, fmt.Sprintf("{\"status\":\"KO\",\"message\":\"%s\"}", message), string(m.Ctx.Response.Body())) +} + +// Assert404KO assert an error response from the service. +func (m *MockAutheliaCtx) Assert404KO(t *testing.T, message string) { +	assert.Equal(t, fasthttp.StatusNotFound, m.Ctx.Response.StatusCode()) +	assert.Equal(t, fmt.Sprintf("{\"status\":\"KO\",\"message\":\"%s\"}", message), string(m.Ctx.Response.Body())) +} + +// Assert500KO assert an error response from the service. +func (m *MockAutheliaCtx) Assert500KO(t *testing.T, message string) { +	assert.Equal(t, fasthttp.StatusInternalServerError, m.Ctx.Response.StatusCode()) +	assert.Equal(t, fmt.Sprintf("{\"status\":\"KO\",\"message\":\"%s\"}", message), string(m.Ctx.Response.Body())) +} +  // Assert200KO assert an error response from the service.  func (m *MockAutheliaCtx) Assert200KO(t *testing.T, message string) {  	assert.Equal(t, fasthttp.StatusOK, m.Ctx.Response.StatusCode()) diff --git a/internal/mocks/duo_api.go b/internal/mocks/duo_api.go index d358f9c74..655dc331b 100644 --- a/internal/mocks/duo_api.go +++ b/internal/mocks/duo_api.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/authelia/authelia/v4/internal/duo (interfaces: API) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination duo_api.go -mock_names API=MockAPI github.com/authelia/authelia/v4/internal/duo API +//  // Package mocks is a generated GoMock package.  package mocks @@ -11,7 +15,7 @@ import (  	duo "github.com/authelia/authelia/v4/internal/duo"  	middlewares "github.com/authelia/authelia/v4/internal/middlewares"  	session "github.com/authelia/authelia/v4/internal/session" -	gomock "github.com/golang/mock/gomock" +	gomock "go.uber.org/mock/gomock"  )  // MockAPI is a mock of API interface. @@ -47,7 +51,7 @@ func (m *MockAPI) AuthCall(arg0 *middlewares.AutheliaCtx, arg1 *session.UserSess  }  // AuthCall indicates an expected call of AuthCall. -func (mr *MockAPIMockRecorder) AuthCall(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockAPIMockRecorder) AuthCall(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthCall", reflect.TypeOf((*MockAPI)(nil).AuthCall), arg0, arg1, arg2)  } @@ -62,7 +66,7 @@ func (m *MockAPI) Call(arg0 *middlewares.AutheliaCtx, arg1 *session.UserSession,  }  // Call indicates an expected call of Call. -func (mr *MockAPIMockRecorder) Call(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockAPIMockRecorder) Call(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Call", reflect.TypeOf((*MockAPI)(nil).Call), arg0, arg1, arg2, arg3, arg4)  } @@ -77,7 +81,7 @@ func (m *MockAPI) PreAuthCall(arg0 *middlewares.AutheliaCtx, arg1 *session.UserS  }  // PreAuthCall indicates an expected call of PreAuthCall. -func (mr *MockAPIMockRecorder) PreAuthCall(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockAPIMockRecorder) PreAuthCall(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PreAuthCall", reflect.TypeOf((*MockAPI)(nil).PreAuthCall), arg0, arg1, arg2)  } diff --git a/internal/mocks/fosite_access_requester.go b/internal/mocks/fosite_access_requester.go index 91ffa6a8a..cea0a3a64 100644 --- a/internal/mocks/fosite_access_requester.go +++ b/internal/mocks/fosite_access_requester.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/ory/fosite (interfaces: AccessRequester) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination fosite_access_requester.go -mock_names Provider=MockAccessRequester github.com/ory/fosite AccessRequester +//  // Package mocks is a generated GoMock package.  package mocks @@ -9,8 +13,8 @@ import (  	reflect "reflect"  	time "time" -	gomock "github.com/golang/mock/gomock"  	fosite "github.com/ory/fosite" +	gomock "go.uber.org/mock/gomock"  )  // MockAccessRequester is a mock of AccessRequester interface. @@ -43,7 +47,7 @@ func (m *MockAccessRequester) AppendRequestedScope(arg0 string) {  }  // AppendRequestedScope indicates an expected call of AppendRequestedScope. -func (mr *MockAccessRequesterMockRecorder) AppendRequestedScope(arg0 interface{}) *gomock.Call { +func (mr *MockAccessRequesterMockRecorder) AppendRequestedScope(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendRequestedScope", reflect.TypeOf((*MockAccessRequester)(nil).AppendRequestedScope), arg0)  } @@ -195,7 +199,7 @@ func (m *MockAccessRequester) GrantAudience(arg0 string) {  }  // GrantAudience indicates an expected call of GrantAudience. -func (mr *MockAccessRequesterMockRecorder) GrantAudience(arg0 interface{}) *gomock.Call { +func (mr *MockAccessRequesterMockRecorder) GrantAudience(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantAudience", reflect.TypeOf((*MockAccessRequester)(nil).GrantAudience), arg0)  } @@ -207,7 +211,7 @@ func (m *MockAccessRequester) GrantScope(arg0 string) {  }  // GrantScope indicates an expected call of GrantScope. -func (mr *MockAccessRequesterMockRecorder) GrantScope(arg0 interface{}) *gomock.Call { +func (mr *MockAccessRequesterMockRecorder) GrantScope(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GrantScope", reflect.TypeOf((*MockAccessRequester)(nil).GrantScope), arg0)  } @@ -219,7 +223,7 @@ func (m *MockAccessRequester) Merge(arg0 fosite.Requester) {  }  // Merge indicates an expected call of Merge. -func (mr *MockAccessRequesterMockRecorder) Merge(arg0 interface{}) *gomock.Call { +func (mr *MockAccessRequesterMockRecorder) Merge(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Merge", reflect.TypeOf((*MockAccessRequester)(nil).Merge), arg0)  } @@ -233,7 +237,7 @@ func (m *MockAccessRequester) Sanitize(arg0 []string) fosite.Requester {  }  // Sanitize indicates an expected call of Sanitize. -func (mr *MockAccessRequesterMockRecorder) Sanitize(arg0 interface{}) *gomock.Call { +func (mr *MockAccessRequesterMockRecorder) Sanitize(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sanitize", reflect.TypeOf((*MockAccessRequester)(nil).Sanitize), arg0)  } @@ -245,7 +249,7 @@ func (m *MockAccessRequester) SetID(arg0 string) {  }  // SetID indicates an expected call of SetID. -func (mr *MockAccessRequesterMockRecorder) SetID(arg0 interface{}) *gomock.Call { +func (mr *MockAccessRequesterMockRecorder) SetID(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetID", reflect.TypeOf((*MockAccessRequester)(nil).SetID), arg0)  } @@ -257,7 +261,7 @@ func (m *MockAccessRequester) SetRequestedAudience(arg0 fosite.Arguments) {  }  // SetRequestedAudience indicates an expected call of SetRequestedAudience. -func (mr *MockAccessRequesterMockRecorder) SetRequestedAudience(arg0 interface{}) *gomock.Call { +func (mr *MockAccessRequesterMockRecorder) SetRequestedAudience(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetRequestedAudience", reflect.TypeOf((*MockAccessRequester)(nil).SetRequestedAudience), arg0)  } @@ -269,7 +273,7 @@ func (m *MockAccessRequester) SetRequestedScopes(arg0 fosite.Arguments) {  }  // SetRequestedScopes indicates an expected call of SetRequestedScopes. -func (mr *MockAccessRequesterMockRecorder) SetRequestedScopes(arg0 interface{}) *gomock.Call { +func (mr *MockAccessRequesterMockRecorder) SetRequestedScopes(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetRequestedScopes", reflect.TypeOf((*MockAccessRequester)(nil).SetRequestedScopes), arg0)  } @@ -281,7 +285,7 @@ func (m *MockAccessRequester) SetSession(arg0 fosite.Session) {  }  // SetSession indicates an expected call of SetSession. -func (mr *MockAccessRequesterMockRecorder) SetSession(arg0 interface{}) *gomock.Call { +func (mr *MockAccessRequesterMockRecorder) SetSession(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetSession", reflect.TypeOf((*MockAccessRequester)(nil).SetSession), arg0)  } diff --git a/internal/mocks/fosite_access_token_strategy.go b/internal/mocks/fosite_access_token_strategy.go index 0e3baa1c6..319c5fd6c 100644 --- a/internal/mocks/fosite_access_token_strategy.go +++ b/internal/mocks/fosite_access_token_strategy.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/ory/fosite/handler/oauth2 (interfaces: AccessTokenStrategy) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination fosite_access_token_strategy.go -mock_names Provider=MockAccessTokenStrategy github.com/ory/fosite/handler/oauth2 AccessTokenStrategy +//  // Package mocks is a generated GoMock package.  package mocks @@ -8,8 +12,8 @@ import (  	context "context"  	reflect "reflect" -	gomock "github.com/golang/mock/gomock"  	fosite "github.com/ory/fosite" +	gomock "go.uber.org/mock/gomock"  )  // MockAccessTokenStrategy is a mock of AccessTokenStrategy interface. @@ -44,7 +48,7 @@ func (m *MockAccessTokenStrategy) AccessTokenSignature(arg0 context.Context, arg  }  // AccessTokenSignature indicates an expected call of AccessTokenSignature. -func (mr *MockAccessTokenStrategyMockRecorder) AccessTokenSignature(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockAccessTokenStrategyMockRecorder) AccessTokenSignature(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AccessTokenSignature", reflect.TypeOf((*MockAccessTokenStrategy)(nil).AccessTokenSignature), arg0, arg1)  } @@ -60,7 +64,7 @@ func (m *MockAccessTokenStrategy) GenerateAccessToken(arg0 context.Context, arg1  }  // GenerateAccessToken indicates an expected call of GenerateAccessToken. -func (mr *MockAccessTokenStrategyMockRecorder) GenerateAccessToken(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockAccessTokenStrategyMockRecorder) GenerateAccessToken(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateAccessToken", reflect.TypeOf((*MockAccessTokenStrategy)(nil).GenerateAccessToken), arg0, arg1)  } @@ -74,7 +78,7 @@ func (m *MockAccessTokenStrategy) ValidateAccessToken(arg0 context.Context, arg1  }  // ValidateAccessToken indicates an expected call of ValidateAccessToken. -func (mr *MockAccessTokenStrategyMockRecorder) ValidateAccessToken(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockAccessTokenStrategyMockRecorder) ValidateAccessToken(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateAccessToken", reflect.TypeOf((*MockAccessTokenStrategy)(nil).ValidateAccessToken), arg0, arg1, arg2)  } diff --git a/internal/mocks/fosite_client_credentials_grant_storage.go b/internal/mocks/fosite_client_credentials_grant_storage.go index 077712a7d..a35d2bc52 100644 --- a/internal/mocks/fosite_client_credentials_grant_storage.go +++ b/internal/mocks/fosite_client_credentials_grant_storage.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/ory/fosite/handler/oauth2 (interfaces: ClientCredentialsGrantStorage) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination fosite_client_credentials_grant_storage.go -mock_names Provider=MockClientCredentialsGrantStorage github.com/ory/fosite/handler/oauth2 ClientCredentialsGrantStorage +//  // Package mocks is a generated GoMock package.  package mocks @@ -8,8 +12,8 @@ import (  	context "context"  	reflect "reflect" -	gomock "github.com/golang/mock/gomock"  	fosite "github.com/ory/fosite" +	gomock "go.uber.org/mock/gomock"  )  // MockClientCredentialsGrantStorage is a mock of ClientCredentialsGrantStorage interface. @@ -44,7 +48,7 @@ func (m *MockClientCredentialsGrantStorage) CreateAccessTokenSession(arg0 contex  }  // CreateAccessTokenSession indicates an expected call of CreateAccessTokenSession. -func (mr *MockClientCredentialsGrantStorageMockRecorder) CreateAccessTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockClientCredentialsGrantStorageMockRecorder) CreateAccessTokenSession(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAccessTokenSession", reflect.TypeOf((*MockClientCredentialsGrantStorage)(nil).CreateAccessTokenSession), arg0, arg1, arg2)  } @@ -58,7 +62,7 @@ func (m *MockClientCredentialsGrantStorage) DeleteAccessTokenSession(arg0 contex  }  // DeleteAccessTokenSession indicates an expected call of DeleteAccessTokenSession. -func (mr *MockClientCredentialsGrantStorageMockRecorder) DeleteAccessTokenSession(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockClientCredentialsGrantStorageMockRecorder) DeleteAccessTokenSession(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccessTokenSession", reflect.TypeOf((*MockClientCredentialsGrantStorage)(nil).DeleteAccessTokenSession), arg0, arg1)  } @@ -73,7 +77,7 @@ func (m *MockClientCredentialsGrantStorage) GetAccessTokenSession(arg0 context.C  }  // GetAccessTokenSession indicates an expected call of GetAccessTokenSession. -func (mr *MockClientCredentialsGrantStorageMockRecorder) GetAccessTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockClientCredentialsGrantStorageMockRecorder) GetAccessTokenSession(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccessTokenSession", reflect.TypeOf((*MockClientCredentialsGrantStorage)(nil).GetAccessTokenSession), arg0, arg1, arg2)  } diff --git a/internal/mocks/fosite_pkce_request_storage.go b/internal/mocks/fosite_pkce_request_storage.go index be8191bc0..1ada6e2d5 100644 --- a/internal/mocks/fosite_pkce_request_storage.go +++ b/internal/mocks/fosite_pkce_request_storage.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/ory/fosite/handler/pkce (interfaces: PKCERequestStorage) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination fosite_pkce_request_storage.go -mock_names Provider=MockPKCERequestStorage github.com/ory/fosite/handler/pkce PKCERequestStorage +//  // Package mocks is a generated GoMock package.  package mocks @@ -8,8 +12,8 @@ import (  	context "context"  	reflect "reflect" -	gomock "github.com/golang/mock/gomock"  	fosite "github.com/ory/fosite" +	gomock "go.uber.org/mock/gomock"  )  // MockPKCERequestStorage is a mock of PKCERequestStorage interface. @@ -44,7 +48,7 @@ func (m *MockPKCERequestStorage) CreatePKCERequestSession(arg0 context.Context,  }  // CreatePKCERequestSession indicates an expected call of CreatePKCERequestSession. -func (mr *MockPKCERequestStorageMockRecorder) CreatePKCERequestSession(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockPKCERequestStorageMockRecorder) CreatePKCERequestSession(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePKCERequestSession", reflect.TypeOf((*MockPKCERequestStorage)(nil).CreatePKCERequestSession), arg0, arg1, arg2)  } @@ -58,7 +62,7 @@ func (m *MockPKCERequestStorage) DeletePKCERequestSession(arg0 context.Context,  }  // DeletePKCERequestSession indicates an expected call of DeletePKCERequestSession. -func (mr *MockPKCERequestStorageMockRecorder) DeletePKCERequestSession(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockPKCERequestStorageMockRecorder) DeletePKCERequestSession(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePKCERequestSession", reflect.TypeOf((*MockPKCERequestStorage)(nil).DeletePKCERequestSession), arg0, arg1)  } @@ -73,7 +77,7 @@ func (m *MockPKCERequestStorage) GetPKCERequestSession(arg0 context.Context, arg  }  // GetPKCERequestSession indicates an expected call of GetPKCERequestSession. -func (mr *MockPKCERequestStorageMockRecorder) GetPKCERequestSession(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockPKCERequestStorageMockRecorder) GetPKCERequestSession(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPKCERequestSession", reflect.TypeOf((*MockPKCERequestStorage)(nil).GetPKCERequestSession), arg0, arg1, arg2)  } diff --git a/internal/mocks/fosite_storage.go b/internal/mocks/fosite_storage.go index aea59a69d..e7b99271b 100644 --- a/internal/mocks/fosite_storage.go +++ b/internal/mocks/fosite_storage.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/ory/fosite (interfaces: Storage) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination fosite_storage.go -mock_names Storage=MockFositeStorage github.com/ory/fosite Storage +//  // Package mocks is a generated GoMock package.  package mocks @@ -9,8 +13,8 @@ import (  	reflect "reflect"  	time "time" -	gomock "github.com/golang/mock/gomock"  	fosite "github.com/ory/fosite" +	gomock "go.uber.org/mock/gomock"  )  // MockFositeStorage is a mock of Storage interface. @@ -45,7 +49,7 @@ func (m *MockFositeStorage) ClientAssertionJWTValid(arg0 context.Context, arg1 s  }  // ClientAssertionJWTValid indicates an expected call of ClientAssertionJWTValid. -func (mr *MockFositeStorageMockRecorder) ClientAssertionJWTValid(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockFositeStorageMockRecorder) ClientAssertionJWTValid(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClientAssertionJWTValid", reflect.TypeOf((*MockFositeStorage)(nil).ClientAssertionJWTValid), arg0, arg1)  } @@ -60,7 +64,7 @@ func (m *MockFositeStorage) GetClient(arg0 context.Context, arg1 string) (fosite  }  // GetClient indicates an expected call of GetClient. -func (mr *MockFositeStorageMockRecorder) GetClient(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockFositeStorageMockRecorder) GetClient(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetClient", reflect.TypeOf((*MockFositeStorage)(nil).GetClient), arg0, arg1)  } @@ -74,7 +78,7 @@ func (m *MockFositeStorage) SetClientAssertionJWT(arg0 context.Context, arg1 str  }  // SetClientAssertionJWT indicates an expected call of SetClientAssertionJWT. -func (mr *MockFositeStorageMockRecorder) SetClientAssertionJWT(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockFositeStorageMockRecorder) SetClientAssertionJWT(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetClientAssertionJWT", reflect.TypeOf((*MockFositeStorage)(nil).SetClientAssertionJWT), arg0, arg1, arg2)  } diff --git a/internal/mocks/fosite_token_introspector.go b/internal/mocks/fosite_token_introspector.go index 0dd8ebd66..52760ea76 100644 --- a/internal/mocks/fosite_token_introspector.go +++ b/internal/mocks/fosite_token_introspector.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/ory/fosite (interfaces: TokenIntrospector) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination fosite_token_introspector.go -mock_names TokenIntrospector=MockTokenIntrospector github.com/ory/fosite TokenIntrospector +//  // Package mocks is a generated GoMock package.  package mocks @@ -8,8 +12,8 @@ import (  	context "context"  	reflect "reflect" -	gomock "github.com/golang/mock/gomock"  	fosite "github.com/ory/fosite" +	gomock "go.uber.org/mock/gomock"  )  // MockTokenIntrospector is a mock of TokenIntrospector interface. @@ -45,7 +49,7 @@ func (m *MockTokenIntrospector) IntrospectToken(arg0 context.Context, arg1 strin  }  // IntrospectToken indicates an expected call of IntrospectToken. -func (mr *MockTokenIntrospectorMockRecorder) IntrospectToken(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockTokenIntrospectorMockRecorder) IntrospectToken(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IntrospectToken", reflect.TypeOf((*MockTokenIntrospector)(nil).IntrospectToken), arg0, arg1, arg2, arg3, arg4)  } diff --git a/internal/mocks/fosite_token_revocation_storage.go b/internal/mocks/fosite_token_revocation_storage.go index b47d9aef6..d9836c373 100644 --- a/internal/mocks/fosite_token_revocation_storage.go +++ b/internal/mocks/fosite_token_revocation_storage.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/ory/fosite/handler/oauth2 (interfaces: TokenRevocationStorage) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination fosite_token_revocation_storage.go -mock_names Provider=MockTokenRevocationStorage github.com/ory/fosite/handler/oauth2 TokenRevocationStorage +//  // Package mocks is a generated GoMock package.  package mocks @@ -8,8 +12,8 @@ import (  	context "context"  	reflect "reflect" -	gomock "github.com/golang/mock/gomock"  	fosite "github.com/ory/fosite" +	gomock "go.uber.org/mock/gomock"  )  // MockTokenRevocationStorage is a mock of TokenRevocationStorage interface. @@ -44,7 +48,7 @@ func (m *MockTokenRevocationStorage) CreateAccessTokenSession(arg0 context.Conte  }  // CreateAccessTokenSession indicates an expected call of CreateAccessTokenSession. -func (mr *MockTokenRevocationStorageMockRecorder) CreateAccessTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockTokenRevocationStorageMockRecorder) CreateAccessTokenSession(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAccessTokenSession", reflect.TypeOf((*MockTokenRevocationStorage)(nil).CreateAccessTokenSession), arg0, arg1, arg2)  } @@ -58,7 +62,7 @@ func (m *MockTokenRevocationStorage) CreateRefreshTokenSession(arg0 context.Cont  }  // CreateRefreshTokenSession indicates an expected call of CreateRefreshTokenSession. -func (mr *MockTokenRevocationStorageMockRecorder) CreateRefreshTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockTokenRevocationStorageMockRecorder) CreateRefreshTokenSession(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateRefreshTokenSession", reflect.TypeOf((*MockTokenRevocationStorage)(nil).CreateRefreshTokenSession), arg0, arg1, arg2)  } @@ -72,7 +76,7 @@ func (m *MockTokenRevocationStorage) DeleteAccessTokenSession(arg0 context.Conte  }  // DeleteAccessTokenSession indicates an expected call of DeleteAccessTokenSession. -func (mr *MockTokenRevocationStorageMockRecorder) DeleteAccessTokenSession(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockTokenRevocationStorageMockRecorder) DeleteAccessTokenSession(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAccessTokenSession", reflect.TypeOf((*MockTokenRevocationStorage)(nil).DeleteAccessTokenSession), arg0, arg1)  } @@ -86,7 +90,7 @@ func (m *MockTokenRevocationStorage) DeleteRefreshTokenSession(arg0 context.Cont  }  // DeleteRefreshTokenSession indicates an expected call of DeleteRefreshTokenSession. -func (mr *MockTokenRevocationStorageMockRecorder) DeleteRefreshTokenSession(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockTokenRevocationStorageMockRecorder) DeleteRefreshTokenSession(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteRefreshTokenSession", reflect.TypeOf((*MockTokenRevocationStorage)(nil).DeleteRefreshTokenSession), arg0, arg1)  } @@ -101,7 +105,7 @@ func (m *MockTokenRevocationStorage) GetAccessTokenSession(arg0 context.Context,  }  // GetAccessTokenSession indicates an expected call of GetAccessTokenSession. -func (mr *MockTokenRevocationStorageMockRecorder) GetAccessTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockTokenRevocationStorageMockRecorder) GetAccessTokenSession(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAccessTokenSession", reflect.TypeOf((*MockTokenRevocationStorage)(nil).GetAccessTokenSession), arg0, arg1, arg2)  } @@ -116,7 +120,7 @@ func (m *MockTokenRevocationStorage) GetRefreshTokenSession(arg0 context.Context  }  // GetRefreshTokenSession indicates an expected call of GetRefreshTokenSession. -func (mr *MockTokenRevocationStorageMockRecorder) GetRefreshTokenSession(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockTokenRevocationStorageMockRecorder) GetRefreshTokenSession(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRefreshTokenSession", reflect.TypeOf((*MockTokenRevocationStorage)(nil).GetRefreshTokenSession), arg0, arg1, arg2)  } @@ -130,7 +134,7 @@ func (m *MockTokenRevocationStorage) RevokeAccessToken(arg0 context.Context, arg  }  // RevokeAccessToken indicates an expected call of RevokeAccessToken. -func (mr *MockTokenRevocationStorageMockRecorder) RevokeAccessToken(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockTokenRevocationStorageMockRecorder) RevokeAccessToken(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeAccessToken", reflect.TypeOf((*MockTokenRevocationStorage)(nil).RevokeAccessToken), arg0, arg1)  } @@ -144,7 +148,7 @@ func (m *MockTokenRevocationStorage) RevokeRefreshToken(arg0 context.Context, ar  }  // RevokeRefreshToken indicates an expected call of RevokeRefreshToken. -func (mr *MockTokenRevocationStorageMockRecorder) RevokeRefreshToken(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockTokenRevocationStorageMockRecorder) RevokeRefreshToken(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeRefreshToken", reflect.TypeOf((*MockTokenRevocationStorage)(nil).RevokeRefreshToken), arg0, arg1)  } @@ -158,7 +162,7 @@ func (m *MockTokenRevocationStorage) RevokeRefreshTokenMaybeGracePeriod(arg0 con  }  // RevokeRefreshTokenMaybeGracePeriod indicates an expected call of RevokeRefreshTokenMaybeGracePeriod. -func (mr *MockTokenRevocationStorageMockRecorder) RevokeRefreshTokenMaybeGracePeriod(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockTokenRevocationStorageMockRecorder) RevokeRefreshTokenMaybeGracePeriod(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeRefreshTokenMaybeGracePeriod", reflect.TypeOf((*MockTokenRevocationStorage)(nil).RevokeRefreshTokenMaybeGracePeriod), arg0, arg1, arg2)  } diff --git a/internal/mocks/fosite_transactional.go b/internal/mocks/fosite_transactional.go index 817bef67b..3ff7a11aa 100644 --- a/internal/mocks/fosite_transactional.go +++ b/internal/mocks/fosite_transactional.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/ory/fosite/storage (interfaces: Transactional) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination fosite_transactional.go -mock_names Provider=MockTransactional github.com/ory/fosite/storage Transactional +//  // Package mocks is a generated GoMock package.  package mocks @@ -8,7 +12,7 @@ import (  	context "context"  	reflect "reflect" -	gomock "github.com/golang/mock/gomock" +	gomock "go.uber.org/mock/gomock"  )  // MockTransactional is a mock of Transactional interface. @@ -44,7 +48,7 @@ func (m *MockTransactional) BeginTX(arg0 context.Context) (context.Context, erro  }  // BeginTX indicates an expected call of BeginTX. -func (mr *MockTransactionalMockRecorder) BeginTX(arg0 interface{}) *gomock.Call { +func (mr *MockTransactionalMockRecorder) BeginTX(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTX", reflect.TypeOf((*MockTransactional)(nil).BeginTX), arg0)  } @@ -58,7 +62,7 @@ func (m *MockTransactional) Commit(arg0 context.Context) error {  }  // Commit indicates an expected call of Commit. -func (mr *MockTransactionalMockRecorder) Commit(arg0 interface{}) *gomock.Call { +func (mr *MockTransactionalMockRecorder) Commit(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockTransactional)(nil).Commit), arg0)  } @@ -72,7 +76,7 @@ func (m *MockTransactional) Rollback(arg0 context.Context) error {  }  // Rollback indicates an expected call of Rollback. -func (mr *MockTransactionalMockRecorder) Rollback(arg0 interface{}) *gomock.Call { +func (mr *MockTransactionalMockRecorder) Rollback(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Rollback", reflect.TypeOf((*MockTransactional)(nil).Rollback), arg0)  } diff --git a/internal/mocks/notifier.go b/internal/mocks/notifier.go index b568e808c..6f31b8e7a 100644 --- a/internal/mocks/notifier.go +++ b/internal/mocks/notifier.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/authelia/authelia/v4/internal/notification (interfaces: Notifier) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination notifier.go -mock_names Notifier=MockNotifier github.com/authelia/authelia/v4/internal/notification Notifier +//  // Package mocks is a generated GoMock package.  package mocks @@ -10,7 +14,7 @@ import (  	reflect "reflect"  	templates "github.com/authelia/authelia/v4/internal/templates" -	gomock "github.com/golang/mock/gomock" +	gomock "go.uber.org/mock/gomock"  )  // MockNotifier is a mock of Notifier interface. @@ -37,7 +41,7 @@ func (m *MockNotifier) EXPECT() *MockNotifierMockRecorder {  }  // Send mocks base method. -func (m *MockNotifier) Send(arg0 context.Context, arg1 mail.Address, arg2 string, arg3 *templates.EmailTemplate, arg4 interface{}) error { +func (m *MockNotifier) Send(arg0 context.Context, arg1 mail.Address, arg2 string, arg3 *templates.EmailTemplate, arg4 any) error {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "Send", arg0, arg1, arg2, arg3, arg4)  	ret0, _ := ret[0].(error) @@ -45,7 +49,7 @@ func (m *MockNotifier) Send(arg0 context.Context, arg1 mail.Address, arg2 string  }  // Send indicates an expected call of Send. -func (mr *MockNotifierMockRecorder) Send(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockNotifierMockRecorder) Send(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockNotifier)(nil).Send), arg0, arg1, arg2, arg3, arg4)  } diff --git a/internal/mocks/random.go b/internal/mocks/random.go index 497bc4d1e..f566c59a1 100644 --- a/internal/mocks/random.go +++ b/internal/mocks/random.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/authelia/authelia/v4/internal/random (interfaces: Provider) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination random.go -mock_names Provider=MockRandom github.com/authelia/authelia/v4/internal/random Provider +//  // Package mocks is a generated GoMock package.  package mocks @@ -8,7 +12,7 @@ import (  	big "math/big"  	reflect "reflect" -	gomock "github.com/golang/mock/gomock" +	gomock "go.uber.org/mock/gomock"  )  // MockRandom is a mock of Provider interface. @@ -57,7 +61,7 @@ func (m *MockRandom) BytesCustom(arg0 int, arg1 []byte) []byte {  }  // BytesCustom indicates an expected call of BytesCustom. -func (mr *MockRandomMockRecorder) BytesCustom(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockRandomMockRecorder) BytesCustom(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BytesCustom", reflect.TypeOf((*MockRandom)(nil).BytesCustom), arg0, arg1)  } @@ -72,7 +76,7 @@ func (m *MockRandom) BytesCustomErr(arg0 int, arg1 []byte) ([]byte, error) {  }  // BytesCustomErr indicates an expected call of BytesCustomErr. -func (mr *MockRandomMockRecorder) BytesCustomErr(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockRandomMockRecorder) BytesCustomErr(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BytesCustomErr", reflect.TypeOf((*MockRandom)(nil).BytesCustomErr), arg0, arg1)  } @@ -101,7 +105,7 @@ func (m *MockRandom) Int(arg0 *big.Int) *big.Int {  }  // Int indicates an expected call of Int. -func (mr *MockRandomMockRecorder) Int(arg0 interface{}) *gomock.Call { +func (mr *MockRandomMockRecorder) Int(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Int", reflect.TypeOf((*MockRandom)(nil).Int), arg0)  } @@ -116,7 +120,7 @@ func (m *MockRandom) IntErr(arg0 *big.Int) (*big.Int, error) {  }  // IntErr indicates an expected call of IntErr. -func (mr *MockRandomMockRecorder) IntErr(arg0 interface{}) *gomock.Call { +func (mr *MockRandomMockRecorder) IntErr(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IntErr", reflect.TypeOf((*MockRandom)(nil).IntErr), arg0)  } @@ -130,7 +134,7 @@ func (m *MockRandom) Intn(arg0 int) int {  }  // Intn indicates an expected call of Intn. -func (mr *MockRandomMockRecorder) Intn(arg0 interface{}) *gomock.Call { +func (mr *MockRandomMockRecorder) Intn(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Intn", reflect.TypeOf((*MockRandom)(nil).Intn), arg0)  } @@ -145,7 +149,7 @@ func (m *MockRandom) IntnErr(arg0 int) (int, error) {  }  // IntnErr indicates an expected call of IntnErr. -func (mr *MockRandomMockRecorder) IntnErr(arg0 interface{}) *gomock.Call { +func (mr *MockRandomMockRecorder) IntnErr(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IntnErr", reflect.TypeOf((*MockRandom)(nil).IntnErr), arg0)  } @@ -160,7 +164,7 @@ func (m *MockRandom) Prime(arg0 int) (*big.Int, error) {  }  // Prime indicates an expected call of Prime. -func (mr *MockRandomMockRecorder) Prime(arg0 interface{}) *gomock.Call { +func (mr *MockRandomMockRecorder) Prime(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Prime", reflect.TypeOf((*MockRandom)(nil).Prime), arg0)  } @@ -175,7 +179,7 @@ func (m *MockRandom) Read(arg0 []byte) (int, error) {  }  // Read indicates an expected call of Read. -func (mr *MockRandomMockRecorder) Read(arg0 interface{}) *gomock.Call { +func (mr *MockRandomMockRecorder) Read(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockRandom)(nil).Read), arg0)  } @@ -189,7 +193,7 @@ func (m *MockRandom) StringCustom(arg0 int, arg1 string) string {  }  // StringCustom indicates an expected call of StringCustom. -func (mr *MockRandomMockRecorder) StringCustom(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockRandomMockRecorder) StringCustom(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StringCustom", reflect.TypeOf((*MockRandom)(nil).StringCustom), arg0, arg1)  } @@ -204,7 +208,7 @@ func (m *MockRandom) StringCustomErr(arg0 int, arg1 string) (string, error) {  }  // StringCustomErr indicates an expected call of StringCustomErr. -func (mr *MockRandomMockRecorder) StringCustomErr(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockRandomMockRecorder) StringCustomErr(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StringCustomErr", reflect.TypeOf((*MockRandom)(nil).StringCustomErr), arg0, arg1)  } diff --git a/internal/mocks/storage.go b/internal/mocks/storage.go index 26110b898..993a11737 100644 --- a/internal/mocks/storage.go +++ b/internal/mocks/storage.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/authelia/authelia/v4/internal/storage (interfaces: Provider) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination storage.go -mock_names Provider=MockStorage github.com/authelia/authelia/v4/internal/storage Provider +//  // Package mocks is a generated GoMock package.  package mocks @@ -12,8 +16,8 @@ import (  	model "github.com/authelia/authelia/v4/internal/model"  	storage "github.com/authelia/authelia/v4/internal/storage" -	gomock "github.com/golang/mock/gomock"  	uuid "github.com/google/uuid" +	gomock "go.uber.org/mock/gomock"  )  // MockStorage is a mock of Provider interface. @@ -48,7 +52,7 @@ func (m *MockStorage) AppendAuthenticationLog(arg0 context.Context, arg1 model.A  }  // AppendAuthenticationLog indicates an expected call of AppendAuthenticationLog. -func (mr *MockStorageMockRecorder) AppendAuthenticationLog(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) AppendAuthenticationLog(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendAuthenticationLog", reflect.TypeOf((*MockStorage)(nil).AppendAuthenticationLog), arg0, arg1)  } @@ -63,7 +67,7 @@ func (m *MockStorage) BeginTX(arg0 context.Context) (context.Context, error) {  }  // BeginTX indicates an expected call of BeginTX. -func (mr *MockStorageMockRecorder) BeginTX(arg0 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) BeginTX(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginTX", reflect.TypeOf((*MockStorage)(nil).BeginTX), arg0)  } @@ -91,7 +95,7 @@ func (m *MockStorage) Commit(arg0 context.Context) error {  }  // Commit indicates an expected call of Commit. -func (mr *MockStorageMockRecorder) Commit(arg0 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) Commit(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Commit", reflect.TypeOf((*MockStorage)(nil).Commit), arg0)  } @@ -105,7 +109,7 @@ func (m *MockStorage) ConsumeIdentityVerification(arg0 context.Context, arg1 str  }  // ConsumeIdentityVerification indicates an expected call of ConsumeIdentityVerification. -func (mr *MockStorageMockRecorder) ConsumeIdentityVerification(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) ConsumeIdentityVerification(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConsumeIdentityVerification", reflect.TypeOf((*MockStorage)(nil).ConsumeIdentityVerification), arg0, arg1, arg2)  } @@ -119,7 +123,7 @@ func (m *MockStorage) ConsumeOneTimeCode(arg0 context.Context, arg1 *model.OneTi  }  // ConsumeOneTimeCode indicates an expected call of ConsumeOneTimeCode. -func (mr *MockStorageMockRecorder) ConsumeOneTimeCode(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) ConsumeOneTimeCode(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConsumeOneTimeCode", reflect.TypeOf((*MockStorage)(nil).ConsumeOneTimeCode), arg0, arg1)  } @@ -133,7 +137,7 @@ func (m *MockStorage) DeactivateOAuth2Session(arg0 context.Context, arg1 storage  }  // DeactivateOAuth2Session indicates an expected call of DeactivateOAuth2Session. -func (mr *MockStorageMockRecorder) DeactivateOAuth2Session(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) DeactivateOAuth2Session(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeactivateOAuth2Session", reflect.TypeOf((*MockStorage)(nil).DeactivateOAuth2Session), arg0, arg1, arg2)  } @@ -147,7 +151,7 @@ func (m *MockStorage) DeactivateOAuth2SessionByRequestID(arg0 context.Context, a  }  // DeactivateOAuth2SessionByRequestID indicates an expected call of DeactivateOAuth2SessionByRequestID. -func (mr *MockStorageMockRecorder) DeactivateOAuth2SessionByRequestID(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) DeactivateOAuth2SessionByRequestID(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeactivateOAuth2SessionByRequestID", reflect.TypeOf((*MockStorage)(nil).DeactivateOAuth2SessionByRequestID), arg0, arg1, arg2)  } @@ -161,7 +165,7 @@ func (m *MockStorage) DeletePreferredDuoDevice(arg0 context.Context, arg1 string  }  // DeletePreferredDuoDevice indicates an expected call of DeletePreferredDuoDevice. -func (mr *MockStorageMockRecorder) DeletePreferredDuoDevice(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) DeletePreferredDuoDevice(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePreferredDuoDevice", reflect.TypeOf((*MockStorage)(nil).DeletePreferredDuoDevice), arg0, arg1)  } @@ -175,7 +179,7 @@ func (m *MockStorage) DeleteTOTPConfiguration(arg0 context.Context, arg1 string)  }  // DeleteTOTPConfiguration indicates an expected call of DeleteTOTPConfiguration. -func (mr *MockStorageMockRecorder) DeleteTOTPConfiguration(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) DeleteTOTPConfiguration(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteTOTPConfiguration", reflect.TypeOf((*MockStorage)(nil).DeleteTOTPConfiguration), arg0, arg1)  } @@ -189,7 +193,7 @@ func (m *MockStorage) DeleteWebAuthnCredential(arg0 context.Context, arg1 string  }  // DeleteWebAuthnCredential indicates an expected call of DeleteWebAuthnCredential. -func (mr *MockStorageMockRecorder) DeleteWebAuthnCredential(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) DeleteWebAuthnCredential(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWebAuthnCredential", reflect.TypeOf((*MockStorage)(nil).DeleteWebAuthnCredential), arg0, arg1)  } @@ -203,11 +207,26 @@ func (m *MockStorage) DeleteWebAuthnCredentialByUsername(arg0 context.Context, a  }  // DeleteWebAuthnCredentialByUsername indicates an expected call of DeleteWebAuthnCredentialByUsername. -func (mr *MockStorageMockRecorder) DeleteWebAuthnCredentialByUsername(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) DeleteWebAuthnCredentialByUsername(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteWebAuthnCredentialByUsername", reflect.TypeOf((*MockStorage)(nil).DeleteWebAuthnCredentialByUsername), arg0, arg1, arg2)  } +// ExistsTOTPHistory mocks base method. +func (m *MockStorage) ExistsTOTPHistory(arg0 context.Context, arg1 string, arg2 uint64, arg3 time.Time) (bool, error) { +	m.ctrl.T.Helper() +	ret := m.ctrl.Call(m, "ExistsTOTPHistory", arg0, arg1, arg2, arg3) +	ret0, _ := ret[0].(bool) +	ret1, _ := ret[1].(error) +	return ret0, ret1 +} + +// ExistsTOTPHistory indicates an expected call of ExistsTOTPHistory. +func (mr *MockStorageMockRecorder) ExistsTOTPHistory(arg0, arg1, arg2, arg3 any) *gomock.Call { +	mr.mock.ctrl.T.Helper() +	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExistsTOTPHistory", reflect.TypeOf((*MockStorage)(nil).ExistsTOTPHistory), arg0, arg1, arg2, arg3) +} +  // FindIdentityVerification mocks base method.  func (m *MockStorage) FindIdentityVerification(arg0 context.Context, arg1 string) (bool, error) {  	m.ctrl.T.Helper() @@ -218,7 +237,7 @@ func (m *MockStorage) FindIdentityVerification(arg0 context.Context, arg1 string  }  // FindIdentityVerification indicates an expected call of FindIdentityVerification. -func (mr *MockStorageMockRecorder) FindIdentityVerification(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) FindIdentityVerification(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindIdentityVerification", reflect.TypeOf((*MockStorage)(nil).FindIdentityVerification), arg0, arg1)  } @@ -233,7 +252,7 @@ func (m *MockStorage) LoadAuthenticationLogs(arg0 context.Context, arg1 string,  }  // LoadAuthenticationLogs indicates an expected call of LoadAuthenticationLogs. -func (mr *MockStorageMockRecorder) LoadAuthenticationLogs(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadAuthenticationLogs(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadAuthenticationLogs", reflect.TypeOf((*MockStorage)(nil).LoadAuthenticationLogs), arg0, arg1, arg2, arg3, arg4)  } @@ -248,7 +267,7 @@ func (m *MockStorage) LoadIdentityVerification(arg0 context.Context, arg1 string  }  // LoadIdentityVerification indicates an expected call of LoadIdentityVerification. -func (mr *MockStorageMockRecorder) LoadIdentityVerification(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadIdentityVerification(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadIdentityVerification", reflect.TypeOf((*MockStorage)(nil).LoadIdentityVerification), arg0, arg1)  } @@ -263,7 +282,7 @@ func (m *MockStorage) LoadOAuth2BlacklistedJTI(arg0 context.Context, arg1 string  }  // LoadOAuth2BlacklistedJTI indicates an expected call of LoadOAuth2BlacklistedJTI. -func (mr *MockStorageMockRecorder) LoadOAuth2BlacklistedJTI(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadOAuth2BlacklistedJTI(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOAuth2BlacklistedJTI", reflect.TypeOf((*MockStorage)(nil).LoadOAuth2BlacklistedJTI), arg0, arg1)  } @@ -278,7 +297,7 @@ func (m *MockStorage) LoadOAuth2ConsentPreConfigurations(arg0 context.Context, a  }  // LoadOAuth2ConsentPreConfigurations indicates an expected call of LoadOAuth2ConsentPreConfigurations. -func (mr *MockStorageMockRecorder) LoadOAuth2ConsentPreConfigurations(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadOAuth2ConsentPreConfigurations(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOAuth2ConsentPreConfigurations", reflect.TypeOf((*MockStorage)(nil).LoadOAuth2ConsentPreConfigurations), arg0, arg1, arg2)  } @@ -293,7 +312,7 @@ func (m *MockStorage) LoadOAuth2ConsentSessionByChallengeID(arg0 context.Context  }  // LoadOAuth2ConsentSessionByChallengeID indicates an expected call of LoadOAuth2ConsentSessionByChallengeID. -func (mr *MockStorageMockRecorder) LoadOAuth2ConsentSessionByChallengeID(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadOAuth2ConsentSessionByChallengeID(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOAuth2ConsentSessionByChallengeID", reflect.TypeOf((*MockStorage)(nil).LoadOAuth2ConsentSessionByChallengeID), arg0, arg1)  } @@ -308,7 +327,7 @@ func (m *MockStorage) LoadOAuth2PARContext(arg0 context.Context, arg1 string) (*  }  // LoadOAuth2PARContext indicates an expected call of LoadOAuth2PARContext. -func (mr *MockStorageMockRecorder) LoadOAuth2PARContext(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadOAuth2PARContext(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOAuth2PARContext", reflect.TypeOf((*MockStorage)(nil).LoadOAuth2PARContext), arg0, arg1)  } @@ -323,7 +342,7 @@ func (m *MockStorage) LoadOAuth2Session(arg0 context.Context, arg1 storage.OAuth  }  // LoadOAuth2Session indicates an expected call of LoadOAuth2Session. -func (mr *MockStorageMockRecorder) LoadOAuth2Session(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadOAuth2Session(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOAuth2Session", reflect.TypeOf((*MockStorage)(nil).LoadOAuth2Session), arg0, arg1, arg2)  } @@ -338,7 +357,7 @@ func (m *MockStorage) LoadOneTimeCode(arg0 context.Context, arg1, arg2, arg3 str  }  // LoadOneTimeCode indicates an expected call of LoadOneTimeCode. -func (mr *MockStorageMockRecorder) LoadOneTimeCode(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadOneTimeCode(arg0, arg1, arg2, arg3 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOneTimeCode", reflect.TypeOf((*MockStorage)(nil).LoadOneTimeCode), arg0, arg1, arg2, arg3)  } @@ -353,7 +372,7 @@ func (m *MockStorage) LoadOneTimeCodeByID(arg0 context.Context, arg1 int) (*mode  }  // LoadOneTimeCodeByID indicates an expected call of LoadOneTimeCodeByID. -func (mr *MockStorageMockRecorder) LoadOneTimeCodeByID(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadOneTimeCodeByID(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOneTimeCodeByID", reflect.TypeOf((*MockStorage)(nil).LoadOneTimeCodeByID), arg0, arg1)  } @@ -368,7 +387,7 @@ func (m *MockStorage) LoadOneTimeCodeByPublicID(arg0 context.Context, arg1 uuid.  }  // LoadOneTimeCodeByPublicID indicates an expected call of LoadOneTimeCodeByPublicID. -func (mr *MockStorageMockRecorder) LoadOneTimeCodeByPublicID(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadOneTimeCodeByPublicID(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOneTimeCodeByPublicID", reflect.TypeOf((*MockStorage)(nil).LoadOneTimeCodeByPublicID), arg0, arg1)  } @@ -383,7 +402,7 @@ func (m *MockStorage) LoadOneTimeCodeBySignature(arg0 context.Context, arg1 stri  }  // LoadOneTimeCodeBySignature indicates an expected call of LoadOneTimeCodeBySignature. -func (mr *MockStorageMockRecorder) LoadOneTimeCodeBySignature(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadOneTimeCodeBySignature(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadOneTimeCodeBySignature", reflect.TypeOf((*MockStorage)(nil).LoadOneTimeCodeBySignature), arg0, arg1)  } @@ -398,7 +417,7 @@ func (m *MockStorage) LoadPreferred2FAMethod(arg0 context.Context, arg1 string)  }  // LoadPreferred2FAMethod indicates an expected call of LoadPreferred2FAMethod. -func (mr *MockStorageMockRecorder) LoadPreferred2FAMethod(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadPreferred2FAMethod(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadPreferred2FAMethod", reflect.TypeOf((*MockStorage)(nil).LoadPreferred2FAMethod), arg0, arg1)  } @@ -413,7 +432,7 @@ func (m *MockStorage) LoadPreferredDuoDevice(arg0 context.Context, arg1 string)  }  // LoadPreferredDuoDevice indicates an expected call of LoadPreferredDuoDevice. -func (mr *MockStorageMockRecorder) LoadPreferredDuoDevice(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadPreferredDuoDevice(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadPreferredDuoDevice", reflect.TypeOf((*MockStorage)(nil).LoadPreferredDuoDevice), arg0, arg1)  } @@ -428,7 +447,7 @@ func (m *MockStorage) LoadTOTPConfiguration(arg0 context.Context, arg1 string) (  }  // LoadTOTPConfiguration indicates an expected call of LoadTOTPConfiguration. -func (mr *MockStorageMockRecorder) LoadTOTPConfiguration(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadTOTPConfiguration(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadTOTPConfiguration", reflect.TypeOf((*MockStorage)(nil).LoadTOTPConfiguration), arg0, arg1)  } @@ -443,7 +462,7 @@ func (m *MockStorage) LoadTOTPConfigurations(arg0 context.Context, arg1, arg2 in  }  // LoadTOTPConfigurations indicates an expected call of LoadTOTPConfigurations. -func (mr *MockStorageMockRecorder) LoadTOTPConfigurations(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadTOTPConfigurations(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadTOTPConfigurations", reflect.TypeOf((*MockStorage)(nil).LoadTOTPConfigurations), arg0, arg1, arg2)  } @@ -458,7 +477,7 @@ func (m *MockStorage) LoadUserInfo(arg0 context.Context, arg1 string) (model.Use  }  // LoadUserInfo indicates an expected call of LoadUserInfo. -func (mr *MockStorageMockRecorder) LoadUserInfo(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadUserInfo(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadUserInfo", reflect.TypeOf((*MockStorage)(nil).LoadUserInfo), arg0, arg1)  } @@ -473,7 +492,7 @@ func (m *MockStorage) LoadUserOpaqueIdentifier(arg0 context.Context, arg1 uuid.U  }  // LoadUserOpaqueIdentifier indicates an expected call of LoadUserOpaqueIdentifier. -func (mr *MockStorageMockRecorder) LoadUserOpaqueIdentifier(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadUserOpaqueIdentifier(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadUserOpaqueIdentifier", reflect.TypeOf((*MockStorage)(nil).LoadUserOpaqueIdentifier), arg0, arg1)  } @@ -488,7 +507,7 @@ func (m *MockStorage) LoadUserOpaqueIdentifierBySignature(arg0 context.Context,  }  // LoadUserOpaqueIdentifierBySignature indicates an expected call of LoadUserOpaqueIdentifierBySignature. -func (mr *MockStorageMockRecorder) LoadUserOpaqueIdentifierBySignature(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadUserOpaqueIdentifierBySignature(arg0, arg1, arg2, arg3 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadUserOpaqueIdentifierBySignature", reflect.TypeOf((*MockStorage)(nil).LoadUserOpaqueIdentifierBySignature), arg0, arg1, arg2, arg3)  } @@ -503,7 +522,7 @@ func (m *MockStorage) LoadUserOpaqueIdentifiers(arg0 context.Context) ([]model.U  }  // LoadUserOpaqueIdentifiers indicates an expected call of LoadUserOpaqueIdentifiers. -func (mr *MockStorageMockRecorder) LoadUserOpaqueIdentifiers(arg0 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadUserOpaqueIdentifiers(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadUserOpaqueIdentifiers", reflect.TypeOf((*MockStorage)(nil).LoadUserOpaqueIdentifiers), arg0)  } @@ -518,7 +537,7 @@ func (m *MockStorage) LoadWebAuthnCredentialByID(arg0 context.Context, arg1 int)  }  // LoadWebAuthnCredentialByID indicates an expected call of LoadWebAuthnCredentialByID. -func (mr *MockStorageMockRecorder) LoadWebAuthnCredentialByID(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadWebAuthnCredentialByID(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebAuthnCredentialByID", reflect.TypeOf((*MockStorage)(nil).LoadWebAuthnCredentialByID), arg0, arg1)  } @@ -533,7 +552,7 @@ func (m *MockStorage) LoadWebAuthnCredentials(arg0 context.Context, arg1, arg2 i  }  // LoadWebAuthnCredentials indicates an expected call of LoadWebAuthnCredentials. -func (mr *MockStorageMockRecorder) LoadWebAuthnCredentials(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadWebAuthnCredentials(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebAuthnCredentials", reflect.TypeOf((*MockStorage)(nil).LoadWebAuthnCredentials), arg0, arg1, arg2)  } @@ -548,7 +567,7 @@ func (m *MockStorage) LoadWebAuthnCredentialsByUsername(arg0 context.Context, ar  }  // LoadWebAuthnCredentialsByUsername indicates an expected call of LoadWebAuthnCredentialsByUsername. -func (mr *MockStorageMockRecorder) LoadWebAuthnCredentialsByUsername(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadWebAuthnCredentialsByUsername(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebAuthnCredentialsByUsername", reflect.TypeOf((*MockStorage)(nil).LoadWebAuthnCredentialsByUsername), arg0, arg1, arg2)  } @@ -563,7 +582,7 @@ func (m *MockStorage) LoadWebAuthnUser(arg0 context.Context, arg1, arg2 string)  }  // LoadWebAuthnUser indicates an expected call of LoadWebAuthnUser. -func (mr *MockStorageMockRecorder) LoadWebAuthnUser(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) LoadWebAuthnUser(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadWebAuthnUser", reflect.TypeOf((*MockStorage)(nil).LoadWebAuthnUser), arg0, arg1, arg2)  } @@ -577,7 +596,7 @@ func (m *MockStorage) RevokeIdentityVerification(arg0 context.Context, arg1 stri  }  // RevokeIdentityVerification indicates an expected call of RevokeIdentityVerification. -func (mr *MockStorageMockRecorder) RevokeIdentityVerification(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) RevokeIdentityVerification(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeIdentityVerification", reflect.TypeOf((*MockStorage)(nil).RevokeIdentityVerification), arg0, arg1, arg2)  } @@ -591,7 +610,7 @@ func (m *MockStorage) RevokeOAuth2PARContext(arg0 context.Context, arg1 string)  }  // RevokeOAuth2PARContext indicates an expected call of RevokeOAuth2PARContext. -func (mr *MockStorageMockRecorder) RevokeOAuth2PARContext(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) RevokeOAuth2PARContext(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeOAuth2PARContext", reflect.TypeOf((*MockStorage)(nil).RevokeOAuth2PARContext), arg0, arg1)  } @@ -605,7 +624,7 @@ func (m *MockStorage) RevokeOAuth2Session(arg0 context.Context, arg1 storage.OAu  }  // RevokeOAuth2Session indicates an expected call of RevokeOAuth2Session. -func (mr *MockStorageMockRecorder) RevokeOAuth2Session(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) RevokeOAuth2Session(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeOAuth2Session", reflect.TypeOf((*MockStorage)(nil).RevokeOAuth2Session), arg0, arg1, arg2)  } @@ -619,7 +638,7 @@ func (m *MockStorage) RevokeOAuth2SessionByRequestID(arg0 context.Context, arg1  }  // RevokeOAuth2SessionByRequestID indicates an expected call of RevokeOAuth2SessionByRequestID. -func (mr *MockStorageMockRecorder) RevokeOAuth2SessionByRequestID(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) RevokeOAuth2SessionByRequestID(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeOAuth2SessionByRequestID", reflect.TypeOf((*MockStorage)(nil).RevokeOAuth2SessionByRequestID), arg0, arg1, arg2)  } @@ -633,7 +652,7 @@ func (m *MockStorage) RevokeOneTimeCode(arg0 context.Context, arg1 uuid.UUID, ar  }  // RevokeOneTimeCode indicates an expected call of RevokeOneTimeCode. -func (mr *MockStorageMockRecorder) RevokeOneTimeCode(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) RevokeOneTimeCode(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RevokeOneTimeCode", reflect.TypeOf((*MockStorage)(nil).RevokeOneTimeCode), arg0, arg1, arg2)  } @@ -647,7 +666,7 @@ func (m *MockStorage) Rollback(arg0 context.Context) error {  }  // Rollback indicates an expected call of Rollback. -func (mr *MockStorageMockRecorder) Rollback(arg0 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) Rollback(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Rollback", reflect.TypeOf((*MockStorage)(nil).Rollback), arg0)  } @@ -661,7 +680,7 @@ func (m *MockStorage) SaveIdentityVerification(arg0 context.Context, arg1 model.  }  // SaveIdentityVerification indicates an expected call of SaveIdentityVerification. -func (mr *MockStorageMockRecorder) SaveIdentityVerification(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveIdentityVerification(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveIdentityVerification", reflect.TypeOf((*MockStorage)(nil).SaveIdentityVerification), arg0, arg1)  } @@ -675,7 +694,7 @@ func (m *MockStorage) SaveOAuth2BlacklistedJTI(arg0 context.Context, arg1 model.  }  // SaveOAuth2BlacklistedJTI indicates an expected call of SaveOAuth2BlacklistedJTI. -func (mr *MockStorageMockRecorder) SaveOAuth2BlacklistedJTI(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveOAuth2BlacklistedJTI(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2BlacklistedJTI", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2BlacklistedJTI), arg0, arg1)  } @@ -690,7 +709,7 @@ func (m *MockStorage) SaveOAuth2ConsentPreConfiguration(arg0 context.Context, ar  }  // SaveOAuth2ConsentPreConfiguration indicates an expected call of SaveOAuth2ConsentPreConfiguration. -func (mr *MockStorageMockRecorder) SaveOAuth2ConsentPreConfiguration(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveOAuth2ConsentPreConfiguration(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentPreConfiguration", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentPreConfiguration), arg0, arg1)  } @@ -704,7 +723,7 @@ func (m *MockStorage) SaveOAuth2ConsentSession(arg0 context.Context, arg1 model.  }  // SaveOAuth2ConsentSession indicates an expected call of SaveOAuth2ConsentSession. -func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSession(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSession(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentSession", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentSession), arg0, arg1)  } @@ -718,7 +737,7 @@ func (m *MockStorage) SaveOAuth2ConsentSessionGranted(arg0 context.Context, arg1  }  // SaveOAuth2ConsentSessionGranted indicates an expected call of SaveOAuth2ConsentSessionGranted. -func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSessionGranted(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSessionGranted(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentSessionGranted", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentSessionGranted), arg0, arg1)  } @@ -732,7 +751,7 @@ func (m *MockStorage) SaveOAuth2ConsentSessionResponse(arg0 context.Context, arg  }  // SaveOAuth2ConsentSessionResponse indicates an expected call of SaveOAuth2ConsentSessionResponse. -func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSessionResponse(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSessionResponse(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentSessionResponse", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentSessionResponse), arg0, arg1, arg2)  } @@ -746,7 +765,7 @@ func (m *MockStorage) SaveOAuth2ConsentSessionSubject(arg0 context.Context, arg1  }  // SaveOAuth2ConsentSessionSubject indicates an expected call of SaveOAuth2ConsentSessionSubject. -func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSessionSubject(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveOAuth2ConsentSessionSubject(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2ConsentSessionSubject", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2ConsentSessionSubject), arg0, arg1)  } @@ -760,7 +779,7 @@ func (m *MockStorage) SaveOAuth2PARContext(arg0 context.Context, arg1 model.OAut  }  // SaveOAuth2PARContext indicates an expected call of SaveOAuth2PARContext. -func (mr *MockStorageMockRecorder) SaveOAuth2PARContext(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveOAuth2PARContext(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2PARContext", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2PARContext), arg0, arg1)  } @@ -774,7 +793,7 @@ func (m *MockStorage) SaveOAuth2Session(arg0 context.Context, arg1 storage.OAuth  }  // SaveOAuth2Session indicates an expected call of SaveOAuth2Session. -func (mr *MockStorageMockRecorder) SaveOAuth2Session(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveOAuth2Session(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOAuth2Session", reflect.TypeOf((*MockStorage)(nil).SaveOAuth2Session), arg0, arg1, arg2)  } @@ -789,7 +808,7 @@ func (m *MockStorage) SaveOneTimeCode(arg0 context.Context, arg1 model.OneTimeCo  }  // SaveOneTimeCode indicates an expected call of SaveOneTimeCode. -func (mr *MockStorageMockRecorder) SaveOneTimeCode(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveOneTimeCode(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveOneTimeCode", reflect.TypeOf((*MockStorage)(nil).SaveOneTimeCode), arg0, arg1)  } @@ -803,7 +822,7 @@ func (m *MockStorage) SavePreferred2FAMethod(arg0 context.Context, arg1, arg2 st  }  // SavePreferred2FAMethod indicates an expected call of SavePreferred2FAMethod. -func (mr *MockStorageMockRecorder) SavePreferred2FAMethod(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SavePreferred2FAMethod(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePreferred2FAMethod", reflect.TypeOf((*MockStorage)(nil).SavePreferred2FAMethod), arg0, arg1, arg2)  } @@ -817,7 +836,7 @@ func (m *MockStorage) SavePreferredDuoDevice(arg0 context.Context, arg1 model.Du  }  // SavePreferredDuoDevice indicates an expected call of SavePreferredDuoDevice. -func (mr *MockStorageMockRecorder) SavePreferredDuoDevice(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SavePreferredDuoDevice(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SavePreferredDuoDevice", reflect.TypeOf((*MockStorage)(nil).SavePreferredDuoDevice), arg0, arg1)  } @@ -831,11 +850,25 @@ func (m *MockStorage) SaveTOTPConfiguration(arg0 context.Context, arg1 model.TOT  }  // SaveTOTPConfiguration indicates an expected call of SaveTOTPConfiguration. -func (mr *MockStorageMockRecorder) SaveTOTPConfiguration(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveTOTPConfiguration(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveTOTPConfiguration", reflect.TypeOf((*MockStorage)(nil).SaveTOTPConfiguration), arg0, arg1)  } +// SaveTOTPHistory mocks base method. +func (m *MockStorage) SaveTOTPHistory(arg0 context.Context, arg1 string, arg2 uint64) error { +	m.ctrl.T.Helper() +	ret := m.ctrl.Call(m, "SaveTOTPHistory", arg0, arg1, arg2) +	ret0, _ := ret[0].(error) +	return ret0 +} + +// SaveTOTPHistory indicates an expected call of SaveTOTPHistory. +func (mr *MockStorageMockRecorder) SaveTOTPHistory(arg0, arg1, arg2 any) *gomock.Call { +	mr.mock.ctrl.T.Helper() +	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveTOTPHistory", reflect.TypeOf((*MockStorage)(nil).SaveTOTPHistory), arg0, arg1, arg2) +} +  // SaveUserOpaqueIdentifier mocks base method.  func (m *MockStorage) SaveUserOpaqueIdentifier(arg0 context.Context, arg1 model.UserOpaqueIdentifier) error {  	m.ctrl.T.Helper() @@ -845,7 +878,7 @@ func (m *MockStorage) SaveUserOpaqueIdentifier(arg0 context.Context, arg1 model.  }  // SaveUserOpaqueIdentifier indicates an expected call of SaveUserOpaqueIdentifier. -func (mr *MockStorageMockRecorder) SaveUserOpaqueIdentifier(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveUserOpaqueIdentifier(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveUserOpaqueIdentifier", reflect.TypeOf((*MockStorage)(nil).SaveUserOpaqueIdentifier), arg0, arg1)  } @@ -859,7 +892,7 @@ func (m *MockStorage) SaveWebAuthnCredential(arg0 context.Context, arg1 model.We  }  // SaveWebAuthnCredential indicates an expected call of SaveWebAuthnCredential. -func (mr *MockStorageMockRecorder) SaveWebAuthnCredential(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveWebAuthnCredential(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveWebAuthnCredential", reflect.TypeOf((*MockStorage)(nil).SaveWebAuthnCredential), arg0, arg1)  } @@ -873,7 +906,7 @@ func (m *MockStorage) SaveWebAuthnUser(arg0 context.Context, arg1 model.WebAuthn  }  // SaveWebAuthnUser indicates an expected call of SaveWebAuthnUser. -func (mr *MockStorageMockRecorder) SaveWebAuthnUser(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SaveWebAuthnUser(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveWebAuthnUser", reflect.TypeOf((*MockStorage)(nil).SaveWebAuthnUser), arg0, arg1)  } @@ -887,7 +920,7 @@ func (m *MockStorage) SchemaEncryptionChangeKey(arg0 context.Context, arg1 strin  }  // SchemaEncryptionChangeKey indicates an expected call of SchemaEncryptionChangeKey. -func (mr *MockStorageMockRecorder) SchemaEncryptionChangeKey(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SchemaEncryptionChangeKey(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchemaEncryptionChangeKey", reflect.TypeOf((*MockStorage)(nil).SchemaEncryptionChangeKey), arg0, arg1)  } @@ -902,7 +935,7 @@ func (m *MockStorage) SchemaEncryptionCheckKey(arg0 context.Context, arg1 bool)  }  // SchemaEncryptionCheckKey indicates an expected call of SchemaEncryptionCheckKey. -func (mr *MockStorageMockRecorder) SchemaEncryptionCheckKey(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SchemaEncryptionCheckKey(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchemaEncryptionCheckKey", reflect.TypeOf((*MockStorage)(nil).SchemaEncryptionCheckKey), arg0, arg1)  } @@ -931,7 +964,7 @@ func (m *MockStorage) SchemaMigrate(arg0 context.Context, arg1 bool, arg2 int) e  }  // SchemaMigrate indicates an expected call of SchemaMigrate. -func (mr *MockStorageMockRecorder) SchemaMigrate(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SchemaMigrate(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchemaMigrate", reflect.TypeOf((*MockStorage)(nil).SchemaMigrate), arg0, arg1, arg2)  } @@ -946,7 +979,7 @@ func (m *MockStorage) SchemaMigrationHistory(arg0 context.Context) ([]model.Migr  }  // SchemaMigrationHistory indicates an expected call of SchemaMigrationHistory. -func (mr *MockStorageMockRecorder) SchemaMigrationHistory(arg0 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SchemaMigrationHistory(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchemaMigrationHistory", reflect.TypeOf((*MockStorage)(nil).SchemaMigrationHistory), arg0)  } @@ -961,7 +994,7 @@ func (m *MockStorage) SchemaMigrationsDown(arg0 context.Context, arg1 int) ([]mo  }  // SchemaMigrationsDown indicates an expected call of SchemaMigrationsDown. -func (mr *MockStorageMockRecorder) SchemaMigrationsDown(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SchemaMigrationsDown(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchemaMigrationsDown", reflect.TypeOf((*MockStorage)(nil).SchemaMigrationsDown), arg0, arg1)  } @@ -976,7 +1009,7 @@ func (m *MockStorage) SchemaMigrationsUp(arg0 context.Context, arg1 int) ([]mode  }  // SchemaMigrationsUp indicates an expected call of SchemaMigrationsUp. -func (mr *MockStorageMockRecorder) SchemaMigrationsUp(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SchemaMigrationsUp(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchemaMigrationsUp", reflect.TypeOf((*MockStorage)(nil).SchemaMigrationsUp), arg0, arg1)  } @@ -991,7 +1024,7 @@ func (m *MockStorage) SchemaTables(arg0 context.Context) ([]string, error) {  }  // SchemaTables indicates an expected call of SchemaTables. -func (mr *MockStorageMockRecorder) SchemaTables(arg0 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SchemaTables(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchemaTables", reflect.TypeOf((*MockStorage)(nil).SchemaTables), arg0)  } @@ -1006,7 +1039,7 @@ func (m *MockStorage) SchemaVersion(arg0 context.Context) (int, error) {  }  // SchemaVersion indicates an expected call of SchemaVersion. -func (mr *MockStorageMockRecorder) SchemaVersion(arg0 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) SchemaVersion(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SchemaVersion", reflect.TypeOf((*MockStorage)(nil).SchemaVersion), arg0)  } @@ -1034,7 +1067,7 @@ func (m *MockStorage) UpdateOAuth2PARContext(arg0 context.Context, arg1 model.OA  }  // UpdateOAuth2PARContext indicates an expected call of UpdateOAuth2PARContext. -func (mr *MockStorageMockRecorder) UpdateOAuth2PARContext(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) UpdateOAuth2PARContext(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateOAuth2PARContext", reflect.TypeOf((*MockStorage)(nil).UpdateOAuth2PARContext), arg0, arg1)  } @@ -1048,7 +1081,7 @@ func (m *MockStorage) UpdateTOTPConfigurationSignIn(arg0 context.Context, arg1 i  }  // UpdateTOTPConfigurationSignIn indicates an expected call of UpdateTOTPConfigurationSignIn. -func (mr *MockStorageMockRecorder) UpdateTOTPConfigurationSignIn(arg0, arg1, arg2 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) UpdateTOTPConfigurationSignIn(arg0, arg1, arg2 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTOTPConfigurationSignIn", reflect.TypeOf((*MockStorage)(nil).UpdateTOTPConfigurationSignIn), arg0, arg1, arg2)  } @@ -1062,7 +1095,7 @@ func (m *MockStorage) UpdateWebAuthnCredentialDescription(arg0 context.Context,  }  // UpdateWebAuthnCredentialDescription indicates an expected call of UpdateWebAuthnCredentialDescription. -func (mr *MockStorageMockRecorder) UpdateWebAuthnCredentialDescription(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) UpdateWebAuthnCredentialDescription(arg0, arg1, arg2, arg3 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWebAuthnCredentialDescription", reflect.TypeOf((*MockStorage)(nil).UpdateWebAuthnCredentialDescription), arg0, arg1, arg2, arg3)  } @@ -1076,7 +1109,7 @@ func (m *MockStorage) UpdateWebAuthnCredentialSignIn(arg0 context.Context, arg1  }  // UpdateWebAuthnCredentialSignIn indicates an expected call of UpdateWebAuthnCredentialSignIn. -func (mr *MockStorageMockRecorder) UpdateWebAuthnCredentialSignIn(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockStorageMockRecorder) UpdateWebAuthnCredentialSignIn(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWebAuthnCredentialSignIn", reflect.TypeOf((*MockStorage)(nil).UpdateWebAuthnCredentialSignIn), arg0, arg1)  } diff --git a/internal/mocks/totp.go b/internal/mocks/totp.go index ab61dfe30..87738aac0 100644 --- a/internal/mocks/totp.go +++ b/internal/mocks/totp.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/authelia/authelia/v4/internal/totp (interfaces: Provider) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination totp.go -mock_names Provider=MockTOTP github.com/authelia/authelia/v4/internal/totp Provider +//  // Package mocks is a generated GoMock package.  package mocks @@ -8,7 +12,7 @@ import (  	reflect "reflect"  	model "github.com/authelia/authelia/v4/internal/model" -	gomock "github.com/golang/mock/gomock" +	gomock "go.uber.org/mock/gomock"  )  // MockTOTP is a mock of Provider interface. @@ -44,7 +48,7 @@ func (m *MockTOTP) Generate(arg0 string) (*model.TOTPConfiguration, error) {  }  // Generate indicates an expected call of Generate. -func (mr *MockTOTPMockRecorder) Generate(arg0 interface{}) *gomock.Call { +func (mr *MockTOTPMockRecorder) Generate(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Generate", reflect.TypeOf((*MockTOTP)(nil).Generate), arg0)  } @@ -59,7 +63,7 @@ func (m *MockTOTP) GenerateCustom(arg0, arg1, arg2 string, arg3, arg4, arg5 uint  }  // GenerateCustom indicates an expected call of GenerateCustom. -func (mr *MockTOTPMockRecorder) GenerateCustom(arg0, arg1, arg2, arg3, arg4, arg5 interface{}) *gomock.Call { +func (mr *MockTOTPMockRecorder) GenerateCustom(arg0, arg1, arg2, arg3, arg4, arg5 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateCustom", reflect.TypeOf((*MockTOTP)(nil).GenerateCustom), arg0, arg1, arg2, arg3, arg4, arg5)  } @@ -79,16 +83,17 @@ func (mr *MockTOTPMockRecorder) Options() *gomock.Call {  }  // Validate mocks base method. -func (m *MockTOTP) Validate(arg0 string, arg1 *model.TOTPConfiguration) (bool, error) { +func (m *MockTOTP) Validate(arg0 string, arg1 *model.TOTPConfiguration) (bool, uint64, error) {  	m.ctrl.T.Helper()  	ret := m.ctrl.Call(m, "Validate", arg0, arg1)  	ret0, _ := ret[0].(bool) -	ret1, _ := ret[1].(error) -	return ret0, ret1 +	ret1, _ := ret[1].(uint64) +	ret2, _ := ret[2].(error) +	return ret0, ret1, ret2  }  // Validate indicates an expected call of Validate. -func (mr *MockTOTPMockRecorder) Validate(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockTOTPMockRecorder) Validate(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Validate", reflect.TypeOf((*MockTOTP)(nil).Validate), arg0, arg1)  } diff --git a/internal/mocks/user_provider.go b/internal/mocks/user_provider.go index 4cc592dc9..54e3e478a 100644 --- a/internal/mocks/user_provider.go +++ b/internal/mocks/user_provider.go @@ -1,6 +1,10 @@  // Code generated by MockGen. DO NOT EDIT.  // Source: github.com/authelia/authelia/v4/internal/authentication (interfaces: UserProvider) - +// +// Generated by this command: +// +//	mockgen -package mocks -destination user_provider.go -mock_names UserProvider=MockUserProvider github.com/authelia/authelia/v4/internal/authentication UserProvider +//  // Package mocks is a generated GoMock package.  package mocks @@ -8,7 +12,7 @@ import (  	reflect "reflect"  	authentication "github.com/authelia/authelia/v4/internal/authentication" -	gomock "github.com/golang/mock/gomock" +	gomock "go.uber.org/mock/gomock"  )  // MockUserProvider is a mock of UserProvider interface. @@ -44,7 +48,7 @@ func (m *MockUserProvider) CheckUserPassword(arg0, arg1 string) (bool, error) {  }  // CheckUserPassword indicates an expected call of CheckUserPassword. -func (mr *MockUserProviderMockRecorder) CheckUserPassword(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockUserProviderMockRecorder) CheckUserPassword(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckUserPassword", reflect.TypeOf((*MockUserProvider)(nil).CheckUserPassword), arg0, arg1)  } @@ -59,7 +63,7 @@ func (m *MockUserProvider) GetDetails(arg0 string) (*authentication.UserDetails,  }  // GetDetails indicates an expected call of GetDetails. -func (mr *MockUserProviderMockRecorder) GetDetails(arg0 interface{}) *gomock.Call { +func (mr *MockUserProviderMockRecorder) GetDetails(arg0 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDetails", reflect.TypeOf((*MockUserProvider)(nil).GetDetails), arg0)  } @@ -87,7 +91,7 @@ func (m *MockUserProvider) UpdatePassword(arg0, arg1 string) error {  }  // UpdatePassword indicates an expected call of UpdatePassword. -func (mr *MockUserProviderMockRecorder) UpdatePassword(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockUserProviderMockRecorder) UpdatePassword(arg0, arg1 any) *gomock.Call {  	mr.mock.ctrl.T.Helper()  	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePassword", reflect.TypeOf((*MockUserProvider)(nil).UpdatePassword), arg0, arg1)  } diff --git a/internal/model/oidc_test.go b/internal/model/oidc_test.go index bcde86833..fde5d7f2c 100644 --- a/internal/model/oidc_test.go +++ b/internal/model/oidc_test.go @@ -9,12 +9,12 @@ import (  	"testing"  	"time" -	"github.com/golang/mock/gomock"  	"github.com/google/uuid"  	"github.com/ory/fosite"  	"github.com/ory/fosite/handler/openid"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/mocks"  	"github.com/authelia/authelia/v4/internal/model" diff --git a/internal/model/totp_configuration.go b/internal/model/totp_configuration.go index 67fc492b7..275afc67c 100644 --- a/internal/model/totp_configuration.go +++ b/internal/model/totp_configuration.go @@ -9,7 +9,7 @@ import (  	"strconv"  	"time" -	"github.com/pquerna/otp" +	"github.com/authelia/otp"  	"gopkg.in/yaml.v3"  ) @@ -64,6 +64,19 @@ func (c TOTPConfiguration) MarshalJSON() (data []byte, err error) {  	return json.Marshal(o)  } +// HistorySince provides a reasonably accurate window for previously successful attempts to check for history. +func (c *TOTPConfiguration) HistorySince(now time.Time, skew *int) time.Time { +	var s int + +	if skew == nil { +		s = 2 +	} else { +		s = *skew + 2 +	} + +	return now.Add(-time.Second * time.Duration(c.Period) * time.Duration(s)) +} +  // LastUsed provides LastUsedAt as a *time.Time instead of sql.NullTime.  func (c *TOTPConfiguration) LastUsed() *time.Time {  	if c.LastUsedAt.Valid { diff --git a/internal/oidc/client_credentials_test.go b/internal/oidc/client_credentials_test.go index d9642b180..9fbc6df22 100644 --- a/internal/oidc/client_credentials_test.go +++ b/internal/oidc/client_credentials_test.go @@ -15,7 +15,6 @@ import (  	"time"  	"github.com/golang-jwt/jwt/v5" -	"github.com/golang/mock/gomock"  	"github.com/google/uuid"  	"github.com/ory/fosite"  	"github.com/ory/fosite/storage" @@ -23,6 +22,7 @@ import (  	"github.com/stretchr/testify/require"  	"github.com/stretchr/testify/suite"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"gopkg.in/square/go-jose.v2"  	"github.com/authelia/authelia/v4/internal/authorization" diff --git a/internal/oidc/flow_client_credentials_test.go b/internal/oidc/flow_client_credentials_test.go index 82c5e5b54..0e1b0ef50 100644 --- a/internal/oidc/flow_client_credentials_test.go +++ b/internal/oidc/flow_client_credentials_test.go @@ -6,10 +6,10 @@ import (  	"testing"  	"time" -	"github.com/golang/mock/gomock"  	"github.com/ory/fosite"  	"github.com/ory/fosite/handler/oauth2"  	"github.com/stretchr/testify/assert" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/mocks"  	"github.com/authelia/authelia/v4/internal/oidc" diff --git a/internal/oidc/flow_refresh_test.go b/internal/oidc/flow_refresh_test.go index 7efc4be9f..3d6add0d8 100644 --- a/internal/oidc/flow_refresh_test.go +++ b/internal/oidc/flow_refresh_test.go @@ -8,13 +8,13 @@ import (  	"testing"  	"time" -	"github.com/golang/mock/gomock"  	"github.com/ory/fosite"  	"github.com/ory/fosite/handler/oauth2"  	"github.com/ory/fosite/storage"  	"github.com/ory/fosite/token/hmac"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/mocks"  	"github.com/authelia/authelia/v4/internal/oidc" diff --git a/internal/oidc/handler_introspection_test.go b/internal/oidc/handler_introspection_test.go index 74e79e75e..adf7afa3a 100644 --- a/internal/oidc/handler_introspection_test.go +++ b/internal/oidc/handler_introspection_test.go @@ -8,11 +8,11 @@ import (  	"net/url"  	"testing" -	"github.com/golang/mock/gomock"  	"github.com/ory/fosite"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/configuration/schema"  	"github.com/authelia/authelia/v4/internal/mocks" diff --git a/internal/oidc/store_test.go b/internal/oidc/store_test.go index 544f93b74..f5057b42d 100644 --- a/internal/oidc/store_test.go +++ b/internal/oidc/store_test.go @@ -8,11 +8,11 @@ import (  	"testing"  	"time" -	"github.com/golang/mock/gomock"  	"github.com/ory/fosite"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  	"github.com/stretchr/testify/suite" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/authorization"  	"github.com/authelia/authelia/v4/internal/configuration/schema" diff --git a/internal/regulation/regulator_test.go b/internal/regulation/regulator_test.go index 536d674ae..55b783889 100644 --- a/internal/regulation/regulator_test.go +++ b/internal/regulation/regulator_test.go @@ -6,10 +6,10 @@ import (  	"testing"  	"time" -	"github.com/golang/mock/gomock"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/suite"  	"github.com/valyala/fasthttp" +	"go.uber.org/mock/gomock"  	"github.com/authelia/authelia/v4/internal/configuration/schema"  	"github.com/authelia/authelia/v4/internal/mocks" diff --git a/internal/storage/const.go b/internal/storage/const.go index c00a957d7..907842b89 100644 --- a/internal/storage/const.go +++ b/internal/storage/const.go @@ -10,6 +10,7 @@ const (  	tableIdentityVerification = "identity_verification"  	tableOneTimeCode          = "one_time_code"  	tableTOTPConfigurations   = "totp_configurations" +	tableTOTPHistory          = "totp_history"  	tableUserOpaqueIdentifier = "user_opaque_identifier"  	tableUserPreferences      = "user_preferences"  	tableWebAuthnCredentials  = "webauthn_credentials" //nolint:gosec // This is a table name, not a credential. diff --git a/internal/storage/migrations/mysql/V0013.OneTimeCode.up.sql b/internal/storage/migrations/mysql/V0013.OneTimeCode.up.sql index a27f6c709..8edd66979 100644 --- a/internal/storage/migrations/mysql/V0013.OneTimeCode.up.sql +++ b/internal/storage/migrations/mysql/V0013.OneTimeCode.up.sql @@ -14,5 +14,4 @@ CREATE TABLE IF NOT EXISTS one_time_code (      code BLOB NOT NULL  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; -CREATE UNIQUE INDEX one_time_code_signature ON one_time_code (signature); -CREATE INDEX one_time_code_lookup ON one_time_code (signature, username); +CREATE UNIQUE INDEX one_time_code_signature ON one_time_code (signature, username); diff --git a/internal/storage/migrations/mysql/V0015.TOTPEnhance.down.sql b/internal/storage/migrations/mysql/V0015.TOTPEnhance.down.sql new file mode 100644 index 000000000..53d9ae5ab --- /dev/null +++ b/internal/storage/migrations/mysql/V0015.TOTPEnhance.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS totp_history; diff --git a/internal/storage/migrations/mysql/V0015.TOTPEnhance.up.sql b/internal/storage/migrations/mysql/V0015.TOTPEnhance.up.sql new file mode 100644 index 000000000..e74462ffd --- /dev/null +++ b/internal/storage/migrations/mysql/V0015.TOTPEnhance.up.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS totp_history ( +    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, +	created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, +    username VARCHAR(100) NOT NULL, +	step CHAR(128) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + +CREATE UNIQUE INDEX totp_history_lookup_key ON totp_history (username, step); diff --git a/internal/storage/migrations/postgres/V0013.OneTimeCode.up.sql b/internal/storage/migrations/postgres/V0013.OneTimeCode.up.sql index 434068170..77016c801 100644 --- a/internal/storage/migrations/postgres/V0013.OneTimeCode.up.sql +++ b/internal/storage/migrations/postgres/V0013.OneTimeCode.up.sql @@ -15,4 +15,3 @@ CREATE TABLE IF NOT EXISTS one_time_code (  );  CREATE UNIQUE INDEX one_time_code_lookup_key ON one_time_code (signature, username); -CREATE INDEX one_time_code_lookup ON one_time_code (signature, username); diff --git a/internal/storage/migrations/postgres/V0015.TOTPEnhance.down.sql b/internal/storage/migrations/postgres/V0015.TOTPEnhance.down.sql new file mode 100644 index 000000000..53d9ae5ab --- /dev/null +++ b/internal/storage/migrations/postgres/V0015.TOTPEnhance.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS totp_history; diff --git a/internal/storage/migrations/postgres/V0015.TOTPEnhance.up.sql b/internal/storage/migrations/postgres/V0015.TOTPEnhance.up.sql new file mode 100644 index 000000000..22c6e23c1 --- /dev/null +++ b/internal/storage/migrations/postgres/V0015.TOTPEnhance.up.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS totp_history ( +    id SERIAL CONSTRAINT one_time_code_pkey PRIMARY KEY, +	created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, +    username VARCHAR(100) NOT NULL, +	step CHAR(128) NOT NULL +); + +CREATE UNIQUE INDEX totp_history_lookup_key ON totp_history (username, step); diff --git a/internal/storage/migrations/sqlite/V0013.OneTimeCode.up.sql b/internal/storage/migrations/sqlite/V0013.OneTimeCode.up.sql index f7e2ab0f7..6fc19d524 100644 --- a/internal/storage/migrations/sqlite/V0013.OneTimeCode.up.sql +++ b/internal/storage/migrations/sqlite/V0013.OneTimeCode.up.sql @@ -15,4 +15,3 @@ CREATE TABLE IF NOT EXISTS one_time_code (  );  CREATE UNIQUE INDEX one_time_code_lookup_key ON one_time_code (signature, username); -CREATE INDEX one_time_code_lookup ON one_time_code (signature, username); diff --git a/internal/storage/migrations/sqlite/V0015.TOTPEnhance.down.sql b/internal/storage/migrations/sqlite/V0015.TOTPEnhance.down.sql new file mode 100644 index 000000000..53d9ae5ab --- /dev/null +++ b/internal/storage/migrations/sqlite/V0015.TOTPEnhance.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS totp_history; diff --git a/internal/storage/migrations/sqlite/V0015.TOTPEnhance.up.sql b/internal/storage/migrations/sqlite/V0015.TOTPEnhance.up.sql new file mode 100644 index 000000000..21a3c5667 --- /dev/null +++ b/internal/storage/migrations/sqlite/V0015.TOTPEnhance.up.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS totp_history ( +    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, +	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, +    username VARCHAR(100) NOT NULL, +	step CHAR(128) NOT NULL +); + +CREATE UNIQUE INDEX totp_history_lookup_key ON totp_history (username, step); diff --git a/internal/storage/migrations_test.go b/internal/storage/migrations_test.go index 5ebf686e2..7cfedace2 100644 --- a/internal/storage/migrations_test.go +++ b/internal/storage/migrations_test.go @@ -11,7 +11,7 @@ import (  const (  	// This is the latest schema version for the purpose of tests. -	LatestVersion = 14 +	LatestVersion = 15  )  func TestShouldObtainCorrectMigrations(t *testing.T) { diff --git a/internal/storage/provider.go b/internal/storage/provider.go index a0f23627d..e53cf7ed1 100644 --- a/internal/storage/provider.go +++ b/internal/storage/provider.go @@ -71,6 +71,16 @@ type Provider interface {  	LoadTOTPConfigurations(ctx context.Context, limit, page int) (configs []model.TOTPConfiguration, err error)  	/* +		Implementation for User TOTP History. +	*/ + +	// SaveTOTPHistory saves a TOTP history item in the storage provider. +	SaveTOTPHistory(ctx context.Context, username string, step uint64) (err error) + +	// ExistsTOTPHistory checks if a TOTP history item exists in the storage provider. +	ExistsTOTPHistory(ctx context.Context, username string, step uint64, since time.Time) (exists bool, err error) + +	/*  		Implementation for User WebAuthn Information.  	*/ diff --git a/internal/storage/sql_provider.go b/internal/storage/sql_provider.go index 66f8a7795..cb86e137c 100644 --- a/internal/storage/sql_provider.go +++ b/internal/storage/sql_provider.go @@ -6,6 +6,7 @@ import (  	"database/sql"  	"errors"  	"fmt" +	"strconv"  	"strings"  	"time" @@ -59,6 +60,9 @@ func NewSQLProvider(config *schema.Configuration, name, driverName, dataSourceNa  		sqlUpdateTOTPConfigRecordSignIn:           fmt.Sprintf(queryFmtUpdateTOTPConfigRecordSignIn, tableTOTPConfigurations),  		sqlUpdateTOTPConfigRecordSignInByUsername: fmt.Sprintf(queryFmtUpdateTOTPConfigRecordSignInByUsername, tableTOTPConfigurations), +		sqlInsertTOTPHistory: fmt.Sprintf(queryFmtInsertTOTPHistory, tableTOTPHistory), +		sqlSelectTOTPHistory: fmt.Sprintf(queryFmtSelectTOTPHistory, tableTOTPHistory), +  		sqlInsertWebAuthnUser: fmt.Sprintf(queryFmtInsertWebAuthnUser, tableWebAuthnUsers),  		sqlSelectWebAuthnUser: fmt.Sprintf(queryFmtSelectWebAuthnUser, tableWebAuthnUsers), @@ -193,6 +197,10 @@ type SQLProvider struct {  	sqlUpdateTOTPConfigRecordSignIn           string  	sqlUpdateTOTPConfigRecordSignInByUsername string +	// Table: totp_history. +	sqlInsertTOTPHistory string +	sqlSelectTOTPHistory string +  	// Table: webauthn_users.  	sqlInsertWebAuthnUser string  	sqlSelectWebAuthnUser string @@ -546,6 +554,30 @@ func (p *SQLProvider) LoadTOTPConfiguration(ctx context.Context, username string  	return config, nil  } +// SaveTOTPHistory saves a TOTP history item in the storage provider. +func (p *SQLProvider) SaveTOTPHistory(ctx context.Context, username string, step uint64) (err error) { +	signature := p.hmacSignature([]byte(strconv.Itoa(int(step))), []byte(username)) + +	if _, err = p.db.ExecContext(ctx, p.sqlInsertTOTPHistory, username, signature); err != nil { +		return fmt.Errorf("error inserting TOTP history for user '%s': %w", username, err) +	} + +	return nil +} + +// ExistsTOTPHistory checks if a TOTP history item exists in the storage provider. +func (p *SQLProvider) ExistsTOTPHistory(ctx context.Context, username string, step uint64, since time.Time) (exists bool, err error) { +	var count int + +	signature := p.hmacSignature([]byte(strconv.Itoa(int(step))), []byte(username)) + +	if err = p.db.SelectContext(ctx, &count, p.sqlSelectTOTPHistory, username, signature, since); err != nil { +		return false, fmt.Errorf("error checking if TOTP history exists: %w", err) +	} + +	return count != 0, nil +} +  // LoadTOTPConfigurations load a set of TOTP configurations from the storage provider.  func (p *SQLProvider) LoadTOTPConfigurations(ctx context.Context, limit, page int) (configs []model.TOTPConfiguration, err error) {  	configs = make([]model.TOTPConfiguration, 0, limit) diff --git a/internal/storage/sql_provider_backend_postgres.go b/internal/storage/sql_provider_backend_postgres.go index 4a049cffd..ee6aa434b 100644 --- a/internal/storage/sql_provider_backend_postgres.go +++ b/internal/storage/sql_provider_backend_postgres.go @@ -66,6 +66,9 @@ func NewPostgreSQLProvider(config *schema.Configuration, caCertPool *x509.CertPo  	provider.sqlDeleteTOTPConfig = provider.db.Rebind(provider.sqlDeleteTOTPConfig)  	provider.sqlSelectTOTPConfigs = provider.db.Rebind(provider.sqlSelectTOTPConfigs) +	provider.sqlInsertTOTPHistory = provider.db.Rebind(provider.sqlInsertTOTPHistory) +	provider.sqlSelectTOTPHistory = provider.db.Rebind(provider.sqlSelectTOTPHistory) +  	provider.sqlInsertWebAuthnUser = provider.db.Rebind(provider.sqlInsertWebAuthnUser)  	provider.sqlSelectWebAuthnUser = provider.db.Rebind(provider.sqlSelectWebAuthnUser) diff --git a/internal/storage/sql_provider_queries.go b/internal/storage/sql_provider_queries.go index bff832dbd..48be6d2af 100644 --- a/internal/storage/sql_provider_queries.go +++ b/internal/storage/sql_provider_queries.go @@ -167,6 +167,17 @@ const (  		WHERE id = ?;`  ) +const ( +	queryFmtInsertTOTPHistory = ` +		INSERT INTO %s (username, step) +		VALUES (?, ?);` + +	queryFmtSelectTOTPHistory = ` +		SELECT COUNT(id) +		FROM %s +		WHERE username = ? AND step = ?;` +) +  //nolint:gosec // The following queries are not hard coded credentials.  const (  	queryFmtSelectWebAuthnCredentials = ` diff --git a/internal/suites/action_totp.go b/internal/suites/action_totp.go index fd75f7e8c..3f4f9f77f 100644 --- a/internal/suites/action_totp.go +++ b/internal/suites/action_totp.go @@ -7,10 +7,10 @@ import (  	"testing"  	"time" +	"github.com/authelia/otp" +	"github.com/authelia/otp/totp"  	"github.com/go-rod/rod"  	"github.com/go-rod/rod/lib/input" -	"github.com/pquerna/otp" -	"github.com/pquerna/otp/totp"  	"github.com/stretchr/testify/assert"  	"github.com/stretchr/testify/require"  ) diff --git a/internal/suites/suites_credentials.go b/internal/suites/suites_credentials.go index d59007144..ac9a1861c 100644 --- a/internal/suites/suites_credentials.go +++ b/internal/suites/suites_credentials.go @@ -4,7 +4,7 @@ import (  	"sync"  	"time" -	"github.com/pquerna/otp/totp" +	"github.com/authelia/otp/totp"  )  func NewRodSuiteCredentials() *RodSuiteCredentials { diff --git a/internal/totp/helpers.go b/internal/totp/helpers.go index e464c1d72..1f56ed832 100644 --- a/internal/totp/helpers.go +++ b/internal/totp/helpers.go @@ -1,7 +1,7 @@  package totp  import ( -	"github.com/pquerna/otp" +	"github.com/authelia/otp"  	"github.com/authelia/authelia/v4/internal/configuration/schema"  ) diff --git a/internal/totp/helpers_test.go b/internal/totp/helpers_test.go index 19c551cbf..060c775de 100644 --- a/internal/totp/helpers_test.go +++ b/internal/totp/helpers_test.go @@ -3,7 +3,7 @@ package totp  import (  	"testing" -	"github.com/pquerna/otp" +	"github.com/authelia/otp"  	"github.com/stretchr/testify/assert"  ) diff --git a/internal/totp/provider.go b/internal/totp/provider.go index 91ed49a10..6686ec904 100644 --- a/internal/totp/provider.go +++ b/internal/totp/provider.go @@ -8,6 +8,6 @@ import (  type Provider interface {  	Generate(username string) (config *model.TOTPConfiguration, err error)  	GenerateCustom(username string, algorithm, secret string, digits, period, secretSize uint) (config *model.TOTPConfiguration, err error) -	Validate(token string, config *model.TOTPConfiguration) (valid bool, err error) +	Validate(token string, config *model.TOTPConfiguration) (valid bool, step uint64, err error)  	Options() model.TOTPOptions  } diff --git a/internal/totp/totp.go b/internal/totp/totp.go index d1659a5d9..986626d6f 100644 --- a/internal/totp/totp.go +++ b/internal/totp/totp.go @@ -5,8 +5,8 @@ import (  	"fmt"  	"time" -	"github.com/pquerna/otp" -	"github.com/pquerna/otp/totp" +	"github.com/authelia/otp" +	"github.com/authelia/otp/totp"  	"github.com/authelia/authelia/v4/internal/configuration/schema"  	"github.com/authelia/authelia/v4/internal/model" @@ -104,7 +104,7 @@ func (p TimeBased) Generate(username string) (config *model.TOTPConfiguration, e  }  // Validate the token against the given configuration. -func (p TimeBased) Validate(token string, config *model.TOTPConfiguration) (valid bool, err error) { +func (p TimeBased) Validate(token string, config *model.TOTPConfiguration) (valid bool, step uint64, err error) {  	opts := totp.ValidateOpts{  		Period:    config.Period,  		Skew:      p.skew, @@ -112,10 +112,14 @@ func (p TimeBased) Validate(token string, config *model.TOTPConfiguration) (vali  		Algorithm: otpStringToAlgo(config.Algorithm),  	} -	return totp.ValidateCustom(token, string(config.Secret), time.Now().UTC(), opts) +	return totp.ValidateCustomStep(token, string(config.Secret), time.Now().UTC(), opts)  }  // Options returns the configured options for this provider.  func (p TimeBased) Options() model.TOTPOptions {  	return *p.opts  } + +var ( +	_ Provider = (*TimeBased)(nil) +)  | 
