mirror of
https://github.com/go-gitea/gitea.git
synced 2026-08-10 11:35:43 +02:00
Added displaying banner for admin user in case of failure during required additional information retrieval
This commit is contained in:
@@ -5,8 +5,14 @@ package oauth2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIsGoogleGroupClaimRequiredForLoginFlow(t *testing.T) {
|
||||
@@ -60,3 +66,45 @@ func TestIsGoogleGroupClaimRequiredForLoginFlow(t *testing.T) {
|
||||
assert.False(t, isGoogleGroupClaimRequiredForLoginFlow(source))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRequiredAdditionalInfoFailureWarningLifecycle(t *testing.T) {
|
||||
c, err := cache.NewStringCache(setting.Cache{Adapter: "memory", Interval: 1})
|
||||
require.NoError(t, err)
|
||||
|
||||
mockNow := time.Unix(1_700_000_000, 0)
|
||||
defer timeutil.MockSet(mockNow)()
|
||||
SetRequiredAdditionalInfoFetchFailureWarning(c, "Google Workspace")
|
||||
|
||||
warning := GetRequiredAdditionalInfoFailureWarning(c)
|
||||
require.NotNil(t, warning)
|
||||
assert.Equal(t, "Google Workspace", warning.SourceName)
|
||||
assert.Equal(t, timeutil.TimeStamp(mockNow.Unix()), warning.LastFailedUnix)
|
||||
|
||||
ClearRequiredAdditionalInfoFetchFailureWarning(c)
|
||||
assert.Nil(t, GetRequiredAdditionalInfoFailureWarning(c))
|
||||
}
|
||||
|
||||
func TestRequiredAdditionalInfoFailureWarningThrottle(t *testing.T) {
|
||||
c, err := cache.NewStringCache(setting.Cache{Adapter: "memory", Interval: 1})
|
||||
require.NoError(t, err)
|
||||
|
||||
first := time.Unix(1_700_000_000, 0)
|
||||
defer timeutil.MockSet(first)()
|
||||
SetRequiredAdditionalInfoFetchFailureWarning(c, "Google Workspace")
|
||||
initial := GetRequiredAdditionalInfoFailureWarning(c)
|
||||
require.NotNil(t, initial)
|
||||
|
||||
// Within throttle window, keep previous timestamp to avoid cache churn.
|
||||
timeutil.MockSet(first.Add(30 * time.Second))
|
||||
SetRequiredAdditionalInfoFetchFailureWarning(c, "Google Workspace")
|
||||
throttled := GetRequiredAdditionalInfoFailureWarning(c)
|
||||
require.NotNil(t, throttled)
|
||||
assert.Equal(t, initial.LastFailedUnix, throttled.LastFailedUnix)
|
||||
|
||||
// After throttle window, timestamp is refreshed.
|
||||
timeutil.MockSet(first.Add(61 * time.Second))
|
||||
SetRequiredAdditionalInfoFetchFailureWarning(c, "Google Workspace")
|
||||
refreshed := GetRequiredAdditionalInfoFailureWarning(c)
|
||||
require.NotNil(t, refreshed)
|
||||
assert.Equal(t, timeutil.TimeStamp(first.Add(61*time.Second).Unix()), refreshed.LastFailedUnix)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2026 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
)
|
||||
|
||||
const (
|
||||
requiredAdditionalInfoFailureWarningCacheKey = "oauth2.required.additional.info.failure.warning"
|
||||
requiredAdditionalInfoFailureWarningTTL = 3600
|
||||
requiredAdditionalInfoFailureWarningThrottle = 60
|
||||
)
|
||||
|
||||
type RequiredAdditionalInfoFailureWarning struct {
|
||||
SourceName string `json:"sourceName"`
|
||||
LastFailedUnix timeutil.TimeStamp `json:"lastFailedUnix"`
|
||||
}
|
||||
|
||||
func GetRequiredAdditionalInfoFailureWarning(c cache.StringCache) *RequiredAdditionalInfoFailureWarning {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
rawWarning, ok := c.Get(requiredAdditionalInfoFailureWarningCacheKey)
|
||||
if !ok || rawWarning == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
warning := &RequiredAdditionalInfoFailureWarning{}
|
||||
if err := json.Unmarshal([]byte(rawWarning), warning); err != nil {
|
||||
_ = c.Delete(requiredAdditionalInfoFailureWarningCacheKey)
|
||||
return nil
|
||||
}
|
||||
if warning.SourceName == "" || warning.LastFailedUnix.IsZero() {
|
||||
_ = c.Delete(requiredAdditionalInfoFailureWarningCacheKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
return warning
|
||||
}
|
||||
|
||||
func SetRequiredAdditionalInfoFetchFailureWarning(c cache.StringCache, sourceName string) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
if sourceName == "" {
|
||||
sourceName = "OAuth2"
|
||||
}
|
||||
|
||||
now := timeutil.TimeStampNow()
|
||||
current := GetRequiredAdditionalInfoFailureWarning(c)
|
||||
if current != nil && current.SourceName == sourceName && now-current.LastFailedUnix < requiredAdditionalInfoFailureWarningThrottle {
|
||||
return
|
||||
}
|
||||
|
||||
rawWarning, err := json.Marshal(&RequiredAdditionalInfoFailureWarning{
|
||||
SourceName: sourceName,
|
||||
LastFailedUnix: now,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err := c.Put(requiredAdditionalInfoFailureWarningCacheKey, string(rawWarning), requiredAdditionalInfoFailureWarningTTL); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func ClearRequiredAdditionalInfoFetchFailureWarning(c cache.StringCache) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
_ = c.Delete(requiredAdditionalInfoFailureWarningCacheKey)
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
oauth2_source "code.gitea.io/gitea/services/auth/source/oauth2"
|
||||
"code.gitea.io/gitea/services/webtheme"
|
||||
)
|
||||
|
||||
@@ -78,6 +79,14 @@ func (c TemplateContext) CurrentWebBanner() *setting.WebBannerType {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c TemplateContext) CurrentRequiredAdditionalInfoFailureWarning() *oauth2_source.RequiredAdditionalInfoFailureWarning {
|
||||
webCtx := GetWebContext(c)
|
||||
if webCtx == nil || webCtx.Doer == nil || !webCtx.Doer.IsAdmin {
|
||||
return nil
|
||||
}
|
||||
return oauth2_source.GetRequiredAdditionalInfoFailureWarning(webCtx.Cache)
|
||||
}
|
||||
|
||||
// AppFullLink returns a full URL link with AppSubURL for the given app link (no AppSubURL)
|
||||
// If no link is given, it returns the current app full URL with sub-path but without trailing slash (that's why it is not named as AppURL)
|
||||
func (c TemplateContext) AppFullLink(link ...string) template.URL {
|
||||
|
||||
Reference in New Issue
Block a user