mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
fix(org,team): guard the owner team rename, show permission-only team access and tidy member lookups
Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
10 files changed
+124
-12
No files matched your search
@@ -539,6 +539,9 @@ func commandOutputFields(cmd *cobra.Command) []string {
|
||||
case "org":
|
||||
return jsonFields[services.Organization]()
|
||||
case "member":
|
||||
if cmd.Parent().Parent() != nil && cmd.Parent().Parent().Name() == "team" {
|
||||
return jsonFields[services.TeamMember]()
|
||||
}
|
||||
return jsonFields[services.OrganizationMember]()
|
||||
case "asset":
|
||||
return jsonFields[services.ReleaseAsset]()
|
||||
|
||||
@@ -83,6 +83,10 @@ func writeTable[T any](a *application, cmd *cobra.Command, items []T, spec listS
|
||||
}
|
||||
d := a.display()
|
||||
if len(items) == 0 {
|
||||
// A filtered page can be empty while later pages are not.
|
||||
if page.More {
|
||||
return a.morePagesHint(cmd, true, page.Number)
|
||||
}
|
||||
if !d.TTY || spec.Empty == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -282,6 +282,8 @@ func TestWriteTable(t *testing.T) {
|
||||
},
|
||||
{name: "empty message on a terminal", tty: true, page: listPage{Number: 1}, stderr: "no open issues in alice/x\n"},
|
||||
{name: "nothing when empty and piped", page: listPage{Number: 1}},
|
||||
{name: "next-page hint when an empty page has more", tty: true, page: listPage{Number: 1, More: true}, stderr: "more results available; use --page 2\n"},
|
||||
{name: "next-page hint when an empty page has more and piped", page: listPage{Number: 1, More: true}, stderr: "more results available; use --page 2\n"},
|
||||
{name: "JSON", tty: true, json: "number", items: items[:1], page: listPage{Number: 1, More: true}, out: "[\n {\n \"number\": 12\n }\n]\n"},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
@@ -326,7 +326,7 @@ func testTeamReviewRequests(t *testing.T, deps *Dependencies, execute func(...st
|
||||
t.Errorf("team review repository cleanup: %v", err)
|
||||
}
|
||||
})
|
||||
execute("team", "repo", "add", teamID, "-R", repo.FullName)
|
||||
execute("team", "repo", "add", teamID, repo.FullName)
|
||||
execute("branch", "create", "feature", "-R", repo.FullName)
|
||||
deps.Streams.In = strings.NewReader(`{"branch":"feature","content":"dGVzdAo=","message":"Disposable team review change"}`)
|
||||
execute("api", "repos/"+repo.FullName+"/contents/change.txt", "--input", "-")
|
||||
|
||||
@@ -197,13 +197,13 @@ $ tea team view 2 --json units`,
|
||||
return writeView(a, cmd, team, func(team services.Team) terminal.View {
|
||||
var units []string
|
||||
for _, unit := range slices.Concat(services.TeamUnits, services.ExternalTeamUnits) {
|
||||
if access := team.Units[unit]; access != "" && access != "none" {
|
||||
if access := team.UnitLevel(unit); access != "none" {
|
||||
units = append(units, strings.TrimPrefix(unit, "repo.")+": "+access)
|
||||
}
|
||||
}
|
||||
memberNames := ""
|
||||
if !membersHidden {
|
||||
memberNames = listedNames(members, func(member services.OrganizationMember) string { return member.Username })
|
||||
memberNames = listedNames(members, func(member services.TeamMember) string { return member.Username })
|
||||
}
|
||||
repositories := listedNames(repos, func(repo services.Repository) string { return repo.FullName })
|
||||
if team.AllRepositories {
|
||||
@@ -425,6 +425,10 @@ $ tea team edit acme/developers --name devs --description "Core developers"`,
|
||||
if opts.Units != nil && !opts.ReplaceUnits && (team.Permission == "admin" || team.Permission == "owner") {
|
||||
return clierr.Wrap(clierr.Usage, fmt.Errorf("--unit cannot restrict the %s access of team %s; pass --permission read or write, or --unit-replace", team.Permission, team.Slug()))
|
||||
}
|
||||
// Gitea finds the owner team by its name, so renaming it leaves the organization without owners.
|
||||
if team.Permission == "owner" && opts.Name != nil && !strings.EqualFold(*opts.Name, team.Name) {
|
||||
return clierr.Wrap(clierr.Usage, fmt.Errorf("team %s is the organization's owner team and cannot be renamed", team.Slug()))
|
||||
}
|
||||
edited, err := service.Edit(cmd.Context(), team.ID, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -502,9 +506,9 @@ also makes them a member of the organization.`}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeTable(a, cmd, members.Items, listSpec[services.OrganizationMember]{
|
||||
return writeTable(a, cmd, members.Items, listSpec[services.TeamMember]{
|
||||
Columns: []terminal.Column{{Header: "Username"}, {Header: "Name", Flex: true}},
|
||||
Row: func(member services.OrganizationMember) []terminal.Cell {
|
||||
Row: func(member services.TeamMember) []terminal.Cell {
|
||||
return []terminal.Cell{{Text: member.Username, Link: webURL(target, member.Username), Bold: true}, {Text: member.FullName}}
|
||||
},
|
||||
Context: "members of team " + team.Slug(), Empty: "no members in team " + team.Slug(),
|
||||
@@ -543,7 +547,7 @@ $ tea team member add 2 bob carol`,
|
||||
Annotations: map[string]string{"tea.text": "team membership changes do not return a resource", "tea.confirm": "true", "tea.arguments": teamArguments},
|
||||
ValidArgsFunction: a.completeTeamThen(func(ctx context.Context, t target, team services.Team) ([]cobra.Completion, error) {
|
||||
members, err := services.Teams{Client: t.client}.Members(ctx, team.ID, completionLimit, 1)
|
||||
return describe(members.Items, func(member services.OrganizationMember) (string, string) { return member.Username, member.FullName }), err
|
||||
return describe(members.Items, func(member services.TeamMember) (string, string) { return member.Username, member.FullName }), err
|
||||
}),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
_, service, team, err := a.team(cmd, args[0])
|
||||
|
||||
@@ -81,6 +81,24 @@ func TestTeamEditMergesUnitsIntoTheCurrentPolicy(t *testing.T) {
|
||||
require.Len(t, sent, 2)
|
||||
}
|
||||
|
||||
func TestTeamEditRefusesToRenameTheOwnerTeam(t *testing.T) {
|
||||
var patched int
|
||||
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodPatch {
|
||||
patched++
|
||||
}
|
||||
_, _ = w.Write([]byte(`{"id":3,"name":"Owners","permission":"owner","organization":{"username":"acme"}}`))
|
||||
}))
|
||||
defer server.Close()
|
||||
deps, _, _ := serverDeps(t, server.URL)
|
||||
err := Execute(context.Background(), deps, []string{"team", "edit", "3", "--name", "Admins"})
|
||||
require.Equal(t, clierr.Usage, clierr.ExitCode(err), "%v", err)
|
||||
require.ErrorContains(t, err, "owner team and cannot be renamed")
|
||||
require.Zero(t, patched)
|
||||
require.NoError(t, Execute(context.Background(), deps, []string{"team", "edit", "3", "--description", "Owners", "--json"}))
|
||||
require.Equal(t, 1, patched)
|
||||
}
|
||||
|
||||
func TestTeamRepoAddTakesRepositoriesAsArguments(t *testing.T) {
|
||||
var granted []string
|
||||
server := newAuthenticatedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in new issue
Block a user