Do not require login_name & source_id for /admin/user/{username}
When editing a user via the API, do not require setting `login_name` or `source_id`: for local accounts, these do not matter. However, when editing a non-local account, require *both*, as before. Fixes #1861. Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
This commit is contained in:
parent
787bc6ed94
commit
d07c8c821c
|
@ -30,10 +30,8 @@ type CreateUserOption struct {
|
||||||
|
|
||||||
// EditUserOption edit user options
|
// EditUserOption edit user options
|
||||||
type EditUserOption struct {
|
type EditUserOption struct {
|
||||||
// required: true
|
SourceID *int64 `json:"source_id"`
|
||||||
SourceID int64 `json:"source_id"`
|
LoginName *string `json:"login_name"`
|
||||||
// required: true
|
|
||||||
LoginName string `json:"login_name" binding:"Required"`
|
|
||||||
// swagger:strfmt email
|
// swagger:strfmt email
|
||||||
Email *string `json:"email" binding:"MaxSize(254)"`
|
Email *string `json:"email" binding:"MaxSize(254)"`
|
||||||
FullName *string `json:"full_name" binding:"MaxSize(100)"`
|
FullName *string `json:"full_name" binding:"MaxSize(100)"`
|
||||||
|
|
|
@ -192,9 +192,17 @@ func EditUser(ctx *context.APIContext) {
|
||||||
|
|
||||||
form := web.GetForm(ctx).(*api.EditUserOption)
|
form := web.GetForm(ctx).(*api.EditUserOption)
|
||||||
|
|
||||||
|
// If either LoginSource or LoginName is given, the other must be present too.
|
||||||
|
if form.SourceID != nil || form.LoginName != nil {
|
||||||
|
if form.SourceID == nil || form.LoginName == nil {
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, "LoginSourceAndLoginName", fmt.Errorf("source_id and login_name must be specified together"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
authOpts := &user_service.UpdateAuthOptions{
|
authOpts := &user_service.UpdateAuthOptions{
|
||||||
LoginSource: optional.FromNonDefault(form.SourceID),
|
LoginSource: optional.FromPtr(form.SourceID),
|
||||||
LoginName: optional.Some(form.LoginName),
|
LoginName: optional.FromPtr(form.LoginName),
|
||||||
Password: optional.FromNonDefault(form.Password),
|
Password: optional.FromNonDefault(form.Password),
|
||||||
MustChangePassword: optional.FromPtr(form.MustChangePassword),
|
MustChangePassword: optional.FromPtr(form.MustChangePassword),
|
||||||
ProhibitLogin: optional.FromPtr(form.ProhibitLogin),
|
ProhibitLogin: optional.FromPtr(form.ProhibitLogin),
|
||||||
|
|
4
templates/swagger/v1_json.tmpl
generated
4
templates/swagger/v1_json.tmpl
generated
|
@ -20984,10 +20984,6 @@
|
||||||
"EditUserOption": {
|
"EditUserOption": {
|
||||||
"description": "EditUserOption edit user options",
|
"description": "EditUserOption edit user options",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
|
||||||
"source_id",
|
|
||||||
"login_name"
|
|
||||||
],
|
|
||||||
"properties": {
|
"properties": {
|
||||||
"active": {
|
"active": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|
|
@ -196,19 +196,13 @@ func TestAPIEditUser(t *testing.T) {
|
||||||
urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2")
|
urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2")
|
||||||
|
|
||||||
req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{
|
req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{
|
||||||
// required
|
|
||||||
"login_name": "user2",
|
|
||||||
"source_id": "0",
|
|
||||||
// to change
|
|
||||||
"full_name": "Full Name User 2",
|
"full_name": "Full Name User 2",
|
||||||
}).AddTokenAuth(token)
|
}).AddTokenAuth(token)
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
empty := ""
|
empty := ""
|
||||||
req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
|
req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
|
||||||
LoginName: "user2",
|
Email: &empty,
|
||||||
SourceID: 0,
|
|
||||||
Email: &empty,
|
|
||||||
}).AddTokenAuth(token)
|
}).AddTokenAuth(token)
|
||||||
resp := MakeRequest(t, req, http.StatusBadRequest)
|
resp := MakeRequest(t, req, http.StatusBadRequest)
|
||||||
|
|
||||||
|
@ -220,10 +214,6 @@ func TestAPIEditUser(t *testing.T) {
|
||||||
assert.False(t, user2.IsRestricted)
|
assert.False(t, user2.IsRestricted)
|
||||||
bTrue := true
|
bTrue := true
|
||||||
req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
|
req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
|
||||||
// required
|
|
||||||
LoginName: "user2",
|
|
||||||
SourceID: 0,
|
|
||||||
// to change
|
|
||||||
Restricted: &bTrue,
|
Restricted: &bTrue,
|
||||||
}).AddTokenAuth(token)
|
}).AddTokenAuth(token)
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
|
@ -231,6 +221,45 @@ func TestAPIEditUser(t *testing.T) {
|
||||||
assert.True(t, user2.IsRestricted)
|
assert.True(t, user2.IsRestricted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPIEditUserWithLoginName(t *testing.T) {
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
adminUsername := "user1"
|
||||||
|
token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin)
|
||||||
|
urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2")
|
||||||
|
|
||||||
|
loginName := "user2"
|
||||||
|
loginSource := int64(0)
|
||||||
|
|
||||||
|
t.Run("login_name only", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
|
||||||
|
LoginName: &loginName,
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
MakeRequest(t, req, http.StatusUnprocessableEntity)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("source_id only", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
|
||||||
|
SourceID: &loginSource,
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
MakeRequest(t, req, http.StatusUnprocessableEntity)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("login_name & source_id", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
|
||||||
|
LoginName: &loginName,
|
||||||
|
SourceID: &loginSource,
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
MakeRequest(t, req, http.StatusOK)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestAPICreateRepoForUser(t *testing.T) {
|
func TestAPICreateRepoForUser(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
defer tests.PrepareTestEnv(t)()
|
||||||
adminUsername := "user1"
|
adminUsername := "user1"
|
||||||
|
@ -375,18 +404,14 @@ func TestAPIEditUser_NotAllowedEmailDomain(t *testing.T) {
|
||||||
|
|
||||||
newEmail := "user2@example1.com"
|
newEmail := "user2@example1.com"
|
||||||
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
|
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
|
||||||
LoginName: "user2",
|
Email: &newEmail,
|
||||||
SourceID: 0,
|
|
||||||
Email: &newEmail,
|
|
||||||
}).AddTokenAuth(token)
|
}).AddTokenAuth(token)
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
assert.Equal(t, "the domain of user email user2@example1.com conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", resp.Header().Get("X-Gitea-Warning"))
|
assert.Equal(t, "the domain of user email user2@example1.com conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", resp.Header().Get("X-Gitea-Warning"))
|
||||||
|
|
||||||
originalEmail := "user2@example.com"
|
originalEmail := "user2@example.com"
|
||||||
req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
|
req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{
|
||||||
LoginName: "user2",
|
Email: &originalEmail,
|
||||||
SourceID: 0,
|
|
||||||
Email: &originalEmail,
|
|
||||||
}).AddTokenAuth(token)
|
}).AddTokenAuth(token)
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
|
@ -495,9 +495,7 @@ func TestUserPronouns(t *testing.T) {
|
||||||
// Set the pronouns for user2
|
// Set the pronouns for user2
|
||||||
pronouns := "she/her"
|
pronouns := "she/her"
|
||||||
req := NewRequestWithJSON(t, "PATCH", "/api/v1/admin/users/user2", &api.EditUserOption{
|
req := NewRequestWithJSON(t, "PATCH", "/api/v1/admin/users/user2", &api.EditUserOption{
|
||||||
LoginName: "user2",
|
Pronouns: &pronouns,
|
||||||
SourceID: 0,
|
|
||||||
Pronouns: &pronouns,
|
|
||||||
}).AddTokenAuth(adminToken)
|
}).AddTokenAuth(adminToken)
|
||||||
resp := MakeRequest(t, req, http.StatusOK)
|
resp := MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
@ -596,9 +594,7 @@ func TestUserPronouns(t *testing.T) {
|
||||||
// Set the pronouns to Unspecified (an empty string) via the API
|
// Set the pronouns to Unspecified (an empty string) via the API
|
||||||
pronouns := ""
|
pronouns := ""
|
||||||
req := NewRequestWithJSON(t, "PATCH", "/api/v1/admin/users/user2", &api.EditUserOption{
|
req := NewRequestWithJSON(t, "PATCH", "/api/v1/admin/users/user2", &api.EditUserOption{
|
||||||
LoginName: "user2",
|
Pronouns: &pronouns,
|
||||||
SourceID: 0,
|
|
||||||
Pronouns: &pronouns,
|
|
||||||
}).AddTokenAuth(adminToken)
|
}).AddTokenAuth(adminToken)
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue