diff --git a/backend/internal/service/one_time_access_service.go b/backend/internal/service/one_time_access_service.go index 0f6d5b27..b22a5871 100644 --- a/backend/internal/service/one_time_access_service.go +++ b/backend/internal/service/one_time_access_service.go @@ -194,6 +194,9 @@ func (s *OneTimeAccessService) ExchangeOneTimeAccessToken(ctx context.Context, t if oneTimeAccessToken.DeviceToken != nil && deviceToken != *oneTimeAccessToken.DeviceToken { return model.User{}, "", &common.DeviceCodeInvalid{} } + if oneTimeAccessToken.User.Disabled { + return model.User{}, "", &common.UserDisabledError{} + } accessToken, err := s.jwtService.GenerateAccessToken(oneTimeAccessToken.User, AuthenticationMethodOneTimePassword) if err != nil { diff --git a/backend/internal/service/one_time_access_service_test.go b/backend/internal/service/one_time_access_service_test.go new file mode 100644 index 00000000..44a189fe --- /dev/null +++ b/backend/internal/service/one_time_access_service_test.go @@ -0,0 +1,50 @@ +package service + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/pocket-id/pocket-id/backend/internal/common" + "github.com/pocket-id/pocket-id/backend/internal/model" + datatype "github.com/pocket-id/pocket-id/backend/internal/model/types" + testutils "github.com/pocket-id/pocket-id/backend/internal/utils/testing" +) + +func TestExchangeOneTimeAccessTokenRejectsDisabledUser(t *testing.T) { + db := testutils.NewDatabaseForTest(t) + appConfig := NewTestAppConfigService((&AppConfigService{}).getDefaultDbConfig()) + jwtService := initJwtService(t, db, appConfig, newTestEnvConfig()) + auditLogService := NewAuditLogService(db, appConfig, nil, &GeoLiteService{}) + oneTimeAccessService := NewOneTimeAccessService(db, nil, jwtService, auditLogService, nil, appConfig) + + user := model.User{ + Base: model.Base{ID: "disabled-user"}, + Username: "disabled-user", + Disabled: true, + } + require.NoError(t, db.Create(&user).Error) + + loginCode := model.OneTimeAccessToken{ + Base: model.Base{ID: "disabled-user-login-code"}, + Token: "ABCDEF", + ExpiresAt: datatype.DateTime(time.Now().Add(time.Minute)), + UserID: user.ID, + } + require.NoError(t, db.Create(&loginCode).Error) + + exchangedUser, accessToken, err := oneTimeAccessService.ExchangeOneTimeAccessToken(t.Context(), loginCode.Token, "", "", "") + + var userDisabledErr *common.UserDisabledError + require.ErrorAs(t, err, &userDisabledErr) + require.Empty(t, exchangedUser.ID) + require.Empty(t, accessToken) + + var remainingLoginCode model.OneTimeAccessToken + require.NoError(t, db.Where("token = ?", loginCode.Token).First(&remainingLoginCode).Error) + + var auditLogCount int64 + require.NoError(t, db.Model(&model.AuditLog{}).Where("user_id = ?", user.ID).Count(&auditLogCount).Error) + require.Zero(t, auditLogCount) +}