Skip to content

Commit

Permalink
Redpanda Roles (#1235)
Browse files Browse the repository at this point in the history
* frontend: remove old rbac api mock

* wip: rbac

* frontend: fix principal type dropdown

* frontend: migrate create user component from modal to page, fix table search

* frontend: add routing for users and roles

* frontend: remove old rbac api mock

* wip: rbac

* frontend: fix principal type dropdown

* frontend: migrate create user component from modal to page, fix table search

* frontend: add routing for users and roles

* proto: initial definition of security api (#1201)

* frontend: remove old rbac api mock

* wip: rbac

* frontend: fix principal type dropdown

* frontend: migrate create user component from modal to page, fix table search

* frontend: add routing for users and roles

* proto: initial definition of security api

* proto: fix filter options and add pagination

* proto: add ListRolesWithMembers

* proto: add next_page_token to list responses, a few fixes from PR feedback

* proto: more security api fixes from review

* proto: update spelling and make role message an object in requests

* proto: remove google.http.api and grpc gateway annotations from security api definitions

* proto: remove ListUserRoles

* proto: remove principal type

* proto: remove ListRolesWithMembers

* use role_name instead of role

Co-authored-by: Martin Schneppenheim <[email protected]>

* proto: use role_name and principal where more appropriate in security api

* proto: use role_name and principal where more appropriate in security api

* proto: make principal not optional in filter

* proto: remove optionals and add tole_name to ListRoleMembersResponse

---------

Co-authored-by: rikimaru0345 <[email protected]>
Co-authored-by: Martin Schneppenheim <[email protected]>

* frontend: add placeholder role memberships for  testing

* frontend: change breadcrumbs in user details page

* proto: remove UpdateRole security API

* add implementations for all basic roleApi methods

* RBAC Users

* Editing users works in RBAC

* Better deleting of ACL in roles

* Adds regexp validation to Roles in RBAC

* Adds number of users to a RBAC role detail

* Delete role only available after typing confirm in a modal

* Changes position of save/cancel buttons in RBAC

* backend: add roles for auth hooks

* frontend: skip over empty role assignements and fix breadcrumbs in user edit page

* Adds Edit button to Roles list

* Cleanup of the RoleCreate page

* Fixes CreateRole page loading

* More safe routing in the RBAC Roles

* backend: rename role authz hooks to use Redpanda

* frontend: align design of update user edit/details to role edit/details

* frontend: remove debug code, merge stash: "PermissionAssignementDetails"

* frontend: fix delete user button in user details

* frontend: update how user create "done" page looks

* update redpanda/ui; use inputMatchText of ConfirmItemDeleteModal for roles and users

* backend: add unimplemented version of security service

* backend: add security service to endpoint capability

* Table heights in rbac

* Users renamed to principals

* Revert URL changes

* frontend: remove ability to create acls for roles in the acl editor, users should use the role details/edit role pages for that

* frontend: only show users in the acls list; acls for roles should be managed in roles details and edit role pages

* frontend: add user to selected roles on user create

* Show assigned roles in a principal table

* Adds duplicity validation for users & roles

* Fixes lint

* frontend: check if SecurityService is available for roles api, and disable roles tab if not

* frontend: also list principals that are only referenced through roles or acls (and are not service accounts)

* frontend: remove "delete" from user details page when it is not a service account

* frontend: add little indicator tag to differentiate between service account (redpanda) users and and custom principals

* Show prefixed ACLs in user details

* backend: fix compatibility endpoint

* Include * in PermissionAssignemntsDetails

* Update frontend/src/components/pages/acls/Models.ts

* Change the name of a button from Principal -> User

* frontend: fix passing cluster acls

* frontend: create empty clusterAcls when editting a role

* frontend: remove user from all its role, when it gets deleted

* frontend: properly redirect to user list after deleting a user

* frontend: change create acl dropdown to disabled, as its always about users (roles should be editted in the RoleCreate and RoleEdit pages)

* frontend: refresh role memberships after deleting a user, so it doesn't become a phantom entry in the list

* backend: add redpanda min version to compatibility endpoint

* frontend: guard roles related components and actions behind roles api… (#1234)

* frontend: guard roles related components and actions behind roles api feature flag

* frontend: add roles api check to rendering and to roles api

* frontend: fix promises and edit user button visibility

* frontend: fix promises

* frontend: fix refreshRoles promise

* frontend: refresh role members after role delete

---------

Co-authored-by: rikimaru0345 <[email protected]>
Co-authored-by: Martin Schneppenheim <[email protected]>
Co-authored-by: Jan Vorcak <[email protected]>
  • Loading branch information
4 people authored Apr 25, 2024
1 parent 4fa60b5 commit c2ad9f3
Show file tree
Hide file tree
Showing 41 changed files with 6,513 additions and 846 deletions.
1 change: 1 addition & 0 deletions backend/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/carlmjohnson/requests v0.23.5
github.com/cloudhut/common v0.10.0
github.com/cloudhut/connect-client v0.0.0-20240122153328-02a3103805d8
github.com/coreos/go-semver v0.3.1
github.com/docker/go-connections v0.5.0
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204
github.com/getkin/kin-openapi v0.124.0
Expand Down
2 changes: 2 additions & 0 deletions backend/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K3
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E=
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
Expand Down
28 changes: 25 additions & 3 deletions backend/pkg/api/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,23 @@ type Hooks struct {
type ConfigConnectRPCRequest struct {
BaseInterceptors []connect.Interceptor
GRPCGatewayMux *runtime.ServeMux
Services map[string]any
}

// ConfigConnectRPCResponse configures connect services.
type ConfigConnectRPCResponse struct {
// Instructs OSS to use these intercptors for all connect services
Interceptors []connect.Interceptor

// Instructs OSS to register these services in addition to the OSS ones
AdditionalServices []ConnectService

// HTTPMiddlewares are middlewares that shall be used for the router
// that serves all ConnectRPC requests.
HTTPMiddlewares []func(http.Handler) http.Handler

// Original, possibly mutated, services
Services map[string]any

// Instructs OSS to register these services in addition to the OSS ones
AdditionalServices []ConnectService
}

// ConnectService is a Connect handler along with its metadata
Expand Down Expand Up @@ -144,6 +148,11 @@ type AuthorizationHooks interface {
CanCreateSchemas(ctx context.Context) (bool, *rest.Error)
CanDeleteSchemas(ctx context.Context) (bool, *rest.Error)
CanManageSchemaRegistry(ctx context.Context) (bool, *rest.Error)

// Kafka Role Hooks
CanListRedpandaRoles(ctx context.Context) (bool, *rest.Error)
CanCreateRedpandaRoles(ctx context.Context) (bool, *rest.Error)
CanDeleteRedpandaRoles(ctx context.Context) (bool, *rest.Error)
}

// ConsoleHooks are hooks for providing additional context to the Frontend where needed.
Expand Down Expand Up @@ -209,6 +218,7 @@ func (*defaultHooks) ConfigConnectRPC(req ConfigConnectRPCRequest) ConfigConnect
return ConfigConnectRPCResponse{
Interceptors: req.BaseInterceptors,
AdditionalServices: []ConnectService{},
Services: req.Services,
}
}

Expand Down Expand Up @@ -379,3 +389,15 @@ func (*defaultHooks) AdditionalLogFields(_ context.Context) []zapcore.Field {
func (*defaultHooks) EnabledConnectClusterFeatures(_ context.Context, _ string) []pkgconnect.ClusterFeature {
return nil
}

func (*defaultHooks) CanListRedpandaRoles(_ context.Context) (bool, *rest.Error) {
return true, nil
}

func (*defaultHooks) CanCreateRedpandaRoles(_ context.Context) (bool, *rest.Error) {
return true, nil
}

func (*defaultHooks) CanDeleteRedpandaRoles(_ context.Context) (bool, *rest.Error) {
return true, nil
}
5 changes: 5 additions & 0 deletions backend/pkg/api/hooks/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ type AuthorizationHooks interface {
CanCreateSchemas(ctx context.Context) (bool, *rest.Error)
CanDeleteSchemas(ctx context.Context) (bool, *rest.Error)
CanManageSchemaRegistry(ctx context.Context) (bool, *rest.Error)

// Redpanda Role Hooks
CanListRedpandaRoles(ctx context.Context) (bool, *rest.Error)
CanCreateRedpandaRoles(ctx context.Context) (bool, *rest.Error)
CanDeleteRedpandaRoles(ctx context.Context) (bool, *rest.Error)
}

// ConsoleHooks are hooks for providing additional context to the Frontend where needed.
Expand Down
45 changes: 45 additions & 0 deletions backend/pkg/api/mocks/authz_hooks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 44 additions & 14 deletions backend/pkg/api/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,28 @@ func (api *API) setupConnectWithGRPCGateway(r chi.Router) {
runtime.WithUnescapingMode(runtime.UnescapingModeAllExceptReserved),
)

// Create OSS Connect handlers only after calling hook. We need the hook output's final list of interceptors.
userSvc := apiusersvc.NewService(api.Cfg, api.Logger.Named("user_service"), api.RedpandaSvc, api.ConsoleSvc, api.Hooks.Authorization.IsProtectedKafkaUser)
aclSvc := apiaclsvc.NewService(api.Cfg, api.Logger.Named("kafka_service"), api.ConsoleSvc)
kafkaConnectSvc := apikafkaconnectsvc.NewService(api.Cfg, api.Logger.Named("kafka_connect_service"), api.ConnectSvc)
topicSvc := topicsvc.NewService(api.Cfg, api.Logger.Named("topic_service"), api.ConsoleSvc)
transformSvc := transformsvc.NewService(api.Cfg, api.Logger.Named("transform_service"), api.RedpandaSvc, v)
consoleSvc := consolesvc.NewService(api.Logger.Named("console_service"), api.ConsoleSvc, api.Hooks.Authorization)
securitySvc := consolev1alpha1connect.UnimplementedSecurityServiceHandler{}

// Call Hook
hookOutput := api.Hooks.Route.ConfigConnectRPC(ConfigConnectRPCRequest{
BaseInterceptors: baseInterceptors,
GRPCGatewayMux: gwMux,
Services: map[string]any{
dataplanev1alpha1connect.UserServiceName: userSvc,
dataplanev1alpha1connect.ACLServiceName: aclSvc,
dataplanev1alpha1connect.KafkaConnectServiceName: kafkaConnectSvc,
dataplanev1alpha1connect.TopicServiceName: topicSvc,
dataplanev1alpha1connect.TransformServiceName: transformSvc,
consolev1alpha1connect.ConsoleServiceName: consoleSvc,
consolev1alpha1connect.SecurityServiceName: securitySvc,
},
})

// Use HTTP Middlewares that are configured by the Hook
Expand All @@ -102,23 +120,30 @@ func (api *API) setupConnectWithGRPCGateway(r chi.Router) {
}
r.Mount("/v1alpha1", gwMux) // Dataplane API

// Create OSS Connect handlers only after calling hook. We need the hook output's final list of interceptors.
userSvc := apiusersvc.NewService(api.Cfg, api.Logger.Named("user_service"), api.RedpandaSvc, api.ConsoleSvc, api.Hooks.Authorization.IsProtectedKafkaUser)
aclSvc := apiaclsvc.NewService(api.Cfg, api.Logger.Named("kafka_service"), api.ConsoleSvc)
consoleSvc := consolesvc.NewService(api.Logger.Named("console_service"), api.ConsoleSvc, api.Hooks.Authorization)
kafkaConnectSvc := apikafkaconnectsvc.NewService(api.Cfg, api.Logger.Named("kafka_connect_service"), api.ConnectSvc)
topicSvc := topicsvc.NewService(api.Cfg, api.Logger.Named("topic_service"), api.ConsoleSvc)
transformSvc := transformsvc.NewService(api.Cfg, api.Logger.Named("transform_service"), api.RedpandaSvc, v)

// Wasm Transforms
r.Put("/v1alpha1/transforms", transformSvc.HandleDeployTransform())

userSvcPath, userSvcHandler := dataplanev1alpha1connect.NewUserServiceHandler(userSvc, connect.WithInterceptors(hookOutput.Interceptors...))
aclSvcPath, aclSvcHandler := dataplanev1alpha1connect.NewACLServiceHandler(aclSvc, connect.WithInterceptors(hookOutput.Interceptors...))
kafkaConnectPath, kafkaConnectHandler := dataplanev1alpha1connect.NewKafkaConnectServiceHandler(kafkaConnectSvc, connect.WithInterceptors(hookOutput.Interceptors...))
consoleServicePath, consoleServiceHandler := consolev1alpha1connect.NewConsoleServiceHandler(consoleSvc, connect.WithInterceptors(hookOutput.Interceptors...))
topicSvcPath, topicSvcHandler := dataplanev1alpha1connect.NewTopicServiceHandler(topicSvc, connect.WithInterceptors(hookOutput.Interceptors...))
transformSvcPath, transformSvcHandler := dataplanev1alpha1connect.NewTransformServiceHandler(transformSvc, connect.WithInterceptors(hookOutput.Interceptors...))
userSvcPath, userSvcHandler := dataplanev1alpha1connect.NewUserServiceHandler(
hookOutput.Services[dataplanev1alpha1connect.UserServiceName].(dataplanev1alpha1connect.UserServiceHandler),
connect.WithInterceptors(hookOutput.Interceptors...))
aclSvcPath, aclSvcHandler := dataplanev1alpha1connect.NewACLServiceHandler(
hookOutput.Services[dataplanev1alpha1connect.ACLServiceName].(dataplanev1alpha1connect.ACLServiceHandler),
connect.WithInterceptors(hookOutput.Interceptors...))
kafkaConnectPath, kafkaConnectHandler := dataplanev1alpha1connect.NewKafkaConnectServiceHandler(
hookOutput.Services[dataplanev1alpha1connect.KafkaConnectServiceName].(dataplanev1alpha1connect.KafkaConnectServiceHandler),
connect.WithInterceptors(hookOutput.Interceptors...))
topicSvcPath, topicSvcHandler := dataplanev1alpha1connect.NewTopicServiceHandler(
hookOutput.Services[dataplanev1alpha1connect.TopicServiceName].(dataplanev1alpha1connect.TopicServiceHandler),
connect.WithInterceptors(hookOutput.Interceptors...))
transformSvcPath, transformSvcHandler := dataplanev1alpha1connect.NewTransformServiceHandler(
hookOutput.Services[dataplanev1alpha1connect.TransformServiceName].(dataplanev1alpha1connect.TransformServiceHandler),
connect.WithInterceptors(hookOutput.Interceptors...))
consoleServicePath, consoleServiceHandler := consolev1alpha1connect.NewConsoleServiceHandler(
hookOutput.Services[consolev1alpha1connect.ConsoleServiceName].(consolev1alpha1connect.ConsoleServiceHandler),
connect.WithInterceptors(hookOutput.Interceptors...))
securityServicePath, securityServiceHandler := consolev1alpha1connect.NewSecurityServiceHandler(
hookOutput.Services[consolev1alpha1connect.SecurityServiceName].(consolev1alpha1connect.SecurityServiceHandler),
connect.WithInterceptors(hookOutput.Interceptors...))

ossServices := []ConnectService{
{
Expand Down Expand Up @@ -151,6 +176,11 @@ func (api *API) setupConnectWithGRPCGateway(r chi.Router) {
MountPath: transformSvcPath,
Handler: transformSvcHandler,
},
{
ServiceName: consolev1alpha1connect.SecurityServiceName,
MountPath: securityServicePath,
Handler: securityServiceHandler,
},
}

// Order matters. OSS services first, so Enterprise handlers override OSS.
Expand Down
33 changes: 29 additions & 4 deletions backend/pkg/console/endpoint_compatibility.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@ import (
"context"
"fmt"

"github.com/coreos/go-semver/semver"
"github.com/twmb/franz-go/pkg/kmsg"
"github.com/twmb/franz-go/pkg/kversion"

"github.com/redpanda-data/console/backend/pkg/protogen/redpanda/api/console/v1alpha1/consolev1alpha1connect"
)

// EndpointCompatibility describes what Console endpoints can be offered to the frontend,
Expand Down Expand Up @@ -46,10 +49,11 @@ func (s *Service) GetEndpointCompatibility(ctx context.Context) (EndpointCompati

// Required kafka requests per API endpoint
type endpoint struct {
URL string
Method string
Requests []kmsg.Request
HasRedpandaAPI bool
URL string
Method string
Requests []kmsg.Request
HasRedpandaAPI bool
MinRedpandaVersion string
}
endpointRequirements := []endpoint{
{
Expand Down Expand Up @@ -115,12 +119,25 @@ func (s *Service) GetEndpointCompatibility(ctx context.Context) (EndpointCompati
Requests: []kmsg.Request{&kmsg.AlterUserSCRAMCredentialsRequest{}},
HasRedpandaAPI: true,
},
{
URL: consolev1alpha1connect.SecurityServiceName,
Method: "POST",
HasRedpandaAPI: true,
MinRedpandaVersion: "24.1.1-rc5", // no v prefix
},
}

endpoints := make([]EndpointCompatibilityEndpoint, 0, len(endpointRequirements))
for _, endpointReq := range endpointRequirements {
endpointSupported := true

if len(endpointReq.Requests) == 0 {
// not a Kafka API endpoint
// it requires Redpanda Admin API only
// default it to false
endpointSupported = false
}

for _, req := range endpointReq.Requests {
_, isSupported := versions.LookupMaxKeyVersion(req.Key())
if !isSupported {
Expand All @@ -134,6 +151,14 @@ func (s *Service) GetEndpointCompatibility(ctx context.Context) (EndpointCompati
// this feature anyways.
if endpointReq.HasRedpandaAPI && s.redpandaSvc != nil {
endpointSupported = true

if endpointReq.MinRedpandaVersion != "" {
minV, _ := semver.NewVersion(endpointReq.MinRedpandaVersion)
rpV, _ := semver.NewVersion(s.redpandaSvc.ClusterVersion())
if minV != nil && rpV != nil {
endpointSupported = rpV.Compare(*minV) >= 0
}
}
}

endpoints = append(endpoints, EndpointCompatibilityEndpoint{
Expand Down
Loading

0 comments on commit c2ad9f3

Please sign in to comment.