diff options
Diffstat (limited to 'routers/api/v1/org')
-rw-r--r-- | routers/api/v1/org/action.go | 278 | ||||
-rw-r--r-- | routers/api/v1/org/avatar.go | 6 | ||||
-rw-r--r-- | routers/api/v1/org/block.go | 6 | ||||
-rw-r--r-- | routers/api/v1/org/hook.go | 2 | ||||
-rw-r--r-- | routers/api/v1/org/label.go | 28 | ||||
-rw-r--r-- | routers/api/v1/org/member.go | 67 | ||||
-rw-r--r-- | routers/api/v1/org/org.go | 84 | ||||
-rw-r--r-- | routers/api/v1/org/team.go | 120 |
8 files changed, 413 insertions, 178 deletions
diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go index 199ee7d777..3ae5e60585 100644 --- a/routers/api/v1/org/action.go +++ b/routers/api/v1/org/action.go @@ -54,15 +54,16 @@ func (Action) ListActionsSecrets(ctx *context.APIContext) { secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts) if err != nil { - ctx.InternalServerError(err) + ctx.APIErrorInternal(err) return } apiSecrets := make([]*api.Secret, len(secrets)) for k, v := range secrets { apiSecrets[k] = &api.Secret{ - Name: v.Name, - Created: v.CreatedUnix.AsTime(), + Name: v.Name, + Description: v.Description, + Created: v.CreatedUnix.AsTime(), } } @@ -106,14 +107,14 @@ func (Action) CreateOrUpdateSecret(ctx *context.APIContext) { opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) - _, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname"), opt.Data) + _, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname"), opt.Data, opt.Description) if err != nil { if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err) + ctx.APIError(http.StatusBadRequest, err) } else if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "CreateOrUpdateSecret", err) + ctx.APIError(http.StatusNotFound, err) } else { - ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err) + ctx.APIErrorInternal(err) } return } @@ -156,11 +157,11 @@ func (Action) DeleteSecret(ctx *context.APIContext) { err := secret_service.DeleteSecretByName(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname")) if err != nil { if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "DeleteSecret", err) + ctx.APIError(http.StatusBadRequest, err) } else if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "DeleteSecret", err) + ctx.APIError(http.StatusNotFound, err) } else { - ctx.Error(http.StatusInternalServerError, "DeleteSecret", err) + ctx.APIErrorInternal(err) } return } @@ -189,6 +190,27 @@ func (Action) GetRegistrationToken(ctx *context.APIContext) { shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0) } +// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization +// CreateRegistrationToken returns the token to register org runners +func (Action) CreateRegistrationToken(ctx *context.APIContext) { + // swagger:operation POST /orgs/{org}/actions/runners/registration-token organization orgCreateRunnerRegistrationToken + // --- + // summary: Get an organization's actions runner registration token + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/RegistrationToken" + + shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0) +} + // ListVariables list org-level variables func (Action) ListVariables(ctx *context.APIContext) { // swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList @@ -223,17 +245,18 @@ func (Action) ListVariables(ctx *context.APIContext) { ListOptions: utils.GetListOptions(ctx), }) if err != nil { - ctx.Error(http.StatusInternalServerError, "FindVariables", err) + ctx.APIErrorInternal(err) return } variables := make([]*api.ActionVariable, len(vars)) for i, v := range vars { variables[i] = &api.ActionVariable{ - OwnerID: v.OwnerID, - RepoID: v.RepoID, - Name: v.Name, - Data: v.Data, + OwnerID: v.OwnerID, + RepoID: v.RepoID, + Name: v.Name, + Data: v.Data, + Description: v.Description, } } @@ -273,18 +296,19 @@ func (Action) GetVariable(ctx *context.APIContext) { }) if err != nil { if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "GetVariable", err) + ctx.APIError(http.StatusNotFound, err) } else { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) + ctx.APIErrorInternal(err) } return } variable := &api.ActionVariable{ - OwnerID: v.OwnerID, - RepoID: v.RepoID, - Name: v.Name, - Data: v.Data, + OwnerID: v.OwnerID, + RepoID: v.RepoID, + Name: v.Name, + Data: v.Data, + Description: v.Description, } ctx.JSON(http.StatusOK, variable) @@ -322,11 +346,11 @@ func (Action) DeleteVariable(ctx *context.APIContext) { if err := actions_service.DeleteVariableByName(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("variablename")); err != nil { if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "DeleteVariableByName", err) + ctx.APIError(http.StatusBadRequest, err) } else if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "DeleteVariableByName", err) + ctx.APIError(http.StatusNotFound, err) } else { - ctx.Error(http.StatusInternalServerError, "DeleteVariableByName", err) + ctx.APIErrorInternal(err) } return } @@ -360,13 +384,13 @@ func (Action) CreateVariable(ctx *context.APIContext) { // "$ref": "#/definitions/CreateVariableOption" // responses: // "201": - // description: response when creating an org-level variable - // "204": - // description: response when creating an org-level variable + // description: successfully created the org-level variable // "400": // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" + // "409": + // description: variable name already exists. + // "500": + // "$ref": "#/responses/error" opt := web.GetForm(ctx).(*api.CreateVariableOption) @@ -378,24 +402,24 @@ func (Action) CreateVariable(ctx *context.APIContext) { Name: variableName, }) if err != nil && !errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) + ctx.APIErrorInternal(err) return } if v != nil && v.ID > 0 { - ctx.Error(http.StatusConflict, "VariableNameAlreadyExists", util.NewAlreadyExistErrorf("variable name %s already exists", variableName)) + ctx.APIError(http.StatusConflict, util.NewAlreadyExistErrorf("variable name %s already exists", variableName)) return } - if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value); err != nil { + if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value, opt.Description); err != nil { if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "CreateVariable", err) + ctx.APIError(http.StatusBadRequest, err) } else { - ctx.Error(http.StatusInternalServerError, "CreateVariable", err) + ctx.APIErrorInternal(err) } return } - ctx.Status(http.StatusNoContent) + ctx.Status(http.StatusCreated) } // UpdateVariable update an org-level variable @@ -440,9 +464,9 @@ func (Action) UpdateVariable(ctx *context.APIContext) { }) if err != nil { if errors.Is(err, util.ErrNotExist) { - ctx.Error(http.StatusNotFound, "GetVariable", err) + ctx.APIError(http.StatusNotFound, err) } else { - ctx.Error(http.StatusInternalServerError, "GetVariable", err) + ctx.APIErrorInternal(err) } return } @@ -450,11 +474,16 @@ func (Action) UpdateVariable(ctx *context.APIContext) { if opt.Name == "" { opt.Name = ctx.PathParam("variablename") } - if _, err := actions_service.UpdateVariable(ctx, v.ID, opt.Name, opt.Value); err != nil { + + v.Name = opt.Name + v.Data = opt.Value + v.Description = opt.Description + + if _, err := actions_service.UpdateVariableNameData(ctx, v); err != nil { if errors.Is(err, util.ErrInvalidArgument) { - ctx.Error(http.StatusBadRequest, "UpdateVariable", err) + ctx.APIError(http.StatusBadRequest, err) } else { - ctx.Error(http.StatusInternalServerError, "UpdateVariable", err) + ctx.APIErrorInternal(err) } return } @@ -462,6 +491,175 @@ func (Action) UpdateVariable(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } +// ListRunners get org-level runners +func (Action) ListRunners(ctx *context.APIContext) { + // swagger:operation GET /orgs/{org}/actions/runners organization getOrgRunners + // --- + // summary: Get org-level runners + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/definitions/ActionRunnersResponse" + // "400": + // "$ref": "#/responses/error" + // "404": + // "$ref": "#/responses/notFound" + shared.ListRunners(ctx, ctx.Org.Organization.ID, 0) +} + +// GetRunner get an org-level runner +func (Action) GetRunner(ctx *context.APIContext) { + // swagger:operation GET /orgs/{org}/actions/runners/{runner_id} organization getOrgRunner + // --- + // summary: Get an org-level runner + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // - name: runner_id + // in: path + // description: id of the runner + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/definitions/ActionRunner" + // "400": + // "$ref": "#/responses/error" + // "404": + // "$ref": "#/responses/notFound" + shared.GetRunner(ctx, ctx.Org.Organization.ID, 0, ctx.PathParamInt64("runner_id")) +} + +// DeleteRunner delete an org-level runner +func (Action) DeleteRunner(ctx *context.APIContext) { + // swagger:operation DELETE /orgs/{org}/actions/runners/{runner_id} organization deleteOrgRunner + // --- + // summary: Delete an org-level runner + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // - name: runner_id + // in: path + // description: id of the runner + // type: string + // required: true + // responses: + // "204": + // description: runner has been deleted + // "400": + // "$ref": "#/responses/error" + // "404": + // "$ref": "#/responses/notFound" + shared.DeleteRunner(ctx, ctx.Org.Organization.ID, 0, ctx.PathParamInt64("runner_id")) +} + +func (Action) ListWorkflowJobs(ctx *context.APIContext) { + // swagger:operation GET /orgs/{org}/actions/jobs organization getOrgWorkflowJobs + // --- + // summary: Get org-level workflow jobs + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // - name: status + // in: query + // description: workflow status (pending, queued, in_progress, failure, success, skipped) + // type: string + // required: false + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // "$ref": "#/responses/WorkflowJobsList" + // "400": + // "$ref": "#/responses/error" + // "404": + // "$ref": "#/responses/notFound" + shared.ListJobs(ctx, ctx.Org.Organization.ID, 0, 0) +} + +func (Action) ListWorkflowRuns(ctx *context.APIContext) { + // swagger:operation GET /orgs/{org}/actions/runs organization getOrgWorkflowRuns + // --- + // summary: Get org-level workflow runs + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of the organization + // type: string + // required: true + // - name: event + // in: query + // description: workflow event name + // type: string + // required: false + // - name: branch + // in: query + // description: workflow branch + // type: string + // required: false + // - name: status + // in: query + // description: workflow status (pending, queued, in_progress, failure, success, skipped) + // type: string + // required: false + // - name: actor + // in: query + // description: triggered by user + // type: string + // required: false + // - name: head_sha + // in: query + // description: triggering sha of the workflow run + // type: string + // required: false + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // "$ref": "#/responses/WorkflowRunsList" + // "400": + // "$ref": "#/responses/error" + // "404": + // "$ref": "#/responses/notFound" + shared.ListRuns(ctx, ctx.Org.Organization.ID, 0) +} + var _ actions_service.API = new(Action) // Action implements actions_service.API diff --git a/routers/api/v1/org/avatar.go b/routers/api/v1/org/avatar.go index f11eb6c1cd..0eb771b2cd 100644 --- a/routers/api/v1/org/avatar.go +++ b/routers/api/v1/org/avatar.go @@ -39,13 +39,13 @@ func UpdateAvatar(ctx *context.APIContext) { content, err := base64.StdEncoding.DecodeString(form.Image) if err != nil { - ctx.Error(http.StatusBadRequest, "DecodeImage", err) + ctx.APIError(http.StatusBadRequest, err) return } err = user_service.UploadAvatar(ctx, ctx.Org.Organization.AsUser(), content) if err != nil { - ctx.Error(http.StatusInternalServerError, "UploadAvatar", err) + ctx.APIErrorInternal(err) return } @@ -72,7 +72,7 @@ func DeleteAvatar(ctx *context.APIContext) { // "$ref": "#/responses/notFound" err := user_service.DeleteAvatar(ctx, ctx.Org.Organization.AsUser()) if err != nil { - ctx.Error(http.StatusInternalServerError, "DeleteAvatar", err) + ctx.APIErrorInternal(err) return } diff --git a/routers/api/v1/org/block.go b/routers/api/v1/org/block.go index 69a5222a20..6b2f3dc615 100644 --- a/routers/api/v1/org/block.go +++ b/routers/api/v1/org/block.go @@ -47,7 +47,7 @@ func CheckUserBlock(ctx *context.APIContext) { // required: true // - name: username // in: path - // description: user to check + // description: username of the user to check // type: string // required: true // responses: @@ -71,7 +71,7 @@ func BlockUser(ctx *context.APIContext) { // required: true // - name: username // in: path - // description: user to block + // description: username of the user to block // type: string // required: true // - name: note @@ -101,7 +101,7 @@ func UnblockUser(ctx *context.APIContext) { // required: true // - name: username // in: path - // description: user to unblock + // description: username of the user to unblock // type: string // required: true // responses: diff --git a/routers/api/v1/org/hook.go b/routers/api/v1/org/hook.go index df82f4e5a2..f9e0684a97 100644 --- a/routers/api/v1/org/hook.go +++ b/routers/api/v1/org/hook.go @@ -78,7 +78,7 @@ func GetHook(ctx *context.APIContext) { apiHook, err := webhook_service.ToHook(ctx.ContextUser.HomeLink(), hook) if err != nil { - ctx.InternalServerError(err) + ctx.APIErrorInternal(err) return } ctx.JSON(http.StatusOK, apiHook) diff --git a/routers/api/v1/org/label.go b/routers/api/v1/org/label.go index 24ee4ed642..b5b70bdc7d 100644 --- a/routers/api/v1/org/label.go +++ b/routers/api/v1/org/label.go @@ -46,13 +46,13 @@ func ListLabels(ctx *context.APIContext) { labels, err := issues_model.GetLabelsByOrgID(ctx, ctx.Org.Organization.ID, ctx.FormString("sort"), utils.GetListOptions(ctx)) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetLabelsByOrgID", err) + ctx.APIErrorInternal(err) return } count, err := issues_model.CountLabelsByOrgID(ctx, ctx.Org.Organization.ID) if err != nil { - ctx.InternalServerError(err) + ctx.APIErrorInternal(err) return } @@ -90,7 +90,7 @@ func CreateLabel(ctx *context.APIContext) { form.Color = strings.Trim(form.Color, " ") color, err := label.NormalizeColor(form.Color) if err != nil { - ctx.Error(http.StatusUnprocessableEntity, "Color", err) + ctx.APIError(http.StatusUnprocessableEntity, err) return } form.Color = color @@ -103,7 +103,7 @@ func CreateLabel(ctx *context.APIContext) { Description: form.Description, } if err := issues_model.NewLabel(ctx, label); err != nil { - ctx.Error(http.StatusInternalServerError, "NewLabel", err) + ctx.APIErrorInternal(err) return } @@ -139,7 +139,7 @@ func GetLabel(ctx *context.APIContext) { label *issues_model.Label err error ) - strID := ctx.PathParam(":id") + strID := ctx.PathParam("id") if intID, err2 := strconv.ParseInt(strID, 10, 64); err2 != nil { label, err = issues_model.GetLabelInOrgByName(ctx, ctx.Org.Organization.ID, strID) } else { @@ -147,9 +147,9 @@ func GetLabel(ctx *context.APIContext) { } if err != nil { if issues_model.IsErrOrgLabelNotExist(err) { - ctx.NotFound() + ctx.APIErrorNotFound() } else { - ctx.Error(http.StatusInternalServerError, "GetLabelByOrgID", err) + ctx.APIErrorInternal(err) } return } @@ -190,12 +190,12 @@ func EditLabel(ctx *context.APIContext) { // "422": // "$ref": "#/responses/validationError" form := web.GetForm(ctx).(*api.EditLabelOption) - l, err := issues_model.GetLabelInOrgByID(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64(":id")) + l, err := issues_model.GetLabelInOrgByID(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64("id")) if err != nil { if issues_model.IsErrOrgLabelNotExist(err) { - ctx.NotFound() + ctx.APIErrorNotFound() } else { - ctx.Error(http.StatusInternalServerError, "GetLabelByRepoID", err) + ctx.APIErrorInternal(err) } return } @@ -209,7 +209,7 @@ func EditLabel(ctx *context.APIContext) { if form.Color != nil { color, err := label.NormalizeColor(*form.Color) if err != nil { - ctx.Error(http.StatusUnprocessableEntity, "Color", err) + ctx.APIError(http.StatusUnprocessableEntity, err) return } l.Color = color @@ -219,7 +219,7 @@ func EditLabel(ctx *context.APIContext) { } l.SetArchived(form.IsArchived != nil && *form.IsArchived) if err := issues_model.UpdateLabel(ctx, l); err != nil { - ctx.Error(http.StatusInternalServerError, "UpdateLabel", err) + ctx.APIErrorInternal(err) return } @@ -249,8 +249,8 @@ func DeleteLabel(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - if err := issues_model.DeleteLabel(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64(":id")); err != nil { - ctx.Error(http.StatusInternalServerError, "DeleteLabel", err) + if err := issues_model.DeleteLabel(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64("id")); err != nil { + ctx.APIErrorInternal(err) return } diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 294d33014d..1c12b0cc94 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -8,6 +8,7 @@ import ( "net/url" "code.gitea.io/gitea/models/organization" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/user" @@ -28,13 +29,13 @@ func listMembers(ctx *context.APIContext, isMember bool) { count, err := organization.CountOrgMembers(ctx, opts) if err != nil { - ctx.InternalServerError(err) + ctx.APIErrorInternal(err) return } members, _, err := organization.FindOrgMembers(ctx, opts) if err != nil { - ctx.InternalServerError(err) + ctx.APIErrorInternal(err) return } @@ -82,7 +83,7 @@ func ListMembers(ctx *context.APIContext) { if ctx.Doer != nil { isMember, err = ctx.Org.Organization.IsOrgMember(ctx, ctx.Doer.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "IsOrgMember", err) + ctx.APIErrorInternal(err) return } } @@ -132,7 +133,7 @@ func IsMember(ctx *context.APIContext) { // required: true // - name: username // in: path - // description: username of the user + // description: username of the user to check for an organization membership // type: string // required: true // responses: @@ -143,27 +144,27 @@ func IsMember(ctx *context.APIContext) { // "404": // description: user is not a member - userToCheck := user.GetUserByParams(ctx) + userToCheck := user.GetContextUserByPathParam(ctx) if ctx.Written() { return } if ctx.Doer != nil { userIsMember, err := ctx.Org.Organization.IsOrgMember(ctx, ctx.Doer.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "IsOrgMember", err) + ctx.APIErrorInternal(err) return } else if userIsMember || ctx.Doer.IsAdmin { userToCheckIsMember, err := ctx.Org.Organization.IsOrgMember(ctx, userToCheck.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "IsOrgMember", err) + ctx.APIErrorInternal(err) } else if userToCheckIsMember { ctx.Status(http.StatusNoContent) } else { - ctx.NotFound() + ctx.APIErrorNotFound() } return } else if ctx.Doer.ID == userToCheck.ID { - ctx.NotFound() + ctx.APIErrorNotFound() return } } @@ -185,7 +186,7 @@ func IsPublicMember(ctx *context.APIContext) { // required: true // - name: username // in: path - // description: username of the user + // description: username of the user to check for a public organization membership // type: string // required: true // responses: @@ -194,19 +195,33 @@ func IsPublicMember(ctx *context.APIContext) { // "404": // description: user is not a public member - userToCheck := user.GetUserByParams(ctx) + userToCheck := user.GetContextUserByPathParam(ctx) if ctx.Written() { return } is, err := organization.IsPublicMembership(ctx, ctx.Org.Organization.ID, userToCheck.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "IsPublicMembership", err) + ctx.APIErrorInternal(err) return } if is { ctx.Status(http.StatusNoContent) } else { - ctx.NotFound() + ctx.APIErrorNotFound() + } +} + +func checkCanChangeOrgUserStatus(ctx *context.APIContext, targetUser *user_model.User) { + // allow user themselves to change their status, and allow admins to change any user + if targetUser.ID == ctx.Doer.ID || ctx.Doer.IsAdmin { + return + } + // allow org owners to change status of members + isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID) + if err != nil { + ctx.APIError(http.StatusInternalServerError, err) + } else if !isOwner { + ctx.APIError(http.StatusForbidden, "Cannot change member visibility") } } @@ -225,7 +240,7 @@ func PublicizeMember(ctx *context.APIContext) { // required: true // - name: username // in: path - // description: username of the user + // description: username of the user whose membership is to be publicized // type: string // required: true // responses: @@ -236,17 +251,17 @@ func PublicizeMember(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - userToPublicize := user.GetUserByParams(ctx) + userToPublicize := user.GetContextUserByPathParam(ctx) if ctx.Written() { return } - if userToPublicize.ID != ctx.Doer.ID { - ctx.Error(http.StatusForbidden, "", "Cannot publicize another member") + checkCanChangeOrgUserStatus(ctx, userToPublicize) + if ctx.Written() { return } err := organization.ChangeOrgUserStatus(ctx, ctx.Org.Organization.ID, userToPublicize.ID, true) if err != nil { - ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err) + ctx.APIErrorInternal(err) return } ctx.Status(http.StatusNoContent) @@ -267,7 +282,7 @@ func ConcealMember(ctx *context.APIContext) { // required: true // - name: username // in: path - // description: username of the user + // description: username of the user whose membership is to be concealed // type: string // required: true // responses: @@ -278,17 +293,17 @@ func ConcealMember(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - userToConceal := user.GetUserByParams(ctx) + userToConceal := user.GetContextUserByPathParam(ctx) if ctx.Written() { return } - if userToConceal.ID != ctx.Doer.ID { - ctx.Error(http.StatusForbidden, "", "Cannot conceal another member") + checkCanChangeOrgUserStatus(ctx, userToConceal) + if ctx.Written() { return } err := organization.ChangeOrgUserStatus(ctx, ctx.Org.Organization.ID, userToConceal.ID, false) if err != nil { - ctx.Error(http.StatusInternalServerError, "ChangeOrgUserStatus", err) + ctx.APIErrorInternal(err) return } ctx.Status(http.StatusNoContent) @@ -309,7 +324,7 @@ func DeleteMember(ctx *context.APIContext) { // required: true // - name: username // in: path - // description: username of the user + // description: username of the user to remove from the organization // type: string // required: true // responses: @@ -318,12 +333,12 @@ func DeleteMember(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - member := user.GetUserByParams(ctx) + member := user.GetContextUserByPathParam(ctx) if ctx.Written() { return } if err := org_service.RemoveOrgUser(ctx, ctx.Org.Organization, member); err != nil { - ctx.Error(http.StatusInternalServerError, "RemoveOrgUser", err) + ctx.APIErrorInternal(err) } ctx.Status(http.StatusNoContent) } diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index 3fb653bcb6..cd67686065 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -26,16 +26,14 @@ import ( func listUserOrgs(ctx *context.APIContext, u *user_model.User) { listOptions := utils.GetListOptions(ctx) - showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == u.ID) - opts := organization.FindOrgOptions{ - ListOptions: listOptions, - UserID: u.ID, - IncludePrivate: showPrivate, + ListOptions: listOptions, + UserID: u.ID, + IncludeVisibility: organization.DoerViewOtherVisibility(ctx.Doer, u), } orgs, maxResults, err := db.FindAndCount[organization.Organization](ctx, opts) if err != nil { - ctx.Error(http.StatusInternalServerError, "db.FindAndCount[organization.Organization]", err) + ctx.APIErrorInternal(err) return } @@ -84,7 +82,7 @@ func ListUserOrgs(ctx *context.APIContext) { // parameters: // - name: username // in: path - // description: username of user + // description: username of the user whose organizations are to be listed // type: string // required: true // - name: page @@ -114,7 +112,7 @@ func GetUserOrgsPermissions(ctx *context.APIContext) { // parameters: // - name: username // in: path - // description: username of user + // description: username of the user whose permissions are to be obtained // type: string // required: true // - name: org @@ -131,21 +129,21 @@ func GetUserOrgsPermissions(ctx *context.APIContext) { // "$ref": "#/responses/notFound" var o *user_model.User - if o = user.GetUserByParamsName(ctx, ":org"); o == nil { + if o = user.GetUserByPathParam(ctx, "org"); o == nil { return } op := api.OrganizationPermissions{} if !organization.HasOrgOrUserVisible(ctx, o, ctx.ContextUser) { - ctx.NotFound("HasOrgOrUserVisible", nil) + ctx.APIErrorNotFound("HasOrgOrUserVisible", nil) return } org := organization.OrgFromUser(o) authorizeLevel, err := org.GetOrgUserMaxAuthorizeLevel(ctx, ctx.ContextUser.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetOrgUserAuthorizeLevel", err) + ctx.APIErrorInternal(err) return } @@ -164,7 +162,7 @@ func GetUserOrgsPermissions(ctx *context.APIContext) { op.CanCreateRepository, err = org.CanCreateOrgRepo(ctx, ctx.ContextUser.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "CanCreateOrgRepo", err) + ctx.APIErrorInternal(err) return } @@ -201,7 +199,7 @@ func GetAll(ctx *context.APIContext) { listOptions := utils.GetListOptions(ctx) - publicOrgs, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ + publicOrgs, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{ Actor: ctx.Doer, ListOptions: listOptions, Type: user_model.UserTypeOrganization, @@ -209,7 +207,7 @@ func GetAll(ctx *context.APIContext) { Visible: vMode, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "SearchOrganizations", err) + ctx.APIErrorInternal(err) return } orgs := make([]*api.Organization, len(publicOrgs)) @@ -245,7 +243,7 @@ func Create(ctx *context.APIContext) { // "$ref": "#/responses/validationError" form := web.GetForm(ctx).(*api.CreateOrgOption) if !ctx.Doer.CanCreateOrganization() { - ctx.Error(http.StatusForbidden, "Create organization not allowed", nil) + ctx.APIError(http.StatusForbidden, nil) return } @@ -271,9 +269,9 @@ func Create(ctx *context.APIContext) { db.IsErrNameReserved(err) || db.IsErrNameCharsNotAllowed(err) || db.IsErrNamePatternNotAllowed(err) { - ctx.Error(http.StatusUnprocessableEntity, "", err) + ctx.APIError(http.StatusUnprocessableEntity, err) } else { - ctx.Error(http.StatusInternalServerError, "CreateOrganization", err) + ctx.APIErrorInternal(err) } return } @@ -301,7 +299,7 @@ func Get(ctx *context.APIContext) { // "$ref": "#/responses/notFound" if !organization.HasOrgOrUserVisible(ctx, ctx.Org.Organization.AsUser(), ctx.Doer) { - ctx.NotFound("HasOrgOrUserVisible", nil) + ctx.APIErrorNotFound("HasOrgOrUserVisible", nil) return } @@ -315,6 +313,44 @@ func Get(ctx *context.APIContext) { ctx.JSON(http.StatusOK, org) } +func Rename(ctx *context.APIContext) { + // swagger:operation POST /orgs/{org}/rename organization renameOrg + // --- + // summary: Rename an organization + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: existing org name + // type: string + // required: true + // - name: body + // in: body + // required: true + // schema: + // "$ref": "#/definitions/RenameOrgOption" + // responses: + // "204": + // "$ref": "#/responses/empty" + // "403": + // "$ref": "#/responses/forbidden" + // "422": + // "$ref": "#/responses/validationError" + + form := web.GetForm(ctx).(*api.RenameOrgOption) + orgUser := ctx.Org.Organization.AsUser() + if err := user_service.RenameUser(ctx, orgUser, form.NewName); err != nil { + if user_model.IsErrUserAlreadyExist(err) || db.IsErrNameReserved(err) || db.IsErrNamePatternNotAllowed(err) || db.IsErrNameCharsNotAllowed(err) { + ctx.APIError(http.StatusUnprocessableEntity, err) + } else { + ctx.APIErrorInternal(err) + } + return + } + ctx.Status(http.StatusNoContent) +} + // Edit change an organization's information func Edit(ctx *context.APIContext) { // swagger:operation PATCH /orgs/{org} organization orgEdit @@ -345,7 +381,7 @@ func Edit(ctx *context.APIContext) { if form.Email != "" { if err := user_service.ReplacePrimaryEmailAddress(ctx, ctx.Org.Organization.AsUser(), form.Email); err != nil { - ctx.Error(http.StatusInternalServerError, "ReplacePrimaryEmailAddress", err) + ctx.APIErrorInternal(err) return } } @@ -355,11 +391,11 @@ func Edit(ctx *context.APIContext) { Description: optional.Some(form.Description), Website: optional.Some(form.Website), Location: optional.Some(form.Location), - Visibility: optional.FromNonDefault(api.VisibilityModes[form.Visibility]), + Visibility: optional.FromMapLookup(api.VisibilityModes, form.Visibility), RepoAdminChangeTeamAccess: optional.FromPtr(form.RepoAdminChangeTeamAccess), } if err := user_service.UpdateUser(ctx, ctx.Org.Organization.AsUser(), opts); err != nil { - ctx.Error(http.StatusInternalServerError, "UpdateUser", err) + ctx.APIErrorInternal(err) return } @@ -386,7 +422,7 @@ func Delete(ctx *context.APIContext) { // "$ref": "#/responses/notFound" if err := org.DeleteOrganization(ctx, ctx.Org.Organization, false); err != nil { - ctx.Error(http.StatusInternalServerError, "DeleteOrganization", err) + ctx.APIErrorInternal(err) return } ctx.Status(http.StatusNoContent) @@ -431,7 +467,7 @@ func ListOrgActivityFeeds(ctx *context.APIContext) { org := organization.OrgFromUser(ctx.ContextUser) isMember, err := org.IsOrgMember(ctx, ctx.Doer.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "IsOrgMember", err) + ctx.APIErrorInternal(err) return } includePrivate = isMember @@ -450,7 +486,7 @@ func ListOrgActivityFeeds(ctx *context.APIContext) { feeds, count, err := feed_service.GetFeeds(ctx, opts) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetFeeds", err) + ctx.APIErrorInternal(err) return } ctx.SetTotalCountHeader(count) diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 8164d2cfe9..1a1710750a 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -59,13 +59,13 @@ func ListTeams(ctx *context.APIContext) { OrgID: ctx.Org.Organization.ID, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "LoadTeams", err) + ctx.APIErrorInternal(err) return } apiTeams, err := convert.ToTeams(ctx, teams, false) if err != nil { - ctx.Error(http.StatusInternalServerError, "ConvertToTeams", err) + ctx.APIErrorInternal(err) return } @@ -98,13 +98,13 @@ func ListUserTeams(ctx *context.APIContext) { UserID: ctx.Doer.ID, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetUserTeams", err) + ctx.APIErrorInternal(err) return } apiTeams, err := convert.ToTeams(ctx, teams, true) if err != nil { - ctx.Error(http.StatusInternalServerError, "ConvertToTeams", err) + ctx.APIErrorInternal(err) return } @@ -134,33 +134,25 @@ func GetTeam(ctx *context.APIContext) { apiTeam, err := convert.ToTeam(ctx, ctx.Org.Team, true) if err != nil { - ctx.InternalServerError(err) + ctx.APIErrorInternal(err) return } ctx.JSON(http.StatusOK, apiTeam) } -func attachTeamUnits(team *organization.Team, units []string) { +func attachTeamUnits(team *organization.Team, defaultAccessMode perm.AccessMode, units []string) { unitTypes, _ := unit_model.FindUnitTypes(units...) team.Units = make([]*organization.TeamUnit, 0, len(units)) for _, tp := range unitTypes { team.Units = append(team.Units, &organization.TeamUnit{ OrgID: team.OrgID, Type: tp, - AccessMode: team.AccessMode, + AccessMode: defaultAccessMode, }) } } -func convertUnitsMap(unitsMap map[string]string) map[unit_model.Type]perm.AccessMode { - res := make(map[unit_model.Type]perm.AccessMode, len(unitsMap)) - for unitKey, p := range unitsMap { - res[unit_model.TypeFromKey(unitKey)] = perm.ParseAccessMode(p) - } - return res -} - func attachTeamUnitsMap(team *organization.Team, unitsMap map[string]string) { team.Units = make([]*organization.TeamUnit, 0, len(unitsMap)) for unitKey, p := range unitsMap { @@ -214,26 +206,24 @@ func CreateTeam(ctx *context.APIContext) { // "422": // "$ref": "#/responses/validationError" form := web.GetForm(ctx).(*api.CreateTeamOption) - p := perm.ParseAccessMode(form.Permission) - if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 { - p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap)) - } + teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin) team := &organization.Team{ OrgID: ctx.Org.Organization.ID, Name: form.Name, Description: form.Description, IncludesAllRepositories: form.IncludesAllRepositories, CanCreateOrgRepo: form.CanCreateOrgRepo, - AccessMode: p, + AccessMode: teamPermission, } if team.AccessMode < perm.AccessModeAdmin { if len(form.UnitsMap) > 0 { attachTeamUnitsMap(team, form.UnitsMap) } else if len(form.Units) > 0 { - attachTeamUnits(team, form.Units) + unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite) + attachTeamUnits(team, unitPerm, form.Units) } else { - ctx.Error(http.StatusInternalServerError, "getTeamUnits", errors.New("units permission should not be empty")) + ctx.APIErrorInternal(errors.New("units permission should not be empty")) return } } else { @@ -242,16 +232,16 @@ func CreateTeam(ctx *context.APIContext) { if err := org_service.NewTeam(ctx, team); err != nil { if organization.IsErrTeamAlreadyExist(err) { - ctx.Error(http.StatusUnprocessableEntity, "", err) + ctx.APIError(http.StatusUnprocessableEntity, err) } else { - ctx.Error(http.StatusInternalServerError, "NewTeam", err) + ctx.APIErrorInternal(err) } return } apiTeam, err := convert.ToTeam(ctx, team, true) if err != nil { - ctx.InternalServerError(err) + ctx.APIErrorInternal(err) return } ctx.JSON(http.StatusCreated, apiTeam) @@ -285,7 +275,7 @@ func EditTeam(ctx *context.APIContext) { form := web.GetForm(ctx).(*api.EditTeamOption) team := ctx.Org.Team if err := team.LoadUnits(ctx); err != nil { - ctx.InternalServerError(err) + ctx.APIErrorInternal(err) return } @@ -304,15 +294,10 @@ func EditTeam(ctx *context.APIContext) { isAuthChanged := false isIncludeAllChanged := false if !team.IsOwnerTeam() && len(form.Permission) != 0 { - // Validate permission level. - p := perm.ParseAccessMode(form.Permission) - if p < perm.AccessModeAdmin && len(form.UnitsMap) > 0 { - p = unit_model.MinUnitAccessMode(convertUnitsMap(form.UnitsMap)) - } - - if team.AccessMode != p { + teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin) + if team.AccessMode != teamPermission { isAuthChanged = true - team.AccessMode = p + team.AccessMode = teamPermission } if form.IncludesAllRepositories != nil { @@ -325,20 +310,21 @@ func EditTeam(ctx *context.APIContext) { if len(form.UnitsMap) > 0 { attachTeamUnitsMap(team, form.UnitsMap) } else if len(form.Units) > 0 { - attachTeamUnits(team, form.Units) + unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite) + attachTeamUnits(team, unitPerm, form.Units) } } else { attachAdminTeamUnits(team) } if err := org_service.UpdateTeam(ctx, team, isAuthChanged, isIncludeAllChanged); err != nil { - ctx.Error(http.StatusInternalServerError, "EditTeam", err) + ctx.APIErrorInternal(err) return } apiTeam, err := convert.ToTeam(ctx, team) if err != nil { - ctx.InternalServerError(err) + ctx.APIErrorInternal(err) return } ctx.JSON(http.StatusOK, apiTeam) @@ -363,7 +349,7 @@ func DeleteTeam(ctx *context.APIContext) { // "$ref": "#/responses/notFound" if err := org_service.DeleteTeam(ctx, ctx.Org.Team); err != nil { - ctx.Error(http.StatusInternalServerError, "DeleteTeam", err) + ctx.APIErrorInternal(err) return } ctx.Status(http.StatusNoContent) @@ -399,10 +385,10 @@ func GetTeamMembers(ctx *context.APIContext) { isMember, err := organization.IsOrganizationMember(ctx, ctx.Org.Team.OrgID, ctx.Doer.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "IsOrganizationMember", err) + ctx.APIErrorInternal(err) return } else if !isMember && !ctx.Doer.IsAdmin { - ctx.NotFound() + ctx.APIErrorNotFound() return } @@ -411,7 +397,7 @@ func GetTeamMembers(ctx *context.APIContext) { TeamID: ctx.Org.Team.ID, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetTeamMembers", err) + ctx.APIErrorInternal(err) return } @@ -440,7 +426,7 @@ func GetTeamMember(ctx *context.APIContext) { // required: true // - name: username // in: path - // description: username of the member to list + // description: username of the user whose data is to be listed // type: string // required: true // responses: @@ -449,17 +435,17 @@ func GetTeamMember(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - u := user.GetUserByParams(ctx) + u := user.GetContextUserByPathParam(ctx) if ctx.Written() { return } teamID := ctx.PathParamInt64("teamid") isTeamMember, err := organization.IsUserInTeams(ctx, u.ID, []int64{teamID}) if err != nil { - ctx.Error(http.StatusInternalServerError, "IsUserInTeams", err) + ctx.APIErrorInternal(err) return } else if !isTeamMember { - ctx.NotFound() + ctx.APIErrorNotFound() return } ctx.JSON(http.StatusOK, convert.ToUser(ctx, u, ctx.Doer)) @@ -481,7 +467,7 @@ func AddTeamMember(ctx *context.APIContext) { // required: true // - name: username // in: path - // description: username of the user to add + // description: username of the user to add to a team // type: string // required: true // responses: @@ -492,15 +478,15 @@ func AddTeamMember(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - u := user.GetUserByParams(ctx) + u := user.GetContextUserByPathParam(ctx) if ctx.Written() { return } if err := org_service.AddTeamMember(ctx, ctx.Org.Team, u); err != nil { if errors.Is(err, user_model.ErrBlockedUser) { - ctx.Error(http.StatusForbidden, "AddTeamMember", err) + ctx.APIError(http.StatusForbidden, err) } else { - ctx.Error(http.StatusInternalServerError, "AddTeamMember", err) + ctx.APIErrorInternal(err) } return } @@ -523,7 +509,7 @@ func RemoveTeamMember(ctx *context.APIContext) { // required: true // - name: username // in: path - // description: username of the user to remove + // description: username of the user to remove from a team // type: string // required: true // responses: @@ -532,13 +518,13 @@ func RemoveTeamMember(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - u := user.GetUserByParams(ctx) + u := user.GetContextUserByPathParam(ctx) if ctx.Written() { return } if err := org_service.RemoveTeamMember(ctx, ctx.Org.Team, u); err != nil { - ctx.Error(http.StatusInternalServerError, "RemoveTeamMember", err) + ctx.APIErrorInternal(err) return } ctx.Status(http.StatusNoContent) @@ -578,14 +564,14 @@ func GetTeamRepos(ctx *context.APIContext) { TeamID: team.ID, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetTeamRepositories", err) + ctx.APIErrorInternal(err) return } repos := make([]*api.Repository, len(teamRepos)) for i, repo := range teamRepos { permission, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) + ctx.APIErrorInternal(err) return } repos[i] = convert.ToRepo(ctx, repo, permission) @@ -630,13 +616,13 @@ func GetTeamRepo(ctx *context.APIContext) { } if !organization.HasTeamRepo(ctx, ctx.Org.Team.OrgID, ctx.Org.Team.ID, repo.ID) { - ctx.NotFound() + ctx.APIErrorNotFound() return } permission, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetTeamRepos", err) + ctx.APIErrorInternal(err) return } @@ -645,12 +631,12 @@ func GetTeamRepo(ctx *context.APIContext) { // getRepositoryByParams get repository by a team's organization ID and repo name func getRepositoryByParams(ctx *context.APIContext) *repo_model.Repository { - repo, err := repo_model.GetRepositoryByName(ctx, ctx.Org.Team.OrgID, ctx.PathParam(":reponame")) + repo, err := repo_model.GetRepositoryByName(ctx, ctx.Org.Team.OrgID, ctx.PathParam("reponame")) if err != nil { if repo_model.IsErrRepoNotExist(err) { - ctx.NotFound() + ctx.APIErrorNotFound() } else { - ctx.Error(http.StatusInternalServerError, "GetRepositoryByName", err) + ctx.APIErrorInternal(err) } return nil } @@ -694,14 +680,14 @@ func AddTeamRepository(ctx *context.APIContext) { return } if access, err := access_model.AccessLevel(ctx, ctx.Doer, repo); err != nil { - ctx.Error(http.StatusInternalServerError, "AccessLevel", err) + ctx.APIErrorInternal(err) return } else if access < perm.AccessModeAdmin { - ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository") + ctx.APIError(http.StatusForbidden, "Must have admin-level access to the repository") return } if err := repo_service.TeamAddRepository(ctx, ctx.Org.Team, repo); err != nil { - ctx.Error(http.StatusInternalServerError, "TeamAddRepository", err) + ctx.APIErrorInternal(err) return } ctx.Status(http.StatusNoContent) @@ -746,14 +732,14 @@ func RemoveTeamRepository(ctx *context.APIContext) { return } if access, err := access_model.AccessLevel(ctx, ctx.Doer, repo); err != nil { - ctx.Error(http.StatusInternalServerError, "AccessLevel", err) + ctx.APIErrorInternal(err) return } else if access < perm.AccessModeAdmin { - ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository") + ctx.APIError(http.StatusForbidden, "Must have admin-level access to the repository") return } if err := repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, repo.ID); err != nil { - ctx.Error(http.StatusInternalServerError, "RemoveRepository", err) + ctx.APIErrorInternal(err) return } ctx.Status(http.StatusNoContent) @@ -829,7 +815,7 @@ func SearchTeam(ctx *context.APIContext) { apiTeams, err := convert.ToTeams(ctx, teams, false) if err != nil { - ctx.InternalServerError(err) + ctx.APIErrorInternal(err) return } @@ -885,7 +871,7 @@ func ListTeamActivityFeeds(ctx *context.APIContext) { feeds, count, err := feed_service.GetFeeds(ctx, opts) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetFeeds", err) + ctx.APIErrorInternal(err) return } ctx.SetTotalCountHeader(count) |