Skip to content

Commit c620eb5

Browse files
6543lafriks
authored andcommitted
Fix #9189 - API Allow only specific Colums to be updated on Issue (#9539)
* dont insert "-1" in any case to issue.poster_id * Make sure API cant override importand fields * code format * fix lint * WIP test * add missing poster_id * fix test * user.IsGhost handle nil * CI.restart() * make sure no -1 is realy added * CI.restart() * @lunny suggestion remove some not allowed fields * seperate issue.LoadMilestone * load milestone and return it on IssueEdit via API * extend Test for TestAPIEditIssue * fix fixtures * declare allowedColumnsUpdateIssueByAPI only once * Update Year * no var just write id drecty into func cal Co-authored-by: Lauris BH <[email protected]>
1 parent d1798f7 commit c620eb5

File tree

8 files changed

+127
-29
lines changed

8 files changed

+127
-29
lines changed

integrations/api_issue_test.go

+58
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,61 @@ func TestAPICreateIssue(t *testing.T) {
6262
Title: title,
6363
})
6464
}
65+
66+
func TestAPIEditIssue(t *testing.T) {
67+
defer prepareTestEnv(t)()
68+
69+
issueBefore := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
70+
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
71+
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
72+
assert.NoError(t, issueBefore.LoadAttributes())
73+
assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
74+
assert.Equal(t, api.StateOpen, issueBefore.State())
75+
76+
session := loginUser(t, owner.Name)
77+
token := getTokenForLoggedInUser(t, session)
78+
79+
// update values of issue
80+
issueState := "closed"
81+
removeDeadline := true
82+
milestone := int64(4)
83+
body := "new content!"
84+
title := "new title from api set"
85+
86+
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repo.Name, issueBefore.Index, token)
87+
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
88+
State: &issueState,
89+
RemoveDeadline: &removeDeadline,
90+
Milestone: &milestone,
91+
Body: &body,
92+
Title: title,
93+
94+
// ToDo change more
95+
})
96+
resp := session.MakeRequest(t, req, http.StatusCreated)
97+
var apiIssue api.Issue
98+
DecodeJSON(t, resp, &apiIssue)
99+
100+
issueAfter := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue)
101+
102+
// check deleted user
103+
assert.Equal(t, int64(500), issueAfter.PosterID)
104+
assert.NoError(t, issueAfter.LoadAttributes())
105+
assert.Equal(t, int64(-1), issueAfter.PosterID)
106+
assert.Equal(t, int64(-1), issueBefore.PosterID)
107+
assert.Equal(t, int64(-1), apiIssue.Poster.ID)
108+
109+
// API response
110+
assert.Equal(t, api.StateClosed, apiIssue.State)
111+
assert.Equal(t, milestone, apiIssue.Milestone.ID)
112+
assert.Equal(t, body, apiIssue.Body)
113+
assert.True(t, apiIssue.Deadline == nil)
114+
assert.Equal(t, title, apiIssue.Title)
115+
116+
// in database
117+
assert.Equal(t, api.StateClosed, issueAfter.State())
118+
assert.Equal(t, milestone, issueAfter.MilestoneID)
119+
assert.Equal(t, int64(0), int64(issueAfter.DeadlineUnix))
120+
assert.Equal(t, body, issueAfter.Content)
121+
assert.Equal(t, title, issueAfter.Title)
122+
}

models/fixtures/issue.yml

+14-1
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,17 @@
108108
is_closed: false
109109
is_pull: true
110110
created_unix: 946684820
111-
updated_unix: 978307180
111+
updated_unix: 978307180
112+
113+
-
114+
id: 10
115+
repo_id: 42
116+
index: 1
117+
poster_id: 500
118+
name: issue from deleted account
119+
content: content from deleted account
120+
is_closed: false
121+
is_pull: false
122+
created_unix: 946684830
123+
updated_unix: 999307200
124+
deadline_unix: 1019307200

models/fixtures/milestone.yml

+8
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,11 @@
2121
content: content3
2222
is_closed: true
2323
num_issues: 0
24+
25+
-
26+
id: 4
27+
repo_id: 42
28+
name: milestone of repo42
29+
content: content random
30+
is_closed: false
31+
num_issues: 0

models/fixtures/repository.yml

+5-4
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,8 @@
547547
is_private: false
548548
num_stars: 0
549549
num_forks: 0
550-
num_issues: 0
550+
num_issues: 1
551+
num_milestones: 1
551552
is_mirror: false
552553

553554
-
@@ -588,7 +589,7 @@
588589
is_mirror: false
589590
status: 0
590591

591-
-
592+
-
592593
id: 46
593594
owner_id: 26
594595
lower_name: repo_external_tracker
@@ -600,7 +601,7 @@
600601
is_mirror: false
601602
status: 0
602603

603-
-
604+
-
604605
id: 47
605606
owner_id: 26
606607
lower_name: repo_external_tracker_numeric
@@ -612,7 +613,7 @@
612613
is_mirror: false
613614
status: 0
614615

615-
-
616+
-
616617
id: 48
617618
owner_id: 26
618619
lower_name: repo_external_tracker_alpha

