code.gitea.io/gitea@v1.21.7/routers/api/v1/repo/collaborators.go (about) 1 // Copyright 2016 The Gogs Authors. All rights reserved. 2 // Copyright 2018 The Gitea Authors. All rights reserved. 3 // SPDX-License-Identifier: MIT 4 5 package repo 6 7 import ( 8 "errors" 9 "net/http" 10 11 "code.gitea.io/gitea/models/perm" 12 access_model "code.gitea.io/gitea/models/perm/access" 13 repo_model "code.gitea.io/gitea/models/repo" 14 user_model "code.gitea.io/gitea/models/user" 15 "code.gitea.io/gitea/modules/context" 16 repo_module "code.gitea.io/gitea/modules/repository" 17 api "code.gitea.io/gitea/modules/structs" 18 "code.gitea.io/gitea/modules/web" 19 "code.gitea.io/gitea/routers/api/v1/utils" 20 "code.gitea.io/gitea/services/convert" 21 repo_service "code.gitea.io/gitea/services/repository" 22 ) 23 24 // ListCollaborators list a repository's collaborators 25 func ListCollaborators(ctx *context.APIContext) { 26 // swagger:operation GET /repos/{owner}/{repo}/collaborators repository repoListCollaborators 27 // --- 28 // summary: List a repository's collaborators 29 // produces: 30 // - application/json 31 // parameters: 32 // - name: owner 33 // in: path 34 // description: owner of the repo 35 // type: string 36 // required: true 37 // - name: repo 38 // in: path 39 // description: name of the repo 40 // type: string 41 // required: true 42 // - name: page 43 // in: query 44 // description: page number of results to return (1-based) 45 // type: integer 46 // - name: limit 47 // in: query 48 // description: page size of results 49 // type: integer 50 // responses: 51 // "200": 52 // "$ref": "#/responses/UserList" 53 // "404": 54 // "$ref": "#/responses/notFound" 55 56 count, err := repo_model.CountCollaborators(ctx, ctx.Repo.Repository.ID) 57 if err != nil { 58 ctx.InternalServerError(err) 59 return 60 } 61 62 collaborators, err := repo_model.GetCollaborators(ctx, ctx.Repo.Repository.ID, utils.GetListOptions(ctx)) 63 if err != nil { 64 ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) 65 return 66 } 67 68 users := make([]*api.User, len(collaborators)) 69 for i, collaborator := range collaborators { 70 users[i] = convert.ToUser(ctx, collaborator.User, ctx.Doer) 71 } 72 73 ctx.SetTotalCountHeader(count) 74 ctx.JSON(http.StatusOK, users) 75 } 76 77 // IsCollaborator check if a user is a collaborator of a repository 78 func IsCollaborator(ctx *context.APIContext) { 79 // swagger:operation GET /repos/{owner}/{repo}/collaborators/{collaborator} repository repoCheckCollaborator 80 // --- 81 // summary: Check if a user is a collaborator of a repository 82 // produces: 83 // - application/json 84 // parameters: 85 // - name: owner 86 // in: path 87 // description: owner of the repo 88 // type: string 89 // required: true 90 // - name: repo 91 // in: path 92 // description: name of the repo 93 // type: string 94 // required: true 95 // - name: collaborator 96 // in: path 97 // description: username of the collaborator 98 // type: string 99 // required: true 100 // responses: 101 // "204": 102 // "$ref": "#/responses/empty" 103 // "404": 104 // "$ref": "#/responses/notFound" 105 // "422": 106 // "$ref": "#/responses/validationError" 107 108 user, err := user_model.GetUserByName(ctx, ctx.Params(":collaborator")) 109 if err != nil { 110 if user_model.IsErrUserNotExist(err) { 111 ctx.Error(http.StatusUnprocessableEntity, "", err) 112 } else { 113 ctx.Error(http.StatusInternalServerError, "GetUserByName", err) 114 } 115 return 116 } 117 isColab, err := repo_model.IsCollaborator(ctx, ctx.Repo.Repository.ID, user.ID) 118 if err != nil { 119 ctx.Error(http.StatusInternalServerError, "IsCollaborator", err) 120 return 121 } 122 if isColab { 123 ctx.Status(http.StatusNoContent) 124 } else { 125 ctx.NotFound() 126 } 127 } 128 129 // AddCollaborator add a collaborator to a repository 130 func AddCollaborator(ctx *context.APIContext) { 131 // swagger:operation PUT /repos/{owner}/{repo}/collaborators/{collaborator} repository repoAddCollaborator 132 // --- 133 // summary: Add a collaborator to a repository 134 // produces: 135 // - application/json 136 // parameters: 137 // - name: owner 138 // in: path 139 // description: owner of the repo 140 // type: string 141 // required: true 142 // - name: repo 143 // in: path 144 // description: name of the repo 145 // type: string 146 // required: true 147 // - name: collaborator 148 // in: path 149 // description: username of the collaborator to add 150 // type: string 151 // required: true 152 // - name: body 153 // in: body 154 // schema: 155 // "$ref": "#/definitions/AddCollaboratorOption" 156 // responses: 157 // "204": 158 // "$ref": "#/responses/empty" 159 // "404": 160 // "$ref": "#/responses/notFound" 161 // "422": 162 // "$ref": "#/responses/validationError" 163 164 form := web.GetForm(ctx).(*api.AddCollaboratorOption) 165 166 collaborator, err := user_model.GetUserByName(ctx, ctx.Params(":collaborator")) 167 if err != nil { 168 if user_model.IsErrUserNotExist(err) { 169 ctx.Error(http.StatusUnprocessableEntity, "", err) 170 } else { 171 ctx.Error(http.StatusInternalServerError, "GetUserByName", err) 172 } 173 return 174 } 175 176 if !collaborator.IsActive { 177 ctx.Error(http.StatusInternalServerError, "InactiveCollaborator", errors.New("collaborator's account is inactive")) 178 return 179 } 180 181 if err := repo_module.AddCollaborator(ctx, ctx.Repo.Repository, collaborator); err != nil { 182 ctx.Error(http.StatusInternalServerError, "AddCollaborator", err) 183 return 184 } 185 186 if form.Permission != nil { 187 if err := repo_model.ChangeCollaborationAccessMode(ctx, ctx.Repo.Repository, collaborator.ID, perm.ParseAccessMode(*form.Permission)); err != nil { 188 ctx.Error(http.StatusInternalServerError, "ChangeCollaborationAccessMode", err) 189 return 190 } 191 } 192 193 ctx.Status(http.StatusNoContent) 194 } 195 196 // DeleteCollaborator delete a collaborator from a repository 197 func DeleteCollaborator(ctx *context.APIContext) { 198 // swagger:operation DELETE /repos/{owner}/{repo}/collaborators/{collaborator} repository repoDeleteCollaborator 199 // --- 200 // summary: Delete a collaborator from a repository 201 // produces: 202 // - application/json 203 // parameters: 204 // - name: owner 205 // in: path 206 // description: owner of the repo 207 // type: string 208 // required: true 209 // - name: repo 210 // in: path 211 // description: name of the repo 212 // type: string 213 // required: true 214 // - name: collaborator 215 // in: path 216 // description: username of the collaborator to delete 217 // type: string 218 // required: true 219 // responses: 220 // "204": 221 // "$ref": "#/responses/empty" 222 // "404": 223 // "$ref": "#/responses/notFound" 224 // "422": 225 // "$ref": "#/responses/validationError" 226 227 collaborator, err := user_model.GetUserByName(ctx, ctx.Params(":collaborator")) 228 if err != nil { 229 if user_model.IsErrUserNotExist(err) { 230 ctx.Error(http.StatusUnprocessableEntity, "", err) 231 } else { 232 ctx.Error(http.StatusInternalServerError, "GetUserByName", err) 233 } 234 return 235 } 236 237 if err := repo_service.DeleteCollaboration(ctx, ctx.Repo.Repository, collaborator.ID); err != nil { 238 ctx.Error(http.StatusInternalServerError, "DeleteCollaboration", err) 239 return 240 } 241 ctx.Status(http.StatusNoContent) 242 } 243 244 // GetRepoPermissions gets repository permissions for a user 245 func GetRepoPermissions(ctx *context.APIContext) { 246 // swagger:operation GET /repos/{owner}/{repo}/collaborators/{collaborator}/permission repository repoGetRepoPermissions 247 // --- 248 // summary: Get repository permissions for a user 249 // produces: 250 // - application/json 251 // parameters: 252 // - name: owner 253 // in: path 254 // description: owner of the repo 255 // type: string 256 // required: true 257 // - name: repo 258 // in: path 259 // description: name of the repo 260 // type: string 261 // required: true 262 // - name: collaborator 263 // in: path 264 // description: username of the collaborator 265 // type: string 266 // required: true 267 // responses: 268 // "200": 269 // "$ref": "#/responses/RepoCollaboratorPermission" 270 // "404": 271 // "$ref": "#/responses/notFound" 272 // "403": 273 // "$ref": "#/responses/forbidden" 274 275 if !ctx.Doer.IsAdmin && ctx.Doer.LoginName != ctx.Params(":collaborator") && !ctx.IsUserRepoAdmin() { 276 ctx.Error(http.StatusForbidden, "User", "Only admins can query all permissions, repo admins can query all repo permissions, collaborators can query only their own") 277 return 278 } 279 280 collaborator, err := user_model.GetUserByName(ctx, ctx.Params(":collaborator")) 281 if err != nil { 282 if user_model.IsErrUserNotExist(err) { 283 ctx.Error(http.StatusNotFound, "GetUserByName", err) 284 } else { 285 ctx.Error(http.StatusInternalServerError, "GetUserByName", err) 286 } 287 return 288 } 289 290 permission, err := access_model.GetUserRepoPermission(ctx, ctx.Repo.Repository, collaborator) 291 if err != nil { 292 ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) 293 return 294 } 295 296 ctx.JSON(http.StatusOK, convert.ToUserAndPermission(ctx, collaborator, ctx.ContextUser, permission.AccessMode)) 297 } 298 299 // GetReviewers return all users that can be requested to review in this repo 300 func GetReviewers(ctx *context.APIContext) { 301 // swagger:operation GET /repos/{owner}/{repo}/reviewers repository repoGetReviewers 302 // --- 303 // summary: Return all users that can be requested to review in this repo 304 // produces: 305 // - application/json 306 // parameters: 307 // - name: owner 308 // in: path 309 // description: owner of the repo 310 // type: string 311 // required: true 312 // - name: repo 313 // in: path 314 // description: name of the repo 315 // type: string 316 // required: true 317 // responses: 318 // "200": 319 // "$ref": "#/responses/UserList" 320 // "404": 321 // "$ref": "#/responses/notFound" 322 323 reviewers, err := repo_model.GetReviewers(ctx, ctx.Repo.Repository, ctx.Doer.ID, 0) 324 if err != nil { 325 ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) 326 return 327 } 328 ctx.JSON(http.StatusOK, convert.ToUsers(ctx, ctx.Doer, reviewers)) 329 } 330 331 // GetAssignees return all users that have write access and can be assigned to issues 332 func GetAssignees(ctx *context.APIContext) { 333 // swagger:operation GET /repos/{owner}/{repo}/assignees repository repoGetAssignees 334 // --- 335 // summary: Return all users that have write access and can be assigned to issues 336 // produces: 337 // - application/json 338 // parameters: 339 // - name: owner 340 // in: path 341 // description: owner of the repo 342 // type: string 343 // required: true 344 // - name: repo 345 // in: path 346 // description: name of the repo 347 // type: string 348 // required: true 349 // responses: 350 // "200": 351 // "$ref": "#/responses/UserList" 352 // "404": 353 // "$ref": "#/responses/notFound" 354 355 assignees, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository) 356 if err != nil { 357 ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) 358 return 359 } 360 ctx.JSON(http.StatusOK, convert.ToUsers(ctx, ctx.Doer, assignees)) 361 }