fix(auth): preserve backward-compatible oauth defaults
This commit is contained in:
@@ -631,7 +631,7 @@ func (s *SettingService) weChatOAuthCapabilitiesFromSettings(settings map[string
|
||||
mpReady := mpEnabled && webRedirectReady && mpAppID != "" && mpAppSecret != ""
|
||||
mobileReady := mobileEnabled && mobileAppID != "" && mobileAppSecret != ""
|
||||
|
||||
return openReady || mpReady || mobileReady, openReady, mpReady, mobileReady
|
||||
return openReady || mpReady, openReady, mpReady, mobileReady
|
||||
}
|
||||
|
||||
// filterUserVisibleMenuItems filters out admin-only menu items from a raw JSON
|
||||
@@ -1693,8 +1693,6 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
|
||||
} else {
|
||||
result.OIDCConnectValidateIDToken = oidcBase.ValidateIDToken
|
||||
}
|
||||
result.OIDCConnectUsePKCE = true
|
||||
result.OIDCConnectValidateIDToken = true
|
||||
if v, ok := settings[SettingKeyOIDCConnectAllowedSigningAlgs]; ok && strings.TrimSpace(v) != "" {
|
||||
result.OIDCConnectAllowedSigningAlgs = strings.TrimSpace(v)
|
||||
} else {
|
||||
@@ -2196,8 +2194,6 @@ func (s *SettingService) GetLinuxDoConnectOAuthConfig(ctx context.Context) (conf
|
||||
if v, ok := settings[SettingKeyLinuxDoConnectRedirectURL]; ok && strings.TrimSpace(v) != "" {
|
||||
effective.RedirectURL = strings.TrimSpace(v)
|
||||
}
|
||||
effective.UsePKCE = true
|
||||
|
||||
if !effective.Enabled {
|
||||
return config.LinuxDoConnectConfig{}, infraerrors.NotFound("OAUTH_DISABLED", "oauth login is disabled")
|
||||
}
|
||||
@@ -2421,8 +2417,6 @@ func (s *SettingService) GetOIDCConnectOAuthConfig(ctx context.Context) (config.
|
||||
if raw, ok := settings[SettingKeyOIDCConnectValidateIDToken]; ok {
|
||||
effective.ValidateIDToken = raw == "true"
|
||||
}
|
||||
effective.UsePKCE = true
|
||||
effective.ValidateIDToken = true
|
||||
if v, ok := settings[SettingKeyOIDCConnectAllowedSigningAlgs]; ok && strings.TrimSpace(v) != "" {
|
||||
effective.AllowedSigningAlgs = strings.TrimSpace(v)
|
||||
}
|
||||
|
||||
@@ -101,3 +101,47 @@ func TestGetOIDCConnectOAuthConfig_ResolvesEndpointsFromIssuerDiscovery(t *testi
|
||||
require.Equal(t, srv.URL+"/issuer/protocol/openid-connect/userinfo", got.UserInfoURL)
|
||||
require.Equal(t, srv.URL+"/issuer/protocol/openid-connect/certs", got.JWKSURL)
|
||||
}
|
||||
|
||||
func TestSettingService_ParseSettings_PreservesOptionalOIDCCompatibilityFlags(t *testing.T) {
|
||||
svc := NewSettingService(&settingOIDCRepoStub{values: map[string]string{}}, &config.Config{})
|
||||
|
||||
got := svc.parseSettings(map[string]string{
|
||||
SettingKeyOIDCConnectEnabled: "true",
|
||||
SettingKeyOIDCConnectUsePKCE: "false",
|
||||
SettingKeyOIDCConnectValidateIDToken: "false",
|
||||
})
|
||||
|
||||
require.False(t, got.OIDCConnectUsePKCE)
|
||||
require.False(t, got.OIDCConnectValidateIDToken)
|
||||
}
|
||||
|
||||
func TestGetOIDCConnectOAuthConfig_AllowsCompatibilityFlagsToDisablePKCEAndIDTokenValidation(t *testing.T) {
|
||||
cfg := &config.Config{
|
||||
OIDC: config.OIDCConnectConfig{
|
||||
Enabled: true,
|
||||
ProviderName: "OIDC",
|
||||
ClientID: "oidc-client",
|
||||
ClientSecret: "oidc-secret",
|
||||
IssuerURL: "https://issuer.example.com",
|
||||
AuthorizeURL: "https://issuer.example.com/auth",
|
||||
TokenURL: "https://issuer.example.com/token",
|
||||
UserInfoURL: "https://issuer.example.com/userinfo",
|
||||
RedirectURL: "https://example.com/api/v1/auth/oauth/oidc/callback",
|
||||
FrontendRedirectURL: "/auth/oidc/callback",
|
||||
Scopes: "openid email profile",
|
||||
TokenAuthMethod: "client_secret_post",
|
||||
},
|
||||
}
|
||||
|
||||
repo := &settingOIDCRepoStub{values: map[string]string{
|
||||
SettingKeyOIDCConnectEnabled: "true",
|
||||
SettingKeyOIDCConnectUsePKCE: "false",
|
||||
SettingKeyOIDCConnectValidateIDToken: "false",
|
||||
}}
|
||||
svc := NewSettingService(repo, cfg)
|
||||
|
||||
got, err := svc.GetOIDCConnectOAuthConfig(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.False(t, got.UsePKCE)
|
||||
require.False(t, got.ValidateIDToken)
|
||||
}
|
||||
|
||||
@@ -112,3 +112,23 @@ func TestSettingService_GetPublicSettings_ExposesWeChatOAuthModeCapabilities(t *
|
||||
require.True(t, settings.WeChatOAuthOpenEnabled)
|
||||
require.True(t, settings.WeChatOAuthMPEnabled)
|
||||
}
|
||||
|
||||
func TestSettingService_GetPublicSettings_DoesNotExposeMobileOnlyWeChatAsWebOAuthAvailable(t *testing.T) {
|
||||
svc := NewSettingService(&settingPublicRepoStub{
|
||||
values: map[string]string{
|
||||
SettingKeyWeChatConnectEnabled: "true",
|
||||
SettingKeyWeChatConnectMobileEnabled: "true",
|
||||
SettingKeyWeChatConnectMode: "mobile",
|
||||
SettingKeyWeChatConnectMobileAppID: "wx-mobile-app",
|
||||
SettingKeyWeChatConnectMobileAppSecret: "wx-mobile-secret",
|
||||
SettingKeyWeChatConnectFrontendRedirectURL: "/auth/wechat/callback",
|
||||
},
|
||||
}, &config.Config{})
|
||||
|
||||
settings, err := svc.GetPublicSettings(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.False(t, settings.WeChatOAuthEnabled)
|
||||
require.False(t, settings.WeChatOAuthOpenEnabled)
|
||||
require.False(t, settings.WeChatOAuthMPEnabled)
|
||||
require.True(t, settings.WeChatOAuthMobileEnabled)
|
||||
}
|
||||
|
||||
@@ -248,12 +248,59 @@ func (s *UserService) GetProfileIdentitySummaries(ctx context.Context, userID in
|
||||
return UserIdentitySummarySet{}, err
|
||||
}
|
||||
|
||||
return UserIdentitySummarySet{
|
||||
summaries := UserIdentitySummarySet{
|
||||
Email: s.buildEmailIdentitySummary(user, records),
|
||||
LinuxDo: s.buildProviderIdentitySummary("linuxdo", user, records),
|
||||
OIDC: s.buildProviderIdentitySummary("oidc", user, records),
|
||||
WeChat: s.buildProviderIdentitySummary("wechat", user, records),
|
||||
}, nil
|
||||
}
|
||||
|
||||
s.applyExplicitProviderAvailability(ctx, &summaries)
|
||||
return summaries, nil
|
||||
}
|
||||
|
||||
func (s *UserService) applyExplicitProviderAvailability(ctx context.Context, summaries *UserIdentitySummarySet) {
|
||||
if s == nil || summaries == nil || s.settingRepo == nil {
|
||||
return
|
||||
}
|
||||
|
||||
settings, err := s.settingRepo.GetMultiple(ctx, []string{
|
||||
SettingKeyLinuxDoConnectEnabled,
|
||||
SettingKeyOIDCConnectEnabled,
|
||||
SettingKeyWeChatConnectEnabled,
|
||||
SettingKeyWeChatConnectOpenEnabled,
|
||||
SettingKeyWeChatConnectMPEnabled,
|
||||
SettingKeyWeChatConnectMobileEnabled,
|
||||
SettingKeyWeChatConnectMode,
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if raw, ok := settings[SettingKeyLinuxDoConnectEnabled]; ok && strings.TrimSpace(raw) != "" && raw != "true" {
|
||||
disableIdentityBindAction(&summaries.LinuxDo)
|
||||
}
|
||||
if raw, ok := settings[SettingKeyOIDCConnectEnabled]; ok && strings.TrimSpace(raw) != "" && raw != "true" {
|
||||
disableIdentityBindAction(&summaries.OIDC)
|
||||
}
|
||||
if raw, ok := settings[SettingKeyWeChatConnectEnabled]; ok && strings.TrimSpace(raw) != "" {
|
||||
if raw != "true" {
|
||||
disableIdentityBindAction(&summaries.WeChat)
|
||||
return
|
||||
}
|
||||
openEnabled, mpEnabled, _ := parseWeChatConnectCapabilitySettings(settings, true, settings[SettingKeyWeChatConnectMode])
|
||||
if !openEnabled && !mpEnabled {
|
||||
disableIdentityBindAction(&summaries.WeChat)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func disableIdentityBindAction(summary *UserIdentitySummary) {
|
||||
if summary == nil || summary.Bound {
|
||||
return
|
||||
}
|
||||
summary.CanBind = false
|
||||
summary.BindStartPath = ""
|
||||
}
|
||||
|
||||
func (s *UserService) PrepareIdentityBindingStart(_ context.Context, req StartUserIdentityBindingRequest) (*StartUserIdentityBindingResult, error) {
|
||||
|
||||
@@ -51,6 +51,44 @@ type mockUserRepoTxState struct {
|
||||
deleteAvatarIDs []int64
|
||||
}
|
||||
|
||||
type mockUserSettingRepo struct {
|
||||
values map[string]string
|
||||
}
|
||||
|
||||
func (m *mockUserSettingRepo) Get(context.Context, string) (*Setting, error) {
|
||||
panic("unexpected Get call")
|
||||
}
|
||||
|
||||
func (m *mockUserSettingRepo) GetValue(context.Context, string) (string, error) {
|
||||
panic("unexpected GetValue call")
|
||||
}
|
||||
|
||||
func (m *mockUserSettingRepo) Set(context.Context, string, string) error {
|
||||
panic("unexpected Set call")
|
||||
}
|
||||
|
||||
func (m *mockUserSettingRepo) GetMultiple(_ context.Context, keys []string) (map[string]string, error) {
|
||||
out := make(map[string]string, len(keys))
|
||||
for _, key := range keys {
|
||||
if value, ok := m.values[key]; ok {
|
||||
out[key] = value
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (m *mockUserSettingRepo) SetMultiple(context.Context, map[string]string) error {
|
||||
panic("unexpected SetMultiple call")
|
||||
}
|
||||
|
||||
func (m *mockUserSettingRepo) GetAll(context.Context) (map[string]string, error) {
|
||||
panic("unexpected GetAll call")
|
||||
}
|
||||
|
||||
func (m *mockUserSettingRepo) Delete(context.Context, string) error {
|
||||
panic("unexpected Delete call")
|
||||
}
|
||||
|
||||
func (m *mockUserRepo) Create(context.Context, *User) error { return nil }
|
||||
func (m *mockUserRepo) GetByID(ctx context.Context, _ int64) (*User, error) {
|
||||
if m.getByIDErr != nil {
|
||||
@@ -382,6 +420,35 @@ func TestUnbindUserAuthProviderRemovesProviderAndReturnsUpdatedProfile(t *testin
|
||||
require.True(t, summaries.LinuxDo.CanBind)
|
||||
}
|
||||
|
||||
func TestGetProfileIdentitySummaries_HidesBindActionWhenProviderExplicitlyDisabled(t *testing.T) {
|
||||
repo := &mockUserRepo{
|
||||
getByIDUser: &User{
|
||||
ID: 15,
|
||||
Email: "alice@example.com",
|
||||
},
|
||||
identities: []UserAuthIdentityRecord{
|
||||
{
|
||||
ProviderType: "email",
|
||||
ProviderKey: "email",
|
||||
ProviderSubject: "alice@example.com",
|
||||
},
|
||||
},
|
||||
}
|
||||
settingRepo := &mockUserSettingRepo{
|
||||
values: map[string]string{
|
||||
SettingKeyLinuxDoConnectEnabled: "false",
|
||||
},
|
||||
}
|
||||
svc := NewUserService(repo, settingRepo, nil, nil)
|
||||
|
||||
summaries, err := svc.GetProfileIdentitySummaries(context.Background(), 15, repo.getByIDUser)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.False(t, summaries.LinuxDo.Bound)
|
||||
require.False(t, summaries.LinuxDo.CanBind)
|
||||
require.Empty(t, summaries.LinuxDo.BindStartPath)
|
||||
}
|
||||
|
||||
func TestUpdateBalance_NilBillingCache_NoPanic(t *testing.T) {
|
||||
repo := &mockUserRepo{}
|
||||
svc := NewUserService(repo, nil, nil, nil) // billingCache = nil
|
||||
|
||||
Reference in New Issue
Block a user