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  }