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 ¶
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 ¶
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 ¶
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" )
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.