models/issue.go

+25-19
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright 2014 The Gogs Authors. All rights reserved.
2+
// Copyright 2020 The Gitea Authors. All rights reserved.
23
// Use of this source code is governed by a MIT-style
34
// license that can be found in the LICENSE file.
45

@@ -239,6 +240,16 @@ func (issue *Issue) loadReactions(e Engine) (err error) {
239240
return nil
240241
}
241242

243+
func (issue *Issue) loadMilestone(e Engine) (err error) {
244+
if issue.Milestone == nil && issue.MilestoneID > 0 {
245+
issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
246+
if err != nil && !IsErrMilestoneNotExist(err) {
247+
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
248+
}
249+
}
250+
return nil
251+
}
252+
242253
func (issue *Issue) loadAttributes(e Engine) (err error) {
243254
if err = issue.loadRepo(e); err != nil {
244255
return
@@ -252,11 +263,8 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
252263
return
253264
}
254265

255-
if issue.Milestone == nil && issue.MilestoneID > 0 {
256-
issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
257-
if err != nil && !IsErrMilestoneNotExist(err) {
258-
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
259-
}
266+
if err = issue.loadMilestone(e); err != nil {
267+
return
260268
}
261269

262270
if err = issue.loadAssignees(e); err != nil {
@@ -296,6 +304,11 @@ func (issue *Issue) LoadAttributes() error {
296304
return issue.loadAttributes(x)
297305
}
298306

307+
// LoadMilestone load milestone of this issue.
308+
func (issue *Issue) LoadMilestone() error {
309+
return issue.loadMilestone(x)
310+
}
311+
299312
// GetIsRead load the `IsRead` field of the issue
300313
func (issue *Issue) GetIsRead(userID int64) error {
301314
issueUser := &IssueUser{IssueID: issue.ID, UID: userID}
@@ -1568,25 +1581,18 @@ func SearchIssueIDsByKeyword(kw string, repoIDs []int64, limit, start int) (int6
15681581
return total, ids, nil
15691582
}
15701583

1571-
func updateIssue(e Engine, issue *Issue) error {
1572-
_, err := e.ID(issue.ID).AllCols().Update(issue)
1573-
if err != nil {
1574-
return err
1575-
}
1576-
return nil
1577-
}
1578-
1579-
// UpdateIssue updates all fields of given issue.
1580-
func UpdateIssue(issue *Issue) error {
1584+
// UpdateIssueByAPI updates all allowed fields of given issue.
1585+
func UpdateIssueByAPI(issue *Issue) error {
15811586
sess := x.NewSession()
15821587
defer sess.Close()
15831588
if err := sess.Begin(); err != nil {
15841589
return err
15851590
}
1586-
if err := updateIssue(sess, issue); err != nil {
1587-
return err
1588-
}
1589-
if err := issue.loadPoster(sess); err != nil {
1591+
1592+
if _, err := sess.ID(issue.ID).Cols(
1593+
"name", "is_closed", "content", "milestone_id", "priority",
1594+
"deadline_unix", "updated_unix", "closed_unix", "is_locked").
1595+
Update(issue); err != nil {
15901596
return err
15911597
}
15921598
if err := issue.addCrossReferences(sess, issue.Poster, true); err != nil {

models/user.go

+8
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,14 @@ func NewGhostUser() *User {
791791
}
792792
}
793793

794+
// IsGhost check if user is fake user for a deleted account
795+
func (u *User) IsGhost() bool {
796+
if u == nil {
797+
return false
798+
}
799+
return u.ID == -1 && u.Name == "Ghost"
800+
}
801+
794802
var (
795803
reservedUsernames = []string{
796804
"attachments",

routers/api/v1/repo/issue.go

+7-3
Original file line numberDiff line numberDiff line change
@@ -524,8 +524,8 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
524524
}
525525
}
526526

527-
if err = models.UpdateIssue(issue); err != nil {
528-
ctx.Error(http.StatusInternalServerError, "UpdateIssue", err)
527+
if err = models.UpdateIssueByAPI(issue); err != nil {
528+
ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err)
529529
return
530530
}
531531
if form.State != nil {
@@ -542,7 +542,11 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
542542
// Refetch from database to assign some automatic values
543543
issue, err = models.GetIssueByID(issue.ID)
544544
if err != nil {
545-
ctx.Error(http.StatusInternalServerError, "GetIssueByID", err)
545+
ctx.InternalServerError(err)
546+
return
547+
}
548+
if err = issue.LoadMilestone(); err != nil {
549+
ctx.InternalServerError(err)
546550
return
547551
}
548552
ctx.JSON(http.StatusCreated, issue.APIFormat())

routers/api/v1/repo/pull.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -450,8 +450,8 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
450450
}
451451
}
452452

453-
if err = models.UpdateIssue(issue); err != nil {
454-
ctx.Error(http.StatusInternalServerError, "UpdateIssue", err)
453+
if err = models.UpdateIssueByAPI(issue); err != nil {
454+
ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err)
455455
return
456456
}
457457
if form.State != nil {

0 commit comments

Comments
 (0)