Skip to content
Merged
29 changes: 29 additions & 0 deletions modules/validation/binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/util"

"gitea.com/go-chi/binding"
"github.com/gobwas/glob"
Expand All @@ -31,6 +32,7 @@ const (
// AddBindingRules adds additional binding rules
func AddBindingRules() {
addGitRefNameBindingRule()
addValidURLListBindingRule()
addValidURLBindingRule()
addValidSiteURLBindingRule()
addGlobPatternRule()
Expand Down Expand Up @@ -58,6 +60,33 @@ func addGitRefNameBindingRule() {
})
}

func addValidURLListBindingRule() {
// URL validation rule
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return strings.HasPrefix(rule, "ValidUrlList")
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if len(str) == 0 {
errs.Add([]string{name}, binding.ERR_URL, "Url")
return false, errs
}

ok := true
urls := util.SplitTrimSpace(str, "\n")
for _, u := range urls {
if !IsValidURL(u) {
ok = false
errs.Add([]string{"RedirectURIs"}, binding.ERR_URL, u)
}
}

return ok, errs
},
})
}

func addValidURLBindingRule() {
// URL validation rule
binding.AddRule(&binding.Rule{
Expand Down
17 changes: 15 additions & 2 deletions routers/web/user/setting/oauth2_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ func (oa *OAuth2CommonHandlers) AddApp(ctx *context.Context) {
return
}

// TODO validate redirect URI
app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{
Name: form.Name,
RedirectURIs: util.SplitTrimSpace(form.RedirectURIs, "\n"),
Expand Down Expand Up @@ -96,11 +95,25 @@ func (oa *OAuth2CommonHandlers) EditSave(ctx *context.Context) {
form := web.GetForm(ctx).(*forms.EditOAuth2ApplicationForm)

if ctx.HasError() {
app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.PathParamInt64("id"))
if err != nil {
if auth.IsErrOAuthApplicationNotFound(err) {
ctx.NotFound("Application not found", err)
return
}
ctx.ServerError("GetOAuth2ApplicationByID", err)
return
}
if app.UID != oa.OwnerID {
ctx.NotFound("Application not found", nil)
return
}
ctx.Data["App"] = app

oa.renderEditPage(ctx)
return
}

// TODO validate redirect URI
var err error
if ctx.Data["App"], err = auth.UpdateOAuth2Application(ctx, auth.UpdateOAuth2ApplicationOptions{
ID: ctx.PathParamInt64("id"),
Expand Down
2 changes: 1 addition & 1 deletion services/forms/user_form.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ func (f *NewAccessTokenForm) GetScope() (auth_model.AccessTokenScope, error) {
// EditOAuth2ApplicationForm form for editing oauth2 applications
type EditOAuth2ApplicationForm struct {
Name string `binding:"Required;MaxSize(255)" form:"application_name"`
RedirectURIs string `binding:"Required" form:"redirect_uris"`
RedirectURIs string `binding:"Required;ValidUrlList" form:"redirect_uris"`
ConfidentialClient bool `form:"confidential_client"`
SkipSecondaryAuthorization bool `form:"skip_secondary_authorization"`
}
Expand Down
117 changes: 93 additions & 24 deletions tests/integration/user_settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/tests"

"github.com/stretchr/testify/assert"
)

// Validate that each navbar setting is correct. This checks that the
Expand Down Expand Up @@ -51,8 +53,10 @@ func WithDisabledFeatures(t *testing.T, features ...string) {
}

func TestUserSettingsAccount(t *testing.T) {
defer tests.PrepareTestEnv(t)()

t.Run("all features enabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/account")
Expand All @@ -68,7 +72,7 @@ func TestUserSettingsAccount(t *testing.T) {
})

t.Run("credentials disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

WithDisabledFeatures(t, setting.UserFeatureManageCredentials)

Expand All @@ -85,7 +89,7 @@ func TestUserSettingsAccount(t *testing.T) {
})

t.Run("deletion disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

WithDisabledFeatures(t, setting.UserFeatureDeletion)

Expand All @@ -102,7 +106,7 @@ func TestUserSettingsAccount(t *testing.T) {
})

t.Run("deletion, credentials and email notifications are disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

mail := setting.Service.EnableNotifyMail
setting.Service.EnableNotifyMail = false
Expand All @@ -119,8 +123,10 @@ func TestUserSettingsAccount(t *testing.T) {
}

func TestUserSettingsUpdatePassword(t *testing.T) {
defer tests.PrepareTestEnv(t)()

t.Run("enabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

session := loginUser(t, "user2")

Expand All @@ -138,7 +144,7 @@ func TestUserSettingsUpdatePassword(t *testing.T) {
})

t.Run("credentials disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

WithDisabledFeatures(t, setting.UserFeatureManageCredentials)

Expand All @@ -156,8 +162,10 @@ func TestUserSettingsUpdatePassword(t *testing.T) {
}

func TestUserSettingsUpdateEmail(t *testing.T) {
defer tests.PrepareTestEnv(t)()

t.Run("credentials disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

WithDisabledFeatures(t, setting.UserFeatureManageCredentials)

Expand All @@ -175,8 +183,10 @@ func TestUserSettingsUpdateEmail(t *testing.T) {
}

func TestUserSettingsDeleteEmail(t *testing.T) {
defer tests.PrepareTestEnv(t)()

t.Run("credentials disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

WithDisabledFeatures(t, setting.UserFeatureManageCredentials)

Expand All @@ -194,8 +204,10 @@ func TestUserSettingsDeleteEmail(t *testing.T) {
}

func TestUserSettingsDelete(t *testing.T) {
defer tests.PrepareTestEnv(t)()

t.Run("deletion disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

WithDisabledFeatures(t, setting.UserFeatureDeletion)

Expand Down Expand Up @@ -224,9 +236,10 @@ func TestUserSettingsAppearance(t *testing.T) {
}

func TestUserSettingsSecurity(t *testing.T) {
t.Run("credentials disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrepareTestEnv(t)()

t.Run("credentials disabled", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()
WithDisabledFeatures(t, setting.UserFeatureManageCredentials)

session := loginUser(t, "user2")
Expand All @@ -240,8 +253,7 @@ func TestUserSettingsSecurity(t *testing.T) {
})

t.Run("mfa disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()

defer tests.PrintCurrentTest(t)()
WithDisabledFeatures(t, setting.UserFeatureManageMFA)

session := loginUser(t, "user2")
Expand All @@ -255,8 +267,7 @@ func TestUserSettingsSecurity(t *testing.T) {
})

t.Run("credentials and mfa disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()

defer tests.PrintCurrentTest(t)()
WithDisabledFeatures(t, setting.UserFeatureManageCredentials, setting.UserFeatureManageMFA)

session := loginUser(t, "user2")
Expand All @@ -268,17 +279,75 @@ func TestUserSettingsSecurity(t *testing.T) {
func TestUserSettingsApplications(t *testing.T) {
defer tests.PrepareTestEnv(t)()

session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/applications")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
t.Run("Applications", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

assertNavbar(t, doc)
session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/applications")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)

assertNavbar(t, doc)
})

t.Run("OAuth2", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

session := loginUser(t, "user2")

t.Run("OAuth2ApplicationShow", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

req := NewRequest(t, "GET", "/user/settings/applications/oauth2/2")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)

assertNavbar(t, doc)
})

t.Run("OAuthApplicationsEdit", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

req := NewRequest(t, "GET", "/user/settings/applications/oauth2/2")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)

t.Run("Invalid URL", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

req := NewRequestWithValues(t, "POST", "/user/settings/applications/oauth2/2", map[string]string{
"_csrf": doc.GetCSRF(),
"application_name": "Test native app",
"redirect_uris": "ftp://127.0.0.1",
"confidential_client": "false",
})
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)

msg := doc.Find(".flash-error p").Text()
assert.Equal(t, `form.RedirectURIs"ftp://127.0.0.1" is not a valid URL.`, msg)
})

t.Run("OK", func(t *testing.T) {
defer tests.PrintCurrentTest(t)()

req := NewRequestWithValues(t, "POST", "/user/settings/applications/oauth2/2", map[string]string{
"_csrf": doc.GetCSRF(),
"application_name": "Test native app",
"redirect_uris": "http://127.0.0.1",
"confidential_client": "false",
})
session.MakeRequest(t, req, http.StatusSeeOther)
})
})
})
}

func TestUserSettingsKeys(t *testing.T) {
defer tests.PrepareTestEnv(t)()

t.Run("all enabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

session := loginUser(t, "user2")
req := NewRequest(t, "GET", "/user/settings/keys")
Expand All @@ -292,7 +361,7 @@ func TestUserSettingsKeys(t *testing.T) {
})

t.Run("ssh keys disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

WithDisabledFeatures(t, setting.UserFeatureManageSSHKeys)

Expand All @@ -308,7 +377,7 @@ func TestUserSettingsKeys(t *testing.T) {
})

t.Run("gpg keys disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

WithDisabledFeatures(t, setting.UserFeatureManageGPGKeys)

Expand All @@ -324,7 +393,7 @@ func TestUserSettingsKeys(t *testing.T) {
})

t.Run("ssh & gpg keys disabled", func(t *testing.T) {
defer tests.PrepareTestEnv(t)()
defer tests.PrintCurrentTest(t)()

WithDisabledFeatures(t, setting.UserFeatureManageSSHKeys, setting.UserFeatureManageGPGKeys)

Expand Down
Loading