IAM: Refactor team bindings to store one pair of team and member (#111871)
* store one pair of team member in team bindings object * generate queries for sql unit tests * remove TeamBinding struct
This commit is contained in:
@@ -4,12 +4,13 @@ TeamBindingSpec: {
|
||||
#Subject: {
|
||||
// uid of the identity
|
||||
name: string
|
||||
// permission of the identity in the team
|
||||
permission: TeamPermission
|
||||
}
|
||||
|
||||
subjects: [...#Subject]
|
||||
subject: #Subject
|
||||
teamRef: TeamRef
|
||||
|
||||
// permission of the identity in the team
|
||||
permission: TeamPermission
|
||||
}
|
||||
|
||||
TeamRef:{
|
||||
|
||||
@@ -6,8 +6,6 @@ package v0alpha1
|
||||
type TeamBindingspecSubject struct {
|
||||
// uid of the identity
|
||||
Name string `json:"name"`
|
||||
// permission of the identity in the team
|
||||
Permission TeamBindingTeamPermission `json:"permission"`
|
||||
}
|
||||
|
||||
// NewTeamBindingspecSubject creates a new TeamBindingspecSubject object.
|
||||
@@ -15,14 +13,6 @@ func NewTeamBindingspecSubject() *TeamBindingspecSubject {
|
||||
return &TeamBindingspecSubject{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type TeamBindingTeamPermission string
|
||||
|
||||
const (
|
||||
TeamBindingTeamPermissionAdmin TeamBindingTeamPermission = "admin"
|
||||
TeamBindingTeamPermissionMember TeamBindingTeamPermission = "member"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type TeamBindingTeamRef struct {
|
||||
// Name is the unique identifier for a team.
|
||||
@@ -34,16 +24,26 @@ func NewTeamBindingTeamRef() *TeamBindingTeamRef {
|
||||
return &TeamBindingTeamRef{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type TeamBindingTeamPermission string
|
||||
|
||||
const (
|
||||
TeamBindingTeamPermissionAdmin TeamBindingTeamPermission = "admin"
|
||||
TeamBindingTeamPermissionMember TeamBindingTeamPermission = "member"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type TeamBindingSpec struct {
|
||||
Subjects []TeamBindingspecSubject `json:"subjects"`
|
||||
TeamRef TeamBindingTeamRef `json:"teamRef"`
|
||||
Subject TeamBindingspecSubject `json:"subject"`
|
||||
TeamRef TeamBindingTeamRef `json:"teamRef"`
|
||||
// permission of the identity in the team
|
||||
Permission TeamBindingTeamPermission `json:"permission"`
|
||||
}
|
||||
|
||||
// NewTeamBindingSpec creates a new TeamBindingSpec object.
|
||||
func NewTeamBindingSpec() *TeamBindingSpec {
|
||||
return &TeamBindingSpec{
|
||||
Subjects: []TeamBindingspecSubject{},
|
||||
TeamRef: *NewTeamBindingTeamRef(),
|
||||
Subject: *NewTeamBindingspecSubject(),
|
||||
TeamRef: *NewTeamBindingTeamRef(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2103,17 +2103,10 @@ func schema_pkg_apis_iam_v0alpha1_TeamBindingSpec(ref common.ReferenceCallback)
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"subjects": {
|
||||
"subject": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingspecSubject"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingspecSubject"),
|
||||
},
|
||||
},
|
||||
"teamRef": {
|
||||
@@ -2122,8 +2115,16 @@ func schema_pkg_apis_iam_v0alpha1_TeamBindingSpec(ref common.ReferenceCallback)
|
||||
Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingTeamRef"),
|
||||
},
|
||||
},
|
||||
"permission": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "permission of the identity in the team",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"subjects", "teamRef"},
|
||||
Required: []string{"subject", "teamRef", "permission"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
@@ -2210,16 +2211,8 @@ func schema_pkg_apis_iam_v0alpha1_TeamBindingspecSubject(ref common.ReferenceCal
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"permission": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "permission of the identity in the team",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"name", "permission"},
|
||||
Required: []string{"name"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ type ListTeamBindingsQuery struct {
|
||||
}
|
||||
|
||||
type ListTeamBindingsResult struct {
|
||||
Bindings []TeamBinding
|
||||
Bindings []TeamMember
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
@@ -445,11 +445,6 @@ func (m TeamMember) MemberID() string {
|
||||
return claims.NewTypeID(claims.TypeUser, m.UserUID)
|
||||
}
|
||||
|
||||
type TeamBinding struct {
|
||||
TeamUID string
|
||||
Members []TeamMember
|
||||
}
|
||||
|
||||
var sqlQueryTeamBindingsTemplate = mustTemplate("team_bindings_query.sql")
|
||||
|
||||
type listTeamBindingsQuery struct {
|
||||
@@ -505,11 +500,11 @@ func (s *legacySQLStore) ListTeamBindings(ctx context.Context, ns claims.Namespa
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &ListTeamBindingsResult{}
|
||||
grouped := map[string][]TeamMember{}
|
||||
res := &ListTeamBindingsResult{
|
||||
Bindings: make([]TeamMember, 0, int(query.Pagination.Limit)),
|
||||
}
|
||||
|
||||
var lastID int64
|
||||
var atTeamLimit bool
|
||||
|
||||
for rows.Next() {
|
||||
m := TeamMember{}
|
||||
@@ -518,16 +513,11 @@ func (s *legacySQLStore) ListTeamBindings(ctx context.Context, ns claims.Namespa
|
||||
return res, err
|
||||
}
|
||||
|
||||
lastID = m.TeamID
|
||||
members, ok := grouped[m.TeamUID]
|
||||
if ok {
|
||||
grouped[m.TeamUID] = append(members, m)
|
||||
} else if !atTeamLimit {
|
||||
grouped[m.TeamUID] = []TeamMember{m}
|
||||
}
|
||||
res.Bindings = append(res.Bindings, m)
|
||||
|
||||
if len(grouped) >= int(query.Pagination.Limit)-1 {
|
||||
atTeamLimit = true
|
||||
lastID = m.ID
|
||||
|
||||
if len(res.Bindings) >= int(query.Pagination.Limit)-1 {
|
||||
res.Continue = lastID
|
||||
}
|
||||
}
|
||||
@@ -536,14 +526,6 @@ func (s *legacySQLStore) ListTeamBindings(ctx context.Context, ns claims.Namespa
|
||||
res.RV, err = sql.GetResourceVersion(ctx, "team_member", "updated")
|
||||
}
|
||||
|
||||
res.Bindings = make([]TeamBinding, 0, len(grouped))
|
||||
for uid, members := range grouped {
|
||||
res.Bindings = append(res.Bindings, TeamBinding{
|
||||
TeamUID: uid,
|
||||
Members: members,
|
||||
})
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
|
||||
@@ -3,18 +3,13 @@ FROM {{ .Ident .TeamMemberTable }} tm
|
||||
INNER JOIN {{ .Ident .TeamTable }} t ON tm.team_id = t.id
|
||||
INNER JOIN {{ .Ident .UserTable }} u ON tm.user_id = u.id
|
||||
WHERE
|
||||
{{ if .Query.UID }}
|
||||
t.uid = {{ .Arg .Query.UID }}
|
||||
{{ else }}
|
||||
t.uid IN(
|
||||
SELECT uid
|
||||
FROM {{ .Ident .TeamTable }} t
|
||||
{{ if .Query.Pagination.Continue }}
|
||||
WHERE t.id >= {{ .Arg .Query.Pagination.Continue }}
|
||||
{{ end }}
|
||||
ORDER BY t.id ASC LIMIT {{ .Arg .Query.Pagination.Limit }}
|
||||
)
|
||||
{{ end }}
|
||||
AND tm.org_id = {{ .Arg .Query.OrgID}}
|
||||
tm.org_id = {{ .Arg .Query.OrgID}}
|
||||
{{ if .Query.UID }}
|
||||
AND t.uid = {{ .Arg .Query.UID }}
|
||||
{{ end }}
|
||||
{{- if .Query.Pagination.Continue }}
|
||||
AND tm.id >= {{ .Arg .Query.Pagination.Continue }}
|
||||
{{- end }}
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
ORDER BY t.id ASC
|
||||
LIMIT {{ .Arg .Query.Pagination.Limit }};
|
||||
|
||||
+4
-3
@@ -3,7 +3,8 @@ FROM `grafana`.`team_member` tm
|
||||
INNER JOIN `grafana`.`team` t ON tm.team_id = t.id
|
||||
INNER JOIN `grafana`.`user` u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.org_id = 1
|
||||
tm.org_id = 1
|
||||
AND t.uid = 'team-1'
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
||||
|
||||
+3
-7
@@ -3,11 +3,7 @@ FROM `grafana`.`team_member` tm
|
||||
INNER JOIN `grafana`.`team` t ON tm.team_id = t.id
|
||||
INNER JOIN `grafana`.`user` u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid IN(
|
||||
SELECT uid
|
||||
FROM `grafana`.`team` t
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 5;
|
||||
|
||||
+4
-8
@@ -3,12 +3,8 @@ FROM `grafana`.`team_member` tm
|
||||
INNER JOIN `grafana`.`team` t ON tm.team_id = t.id
|
||||
INNER JOIN `grafana`.`user` u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid IN(
|
||||
SELECT uid
|
||||
FROM `grafana`.`team` t
|
||||
WHERE t.id >= 2
|
||||
ORDER BY t.id ASC LIMIT 1
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
tm.org_id = 1
|
||||
AND tm.id >= 2
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
||||
|
||||
+4
-3
@@ -3,7 +3,8 @@ FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.org_id = 1
|
||||
tm.org_id = 1
|
||||
AND t.uid = 'team-1'
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
||||
|
||||
Vendored
+3
-7
@@ -3,11 +3,7 @@ FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid IN(
|
||||
SELECT uid
|
||||
FROM "grafana"."team" t
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 5;
|
||||
|
||||
Vendored
+4
-8
@@ -3,12 +3,8 @@ FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid IN(
|
||||
SELECT uid
|
||||
FROM "grafana"."team" t
|
||||
WHERE t.id >= 2
|
||||
ORDER BY t.id ASC LIMIT 1
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
tm.org_id = 1
|
||||
AND tm.id >= 2
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
||||
|
||||
+4
-3
@@ -3,7 +3,8 @@ FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid = 'team-1'
|
||||
AND tm.org_id = 1
|
||||
tm.org_id = 1
|
||||
AND t.uid = 'team-1'
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
||||
|
||||
+3
-7
@@ -3,11 +3,7 @@ FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid IN(
|
||||
SELECT uid
|
||||
FROM "grafana"."team" t
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
tm.org_id = 1
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 5;
|
||||
|
||||
+4
-8
@@ -3,12 +3,8 @@ FROM "grafana"."team_member" tm
|
||||
INNER JOIN "grafana"."team" t ON tm.team_id = t.id
|
||||
INNER JOIN "grafana"."user" u ON tm.user_id = u.id
|
||||
WHERE
|
||||
t.uid IN(
|
||||
SELECT uid
|
||||
FROM "grafana"."team" t
|
||||
WHERE t.id >= 2
|
||||
ORDER BY t.id ASC LIMIT 1
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
tm.org_id = 1
|
||||
AND tm.id >= 2
|
||||
AND NOT tm.external
|
||||
ORDER BY t.id ASC;
|
||||
ORDER BY t.id ASC
|
||||
LIMIT 1;
|
||||
|
||||
@@ -117,46 +117,36 @@ func (l *LegacyBindingStore) List(ctx context.Context, options *internalversion.
|
||||
return &list, nil
|
||||
}
|
||||
|
||||
func mapToBindingObject(ns claims.NamespaceInfo, b legacy.TeamBinding) iamv0alpha1.TeamBinding {
|
||||
func mapToBindingObject(ns claims.NamespaceInfo, tm legacy.TeamMember) iamv0alpha1.TeamBinding {
|
||||
rv := time.Time{}
|
||||
ct := time.Now()
|
||||
|
||||
for _, m := range b.Members {
|
||||
if m.Updated.After(rv) {
|
||||
rv = m.Updated
|
||||
}
|
||||
if m.Created.Before(ct) {
|
||||
ct = m.Created
|
||||
}
|
||||
if tm.Updated.After(rv) {
|
||||
rv = tm.Updated
|
||||
}
|
||||
if tm.Created.Before(ct) {
|
||||
ct = tm.Created
|
||||
}
|
||||
|
||||
return iamv0alpha1.TeamBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: b.TeamUID,
|
||||
Name: tm.TeamUID,
|
||||
Namespace: ns.Value,
|
||||
ResourceVersion: strconv.FormatInt(rv.UnixMilli(), 10),
|
||||
CreationTimestamp: metav1.NewTime(ct),
|
||||
},
|
||||
Spec: iamv0alpha1.TeamBindingSpec{
|
||||
TeamRef: iamv0alpha1.TeamBindingTeamRef{
|
||||
Name: b.TeamUID,
|
||||
Name: tm.TeamUID,
|
||||
},
|
||||
Subjects: mapToSubjects(b.Members),
|
||||
Subject: iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: tm.UserUID,
|
||||
},
|
||||
Permission: common.MapTeamPermission(tm.Permission),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func mapToSubjects(members []legacy.TeamMember) []iamv0alpha1.TeamBindingspecSubject {
|
||||
out := make([]iamv0alpha1.TeamBindingspecSubject, 0, len(members))
|
||||
for _, m := range members {
|
||||
out = append(out, iamv0alpha1.TeamBindingspecSubject{
|
||||
Name: m.UserUID,
|
||||
Permission: common.MapTeamPermission(m.Permission),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mapPermisson(p team.PermissionType) iamv0.TeamPermission {
|
||||
if p == team.PermissionTypeAdmin {
|
||||
return iamv0.TeamPermissionAdmin
|
||||
|
||||
@@ -3737,20 +3737,23 @@
|
||||
"com.github.grafana.grafana.apps.iam.pkg.apis.iam.v0alpha1.TeamBindingSpec": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"subjects",
|
||||
"teamRef"
|
||||
"subject",
|
||||
"teamRef",
|
||||
"permission"
|
||||
],
|
||||
"properties": {
|
||||
"subjects": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.iam.pkg.apis.iam.v0alpha1.TeamBindingspecSubject"
|
||||
}
|
||||
]
|
||||
}
|
||||
"permission": {
|
||||
"description": "permission of the identity in the team",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"subject": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.iam.pkg.apis.iam.v0alpha1.TeamBindingspecSubject"
|
||||
}
|
||||
]
|
||||
},
|
||||
"teamRef": {
|
||||
"default": {},
|
||||
@@ -3778,19 +3781,13 @@
|
||||
"com.github.grafana.grafana.apps.iam.pkg.apis.iam.v0alpha1.TeamBindingspecSubject": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"permission"
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "uid of the identity",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"permission": {
|
||||
"description": "permission of the identity in the team",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -5594,15 +5591,18 @@
|
||||
"github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingSpec": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"subjects",
|
||||
"teamRef"
|
||||
"subject",
|
||||
"teamRef",
|
||||
"permission"
|
||||
],
|
||||
"properties": {
|
||||
"subjects": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"default": {}
|
||||
}
|
||||
"permission": {
|
||||
"description": "permission of the identity in the team",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"subject": {
|
||||
"default": {}
|
||||
},
|
||||
"teamRef": {
|
||||
"default": {}
|
||||
@@ -5644,19 +5644,13 @@
|
||||
"github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingspecSubject": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"permission"
|
||||
"name"
|
||||
],
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "uid of the identity",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"permission": {
|
||||
"description": "permission of the identity in the team",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user