From c2ae432489a9e644d83069e2e3bae20b2f1f06eb Mon Sep 17 00:00:00 2001
From: Norwin <noerw@users.noreply.github.com>
Date: Tue, 22 Dec 2020 03:47:17 +0000
Subject: [PATCH] Add user filter to issueTrackedTimes, enable usage for issue
 managers (#14081)

* add user filter to issueTrackedTimes

fixes #14024

* update swagger

* allow user filter for issue writers

* improve swagger doc

* return 404 on invalid user
---
 routers/api/v1/repo/issue_tracked_time.go | 48 +++++++++++++++++------
 templates/swagger/v1_json.tmpl            |  8 +++-
 2 files changed, 44 insertions(+), 12 deletions(-)

diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go
index 67c47d858a..70ce420b77 100644
--- a/routers/api/v1/repo/issue_tracked_time.go
+++ b/routers/api/v1/repo/issue_tracked_time.go
@@ -41,6 +41,10 @@ func ListTrackedTimes(ctx *context.APIContext) {
 	//   type: integer
 	//   format: int64
 	//   required: true
+	// - name: user
+	//   in: query
+	//   description: optional filter by user (available for issue managers)
+	//   type: string
 	// - name: since
 	//   in: query
 	//   description: Only show times updated after the given time. This is a timestamp in RFC 3339 format
@@ -85,13 +89,34 @@ func ListTrackedTimes(ctx *context.APIContext) {
 		IssueID:      issue.ID,
 	}
 
+	qUser := strings.Trim(ctx.Query("user"), " ")
+	if qUser != "" {
+		user, err := models.GetUserByName(qUser)
+		if models.IsErrUserNotExist(err) {
+			ctx.Error(http.StatusNotFound, "User does not exist", err)
+		} else if err != nil {
+			ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
+			return
+		}
+		opts.UserID = user.ID
+	}
+
 	if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {
 		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 		return
 	}
 
-	if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
-		opts.UserID = ctx.User.ID
+	cantSetUser := !ctx.User.IsAdmin &&
+		opts.UserID != ctx.User.ID &&
+		!ctx.IsUserRepoWriter([]models.UnitType{models.UnitTypeIssues})
+
+	if cantSetUser {
+		if opts.UserID == 0 {
+			opts.UserID = ctx.User.ID
+		} else {
+			ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights"))
+			return
+		}
 	}
 
 	trackedTimes, err := models.GetTrackedTimes(opts)
@@ -394,12 +419,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
 	}
 
 	if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin && ctx.User.ID != user.ID {
-		ctx.Error(http.StatusForbidden, "", fmt.Errorf("query user not allowed not enouth rights"))
-		return
-	}
-
-	if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin && ctx.User.ID != user.ID {
-		ctx.Error(http.StatusForbidden, "", fmt.Errorf("query user not allowed not enouth rights"))
+		ctx.Error(http.StatusForbidden, "", fmt.Errorf("query by user not allowed; not enough rights"))
 		return
 	}
 
@@ -440,7 +460,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
 	//   required: true
 	// - name: user
 	//   in: query
-	//   description: optional filter by user
+	//   description: optional filter by user (available for issue managers)
 	//   type: string
 	// - name: since
 	//   in: query
@@ -482,7 +502,9 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
 	qUser := strings.Trim(ctx.Query("user"), " ")
 	if qUser != "" {
 		user, err := models.GetUserByName(qUser)
-		if err != nil {
+		if models.IsErrUserNotExist(err) {
+			ctx.Error(http.StatusNotFound, "User does not exist", err)
+		} else if err != nil {
 			ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
 			return
 		}
@@ -495,7 +517,11 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
 		return
 	}
 
-	if !ctx.IsUserRepoAdmin() && !ctx.User.IsAdmin {
+	cantSetUser := !ctx.User.IsAdmin &&
+		opts.UserID != ctx.User.ID &&
+		!ctx.IsUserRepoWriter([]models.UnitType{models.UnitTypeIssues})
+
+	if cantSetUser {
 		if opts.UserID == 0 {
 			opts.UserID = ctx.User.ID
 		} else {
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index bb3b6b4fed..d0303040c5 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -5840,6 +5840,12 @@
             "in": "path",
             "required": true
           },
+          {
+            "type": "string",
+            "description": "optional filter by user (available for issue managers)",
+            "name": "user",
+            "in": "query"
+          },
           {
             "type": "string",
             "format": "date-time",
@@ -8811,7 +8817,7 @@
           },
           {
             "type": "string",
-            "description": "optional filter by user",
+            "description": "optional filter by user (available for issue managers)",
             "name": "user",
             "in": "query"
           },