[v10.0/forgejo] fix: discard v25 secrets migrations errors instead of failing ()

**Backport:** https://codeberg.org/forgejo/forgejo/pulls/7251

Failing the migration when a corrupted record is found is problematic because there is no transaction and the database may need to be restored from a backup to attempt the migration again, after deleting the corrupted records.

Each documented case of failed migration was resolved by removing the corrupted records. There is no instance of a failed migration that was caused by non corrupted record.

In the unlikely event of a false negative where a two_factor record is discarded although it is in use, the only consequence is that the user will have to enroll again. Detailed logs are displayed so the Forgejo admin can file a bug report if that happens.

Refs: https://codeberg.org/forgejo/forgejo/issues/6637

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Bug fixes
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/7251): <!--number 7251 --><!--line 0 --><!--description V2hlbiBtaWdyYXRpbmcgZnJvbSBhIEZvcmdlam8gdmVyc2lvbiBsb3dlciB0aGFuIHYxMCwgdGhlIFRPVFAgc2VjcmV0cyBmb3VuZCB0byBiZSBjb3JydXB0ZWQgYXJlIG5vdyB0cmFuc3BhcmVudGx5IHJlbW92ZWQgZnJvbSB0aGUgZGF0YWJhc2UgaW5zdGVhZCBvZiBmYWlsaW5nIHRoZSBtaWdyYXRpb24uIFRPVFAgaXMgbm8gbG9uZ2VyIHJlcXVpcmVkIHRvIGxvZ2luIHdpdGggdGhlIGFzc29jaWF0ZWQgdXNlcnMuIFRoZXkgc2hvdWxkIGJlIGluZm9ybWVkIGJlY2F1c2UgdGhleSB3aWxsIG5lZWQgdG8gdmlzaXQgdGhlaXIgc2VjdXJpdHkgc2V0dGluZ3MgYW5kIGNvbmZpZ3VyZSBUT1RQIGFnYWluLiBObyBvdGhlciBhY3Rpb24gaXMgcmVxdWlyZWQu-->When migrating from a Forgejo version lower than v10, the TOTP secrets found to be corrupted are now transparently removed from the database instead of failing the migration. TOTP is no longer required to login with the associated users. They should be informed because they will need to visit their security settings and configure TOTP again. No other action is required.<!--description-->
<!--end release-notes-assistant-->

Co-authored-by: Earl Warren <contact@earl-warren.org>
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7255
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
This commit is contained in:
forgejo-backport-action 2025-03-17 17:55:32 +00:00 committed by Earl Warren
parent e7d103319e
commit 400bd08cfe
4 changed files with 39 additions and 4 deletions
models
forgejo_migrations
migrations/fixtures/Test_MigrateTwoFactorToKeying
release-notes

View file

@ -7,9 +7,11 @@ import (
"context"
"crypto/md5"
"encoding/base64"
"fmt"
"code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/secret"
"code.gitea.io/gitea/modules/setting"
@ -57,19 +59,38 @@ func MigrateTwoFactorToKeying(x *xorm.Engine) error {
oldEncryptionKey := md5.Sum([]byte(setting.SecretKey))
return db.Iterate(context.Background(), nil, func(ctx context.Context, bean *auth.TwoFactor) error {
messages := make([]string, 0, 100)
ids := make([]int64, 0, 100)
err = db.Iterate(context.Background(), nil, func(ctx context.Context, bean *auth.TwoFactor) error {
decodedStoredSecret, err := base64.StdEncoding.DecodeString(string(bean.Secret))
if err != nil {
return err
messages = append(messages, fmt.Sprintf("two_factor.id=%d, two_factor.uid=%d: base64.StdEncoding.DecodeString: %v", bean.ID, bean.UID, err))
ids = append(ids, bean.ID)
return nil
}
secretBytes, err := secret.AesDecrypt(oldEncryptionKey[:], decodedStoredSecret)
if err != nil {
return err
messages = append(messages, fmt.Sprintf("two_factor.id=%d, two_factor.uid=%d: secret.AesDecrypt: %v", bean.ID, bean.UID, err))
ids = append(ids, bean.ID)
return nil
}
bean.SetSecret(string(secretBytes))
_, err = db.GetEngine(ctx).Cols("secret").ID(bean.ID).Update(bean)
return err
})
if err == nil {
if len(ids) > 0 {
log.Error("Forgejo migration[25]: The following TOTP secrets were found to be corrupted and removed from the database. TOTP is no longer required to login with the associated users. They should be informed because they will need to visit their security settings and configure TOTP again. No other action is required. See https://codeberg.org/forgejo/forgejo/issues/6637 for more context on the various causes for such a corruption.")
for _, message := range messages {
log.Error("Forgejo migration[25]: %s", message)
}
_, err = db.GetEngine(context.Background()).In("id", ids).NoAutoCondition().NoAutoTime().Delete(&auth.TwoFactor{})
}
}
return err
}

View file

@ -36,10 +36,14 @@ func Test_MigrateTwoFactorToKeying(t *testing.T) {
cnt, err := x.Table("two_factor").Count()
require.NoError(t, err)
assert.EqualValues(t, 1, cnt)
assert.EqualValues(t, 2, cnt)
require.NoError(t, MigrateTwoFactorToKeying(x))
cnt, err = x.Table("two_factor").Count()
require.NoError(t, err)
assert.EqualValues(t, 1, cnt)
var twofactor auth.TwoFactor
_, err = x.Table("two_factor").ID(1).Get(&twofactor)
require.NoError(t, err)

View file

@ -7,3 +7,12 @@
last_used_passcode:
created_unix: 1564253724
updated_unix: 1564253724
-
id: 2
uid: 23
secret: badbad
scratch_salt: badbad
scratch_hash: badbad
last_used_passcode:
created_unix: 1564253724
updated_unix: 1564253724

1
release-notes/7251.md Normal file
View file

@ -0,0 +1 @@
When migrating from a Forgejo version lower than v10, the TOTP secrets found to be corrupted are now transparently removed from the database instead of failing the migration. TOTP is no longer required to login with the associated users. They should be informed because they will need to visit their security settings and configure TOTP again. No other action is required.