team

package
v0.2.0-alpha.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package team owns what a team's roles may do.

The decision is here, and only here, because it has two enforcers: the HTTP guard refuses a request before a handler runs, and the team service refuses a command whatever called it. Defence in depth is deliberate; two definitions of the same rule were not, and the one nobody remembered to update would have been the permissive one.

The role values, the Team it belongs to, and the store contract are in team.go beside it.

Index

Constants

View Source
const (
	// RoleOwner is the initial role for the user who creates a team.
	RoleOwner = "owner"
	// RoleAdmin can manage shared automation assets but not membership ownership.
	RoleAdmin = "admin"
	// RoleMember is the basic collaboration role for invited members.
	RoleMember = "member"
	// DefaultPersonalName is the initial UX-facing name for a user's own space.
	DefaultPersonalName = "My Space"
)

Variables

This section is empty.

Functions

func Allows

func Allows(role string, action Action) bool

Allows reports whether a member holding role may perform action.

It answers about a stated role. An unknown role and an unknown action are both refused: a caller with neither has not been given permission, and defaulting either to true would make a typo an escalation. An empty role is not stated, so it is refused here too — EffectiveRole is what turns a stored row into the role to ask about.

func EffectiveRole

func EffectiveRole(role string) string

EffectiveRole is what a membership row's stored role means.

A row with no role is a member. The row is what says somebody belongs to the team; the role only says how much they may do, and the least of the three is what a row that never got one has been given. Reading it as "not a member" instead would make a data defect look like an absent membership, and reading it as anything higher would let a missing value grant something.

Callers normalize before asking Allows, which answers about a stated role.

Types

type Action

type Action string

An Action is something a caller wants to do to a team, named at the coarseness the role rules actually distinguish. It is not one action per route: several routes share a permission, and naming the permission rather than the route is what stops a new route from arriving without one.

const (
	// ActionManageTeamMembers covers adding and removing members. Granting
	// ownership is not part of it: the service refuses any role but member, so
	// an escalation cannot look like a routine invitation.
	ActionManageTeamMembers Action = "manage_team_members"
	ActionManageAgents      Action = "manage_agents"
	ActionManageWorkflows   Action = "manage_workflows"
	// ActionAssignIssueWorkflow is assigning work to a workflow, which is a
	// change to what the team automates rather than a use of it.
	ActionAssignIssueWorkflow Action = "assign_issue_workflow"
	ActionRunWorkflow         Action = "run_workflow"
	ActionReadAuditTrail      Action = "read_audit_trail"
	ActionCommentIssue        Action = "comment_issue"
	// ActionModerateIssueComments covers deleting a comment the caller did not
	// write. Editing another author's comment is permitted to nobody, so it is
	// not an action here — see internal/service/issue.
	ActionModerateIssueComments Action = "moderate_issue_comments"
)

func Actions

func Actions() []Action

Actions returns every action, so a test can prove the matrix covers each one rather than only the ones somebody remembered.

type Member

type Member struct {
	TeamID    string    `json:"team_id"`
	UserID    string    `json:"user_id"`
	Role      string    `json:"role"`
	CreatedAt time.Time `json:"created_at"`
}

Member is one user's membership in a team.

type Store

type Store interface {
	// GetTeam returns the team by team_id, or (nil, nil) when not found.
	GetTeam(ctx context.Context, teamID string) (*Team, error)
	// GetPersonalTeamByUser returns the default personal team for the user, or (nil, nil) when not found.
	GetPersonalTeamByUser(ctx context.Context, userID string) (*Team, error)
	// ListTeamsByUser returns all teams the user belongs to, ordered by created_at ASC.
	ListTeamsByUser(ctx context.Context, userID string) ([]Team, error)
	// CreateTeam creates a new team and owner membership.
	CreateTeam(ctx context.Context, name, createdBy, quotaTier string) (*Team, error)
	// AddTeamMember adds or updates a team membership.
	AddTeamMember(ctx context.Context, teamID, userID, role string) (*Member, error)
	// RemoveTeamMember removes one membership from a team.
	RemoveTeamMember(ctx context.Context, teamID, userID string) error
	// ListTeamMembers returns members of the team ordered by created_at ASC.
	ListTeamMembers(ctx context.Context, teamID string) ([]Member, error)
	// ListAllTeams returns every team newest first, with the total count. A
	// non-empty query filters on name as a substring.
	//
	// It is the one method here that ignores membership, so only
	// deployment-scoped callers may reach it. It returns teams, never their
	// contents: an administrator learns that a team exists and how large it is,
	// not what is in it.
	ListAllTeams(ctx context.Context, query string, limit, offset int) ([]Team, int, error)
	// CountTeamMembers returns member counts for the given teams, keyed by
	// team id. It exists so listing teams is two queries rather than one per
	// row.
	CountTeamMembers(ctx context.Context, teamIDs []string) (map[string]int, error)
	// SetTeamPluginCuration records who fills the team's plugin activation
	// list, or returns ErrNotFound. The value is validated above this layer.
	SetTeamPluginCuration(ctx context.Context, teamID string, mode coreplugin.Curation) error
}

Store provides team persistence and membership lookup.

type Team

type Team struct {
	ID                string  `json:"id"`
	Name              string  `json:"name"`
	PersonalForUserID *string `json:"personal_for_user_id,omitempty"`
	QuotaTier         string  `json:"quota_tier,omitempty"`
	// PluginCuration is who fills this team's plugin activation list; empty
	// reads as plugin.CurationOpen. See core/plugin/activation.go.
	PluginCuration coreplugin.Curation `json:"plugin_curation,omitempty"`
	CreatedBy      string              `json:"created_by"`
	CreatedAt      time.Time           `json:"created_at"`
	UpdatedAt      time.Time           `json:"updated_at"`
}

Team is the ownership and collaboration boundary for working resources. A user's default personal team is represented by personal_for_user_id.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL