package api import ( "database/sql" "net/http" ) // teamOIDCGroups is one team's own OIDC binding: which group, if any, grants // member access and which grants owner access. The same shape answers GET and // is accepted by PUT. An empty string means no group grants that role here. type teamOIDCGroups struct { MemberGroup string `json:"member_group"` OwnerGroup string `json:"owner_group"` } // handleGetTeamOIDCGroups answers which groups control a team's membership. // Member-gated like the member list itself: this is part of "who is in the // team and why", not a setting only an owner should be able to see. func handleGetTeamOIDCGroups(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { teamID, ok := teamParam(w, r) if !ok { return } if !requireTeamMember(w, r, teamID) { return } var g teamOIDCGroups err := db.QueryRowContext(r.Context(), "SELECT COALESCE(oidc_member_group, ''), COALESCE(oidc_owner_group, '') FROM teams WHERE id = $1", teamID).Scan(&g.MemberGroup, &g.OwnerGroup) if err != nil { respond(w, http.StatusInternalServerError, errResp("internal error")) return } respond(w, http.StatusOK, g) } } // handleSetTeamOIDCGroups sets which groups control a team's membership. // // Owner-gated, the same as the schedule, the integrations and the escalation // ladder: this decides who can end up in the team, which is exactly the kind // of thing only the team's own owner (or an administrator repairing it) should // be able to change. An empty string clears a binding. func handleSetTeamOIDCGroups(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { teamID, ok := teamParam(w, r) if !ok { return } if !requireTeamOwner(w, r, teamID) { return } var req teamOIDCGroups if err := decodeJSON(r, &req); err != nil { respond(w, http.StatusBadRequest, errResp("invalid request body")) return } if _, err := db.ExecContext(r.Context(), ` UPDATE teams SET oidc_member_group = NULLIF($1, ''), oidc_owner_group = NULLIF($2, '') WHERE id = $3`, req.MemberGroup, req.OwnerGroup, teamID); err != nil { respond(w, http.StatusInternalServerError, errResp("internal error")) return } w.WriteHeader(http.StatusNoContent) } }