IAM: Add role to User (#111256)
* codegen fix * Return user role from the legacy store * Lint * Add tests, gen openapi * make generate * revert go.mod, go.sum, go.work.sum changes * Update go.mod and go.sum
This commit is contained in:
+2
-2
@@ -338,7 +338,7 @@ require (
|
||||
github.com/sethvargo/go-retry v0.3.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
@@ -433,7 +433,7 @@ require (
|
||||
gopkg.in/mail.v2 v2.3.1 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/src-d/go-errors.v1 v1.0.0 // indirect
|
||||
gopkg.in/telebot.v3 v3.2.1 // indirect
|
||||
gopkg.in/telebot.v3 v3.3.8 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.34.1 // indirect
|
||||
|
||||
+4
-4
@@ -1217,8 +1217,8 @@ github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+D
|
||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
|
||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvqWEUH6SjNiu7VhSjuVFTFiTcphaLU=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 h1:OfRzdxCzDhp+rsKWXuOO2I/quKMJ/+TQwVbIP/gltZg=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92/go.mod h1:7/OT02F6S6I7v6WXb+IjhMuZEYfH/RJ5RwEWnEo5BMg=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
@@ -2058,8 +2058,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/src-d/go-errors.v1 v1.0.0 h1:cooGdZnCjYbeS1zb1s6pVAAimTdKceRrpn7aKOnNIfc=
|
||||
gopkg.in/src-d/go-errors.v1 v1.0.0/go.mod h1:q1cBlomlw2FnDBDNGlnh6X0jPihy+QxZfMMNxPCbdYg=
|
||||
gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs=
|
||||
gopkg.in/telebot.v3 v3.2.1/go.mod h1:GJKwwWqp9nSkIVN51eRKU78aB5f5OnQuWdwiIZfPbko=
|
||||
gopkg.in/telebot.v3 v3.3.8 h1:uVDGjak9l824FN9YARWUHMsiNZnlohAVwUycw21k6t8=
|
||||
gopkg.in/telebot.v3 v3.3.8/go.mod h1:1mlbqcLTVSfK9dx7fdp+Nb5HZsy4LLPtpZTKmwhwtzM=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
@@ -8,6 +8,5 @@ UserSpec: {
|
||||
login: string
|
||||
name: string
|
||||
provisioned: bool
|
||||
|
||||
// What to do with salt, rands and password?
|
||||
role: string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type CoreRoleClient struct {
|
||||
client *resource.TypedClient[*CoreRole, *CoreRoleList]
|
||||
}
|
||||
|
||||
func NewCoreRoleClient(client resource.Client) *CoreRoleClient {
|
||||
return &CoreRoleClient{
|
||||
client: resource.NewTypedClient[*CoreRole, *CoreRoleList](client, CoreRoleKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewCoreRoleClientFromGenerator(generator resource.ClientGenerator) (*CoreRoleClient, error) {
|
||||
c, err := generator.ClientFor(CoreRoleKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewCoreRoleClient(c), nil
|
||||
}
|
||||
|
||||
func (c *CoreRoleClient) Get(ctx context.Context, identifier resource.Identifier) (*CoreRole, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *CoreRoleClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*CoreRoleList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *CoreRoleClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*CoreRoleList, error) {
|
||||
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for resp.GetContinue() != "" {
|
||||
page, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
Continue: resp.GetContinue(),
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.SetContinue(page.GetContinue())
|
||||
resp.SetResourceVersion(page.GetResourceVersion())
|
||||
resp.SetItems(append(resp.GetItems(), page.GetItems()...))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *CoreRoleClient) Create(ctx context.Context, obj *CoreRole, opts resource.CreateOptions) (*CoreRole, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = CoreRoleKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *CoreRoleClient) Update(ctx context.Context, obj *CoreRole, opts resource.UpdateOptions) (*CoreRole, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *CoreRoleClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*CoreRole, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *CoreRoleClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus CoreRoleStatus, opts resource.UpdateOptions) (*CoreRole, error) {
|
||||
return c.client.Update(ctx, &CoreRole{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: CoreRoleKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *CoreRoleClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type GlobalRoleClient struct {
|
||||
client *resource.TypedClient[*GlobalRole, *GlobalRoleList]
|
||||
}
|
||||
|
||||
func NewGlobalRoleClient(client resource.Client) *GlobalRoleClient {
|
||||
return &GlobalRoleClient{
|
||||
client: resource.NewTypedClient[*GlobalRole, *GlobalRoleList](client, GlobalRoleKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewGlobalRoleClientFromGenerator(generator resource.ClientGenerator) (*GlobalRoleClient, error) {
|
||||
c, err := generator.ClientFor(GlobalRoleKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewGlobalRoleClient(c), nil
|
||||
}
|
||||
|
||||
func (c *GlobalRoleClient) Get(ctx context.Context, identifier resource.Identifier) (*GlobalRole, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *GlobalRoleClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*GlobalRoleList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *GlobalRoleClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*GlobalRoleList, error) {
|
||||
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for resp.GetContinue() != "" {
|
||||
page, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
Continue: resp.GetContinue(),
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.SetContinue(page.GetContinue())
|
||||
resp.SetResourceVersion(page.GetResourceVersion())
|
||||
resp.SetItems(append(resp.GetItems(), page.GetItems()...))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *GlobalRoleClient) Create(ctx context.Context, obj *GlobalRole, opts resource.CreateOptions) (*GlobalRole, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = GlobalRoleKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *GlobalRoleClient) Update(ctx context.Context, obj *GlobalRole, opts resource.UpdateOptions) (*GlobalRole, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *GlobalRoleClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*GlobalRole, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *GlobalRoleClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus GlobalRoleStatus, opts resource.UpdateOptions) (*GlobalRole, error) {
|
||||
return c.client.Update(ctx, &GlobalRole{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: GlobalRoleKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *GlobalRoleClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type GlobalRoleBindingClient struct {
|
||||
client *resource.TypedClient[*GlobalRoleBinding, *GlobalRoleBindingList]
|
||||
}
|
||||
|
||||
func NewGlobalRoleBindingClient(client resource.Client) *GlobalRoleBindingClient {
|
||||
return &GlobalRoleBindingClient{
|
||||
client: resource.NewTypedClient[*GlobalRoleBinding, *GlobalRoleBindingList](client, GlobalRoleBindingKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewGlobalRoleBindingClientFromGenerator(generator resource.ClientGenerator) (*GlobalRoleBindingClient, error) {
|
||||
c, err := generator.ClientFor(GlobalRoleBindingKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewGlobalRoleBindingClient(c), nil
|
||||
}
|
||||
|
||||
func (c *GlobalRoleBindingClient) Get(ctx context.Context, identifier resource.Identifier) (*GlobalRoleBinding, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *GlobalRoleBindingClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*GlobalRoleBindingList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *GlobalRoleBindingClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*GlobalRoleBindingList, error) {
|
||||
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for resp.GetContinue() != "" {
|
||||
page, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
Continue: resp.GetContinue(),
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.SetContinue(page.GetContinue())
|
||||
resp.SetResourceVersion(page.GetResourceVersion())
|
||||
resp.SetItems(append(resp.GetItems(), page.GetItems()...))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *GlobalRoleBindingClient) Create(ctx context.Context, obj *GlobalRoleBinding, opts resource.CreateOptions) (*GlobalRoleBinding, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = GlobalRoleBindingKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *GlobalRoleBindingClient) Update(ctx context.Context, obj *GlobalRoleBinding, opts resource.UpdateOptions) (*GlobalRoleBinding, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *GlobalRoleBindingClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*GlobalRoleBinding, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *GlobalRoleBindingClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus GlobalRoleBindingStatus, opts resource.UpdateOptions) (*GlobalRoleBinding, error) {
|
||||
return c.client.Update(ctx, &GlobalRoleBinding{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: GlobalRoleBindingKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *GlobalRoleBindingClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type ResourcePermissionClient struct {
|
||||
client *resource.TypedClient[*ResourcePermission, *ResourcePermissionList]
|
||||
}
|
||||
|
||||
func NewResourcePermissionClient(client resource.Client) *ResourcePermissionClient {
|
||||
return &ResourcePermissionClient{
|
||||
client: resource.NewTypedClient[*ResourcePermission, *ResourcePermissionList](client, ResourcePermissionKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewResourcePermissionClientFromGenerator(generator resource.ClientGenerator) (*ResourcePermissionClient, error) {
|
||||
c, err := generator.ClientFor(ResourcePermissionKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewResourcePermissionClient(c), nil
|
||||
}
|
||||
|
||||
func (c *ResourcePermissionClient) Get(ctx context.Context, identifier resource.Identifier) (*ResourcePermission, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *ResourcePermissionClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*ResourcePermissionList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *ResourcePermissionClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*ResourcePermissionList, error) {
|
||||
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for resp.GetContinue() != "" {
|
||||
page, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
Continue: resp.GetContinue(),
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.SetContinue(page.GetContinue())
|
||||
resp.SetResourceVersion(page.GetResourceVersion())
|
||||
resp.SetItems(append(resp.GetItems(), page.GetItems()...))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *ResourcePermissionClient) Create(ctx context.Context, obj *ResourcePermission, opts resource.CreateOptions) (*ResourcePermission, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = ResourcePermissionKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *ResourcePermissionClient) Update(ctx context.Context, obj *ResourcePermission, opts resource.UpdateOptions) (*ResourcePermission, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *ResourcePermissionClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*ResourcePermission, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *ResourcePermissionClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus ResourcePermissionStatus, opts resource.UpdateOptions) (*ResourcePermission, error) {
|
||||
return c.client.Update(ctx, &ResourcePermission{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: ResourcePermissionKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *ResourcePermissionClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type RoleClient struct {
|
||||
client *resource.TypedClient[*Role, *RoleList]
|
||||
}
|
||||
|
||||
func NewRoleClient(client resource.Client) *RoleClient {
|
||||
return &RoleClient{
|
||||
client: resource.NewTypedClient[*Role, *RoleList](client, RoleKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewRoleClientFromGenerator(generator resource.ClientGenerator) (*RoleClient, error) {
|
||||
c, err := generator.ClientFor(RoleKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewRoleClient(c), nil
|
||||
}
|
||||
|
||||
func (c *RoleClient) Get(ctx context.Context, identifier resource.Identifier) (*Role, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *RoleClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*RoleList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *RoleClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*RoleList, error) {
|
||||
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for resp.GetContinue() != "" {
|
||||
page, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
Continue: resp.GetContinue(),
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.SetContinue(page.GetContinue())
|
||||
resp.SetResourceVersion(page.GetResourceVersion())
|
||||
resp.SetItems(append(resp.GetItems(), page.GetItems()...))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *RoleClient) Create(ctx context.Context, obj *Role, opts resource.CreateOptions) (*Role, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = RoleKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *RoleClient) Update(ctx context.Context, obj *Role, opts resource.UpdateOptions) (*Role, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *RoleClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*Role, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *RoleClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus RoleStatus, opts resource.UpdateOptions) (*Role, error) {
|
||||
return c.client.Update(ctx, &Role{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: RoleKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *RoleClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type RoleBindingClient struct {
|
||||
client *resource.TypedClient[*RoleBinding, *RoleBindingList]
|
||||
}
|
||||
|
||||
func NewRoleBindingClient(client resource.Client) *RoleBindingClient {
|
||||
return &RoleBindingClient{
|
||||
client: resource.NewTypedClient[*RoleBinding, *RoleBindingList](client, RoleBindingKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewRoleBindingClientFromGenerator(generator resource.ClientGenerator) (*RoleBindingClient, error) {
|
||||
c, err := generator.ClientFor(RoleBindingKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewRoleBindingClient(c), nil
|
||||
}
|
||||
|
||||
func (c *RoleBindingClient) Get(ctx context.Context, identifier resource.Identifier) (*RoleBinding, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *RoleBindingClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*RoleBindingList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *RoleBindingClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*RoleBindingList, error) {
|
||||
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for resp.GetContinue() != "" {
|
||||
page, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
Continue: resp.GetContinue(),
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.SetContinue(page.GetContinue())
|
||||
resp.SetResourceVersion(page.GetResourceVersion())
|
||||
resp.SetItems(append(resp.GetItems(), page.GetItems()...))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *RoleBindingClient) Create(ctx context.Context, obj *RoleBinding, opts resource.CreateOptions) (*RoleBinding, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = RoleBindingKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *RoleBindingClient) Update(ctx context.Context, obj *RoleBinding, opts resource.UpdateOptions) (*RoleBinding, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *RoleBindingClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*RoleBinding, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *RoleBindingClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus RoleBindingStatus, opts resource.UpdateOptions) (*RoleBinding, error) {
|
||||
return c.client.Update(ctx, &RoleBinding{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: RoleBindingKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *RoleBindingClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type ServiceAccountClient struct {
|
||||
client *resource.TypedClient[*ServiceAccount, *ServiceAccountList]
|
||||
}
|
||||
|
||||
func NewServiceAccountClient(client resource.Client) *ServiceAccountClient {
|
||||
return &ServiceAccountClient{
|
||||
client: resource.NewTypedClient[*ServiceAccount, *ServiceAccountList](client, ServiceAccountKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewServiceAccountClientFromGenerator(generator resource.ClientGenerator) (*ServiceAccountClient, error) {
|
||||
c, err := generator.ClientFor(ServiceAccountKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewServiceAccountClient(c), nil
|
||||
}
|
||||
|
||||
func (c *ServiceAccountClient) Get(ctx context.Context, identifier resource.Identifier) (*ServiceAccount, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *ServiceAccountClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*ServiceAccountList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *ServiceAccountClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*ServiceAccountList, error) {
|
||||
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for resp.GetContinue() != "" {
|
||||
page, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
Continue: resp.GetContinue(),
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.SetContinue(page.GetContinue())
|
||||
resp.SetResourceVersion(page.GetResourceVersion())
|
||||
resp.SetItems(append(resp.GetItems(), page.GetItems()...))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *ServiceAccountClient) Create(ctx context.Context, obj *ServiceAccount, opts resource.CreateOptions) (*ServiceAccount, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = ServiceAccountKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *ServiceAccountClient) Update(ctx context.Context, obj *ServiceAccount, opts resource.UpdateOptions) (*ServiceAccount, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *ServiceAccountClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*ServiceAccount, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *ServiceAccountClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus ServiceAccountStatus, opts resource.UpdateOptions) (*ServiceAccount, error) {
|
||||
return c.client.Update(ctx, &ServiceAccount{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: ServiceAccountKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *ServiceAccountClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type TeamClient struct {
|
||||
client *resource.TypedClient[*Team, *TeamList]
|
||||
}
|
||||
|
||||
func NewTeamClient(client resource.Client) *TeamClient {
|
||||
return &TeamClient{
|
||||
client: resource.NewTypedClient[*Team, *TeamList](client, TeamKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTeamClientFromGenerator(generator resource.ClientGenerator) (*TeamClient, error) {
|
||||
c, err := generator.ClientFor(TeamKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewTeamClient(c), nil
|
||||
}
|
||||
|
||||
func (c *TeamClient) Get(ctx context.Context, identifier resource.Identifier) (*Team, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *TeamClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*TeamList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *TeamClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*TeamList, error) {
|
||||
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for resp.GetContinue() != "" {
|
||||
page, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
Continue: resp.GetContinue(),
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.SetContinue(page.GetContinue())
|
||||
resp.SetResourceVersion(page.GetResourceVersion())
|
||||
resp.SetItems(append(resp.GetItems(), page.GetItems()...))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *TeamClient) Create(ctx context.Context, obj *Team, opts resource.CreateOptions) (*Team, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = TeamKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *TeamClient) Update(ctx context.Context, obj *Team, opts resource.UpdateOptions) (*Team, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *TeamClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*Team, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *TeamClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus TeamStatus, opts resource.UpdateOptions) (*Team, error) {
|
||||
return c.client.Update(ctx, &Team{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: TeamKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *TeamClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type TeamBindingClient struct {
|
||||
client *resource.TypedClient[*TeamBinding, *TeamBindingList]
|
||||
}
|
||||
|
||||
func NewTeamBindingClient(client resource.Client) *TeamBindingClient {
|
||||
return &TeamBindingClient{
|
||||
client: resource.NewTypedClient[*TeamBinding, *TeamBindingList](client, TeamBindingKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewTeamBindingClientFromGenerator(generator resource.ClientGenerator) (*TeamBindingClient, error) {
|
||||
c, err := generator.ClientFor(TeamBindingKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewTeamBindingClient(c), nil
|
||||
}
|
||||
|
||||
func (c *TeamBindingClient) Get(ctx context.Context, identifier resource.Identifier) (*TeamBinding, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *TeamBindingClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*TeamBindingList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *TeamBindingClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*TeamBindingList, error) {
|
||||
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for resp.GetContinue() != "" {
|
||||
page, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
Continue: resp.GetContinue(),
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.SetContinue(page.GetContinue())
|
||||
resp.SetResourceVersion(page.GetResourceVersion())
|
||||
resp.SetItems(append(resp.GetItems(), page.GetItems()...))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *TeamBindingClient) Create(ctx context.Context, obj *TeamBinding, opts resource.CreateOptions) (*TeamBinding, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = TeamBindingKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *TeamBindingClient) Update(ctx context.Context, obj *TeamBinding, opts resource.UpdateOptions) (*TeamBinding, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *TeamBindingClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*TeamBinding, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *TeamBindingClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus TeamBindingStatus, opts resource.UpdateOptions) (*TeamBinding, error) {
|
||||
return c.client.Update(ctx, &TeamBinding{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: TeamBindingKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *TeamBindingClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type UserClient struct {
|
||||
client *resource.TypedClient[*User, *UserList]
|
||||
}
|
||||
|
||||
func NewUserClient(client resource.Client) *UserClient {
|
||||
return &UserClient{
|
||||
client: resource.NewTypedClient[*User, *UserList](client, UserKind()),
|
||||
}
|
||||
}
|
||||
|
||||
func NewUserClientFromGenerator(generator resource.ClientGenerator) (*UserClient, error) {
|
||||
c, err := generator.ClientFor(UserKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewUserClient(c), nil
|
||||
}
|
||||
|
||||
func (c *UserClient) Get(ctx context.Context, identifier resource.Identifier) (*User, error) {
|
||||
return c.client.Get(ctx, identifier)
|
||||
}
|
||||
|
||||
func (c *UserClient) List(ctx context.Context, namespace string, opts resource.ListOptions) (*UserList, error) {
|
||||
return c.client.List(ctx, namespace, opts)
|
||||
}
|
||||
|
||||
func (c *UserClient) ListAll(ctx context.Context, namespace string, opts resource.ListOptions) (*UserList, error) {
|
||||
resp, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for resp.GetContinue() != "" {
|
||||
page, err := c.client.List(ctx, namespace, resource.ListOptions{
|
||||
Continue: resp.GetContinue(),
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Limit: opts.Limit,
|
||||
LabelFilters: opts.LabelFilters,
|
||||
FieldSelectors: opts.FieldSelectors,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.SetContinue(page.GetContinue())
|
||||
resp.SetResourceVersion(page.GetResourceVersion())
|
||||
resp.SetItems(append(resp.GetItems(), page.GetItems()...))
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *UserClient) Create(ctx context.Context, obj *User, opts resource.CreateOptions) (*User, error) {
|
||||
// Make sure apiVersion and kind are set
|
||||
obj.APIVersion = GroupVersion.Identifier()
|
||||
obj.Kind = UserKind().Kind()
|
||||
return c.client.Create(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *UserClient) Update(ctx context.Context, obj *User, opts resource.UpdateOptions) (*User, error) {
|
||||
return c.client.Update(ctx, obj, opts)
|
||||
}
|
||||
|
||||
func (c *UserClient) Patch(ctx context.Context, identifier resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions) (*User, error) {
|
||||
return c.client.Patch(ctx, identifier, req, opts)
|
||||
}
|
||||
|
||||
func (c *UserClient) UpdateStatus(ctx context.Context, identifier resource.Identifier, newStatus UserStatus, opts resource.UpdateOptions) (*User, error) {
|
||||
return c.client.Update(ctx, &User{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: UserKind().Kind(),
|
||||
APIVersion: GroupVersion.Identifier(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
Namespace: identifier.Namespace,
|
||||
Name: identifier.Name,
|
||||
},
|
||||
Status: newStatus,
|
||||
}, resource.UpdateOptions{
|
||||
Subresource: "status",
|
||||
ResourceVersion: opts.ResourceVersion,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *UserClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error {
|
||||
return c.client.Delete(ctx, identifier, opts)
|
||||
}
|
||||
@@ -10,8 +10,8 @@ type UserSpec struct {
|
||||
GrafanaAdmin bool `json:"grafanaAdmin"`
|
||||
Login string `json:"login"`
|
||||
Name string `json:"name"`
|
||||
// What to do with salt, rands and password?
|
||||
Provisioned bool `json:"provisioned"`
|
||||
Provisioned bool `json:"provisioned"`
|
||||
Role string `json:"role"`
|
||||
}
|
||||
|
||||
// NewUserSpec creates a new UserSpec object.
|
||||
|
||||
@@ -2660,14 +2660,20 @@ func schema_pkg_apis_iam_v0alpha1_UserSpec(ref common.ReferenceCallback) common.
|
||||
},
|
||||
"provisioned": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "What to do with salt, rands and password?",
|
||||
Default: false,
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
Default: false,
|
||||
Type: []string{"boolean"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"role": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"disabled", "email", "emailVerified", "grafanaAdmin", "login", "name", "provisioned"},
|
||||
Required: []string{"disabled", "email", "emailVerified", "grafanaAdmin", "login", "name", "provisioned", "role"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/kube-openapi/pkg/spec3"
|
||||
|
||||
v0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
)
|
||||
@@ -93,6 +95,10 @@ var appManifestData = app.ManifestData{
|
||||
Conversion: false,
|
||||
},
|
||||
},
|
||||
Routes: app.ManifestVersionRoutes{
|
||||
Namespaced: map[string]spec3.PathProps{},
|
||||
Cluster: map[string]spec3.PathProps{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -130,6 +136,7 @@ var customRouteToGoResponseType = map[string]any{}
|
||||
// ManifestCustomRouteResponsesAssociator returns the associated response go type for a given kind, version, custom route path, and method, if one exists.
|
||||
// kind may be empty for custom routes which are not kind subroutes. Leading slashes are removed from subroute paths.
|
||||
// If there is no association for the provided kind, version, custom route path, and method, exists will return false.
|
||||
// Resource routes (those without a kind) should prefix their route with "<namespace>/" if the route is namespaced (otherwise the route is assumed to be cluster-scope)
|
||||
func ManifestCustomRouteResponsesAssociator(kind, version, path, verb string) (goType any, exists bool) {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
@@ -137,3 +144,42 @@ func ManifestCustomRouteResponsesAssociator(kind, version, path, verb string) (g
|
||||
goType, exists = customRouteToGoResponseType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
var customRouteToGoParamsType = map[string]runtime.Object{}
|
||||
|
||||
func ManifestCustomRouteQueryAssociator(kind, version, path, verb string) (goType runtime.Object, exists bool) {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
goType, exists = customRouteToGoParamsType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
var customRouteToGoRequestBodyType = map[string]any{}
|
||||
|
||||
func ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb string) (goType any, exists bool) {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
goType, exists = customRouteToGoRequestBodyType[fmt.Sprintf("%s|%s|%s|%s", version, kind, path, strings.ToUpper(verb))]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
type GoTypeAssociator struct{}
|
||||
|
||||
func NewGoTypeAssociator() *GoTypeAssociator {
|
||||
return &GoTypeAssociator{}
|
||||
}
|
||||
|
||||
func (g *GoTypeAssociator) KindToGoType(kind, version string) (goType resource.Kind, exists bool) {
|
||||
return ManifestGoTypeAssociator(kind, version)
|
||||
}
|
||||
func (g *GoTypeAssociator) CustomRouteReturnGoType(kind, version, path, verb string) (goType any, exists bool) {
|
||||
return ManifestCustomRouteResponsesAssociator(kind, version, path, verb)
|
||||
}
|
||||
func (g *GoTypeAssociator) CustomRouteQueryGoType(kind, version, path, verb string) (goType runtime.Object, exists bool) {
|
||||
return ManifestCustomRouteQueryAssociator(kind, version, path, verb)
|
||||
}
|
||||
func (g *GoTypeAssociator) CustomRouteRequestBodyGoType(kind, version, path, verb string) (goType any, exists bool) {
|
||||
return ManifestCustomRouteRequestBodyAssociator(kind, version, path, verb)
|
||||
}
|
||||
|
||||
@@ -567,7 +567,7 @@ require (
|
||||
github.com/sethvargo/go-retry v0.3.0 // indirect
|
||||
github.com/shadowspore/fossil-delta v0.0.0-20241213113458-1d797d70cbe3 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/smarty/assertions v1.15.0 // indirect
|
||||
github.com/sony/gobreaker v0.5.0 // indirect
|
||||
@@ -639,7 +639,7 @@ require (
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/src-d/go-errors.v1 v1.0.0 // indirect
|
||||
gopkg.in/telebot.v3 v3.2.1 // indirect
|
||||
gopkg.in/telebot.v3 v3.3.8 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.34.1 // indirect
|
||||
k8s.io/kms v0.34.1 // indirect
|
||||
modernc.org/libc v1.66.3 // indirect
|
||||
|
||||
@@ -2353,8 +2353,9 @@ github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+Yg
|
||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvqWEUH6SjNiu7VhSjuVFTFiTcphaLU=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 h1:OfRzdxCzDhp+rsKWXuOO2I/quKMJ/+TQwVbIP/gltZg=
|
||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92/go.mod h1:7/OT02F6S6I7v6WXb+IjhMuZEYfH/RJ5RwEWnEo5BMg=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
@@ -3563,8 +3564,9 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/src-d/go-errors.v1 v1.0.0 h1:cooGdZnCjYbeS1zb1s6pVAAimTdKceRrpn7aKOnNIfc=
|
||||
gopkg.in/src-d/go-errors.v1 v1.0.0/go.mod h1:q1cBlomlw2FnDBDNGlnh6X0jPihy+QxZfMMNxPCbdYg=
|
||||
gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs=
|
||||
gopkg.in/telebot.v3 v3.2.1/go.mod h1:GJKwwWqp9nSkIVN51eRKU78aB5f5OnQuWdwiIZfPbko=
|
||||
gopkg.in/telebot.v3 v3.3.8 h1:uVDGjak9l824FN9YARWUHMsiNZnlohAVwUycw21k6t8=
|
||||
gopkg.in/telebot.v3 v3.3.8/go.mod h1:1mlbqcLTVSfK9dx7fdp+Nb5HZsy4LLPtpZTKmwhwtzM=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package common
|
||||
|
||||
import "github.com/grafana/grafana/pkg/services/user"
|
||||
|
||||
type UserWithRole struct {
|
||||
user.User
|
||||
Role string
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin,
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at, o.role
|
||||
FROM `grafana`.`user` as u JOIN `grafana`.`org_user` as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND NOT u.is_service_account
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin,
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at, o.role
|
||||
FROM `grafana`.`user` as u JOIN `grafana`.`org_user` as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND NOT u.is_service_account
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin,
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at, o.role
|
||||
FROM `grafana`.`user` as u JOIN `grafana`.`org_user` as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND NOT u.is_service_account
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin,
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at, o.role
|
||||
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND NOT u.is_service_account
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin,
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at, o.role
|
||||
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND NOT u.is_service_account
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin,
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at, o.role
|
||||
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND NOT u.is_service_account
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin,
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at, o.role
|
||||
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND NOT u.is_service_account
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin,
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at, o.role
|
||||
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND NOT u.is_service_account
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin,
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at, o.role
|
||||
FROM "grafana"."user" as u JOIN "grafana"."org_user" as o ON u.id = o.user_id
|
||||
WHERE o.org_id = 0
|
||||
AND NOT u.is_service_account
|
||||
|
||||
@@ -98,7 +98,7 @@ type ListUserQuery struct {
|
||||
}
|
||||
|
||||
type ListUserResult struct {
|
||||
Users []user.User
|
||||
Users []common.UserWithRole
|
||||
Continue int64
|
||||
RV int64
|
||||
}
|
||||
@@ -166,10 +166,10 @@ func (s *legacySQLStore) queryUsers(ctx context.Context, sql *legacysql.LegacyDa
|
||||
if err == nil {
|
||||
var lastID int64
|
||||
for rows.Next() {
|
||||
u := user.User{}
|
||||
u := common.UserWithRole{}
|
||||
err = rows.Scan(&u.OrgID, &u.ID, &u.UID, &u.Login, &u.Email, &u.Name,
|
||||
&u.Created, &u.Updated, &u.IsServiceAccount, &u.IsDisabled, &u.IsAdmin, &u.EmailVerified,
|
||||
&u.IsProvisioned, &u.LastSeenAt,
|
||||
&u.IsProvisioned, &u.LastSeenAt, &u.Role,
|
||||
)
|
||||
if err != nil {
|
||||
return res, err
|
||||
@@ -310,7 +310,7 @@ type CreateUserCommand struct {
|
||||
}
|
||||
|
||||
type CreateUserResult struct {
|
||||
User user.User
|
||||
User common.UserWithRole
|
||||
}
|
||||
|
||||
type CreateOrgUserCommand struct {
|
||||
@@ -398,7 +398,7 @@ func (s *legacySQLStore) CreateUser(ctx context.Context, ns claims.NamespaceInfo
|
||||
|
||||
req := newCreateUser(sql, &cmd)
|
||||
|
||||
var createdUser user.User
|
||||
var createdUser common.UserWithRole
|
||||
err = sql.DB.GetSqlxSession().WithTransaction(ctx, func(st *session.SessionTx) error {
|
||||
userQuery, err := sqltemplate.Execute(sqlCreateUserTemplate, req)
|
||||
if err != nil {
|
||||
@@ -429,23 +429,26 @@ func (s *legacySQLStore) CreateUser(ctx context.Context, ns claims.NamespaceInfo
|
||||
return fmt.Errorf("failed to create org_user relationship: %w", err)
|
||||
}
|
||||
|
||||
createdUser = user.User{
|
||||
ID: userID,
|
||||
UID: cmd.UID,
|
||||
Login: cmd.Login,
|
||||
Email: cmd.Email,
|
||||
Name: cmd.Name,
|
||||
OrgID: cmd.OrgID,
|
||||
IsAdmin: cmd.IsAdmin,
|
||||
IsDisabled: cmd.IsDisabled,
|
||||
EmailVerified: cmd.EmailVerified,
|
||||
IsProvisioned: cmd.IsProvisioned,
|
||||
Salt: cmd.Salt,
|
||||
Rands: cmd.Rands,
|
||||
Created: cmd.Created.Time,
|
||||
Updated: cmd.Updated.Time,
|
||||
LastSeenAt: cmd.LastSeenAt.Time,
|
||||
IsServiceAccount: false,
|
||||
createdUser = common.UserWithRole{
|
||||
User: user.User{
|
||||
ID: userID,
|
||||
UID: cmd.UID,
|
||||
Login: cmd.Login,
|
||||
Email: cmd.Email,
|
||||
Name: cmd.Name,
|
||||
OrgID: cmd.OrgID,
|
||||
IsAdmin: cmd.IsAdmin,
|
||||
IsDisabled: cmd.IsDisabled,
|
||||
EmailVerified: cmd.EmailVerified,
|
||||
IsProvisioned: cmd.IsProvisioned,
|
||||
Salt: cmd.Salt,
|
||||
Rands: cmd.Rands,
|
||||
Created: cmd.Created.Time,
|
||||
Updated: cmd.Updated.Time,
|
||||
LastSeenAt: cmd.LastSeenAt.Time,
|
||||
IsServiceAccount: false,
|
||||
},
|
||||
Role: cmd.Role,
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SELECT o.org_id, u.id, u.uid, u.login, u.email, u.name,
|
||||
u.created, u.updated, u.is_service_account, u.is_disabled, u.is_admin,
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at
|
||||
u.email_verified, u.is_provisioned, u.last_seen_at, o.role
|
||||
FROM {{ .Ident .UserTable }} as u JOIN {{ .Ident .OrgUserTable }} as o ON u.id = o.user_id
|
||||
WHERE o.org_id = {{ .Arg .Query.OrgID }}
|
||||
AND NOT u.is_service_account
|
||||
|
||||
@@ -2,12 +2,10 @@ package iam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"strings"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
@@ -325,7 +323,7 @@ func (b *IdentityAccessManagementAPIBuilder) Validate(ctx context.Context, a adm
|
||||
case admission.Create:
|
||||
switch typedObj := a.GetObject().(type) {
|
||||
case *iamv0.User:
|
||||
return b.validateCreateUser(ctx, a, o)
|
||||
return user.ValidateOnCreate(ctx, typedObj)
|
||||
case *iamv0.ServiceAccount:
|
||||
return serviceaccount.ValidateOnCreate(ctx, typedObj)
|
||||
case *iamv0.Team:
|
||||
@@ -343,31 +341,6 @@ func (b *IdentityAccessManagementAPIBuilder) Validate(ctx context.Context, a adm
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *IdentityAccessManagementAPIBuilder) validateCreateUser(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
userObj, ok := a.GetObject().(*iamv0.User)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return apierrors.NewUnauthorized("no identity found")
|
||||
}
|
||||
|
||||
// Temporary validation that the user is not trying to create a Grafana Admin without being a Grafana Admin.
|
||||
if userObj.Spec.GrafanaAdmin && !requester.GetIsGrafanaAdmin() {
|
||||
return apierrors.NewForbidden(iamv0.UserResourceInfo.GroupResource(),
|
||||
userObj.Name,
|
||||
fmt.Errorf("only grafana admins can create grafana admins"))
|
||||
}
|
||||
|
||||
if userObj.Spec.Login == "" && userObj.Spec.Email == "" {
|
||||
return apierrors.NewBadRequest("user must have either login or email")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mutate implements builder.APIGroupMutation.
|
||||
// TODO: Move this to the MutateFunc of the user resource after moving the APIs to use the app-platofrm-sdk.
|
||||
// TODO: https://github.com/grafana/grafana/blob/main/apps/playlist/pkg/app/app.go#L62
|
||||
|
||||
@@ -14,9 +14,6 @@ func MutateOnCreate(ctx context.Context, obj *iamv0alpha1.User) error {
|
||||
if obj.Spec.Login == "" {
|
||||
obj.Spec.Login = obj.Spec.Email
|
||||
}
|
||||
if obj.Spec.Email == "" {
|
||||
obj.Spec.Email = obj.Spec.Login
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ func TestMutateOnCreate_LoginEmail(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expectedLogin: "only.login",
|
||||
expectedEmail: "only.login",
|
||||
},
|
||||
{
|
||||
name: "login and email already lowercase",
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/common"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
@@ -196,16 +196,17 @@ func (s *LegacyStore) Create(ctx context.Context, obj runtime.Object, createVali
|
||||
return nil, fmt.Errorf("expected User object, got %T", obj)
|
||||
}
|
||||
|
||||
if userObj.GenerateName != "" {
|
||||
userObj.Name = userObj.GenerateName + util.GenerateShortUID()
|
||||
userObj.GenerateName = ""
|
||||
}
|
||||
|
||||
if createValidation != nil {
|
||||
if err := createValidation(ctx, obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if userObj.Spec.Login == "" && userObj.Spec.Email == "" {
|
||||
return nil, fmt.Errorf("user must have either login or email")
|
||||
}
|
||||
|
||||
createCmd := legacy.CreateUserCommand{
|
||||
UID: userObj.Name,
|
||||
Login: userObj.Spec.Login,
|
||||
@@ -215,6 +216,7 @@ func (s *LegacyStore) Create(ctx context.Context, obj runtime.Object, createVali
|
||||
IsDisabled: userObj.Spec.Disabled,
|
||||
EmailVerified: userObj.Spec.EmailVerified,
|
||||
IsProvisioned: userObj.Spec.Provisioned,
|
||||
Role: userObj.Spec.Role,
|
||||
}
|
||||
|
||||
result, err := s.store.CreateUser(ctx, ns, createCmd)
|
||||
@@ -226,7 +228,7 @@ func (s *LegacyStore) Create(ctx context.Context, obj runtime.Object, createVali
|
||||
return &iamUser, nil
|
||||
}
|
||||
|
||||
func toUserItem(u *user.User, ns string) iamv0alpha1.User {
|
||||
func toUserItem(u *common.UserWithRole, ns string) iamv0alpha1.User {
|
||||
item := &iamv0alpha1.User{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: u.UID,
|
||||
@@ -242,6 +244,7 @@ func toUserItem(u *user.User, ns string) iamv0alpha1.User {
|
||||
Disabled: u.IsDisabled,
|
||||
GrafanaAdmin: u.IsAdmin,
|
||||
Provisioned: u.IsProvisioned,
|
||||
Role: u.Role,
|
||||
},
|
||||
}
|
||||
obj, _ := utils.MetaAccessor(item)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
iamv0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
)
|
||||
|
||||
func ValidateOnCreate(ctx context.Context, obj *iamv0alpha1.User) error {
|
||||
requester, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return apierrors.NewUnauthorized("no identity found")
|
||||
}
|
||||
|
||||
// Temporary validation that the user is not trying to create a Grafana Admin without being a Grafana Admin.
|
||||
if obj.Spec.GrafanaAdmin && !requester.GetIsGrafanaAdmin() {
|
||||
return apierrors.NewForbidden(iamv0alpha1.UserResourceInfo.GroupResource(),
|
||||
obj.Name,
|
||||
fmt.Errorf("only grafana admins can create grafana admins"))
|
||||
}
|
||||
|
||||
if obj.Spec.Login == "" && obj.Spec.Email == "" {
|
||||
return apierrors.NewBadRequest("user must have either login or email")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
iamv0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
)
|
||||
|
||||
func TestValidateOnCreate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
user *iamv0alpha1.User
|
||||
requester *identity.StaticRequester
|
||||
expectError bool
|
||||
errorContains string
|
||||
}{
|
||||
{
|
||||
name: "valid user creation by grafana admin",
|
||||
user: &iamv0alpha1.User{
|
||||
Spec: iamv0alpha1.UserSpec{
|
||||
Login: "testuser",
|
||||
},
|
||||
},
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
IsGrafanaAdmin: true,
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "grafana admin creating another grafana admin",
|
||||
user: &iamv0alpha1.User{
|
||||
Spec: iamv0alpha1.UserSpec{
|
||||
Login: "newadmin",
|
||||
GrafanaAdmin: true,
|
||||
},
|
||||
},
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
IsGrafanaAdmin: true,
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "non-admin trying to create a grafana admin",
|
||||
user: &iamv0alpha1.User{
|
||||
Spec: iamv0alpha1.UserSpec{
|
||||
Login: "newadmin",
|
||||
GrafanaAdmin: true,
|
||||
},
|
||||
},
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
IsGrafanaAdmin: false,
|
||||
},
|
||||
expectError: true,
|
||||
errorContains: "only grafana admins can create grafana admins",
|
||||
},
|
||||
{
|
||||
name: "user with empty login and email",
|
||||
user: &iamv0alpha1.User{
|
||||
Spec: iamv0alpha1.UserSpec{},
|
||||
},
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
IsGrafanaAdmin: false,
|
||||
},
|
||||
expectError: true,
|
||||
errorContains: "user must have either login or email",
|
||||
},
|
||||
{
|
||||
name: "user with only login",
|
||||
user: &iamv0alpha1.User{
|
||||
Spec: iamv0alpha1.UserSpec{
|
||||
Login: "testuser",
|
||||
},
|
||||
},
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
IsGrafanaAdmin: false,
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "user with only email",
|
||||
user: &iamv0alpha1.User{
|
||||
Spec: iamv0alpha1.UserSpec{
|
||||
Email: "test@test.com",
|
||||
},
|
||||
},
|
||||
requester: &identity.StaticRequester{
|
||||
Type: types.TypeUser,
|
||||
IsGrafanaAdmin: false,
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := identity.WithRequester(
|
||||
context.Background(),
|
||||
tt.requester,
|
||||
)
|
||||
|
||||
err := ValidateOnCreate(ctx, tt.user)
|
||||
|
||||
if tt.expectError {
|
||||
require.Error(t, err)
|
||||
if tt.errorContains != "" {
|
||||
require.Contains(t, err.Error(), tt.errorContains)
|
||||
}
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+20
-181
@@ -2,18 +2,13 @@ package identity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
@@ -98,7 +93,8 @@ func TestIntegrationIdentity(t *testing.T) {
|
||||
"grafanaAdmin": true,
|
||||
"login": "admin",
|
||||
"name": "",
|
||||
"provisioned": false
|
||||
"provisioned": false,
|
||||
"role": "Admin"
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
@@ -107,7 +103,8 @@ func TestIntegrationIdentity(t *testing.T) {
|
||||
"grafanaAdmin": true,
|
||||
"login": "grafana-admin",
|
||||
"name": "admin2",
|
||||
"provisioned": false
|
||||
"provisioned": false,
|
||||
"role": "Admin"
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
@@ -116,7 +113,8 @@ func TestIntegrationIdentity(t *testing.T) {
|
||||
"grafanaAdmin": false,
|
||||
"login": "editor",
|
||||
"name": "editor",
|
||||
"provisioned": false
|
||||
"provisioned": false,
|
||||
"role": "Editor"
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
@@ -125,7 +123,8 @@ func TestIntegrationIdentity(t *testing.T) {
|
||||
"grafanaAdmin": false,
|
||||
"login": "viewer",
|
||||
"name": "viewer",
|
||||
"provisioned": false
|
||||
"provisioned": false,
|
||||
"role": "Viewer"
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
@@ -134,7 +133,8 @@ func TestIntegrationIdentity(t *testing.T) {
|
||||
"grafanaAdmin": false,
|
||||
"login": "none",
|
||||
"name": "none",
|
||||
"provisioned": false
|
||||
"provisioned": false,
|
||||
"role": "None"
|
||||
}
|
||||
]`, found)
|
||||
|
||||
@@ -157,7 +157,8 @@ func TestIntegrationIdentity(t *testing.T) {
|
||||
"grafanaAdmin": true,
|
||||
"login": "grafana-admin",
|
||||
"name": "admin2",
|
||||
"provisioned": false
|
||||
"provisioned": false,
|
||||
"role": "Admin"
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
@@ -166,7 +167,8 @@ func TestIntegrationIdentity(t *testing.T) {
|
||||
"grafanaAdmin": false,
|
||||
"login": "admin2-org-2",
|
||||
"name": "admin2",
|
||||
"provisioned": false
|
||||
"provisioned": false,
|
||||
"role": "Admin"
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
@@ -175,7 +177,8 @@ func TestIntegrationIdentity(t *testing.T) {
|
||||
"grafanaAdmin": false,
|
||||
"login": "editor-org-2",
|
||||
"name": "editor",
|
||||
"provisioned": false
|
||||
"provisioned": false,
|
||||
"role": "Editor"
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
@@ -184,7 +187,8 @@ func TestIntegrationIdentity(t *testing.T) {
|
||||
"grafanaAdmin": false,
|
||||
"login": "viewer-org-2",
|
||||
"name": "viewer",
|
||||
"provisioned": false
|
||||
"provisioned": false,
|
||||
"role": "Viewer"
|
||||
},
|
||||
{
|
||||
"disabled": false,
|
||||
@@ -193,174 +197,9 @@ func TestIntegrationIdentity(t *testing.T) {
|
||||
"grafanaAdmin": false,
|
||||
"login": "none-org-2",
|
||||
"name": "none",
|
||||
"provisioned": false
|
||||
"provisioned": false,
|
||||
"role": "None"
|
||||
}
|
||||
] `, found)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntegrationUsers(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
// TODO: Figure out why rest.Mode4 is failing
|
||||
modes := []rest.DualWriterMode{rest.Mode0, rest.Mode1, rest.Mode2, rest.Mode3}
|
||||
for _, mode := range modes {
|
||||
t.Run(fmt.Sprintf("User CRUD operations with dual writer mode %d", mode), func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: false,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"users.iam.grafana.app": {
|
||||
DualWriterMode: mode,
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs,
|
||||
featuremgmt.FlagKubernetesAuthnMutation,
|
||||
},
|
||||
})
|
||||
doUserCRUDTestsUsingTheNewAPIs(t, helper)
|
||||
|
||||
if mode < 3 {
|
||||
doUserCRUDTestsUsingTheLegacyAPIs(t, helper)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func doUserCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
t.Run("should create user and delete it using the new APIs as a GrafanaAdmin", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
userClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: helper.Org1.Admin,
|
||||
Namespace: helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID()),
|
||||
GVR: gvrUsers,
|
||||
})
|
||||
|
||||
// Create the user
|
||||
created, err := userClient.Resource.Create(ctx, helper.LoadYAMLOrJSONFile("testdata/user-test-create-v0.yaml"), metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, created)
|
||||
|
||||
// Verify creation response
|
||||
createdSpec := created.Object["spec"].(map[string]interface{})
|
||||
require.Equal(t, "testuser1@example123.com", createdSpec["email"])
|
||||
require.Equal(t, "testuser1", createdSpec["login"])
|
||||
require.Equal(t, "Test User 1", createdSpec["name"])
|
||||
require.Equal(t, false, createdSpec["provisioned"])
|
||||
|
||||
// Get the UID from created user for fetching
|
||||
createdUID := created.GetName()
|
||||
require.NotEmpty(t, createdUID)
|
||||
|
||||
_, err = userClient.Resource.List(ctx, metav1.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := userClient.Resource.Get(ctx, createdUID, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fetched)
|
||||
|
||||
// Verify fetched user matches created user
|
||||
fetchedSpec := fetched.Object["spec"].(map[string]interface{})
|
||||
require.Equal(t, "testuser1@example123.com", fetchedSpec["email"])
|
||||
require.Equal(t, "testuser1", fetchedSpec["login"])
|
||||
require.Equal(t, "Test User 1", fetchedSpec["name"])
|
||||
require.Equal(t, false, fetchedSpec["provisioned"])
|
||||
|
||||
// Verify metadata
|
||||
require.Equal(t, createdUID, fetched.GetName())
|
||||
require.Equal(t, "default", fetched.GetNamespace())
|
||||
|
||||
// TODO: Uncomment when we know how to handle global scope (global.users:)
|
||||
// err = userClient.Resource.Delete(ctx, createdUID, metav1.DeleteOptions{})
|
||||
// require.NoError(t, err)
|
||||
|
||||
// Verify deletion
|
||||
// _, err = userClient.Resource.Get(ctx, createdUID, metav1.GetOptions{})
|
||||
// require.Error(t, err)
|
||||
// require.Contains(t, err.Error(), "not found")
|
||||
})
|
||||
|
||||
t.Run("should not be able to create user when using a user with insufficient permissions", func(t *testing.T) {
|
||||
for _, user := range []apis.User{
|
||||
helper.OrgB.Admin, // Not a Grafana Admin
|
||||
helper.Org1.Editor,
|
||||
helper.Org1.Viewer,
|
||||
} {
|
||||
t.Run(fmt.Sprintf("with basic role_%s", user.Identity.GetOrgRole()), func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: user,
|
||||
Namespace: helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID()),
|
||||
GVR: gvrUsers,
|
||||
})
|
||||
|
||||
// Create the user
|
||||
_, err := userClient.Resource.Create(ctx, helper.LoadYAMLOrJSONFile("testdata/user-test-create-v0.yaml"), metav1.CreateOptions{})
|
||||
require.Error(t, err)
|
||||
var statusErr *errors.StatusError
|
||||
require.ErrorAs(t, err, &statusErr)
|
||||
require.Equal(t, int32(403), statusErr.ErrStatus.Code)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func doUserCRUDTestsUsingTheLegacyAPIs(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
t.Run("should create user using legacy APIs and delete it using the new APIs", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: helper.Org1.Admin,
|
||||
GVR: gvrUsers,
|
||||
})
|
||||
|
||||
legacyUserPayload := `{
|
||||
"name": "Test User 2",
|
||||
"email": "testuser2@example.com",
|
||||
"login": "testuser2",
|
||||
"password": "password123"
|
||||
}`
|
||||
|
||||
rsp := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: "POST",
|
||||
Path: "/api/admin/users",
|
||||
Body: []byte(legacyUserPayload),
|
||||
}, &user.User{})
|
||||
|
||||
require.NotNil(t, rsp)
|
||||
require.Equal(t, 200, rsp.Response.StatusCode)
|
||||
require.NotEmpty(t, rsp.Result.UID)
|
||||
|
||||
// Now try to fetch the user via the new API
|
||||
user, err := userClient.Resource.Get(context.Background(), rsp.Result.UID, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, user)
|
||||
|
||||
// Verify fetched user matches created user
|
||||
userSpec := user.Object["spec"].(map[string]interface{})
|
||||
require.Equal(t, "testuser2@example.com", userSpec["email"])
|
||||
require.Equal(t, "testuser2", userSpec["login"])
|
||||
require.Equal(t, "Test User 2", userSpec["name"])
|
||||
require.Equal(t, false, userSpec["provisioned"])
|
||||
|
||||
// Verify metadata
|
||||
require.Equal(t, rsp.Result.UID, user.GetName())
|
||||
require.Equal(t, "default", user.GetNamespace())
|
||||
|
||||
// Now delete the user using the legacy API
|
||||
deleteRsp := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: "DELETE",
|
||||
Path: fmt.Sprintf("/api/admin/users/%d", rsp.Result.ID),
|
||||
}, &apis.AnyResource{})
|
||||
require.Equal(t, 200, deleteRsp.Response.StatusCode)
|
||||
|
||||
// Verify deletion
|
||||
_, err = userClient.Resource.Get(ctx, rsp.Result.UID, metav1.GetOptions{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "not found")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apiserver/rest"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestIntegrationUsers(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
// TODO: Figure out why rest.Mode4 is failing
|
||||
modes := []rest.DualWriterMode{rest.Mode0, rest.Mode1, rest.Mode2, rest.Mode3}
|
||||
for _, mode := range modes {
|
||||
t.Run(fmt.Sprintf("User CRUD operations with dual writer mode %d", mode), func(t *testing.T) {
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: false,
|
||||
DisableAnonymous: true,
|
||||
APIServerStorageType: "unified",
|
||||
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
||||
"users.iam.grafana.app": {
|
||||
DualWriterMode: mode,
|
||||
},
|
||||
},
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs,
|
||||
featuremgmt.FlagKubernetesAuthnMutation,
|
||||
},
|
||||
})
|
||||
doUserCRUDTestsUsingTheNewAPIs(t, helper)
|
||||
|
||||
if mode < 3 {
|
||||
doUserCRUDTestsUsingTheLegacyAPIs(t, helper)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func doUserCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
t.Run("should create user and delete it using the new APIs as a GrafanaAdmin", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
userClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: helper.Org1.Admin,
|
||||
Namespace: helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID()),
|
||||
GVR: gvrUsers,
|
||||
})
|
||||
|
||||
// Create the user
|
||||
created, err := userClient.Resource.Create(ctx, helper.LoadYAMLOrJSONFile("testdata/user-test-create-v0.yaml"), metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, created)
|
||||
|
||||
// Verify creation response
|
||||
createdSpec := created.Object["spec"].(map[string]interface{})
|
||||
require.Equal(t, "testuser1@example123.com", createdSpec["email"])
|
||||
require.Equal(t, "testuser1", createdSpec["login"])
|
||||
require.Equal(t, "Test User 1", createdSpec["name"])
|
||||
require.Equal(t, false, createdSpec["provisioned"])
|
||||
|
||||
// Get the UID from created user for fetching
|
||||
createdUID := created.GetName()
|
||||
require.NotEmpty(t, createdUID)
|
||||
|
||||
_, err = userClient.Resource.List(ctx, metav1.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
fetched, err := userClient.Resource.Get(ctx, createdUID, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, fetched)
|
||||
|
||||
// Verify fetched user matches created user
|
||||
fetchedSpec := fetched.Object["spec"].(map[string]interface{})
|
||||
require.Equal(t, "testuser1@example123.com", fetchedSpec["email"])
|
||||
require.Equal(t, "testuser1", fetchedSpec["login"])
|
||||
require.Equal(t, "Test User 1", fetchedSpec["name"])
|
||||
require.Equal(t, false, fetchedSpec["provisioned"])
|
||||
|
||||
// Verify metadata
|
||||
require.Equal(t, createdUID, fetched.GetName())
|
||||
require.Equal(t, "default", fetched.GetNamespace())
|
||||
|
||||
// TODO: Uncomment when we know how to handle global scope (global.users:)
|
||||
// err = userClient.Resource.Delete(ctx, createdUID, metav1.DeleteOptions{})
|
||||
// require.NoError(t, err)
|
||||
|
||||
// Verify deletion
|
||||
// _, err = userClient.Resource.Get(ctx, createdUID, metav1.GetOptions{})
|
||||
// require.Error(t, err)
|
||||
// require.Contains(t, err.Error(), "not found")
|
||||
})
|
||||
|
||||
t.Run("should not be able to create user when using a user with insufficient permissions", func(t *testing.T) {
|
||||
for _, user := range []apis.User{
|
||||
helper.OrgB.Admin, // Not a Grafana Admin
|
||||
helper.Org1.Editor,
|
||||
helper.Org1.Viewer,
|
||||
} {
|
||||
t.Run(fmt.Sprintf("with basic role_%s", user.Identity.GetOrgRole()), func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: user,
|
||||
Namespace: helper.Namespacer(helper.Org1.Admin.Identity.GetOrgID()),
|
||||
GVR: gvrUsers,
|
||||
})
|
||||
|
||||
// Create the user
|
||||
_, err := userClient.Resource.Create(ctx, helper.LoadYAMLOrJSONFile("testdata/user-test-create-v0.yaml"), metav1.CreateOptions{})
|
||||
require.Error(t, err)
|
||||
var statusErr *errors.StatusError
|
||||
require.ErrorAs(t, err, &statusErr)
|
||||
require.Equal(t, int32(403), statusErr.ErrStatus.Code)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func doUserCRUDTestsUsingTheLegacyAPIs(t *testing.T, helper *apis.K8sTestHelper) {
|
||||
t.Run("should create user using legacy APIs and delete it using the new APIs", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
||||
User: helper.Org1.Admin,
|
||||
GVR: gvrUsers,
|
||||
})
|
||||
|
||||
legacyUserPayload := `{
|
||||
"name": "Test User 2",
|
||||
"email": "testuser2@example.com",
|
||||
"login": "testuser2",
|
||||
"password": "password123"
|
||||
}`
|
||||
|
||||
rsp := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: "POST",
|
||||
Path: "/api/admin/users",
|
||||
Body: []byte(legacyUserPayload),
|
||||
}, &user.User{})
|
||||
|
||||
require.NotNil(t, rsp)
|
||||
require.Equal(t, 200, rsp.Response.StatusCode)
|
||||
require.NotEmpty(t, rsp.Result.UID)
|
||||
|
||||
// Now try to fetch the user via the new API
|
||||
user, err := userClient.Resource.Get(context.Background(), rsp.Result.UID, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, user)
|
||||
|
||||
// Verify fetched user matches created user
|
||||
userSpec := user.Object["spec"].(map[string]interface{})
|
||||
require.Equal(t, "testuser2@example.com", userSpec["email"])
|
||||
require.Equal(t, "testuser2", userSpec["login"])
|
||||
require.Equal(t, "Test User 2", userSpec["name"])
|
||||
require.Equal(t, false, userSpec["provisioned"])
|
||||
|
||||
// Verify metadata
|
||||
require.Equal(t, rsp.Result.UID, user.GetName())
|
||||
require.Equal(t, "default", user.GetNamespace())
|
||||
|
||||
// Now delete the user using the legacy API
|
||||
deleteRsp := apis.DoRequest(helper, apis.RequestParams{
|
||||
User: helper.Org1.Admin,
|
||||
Method: "DELETE",
|
||||
Path: fmt.Sprintf("/api/admin/users/%d", rsp.Result.ID),
|
||||
}, &apis.AnyResource{})
|
||||
require.Equal(t, 200, deleteRsp.Response.StatusCode)
|
||||
|
||||
// Verify deletion
|
||||
_, err = userClient.Resource.Get(ctx, rsp.Result.UID, metav1.GetOptions{})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "not found")
|
||||
})
|
||||
}
|
||||
@@ -4155,7 +4155,8 @@
|
||||
"grafanaAdmin",
|
||||
"login",
|
||||
"name",
|
||||
"provisioned"
|
||||
"provisioned",
|
||||
"role"
|
||||
],
|
||||
"properties": {
|
||||
"disabled": {
|
||||
@@ -4183,9 +4184,12 @@
|
||||
"default": ""
|
||||
},
|
||||
"provisioned": {
|
||||
"description": "What to do with salt, rands and password?",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6139,7 +6143,8 @@
|
||||
"grafanaAdmin",
|
||||
"login",
|
||||
"name",
|
||||
"provisioned"
|
||||
"provisioned",
|
||||
"role"
|
||||
],
|
||||
"properties": {
|
||||
"disabled": {
|
||||
@@ -6167,9 +6172,12 @@
|
||||
"default": ""
|
||||
},
|
||||
"provisioned": {
|
||||
"description": "What to do with salt, rands and password?",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user