Teams: Add apis for team membership (#92204)
* Add TeamBinding resource * Implement read api:s for TeamBindings
This commit is contained in:
@@ -121,6 +121,30 @@ var SSOSettingResourceInfo = common.NewResourceInfo(
|
||||
},
|
||||
)
|
||||
|
||||
var TeamBindingResourceInfo = common.NewResourceInfo(
|
||||
GROUP, VERSION, "teambindings", "teambinding", "TeamBinding",
|
||||
func() runtime.Object { return &TeamBinding{} },
|
||||
func() runtime.Object { return &TeamBindingList{} },
|
||||
utils.TableColumns{
|
||||
Definition: []metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Team", Type: "string"},
|
||||
{Name: "Created At", Type: "string", Format: "date"},
|
||||
},
|
||||
Reader: func(obj any) ([]interface{}, error) {
|
||||
m, ok := obj.(*TeamBinding)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected team binding")
|
||||
}
|
||||
return []interface{}{
|
||||
m.Name,
|
||||
m.Spec.TeamRef.Name,
|
||||
m.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
var (
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
SchemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}
|
||||
@@ -144,6 +168,8 @@ func AddKnownTypes(scheme *runtime.Scheme, version string) {
|
||||
&IdentityDisplayResults{},
|
||||
&SSOSetting{},
|
||||
&SSOSettingList{},
|
||||
&TeamBinding{},
|
||||
&TeamBindingList{},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package v0alpha1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type Team struct {
|
||||
@@ -9,6 +11,7 @@ type Team struct {
|
||||
|
||||
Spec TeamSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
type TeamSpec struct {
|
||||
Title string `json:"name,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
@@ -21,3 +24,47 @@ type TeamList struct {
|
||||
|
||||
Items []Team `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type TeamBinding struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec TeamBindingSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
type TeamBindingSpec struct {
|
||||
Subjects []TeamSubject `json:"subjects,omitempty"`
|
||||
TeamRef TeamRef `json:"teamRef,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type TeamBindingList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []TeamBinding `json:"items,omitempty"`
|
||||
}
|
||||
|
||||
type TeamSubject struct {
|
||||
// Name is the unique identifier for subject.
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// Permission subject has in permission.
|
||||
// Can be either admin or member.
|
||||
Permission TeamPermission `json:"permission,omitempty"`
|
||||
}
|
||||
|
||||
type TeamRef struct {
|
||||
// Name is the unique identifier for a team.
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// TeamPermission for subject
|
||||
// +enum
|
||||
type TeamPermission string
|
||||
|
||||
const (
|
||||
TeamPermissionAdmin TeamPermission = "admin"
|
||||
TeamPermissionMember TeamPermission = "member"
|
||||
)
|
||||
|
||||
@@ -247,6 +247,88 @@ func (in *Team) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TeamBinding) DeepCopyInto(out *TeamBinding) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeamBinding.
|
||||
func (in *TeamBinding) DeepCopy() *TeamBinding {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TeamBinding)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *TeamBinding) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TeamBindingList) DeepCopyInto(out *TeamBindingList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]TeamBinding, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeamBindingList.
|
||||
func (in *TeamBindingList) DeepCopy() *TeamBindingList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TeamBindingList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *TeamBindingList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TeamBindingSpec) DeepCopyInto(out *TeamBindingSpec) {
|
||||
*out = *in
|
||||
if in.Subjects != nil {
|
||||
in, out := &in.Subjects, &out.Subjects
|
||||
*out = make([]TeamSubject, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
out.TeamRef = in.TeamRef
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeamBindingSpec.
|
||||
func (in *TeamBindingSpec) DeepCopy() *TeamBindingSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TeamBindingSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TeamList) DeepCopyInto(out *TeamList) {
|
||||
*out = *in
|
||||
@@ -280,6 +362,22 @@ func (in *TeamList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TeamRef) DeepCopyInto(out *TeamRef) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeamRef.
|
||||
func (in *TeamRef) DeepCopy() *TeamRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TeamRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TeamSpec) DeepCopyInto(out *TeamSpec) {
|
||||
*out = *in
|
||||
@@ -296,6 +394,22 @@ func (in *TeamSpec) DeepCopy() *TeamSpec {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TeamSubject) DeepCopyInto(out *TeamSubject) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TeamSubject.
|
||||
func (in *TeamSubject) DeepCopy() *TeamSubject {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TeamSubject)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *User) DeepCopyInto(out *User) {
|
||||
*out = *in
|
||||
|
||||
@@ -23,8 +23,13 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.ServiceAccountList": schema_pkg_apis_identity_v0alpha1_ServiceAccountList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.ServiceAccountSpec": schema_pkg_apis_identity_v0alpha1_ServiceAccountSpec(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.Team": schema_pkg_apis_identity_v0alpha1_Team(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamBinding": schema_pkg_apis_identity_v0alpha1_TeamBinding(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamBindingList": schema_pkg_apis_identity_v0alpha1_TeamBindingList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamBindingSpec": schema_pkg_apis_identity_v0alpha1_TeamBindingSpec(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamList": schema_pkg_apis_identity_v0alpha1_TeamList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamRef": schema_pkg_apis_identity_v0alpha1_TeamRef(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamSpec": schema_pkg_apis_identity_v0alpha1_TeamSpec(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamSubject": schema_pkg_apis_identity_v0alpha1_TeamSubject(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.User": schema_pkg_apis_identity_v0alpha1_User(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.UserList": schema_pkg_apis_identity_v0alpha1_UserList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.UserSpec": schema_pkg_apis_identity_v0alpha1_UserSpec(ref),
|
||||
@@ -449,6 +454,126 @@ func schema_pkg_apis_identity_v0alpha1_Team(ref common.ReferenceCallback) common
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_identity_v0alpha1_TeamBinding(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamBindingSpec"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamBindingSpec", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_identity_v0alpha1_TeamBindingList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
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/pkg/apis/identity/v0alpha1.TeamBinding"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamBinding", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_identity_v0alpha1_TeamBindingSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"subjects": {
|
||||
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/pkg/apis/identity/v0alpha1.TeamSubject"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"teamRef": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamRef"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamRef", "github.com/grafana/grafana/pkg/apis/identity/v0alpha1.TeamSubject"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_identity_v0alpha1_TeamList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
@@ -496,6 +621,25 @@ func schema_pkg_apis_identity_v0alpha1_TeamList(ref common.ReferenceCallback) co
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_identity_v0alpha1_TeamRef(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"name": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Name is the unique identifier for a team.",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_identity_v0alpha1_TeamSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
@@ -520,6 +664,33 @@ func schema_pkg_apis_identity_v0alpha1_TeamSpec(ref common.ReferenceCallback) co
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_identity_v0alpha1_TeamSubject(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"name": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Name is the unique identifier for subject.",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"permission": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Permission subject has in permission. Can be either admin or member.\n\nPossible enum values:\n - `\"admin\"`\n - `\"member\"`",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
Enum: []interface{}{"admin", "member"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_identity_v0alpha1_User(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/identity/v0alpha1,TeamBindingSpec,Subjects
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/identity/v0alpha1,IdentityDisplay,IdentityType
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/identity/v0alpha1,IdentityDisplay,InternalID
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/identity/v0alpha1,TeamSpec,Title
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
)
|
||||
|
||||
// GetContinueID is a helper to parse options.Continue as int64.
|
||||
// If no continue token is provided 0 is returned.
|
||||
func GetContinueID(options *internalversion.ListOptions) (int64, error) {
|
||||
if options.Continue != "" {
|
||||
continueID, err := strconv.ParseInt(options.Continue, 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid continue token: %w", err)
|
||||
}
|
||||
return continueID, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// OptonalFormatInt formats num as a string. If num is less or equal than 0
|
||||
// an empty string is returned.
|
||||
func OptionalFormatInt(num int64) string {
|
||||
if num > 0 {
|
||||
return strconv.FormatInt(num, 10)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -16,16 +16,16 @@ var (
|
||||
_ LegacyIdentityStore = (*legacySQLStore)(nil)
|
||||
)
|
||||
|
||||
type legacySQLStore struct {
|
||||
sql legacysql.LegacyDatabaseProvider
|
||||
}
|
||||
|
||||
func NewLegacySQLStores(sql legacysql.LegacyDatabaseProvider) LegacyIdentityStore {
|
||||
return &legacySQLStore{
|
||||
sql: sql,
|
||||
}
|
||||
}
|
||||
|
||||
type legacySQLStore struct {
|
||||
sql legacysql.LegacyDatabaseProvider
|
||||
}
|
||||
|
||||
// ListTeams implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) ListTeams(ctx context.Context, ns claims.NamespaceInfo, query ListTeamQuery) (*ListTeamResult, error) {
|
||||
if query.Limit < 1 {
|
||||
@@ -51,8 +51,6 @@ func (s *legacySQLStore) ListTeams(ctx context.Context, ns claims.NamespaceInfo,
|
||||
}
|
||||
q := rawQuery
|
||||
|
||||
// fmt.Printf("%s // %v\n", rawQuery, req.GetArgs())
|
||||
|
||||
res := &ListTeamResult{}
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||
defer func() {
|
||||
@@ -126,13 +124,10 @@ func (s *legacySQLStore) GetDisplay(ctx context.Context, ns claims.NamespaceInfo
|
||||
}
|
||||
|
||||
func (s *legacySQLStore) queryUsers(ctx context.Context, sql *legacysql.LegacyDatabaseHelper, t *template.Template, req sqltemplate.Args, limit int) (*ListUserResult, error) {
|
||||
rawQuery, err := sqltemplate.Execute(t, req)
|
||||
q, err := sqltemplate.Execute(t, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlQueryUsers.Name(), err)
|
||||
}
|
||||
q := rawQuery
|
||||
|
||||
// fmt.Printf("%s // %v\n", rawQuery, req.GetArgs())
|
||||
|
||||
res := &ListUserResult{}
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||
@@ -163,6 +158,83 @@ func (s *legacySQLStore) queryUsers(ctx context.Context, sql *legacysql.LegacyDa
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ListTeamsBindings implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) ListTeamBindings(ctx context.Context, ns claims.NamespaceInfo, query ListTeamBindingsQuery) (*ListTeamBindingsResult, error) {
|
||||
if query.Limit < 1 {
|
||||
query.Limit = 50
|
||||
}
|
||||
|
||||
limit := int(query.Limit)
|
||||
query.Limit += 1 // for continue
|
||||
query.OrgID = ns.OrgID
|
||||
if query.OrgID == 0 {
|
||||
return nil, fmt.Errorf("expected non zero orgID")
|
||||
}
|
||||
|
||||
sql, err := s.sql(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := newListTeamBindings(sql, &query)
|
||||
q, err := sqltemplate.Execute(sqlQueryTeamBindings, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("execute template %q: %w", sqlQueryTeams.Name(), err)
|
||||
}
|
||||
|
||||
rows, err := sql.DB.GetSqlxSession().Query(ctx, q, req.GetArgs()...)
|
||||
defer func() {
|
||||
if rows != nil {
|
||||
_ = rows.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &ListTeamBindingsResult{}
|
||||
grouped := map[string][]TeamMember{}
|
||||
|
||||
var lastID int64
|
||||
var atTeamLimit bool
|
||||
|
||||
for rows.Next() {
|
||||
m := TeamMember{}
|
||||
err = rows.Scan(&m.ID, &m.TeamUID, &m.TeamID, &m.UserUID, &m.Created, &m.Updated, &m.Permission)
|
||||
if err != nil {
|
||||
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}
|
||||
}
|
||||
|
||||
if len(grouped) >= limit {
|
||||
atTeamLimit = true
|
||||
res.ContinueID = lastID
|
||||
}
|
||||
}
|
||||
|
||||
if query.UID == "" {
|
||||
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
|
||||
}
|
||||
|
||||
// GetUserTeams implements LegacyIdentityStore.
|
||||
func (s *legacySQLStore) GetUserTeams(ctx context.Context, ns claims.NamespaceInfo, uid string) ([]team.Team, error) {
|
||||
panic("unimplemented")
|
||||
|
||||
@@ -26,9 +26,10 @@ func mustTemplate(filename string) *template.Template {
|
||||
|
||||
// Templates.
|
||||
var (
|
||||
sqlQueryTeams = mustTemplate("query_teams.sql")
|
||||
sqlQueryUsers = mustTemplate("query_users.sql")
|
||||
sqlQueryDisplay = mustTemplate("query_display.sql")
|
||||
sqlQueryTeams = mustTemplate("query_teams.sql")
|
||||
sqlQueryUsers = mustTemplate("query_users.sql")
|
||||
sqlQueryDisplay = mustTemplate("query_display.sql")
|
||||
sqlQueryTeamBindings = mustTemplate("query_team_bindings.sql")
|
||||
)
|
||||
|
||||
type sqlQueryListUsers struct {
|
||||
@@ -88,3 +89,25 @@ func newGetDisplay(sql *legacysql.LegacyDatabaseHelper, q *GetUserDisplayQuery)
|
||||
func (r sqlQueryGetDisplay) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
type sqlQueryListTeamBindings struct {
|
||||
sqltemplate.SQLTemplate
|
||||
Query *ListTeamBindingsQuery
|
||||
UserTable string
|
||||
TeamTable string
|
||||
TeamMemberTable string
|
||||
}
|
||||
|
||||
func (r sqlQueryListTeamBindings) Validate() error {
|
||||
return nil // TODO
|
||||
}
|
||||
|
||||
func newListTeamBindings(sql *legacysql.LegacyDatabaseHelper, q *ListTeamBindingsQuery) sqlQueryListTeamBindings {
|
||||
return sqlQueryListTeamBindings{
|
||||
SQLTemplate: sqltemplate.New(sql.DialectForDriver()),
|
||||
UserTable: sql.Table("user"),
|
||||
TeamTable: sql.Table("team"),
|
||||
TeamMemberTable: sql.Table("team_member"),
|
||||
Query: q,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,12 @@ func TestQueries(t *testing.T) {
|
||||
return &v
|
||||
}
|
||||
|
||||
listTeamBindings := func(q *ListTeamBindingsQuery) sqltemplate.SQLTemplate {
|
||||
v := newListTeamBindings(nodb, q)
|
||||
v.SQLTemplate = mocks.NewTestingSQLTemplate()
|
||||
return &v
|
||||
}
|
||||
|
||||
mocks.CheckQuerySnapshots(t, mocks.TemplateTestSetup{
|
||||
RootDir: "testdata",
|
||||
Templates: map[*template.Template][]mocks.TemplateTestCase{
|
||||
@@ -104,6 +110,30 @@ func TestQueries(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
},
|
||||
sqlQueryTeamBindings: {
|
||||
{
|
||||
Name: "team_1_bindings",
|
||||
Data: listTeamBindings(&ListTeamBindingsQuery{
|
||||
OrgID: 1,
|
||||
UID: "team-1",
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "team_bindings_page_1",
|
||||
Data: listTeamBindings(&ListTeamBindingsQuery{
|
||||
OrgID: 1,
|
||||
Limit: 5,
|
||||
}),
|
||||
},
|
||||
{
|
||||
Name: "team_bindings_page_2",
|
||||
Data: listTeamBindings(&ListTeamBindingsQuery{
|
||||
OrgID: 1,
|
||||
Limit: 5,
|
||||
ContinueID: 5,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission 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.ContinueID }}
|
||||
WHERE t.id >= {{ .Arg .Query.ContinueID }}
|
||||
{{ end }}
|
||||
ORDER BY t.id ASC LIMIT {{ .Arg .Query.Limit }}
|
||||
)
|
||||
{{ end }}
|
||||
AND tm.org_id = {{ .Arg .Query.OrgID}}
|
||||
ORDER BY t.id ASC;
|
||||
Vendored
Executable
+7
@@ -0,0 +1,7 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission 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
|
||||
ORDER BY t.id ASC;
|
||||
Vendored
Executable
+11
@@ -0,0 +1,11 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission 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
|
||||
ORDER BY t.id ASC;
|
||||
Vendored
Executable
+12
@@ -0,0 +1,12 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission 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 >= 5
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
ORDER BY t.id ASC;
|
||||
Vendored
Executable
+7
@@ -0,0 +1,7 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission 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
|
||||
ORDER BY t.id ASC;
|
||||
Vendored
Executable
+11
@@ -0,0 +1,11 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission 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
|
||||
ORDER BY t.id ASC;
|
||||
Vendored
Executable
+12
@@ -0,0 +1,12 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission 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 >= 5
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
ORDER BY t.id ASC;
|
||||
Vendored
Executable
+7
@@ -0,0 +1,7 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission 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
|
||||
ORDER BY t.id ASC;
|
||||
Vendored
Executable
+11
@@ -0,0 +1,11 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission 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
|
||||
ORDER BY t.id ASC;
|
||||
Vendored
Executable
+12
@@ -0,0 +1,12 @@
|
||||
SELECT tm.id as id, t.uid as team_uid, t.id as team_id, u.uid as user_uid, tm.created, tm.updated, tm.permission 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 >= 5
|
||||
ORDER BY t.id ASC LIMIT 5
|
||||
)
|
||||
AND tm.org_id = 1
|
||||
ORDER BY t.id ASC;
|
||||
@@ -2,8 +2,10 @@ package legacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
@@ -22,6 +24,12 @@ type ListUserResult struct {
|
||||
RV int64
|
||||
}
|
||||
|
||||
type GetUserDisplayQuery struct {
|
||||
OrgID int64
|
||||
UIDs []string
|
||||
IDs []int64
|
||||
}
|
||||
|
||||
type ListTeamQuery struct {
|
||||
OrgID int64
|
||||
UID string
|
||||
@@ -35,16 +43,46 @@ type ListTeamResult struct {
|
||||
RV int64
|
||||
}
|
||||
|
||||
type GetUserDisplayQuery struct {
|
||||
OrgID int64
|
||||
UIDs []string
|
||||
IDs []int64
|
||||
type TeamMember struct {
|
||||
ID int64
|
||||
TeamID int64
|
||||
TeamUID string
|
||||
UserUID string
|
||||
Updated time.Time
|
||||
Created time.Time
|
||||
Permission team.PermissionType
|
||||
}
|
||||
|
||||
func (m TeamMember) MemberID() string {
|
||||
return identity.NewTypedIDString(claims.TypeUser, m.UserUID)
|
||||
}
|
||||
|
||||
type TeamBinding struct {
|
||||
TeamUID string
|
||||
Members []TeamMember
|
||||
}
|
||||
|
||||
type ListTeamBindingsQuery struct {
|
||||
// UID is team uid to list bindings for. If not set store should list bindings for all teams
|
||||
UID string
|
||||
OrgID int64
|
||||
ContinueID int64 // ContinueID
|
||||
Limit int64
|
||||
}
|
||||
|
||||
type ListTeamBindingsResult struct {
|
||||
Bindings []TeamBinding
|
||||
ContinueID int64
|
||||
RV int64
|
||||
}
|
||||
|
||||
// In every case, RBAC should be applied before calling, or before returning results to the requester
|
||||
type LegacyIdentityStore interface {
|
||||
ListUsers(ctx context.Context, ns claims.NamespaceInfo, query ListUserQuery) (*ListUserResult, error)
|
||||
ListTeams(ctx context.Context, ns claims.NamespaceInfo, query ListTeamQuery) (*ListTeamResult, error)
|
||||
GetDisplay(ctx context.Context, ns claims.NamespaceInfo, query GetUserDisplayQuery) (*ListUserResult, error)
|
||||
|
||||
ListTeams(ctx context.Context, ns claims.NamespaceInfo, query ListTeamQuery) (*ListTeamResult, error)
|
||||
ListTeamBindings(ctx context.Context, ns claims.NamespaceInfo, query ListTeamBindingsQuery) (*ListTeamBindingsResult, error)
|
||||
|
||||
GetUserTeams(ctx context.Context, ns claims.NamespaceInfo, uid string) ([]team.Team, error)
|
||||
}
|
||||
|
||||
@@ -83,6 +83,9 @@ func (b *IdentityAPIBuilder) GetAPIGroupInfo(
|
||||
teamResource := identityv0.TeamResourceInfo
|
||||
storage[teamResource.StoragePath()] = team.NewLegacyStore(b.Store)
|
||||
|
||||
teamBindingResource := identityv0.TeamBindingResourceInfo
|
||||
storage[teamBindingResource.StoragePath()] = team.NewLegacyBindingStore(b.Store)
|
||||
|
||||
userResource := identityv0.UserResourceInfo
|
||||
storage[userResource.StoragePath()] = user.NewLegacyStore(b.Store)
|
||||
storage[userResource.StoragePath("teams")] = team.NewLegacyUserTeamsStore(b.Store)
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
package team
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
"github.com/grafana/authlib/claims"
|
||||
identityv0 "github.com/grafana/grafana/pkg/apis/identity/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/common"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/identity/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
)
|
||||
|
||||
var bindingResource = identityv0.TeamBindingResourceInfo
|
||||
|
||||
var (
|
||||
_ rest.Storage = (*LegacyBindingStore)(nil)
|
||||
_ rest.Scoper = (*LegacyBindingStore)(nil)
|
||||
_ rest.SingularNameProvider = (*LegacyBindingStore)(nil)
|
||||
_ rest.Getter = (*LegacyBindingStore)(nil)
|
||||
_ rest.Lister = (*LegacyBindingStore)(nil)
|
||||
)
|
||||
|
||||
func NewLegacyBindingStore(store legacy.LegacyIdentityStore) *LegacyBindingStore {
|
||||
return &LegacyBindingStore{store}
|
||||
}
|
||||
|
||||
type LegacyBindingStore struct {
|
||||
store legacy.LegacyIdentityStore
|
||||
}
|
||||
|
||||
// Destroy implements rest.Storage.
|
||||
func (l *LegacyBindingStore) Destroy() {}
|
||||
|
||||
// New implements rest.Storage.
|
||||
func (l *LegacyBindingStore) New() runtime.Object {
|
||||
return bindingResource.NewFunc()
|
||||
}
|
||||
|
||||
// NewList implements rest.Lister.
|
||||
func (l *LegacyBindingStore) NewList() runtime.Object {
|
||||
return bindingResource.NewListFunc()
|
||||
}
|
||||
|
||||
// NamespaceScoped implements rest.Scoper.
|
||||
func (l *LegacyBindingStore) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// GetSingularName implements rest.SingularNameProvider.
|
||||
func (l *LegacyBindingStore) GetSingularName() string {
|
||||
return bindingResource.GetSingularName()
|
||||
}
|
||||
|
||||
// ConvertToTable implements rest.Lister.
|
||||
func (l *LegacyBindingStore) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||
return bindingResource.TableConverter().ConvertToTable(ctx, object, tableOptions)
|
||||
}
|
||||
|
||||
// Get implements rest.Getter.
|
||||
func (l *LegacyBindingStore) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
ns, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := l.store.ListTeamBindings(ctx, ns, legacy.ListTeamBindingsQuery{
|
||||
UID: name,
|
||||
Limit: 1,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(res.Bindings) != 1 {
|
||||
// FIXME: maybe empty result?
|
||||
return nil, resource.NewNotFound(name)
|
||||
}
|
||||
|
||||
obj := mapToBindingObject(ns, res.Bindings[0])
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
// List implements rest.Lister.
|
||||
func (l *LegacyBindingStore) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
ns, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
continueID, err := common.GetContinueID(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := l.store.ListTeamBindings(ctx, ns, legacy.ListTeamBindingsQuery{
|
||||
ContinueID: continueID,
|
||||
Limit: options.Limit,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := identityv0.TeamBindingList{
|
||||
Items: make([]identityv0.TeamBinding, 0, len(res.Bindings)),
|
||||
}
|
||||
|
||||
for _, b := range res.Bindings {
|
||||
list.Items = append(list.Items, mapToBindingObject(ns, b))
|
||||
}
|
||||
|
||||
list.ListMeta.Continue = common.OptionalFormatInt(res.ContinueID)
|
||||
list.ListMeta.ResourceVersion = common.OptionalFormatInt(res.RV)
|
||||
|
||||
return &list, nil
|
||||
}
|
||||
|
||||
func mapToBindingObject(ns claims.NamespaceInfo, b legacy.TeamBinding) identityv0.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
|
||||
}
|
||||
}
|
||||
|
||||
return identityv0.TeamBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: b.TeamUID,
|
||||
Namespace: ns.Value,
|
||||
ResourceVersion: strconv.FormatInt(rv.UnixMilli(), 10),
|
||||
CreationTimestamp: metav1.NewTime(ct),
|
||||
},
|
||||
Spec: identityv0.TeamBindingSpec{
|
||||
TeamRef: identityv0.TeamRef{
|
||||
Name: b.TeamUID,
|
||||
},
|
||||
Subjects: mapToSubjects(b.Members),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func mapToSubjects(members []legacy.TeamMember) []identityv0.TeamSubject {
|
||||
out := make([]identityv0.TeamSubject, 0, len(members))
|
||||
for _, m := range members {
|
||||
out = append(out, identityv0.TeamSubject{
|
||||
Name: m.MemberID(),
|
||||
Permission: mapPermisson(m.Permission),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mapPermisson(p team.PermissionType) identityv0.TeamPermission {
|
||||
if p == team.PermissionTypeAdmin {
|
||||
return identityv0.TeamPermissionAdmin
|
||||
} else {
|
||||
return identityv0.TeamPermissionMember
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user