MM-52477 - remove recovery tokens when creating a new one (#23171)

* remove recovery tokens when creating a new one

* add app layers, unit test, i18 extract

* lint fixes

* fix unit test
Этот коммит содержится в:
Scott Bishel
2023-05-15 11:28:54 -06:00
коммит произвёл GitHub
родитель e4075dae18
Коммит 355bc6502c
5 изменённых файлов: 107 добавлений и 1 удалений

Просмотреть файл

@@ -887,6 +887,7 @@ type AppIface interface {
InvalidateAllEmailInvites() *model.AppError
InvalidateAllResendInviteEmailJobs() *model.AppError
InvalidateCacheForUser(userID string)
InvalidatePasswordRecoveryTokensForUser(userID string) *model.AppError
InviteGuestsToChannels(teamID string, guestsInvite *model.GuestsInvite, senderId string) *model.AppError
InviteGuestsToChannelsGracefully(teamID string, guestsInvite *model.GuestsInvite, senderId string) ([]*model.EmailInviteWithError, *model.AppError)
InviteNewUsersToTeam(emailList []string, teamID, senderId string) *model.AppError

Просмотреть файл

@@ -11904,6 +11904,28 @@ func (a *OpenTracingAppLayer) InvalidateCacheForUser(userID string) {
a.app.InvalidateCacheForUser(userID)
}
func (a *OpenTracingAppLayer) InvalidatePasswordRecoveryTokensForUser(userID string) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.InvalidatePasswordRecoveryTokensForUser")
a.ctx = newCtx
a.app.Srv().Store().SetContext(newCtx)
defer func() {
a.app.Srv().Store().SetContext(origCtx)
a.ctx = origCtx
}()
defer span.Finish()
resultVar0 := a.app.InvalidatePasswordRecoveryTokensForUser(userID)
if resultVar0 != nil {
span.LogFields(spanlog.Error(resultVar0))
ext.Error.Set(span, true)
}
return resultVar0
}
func (a *OpenTracingAppLayer) InviteGuestsToChannels(teamID string, guestsInvite *model.GuestsInvite, senderId string) *model.AppError {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.InviteGuestsToChannels")

Просмотреть файл

@@ -1487,8 +1487,13 @@ func (a *App) CreatePasswordRecoveryToken(userID, email string) (*model.Token, *
return nil, model.NewAppError("CreatePasswordRecoveryToken", "api.user.create_password_token.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
token := model.NewToken(TokenTypePasswordRecovery, string(jsonData))
// remove any previously created tokens for user
appErr := a.InvalidatePasswordRecoveryTokensForUser(userID)
if appErr != nil {
mlog.Warn("Error while deleting additional user tokens.", mlog.Err(err))
}
token := model.NewToken(TokenTypePasswordRecovery, string(jsonData))
if err := a.Srv().Store().Token().Save(token); err != nil {
var appErr *model.AppError
switch {
@@ -1502,6 +1507,34 @@ func (a *App) CreatePasswordRecoveryToken(userID, email string) (*model.Token, *
return token, nil
}
func (a *App) InvalidatePasswordRecoveryTokensForUser(userID string) *model.AppError {
tokens, err := a.Srv().Store().Token().GetAllTokensByType(TokenTypePasswordRecovery)
if err != nil {
return model.NewAppError("InvalidatePasswordRecoveryTokensForUser", "api.user.invalidate_password_recovery_tokens.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
var appErr *model.AppError = nil
for _, token := range tokens {
tokenExtra := struct {
UserId string
Email string
}{}
if err := json.Unmarshal([]byte(token.Extra), &tokenExtra); err != nil {
appErr = model.NewAppError("InvalidatePasswordRecoveryTokensForUser", "api.user.invalidate_password_recovery_tokens_parse.error", nil, "", http.StatusInternalServerError).Wrap(err)
continue
}
if tokenExtra.UserId != userID {
continue
}
if err := a.Srv().Store().Token().Delete(token.Token); err != nil {
appErr = model.NewAppError("InvalidatePasswordRecoveryTokensForUser", "api.user.invalidate_password_recovery_tokens_delete.error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
return appErr
}
func (a *App) GetPasswordRecoveryToken(token string) (*model.Token, *model.AppError) {
rtoken, err := a.Srv().Store().Token().GetByToken(token)
if err != nil {

Просмотреть файл

@@ -1173,6 +1173,44 @@ func TestPasswordRecovery(t *testing.T) {
}
func TestInvalidatePasswordRecoveryTokens(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
t.Run("remove manually added tokens", func(t *testing.T) {
for i := 0; i < 5; i++ {
token := model.NewToken(
TokenTypePasswordRecovery,
model.MapToJSON(map[string]string{"UserId": th.BasicUser.Id, "email": th.BasicUser.Email}),
)
require.NoError(t, th.App.Srv().Store().Token().Save(token))
}
tokens, err := th.App.Srv().Store().Token().GetAllTokensByType(TokenTypePasswordRecovery)
assert.NoError(t, err)
assert.Equal(t, 5, len(tokens))
appErr := th.App.InvalidatePasswordRecoveryTokensForUser(th.BasicUser.Id)
assert.Nil(t, appErr)
tokens, err = th.App.Srv().Store().Token().GetAllTokensByType(TokenTypePasswordRecovery)
assert.NoError(t, err)
assert.Equal(t, 0, len(tokens))
})
t.Run("add multiple tokens, should only be one valid", func(t *testing.T) {
_, appErr := th.App.CreatePasswordRecoveryToken(th.BasicUser.Id, th.BasicUser.Email)
assert.Nil(t, appErr)
token, appErr := th.App.CreatePasswordRecoveryToken(th.BasicUser.Id, th.BasicUser.Email)
assert.Nil(t, appErr)
tokens, err := th.App.Srv().Store().Token().GetAllTokensByType(TokenTypePasswordRecovery)
assert.NoError(t, err)
assert.Equal(t, 1, len(tokens))
assert.Equal(t, token.Token, tokens[0].Token)
})
}
func TestGetViewUsersRestrictions(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()

Просмотреть файл

@@ -4163,6 +4163,18 @@
"id": "api.user.get_users.validation.app_error",
"translation": "Error fetching roles during validation."
},
{
"id": "api.user.invalidate_password_recovery_tokens.error",
"translation": "Unable to get tokens by type when invalidating password recovery tokens"
},
{
"id": "api.user.invalidate_password_recovery_tokens_delete.error",
"translation": "Unable to remove token when invalidating password recovery tokens"
},
{
"id": "api.user.invalidate_password_recovery_tokens_parse.error",
"translation": "Unable to parse token when invalidating password recovery tokens"
},
{
"id": "api.user.invalidate_verify_email_tokens.error",
"translation": "Unable to get tokens by type when invalidating email verification tokens"