code.gitea.io/gitea@v1.22.3/routers/api/v1/org/org.go (about)

     1  // Copyright 2015 The Gogs Authors. All rights reserved.
     2  // Copyright 2018 The Gitea Authors. All rights reserved.
     3  // SPDX-License-Identifier: MIT
     4  
     5  package org
     6  
     7  import (
     8  	"net/http"
     9  
    10  	activities_model "code.gitea.io/gitea/models/activities"
    11  	"code.gitea.io/gitea/models/db"
    12  	"code.gitea.io/gitea/models/organization"
    13  	"code.gitea.io/gitea/models/perm"
    14  	user_model "code.gitea.io/gitea/models/user"
    15  	"code.gitea.io/gitea/modules/optional"
    16  	api "code.gitea.io/gitea/modules/structs"
    17  	"code.gitea.io/gitea/modules/web"
    18  	"code.gitea.io/gitea/routers/api/v1/user"
    19  	"code.gitea.io/gitea/routers/api/v1/utils"
    20  	"code.gitea.io/gitea/services/context"
    21  	"code.gitea.io/gitea/services/convert"
    22  	"code.gitea.io/gitea/services/org"
    23  	user_service "code.gitea.io/gitea/services/user"
    24  )
    25  
    26  func listUserOrgs(ctx *context.APIContext, u *user_model.User) {
    27  	listOptions := utils.GetListOptions(ctx)
    28  	showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == u.ID)
    29  
    30  	opts := organization.FindOrgOptions{
    31  		ListOptions:    listOptions,
    32  		UserID:         u.ID,
    33  		IncludePrivate: showPrivate,
    34  	}
    35  	orgs, maxResults, err := db.FindAndCount[organization.Organization](ctx, opts)
    36  	if err != nil {
    37  		ctx.Error(http.StatusInternalServerError, "db.FindAndCount[organization.Organization]", err)
    38  		return
    39  	}
    40  
    41  	apiOrgs := make([]*api.Organization, len(orgs))
    42  	for i := range orgs {
    43  		apiOrgs[i] = convert.ToOrganization(ctx, orgs[i])
    44  	}
    45  
    46  	ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
    47  	ctx.SetTotalCountHeader(maxResults)
    48  	ctx.JSON(http.StatusOK, &apiOrgs)
    49  }
    50  
    51  // ListMyOrgs list all my orgs
    52  func ListMyOrgs(ctx *context.APIContext) {
    53  	// swagger:operation GET /user/orgs organization orgListCurrentUserOrgs
    54  	// ---
    55  	// summary: List the current user's organizations
    56  	// produces:
    57  	// - application/json
    58  	// parameters:
    59  	// - name: page
    60  	//   in: query
    61  	//   description: page number of results to return (1-based)
    62  	//   type: integer
    63  	// - name: limit
    64  	//   in: query
    65  	//   description: page size of results
    66  	//   type: integer
    67  	// responses:
    68  	//   "200":
    69  	//     "$ref": "#/responses/OrganizationList"
    70  	//   "404":
    71  	//     "$ref": "#/responses/notFound"
    72  
    73  	listUserOrgs(ctx, ctx.Doer)
    74  }
    75  
    76  // ListUserOrgs list user's orgs
    77  func ListUserOrgs(ctx *context.APIContext) {
    78  	// swagger:operation GET /users/{username}/orgs organization orgListUserOrgs
    79  	// ---
    80  	// summary: List a user's organizations
    81  	// produces:
    82  	// - application/json
    83  	// parameters:
    84  	// - name: username
    85  	//   in: path
    86  	//   description: username of user
    87  	//   type: string
    88  	//   required: true
    89  	// - name: page
    90  	//   in: query
    91  	//   description: page number of results to return (1-based)
    92  	//   type: integer
    93  	// - name: limit
    94  	//   in: query
    95  	//   description: page size of results
    96  	//   type: integer
    97  	// responses:
    98  	//   "200":
    99  	//     "$ref": "#/responses/OrganizationList"
   100  	//   "404":
   101  	//     "$ref": "#/responses/notFound"
   102  
   103  	listUserOrgs(ctx, ctx.ContextUser)
   104  }
   105  
   106  // GetUserOrgsPermissions get user permissions in organization
   107  func GetUserOrgsPermissions(ctx *context.APIContext) {
   108  	// swagger:operation GET /users/{username}/orgs/{org}/permissions organization orgGetUserPermissions
   109  	// ---
   110  	// summary: Get user permissions in organization
   111  	// produces:
   112  	// - application/json
   113  	// parameters:
   114  	// - name: username
   115  	//   in: path
   116  	//   description: username of user
   117  	//   type: string
   118  	//   required: true
   119  	// - name: org
   120  	//   in: path
   121  	//   description: name of the organization
   122  	//   type: string
   123  	//   required: true
   124  	// responses:
   125  	//   "200":
   126  	//     "$ref": "#/responses/OrganizationPermissions"
   127  	//   "403":
   128  	//     "$ref": "#/responses/forbidden"
   129  	//   "404":
   130  	//     "$ref": "#/responses/notFound"
   131  
   132  	var o *user_model.User
   133  	if o = user.GetUserByParamsName(ctx, ":org"); o == nil {
   134  		return
   135  	}
   136  
   137  	op := api.OrganizationPermissions{}
   138  
   139  	if !organization.HasOrgOrUserVisible(ctx, o, ctx.ContextUser) {
   140  		ctx.NotFound("HasOrgOrUserVisible", nil)
   141  		return
   142  	}
   143  
   144  	org := organization.OrgFromUser(o)
   145  	authorizeLevel, err := org.GetOrgUserMaxAuthorizeLevel(ctx, ctx.ContextUser.ID)
   146  	if err != nil {
   147  		ctx.Error(http.StatusInternalServerError, "GetOrgUserAuthorizeLevel", err)
   148  		return
   149  	}
   150  
   151  	if authorizeLevel > perm.AccessModeNone {
   152  		op.CanRead = true
   153  	}
   154  	if authorizeLevel > perm.AccessModeRead {
   155  		op.CanWrite = true
   156  	}
   157  	if authorizeLevel > perm.AccessModeWrite {
   158  		op.IsAdmin = true
   159  	}
   160  	if authorizeLevel > perm.AccessModeAdmin {
   161  		op.IsOwner = true
   162  	}
   163  
   164  	op.CanCreateRepository, err = org.CanCreateOrgRepo(ctx, ctx.ContextUser.ID)
   165  	if err != nil {
   166  		ctx.Error(http.StatusInternalServerError, "CanCreateOrgRepo", err)
   167  		return
   168  	}
   169  
   170  	ctx.JSON(http.StatusOK, op)
   171  }
   172  
   173  // GetAll return list of all public organizations
   174  func GetAll(ctx *context.APIContext) {
   175  	// swagger:operation Get /orgs organization orgGetAll
   176  	// ---
   177  	// summary: Get list of organizations
   178  	// produces:
   179  	// - application/json
   180  	// parameters:
   181  	// - name: page
   182  	//   in: query
   183  	//   description: page number of results to return (1-based)
   184  	//   type: integer
   185  	// - name: limit
   186  	//   in: query
   187  	//   description: page size of results
   188  	//   type: integer
   189  	// responses:
   190  	//   "200":
   191  	//     "$ref": "#/responses/OrganizationList"
   192  
   193  	vMode := []api.VisibleType{api.VisibleTypePublic}
   194  	if ctx.IsSigned && !ctx.PublicOnly {
   195  		vMode = append(vMode, api.VisibleTypeLimited)
   196  		if ctx.Doer.IsAdmin {
   197  			vMode = append(vMode, api.VisibleTypePrivate)
   198  		}
   199  	}
   200  
   201  	listOptions := utils.GetListOptions(ctx)
   202  
   203  	publicOrgs, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
   204  		Actor:       ctx.Doer,
   205  		ListOptions: listOptions,
   206  		Type:        user_model.UserTypeOrganization,
   207  		OrderBy:     db.SearchOrderByAlphabetically,
   208  		Visible:     vMode,
   209  	})
   210  	if err != nil {
   211  		ctx.Error(http.StatusInternalServerError, "SearchOrganizations", err)
   212  		return
   213  	}
   214  	orgs := make([]*api.Organization, len(publicOrgs))
   215  	for i := range publicOrgs {
   216  		orgs[i] = convert.ToOrganization(ctx, organization.OrgFromUser(publicOrgs[i]))
   217  	}
   218  
   219  	ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
   220  	ctx.SetTotalCountHeader(maxResults)
   221  	ctx.JSON(http.StatusOK, &orgs)
   222  }
   223  
   224  // Create api for create organization
   225  func Create(ctx *context.APIContext) {
   226  	// swagger:operation POST /orgs organization orgCreate
   227  	// ---
   228  	// summary: Create an organization
   229  	// consumes:
   230  	// - application/json
   231  	// produces:
   232  	// - application/json
   233  	// parameters:
   234  	// - name: organization
   235  	//   in: body
   236  	//   required: true
   237  	//   schema: { "$ref": "#/definitions/CreateOrgOption" }
   238  	// responses:
   239  	//   "201":
   240  	//     "$ref": "#/responses/Organization"
   241  	//   "403":
   242  	//     "$ref": "#/responses/forbidden"
   243  	//   "422":
   244  	//     "$ref": "#/responses/validationError"
   245  	form := web.GetForm(ctx).(*api.CreateOrgOption)
   246  	if !ctx.Doer.CanCreateOrganization() {
   247  		ctx.Error(http.StatusForbidden, "Create organization not allowed", nil)
   248  		return
   249  	}
   250  
   251  	visibility := api.VisibleTypePublic
   252  	if form.Visibility != "" {
   253  		visibility = api.VisibilityModes[form.Visibility]
   254  	}
   255  
   256  	org := &organization.Organization{
   257  		Name:                      form.UserName,
   258  		FullName:                  form.FullName,
   259  		Email:                     form.Email,
   260  		Description:               form.Description,
   261  		Website:                   form.Website,
   262  		Location:                  form.Location,
   263  		IsActive:                  true,
   264  		Type:                      user_model.UserTypeOrganization,
   265  		Visibility:                visibility,
   266  		RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess,
   267  	}
   268  	if err := organization.CreateOrganization(ctx, org, ctx.Doer); err != nil {
   269  		if user_model.IsErrUserAlreadyExist(err) ||
   270  			db.IsErrNameReserved(err) ||
   271  			db.IsErrNameCharsNotAllowed(err) ||
   272  			db.IsErrNamePatternNotAllowed(err) {
   273  			ctx.Error(http.StatusUnprocessableEntity, "", err)
   274  		} else {
   275  			ctx.Error(http.StatusInternalServerError, "CreateOrganization", err)
   276  		}
   277  		return
   278  	}
   279  
   280  	ctx.JSON(http.StatusCreated, convert.ToOrganization(ctx, org))
   281  }
   282  
   283  // Get get an organization
   284  func Get(ctx *context.APIContext) {
   285  	// swagger:operation GET /orgs/{org} organization orgGet
   286  	// ---
   287  	// summary: Get an organization
   288  	// produces:
   289  	// - application/json
   290  	// parameters:
   291  	// - name: org
   292  	//   in: path
   293  	//   description: name of the organization to get
   294  	//   type: string
   295  	//   required: true
   296  	// responses:
   297  	//   "200":
   298  	//     "$ref": "#/responses/Organization"
   299  	//   "404":
   300  	//     "$ref": "#/responses/notFound"
   301  
   302  	if !organization.HasOrgOrUserVisible(ctx, ctx.Org.Organization.AsUser(), ctx.Doer) {
   303  		ctx.NotFound("HasOrgOrUserVisible", nil)
   304  		return
   305  	}
   306  
   307  	org := convert.ToOrganization(ctx, ctx.Org.Organization)
   308  
   309  	// Don't show Mail, when User is not logged in
   310  	if ctx.Doer == nil {
   311  		org.Email = ""
   312  	}
   313  
   314  	ctx.JSON(http.StatusOK, org)
   315  }
   316  
   317  // Edit change an organization's information
   318  func Edit(ctx *context.APIContext) {
   319  	// swagger:operation PATCH /orgs/{org} organization orgEdit
   320  	// ---
   321  	// summary: Edit an organization
   322  	// consumes:
   323  	// - application/json
   324  	// produces:
   325  	// - application/json
   326  	// parameters:
   327  	// - name: org
   328  	//   in: path
   329  	//   description: name of the organization to edit
   330  	//   type: string
   331  	//   required: true
   332  	// - name: body
   333  	//   in: body
   334  	//   required: true
   335  	//   schema:
   336  	//     "$ref": "#/definitions/EditOrgOption"
   337  	// responses:
   338  	//   "200":
   339  	//     "$ref": "#/responses/Organization"
   340  	//   "404":
   341  	//     "$ref": "#/responses/notFound"
   342  
   343  	form := web.GetForm(ctx).(*api.EditOrgOption)
   344  
   345  	if form.Email != "" {
   346  		if err := user_service.ReplacePrimaryEmailAddress(ctx, ctx.Org.Organization.AsUser(), form.Email); err != nil {
   347  			ctx.Error(http.StatusInternalServerError, "ReplacePrimaryEmailAddress", err)
   348  			return
   349  		}
   350  	}
   351  
   352  	opts := &user_service.UpdateOptions{
   353  		FullName:                  optional.Some(form.FullName),
   354  		Description:               optional.Some(form.Description),
   355  		Website:                   optional.Some(form.Website),
   356  		Location:                  optional.Some(form.Location),
   357  		Visibility:                optional.FromNonDefault(api.VisibilityModes[form.Visibility]),
   358  		RepoAdminChangeTeamAccess: optional.FromPtr(form.RepoAdminChangeTeamAccess),
   359  	}
   360  	if err := user_service.UpdateUser(ctx, ctx.Org.Organization.AsUser(), opts); err != nil {
   361  		ctx.Error(http.StatusInternalServerError, "UpdateUser", err)
   362  		return
   363  	}
   364  
   365  	ctx.JSON(http.StatusOK, convert.ToOrganization(ctx, ctx.Org.Organization))
   366  }
   367  
   368  // Delete an organization
   369  func Delete(ctx *context.APIContext) {
   370  	// swagger:operation DELETE /orgs/{org} organization orgDelete
   371  	// ---
   372  	// summary: Delete an organization
   373  	// produces:
   374  	// - application/json
   375  	// parameters:
   376  	// - name: org
   377  	//   in: path
   378  	//   description: organization that is to be deleted
   379  	//   type: string
   380  	//   required: true
   381  	// responses:
   382  	//   "204":
   383  	//     "$ref": "#/responses/empty"
   384  	//   "404":
   385  	//     "$ref": "#/responses/notFound"
   386  
   387  	if err := org.DeleteOrganization(ctx, ctx.Org.Organization, false); err != nil {
   388  		ctx.Error(http.StatusInternalServerError, "DeleteOrganization", err)
   389  		return
   390  	}
   391  	ctx.Status(http.StatusNoContent)
   392  }
   393  
   394  func ListOrgActivityFeeds(ctx *context.APIContext) {
   395  	// swagger:operation GET /orgs/{org}/activities/feeds organization orgListActivityFeeds
   396  	// ---
   397  	// summary: List an organization's activity feeds
   398  	// produces:
   399  	// - application/json
   400  	// parameters:
   401  	// - name: org
   402  	//   in: path
   403  	//   description: name of the org
   404  	//   type: string
   405  	//   required: true
   406  	// - name: date
   407  	//   in: query
   408  	//   description: the date of the activities to be found
   409  	//   type: string
   410  	//   format: date
   411  	// - name: page
   412  	//   in: query
   413  	//   description: page number of results to return (1-based)
   414  	//   type: integer
   415  	// - name: limit
   416  	//   in: query
   417  	//   description: page size of results
   418  	//   type: integer
   419  	// responses:
   420  	//   "200":
   421  	//     "$ref": "#/responses/ActivityFeedsList"
   422  	//   "404":
   423  	//     "$ref": "#/responses/notFound"
   424  
   425  	includePrivate := false
   426  	if ctx.IsSigned {
   427  		if ctx.Doer.IsAdmin {
   428  			includePrivate = true
   429  		} else {
   430  			org := organization.OrgFromUser(ctx.ContextUser)
   431  			isMember, err := org.IsOrgMember(ctx, ctx.Doer.ID)
   432  			if err != nil {
   433  				ctx.Error(http.StatusInternalServerError, "IsOrgMember", err)
   434  				return
   435  			}
   436  			includePrivate = isMember
   437  		}
   438  	}
   439  
   440  	listOptions := utils.GetListOptions(ctx)
   441  
   442  	opts := activities_model.GetFeedsOptions{
   443  		RequestedUser:  ctx.ContextUser,
   444  		Actor:          ctx.Doer,
   445  		IncludePrivate: includePrivate,
   446  		Date:           ctx.FormString("date"),
   447  		ListOptions:    listOptions,
   448  	}
   449  
   450  	feeds, count, err := activities_model.GetFeeds(ctx, opts)
   451  	if err != nil {
   452  		ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
   453  		return
   454  	}
   455  	ctx.SetTotalCountHeader(count)
   456  
   457  	ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
   458  }