Compare commits

..

No commits in common. "1007ce764ea80b48120b796175d7d1210cbb6f74" and "edbf74c418061b013a5855f604dd6be6baf34132" have entirely different histories.

26 changed files with 149 additions and 258 deletions

View File

@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"strings"
"sync/atomic"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/modules/container"
@ -107,23 +106,10 @@ var (
TypeExternalTracker,
}
disabledRepoUnitsAtomic atomic.Pointer[[]Type] // the units that have been globally disabled
// DisabledRepoUnits contains the units that have been globally disabled
DisabledRepoUnits = []Type{}
)
// DisabledRepoUnitsGet returns the globally disabled units, it is a quick patch to fix data-race during testing.
// Because the queue worker might read when a test is mocking the value. FIXME: refactor to a clear solution later.
func DisabledRepoUnitsGet() []Type {
v := disabledRepoUnitsAtomic.Load()
if v == nil {
return nil
}
return *v
}
func DisabledRepoUnitsSet(v []Type) {
disabledRepoUnitsAtomic.Store(&v)
}
// Get valid set of default repository units from settings
func validateDefaultRepoUnits(defaultUnits, settingDefaultUnits []Type) []Type {
units := defaultUnits
@ -141,7 +127,7 @@ func validateDefaultRepoUnits(defaultUnits, settingDefaultUnits []Type) []Type {
}
// Remove disabled units
for _, disabledUnit := range DisabledRepoUnitsGet() {
for _, disabledUnit := range DisabledRepoUnits {
for i, unit := range units {
if unit == disabledUnit {
units = append(units[:i], units[i+1:]...)
@ -154,11 +140,11 @@ func validateDefaultRepoUnits(defaultUnits, settingDefaultUnits []Type) []Type {
// LoadUnitConfig load units from settings
func LoadUnitConfig() error {
disabledRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DisabledRepoUnits...)
var invalidKeys []string
DisabledRepoUnits, invalidKeys = FindUnitTypes(setting.Repository.DisabledRepoUnits...)
if len(invalidKeys) > 0 {
log.Warn("Invalid keys in disabled repo units: %s", strings.Join(invalidKeys, ", "))
}
DisabledRepoUnitsSet(disabledRepoUnits)
setDefaultRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultRepoUnits...)
if len(invalidKeys) > 0 {
@ -181,7 +167,7 @@ func LoadUnitConfig() error {
// UnitGlobalDisabled checks if unit type is global disabled
func (u Type) UnitGlobalDisabled() bool {
for _, ud := range DisabledRepoUnitsGet() {
for _, ud := range DisabledRepoUnits {
if u == ud {
return true
}

View File

@ -14,10 +14,10 @@ import (
func TestLoadUnitConfig(t *testing.T) {
t.Run("regular", func(t *testing.T) {
defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) {
DisabledRepoUnitsSet(disabledRepoUnits)
DisabledRepoUnits = disabledRepoUnits
DefaultRepoUnits = defaultRepoUnits
DefaultForkRepoUnits = defaultForkRepoUnits
}(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits)
}(DisabledRepoUnits, DefaultRepoUnits, DefaultForkRepoUnits)
defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) {
setting.Repository.DisabledRepoUnits = disabledRepoUnits
setting.Repository.DefaultRepoUnits = defaultRepoUnits
@ -28,16 +28,16 @@ func TestLoadUnitConfig(t *testing.T) {
setting.Repository.DefaultRepoUnits = []string{"repo.code", "repo.releases", "repo.issues", "repo.pulls"}
setting.Repository.DefaultForkRepoUnits = []string{"repo.releases"}
assert.NoError(t, LoadUnitConfig())
assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet())
assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnits)
assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits)
assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits)
})
t.Run("invalid", func(t *testing.T) {
defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) {
DisabledRepoUnitsSet(disabledRepoUnits)
DisabledRepoUnits = disabledRepoUnits
DefaultRepoUnits = defaultRepoUnits
DefaultForkRepoUnits = defaultForkRepoUnits
}(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits)
}(DisabledRepoUnits, DefaultRepoUnits, DefaultForkRepoUnits)
defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) {
setting.Repository.DisabledRepoUnits = disabledRepoUnits
setting.Repository.DefaultRepoUnits = defaultRepoUnits
@ -48,16 +48,16 @@ func TestLoadUnitConfig(t *testing.T) {
setting.Repository.DefaultRepoUnits = []string{"repo.code", "invalid.2", "repo.releases", "repo.issues", "repo.pulls"}
setting.Repository.DefaultForkRepoUnits = []string{"invalid.3", "repo.releases"}
assert.NoError(t, LoadUnitConfig())
assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet())
assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnits)
assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits)
assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits)
})
t.Run("duplicate", func(t *testing.T) {
defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) {
DisabledRepoUnitsSet(disabledRepoUnits)
DisabledRepoUnits = disabledRepoUnits
DefaultRepoUnits = defaultRepoUnits
DefaultForkRepoUnits = defaultForkRepoUnits
}(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits)
}(DisabledRepoUnits, DefaultRepoUnits, DefaultForkRepoUnits)
defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) {
setting.Repository.DisabledRepoUnits = disabledRepoUnits
setting.Repository.DefaultRepoUnits = defaultRepoUnits
@ -68,16 +68,16 @@ func TestLoadUnitConfig(t *testing.T) {
setting.Repository.DefaultRepoUnits = []string{"repo.code", "repo.releases", "repo.issues", "repo.pulls", "repo.code"}
setting.Repository.DefaultForkRepoUnits = []string{"repo.releases", "repo.releases"}
assert.NoError(t, LoadUnitConfig())
assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet())
assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnits)
assert.Equal(t, []Type{TypeCode, TypeReleases, TypePullRequests}, DefaultRepoUnits)
assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits)
})
t.Run("empty_default", func(t *testing.T) {
defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []Type) {
DisabledRepoUnitsSet(disabledRepoUnits)
DisabledRepoUnits = disabledRepoUnits
DefaultRepoUnits = defaultRepoUnits
DefaultForkRepoUnits = defaultForkRepoUnits
}(DisabledRepoUnitsGet(), DefaultRepoUnits, DefaultForkRepoUnits)
}(DisabledRepoUnits, DefaultRepoUnits, DefaultForkRepoUnits)
defer func(disabledRepoUnits, defaultRepoUnits, defaultForkRepoUnits []string) {
setting.Repository.DisabledRepoUnits = disabledRepoUnits
setting.Repository.DefaultRepoUnits = defaultRepoUnits
@ -88,7 +88,7 @@ func TestLoadUnitConfig(t *testing.T) {
setting.Repository.DefaultRepoUnits = []string{}
setting.Repository.DefaultForkRepoUnits = []string{"repo.releases", "repo.releases"}
assert.NoError(t, LoadUnitConfig())
assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnitsGet())
assert.Equal(t, []Type{TypeIssues}, DisabledRepoUnits)
assert.ElementsMatch(t, []Type{TypeCode, TypePullRequests, TypeReleases, TypeWiki, TypePackages, TypeProjects, TypeActions}, DefaultRepoUnits)
assert.Equal(t, []Type{TypeReleases}, DefaultForkRepoUnits)
})

