diff --git a/Makefile b/Makefile index 232875402..bd0f3d39c 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ api: .PHONY: config # generate config proto config: - cd ./internal/credentials/api && buf generate + cd ./pkg/credentials/api && buf generate make -C ./app/controlplane config make -C ./app/artifact-cas config diff --git a/app/controlplane/cmd/wire.go b/app/controlplane/cmd/wire.go index 296a6b98d..776c9c631 100644 --- a/app/controlplane/cmd/wire.go +++ b/app/controlplane/cmd/wire.go @@ -48,7 +48,7 @@ func wireApp(*conf.Bootstrap, credentials.ReaderWriter, log.Logger, sdk.Availabl wire.Bind(new(biz.CASClient), new(*biz.CASClientUseCase)), serviceOpts, wire.Value([]biz.CASClientOpts{}), - wire.FieldsOf(new(*conf.Bootstrap), "Server", "Auth", "Data", "CasServer", "ReferrerSharedIndex"), + wire.FieldsOf(new(*conf.Bootstrap), "Server", "Auth", "Data", "CasServer", "ReferrerSharedIndex", "Onboarding"), wire.FieldsOf(new(*conf.Data), "Database"), dispatcher.New, authz.NewDatabaseEnforcer, diff --git a/app/controlplane/cmd/wire_gen.go b/app/controlplane/cmd/wire_gen.go index 383ff3b80..e7f17e699 100644 --- a/app/controlplane/cmd/wire_gen.go +++ b/app/controlplane/cmd/wire_gen.go @@ -48,12 +48,14 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l Logger: logger, } integrationUseCase := biz.NewIntegrationUseCase(newIntegrationUseCaseOpts) - organizationUseCase := biz.NewOrganizationUseCase(organizationRepo, casBackendUseCase, integrationUseCase, membershipRepo, logger) + v := bootstrap.Onboarding + organizationUseCase := biz.NewOrganizationUseCase(organizationRepo, casBackendUseCase, integrationUseCase, membershipRepo, v, logger) membershipUseCase := biz.NewMembershipUseCase(membershipRepo, organizationUseCase, logger) newUserUseCaseParams := &biz.NewUserUseCaseParams{ UserRepo: userRepo, MembershipUseCase: membershipUseCase, OrganizationUseCase: organizationUseCase, + OnboardingConfig: v, Logger: logger, } userUseCase := biz.NewUserUseCase(newUserUseCaseParams) @@ -66,8 +68,8 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l return nil, nil, err } bootstrap_CASServer := bootstrap.CasServer - v := _wireValue - casClientUseCase := biz.NewCASClientUseCase(casCredentialsUseCase, bootstrap_CASServer, logger, v...) + v2 := _wireValue + casClientUseCase := biz.NewCASClientUseCase(casCredentialsUseCase, bootstrap_CASServer, logger, v2...) referrerRepo := data.NewReferrerRepo(dataData, workflowRepo, logger) referrerSharedIndex := bootstrap.ReferrerSharedIndex referrerUseCase, err := biz.NewReferrerUseCase(referrerRepo, workflowRepo, membershipRepo, referrerSharedIndex, logger) @@ -89,8 +91,8 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l workflowContractRepo := data.NewWorkflowContractRepo(dataData, logger) workflowContractUseCase := biz.NewWorkflowContractUseCase(workflowContractRepo, logger) workflowUseCase := biz.NewWorkflowUsecase(workflowRepo, workflowContractUseCase, logger) - v2 := serviceOpts(logger) - workflowService := service.NewWorkflowService(workflowUseCase, v2...) + v3 := serviceOpts(logger) + workflowService := service.NewWorkflowService(workflowUseCase, v3...) orgInvitationRepo := data.NewOrgInvitation(dataData, logger) orgInvitationUseCase, err := biz.NewOrgInvitationUseCase(orgInvitationRepo, membershipRepo, userRepo, logger) if err != nil { @@ -98,12 +100,12 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l return nil, nil, err } confServer := bootstrap.Server - authService, err := service.NewAuthService(userUseCase, organizationUseCase, membershipUseCase, orgInvitationUseCase, auth, confServer, v2...) + authService, err := service.NewAuthService(userUseCase, organizationUseCase, membershipUseCase, orgInvitationUseCase, auth, confServer, v3...) if err != nil { cleanup() return nil, nil, err } - robotAccountService := service.NewRobotAccountService(robotAccountUseCase, v2...) + robotAccountService := service.NewRobotAccountService(robotAccountUseCase, v3...) workflowRunRepo := data.NewWorkflowRunRepo(dataData, logger) workflowRunUseCase, err := biz.NewWorkflowRunUseCase(workflowRunRepo, workflowRepo, logger) if err != nil { @@ -115,7 +117,7 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l WorkflowUC: workflowUseCase, WorkflowContractUC: workflowContractUseCase, CredsReader: readerWriter, - Opts: v2, + Opts: v3, } workflowRunService := service.NewWorkflowRunService(newWorkflowRunServiceOpts) attestationUseCase := biz.NewAttestationUseCase(casClientUseCase, logger) @@ -135,30 +137,30 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l CASMappingUseCase: casMappingUseCase, ReferrerUC: referrerUseCase, OrgUC: organizationUseCase, - Opts: v2, + Opts: v3, } attestationService := service.NewAttestationService(newAttestationServiceOpts) - workflowContractService := service.NewWorkflowSchemaService(workflowContractUseCase, v2...) - contextService := service.NewContextService(casBackendUseCase, userUseCase, v2...) - casCredentialsService := service.NewCASCredentialsService(casCredentialsUseCase, casMappingUseCase, casBackendUseCase, enforcer, v2...) + workflowContractService := service.NewWorkflowSchemaService(workflowContractUseCase, v3...) + contextService := service.NewContextService(casBackendUseCase, userUseCase, v3...) + casCredentialsService := service.NewCASCredentialsService(casCredentialsUseCase, casMappingUseCase, casBackendUseCase, enforcer, v3...) orgMetricsRepo := data.NewOrgMetricsRepo(dataData, logger) orgMetricsUseCase, err := biz.NewOrgMetricsUseCase(orgMetricsRepo, logger) if err != nil { cleanup() return nil, nil, err } - orgMetricsService := service.NewOrgMetricsService(orgMetricsUseCase, v2...) - integrationsService := service.NewIntegrationsService(integrationUseCase, workflowUseCase, availablePlugins, v2...) - organizationService := service.NewOrganizationService(membershipUseCase, organizationUseCase, v2...) - casBackendService := service.NewCASBackendService(casBackendUseCase, providers, v2...) - casRedirectService, err := service.NewCASRedirectService(casMappingUseCase, casCredentialsUseCase, bootstrap_CASServer, v2...) + orgMetricsService := service.NewOrgMetricsService(orgMetricsUseCase, v3...) + integrationsService := service.NewIntegrationsService(integrationUseCase, workflowUseCase, availablePlugins, v3...) + organizationService := service.NewOrganizationService(membershipUseCase, organizationUseCase, v3...) + casBackendService := service.NewCASBackendService(casBackendUseCase, providers, v3...) + casRedirectService, err := service.NewCASRedirectService(casMappingUseCase, casCredentialsUseCase, bootstrap_CASServer, v3...) if err != nil { cleanup() return nil, nil, err } - orgInvitationService := service.NewOrgInvitationService(orgInvitationUseCase, v2...) - referrerService := service.NewReferrerService(referrerUseCase, v2...) - apiTokenService := service.NewAPITokenService(apiTokenUseCase, v2...) + orgInvitationService := service.NewOrgInvitationService(orgInvitationUseCase, v3...) + referrerService := service.NewReferrerService(referrerUseCase, v3...) + apiTokenService := service.NewAPITokenService(apiTokenUseCase, v3...) attestationStateRepo := data.NewAttestationStateRepo(dataData, logger) attestationStateUseCase, err := biz.NewAttestationStateUseCase(attestationStateRepo, workflowRunRepo) if err != nil { @@ -169,12 +171,12 @@ func wireApp(bootstrap *conf.Bootstrap, readerWriter credentials.ReaderWriter, l AttestationStateUseCase: attestationStateUseCase, WorkflowUseCase: workflowUseCase, WorkflowRunUseCase: workflowRunUseCase, - Opts: v2, + Opts: v3, } attestationStateService := service.NewAttestationStateService(newAttestationStateServiceOpt) - userService := service.NewUserService(membershipUseCase, organizationUseCase, v2...) + userService := service.NewUserService(membershipUseCase, organizationUseCase, v3...) signingUseCase := biz.NewChainloopSigningUseCase(certificateAuthority) - signingService := service.NewSigningService(signingUseCase, v2...) + signingService := service.NewSigningService(signingUseCase, v3...) validator, err := newProtoValidator() if err != nil { cleanup() diff --git a/app/controlplane/configs/config.devel.yaml b/app/controlplane/configs/config.devel.yaml index 49b5c379a..9bcfedba0 100644 --- a/app/controlplane/configs/config.devel.yaml +++ b/app/controlplane/configs/config.devel.yaml @@ -58,3 +58,4 @@ auth: # enabled: true # allowed_orgs: # - deadbeef + diff --git a/app/controlplane/configs/samples/config.yaml b/app/controlplane/configs/samples/config.yaml index 7bcccfda4..d4121d2b1 100644 --- a/app/controlplane/configs/samples/config.yaml +++ b/app/controlplane/configs/samples/config.yaml @@ -56,4 +56,10 @@ credentials_service: referrer_shared_index: enabled: true allowed_orgs: - - deadbeef \ No newline at end of file + - deadbeef + +onboarding: + - name: "read-only-demo" + role: "viewer" + - name: "read-write-demo" + role: "owner" \ No newline at end of file diff --git a/app/controlplane/internal/conf/buf.lock b/app/controlplane/internal/conf/buf.lock index f1d8f9aa8..a55648428 100644 --- a/app/controlplane/internal/conf/buf.lock +++ b/app/controlplane/internal/conf/buf.lock @@ -4,8 +4,12 @@ deps: - remote: buf.build owner: bufbuild repository: protovalidate - commit: b983156c5e994cc9892e0ce3e64e17e0 + commit: 46a4cf4ba1094a34bcd89a6c67163b4b - remote: buf.build owner: googleapis repository: googleapis - commit: 7a6bc1e3207144b38e9066861e1de0ff + commit: f0e53af8f2fc4556b94f482688b57223 + - remote: buf.build + owner: kratos-go + repository: kratos + commit: e1d52e944e3845c6862a566db322432d diff --git a/app/controlplane/internal/conf/buf.yaml b/app/controlplane/internal/conf/buf.yaml index 097510831..c98a57a3d 100644 --- a/app/controlplane/internal/conf/buf.yaml +++ b/app/controlplane/internal/conf/buf.yaml @@ -5,6 +5,7 @@ breaking: deps: - buf.build/googleapis/googleapis - buf.build/bufbuild/protovalidate + - buf.build/kratos-go/kratos lint: use: - DEFAULT diff --git a/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go b/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go index 4f0a6a605..4b02e8abd 100644 --- a/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go +++ b/app/controlplane/internal/conf/controlplane/config/v1/conf.pb.go @@ -23,6 +23,7 @@ package conf import ( _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + v11 "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" v1 "github.com/chainloop-dev/chainloop/pkg/credentials/api/credentials/v1" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -57,6 +58,8 @@ type Bootstrap struct { ReferrerSharedIndex *ReferrerSharedIndex `protobuf:"bytes,8,opt,name=referrer_shared_index,json=referrerSharedIndex,proto3" json:"referrer_shared_index,omitempty"` // The certificate authority used for keyless signing CertificateAuthority *CA `protobuf:"bytes,9,opt,name=certificate_authority,json=certificateAuthority,proto3" json:"certificate_authority,omitempty"` + // Configuration for onboarding users in organizations with specific roles + Onboarding []*OnboardingSpec `protobuf:"bytes,10,rep,name=onboarding,proto3" json:"onboarding,omitempty"` } func (x *Bootstrap) Reset() { @@ -154,6 +157,13 @@ func (x *Bootstrap) GetCertificateAuthority() *CA { return nil } +func (x *Bootstrap) GetOnboarding() []*OnboardingSpec { + if x != nil { + return x.Onboarding + } + return nil +} + // Configuration used to enable a shared index API endpoint that can be used to discover metadata referrers // To populate the shared index you need to enable the feature and configure the allowed orgs // The reason to have an org allowList is to avoid leaking metadata from other organizations and set the stage for a trusted publisher model @@ -467,6 +477,64 @@ type CA_FileCa struct { func (*CA_FileCa) isCA_Ca() {} +// OnboardingSpec is a configuration to automatically onboard users in organizations with specific roles +type OnboardingSpec struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Name of the organization + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Role to assign to the user + Role v11.MembershipRole `protobuf:"varint,2,opt,name=role,proto3,enum=controlplane.v1.MembershipRole" json:"role,omitempty"` +} + +func (x *OnboardingSpec) Reset() { + *x = OnboardingSpec{} + if protoimpl.UnsafeEnabled { + mi := &file_controlplane_config_v1_conf_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *OnboardingSpec) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*OnboardingSpec) ProtoMessage() {} + +func (x *OnboardingSpec) ProtoReflect() protoreflect.Message { + mi := &file_controlplane_config_v1_conf_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use OnboardingSpec.ProtoReflect.Descriptor instead. +func (*OnboardingSpec) Descriptor() ([]byte, []int) { + return file_controlplane_config_v1_conf_proto_rawDescGZIP(), []int{6} +} + +func (x *OnboardingSpec) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *OnboardingSpec) GetRole() v11.MembershipRole { + if x != nil { + return x.Role + } + return v11.MembershipRole(0) +} + type Bootstrap_Observability struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -478,7 +546,7 @@ type Bootstrap_Observability struct { func (x *Bootstrap_Observability) Reset() { *x = Bootstrap_Observability{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[6] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -491,7 +559,7 @@ func (x *Bootstrap_Observability) String() string { func (*Bootstrap_Observability) ProtoMessage() {} func (x *Bootstrap_Observability) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[6] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -532,7 +600,7 @@ type Bootstrap_CASServer struct { func (x *Bootstrap_CASServer) Reset() { *x = Bootstrap_CASServer{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[7] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -545,7 +613,7 @@ func (x *Bootstrap_CASServer) String() string { func (*Bootstrap_CASServer) ProtoMessage() {} func (x *Bootstrap_CASServer) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[7] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -594,7 +662,7 @@ type Bootstrap_Observability_Sentry struct { func (x *Bootstrap_Observability_Sentry) Reset() { *x = Bootstrap_Observability_Sentry{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[8] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -607,7 +675,7 @@ func (x *Bootstrap_Observability_Sentry) String() string { func (*Bootstrap_Observability_Sentry) ProtoMessage() {} func (x *Bootstrap_Observability_Sentry) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[8] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -653,7 +721,7 @@ type Server_HTTP struct { func (x *Server_HTTP) Reset() { *x = Server_HTTP{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[9] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -666,7 +734,7 @@ func (x *Server_HTTP) String() string { func (*Server_HTTP) ProtoMessage() {} func (x *Server_HTTP) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[9] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -723,7 +791,7 @@ type Server_TLS struct { func (x *Server_TLS) Reset() { *x = Server_TLS{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[10] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -736,7 +804,7 @@ func (x *Server_TLS) String() string { func (*Server_TLS) ProtoMessage() {} func (x *Server_TLS) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[10] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -780,7 +848,7 @@ type Server_GRPC struct { func (x *Server_GRPC) Reset() { *x = Server_GRPC{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[11] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -793,7 +861,7 @@ func (x *Server_GRPC) String() string { func (*Server_GRPC) ProtoMessage() {} func (x *Server_GRPC) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[11] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -849,7 +917,7 @@ type Data_Database struct { func (x *Data_Database) Reset() { *x = Data_Database{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[12] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -862,7 +930,7 @@ func (x *Data_Database) String() string { func (*Data_Database) ProtoMessage() {} func (x *Data_Database) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[12] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -906,7 +974,7 @@ type Auth_OIDC struct { func (x *Auth_OIDC) Reset() { *x = Auth_OIDC{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[13] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -919,7 +987,7 @@ func (x *Auth_OIDC) String() string { func (*Auth_OIDC) ProtoMessage() {} func (x *Auth_OIDC) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[13] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -976,7 +1044,7 @@ type CA_FileCA struct { func (x *CA_FileCA) Reset() { *x = CA_FileCA{} if protoimpl.UnsafeEnabled { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[14] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -989,7 +1057,7 @@ func (x *CA_FileCA) String() string { func (*CA_FileCA) ProtoMessage() {} func (x *CA_FileCA) ProtoReflect() protoreflect.Message { - mi := &file_controlplane_config_v1_conf_proto_msgTypes[14] + mi := &file_controlplane_config_v1_conf_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1038,156 +1106,170 @@ var file_controlplane_config_v1_conf_proto_rawDesc = []byte{ 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x99, 0x07, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, - 0x72, 0x61, 0x70, 0x12, 0x36, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x27, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x5f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe1, + 0x07, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x12, 0x36, 0x0a, 0x06, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x06, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, + 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x61, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, + 0x74, 0x68, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x12, 0x55, 0x0a, 0x0d, 0x6f, 0x62, 0x73, 0x65, + 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, + 0x61, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, + 0x52, 0x0d, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, + 0x4c, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x5f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x52, 0x12, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, + 0x0a, 0x63, 0x61, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x6f, 0x74, 0x73, + 0x74, 0x72, 0x61, 0x70, 0x2e, 0x43, 0x41, 0x53, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x09, + 0x63, 0x61, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6c, 0x75, + 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x44, 0x69, 0x72, 0x12, 0x5f, 0x0a, 0x15, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x72, 0x65, 0x72, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x76, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x30, 0x0a, - 0x04, 0x61, 0x75, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x12, - 0x55, 0x0a, 0x0d, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, + 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x13, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x72, + 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x4f, 0x0a, 0x15, 0x63, + 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, + 0x72, 0x69, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, + 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x41, 0x52, 0x14, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x46, 0x0a, 0x0a, + 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x26, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x53, 0x70, 0x65, 0x63, 0x52, 0x0a, 0x6f, 0x6e, 0x62, 0x6f, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x1a, 0x9d, 0x01, 0x0a, 0x0d, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x4e, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x72, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0d, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x4c, 0x0a, 0x13, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, - 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, - 0x52, 0x12, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x0a, 0x63, 0x61, 0x73, 0x5f, 0x73, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, - 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2e, 0x43, 0x41, 0x53, 0x53, - 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x09, 0x63, 0x61, 0x73, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x5f, 0x64, 0x69, 0x72, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x73, 0x44, 0x69, - 0x72, 0x12, 0x5f, 0x0a, 0x15, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x72, 0x5f, 0x73, 0x68, - 0x61, 0x72, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x65, 0x72, 0x72, - 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x13, 0x72, - 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x12, 0x4f, 0x0a, 0x15, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, - 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x41, 0x52, 0x14, 0x63, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x1a, 0x9d, 0x01, 0x0a, 0x0d, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x4e, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, - 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x42, - 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2e, 0x4f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x53, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x73, - 0x65, 0x6e, 0x74, 0x72, 0x79, 0x1a, 0x3c, 0x0a, 0x06, 0x53, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x64, 0x73, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x73, - 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, - 0x65, 0x6e, 0x74, 0x1a, 0x8b, 0x01, 0x0a, 0x09, 0x43, 0x41, 0x53, 0x53, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x12, 0x3f, 0x0a, 0x04, 0x67, 0x72, 0x70, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x53, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, + 0x73, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x1a, 0x3c, 0x0a, 0x06, 0x53, 0x65, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x64, 0x73, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, + 0x73, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, + 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0x8b, 0x01, 0x0a, 0x09, 0x43, 0x41, 0x53, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x04, 0x67, 0x72, 0x70, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x2e, 0x47, 0x52, 0x50, 0x43, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x04, 0x67, + 0x72, 0x70, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, + 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x75, 0x72, 0x6c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x55, + 0x72, 0x6c, 0x22, 0x52, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x72, 0x53, 0x68, + 0x61, 0x72, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, + 0x72, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, + 0x65, 0x64, 0x4f, 0x72, 0x67, 0x73, 0x22, 0xd3, 0x04, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x12, 0x37, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, - 0x47, 0x52, 0x50, 0x43, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x04, 0x67, 0x72, - 0x70, 0x63, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x69, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x21, - 0x0a, 0x0c, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x55, 0x72, - 0x6c, 0x22, 0x52, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x72, 0x53, 0x68, 0x61, - 0x72, 0x65, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x6f, 0x72, - 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, - 0x64, 0x4f, 0x72, 0x67, 0x73, 0x22, 0xd3, 0x04, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x12, 0x37, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, - 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, - 0x54, 0x54, 0x50, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x37, 0x0a, 0x04, 0x67, 0x72, 0x70, - 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, - 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x47, 0x52, 0x50, 0x43, 0x52, 0x04, 0x67, 0x72, - 0x70, 0x63, 0x12, 0x46, 0x0a, 0x0c, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, - 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x48, 0x54, 0x54, 0x50, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x12, 0x37, 0x0a, 0x04, 0x67, 0x72, + 0x70, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x0b, 0x68, - 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0x8c, 0x01, 0x0a, 0x04, 0x48, - 0x54, 0x54, 0x50, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, - 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, - 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x75, 0x72, - 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x55, 0x72, 0x6c, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x47, 0x52, 0x50, 0x43, 0x52, 0x04, 0x67, + 0x72, 0x70, 0x63, 0x12, 0x46, 0x0a, 0x0c, 0x68, 0x74, 0x74, 0x70, 0x5f, 0x6d, 0x65, 0x74, 0x72, + 0x69, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x0b, + 0x68, 0x74, 0x74, 0x70, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x1a, 0x8c, 0x01, 0x0a, 0x04, + 0x48, 0x54, 0x54, 0x50, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, + 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, + 0x64, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x75, + 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x55, 0x72, 0x6c, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x1a, 0x48, 0x0a, 0x03, 0x54, 0x4c, + 0x53, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, + 0x65, 0x4b, 0x65, 0x79, 0x1a, 0xb5, 0x01, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x12, 0x18, 0x0a, + 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1b, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, + 0x61, 0x64, 0x64, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x1a, 0x48, 0x0a, 0x03, 0x54, 0x4c, 0x53, - 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, - 0x4b, 0x65, 0x79, 0x1a, 0xb5, 0x01, 0x0a, 0x04, 0x47, 0x52, 0x50, 0x43, 0x12, 0x18, 0x0a, 0x07, - 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, - 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x1b, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x61, - 0x64, 0x64, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x41, 0x0a, 0x0a, 0x74, 0x6c, 0x73, 0x5f, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x4c, 0x53, - 0x52, 0x09, 0x74, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x85, 0x01, 0x0a, 0x04, - 0x44, 0x61, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, - 0x44, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x08, 0x64, - 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x1a, 0x3a, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, - 0x61, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x22, 0xf5, 0x02, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x39, 0x0a, 0x19, - 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6a, 0x77, 0x73, 0x5f, 0x68, 0x6d, - 0x61, 0x63, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x16, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x4a, 0x77, 0x73, 0x48, 0x6d, 0x61, - 0x63, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, - 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x6c, 0x6c, - 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x49, 0x0a, 0x22, 0x63, 0x61, 0x73, 0x5f, 0x72, 0x6f, - 0x62, 0x6f, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x1d, 0x63, 0x61, 0x73, 0x52, 0x6f, 0x62, 0x6f, 0x74, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, 0x74, - 0x68, 0x12, 0x35, 0x0a, 0x04, 0x6f, 0x69, 0x64, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x4f, 0x49, - 0x44, 0x43, 0x52, 0x04, 0x6f, 0x69, 0x64, 0x63, 0x1a, 0x90, 0x01, 0x0a, 0x04, 0x4f, 0x49, 0x44, - 0x43, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x72, - 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x5f, 0x73, 0x63, 0x68, 0x65, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, - 0x63, 0x74, 0x55, 0x72, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x22, 0xa5, 0x01, 0x0a, 0x02, - 0x43, 0x41, 0x12, 0x3c, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, - 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x41, 0x2e, - 0x46, 0x69, 0x6c, 0x65, 0x43, 0x41, 0x48, 0x00, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x61, - 0x1a, 0x5b, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x41, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x65, - 0x72, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, - 0x65, 0x72, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x70, - 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x50, 0x61, - 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x73, 0x73, 0x42, 0x04, 0x0a, - 0x02, 0x63, 0x61, 0x42, 0x5f, 0x5a, 0x5d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x63, 0x6f, - 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, - 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x76, 0x31, 0x3b, - 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x41, 0x0a, 0x0a, 0x74, 0x6c, 0x73, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x54, 0x4c, + 0x53, 0x52, 0x09, 0x74, 0x6c, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x85, 0x01, 0x0a, + 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x41, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, + 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x08, + 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x1a, 0x3a, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, + 0x62, 0x61, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x22, 0xf5, 0x02, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x39, 0x0a, + 0x19, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6a, 0x77, 0x73, 0x5f, 0x68, + 0x6d, 0x61, 0x63, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x16, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x4a, 0x77, 0x73, 0x48, 0x6d, + 0x61, 0x63, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, + 0x77, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x6c, + 0x6c, 0x6f, 0x77, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x49, 0x0a, 0x22, 0x63, 0x61, 0x73, 0x5f, 0x72, + 0x6f, 0x62, 0x6f, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x1d, 0x63, 0x61, 0x73, 0x52, 0x6f, 0x62, 0x6f, 0x74, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x50, 0x61, + 0x74, 0x68, 0x12, 0x35, 0x0a, 0x04, 0x6f, 0x69, 0x64, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2e, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x4f, + 0x49, 0x44, 0x43, 0x52, 0x04, 0x6f, 0x69, 0x64, 0x63, 0x1a, 0x90, 0x01, 0x0a, 0x04, 0x4f, 0x49, + 0x44, 0x43, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2e, 0x0a, 0x13, + 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x6c, 0x5f, 0x73, 0x63, 0x68, + 0x65, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x64, 0x69, 0x72, + 0x65, 0x63, 0x74, 0x55, 0x72, 0x6c, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x22, 0xa5, 0x01, 0x0a, + 0x02, 0x43, 0x41, 0x12, 0x3c, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x61, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, + 0x61, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x41, + 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x41, 0x48, 0x00, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x65, 0x43, + 0x61, 0x1a, 0x5b, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x65, 0x43, 0x41, 0x12, 0x1b, 0x0a, 0x09, 0x63, + 0x65, 0x72, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x63, 0x65, 0x72, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, + 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x50, + 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x73, 0x73, 0x42, 0x04, + 0x0a, 0x02, 0x63, 0x61, 0x22, 0x62, 0x0a, 0x0e, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x67, 0x53, 0x70, 0x65, 0x63, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x33, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x1f, 0x2e, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x6f, + 0x6c, 0x65, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x42, 0x5f, 0x5a, 0x5d, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, + 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x61, + 0x70, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2f, 0x76, 0x31, 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -1202,7 +1284,7 @@ func file_controlplane_config_v1_conf_proto_rawDescGZIP() []byte { return file_controlplane_config_v1_conf_proto_rawDescData } -var file_controlplane_config_v1_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 15) +var file_controlplane_config_v1_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_controlplane_config_v1_conf_proto_goTypes = []interface{}{ (*Bootstrap)(nil), // 0: controlplane.config.v1.Bootstrap (*ReferrerSharedIndex)(nil), // 1: controlplane.config.v1.ReferrerSharedIndex @@ -1210,43 +1292,47 @@ var file_controlplane_config_v1_conf_proto_goTypes = []interface{}{ (*Data)(nil), // 3: controlplane.config.v1.Data (*Auth)(nil), // 4: controlplane.config.v1.Auth (*CA)(nil), // 5: controlplane.config.v1.CA - (*Bootstrap_Observability)(nil), // 6: controlplane.config.v1.Bootstrap.Observability - (*Bootstrap_CASServer)(nil), // 7: controlplane.config.v1.Bootstrap.CASServer - (*Bootstrap_Observability_Sentry)(nil), // 8: controlplane.config.v1.Bootstrap.Observability.Sentry - (*Server_HTTP)(nil), // 9: controlplane.config.v1.Server.HTTP - (*Server_TLS)(nil), // 10: controlplane.config.v1.Server.TLS - (*Server_GRPC)(nil), // 11: controlplane.config.v1.Server.GRPC - (*Data_Database)(nil), // 12: controlplane.config.v1.Data.Database - (*Auth_OIDC)(nil), // 13: controlplane.config.v1.Auth.OIDC - (*CA_FileCA)(nil), // 14: controlplane.config.v1.CA.FileCA - (*v1.Credentials)(nil), // 15: credentials.v1.Credentials - (*durationpb.Duration)(nil), // 16: google.protobuf.Duration + (*OnboardingSpec)(nil), // 6: controlplane.config.v1.OnboardingSpec + (*Bootstrap_Observability)(nil), // 7: controlplane.config.v1.Bootstrap.Observability + (*Bootstrap_CASServer)(nil), // 8: controlplane.config.v1.Bootstrap.CASServer + (*Bootstrap_Observability_Sentry)(nil), // 9: controlplane.config.v1.Bootstrap.Observability.Sentry + (*Server_HTTP)(nil), // 10: controlplane.config.v1.Server.HTTP + (*Server_TLS)(nil), // 11: controlplane.config.v1.Server.TLS + (*Server_GRPC)(nil), // 12: controlplane.config.v1.Server.GRPC + (*Data_Database)(nil), // 13: controlplane.config.v1.Data.Database + (*Auth_OIDC)(nil), // 14: controlplane.config.v1.Auth.OIDC + (*CA_FileCA)(nil), // 15: controlplane.config.v1.CA.FileCA + (*v1.Credentials)(nil), // 16: credentials.v1.Credentials + (v11.MembershipRole)(0), // 17: controlplane.v1.MembershipRole + (*durationpb.Duration)(nil), // 18: google.protobuf.Duration } var file_controlplane_config_v1_conf_proto_depIdxs = []int32{ 2, // 0: controlplane.config.v1.Bootstrap.server:type_name -> controlplane.config.v1.Server 3, // 1: controlplane.config.v1.Bootstrap.data:type_name -> controlplane.config.v1.Data 4, // 2: controlplane.config.v1.Bootstrap.auth:type_name -> controlplane.config.v1.Auth - 6, // 3: controlplane.config.v1.Bootstrap.observability:type_name -> controlplane.config.v1.Bootstrap.Observability - 15, // 4: controlplane.config.v1.Bootstrap.credentials_service:type_name -> credentials.v1.Credentials - 7, // 5: controlplane.config.v1.Bootstrap.cas_server:type_name -> controlplane.config.v1.Bootstrap.CASServer + 7, // 3: controlplane.config.v1.Bootstrap.observability:type_name -> controlplane.config.v1.Bootstrap.Observability + 16, // 4: controlplane.config.v1.Bootstrap.credentials_service:type_name -> credentials.v1.Credentials + 8, // 5: controlplane.config.v1.Bootstrap.cas_server:type_name -> controlplane.config.v1.Bootstrap.CASServer 1, // 6: controlplane.config.v1.Bootstrap.referrer_shared_index:type_name -> controlplane.config.v1.ReferrerSharedIndex 5, // 7: controlplane.config.v1.Bootstrap.certificate_authority:type_name -> controlplane.config.v1.CA - 9, // 8: controlplane.config.v1.Server.http:type_name -> controlplane.config.v1.Server.HTTP - 11, // 9: controlplane.config.v1.Server.grpc:type_name -> controlplane.config.v1.Server.GRPC - 9, // 10: controlplane.config.v1.Server.http_metrics:type_name -> controlplane.config.v1.Server.HTTP - 12, // 11: controlplane.config.v1.Data.database:type_name -> controlplane.config.v1.Data.Database - 13, // 12: controlplane.config.v1.Auth.oidc:type_name -> controlplane.config.v1.Auth.OIDC - 14, // 13: controlplane.config.v1.CA.file_ca:type_name -> controlplane.config.v1.CA.FileCA - 8, // 14: controlplane.config.v1.Bootstrap.Observability.sentry:type_name -> controlplane.config.v1.Bootstrap.Observability.Sentry - 11, // 15: controlplane.config.v1.Bootstrap.CASServer.grpc:type_name -> controlplane.config.v1.Server.GRPC - 16, // 16: controlplane.config.v1.Server.HTTP.timeout:type_name -> google.protobuf.Duration - 16, // 17: controlplane.config.v1.Server.GRPC.timeout:type_name -> google.protobuf.Duration - 10, // 18: controlplane.config.v1.Server.GRPC.tls_config:type_name -> controlplane.config.v1.Server.TLS - 19, // [19:19] is the sub-list for method output_type - 19, // [19:19] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name + 6, // 8: controlplane.config.v1.Bootstrap.onboarding:type_name -> controlplane.config.v1.OnboardingSpec + 10, // 9: controlplane.config.v1.Server.http:type_name -> controlplane.config.v1.Server.HTTP + 12, // 10: controlplane.config.v1.Server.grpc:type_name -> controlplane.config.v1.Server.GRPC + 10, // 11: controlplane.config.v1.Server.http_metrics:type_name -> controlplane.config.v1.Server.HTTP + 13, // 12: controlplane.config.v1.Data.database:type_name -> controlplane.config.v1.Data.Database + 14, // 13: controlplane.config.v1.Auth.oidc:type_name -> controlplane.config.v1.Auth.OIDC + 15, // 14: controlplane.config.v1.CA.file_ca:type_name -> controlplane.config.v1.CA.FileCA + 17, // 15: controlplane.config.v1.OnboardingSpec.role:type_name -> controlplane.v1.MembershipRole + 9, // 16: controlplane.config.v1.Bootstrap.Observability.sentry:type_name -> controlplane.config.v1.Bootstrap.Observability.Sentry + 12, // 17: controlplane.config.v1.Bootstrap.CASServer.grpc:type_name -> controlplane.config.v1.Server.GRPC + 18, // 18: controlplane.config.v1.Server.HTTP.timeout:type_name -> google.protobuf.Duration + 18, // 19: controlplane.config.v1.Server.GRPC.timeout:type_name -> google.protobuf.Duration + 11, // 20: controlplane.config.v1.Server.GRPC.tls_config:type_name -> controlplane.config.v1.Server.TLS + 21, // [21:21] is the sub-list for method output_type + 21, // [21:21] is the sub-list for method input_type + 21, // [21:21] is the sub-list for extension type_name + 21, // [21:21] is the sub-list for extension extendee + 0, // [0:21] is the sub-list for field type_name } func init() { file_controlplane_config_v1_conf_proto_init() } @@ -1328,7 +1414,7 @@ func file_controlplane_config_v1_conf_proto_init() { } } file_controlplane_config_v1_conf_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Bootstrap_Observability); i { + switch v := v.(*OnboardingSpec); i { case 0: return &v.state case 1: @@ -1340,7 +1426,7 @@ func file_controlplane_config_v1_conf_proto_init() { } } file_controlplane_config_v1_conf_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Bootstrap_CASServer); i { + switch v := v.(*Bootstrap_Observability); i { case 0: return &v.state case 1: @@ -1352,7 +1438,7 @@ func file_controlplane_config_v1_conf_proto_init() { } } file_controlplane_config_v1_conf_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Bootstrap_Observability_Sentry); i { + switch v := v.(*Bootstrap_CASServer); i { case 0: return &v.state case 1: @@ -1364,7 +1450,7 @@ func file_controlplane_config_v1_conf_proto_init() { } } file_controlplane_config_v1_conf_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Server_HTTP); i { + switch v := v.(*Bootstrap_Observability_Sentry); i { case 0: return &v.state case 1: @@ -1376,7 +1462,7 @@ func file_controlplane_config_v1_conf_proto_init() { } } file_controlplane_config_v1_conf_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Server_TLS); i { + switch v := v.(*Server_HTTP); i { case 0: return &v.state case 1: @@ -1388,7 +1474,7 @@ func file_controlplane_config_v1_conf_proto_init() { } } file_controlplane_config_v1_conf_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Server_GRPC); i { + switch v := v.(*Server_TLS); i { case 0: return &v.state case 1: @@ -1400,7 +1486,7 @@ func file_controlplane_config_v1_conf_proto_init() { } } file_controlplane_config_v1_conf_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Data_Database); i { + switch v := v.(*Server_GRPC); i { case 0: return &v.state case 1: @@ -1412,7 +1498,7 @@ func file_controlplane_config_v1_conf_proto_init() { } } file_controlplane_config_v1_conf_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Auth_OIDC); i { + switch v := v.(*Data_Database); i { case 0: return &v.state case 1: @@ -1424,6 +1510,18 @@ func file_controlplane_config_v1_conf_proto_init() { } } file_controlplane_config_v1_conf_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Auth_OIDC); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_controlplane_config_v1_conf_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CA_FileCA); i { case 0: return &v.state @@ -1445,7 +1543,7 @@ func file_controlplane_config_v1_conf_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_controlplane_config_v1_conf_proto_rawDesc, NumEnums: 0, - NumMessages: 15, + NumMessages: 16, NumExtensions: 0, NumServices: 0, }, diff --git a/app/controlplane/internal/conf/controlplane/config/v1/conf.proto b/app/controlplane/internal/conf/controlplane/config/v1/conf.proto index a3f0eb740..3f68a3328 100644 --- a/app/controlplane/internal/conf/controlplane/config/v1/conf.proto +++ b/app/controlplane/internal/conf/controlplane/config/v1/conf.proto @@ -20,6 +20,7 @@ package controlplane.config.v1; import "buf/validate/validate.proto"; import "credentials/v1/config.proto"; import "google/protobuf/duration.proto"; +import "controlplane/v1/response_messages.proto"; option go_package = "github.com/chainloop-dev/chainloop/app/controlplane/internal/conf/controlplane/config/v1;conf"; @@ -58,6 +59,9 @@ message Bootstrap { // https://github.com/chainloop-dev/chainloop/blob/126f47b6c0803eac844b8e3e1a21d582f00e4dc6/app/artifact-cas/internal/service/download.go#L34 string download_url = 3; } + + // Configuration for onboarding users in organizations with specific roles + repeated OnboardingSpec onboarding = 10; } // Configuration used to enable a shared index API endpoint that can be used to discover metadata referrers @@ -135,3 +139,11 @@ message CA { string key_pass = 3; } } + +// OnboardingSpec is a configuration to automatically onboard users in organizations with specific roles +message OnboardingSpec { + // Name of the organization + string name = 1 [(buf.validate.field).string.min_len = 1];; + // Role to assign to the user + controlplane.v1.MembershipRole role = 2; +} diff --git a/app/controlplane/internal/service/organization.go b/app/controlplane/internal/service/organization.go index 85105a54d..c87eb0587 100644 --- a/app/controlplane/internal/service/organization.go +++ b/app/controlplane/internal/service/organization.go @@ -121,7 +121,7 @@ func (s *OrganizationService) UpdateMembership(ctx context.Context, req *pb.Orga return nil, err } - m, err := s.membershipUC.UpdateRole(ctx, currentOrg.ID, currentUser.ID, req.MembershipId, pbRoleToBiz(req.Role)) + m, err := s.membershipUC.UpdateRole(ctx, currentOrg.ID, currentUser.ID, req.MembershipId, biz.PbRoleToBiz(req.Role)) if err != nil { return nil, handleUseCaseErr(err, s.log) } diff --git a/app/controlplane/internal/service/orginvitation.go b/app/controlplane/internal/service/orginvitation.go index cae207256..c05b00829 100644 --- a/app/controlplane/internal/service/orginvitation.go +++ b/app/controlplane/internal/service/orginvitation.go @@ -49,7 +49,7 @@ func (s *OrgInvitationService) Create(ctx context.Context, req *pb.OrgInvitation } // Validations and rbac checks are done in the biz layer - i, err := s.useCase.Create(ctx, org.ID, user.ID, req.ReceiverEmail, biz.WithInvitationRole(pbRoleToBiz(req.Role))) + i, err := s.useCase.Create(ctx, org.ID, user.ID, req.ReceiverEmail, biz.WithInvitationRole(biz.PbRoleToBiz(req.Role))) if err != nil { return nil, handleUseCaseErr(err, s.log) } diff --git a/app/controlplane/internal/service/user.go b/app/controlplane/internal/service/user.go index 788556f10..9552b3ad7 100644 --- a/app/controlplane/internal/service/user.go +++ b/app/controlplane/internal/service/user.go @@ -95,19 +95,6 @@ func (s *UserService) DeleteMembership(ctx context.Context, req *pb.DeleteMember return &pb.DeleteMembershipResponse{}, nil } -func pbRoleToBiz(r pb.MembershipRole) authz.Role { - switch r { - case pb.MembershipRole_MEMBERSHIP_ROLE_ORG_OWNER: - return authz.RoleOwner - case pb.MembershipRole_MEMBERSHIP_ROLE_ORG_ADMIN: - return authz.RoleAdmin - case pb.MembershipRole_MEMBERSHIP_ROLE_ORG_VIEWER: - return authz.RoleViewer - default: - return "" - } -} - func bizMembershipToPb(m *biz.Membership) *pb.OrgMembershipItem { item := &pb.OrgMembershipItem{ Id: m.ID.String(), Current: m.Current, diff --git a/app/controlplane/internal/usercontext/apitoken_middleware_test.go b/app/controlplane/internal/usercontext/apitoken_middleware_test.go index 3fa15461a..370efb91a 100644 --- a/app/controlplane/internal/usercontext/apitoken_middleware_test.go +++ b/app/controlplane/internal/usercontext/apitoken_middleware_test.go @@ -101,7 +101,7 @@ func TestWithCurrentAPITokenAndOrgMiddleware(t *testing.T) { orgRepo := bizMocks.NewOrganizationRepo(t) apiTokenUC, err := biz.NewAPITokenUseCase(apiTokenRepo, &conf.Auth{GeneratedJwsHmacSecret: "test"}, nil, nil) require.NoError(t, err) - orgUC := biz.NewOrganizationUseCase(orgRepo, nil, nil, nil, nil) + orgUC := biz.NewOrganizationUseCase(orgRepo, nil, nil, nil, nil, nil) require.NoError(t, err) ctx := context.Background() diff --git a/app/controlplane/pkg/biz/mocks/OrganizationRepo.go b/app/controlplane/pkg/biz/mocks/OrganizationRepo.go index 1c7f314f2..47f537512 100644 --- a/app/controlplane/pkg/biz/mocks/OrganizationRepo.go +++ b/app/controlplane/pkg/biz/mocks/OrganizationRepo.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.20.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ type OrganizationRepo struct { func (_m *OrganizationRepo) Create(ctx context.Context, name string) (*biz.Organization, error) { ret := _m.Called(ctx, name) + if len(ret) == 0 { + panic("no return value specified for Create") + } + var r0 *biz.Organization var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*biz.Organization, error)); ok { @@ -47,6 +51,10 @@ func (_m *OrganizationRepo) Create(ctx context.Context, name string) (*biz.Organ func (_m *OrganizationRepo) Delete(ctx context.Context, ID uuid.UUID) error { ret := _m.Called(ctx, ID) + if len(ret) == 0 { + panic("no return value specified for Delete") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) error); ok { r0 = rf(ctx, ID) @@ -61,6 +69,10 @@ func (_m *OrganizationRepo) Delete(ctx context.Context, ID uuid.UUID) error { func (_m *OrganizationRepo) FindByID(ctx context.Context, orgID uuid.UUID) (*biz.Organization, error) { ret := _m.Called(ctx, orgID) + if len(ret) == 0 { + panic("no return value specified for FindByID") + } + var r0 *biz.Organization var r1 error if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID) (*biz.Organization, error)); ok { @@ -83,10 +95,44 @@ func (_m *OrganizationRepo) FindByID(ctx context.Context, orgID uuid.UUID) (*biz return r0, r1 } +// FindByName provides a mock function with given fields: ctx, name +func (_m *OrganizationRepo) FindByName(ctx context.Context, name string) (*biz.Organization, error) { + ret := _m.Called(ctx, name) + + if len(ret) == 0 { + panic("no return value specified for FindByName") + } + + var r0 *biz.Organization + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*biz.Organization, error)); ok { + return rf(ctx, name) + } + if rf, ok := ret.Get(0).(func(context.Context, string) *biz.Organization); ok { + r0 = rf(ctx, name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*biz.Organization) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // Update provides a mock function with given fields: ctx, id, name func (_m *OrganizationRepo) Update(ctx context.Context, id uuid.UUID, name *string) (*biz.Organization, error) { ret := _m.Called(ctx, id, name) + if len(ret) == 0 { + panic("no return value specified for Update") + } + var r0 *biz.Organization var r1 error if rf, ok := ret.Get(0).(func(context.Context, uuid.UUID, *string) (*biz.Organization, error)); ok { @@ -109,13 +155,12 @@ func (_m *OrganizationRepo) Update(ctx context.Context, id uuid.UUID, name *stri return r0, r1 } -type mockConstructorTestingTNewOrganizationRepo interface { +// NewOrganizationRepo creates a new instance of OrganizationRepo. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewOrganizationRepo(t interface { mock.TestingT Cleanup(func()) -} - -// NewOrganizationRepo creates a new instance of OrganizationRepo. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewOrganizationRepo(t mockConstructorTestingTNewOrganizationRepo) *OrganizationRepo { +}) *OrganizationRepo { mock := &OrganizationRepo{} mock.Mock.Test(t) diff --git a/app/controlplane/pkg/biz/organization.go b/app/controlplane/pkg/biz/organization.go index 9f27b635e..1514c68fb 100644 --- a/app/controlplane/pkg/biz/organization.go +++ b/app/controlplane/pkg/biz/organization.go @@ -21,6 +21,7 @@ import ( "fmt" "time" + conf "github.com/chainloop-dev/chainloop/app/controlplane/internal/conf/controlplane/config/v1" "github.com/go-kratos/kratos/v2/log" "github.com/google/uuid" "github.com/moby/moby/pkg/namesgenerator" @@ -33,6 +34,7 @@ type Organization struct { type OrganizationRepo interface { FindByID(ctx context.Context, orgID uuid.UUID) (*Organization, error) + FindByName(ctx context.Context, name string) (*Organization, error) Create(ctx context.Context, name string) (*Organization, error) Update(ctx context.Context, id uuid.UUID, name *string) (*Organization, error) Delete(ctx context.Context, ID uuid.UUID) error @@ -44,14 +46,16 @@ type OrganizationUseCase struct { casBackendUseCase *CASBackendUseCase integrationUC *IntegrationUseCase membershipRepo MembershipRepo + onboardingConfig []*conf.OnboardingSpec } -func NewOrganizationUseCase(repo OrganizationRepo, repoUC *CASBackendUseCase, iUC *IntegrationUseCase, mRepo MembershipRepo, logger log.Logger) *OrganizationUseCase { +func NewOrganizationUseCase(repo OrganizationRepo, repoUC *CASBackendUseCase, iUC *IntegrationUseCase, mRepo MembershipRepo, onboardingConfig []*conf.OnboardingSpec, logger log.Logger) *OrganizationUseCase { return &OrganizationUseCase{orgRepo: repo, logger: log.NewHelper(logger), casBackendUseCase: repoUC, integrationUC: iUC, membershipRepo: mRepo, + onboardingConfig: onboardingConfig, } } @@ -244,3 +248,79 @@ func (uc *OrganizationUseCase) Delete(ctx context.Context, id string) error { // Delete the organization return uc.orgRepo.Delete(ctx, orgUUID) } + +// AutoOnboardOrganizations creates the organizations specified in the onboarding config and assigns the user to them +// with the specified role if they are not already a member. +func (uc *OrganizationUseCase) AutoOnboardOrganizations(ctx context.Context, userID string) error { + // Parse user UUID + userUUID, err := uuid.Parse(userID) + if err != nil { + return NewErrInvalidUUID(err) + } + + for _, spec := range uc.onboardingConfig { + // Ensure the organization exists or create it if it doesn't + org, err := uc.ensureOrganizationExists(ctx, spec) + if err != nil { + return fmt.Errorf("failed to ensure organization exists: %w", err) + } + + // Parse organization UUID + orgUUID, err := uuid.Parse(org.ID) + if err != nil { + return NewErrInvalidUUID(err) + } + + // Ensure user membership + if err := uc.ensureUserMembership(ctx, orgUUID, userUUID, spec); err != nil { + return fmt.Errorf("failed to ensure user membership: %w", err) + } + } + + return nil +} + +// ensureOrganizationExists ensures that an organization specified by the onboarding configuration exists. +// If the organization does not exist, it creates it. +func (uc *OrganizationUseCase) ensureOrganizationExists(ctx context.Context, spec *conf.OnboardingSpec) (*Organization, error) { + // Ensure the organization exists or create it if it doesn't + org, err := uc.orgRepo.FindByName(ctx, spec.GetName()) + if err != nil { + return nil, fmt.Errorf("failed to find organization: %w", err) + } else if org == nil { + // Create the organization since it does not exist + org, err = uc.orgRepo.Create(ctx, spec.GetName()) + if err != nil { + return nil, fmt.Errorf("failed to create organization: %w", err) + } + + // Create default inline CAS-backend + if _, err := uc.casBackendUseCase.CreateInlineFallbackBackend(ctx, org.ID); err != nil { + return nil, fmt.Errorf("failed to create fallback backend: %w", err) + } + } + + return org, nil +} + +// ensureUserMembership ensures that a user is a member of the specified organization with the appropriate role. +// If the membership does not exist, it creates it. +func (uc *OrganizationUseCase) ensureUserMembership(ctx context.Context, orgUUID, userUUID uuid.UUID, spec *conf.OnboardingSpec) error { + m, err := uc.membershipRepo.FindByOrgAndUser(ctx, orgUUID, userUUID) + if err != nil { + return fmt.Errorf("failed to find membership: %w", err) + } + + if m != nil { + // Membership already exists, no further action needed + return nil + } + + // Create the membership with the specified role + _, err = uc.membershipRepo.Create(ctx, orgUUID, userUUID, true, PbRoleToBiz(spec.GetRole())) + if err != nil { + return fmt.Errorf("failed to create membership: %w", err) + } + + return nil +} diff --git a/app/controlplane/pkg/biz/organization_integration_test.go b/app/controlplane/pkg/biz/organization_integration_test.go index 26a92a7c5..55b34a83d 100644 --- a/app/controlplane/pkg/biz/organization_integration_test.go +++ b/app/controlplane/pkg/biz/organization_integration_test.go @@ -19,6 +19,9 @@ import ( "context" "testing" + v1 "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/authz" + conf "github.com/chainloop-dev/chainloop/app/controlplane/internal/conf/controlplane/config/v1" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz/testhelpers" "github.com/chainloop-dev/chainloop/app/controlplane/plugins/sdk/v1" @@ -193,6 +196,7 @@ func (s *OrgIntegrationTestSuite) TestDeleteOrg() { // Run the tests func TestOrgUseCase(t *testing.T) { suite.Run(t, new(OrgIntegrationTestSuite)) + suite.Run(t, new(AuthOnboardingTestSuite)) } // Utility struct to hold the test suite @@ -268,3 +272,123 @@ func (s *OrgIntegrationTestSuite) SetupTest() { assert.NoError(err) assert.Len(contracts, 1) } + +type AuthOnboardingTestSuite struct { + testhelpers.UseCasesEachTestSuite + usr, usr1 *biz.User + org *biz.Organization + m *biz.Membership +} + +func (s *AuthOnboardingTestSuite) SetupTest() { + t := s.T() + ctx := context.Background() + + s.TestingUseCases = testhelpers.NewTestingUseCases(t, testhelpers.WithOnboardingConfiguration([]*conf.OnboardingSpec{ + { + Name: "testing-org", + Role: v1.MembershipRole_MEMBERSHIP_ROLE_ORG_VIEWER, + }, + })) + + s.setupUsersAndOrganization(ctx) + s.setupMembership(ctx) +} + +func (s *AuthOnboardingTestSuite) setupUsersAndOrganization(ctx context.Context) { + var err error + s.org, err = s.Organization.Create(ctx, "onboarded-org") + require.NoError(s.T(), err) + + s.usr, err = s.User.FindOrCreateByEmail(ctx, "foo@bar", true) + require.NoError(s.T(), err) + + s.usr1, err = s.User.FindOrCreateByEmail(ctx, "bar@foo", true) + require.NoError(s.T(), err) +} + +func (s *AuthOnboardingTestSuite) setupMembership(ctx context.Context) { + var err error + s.m, err = s.Membership.Create(ctx, s.org.ID, s.usr1.ID, biz.WithMembershipRole(authz.RoleViewer)) + s.NoError(err) +} + +func (s *AuthOnboardingTestSuite) TestAutoOnboardOrganizations() { + ctx := context.Background() + + org, err := s.Repos.OrganizationRepo.FindByName(ctx, "testing-org") + s.Nil(err) + s.Nil(org) + + err = s.Organization.AutoOnboardOrganizations(ctx, s.usr.ID) + s.NoError(err) + + org, err = s.Repos.OrganizationRepo.FindByName(ctx, "testing-org") + s.NoError(err) + s.NotNil(org) + + m, err := s.Membership.FindByOrgAndUser(ctx, org.ID, s.usr.ID) + s.NoError(err) + s.NotNil(m) +} + +func (s *AuthOnboardingTestSuite) TestOnboardOrganizationsTwice() { + ctx := context.Background() + + org, err := s.Repos.OrganizationRepo.FindByName(ctx, "testing-org") + s.Nil(err) + s.Nil(org) + + // Call it once + err = s.Organization.AutoOnboardOrganizations(ctx, s.usr.ID) + s.NoError(err) + + // Call it twice + err = s.Organization.AutoOnboardOrganizations(ctx, s.usr.ID) + s.NoError(err) + + org, err = s.Repos.OrganizationRepo.FindByName(ctx, "testing-org") + s.NoError(err) + s.NotNil(org) + + m, err := s.Membership.FindByOrgAndUser(ctx, org.ID, s.usr.ID) + s.NoError(err) + s.NotNil(m) +} + +func (s *AuthOnboardingTestSuite) TestAutoOnboardWithExistingMemberships() { + ctx := context.Background() + + org, err := s.Repos.OrganizationRepo.FindByName(ctx, s.org.Name) + s.Nil(err) + s.NotNil(org) + + m, err := s.Membership.FindByOrgAndUser(ctx, org.ID, s.usr1.ID) + s.NoError(err) + s.NotNil(m) + s.Equal(s.m.Role, m.Role) + + err = s.Organization.AutoOnboardOrganizations(ctx, s.usr1.ID) + s.NoError(err) + + newM, err := s.Membership.FindByOrgAndUser(ctx, org.ID, s.usr1.ID) + s.NoError(err) + s.NotNil(newM) + s.Equal(s.m.Role, newM.Role) +} + +func (s *AuthOnboardingTestSuite) TestAutoOnboardWithoutConfiguration() { + ctx := context.Background() + s.TestingUseCases = testhelpers.NewTestingUseCases(s.T()) + + org, err := s.Repos.OrganizationRepo.FindByName(ctx, "testing-org") + s.Nil(err) + s.Nil(org) + + err = s.Organization.AutoOnboardOrganizations(ctx, s.usr.ID) + s.NoError(err) + + org, err = s.Repos.OrganizationRepo.FindByName(ctx, "testing-org") + s.Nil(err) + s.Nil(org) +} diff --git a/app/controlplane/pkg/biz/organization_test.go b/app/controlplane/pkg/biz/organization_test.go index ce7b5b3a8..ee6a0b355 100644 --- a/app/controlplane/pkg/biz/organization_test.go +++ b/app/controlplane/pkg/biz/organization_test.go @@ -33,7 +33,7 @@ type organizationTestSuite struct { func (s *organizationTestSuite) TestCreateWithRandomName() { repo := repoM.NewOrganizationRepo(s.T()) - uc := biz.NewOrganizationUseCase(repo, nil, nil, nil, log.NewStdLogger(io.Discard)) + uc := biz.NewOrganizationUseCase(repo, nil, nil, nil, nil, log.NewStdLogger(io.Discard)) s.Run("the org exists, we retry", func() { ctx := context.Background() diff --git a/app/controlplane/pkg/biz/testhelpers/database.go b/app/controlplane/pkg/biz/testhelpers/database.go index b970dc39d..3de04b3eb 100644 --- a/app/controlplane/pkg/biz/testhelpers/database.go +++ b/app/controlplane/pkg/biz/testhelpers/database.go @@ -80,12 +80,14 @@ type TestingRepos struct { Workflow biz.WorkflowRepo WorkflowRunRepo biz.WorkflowRunRepo AttestationState biz.AttestationStateRepo + OrganizationRepo biz.OrganizationRepo } type newTestingOpts struct { - credsReaderWriter credentials.ReaderWriter - integrations sdk.AvailablePlugins - providers backends.Providers + credsReaderWriter credentials.ReaderWriter + integrations sdk.AvailablePlugins + providers backends.Providers + onboardingConfiguration []*conf.OnboardingSpec } type NewTestingUCOpt func(*newTestingOpts) @@ -106,6 +108,12 @@ func WithRegisteredIntegration(i sdk.FanOut) NewTestingUCOpt { } } +func WithOnboardingConfiguration(conf []*conf.OnboardingSpec) NewTestingUCOpt { + return func(tu *newTestingOpts) { + tu.onboardingConfiguration = conf + } +} + func NewTestingUseCases(t *testing.T, opts ...NewTestingUCOpt) *TestingUseCases { // default args newArgs := &newTestingOpts{credsReaderWriter: creds.NewReaderWriter(t), @@ -125,7 +133,7 @@ func NewTestingUseCases(t *testing.T, opts ...NewTestingUCOpt) *TestingUseCases testData, _, err := WireTestData(db, t, log, newArgs.credsReaderWriter, &robotaccount.Builder{}, &conf.Auth{ GeneratedJwsHmacSecret: "test", CasRobotAccountPrivateKeyPath: "./testdata/test-key.ec.pem", - }, newArgs.integrations, newArgs.providers) + }, newArgs.onboardingConfiguration, newArgs.integrations, newArgs.providers) assert.NoError(t, err) // Run DB migrations for testing diff --git a/app/controlplane/pkg/biz/testhelpers/wire.go b/app/controlplane/pkg/biz/testhelpers/wire.go index 413192d47..f4077f598 100644 --- a/app/controlplane/pkg/biz/testhelpers/wire.go +++ b/app/controlplane/pkg/biz/testhelpers/wire.go @@ -36,7 +36,7 @@ import ( ) // wireTestData init testing data -func WireTestData(*TestDatabase, *testing.T, log.Logger, credentials.ReaderWriter, *robotaccount.Builder, *conf.Auth, sdk.AvailablePlugins, backends.Providers) (*TestingUseCases, func(), error) { +func WireTestData(*TestDatabase, *testing.T, log.Logger, credentials.ReaderWriter, *robotaccount.Builder, *conf.Auth, []*conf.OnboardingSpec, sdk.AvailablePlugins, backends.Providers) (*TestingUseCases, func(), error) { panic( wire.Build( data.ProviderSet, diff --git a/app/controlplane/pkg/biz/testhelpers/wire_gen.go b/app/controlplane/pkg/biz/testhelpers/wire_gen.go index 5a5448bec..52a3863f3 100644 --- a/app/controlplane/pkg/biz/testhelpers/wire_gen.go +++ b/app/controlplane/pkg/biz/testhelpers/wire_gen.go @@ -26,7 +26,7 @@ import ( // Injectors from wire.go: // wireTestData init testing data -func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, readerWriter credentials.ReaderWriter, builder *robotaccount.Builder, auth *conf.Auth, availablePlugins sdk.AvailablePlugins, providers backend.Providers) (*TestingUseCases, func(), error) { +func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, readerWriter credentials.ReaderWriter, builder *robotaccount.Builder, auth *conf.Auth, arg []*conf.OnboardingSpec, availablePlugins sdk.AvailablePlugins, providers backend.Providers) (*TestingUseCases, func(), error) { confData := NewConfData(testDatabase, t) newConfig := NewDataConfig(confData) dataData, cleanup, err := data.NewData(newConfig, logger) @@ -48,7 +48,7 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r Logger: logger, } integrationUseCase := biz.NewIntegrationUseCase(newIntegrationUseCaseOpts) - organizationUseCase := biz.NewOrganizationUseCase(organizationRepo, casBackendUseCase, integrationUseCase, membershipRepo, logger) + organizationUseCase := biz.NewOrganizationUseCase(organizationRepo, casBackendUseCase, integrationUseCase, membershipRepo, arg, logger) membershipUseCase := biz.NewMembershipUseCase(membershipRepo, organizationUseCase, logger) workflowContractRepo := data.NewWorkflowContractRepo(dataData, logger) workflowContractUseCase := biz.NewWorkflowContractUseCase(workflowContractRepo, logger) @@ -64,6 +64,7 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r UserRepo: userRepo, MembershipUseCase: membershipUseCase, OrganizationUseCase: organizationUseCase, + OnboardingConfig: arg, Logger: logger, } userUseCase := biz.NewUserUseCase(newUserUseCaseParams) @@ -108,6 +109,7 @@ func WireTestData(testDatabase *TestDatabase, t *testing.T, logger log.Logger, r Workflow: workflowRepo, WorkflowRunRepo: workflowRunRepo, AttestationState: attestationStateRepo, + OrganizationRepo: organizationRepo, } testingUseCases := &TestingUseCases{ DB: testDatabase, diff --git a/app/controlplane/pkg/biz/user.go b/app/controlplane/pkg/biz/user.go index 162392799..80883ecd2 100644 --- a/app/controlplane/pkg/biz/user.go +++ b/app/controlplane/pkg/biz/user.go @@ -21,6 +21,9 @@ import ( "fmt" "time" + pb "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" + "github.com/chainloop-dev/chainloop/app/controlplane/internal/authz" + conf "github.com/chainloop-dev/chainloop/app/controlplane/internal/conf/controlplane/config/v1" "github.com/go-kratos/kratos/v2/log" "github.com/google/uuid" ) @@ -48,12 +51,14 @@ type UserUseCase struct { logger *log.Helper membershipUseCase *MembershipUseCase organizationUseCase *OrganizationUseCase + onboardingConfig []*conf.OnboardingSpec } type NewUserUseCaseParams struct { UserRepo UserRepo MembershipUseCase *MembershipUseCase OrganizationUseCase *OrganizationUseCase + OnboardingConfig []*conf.OnboardingSpec Logger log.Logger } @@ -62,6 +67,7 @@ func NewUserUseCase(opts *NewUserUseCaseParams) *UserUseCase { userRepo: opts.UserRepo, membershipUseCase: opts.MembershipUseCase, organizationUseCase: opts.OrganizationUseCase, + onboardingConfig: opts.OnboardingConfig, logger: log.NewHelper(opts.Logger), } } @@ -90,7 +96,10 @@ func (uc *UserUseCase) DeleteUser(ctx context.Context, userID string) error { return uc.userRepo.Delete(ctx, userUUID) } -func (uc *UserUseCase) FindOrCreateByEmail(ctx context.Context, email string) (*User, error) { +// FindOrCreateByEmail finds or creates a user by email. By default, it will auto-onboard the user +// to the organizations defined in the configuration. If disableAutoOnboarding is set to true, it will +// skip the auto-onboarding process. +func (uc *UserUseCase) FindOrCreateByEmail(ctx context.Context, email string, disableAutoOnboarding ...bool) (*User, error) { u, err := uc.userRepo.FindByEmail(ctx, email) if err != nil { return nil, err @@ -98,7 +107,19 @@ func (uc *UserUseCase) FindOrCreateByEmail(ctx context.Context, email string) (* return u, nil } - return uc.userRepo.CreateByEmail(ctx, email) + u, err = uc.userRepo.CreateByEmail(ctx, email) + if err != nil { + return nil, fmt.Errorf("failed to create user: %w", err) + } + + // Check if we should auto-onboard the user + if disableAutoOnboarding == nil || (len(disableAutoOnboarding) > 0 && !disableAutoOnboarding[0]) { + if err := uc.organizationUseCase.AutoOnboardOrganizations(ctx, u.ID); err != nil { + return nil, fmt.Errorf("failed to auto-onboard user: %w", err) + } + } + + return u, err } func (uc *UserUseCase) FindByID(ctx context.Context, userID string) (*User, error) { @@ -137,3 +158,16 @@ func (uc *UserUseCase) CurrentMembership(ctx context.Context, userID string) (*M return m, nil } + +func PbRoleToBiz(r pb.MembershipRole) authz.Role { + switch r { + case pb.MembershipRole_MEMBERSHIP_ROLE_ORG_OWNER: + return authz.RoleOwner + case pb.MembershipRole_MEMBERSHIP_ROLE_ORG_ADMIN: + return authz.RoleAdmin + case pb.MembershipRole_MEMBERSHIP_ROLE_ORG_VIEWER: + return authz.RoleViewer + default: + return "" + } +} diff --git a/app/controlplane/pkg/biz/user_integration_test.go b/app/controlplane/pkg/biz/user_integration_test.go index 79aee19ca..bbdc664f1 100644 --- a/app/controlplane/pkg/biz/user_integration_test.go +++ b/app/controlplane/pkg/biz/user_integration_test.go @@ -19,7 +19,9 @@ import ( "context" "testing" + v1 "github.com/chainloop-dev/chainloop/app/controlplane/api/controlplane/v1" "github.com/chainloop-dev/chainloop/app/controlplane/internal/authz" + conf "github.com/chainloop-dev/chainloop/app/controlplane/internal/conf/controlplane/config/v1" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz/testhelpers" @@ -118,6 +120,43 @@ func (s *userIntegrationTestSuite) TestCurrentMembership() { // Run the tests func TestUserUseCase(t *testing.T) { suite.Run(t, new(userIntegrationTestSuite)) + suite.Run(t, new(userOnboardingTestSuite)) +} + +type userOnboardingTestSuite struct { + testhelpers.UseCasesEachTestSuite +} + +func (s *userOnboardingTestSuite) TestAutoOnboardOrganizationsNoConfiguration() { + ctx := context.Background() + // Create a user with no orgs + user, err := s.User.FindOrCreateByEmail(ctx, "foo@bar.com", true) + s.NoError(err) + s.NotNil(user) +} + +func (s *userOnboardingTestSuite) TestAutoOnboardOrganizationsWithConfiguration() { + ctx := context.Background() + // Create a user with no orgs + + s.TestingUseCases = testhelpers.NewTestingUseCases(s.T(), testhelpers.WithOnboardingConfiguration([]*conf.OnboardingSpec{ + { + Name: "testing-org", + Role: v1.MembershipRole_MEMBERSHIP_ROLE_ORG_VIEWER, + }, + })) + + org, err := s.Repos.OrganizationRepo.FindByName(ctx, "testing-org") + s.Nil(err) + s.Nil(org) + + user, err := s.User.FindOrCreateByEmail(ctx, "foo@bar.com") + s.NoError(err) + s.NotNil(user) + + org, err = s.Repos.OrganizationRepo.FindByName(ctx, "testing-org") + s.NoError(err) + s.NotNil(org) } // Utility struct to hold the test suite diff --git a/app/controlplane/pkg/data/organization.go b/app/controlplane/pkg/data/organization.go index f090318c3..b6a236e5c 100644 --- a/app/controlplane/pkg/data/organization.go +++ b/app/controlplane/pkg/data/organization.go @@ -21,6 +21,7 @@ import ( "github.com/chainloop-dev/chainloop/app/controlplane/pkg/biz" "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent" + "github.com/chainloop-dev/chainloop/app/controlplane/pkg/data/ent/organization" "github.com/go-kratos/kratos/v2/log" "github.com/google/uuid" ) @@ -63,6 +64,18 @@ func (r *OrganizationRepo) FindByID(ctx context.Context, id uuid.UUID) (*biz.Org return entOrgToBizOrg(org), nil } +// FindByName finds an organization by name. +func (r *OrganizationRepo) FindByName(ctx context.Context, name string) (*biz.Organization, error) { + org, err := r.data.DB.Organization.Query().Where(organization.NameEQ(name)).Only(ctx) + if err != nil && !ent.IsNotFound(err) { + return nil, err + } else if org == nil { + return nil, nil + } + + return entOrgToBizOrg(org), nil +} + func (r *OrganizationRepo) Update(ctx context.Context, id uuid.UUID, name *string) (*biz.Organization, error) { req := r.data.DB.Organization.UpdateOneID(id) if name != nil && *name != "" { diff --git a/pkg/credentials/api/credentials/v1/config.pb.go b/pkg/credentials/api/credentials/v1/config.pb.go index c12d5995c..8d5589ec1 100644 --- a/pkg/credentials/api/credentials/v1/config.pb.go +++ b/pkg/credentials/api/credentials/v1/config.pb.go @@ -536,12 +536,12 @@ var file_credentials_v1_config_proto_rawDesc = []byte{ 0x0a, 0x09, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0x90, 0x01, 0x01, 0x52, 0x08, 0x76, 0x61, 0x75, 0x6c, 0x74, 0x55, 0x72, 0x69, 0x42, 0x10, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, - 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x42, 0x4a, 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, - 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, - 0x6c, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x2f, 0x76, 0x31, + 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var (