gitea

Development moved to Codeberg

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  92. 92
  93. 93
  94. 94
  95. 95
  96. 96
  97. 97
  98. 98
  99. 99
  100. 100
  101. 101
  102. 102
  103. 103
  104. 104
  105. 105
  106. 106
  107. 107
  108. 108
  109. 109
  110. 110
  111. 111
  112. 112
  113. 113
  114. 114
  115. 115
  116. 116
  117. 117
  118. 118
  119. 119
  120. 120
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package v1_9 //nolint

import (
	"fmt"

	"code.gitea.io/gitea/models/migrations/base"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/timeutil"
	"code.gitea.io/gitea/modules/util"

	"xorm.io/xorm"
)

func HashAppToken(x *xorm.Engine) error {
	// AccessToken see models/token.go
	type AccessToken struct {
		ID             int64 `xorm:"pk autoincr"`
		UID            int64 `xorm:"INDEX"`
		Name           string
		Sha1           string
		Token          string `xorm:"-"`
		TokenHash      string // sha256 of token - we will ensure UNIQUE later
		TokenSalt      string
		TokenLastEight string `xorm:"token_last_eight"`

		CreatedUnix       timeutil.TimeStamp `xorm:"INDEX created"`
		UpdatedUnix       timeutil.TimeStamp `xorm:"INDEX updated"`
		HasRecentActivity bool               `xorm:"-"`
		HasUsed           bool               `xorm:"-"`
	}

	// First remove the index
	sess := x.NewSession()
	defer sess.Close()

	if err := sess.Begin(); err != nil {
		return err
	}

	if err := sess.Sync2(new(AccessToken)); err != nil {
		return fmt.Errorf("Sync2: %w", err)
	}

	if err := sess.Commit(); err != nil {
		return err
	}

	if err := sess.Begin(); err != nil {
		return err
	}

	// transform all tokens to hashes
	const batchSize = 100
	for start := 0; ; start += batchSize {
		tokens := make([]*AccessToken, 0, batchSize)
		if err := sess.Limit(batchSize, start).Find(&tokens); err != nil {
			return err
		}
		if len(tokens) == 0 {
			break
		}

		for _, token := range tokens {
			// generate salt
			salt, err := util.CryptoRandomString(10)
			if err != nil {
				return err
			}
			token.TokenSalt = salt
			token.TokenHash = base.HashToken(token.Sha1, salt)
			if len(token.Sha1) < 8 {
				log.Warn("Unable to transform token %s with name %s belonging to user ID %d, skipping transformation", token.Sha1, token.Name, token.UID)
				continue
			}
			token.TokenLastEight = token.Sha1[len(token.Sha1)-8:]
			token.Sha1 = "" // ensure to blank out column in case drop column doesn't work

			if _, err := sess.ID(token.ID).Cols("token_hash, token_salt, token_last_eight, sha1").Update(token); err != nil {
				return fmt.Errorf("couldn't add in sha1, token_hash, token_salt and token_last_eight: %w", err)
			}

		}
	}

	// Commit and begin new transaction for dropping columns
	if err := sess.Commit(); err != nil {
		return err
	}
	if err := sess.Begin(); err != nil {
		return err
	}

	if err := base.DropTableColumns(sess, "access_token", "sha1"); err != nil {
		return err
	}
	if err := sess.Commit(); err != nil {
		return err
	}
	return resyncHashAppTokenWithUniqueHash(x)
}

func resyncHashAppTokenWithUniqueHash(x *xorm.Engine) error {
	// AccessToken see models/token.go
	type AccessToken struct {
		TokenHash string `xorm:"UNIQUE"` // sha256 of token - we will ensure UNIQUE later
	}
	sess := x.NewSession()
	defer sess.Close()
	if err := sess.Begin(); err != nil {
		return err
	}
	if err := sess.Sync2(new(AccessToken)); err != nil {
		return fmt.Errorf("Sync2: %w", err)
	}
	return sess.Commit()
}