View File

@ -10,7 +10,6 @@ import (
"net/mail"
"regexp"
"strings"
"time"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
@ -354,12 +353,14 @@ func ChangeInactivePrimaryEmail(ctx context.Context, uid int64, oldEmailAddr, ne
// VerifyActiveEmailCode verifies active email code when active account
func VerifyActiveEmailCode(ctx context.Context, code, email string) *EmailAddress {
minutes := setting.Service.ActiveCodeLives
if user := GetVerifyUser(ctx, code); user != nil {
// time limit code
prefix := code[:base.TimeLimitCodeLength]
data := fmt.Sprintf("%d%s%s%s%s", user.ID, email, user.LowerName, user.Passwd, user.Rands)
if base.VerifyTimeLimitCode(time.Now(), data, setting.Service.ActiveCodeLives, prefix) {
if base.VerifyTimeLimitCode(data, minutes, prefix) {
emailAddress := &EmailAddress{UID: user.ID, Email: email}
if has, _ := db.GetEngine(ctx).Get(emailAddress); has {
return emailAddress

View File

@ -304,7 +304,7 @@ func (u *User) OrganisationLink() string {
func (u *User) GenerateEmailActivateCode(email string) string {
code := base.CreateTimeLimitCode(
fmt.Sprintf("%d%s%s%s%s", u.ID, email, u.LowerName, u.Passwd, u.Rands),
setting.Service.ActiveCodeLives, time.Now(), nil)
setting.Service.ActiveCodeLives, nil)
// Add tail hex username
code += hex.EncodeToString([]byte(u.LowerName))
@ -791,11 +791,14 @@ func GetVerifyUser(ctx context.Context, code string) (user *User) {
// VerifyUserActiveCode verifies active code when active account
func VerifyUserActiveCode(ctx context.Context, code string) (user *User) {
minutes := setting.Service.ActiveCodeLives
if user = GetVerifyUser(ctx, code); user != nil {
// time limit code
prefix := code[:base.TimeLimitCodeLength]
data := fmt.Sprintf("%d%s%s%s%s", user.ID, user.Email, user.LowerName, user.Passwd, user.Rands)
if base.VerifyTimeLimitCode(time.Now(), data, setting.Service.ActiveCodeLives, prefix) {
if base.VerifyTimeLimitCode(data, minutes, prefix) {
return user
}
}

View File

@ -4,15 +4,12 @@
package base
import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"crypto/subtle"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"hash"
"os"
"path/filepath"
"runtime"
@ -28,6 +25,13 @@ import (
"github.com/dustin/go-humanize"
)
// EncodeSha1 string to sha1 hex value.
func EncodeSha1(str string) string {
h := sha1.New()
_, _ = h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
// EncodeSha256 string to sha256 hex value.
func EncodeSha256(str string) string {
h := sha256.New()
@ -58,62 +62,63 @@ func BasicAuthDecode(encoded string) (string, string, error) {
}
// VerifyTimeLimitCode verify time limit code
func VerifyTimeLimitCode(now time.Time, data string, minutes int, code string) bool {
func VerifyTimeLimitCode(data string, minutes int, code string) bool {
if len(code) <= 18 {
return false
}
startTimeStr := code[:12]
aliveTimeStr := code[12:18]
aliveTime, _ := strconv.Atoi(aliveTimeStr) // no need to check err, if anything wrong, the following code check will fail soon
// split code
start := code[:12]
lives := code[12:18]
if d, err := strconv.ParseInt(lives, 10, 0); err == nil {
minutes = int(d)
}
// check code
retCode := CreateTimeLimitCode(data, aliveTime, startTimeStr, nil)
if subtle.ConstantTimeCompare([]byte(retCode), []byte(code)) != 1 {
retCode = CreateTimeLimitCode(data, aliveTime, startTimeStr, sha1.New()) // TODO: this is only for the support of legacy codes, remove this in/after 1.23
if subtle.ConstantTimeCompare([]byte(retCode), []byte(code)) != 1 {
return false
// right active code
retCode := CreateTimeLimitCode(data, minutes, start)
if retCode == code && minutes > 0 {
// check time is expired or not
before, _ := time.ParseInLocation("200601021504", start, time.Local)
now := time.Now()
if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() {
return true
}
}
// check time is expired or not: startTime <= now && now < startTime + minutes
startTime, _ := time.ParseInLocation("200601021504", startTimeStr, time.Local)
return (startTime.Before(now) || startTime.Equal(now)) && now.Before(startTime.Add(time.Minute*time.Duration(minutes)))
return false
}
// TimeLimitCodeLength default value for time limit code
const TimeLimitCodeLength = 12 + 6 + 40
// CreateTimeLimitCode create a time-limited code.
// Format: 12 length date time string + 6 minutes string (not used) + 40 hash string, some other code depends on this fixed length
// If h is nil, then use the default hmac hash.
func CreateTimeLimitCode[T time.Time | string](data string, minutes int, startTimeGeneric T, h hash.Hash) string {
const format = "200601021504"
// CreateTimeLimitCode create a time limit code
// code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
func CreateTimeLimitCode(data string, minutes int, startInf any) string {
format := "200601021504"
var start time.Time
var startTimeAny any = startTimeGeneric
if t, ok := startTimeAny.(time.Time); ok {
start = t
var start, end time.Time
var startStr, endStr string
if startInf == nil {
// Use now time create code
start = time.Now()
startStr = start.Format(format)
} else {
var err error
start, err = time.ParseInLocation(format, startTimeAny.(string), time.Local)
if err != nil {
return "" // return an invalid code because the "parse" failed
}
// use start string create code
startStr = startInf.(string)
start, _ = time.ParseInLocation(format, startStr, time.Local)
startStr = start.Format(format)
}
startStr := start.Format(format)
end := start.Add(time.Minute * time.Duration(minutes))
if h == nil {
h = hmac.New(sha1.New, setting.GetGeneralTokenSigningSecret())
}
_, _ = fmt.Fprintf(h, "%s%s%s%s%d", data, hex.EncodeToString(setting.GetGeneralTokenSigningSecret()), startStr, end.Format(format), minutes)
encoded := hex.EncodeToString(h.Sum(nil))
end = start.Add(time.Minute * time.Duration(minutes))
endStr = end.Format(format)
// create sha1 encode string
sh := sha1.New()
_, _ = sh.Write([]byte(fmt.Sprintf("%s%s%s%s%d", data, hex.EncodeToString(setting.GetGeneralTokenSigningSecret()), startStr, endStr, minutes)))
encoded := hex.EncodeToString(sh.Sum(nil))
code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded)
if len(code) != TimeLimitCodeLength {
panic("there is a hard requirement for the length of time-limited code") // it shouldn't happen
}
return code
}

View File

@ -4,18 +4,20 @@
package base
import (
"crypto/sha1"
"fmt"
"os"
"testing"
"time"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
func TestEncodeSha1(t *testing.T) {
assert.Equal(t,
"8843d7f92416211de9ebb963ff4ce28125932878",
EncodeSha1("foobar"),
)
}
func TestEncodeSha256(t *testing.T) {
assert.Equal(t,
"c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2",
@ -44,54 +46,43 @@ func TestBasicAuthDecode(t *testing.T) {
}
func TestVerifyTimeLimitCode(t *testing.T) {
defer test.MockVariableValue(&setting.InstallLock, true)()
initGeneralSecret := func(secret string) {
setting.InstallLock = true
setting.CfgProvider, _ = setting.NewConfigProviderFromData(fmt.Sprintf(`
[oauth2]
JWT_SECRET = %s
`, secret))
setting.LoadCommonSettings()
tc := []struct {
data string
minutes int
code string
valid bool
}{{
data: "data",
minutes: 2,
code: testCreateTimeLimitCode(t, "data", 2),
valid: true,
}, {
data: "abc123-ß",
minutes: 1,
code: testCreateTimeLimitCode(t, "abc123-ß", 1),
valid: true,
}, {
data: "data",
minutes: 2,
code: "2021012723240000005928251dac409d2c33a6eb82c63410aaad569bed",
valid: false,
}}
for _, test := range tc {
actualValid := VerifyTimeLimitCode(test.data, test.minutes, test.code)
assert.Equal(t, test.valid, actualValid, "data: '%s' code: '%s' should be valid: %t", test.data, test.code, test.valid)
}
}
initGeneralSecret("KZb_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko")
now := time.Now()
func testCreateTimeLimitCode(t *testing.T, data string, m int) string {
result0 := CreateTimeLimitCode(data, m, nil)
result1 := CreateTimeLimitCode(data, m, time.Now().Format("200601021504"))
result2 := CreateTimeLimitCode(data, m, time.Unix(time.Now().Unix()+int64(time.Minute)*int64(m), 0).Format("200601021504"))
t.Run("TestGenericParameter", func(t *testing.T) {
time2000 := time.Date(2000, 1, 2, 3, 4, 5, 0, time.Local)
assert.Equal(t, "2000010203040000026fa5221b2731b7cf80b1b506f5e39e38c115fee5", CreateTimeLimitCode("test-sha1", 2, time2000, sha1.New()))
assert.Equal(t, "2000010203040000026fa5221b2731b7cf80b1b506f5e39e38c115fee5", CreateTimeLimitCode("test-sha1", 2, "200001020304", sha1.New()))
assert.Equal(t, "2000010203040000024842227a2f87041ff82025199c0187410a9297bf", CreateTimeLimitCode("test-hmac", 2, time2000, nil))
assert.Equal(t, "2000010203040000024842227a2f87041ff82025199c0187410a9297bf", CreateTimeLimitCode("test-hmac", 2, "200001020304", nil))
})
assert.Equal(t, result0, result1)
assert.NotEqual(t, result0, result2)
t.Run("TestInvalidCode", func(t *testing.T) {
assert.False(t, VerifyTimeLimitCode(now, "data", 2, ""))
assert.False(t, VerifyTimeLimitCode(now, "data", 2, "invalid code"))
})
t.Run("TestCreateAndVerify", func(t *testing.T) {
code := CreateTimeLimitCode("data", 2, now, nil)
assert.False(t, VerifyTimeLimitCode(now.Add(-time.Minute), "data", 2, code)) // not started yet
assert.True(t, VerifyTimeLimitCode(now, "data", 2, code))
assert.True(t, VerifyTimeLimitCode(now.Add(time.Minute), "data", 2, code))
assert.False(t, VerifyTimeLimitCode(now.Add(time.Minute), "DATA", 2, code)) // invalid data
assert.False(t, VerifyTimeLimitCode(now.Add(2*time.Minute), "data", 2, code)) // expired
})
t.Run("TestDifferentSecret", func(t *testing.T) {
// use another secret to ensure the code is invalid for different secret
verifyDataCode := func(c string) bool {
return VerifyTimeLimitCode(now, "data", 2, c)
}
code1 := CreateTimeLimitCode("data", 2, now, sha1.New())
code2 := CreateTimeLimitCode("data", 2, now, nil)
assert.True(t, verifyDataCode(code1))
assert.True(t, verifyDataCode(code2))
initGeneralSecret("000_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko")
assert.False(t, verifyDataCode(code1))
assert.False(t, verifyDataCode(code2))
})
assert.True(t, len(result0) != 0)
return result0
}
func TestFileSize(t *testing.T) {

View File

@ -4,8 +4,6 @@
package git
import (
"crypto/sha1"
"encoding/hex"
"fmt"
"io"
"os"
@ -130,9 +128,3 @@ func (l *LimitedReaderCloser) Read(p []byte) (n int, err error) {
func (l *LimitedReaderCloser) Close() error {
return l.C.Close()
}
func HashFilePathForWebUI(s string) string {
h := sha1.New()
_, _ = h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}

View File

@ -1,17 +0,0 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestHashFilePathForWebUI(t *testing.T) {
assert.Equal(t,
"8843d7f92416211de9ebb963ff4ce28125932878",
HashFilePathForWebUI("foobar"),
)
}

View File

@ -1,6 +1,5 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package util
import (
@ -9,7 +8,7 @@ import (
"strings"
)
// HexToRBGColor parses color as RGB values in 0..255 range from the hex color string (with or without #)
// Get color as RGB values in 0..255 range from the hex color string (with or without #)
func HexToRBGColor(colorString string) (float64, float64, float64) {
hexString := colorString
if strings.HasPrefix(colorString, "#") {
@ -36,7 +35,7 @@ func HexToRBGColor(colorString string) (float64, float64, float64) {
return r, g, b
}
// GetRelativeLuminance returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance
// Returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance
// Keep this in sync with web_src/js/utils/color.js
func GetRelativeLuminance(color string) float64 {
r, g, b := HexToRBGColor(color)
@ -47,8 +46,8 @@ func UseLightText(backgroundColor string) bool {
return GetRelativeLuminance(backgroundColor) < 0.453
}
// ContrastColor returns a black or white foreground color that the highest contrast ratio.
// In the future, the APCA contrast function, or CSS `contrast-color` will be better.
// Given a background color, returns a black or white foreground color that the highest
// contrast ratio. In the future, the APCA contrast function, or CSS `contrast-color` will be better.
// https://github.com/color-js/color.js/blob/eb7b53f7a13bb716ec8b28c7a56f052cd599acd9/src/contrast/APCA.js#L42
func ContrastColor(backgroundColor string) string {
if UseLightText(backgroundColor) {

View File

@ -3415,7 +3415,6 @@ error.unit_not_allowed = You are not allowed to access this repository section.
title = Packages
desc = Manage repository packages.
empty = There are no packages yet.
no_metadata = No metadata.
empty.documentation = For more information on the package registry, see <a target="_blank" rel="noopener noreferrer" href="%s">the documentation</a>.
empty.repo = Did you upload a package, but it's not shown here? Go to <a href="%[1]s">package settings</a> and link it to this repo.
registry.documentation = For more information on the %s registry, see <a target="_blank" rel="noopener noreferrer" href="%s">the documentation</a>.

View File

@ -436,7 +436,6 @@ oauth_signin_submit=アカウントにリンク
oauth.signin.error=認可リクエストの処理中にエラーが発生しました。このエラーが解決しない場合は、サイト管理者に問い合わせてください。
oauth.signin.error.access_denied=認可リクエストが拒否されました。
oauth.signin.error.temporarily_unavailable=認証サーバーが一時的に利用できないため、認可に失敗しました。後でもう一度やり直してください。
oauth_callback_unable_auto_reg=自動登録が有効になっていますが、OAuth2プロバイダー %[1]s の応答はフィールド %[2]s が不足しており、自動でアカウントを作成することができません。 アカウントを作成またはリンクするか、サイト管理者に問い合わせてください。
openid_connect_submit=接続
openid_connect_title=既存のアカウントに接続
openid_connect_desc=選択したOpenID URIは未登録です。 ここで新しいアカウントと関連付けます。
@ -764,8 +763,6 @@ manage_themes=デフォルトのテーマを選択
manage_openid=OpenIDアドレスの管理
email_desc=プライマリメールアドレスは、通知、パスワードの回復、さらにメールアドレスを隠さない場合は、WebベースのGit操作にも使用されます。
theme_desc=この設定がサイト全体のデフォルトのテーマとなります。
theme_colorblindness_help=色覚障害テーマのサポート
theme_colorblindness_prompt=Giteaには基本的な色覚障害サポートを含むテーマがいくつか入っていますが、それらは色定義が少ししかありません。 作業はまだ進行中です。 テーマCSSファイルにもっと多くの色を定義していくことで、さらに改善できる余地があります。
primary=プライマリー
activated=アクティベート済み
requires_activation=アクティベーションが必要
@ -3320,7 +3317,6 @@ self_check.database_collation_case_insensitive=データベースは照合順序
self_check.database_inconsistent_collation_columns=データベースは照合順序 %s を使用していますが、以下のカラムはそれと一致しない照合順序を使用しており、予期せぬ問題を引き起こす可能性があります。
self_check.database_fix_mysql=MySQL/MariaDBユーザーの方は、"gitea doctor convert" コマンドを使用することで、照合順序の問題を修正できます。 また、"ALTER ... COLLATE ..." のSQLを手で実行しても修正することができます。
self_check.database_fix_mssql=MSSQLユーザーの方は、問題を修正するには今のところ "ALTER ... COLLATE ..." のSQLを手で実行するしかありません。
self_check.location_origin_mismatch=現在のURL (%[1]s) は、Giteaが見ているURL (%[2]s) に一致していません。 リバースプロキシを使用している場合は、"Host" ヘッダーと "X-Forwarded-Proto" ヘッダーが正しく設定されていることを確認してください。
[action]
create_repo=がリポジトリ <a href="%s">%s</a> を作成しました
@ -3348,7 +3344,6 @@ mirror_sync_create=が <a href="%[1]s">%[4]s</a> の新しい参照 <a href="%[2
mirror_sync_delete=が <a href="%[1]s">%[3]s</a> の参照 <code>%[2]s</code> をミラーから反映し、削除しました
approve_pull_request=`が <a href="%[1]s">%[3]s#%[2]s</a> を承認しました`
reject_pull_request=`が <a href="%[1]s">%[3]s#%[2]s</a>について変更を提案しました`
publish_release=`が <a href="%[1]s">%[3]s</a> の <a href="%[2]s">%[4]s</a> をリリースしました`
review_dismissed=`が <b>%[4]s</b> の <a href="%[1]s">%[3]s#%[2]s</a> へのレビューを棄却しました`
review_dismissed_reason=理由:
create_branch=がブランチ <a href="%[2]s">%[3]s</a> を <a href="%[1]s">%[4]s</a> に作成しました

View File

@ -541,16 +541,6 @@ func GrantApplicationOAuth(ctx *context.Context) {
ctx.Error(http.StatusBadRequest)
return
}
if !form.Granted {
handleAuthorizeError(ctx, AuthorizeError{
State: form.State,
ErrorDescription: "the request is denied",
ErrorCode: ErrorCodeAccessDenied,
}, form.RedirectURI)
return
}
app, err := auth.GetOAuth2ApplicationByClientID(ctx, form.ClientID)
if err != nil {
ctx.ServerError("GetOAuth2ApplicationByClientID", err)

View File

@ -931,7 +931,7 @@ func ExcerptBlob(ctx *context.Context) {
}
}
ctx.Data["section"] = section
ctx.Data["FileNameHash"] = git.HashFilePathForWebUI(filePath)
ctx.Data["FileNameHash"] = base.EncodeSha1(filePath)
ctx.Data["AfterCommitID"] = commitID
ctx.Data["Anchor"] = anchor
ctx.HTML(http.StatusOK, tplBlobExcerpt)

View File

@ -161,7 +161,6 @@ func (f *AuthorizationForm) Validate(req *http.Request, errs binding.Errors) bin
// GrantApplicationForm form for authorizing oauth2 clients
type GrantApplicationForm struct {
ClientID string `binding:"Required"`
Granted bool
RedirectURI string
State string
Scope string

View File

@ -23,6 +23,7 @@ import (
pull_model "code.gitea.io/gitea/models/pull"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/analyze"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/highlight"
@ -745,7 +746,7 @@ parsingLoop:
diffLineTypeBuffers[DiffLineAdd] = new(bytes.Buffer)
diffLineTypeBuffers[DiffLineDel] = new(bytes.Buffer)
for _, f := range diff.Files {
f.NameHash = git.HashFilePathForWebUI(f.Name)
f.NameHash = base.EncodeSha1(f.Name)
for _, buffer := range diffLineTypeBuffers {
buffer.Reset()

View File

@ -1,8 +1,4 @@
{{if and (eq .PackageDescriptor.Package.Type "maven") (not .PackageDescriptor.Metadata)}}
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.installation"}}</h4>
<div class="ui attached segment">{{ctx.Locale.Tr "packages.no_metadata"}}</div>
{{end}}
{{if and (eq .PackageDescriptor.Package.Type "maven") .PackageDescriptor.Metadata}}
{{if eq .PackageDescriptor.Package.Type "maven"}}
<h4 class="ui top attached header">{{ctx.Locale.Tr "packages.installation"}}</h4>
<div class="ui attached segment">
<div class="ui form">

View File

@ -1,7 +1,4 @@
{{if and (eq .PackageDescriptor.Package.Type "maven") (not .PackageDescriptor.Metadata)}}
<div class="item">{{svg "octicon-note" 16 "tw-mr-2"}} {{ctx.Locale.Tr "packages.no_metadata"}}</div>
{{end}}
{{if and (eq .PackageDescriptor.Package.Type "maven") .PackageDescriptor.Metadata}}
{{if eq .PackageDescriptor.Package.Type "maven"}}
{{if .PackageDescriptor.Metadata.Name}}<div class="item">{{svg "octicon-note" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.Name}}</div>{{end}}
{{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "tw-mr-2"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{ctx.Locale.Tr "packages.details.project_site"}}</a></div>{{end}}
{{range .PackageDescriptor.Metadata.Licenses}}<div class="item" title="{{ctx.Locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "tw-mr-2"}} {{.}}</div>{{end}}

View File

@ -68,14 +68,18 @@
{{range .Columns}}
<div class="ui segment project-column"{{if .Color}} style="background: {{.Color}} !important; color: {{ContrastColor .Color}} !important"{{end}} data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}">
<div class="project-column-header{{if $canWriteProject}} tw-cursor-grab{{end}}">
<div class="ui circular label project-column-issue-count">
{{.NumIssues ctx}}
<div class="ui large label project-column-title tw-py-1">
<div class="ui small circular grey label project-column-issue-count">
{{.NumIssues ctx}}
</div>
<span class="project-column-title-label">{{.Title}}</span>
</div>
<div class="project-column-title-label gt-ellipsis">{{.Title}}</div>
{{if $canWriteProject}}
<div class="ui dropdown tw-p-1">
{{svg "octicon-kebab-horizontal"}}
<div class="menu">
<div class="ui dropdown jump item">
<div class="tw-px-2">
{{svg "octicon-kebab-horizontal"}}
</div>
<div class="menu user-menu">
<a class="item show-modal button" data-modal="#edit-project-column-modal-{{.ID}}">
{{svg "octicon-pencil"}}
{{ctx.Locale.Tr "repo.projects.column.edit"}}

View File

@ -87,7 +87,7 @@
<td class="eight wide">
{{if .DBBranch.IsDeleted}}
<div class="flex-text-block">
<span class="gt-ellipsis">{{.DBBranch.Name}}</span>
<a class="gt-ellipsis" href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .DBBranch.Name}}">{{.DBBranch.Name}}</a>
<button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DBBranch.Name}}">{{svg "octicon-copy" 14}}</button>
</div>
<p class="info">{{ctx.Locale.Tr "repo.branch.deleted_by" .DBBranch.DeletedBy.Name}} {{TimeSinceUnix .DBBranch.DeletedUnix ctx.Locale}}</p>

View File

@ -1,5 +1,4 @@
{{$file := .file}}
{{$blobExcerptRepoLink := or ctx.RootData.CommitRepoLink ctx.RootData.RepoLink}}
<colgroup>
<col width="50">
<col width="10">
@ -19,17 +18,17 @@
<td class="lines-num lines-num-old">
<div class="tw-flex">
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5)}}
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=down&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=down&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
{{svg "octicon-fold-down"}}
</button>
{{end}}
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4)}}
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=up&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=up&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
{{svg "octicon-fold-up"}}
</button>
{{end}}
{{if eq $line.GetExpandDirection 2}}
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=split&direction=&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
{{svg "octicon-fold"}}
</button>
{{end}}

View File

@ -1,5 +1,4 @@
{{$file := .file}}
{{$blobExcerptRepoLink := or ctx.RootData.CommitRepoLink ctx.RootData.RepoLink}}
<colgroup>
<col width="50">
<col width="50">
@ -15,17 +14,17 @@
<td colspan="2" class="lines-num">
<div class="tw-flex">
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5)}}
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=down&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=down&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
{{svg "octicon-fold-down"}}
</button>
{{end}}
{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 4)}}
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=up&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=up&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
{{svg "octicon-fold-up"}}
</button>
{{end}}
{{if eq $line.GetExpandDirection 2}}
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$blobExcerptRepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
<button class="code-expander-button" hx-target="closest tr" hx-get="{{$.root.RepoLink}}/blob_excerpt/{{PathEscape $.root.AfterCommitID}}?{{$line.GetBlobExcerptQuery}}&style=unified&direction=&wiki={{$.root.PageIsWiki}}&anchor=diff-{{$file.NameHash}}K{{$line.SectionInfo.RightIdx}}">
{{svg "octicon-fold"}}
</button>
{{end}}

View File

@ -23,8 +23,8 @@
<input type="hidden" name="scope" value="{{.Scope}}">
<input type="hidden" name="nonce" value="{{.Nonce}}">
<input type="hidden" name="redirect_uri" value="{{.RedirectURI}}">
<button type="submit" id="authorize-app" name="granted" value="true" class="ui red inline button">{{ctx.Locale.Tr "auth.authorize_application"}}</button>
<button type="submit" name="granted" value="false" class="ui basic primary inline button">{{ctx.Locale.Tr "cancel"}}</button>
<button type="submit" id="authorize-app" value="{{ctx.Locale.Tr "auth.authorize_application"}}" class="ui red inline button">{{ctx.Locale.Tr "auth.authorize_application"}}</button>
<a href="{{.RedirectURI}}" class="ui basic primary inline button">Cancel</a>
</form>
</div>
</div>

View File

@ -15,7 +15,6 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/packages/maven"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@ -242,13 +241,4 @@ func TestPackageMaven(t *testing.T) {
putFile(t, fmt.Sprintf("/%s/maven-metadata.xml", snapshotVersion), "test", http.StatusCreated)
putFile(t, fmt.Sprintf("/%s/maven-metadata.xml", snapshotVersion), "test-overwrite", http.StatusCreated)
})
t.Run("InvalidFile", func(t *testing.T) {
ver := packageVersion + "-invalid"
putFile(t, fmt.Sprintf("/%s/%s", ver, filename), "any invalid content", http.StatusCreated)
req := NewRequestf(t, "GET", "/%s/-/packages/maven/%s-%s/%s", user.Name, groupID, artifactID, ver)
resp := MakeRequest(t, req, http.StatusOK)
assert.Contains(t, resp.Body.String(), "No metadata.")
assert.True(t, test.IsNormalPageCompleted(resp.Body.String()))
})
}

View File

@ -6,14 +6,9 @@ package integration
import (
"fmt"
"net/http"
"net/url"
"strings"
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
repo_service "code.gitea.io/gitea/services/repository"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@ -123,37 +118,3 @@ func TestCompareBranches(t *testing.T) {
inspectCompare(t, htmlDoc, diffCount, diffChanges)
}
func TestCompareCodeExpand(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
repo, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user1, user1, repo_service.CreateRepoOptions{
Name: "test_blob_excerpt",
Readme: "Default",
AutoInit: true,
DefaultBranch: "main",
})
assert.NoError(t, err)
session := loginUser(t, user1.Name)
testEditFile(t, session, user1.Name, repo.Name, "main", "README.md", strings.Repeat("a\n", 30))
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session = loginUser(t, user2.Name)
testRepoFork(t, session, user1.Name, repo.Name, user2.Name, "test_blob_excerpt-fork")
testCreateBranch(t, session, user2.Name, "test_blob_excerpt-fork", "branch/main", "forked-branch", http.StatusSeeOther)
testEditFile(t, session, user2.Name, "test_blob_excerpt-fork", "forked-branch", "README.md", strings.Repeat("a\n", 15)+"CHANGED\n"+strings.Repeat("a\n", 15))
req := NewRequest(t, "GET", "/user1/test_blob_excerpt/compare/main...user2/test_blob_excerpt-fork:forked-branch")
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
els := htmlDoc.Find(`button.code-expander-button[hx-get]`)
// all the links in the comparison should be to the forked repo&branch
assert.NotZero(t, els.Length())
for i := 0; i < els.Length(); i++ {
link := els.Eq(i).AttrOr("hx-get", "")
assert.True(t, strings.HasPrefix(link, "/user2/test_blob_excerpt-fork/blob_excerpt/"))
}
})
}

View File

@ -9,15 +9,13 @@ import (
"testing"
unit_model "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
)
func TestOrgProjectAccess(t *testing.T) {
defer tests.PrepareTestEnv(t)()
disabledRepoUnits := unit_model.DisabledRepoUnitsGet()
unit_model.DisabledRepoUnitsSet(append(slices.Clone(disabledRepoUnits), unit_model.TypeProjects))
defer unit_model.DisabledRepoUnitsSet(disabledRepoUnits)
defer test.MockVariableValue(&unit_model.DisabledRepoUnits, append(slices.Clone(unit_model.DisabledRepoUnits), unit_model.TypeProjects))()
// repo project, 404
req := NewRequest(t, "GET", "/user2/repo1/projects")

View File

@ -14,6 +14,7 @@
width: 320px;
height: calc(100vh - 450px);
min-height: 60vh;
overflow-y: scroll;
flex: 0 0 auto;
overflow: visible;
display: flex;
@ -29,15 +30,17 @@
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5em;
}
.ui.label.project-column-issue-count {
color: inherit;
.project-column-title {
background: none !important;
line-height: 1.25 !important;
cursor: inherit;
}
.project-column-title-label {
flex: 1;
.project-column-title,
.project-column-issue-count {
color: inherit !important;
}
.project-column > .cards {