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