feat: allow to disable local and OIDC signup separately. Closes #868

This commit is contained in:
Ivan Filipenkov
2025-11-28 22:58:13 +01:00
parent 5080344cea
commit fcaa8a2056
9 changed files with 21 additions and 4 deletions

View File

@@ -188,7 +188,8 @@ You can specify configuration options either via a config file (default: `config
| `security.password_salt` /<br> `WAKAPI_PASSWORD_SALT` | - | Pepper to use for password hashing |
| `security.insecure_cookies` /<br> `WAKAPI_INSECURE_COOKIES` | `true` | Whether or not to allow cookies over HTTP. For production, it is **highly recommended** to serve Wakapi via HTTPS and set this to `false`. |
| `security.cookie_max_age` /<br> `WAKAPI_COOKIE_MAX_AGE` | `172800` | Lifetime of authentication cookies in seconds or `0` to use [Session](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Define_the_lifetime_of_a_cookie) cookies |
| `security.allow_signup` /<br> `WAKAPI_ALLOW_SIGNUP` | `true` | Whether to enable user registration |
| `security.allow_signup` /<br> `WAKAPI_ALLOW_SIGNUP` | `true` | Whether to enable local user registration |
| `security.oidc_allow_signup` /<br> `WAKAPI_OIDC_ALLOW_SIGNUP` | `true` | Whether to enable user registration via OIDC |
| `security.signup_captcha` /<br> `WAKAPI_SIGNUP_CAPTCHA` | `false` | Whether the registration form requires solving a CAPTCHA |
| `security.invite_codes` /<br> `WAKAPI_INVITE_CODES` | `true` | Whether to enable registration by invite codes. Primarily useful if registration is disabled (invite-only server). |
| `security.disable_frontpage` /<br> `WAKAPI_DISABLE_FRONTPAGE` | `false` | Whether to disable landing page (useful for personal instances) |

View File

@@ -86,6 +86,7 @@ security:
insecure_cookies: true # should be set to 'false', except when not running with HTTPS (e.g. on localhost)
cookie_max_age: 172800
allow_signup: true
oidc_allow_signup: true
signup_captcha: false
invite_codes: true # whether to enable invite codes for overriding disabled signups
disable_frontpage: false

View File

@@ -117,6 +117,7 @@ type appConfig struct {
type securityConfig struct {
AllowSignup bool `yaml:"allow_signup" default:"true" env:"WAKAPI_ALLOW_SIGNUP"`
OidcAllowSignup bool `yaml:"oidc_allow_signup" default:"true" env:"WAKAPI_OIDC_ALLOW_SIGNUP"`
SignupCaptcha bool `yaml:"signup_captcha" default:"false" env:"WAKAPI_SIGNUP_CAPTCHA"`
InviteCodes bool `yaml:"invite_codes" default:"true" env:"WAKAPI_INVITE_CODES"`
ExposeMetrics bool `yaml:"expose_metrics" default:"false" env:"WAKAPI_EXPOSE_METRICS"`

View File

@@ -431,7 +431,7 @@ func (h *LoginHandler) GetOidcCallback(w http.ResponseWriter, r *http.Request) {
user, err := h.userSrvc.GetUserByOidc(provider.Name, idTokenPayload.Subject)
if err != nil {
// create new user account
if !h.config.IsDev() && !h.config.Security.AllowSignup {
if !h.config.IsDev() && !h.config.Security.OidcAllowSignup {
routeutils.SetError(r, w, "registration is disabled on this server")
http.Redirect(w, r, fmt.Sprintf("%s/login", h.config.Server.BasePath), http.StatusFound)
return

View File

@@ -209,6 +209,7 @@ func (suite *LoginHandlerTestSuite) TestPostSignup_Success() {
suite.UserService.On("Count", mock.Anything).Return(1, nil)
suite.UserService.On("CreateOrGet", mock.Anything, mock.Anything).Return(&models.User{}, true, nil)
suite.Cfg.Security.AllowSignup = true
suite.Cfg.Security.OidcAllowSignup = false
suite.Sut.PostSignup(w, r)
@@ -236,6 +237,7 @@ func (suite *LoginHandlerTestSuite) TestPostSignup_InvalidForm() {
suite.UserService.On("Count", mock.Anything).Return(1, nil)
suite.Cfg.Security.AllowSignup = true
suite.Cfg.Security.OidcAllowSignup = false
suite.Sut.PostSignup(w, r)
body, _ := io.ReadAll(w.Body)
@@ -258,6 +260,7 @@ func (suite *LoginHandlerTestSuite) TestPostSignup_ExistingUser() {
suite.UserService.On("Count", mock.Anything).Return(1, nil)
suite.UserService.On("CreateOrGet", mock.Anything, mock.Anything).Return(suite.TestUser, false, nil)
suite.Cfg.Security.AllowSignup = true
suite.Cfg.Security.OidcAllowSignup = false
suite.Sut.PostSignup(w, r)
body, _ := io.ReadAll(w.Body)
@@ -268,6 +271,9 @@ func (suite *LoginHandlerTestSuite) TestPostSignup_ExistingUser() {
}
func (suite *LoginHandlerTestSuite) TestPostSignup_SignupDisabled() {
suite.Cfg.Security.AllowSignup = false
suite.Cfg.Security.OidcAllowSignup = true
form := url.Values{}
form.Add("username", testUserNewId)
form.Add("password", testUserNewPassword)
@@ -331,7 +337,8 @@ func (suite *LoginHandlerTestSuite) TestGetOidcLoginCallback_Success() {
}
func (suite *LoginHandlerTestSuite) TestGetOidcLoginCallback_Success_CreateUser() {
suite.Cfg.Security.AllowSignup = true
suite.Cfg.Security.AllowSignup = false
suite.Cfg.Security.OidcAllowSignup = true
url := suite.authorizeUser(suite.OidcUserNew)
r := httptest.NewRequest(http.MethodGet, url, nil)
@@ -363,6 +370,9 @@ func (suite *LoginHandlerTestSuite) TestGetOidcLoginCallback_Success_CreateUser(
}
func (suite *LoginHandlerTestSuite) TestGetOidcLoginCallback_SignupDisabled() {
suite.Cfg.Security.AllowSignup = true
suite.Cfg.Security.OidcAllowSignup = false
url := suite.authorizeUser(suite.OidcUserNew)
r := httptest.NewRequest(http.MethodGet, url, nil)
r = WithUrlParam(r, "provider", testProvider)

View File

@@ -36,6 +36,7 @@ security:
insecure_cookies: true
cookie_max_age: 172800
allow_signup: true
oidc_allow_signup: true
expose_metrics: true
signup_max_rate: 999/1s
login_max_rate: 999/1s

View File

@@ -35,6 +35,7 @@ security:
insecure_cookies: true
cookie_max_age: 172800
allow_signup: true
oidc_allow_signup: true
expose_metrics: true
signup_max_rate: 999/1s
login_max_rate: 999/1s

View File

@@ -36,6 +36,7 @@ security:
insecure_cookies: true
cookie_max_age: 172800
allow_signup: true
oidc_allow_signup: true
expose_metrics: true
signup_max_rate: 999/1s
login_max_rate: 999/1s

View File

@@ -36,6 +36,7 @@ security:
insecure_cookies: true
cookie_max_age: 172800
allow_signup: true
oidc_allow_signup: true
expose_metrics: true
signup_max_rate: 999/1s
login_max_rate: 999/1s