fix: single source of truth for local-login lockout rescue
HandleLocalLogin and HandleListPublicProviders each computed their own answer to whether password sign-in must stay available, and they could disagree: an instance with local login off and a licence that lapses loses its only provider and its password form in the same moment, with no endpoint left to recover. services.LocalLoginPermitted is now the one predicate both call.
This commit is contained in:
@@ -67,7 +67,7 @@ func HandleLocalLogin(c *gin.Context) {
|
||||
}
|
||||
// The login page hides the form, but the page is a courtesy and the API is
|
||||
// the boundary.
|
||||
if !services.IsLocalLoginEnabled(instanceID) {
|
||||
if !services.LocalLoginPermitted(instanceID) {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "password sign-in is disabled for this instance"})
|
||||
return
|
||||
}
|
||||
@@ -118,13 +118,7 @@ func HandleListPublicProviders(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
localEnabled := services.IsLocalLoginEnabled(instanceID)
|
||||
// Belt and braces against a hand-edited database: a login page with neither
|
||||
// a form nor a button is unrecoverable without database access.
|
||||
if !localEnabled && len(out) == 0 {
|
||||
localEnabled = true
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"local_enabled": localEnabled, "providers": out})
|
||||
c.JSON(http.StatusOK, gin.H{"local_enabled": services.LocalLoginPermitted(instanceID), "providers": out})
|
||||
}
|
||||
|
||||
func HandleBootstrapStatus(c *gin.Context) {
|
||||
|
||||
@@ -242,6 +242,31 @@ func IsLocalLoginEnabled(instanceID string) bool {
|
||||
return shared.LocalLoginEnabled(s)
|
||||
}
|
||||
|
||||
// LocalLoginPermitted answers whether password sign-in must be accepted for
|
||||
// this instance, which is not the same question as whether an administrator
|
||||
// turned it on. An instance whose only providers have become unusable — a
|
||||
// lapsed licence, or every provider disabled — has to keep its password form,
|
||||
// or nobody can sign in and there is no endpoint left to fix it with.
|
||||
func LocalLoginPermitted(instanceID string) bool {
|
||||
if IsLocalLoginEnabled(instanceID) {
|
||||
return true
|
||||
}
|
||||
return CountUsableAuthProviders(instanceID) == 0
|
||||
}
|
||||
|
||||
// CountUsableAuthProviders counts providers that could actually complete a
|
||||
// sign-in right now: enabled, and permitted by the licence.
|
||||
func CountUsableAuthProviders(instanceID string) int {
|
||||
if !GetLicenseState(instanceID).Feature("oidc") {
|
||||
return 0
|
||||
}
|
||||
n, err := CountEnabledAuthProviders(instanceID)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// randomProviderID is 8 bytes hex: short enough to read in a URL, wide enough
|
||||
// that guessing one is not a way to enumerate an instance's providers.
|
||||
func randomProviderID() (string, error) {
|
||||
|
||||
Reference in New Issue
Block a user