diff --git a/.mockery.yml b/.mockery.yml
index 5574500a57..12d04ac118 100644
--- a/.mockery.yml
+++ b/.mockery.yml
@@ -116,3 +116,7 @@ packages:
AnalyzeInterface:
AnonymizeInterface:
EntityInterface:
+ github.com/nucleuscloud/neosync/backend/internal/userdata:
+ interfaces:
+ Interface:
+ EntityEnforcer:
diff --git a/backend/gen/go/db/mock_Querier.go b/backend/gen/go/db/mock_Querier.go
index bda36d5885..be934d1e4d 100644
--- a/backend/gen/go/db/mock_Querier.go
+++ b/backend/gen/go/db/mock_Querier.go
@@ -1386,6 +1386,65 @@ func (_c *MockQuerier_GetAccountIdFromJobId_Call) RunAndReturn(run func(context.
return _c
}
+// GetAccountIds provides a mock function with given fields: ctx, db
+func (_m *MockQuerier) GetAccountIds(ctx context.Context, db DBTX) ([]pgtype.UUID, error) {
+ ret := _m.Called(ctx, db)
+
+ if len(ret) == 0 {
+ panic("no return value specified for GetAccountIds")
+ }
+
+ var r0 []pgtype.UUID
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context, DBTX) ([]pgtype.UUID, error)); ok {
+ return rf(ctx, db)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, DBTX) []pgtype.UUID); ok {
+ r0 = rf(ctx, db)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]pgtype.UUID)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, DBTX) error); ok {
+ r1 = rf(ctx, db)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// MockQuerier_GetAccountIds_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAccountIds'
+type MockQuerier_GetAccountIds_Call struct {
+ *mock.Call
+}
+
+// GetAccountIds is a helper method to define mock.On call
+// - ctx context.Context
+// - db DBTX
+func (_e *MockQuerier_Expecter) GetAccountIds(ctx interface{}, db interface{}) *MockQuerier_GetAccountIds_Call {
+ return &MockQuerier_GetAccountIds_Call{Call: _e.mock.On("GetAccountIds", ctx, db)}
+}
+
+func (_c *MockQuerier_GetAccountIds_Call) Run(run func(ctx context.Context, db DBTX)) *MockQuerier_GetAccountIds_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(DBTX))
+ })
+ return _c
+}
+
+func (_c *MockQuerier_GetAccountIds_Call) Return(_a0 []pgtype.UUID, _a1 error) *MockQuerier_GetAccountIds_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *MockQuerier_GetAccountIds_Call) RunAndReturn(run func(context.Context, DBTX) ([]pgtype.UUID, error)) *MockQuerier_GetAccountIds_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
// GetAccountInvite provides a mock function with given fields: ctx, db, id
func (_m *MockQuerier) GetAccountInvite(ctx context.Context, db DBTX, id pgtype.UUID) (NeosyncApiAccountInvite, error) {
ret := _m.Called(ctx, db, id)
@@ -1620,6 +1679,66 @@ func (_c *MockQuerier_GetAccountUserAssociation_Call) RunAndReturn(run func(cont
return _c
}
+// GetAccountUsers provides a mock function with given fields: ctx, db, accountid
+func (_m *MockQuerier) GetAccountUsers(ctx context.Context, db DBTX, accountid pgtype.UUID) ([]pgtype.UUID, error) {
+ ret := _m.Called(ctx, db, accountid)
+
+ if len(ret) == 0 {
+ panic("no return value specified for GetAccountUsers")
+ }
+
+ var r0 []pgtype.UUID
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context, DBTX, pgtype.UUID) ([]pgtype.UUID, error)); ok {
+ return rf(ctx, db, accountid)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, DBTX, pgtype.UUID) []pgtype.UUID); ok {
+ r0 = rf(ctx, db, accountid)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]pgtype.UUID)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, DBTX, pgtype.UUID) error); ok {
+ r1 = rf(ctx, db, accountid)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// MockQuerier_GetAccountUsers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetAccountUsers'
+type MockQuerier_GetAccountUsers_Call struct {
+ *mock.Call
+}
+
+// GetAccountUsers is a helper method to define mock.On call
+// - ctx context.Context
+// - db DBTX
+// - accountid pgtype.UUID
+func (_e *MockQuerier_Expecter) GetAccountUsers(ctx interface{}, db interface{}, accountid interface{}) *MockQuerier_GetAccountUsers_Call {
+ return &MockQuerier_GetAccountUsers_Call{Call: _e.mock.On("GetAccountUsers", ctx, db, accountid)}
+}
+
+func (_c *MockQuerier_GetAccountUsers_Call) Run(run func(ctx context.Context, db DBTX, accountid pgtype.UUID)) *MockQuerier_GetAccountUsers_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(DBTX), args[2].(pgtype.UUID))
+ })
+ return _c
+}
+
+func (_c *MockQuerier_GetAccountUsers_Call) Return(_a0 []pgtype.UUID, _a1 error) *MockQuerier_GetAccountUsers_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *MockQuerier_GetAccountUsers_Call) RunAndReturn(run func(context.Context, DBTX, pgtype.UUID) ([]pgtype.UUID, error)) *MockQuerier_GetAccountUsers_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
// GetAccountsByUser provides a mock function with given fields: ctx, db, id
func (_m *MockQuerier) GetAccountsByUser(ctx context.Context, db DBTX, id pgtype.UUID) ([]NeosyncApiAccount, error) {
ret := _m.Called(ctx, db, id)
diff --git a/backend/gen/go/db/models.go b/backend/gen/go/db/models.go
index b73fe4fba9..134efe77d7 100644
--- a/backend/gen/go/db/models.go
+++ b/backend/gen/go/db/models.go
@@ -54,6 +54,18 @@ type NeosyncApiAccountUserAssociation struct {
UpdatedAt pgtype.Timestamp
}
+type NeosyncApiCasbinRule struct {
+ PType string
+ V0 string
+ V1 string
+ V2 string
+ V3 string
+ V4 string
+ V5 string
+ CreatedAt pgtype.Timestamptz
+ UpdatedAt pgtype.Timestamptz
+}
+
type NeosyncApiConnection struct {
ID pgtype.UUID
CreatedAt pgtype.Timestamp
diff --git a/backend/gen/go/db/querier.go b/backend/gen/go/db/querier.go
index b975903ff5..1b24ddd4b8 100644
--- a/backend/gen/go/db/querier.go
+++ b/backend/gen/go/db/querier.go
@@ -36,10 +36,12 @@ type Querier interface {
GetAccountApiKeyByKeyValue(ctx context.Context, db DBTX, keyValue string) (NeosyncApiAccountApiKey, error)
GetAccountApiKeys(ctx context.Context, db DBTX, accountid pgtype.UUID) ([]NeosyncApiAccountApiKey, error)
GetAccountIdFromJobId(ctx context.Context, db DBTX, id pgtype.UUID) (pgtype.UUID, error)
+ GetAccountIds(ctx context.Context, db DBTX) ([]pgtype.UUID, error)
GetAccountInvite(ctx context.Context, db DBTX, id pgtype.UUID) (NeosyncApiAccountInvite, error)
GetAccountInviteByToken(ctx context.Context, db DBTX, token string) (NeosyncApiAccountInvite, error)
GetAccountOnboardingConfig(ctx context.Context, db DBTX, id pgtype.UUID) (*pg_models.AccountOnboardingConfig, error)
GetAccountUserAssociation(ctx context.Context, db DBTX, arg GetAccountUserAssociationParams) (NeosyncApiAccountUserAssociation, error)
+ GetAccountUsers(ctx context.Context, db DBTX, accountid pgtype.UUID) ([]pgtype.UUID, error)
GetAccountsByUser(ctx context.Context, db DBTX, id pgtype.UUID) ([]NeosyncApiAccount, error)
GetActiveAccountInvites(ctx context.Context, db DBTX, accountid pgtype.UUID) ([]NeosyncApiAccountInvite, error)
GetActiveJobHooks(ctx context.Context, db DBTX, jobID pgtype.UUID) ([]NeosyncApiJobHook, error)
diff --git a/backend/gen/go/db/users.sql.go b/backend/gen/go/db/users.sql.go
index 9e2ec0f783..d29593a1b6 100644
--- a/backend/gen/go/db/users.sql.go
+++ b/backend/gen/go/db/users.sql.go
@@ -248,6 +248,30 @@ func (q *Queries) GetAccount(ctx context.Context, db DBTX, id pgtype.UUID) (Neos
return i, err
}
+const getAccountIds = `-- name: GetAccountIds :many
+SELECT id FROM neosync_api.accounts
+`
+
+func (q *Queries) GetAccountIds(ctx context.Context, db DBTX) ([]pgtype.UUID, error) {
+ rows, err := db.Query(ctx, getAccountIds)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []pgtype.UUID
+ for rows.Next() {
+ var id pgtype.UUID
+ if err := rows.Scan(&id); err != nil {
+ return nil, err
+ }
+ items = append(items, id)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
const getAccountInvite = `-- name: GetAccountInvite :one
SELECT id, account_id, sender_user_id, email, token, accepted, created_at, updated_at, expires_at FROM neosync_api.account_invites
WHERE id = $1
@@ -330,6 +354,34 @@ func (q *Queries) GetAccountUserAssociation(ctx context.Context, db DBTX, arg Ge
return i, err
}
+const getAccountUsers = `-- name: GetAccountUsers :many
+SELECT u.id
+FROM neosync_api.users u
+INNER JOIN neosync_api.account_user_associations aua ON aua.user_id = u.id
+INNER JOIN neosync_api.accounts a ON a.id = aua.account_id
+WHERE a.id = $1 AND u.user_type = 0
+`
+
+func (q *Queries) GetAccountUsers(ctx context.Context, db DBTX, accountid pgtype.UUID) ([]pgtype.UUID, error) {
+ rows, err := db.Query(ctx, getAccountUsers, accountid)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ var items []pgtype.UUID
+ for rows.Next() {
+ var id pgtype.UUID
+ if err := rows.Scan(&id); err != nil {
+ return nil, err
+ }
+ items = append(items, id)
+ }
+ if err := rows.Err(); err != nil {
+ return nil, err
+ }
+ return items, nil
+}
+
const getAccountsByUser = `-- name: GetAccountsByUser :many
SELECT a.id, a.created_at, a.updated_at, a.account_type, a.account_slug, a.temporal_config, a.onboarding_config, a.max_allowed_records, a.stripe_customer_id
FROM neosync_api.accounts a
@@ -612,9 +664,10 @@ func (q *Queries) GetUserByProviderSub(ctx context.Context, db DBTX, providerSub
}
const getUserIdentitiesByTeamAccount = `-- name: GetUserIdentitiesByTeamAccount :many
-SELECT aipa.id, aipa.user_id, aipa.provider_sub, aipa.created_at, aipa.updated_at FROM neosync_api.user_identity_provider_associations aipa
-JOIN neosync_api.account_user_associations aua ON aua.user_id = aipa.user_id
-JOIN neosync_api.accounts a ON a.id = aua.account_id
+SELECT aipa.id, aipa.user_id, aipa.provider_sub, aipa.created_at, aipa.updated_at
+FROM neosync_api.user_identity_provider_associations aipa
+INNER JOIN neosync_api.account_user_associations aua ON aua.user_id = aipa.user_id
+INNER JOIN neosync_api.accounts a ON a.id = aua.account_id
WHERE aua.account_id = $1 AND a.account_type = 1
`
diff --git a/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_UserAccountServiceClient.go b/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_UserAccountServiceClient.go
index 4615e5bae0..54efc19f97 100644
--- a/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_UserAccountServiceClient.go
+++ b/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/mock_UserAccountServiceClient.go
@@ -1440,6 +1440,65 @@ func (_c *MockUserAccountServiceClient_SetUser_Call) RunAndReturn(run func(conte
return _c
}
+// SetUserRole provides a mock function with given fields: _a0, _a1
+func (_m *MockUserAccountServiceClient) SetUserRole(_a0 context.Context, _a1 *connect.Request[mgmtv1alpha1.SetUserRoleRequest]) (*connect.Response[mgmtv1alpha1.SetUserRoleResponse], error) {
+ ret := _m.Called(_a0, _a1)
+
+ if len(ret) == 0 {
+ panic("no return value specified for SetUserRole")
+ }
+
+ var r0 *connect.Response[mgmtv1alpha1.SetUserRoleResponse]
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context, *connect.Request[mgmtv1alpha1.SetUserRoleRequest]) (*connect.Response[mgmtv1alpha1.SetUserRoleResponse], error)); ok {
+ return rf(_a0, _a1)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context, *connect.Request[mgmtv1alpha1.SetUserRoleRequest]) *connect.Response[mgmtv1alpha1.SetUserRoleResponse]); ok {
+ r0 = rf(_a0, _a1)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*connect.Response[mgmtv1alpha1.SetUserRoleResponse])
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context, *connect.Request[mgmtv1alpha1.SetUserRoleRequest]) error); ok {
+ r1 = rf(_a0, _a1)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// MockUserAccountServiceClient_SetUserRole_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SetUserRole'
+type MockUserAccountServiceClient_SetUserRole_Call struct {
+ *mock.Call
+}
+
+// SetUserRole is a helper method to define mock.On call
+// - _a0 context.Context
+// - _a1 *connect.Request[mgmtv1alpha1.SetUserRoleRequest]
+func (_e *MockUserAccountServiceClient_Expecter) SetUserRole(_a0 interface{}, _a1 interface{}) *MockUserAccountServiceClient_SetUserRole_Call {
+ return &MockUserAccountServiceClient_SetUserRole_Call{Call: _e.mock.On("SetUserRole", _a0, _a1)}
+}
+
+func (_c *MockUserAccountServiceClient_SetUserRole_Call) Run(run func(_a0 context.Context, _a1 *connect.Request[mgmtv1alpha1.SetUserRoleRequest])) *MockUserAccountServiceClient_SetUserRole_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(*connect.Request[mgmtv1alpha1.SetUserRoleRequest]))
+ })
+ return _c
+}
+
+func (_c *MockUserAccountServiceClient_SetUserRole_Call) Return(_a0 *connect.Response[mgmtv1alpha1.SetUserRoleResponse], _a1 error) *MockUserAccountServiceClient_SetUserRole_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *MockUserAccountServiceClient_SetUserRole_Call) RunAndReturn(run func(context.Context, *connect.Request[mgmtv1alpha1.SetUserRoleRequest]) (*connect.Response[mgmtv1alpha1.SetUserRoleResponse], error)) *MockUserAccountServiceClient_SetUserRole_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
// NewMockUserAccountServiceClient creates a new instance of MockUserAccountServiceClient. 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 NewMockUserAccountServiceClient(t interface {
diff --git a/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/user_account.connect.go b/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/user_account.connect.go
index 8efd566915..10ac38ccdd 100644
--- a/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/user_account.connect.go
+++ b/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect/user_account.connect.go
@@ -105,6 +105,9 @@ const (
// UserAccountServiceSetBillingMeterEventProcedure is the fully-qualified name of the
// UserAccountService's SetBillingMeterEvent RPC.
UserAccountServiceSetBillingMeterEventProcedure = "/mgmt.v1alpha1.UserAccountService/SetBillingMeterEvent"
+ // UserAccountServiceSetUserRoleProcedure is the fully-qualified name of the UserAccountService's
+ // SetUserRole RPC.
+ UserAccountServiceSetUserRoleProcedure = "/mgmt.v1alpha1.UserAccountService/SetUserRole"
)
// These variables are the protoreflect.Descriptor objects for the RPCs defined in this package.
@@ -134,6 +137,7 @@ var (
userAccountServiceGetAccountBillingPortalSessionMethodDescriptor = userAccountServiceServiceDescriptor.Methods().ByName("GetAccountBillingPortalSession")
userAccountServiceGetBillingAccountsMethodDescriptor = userAccountServiceServiceDescriptor.Methods().ByName("GetBillingAccounts")
userAccountServiceSetBillingMeterEventMethodDescriptor = userAccountServiceServiceDescriptor.Methods().ByName("SetBillingMeterEvent")
+ userAccountServiceSetUserRoleMethodDescriptor = userAccountServiceServiceDescriptor.Methods().ByName("SetUserRole")
)
// UserAccountServiceClient is a client for the mgmt.v1alpha1.UserAccountService service.
@@ -170,6 +174,8 @@ type UserAccountServiceClient interface {
GetBillingAccounts(context.Context, *connect.Request[v1alpha1.GetBillingAccountsRequest]) (*connect.Response[v1alpha1.GetBillingAccountsResponse], error)
// Sends a new metered event to the billing system
SetBillingMeterEvent(context.Context, *connect.Request[v1alpha1.SetBillingMeterEventRequest]) (*connect.Response[v1alpha1.SetBillingMeterEventResponse], error)
+ // Sets the users role
+ SetUserRole(context.Context, *connect.Request[v1alpha1.SetUserRoleRequest]) (*connect.Response[v1alpha1.SetUserRoleResponse], error)
}
// NewUserAccountServiceClient constructs a client for the mgmt.v1alpha1.UserAccountService service.
@@ -330,6 +336,12 @@ func NewUserAccountServiceClient(httpClient connect.HTTPClient, baseURL string,
connect.WithSchema(userAccountServiceSetBillingMeterEventMethodDescriptor),
connect.WithClientOptions(opts...),
),
+ setUserRole: connect.NewClient[v1alpha1.SetUserRoleRequest, v1alpha1.SetUserRoleResponse](
+ httpClient,
+ baseURL+UserAccountServiceSetUserRoleProcedure,
+ connect.WithSchema(userAccountServiceSetUserRoleMethodDescriptor),
+ connect.WithClientOptions(opts...),
+ ),
}
}
@@ -359,6 +371,7 @@ type userAccountServiceClient struct {
getAccountBillingPortalSession *connect.Client[v1alpha1.GetAccountBillingPortalSessionRequest, v1alpha1.GetAccountBillingPortalSessionResponse]
getBillingAccounts *connect.Client[v1alpha1.GetBillingAccountsRequest, v1alpha1.GetBillingAccountsResponse]
setBillingMeterEvent *connect.Client[v1alpha1.SetBillingMeterEventRequest, v1alpha1.SetBillingMeterEventResponse]
+ setUserRole *connect.Client[v1alpha1.SetUserRoleRequest, v1alpha1.SetUserRoleResponse]
}
// GetUser calls mgmt.v1alpha1.UserAccountService.GetUser.
@@ -483,6 +496,11 @@ func (c *userAccountServiceClient) SetBillingMeterEvent(ctx context.Context, req
return c.setBillingMeterEvent.CallUnary(ctx, req)
}
+// SetUserRole calls mgmt.v1alpha1.UserAccountService.SetUserRole.
+func (c *userAccountServiceClient) SetUserRole(ctx context.Context, req *connect.Request[v1alpha1.SetUserRoleRequest]) (*connect.Response[v1alpha1.SetUserRoleResponse], error) {
+ return c.setUserRole.CallUnary(ctx, req)
+}
+
// UserAccountServiceHandler is an implementation of the mgmt.v1alpha1.UserAccountService service.
type UserAccountServiceHandler interface {
GetUser(context.Context, *connect.Request[v1alpha1.GetUserRequest]) (*connect.Response[v1alpha1.GetUserResponse], error)
@@ -517,6 +535,8 @@ type UserAccountServiceHandler interface {
GetBillingAccounts(context.Context, *connect.Request[v1alpha1.GetBillingAccountsRequest]) (*connect.Response[v1alpha1.GetBillingAccountsResponse], error)
// Sends a new metered event to the billing system
SetBillingMeterEvent(context.Context, *connect.Request[v1alpha1.SetBillingMeterEventRequest]) (*connect.Response[v1alpha1.SetBillingMeterEventResponse], error)
+ // Sets the users role
+ SetUserRole(context.Context, *connect.Request[v1alpha1.SetUserRoleRequest]) (*connect.Response[v1alpha1.SetUserRoleResponse], error)
}
// NewUserAccountServiceHandler builds an HTTP handler from the service implementation. It returns
@@ -673,6 +693,12 @@ func NewUserAccountServiceHandler(svc UserAccountServiceHandler, opts ...connect
connect.WithSchema(userAccountServiceSetBillingMeterEventMethodDescriptor),
connect.WithHandlerOptions(opts...),
)
+ userAccountServiceSetUserRoleHandler := connect.NewUnaryHandler(
+ UserAccountServiceSetUserRoleProcedure,
+ svc.SetUserRole,
+ connect.WithSchema(userAccountServiceSetUserRoleMethodDescriptor),
+ connect.WithHandlerOptions(opts...),
+ )
return "/mgmt.v1alpha1.UserAccountService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case UserAccountServiceGetUserProcedure:
@@ -723,6 +749,8 @@ func NewUserAccountServiceHandler(svc UserAccountServiceHandler, opts ...connect
userAccountServiceGetBillingAccountsHandler.ServeHTTP(w, r)
case UserAccountServiceSetBillingMeterEventProcedure:
userAccountServiceSetBillingMeterEventHandler.ServeHTTP(w, r)
+ case UserAccountServiceSetUserRoleProcedure:
+ userAccountServiceSetUserRoleHandler.ServeHTTP(w, r)
default:
http.NotFound(w, r)
}
@@ -827,3 +855,7 @@ func (UnimplementedUserAccountServiceHandler) GetBillingAccounts(context.Context
func (UnimplementedUserAccountServiceHandler) SetBillingMeterEvent(context.Context, *connect.Request[v1alpha1.SetBillingMeterEventRequest]) (*connect.Response[v1alpha1.SetBillingMeterEventResponse], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mgmt.v1alpha1.UserAccountService.SetBillingMeterEvent is not implemented"))
}
+
+func (UnimplementedUserAccountServiceHandler) SetUserRole(context.Context, *connect.Request[v1alpha1.SetUserRoleRequest]) (*connect.Response[v1alpha1.SetUserRoleResponse], error) {
+ return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mgmt.v1alpha1.UserAccountService.SetUserRole is not implemented"))
+}
diff --git a/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.go b/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.go
index cc6d22a5ac..88d98eaef8 100644
--- a/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.go
+++ b/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.go
@@ -198,6 +198,66 @@ func (AccountStatus) EnumDescriptor() ([]byte, []int) {
return file_mgmt_v1alpha1_user_account_proto_rawDescGZIP(), []int{2}
}
+type AccountRole int32
+
+const (
+ // Default value, this should not be used, but will default to ACCOUNT_ROLE_JOB_VIEWER
+ AccountRole_ACCOUNT_ROLE_UNSPECIFIED AccountRole = 0
+ // Admin, can do anything in the account.
+ AccountRole_ACCOUNT_ROLE_ADMIN AccountRole = 1
+ // Can view, edit jobs and connections
+ AccountRole_ACCOUNT_ROLE_JOB_DEVELOPER AccountRole = 2
+ // Can view
+ AccountRole_ACCOUNT_ROLE_JOB_VIEWER AccountRole = 3
+ // Can view and execute
+ AccountRole_ACCOUNT_ROLE_JOB_EXECUTOR AccountRole = 4
+)
+
+// Enum value maps for AccountRole.
+var (
+ AccountRole_name = map[int32]string{
+ 0: "ACCOUNT_ROLE_UNSPECIFIED",
+ 1: "ACCOUNT_ROLE_ADMIN",
+ 2: "ACCOUNT_ROLE_JOB_DEVELOPER",
+ 3: "ACCOUNT_ROLE_JOB_VIEWER",
+ 4: "ACCOUNT_ROLE_JOB_EXECUTOR",
+ }
+ AccountRole_value = map[string]int32{
+ "ACCOUNT_ROLE_UNSPECIFIED": 0,
+ "ACCOUNT_ROLE_ADMIN": 1,
+ "ACCOUNT_ROLE_JOB_DEVELOPER": 2,
+ "ACCOUNT_ROLE_JOB_VIEWER": 3,
+ "ACCOUNT_ROLE_JOB_EXECUTOR": 4,
+ }
+)
+
+func (x AccountRole) Enum() *AccountRole {
+ p := new(AccountRole)
+ *p = x
+ return p
+}
+
+func (x AccountRole) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (AccountRole) Descriptor() protoreflect.EnumDescriptor {
+ return file_mgmt_v1alpha1_user_account_proto_enumTypes[3].Descriptor()
+}
+
+func (AccountRole) Type() protoreflect.EnumType {
+ return &file_mgmt_v1alpha1_user_account_proto_enumTypes[3]
+}
+
+func (x AccountRole) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use AccountRole.Descriptor instead.
+func (AccountRole) EnumDescriptor() ([]byte, []int) {
+ return file_mgmt_v1alpha1_user_account_proto_rawDescGZIP(), []int{3}
+}
+
type GetUserRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -2913,6 +2973,106 @@ func (*SetBillingMeterEventResponse) Descriptor() ([]byte, []int) {
return file_mgmt_v1alpha1_user_account_proto_rawDescGZIP(), []int{52}
}
+type SetUserRoleRequest struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ // The account id to apply this role to
+ AccountId string `protobuf:"bytes,1,opt,name=account_id,json=accountId,proto3" json:"account_id,omitempty"`
+ // The user that this will be applied to
+ UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ // The role that this user will obtain
+ Role AccountRole `protobuf:"varint,3,opt,name=role,proto3,enum=mgmt.v1alpha1.AccountRole" json:"role,omitempty"`
+}
+
+func (x *SetUserRoleRequest) Reset() {
+ *x = SetUserRoleRequest{}
+ mi := &file_mgmt_v1alpha1_user_account_proto_msgTypes[53]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *SetUserRoleRequest) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SetUserRoleRequest) ProtoMessage() {}
+
+func (x *SetUserRoleRequest) ProtoReflect() protoreflect.Message {
+ mi := &file_mgmt_v1alpha1_user_account_proto_msgTypes[53]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SetUserRoleRequest.ProtoReflect.Descriptor instead.
+func (*SetUserRoleRequest) Descriptor() ([]byte, []int) {
+ return file_mgmt_v1alpha1_user_account_proto_rawDescGZIP(), []int{53}
+}
+
+func (x *SetUserRoleRequest) GetAccountId() string {
+ if x != nil {
+ return x.AccountId
+ }
+ return ""
+}
+
+func (x *SetUserRoleRequest) GetUserId() string {
+ if x != nil {
+ return x.UserId
+ }
+ return ""
+}
+
+func (x *SetUserRoleRequest) GetRole() AccountRole {
+ if x != nil {
+ return x.Role
+ }
+ return AccountRole_ACCOUNT_ROLE_UNSPECIFIED
+}
+
+type SetUserRoleResponse struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+}
+
+func (x *SetUserRoleResponse) Reset() {
+ *x = SetUserRoleResponse{}
+ mi := &file_mgmt_v1alpha1_user_account_proto_msgTypes[54]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *SetUserRoleResponse) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SetUserRoleResponse) ProtoMessage() {}
+
+func (x *SetUserRoleResponse) ProtoReflect() protoreflect.Message {
+ mi := &file_mgmt_v1alpha1_user_account_proto_msgTypes[54]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use SetUserRoleResponse.ProtoReflect.Descriptor instead.
+func (*SetUserRoleResponse) Descriptor() ([]byte, []int) {
+ return file_mgmt_v1alpha1_user_account_proto_rawDescGZIP(), []int{54}
+}
+
var File_mgmt_v1alpha1_user_account_proto protoreflect.FileDescriptor
var file_mgmt_v1alpha1_user_account_proto_rawDesc = []byte{
@@ -3278,235 +3438,262 @@ var file_mgmt_v1alpha1_user_account_proto_rawDesc = []byte{
0x6d, 0x70, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74,
0x61, 0x6d, 0x70, 0x22, 0x1e, 0x0a, 0x1c, 0x53, 0x65, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e,
0x67, 0x4d, 0x65, 0x74, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
- 0x6e, 0x73, 0x65, 0x2a, 0x92, 0x01, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f,
- 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x21, 0x0a, 0x1d, 0x55, 0x53, 0x45, 0x52, 0x5f,
- 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53,
- 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x55, 0x53,
- 0x45, 0x52, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
- 0x50, 0x45, 0x52, 0x53, 0x4f, 0x4e, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x53,
- 0x45, 0x52, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f,
- 0x54, 0x45, 0x41, 0x4d, 0x10, 0x02, 0x12, 0x20, 0x0a, 0x1c, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41,
- 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x54, 0x45,
- 0x52, 0x50, 0x52, 0x49, 0x53, 0x45, 0x10, 0x03, 0x2a, 0xa9, 0x01, 0x0a, 0x0d, 0x42, 0x69, 0x6c,
- 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, 0x1a, 0x42, 0x49,
- 0x4c, 0x4c, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53,
- 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x49,
- 0x4c, 0x4c, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54,
- 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x42, 0x49, 0x4c, 0x4c, 0x49, 0x4e, 0x47,
- 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10,
- 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x42, 0x49, 0x4c, 0x4c, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x41,
- 0x54, 0x55, 0x53, 0x5f, 0x54, 0x52, 0x49, 0x41, 0x4c, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45,
- 0x10, 0x03, 0x12, 0x20, 0x0a, 0x1c, 0x42, 0x49, 0x4c, 0x4c, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54,
- 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54, 0x52, 0x49, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52,
- 0x45, 0x44, 0x10, 0x04, 0x2a, 0x8c, 0x02, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
- 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x25, 0x0a, 0x21, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e,
- 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f,
- 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x28, 0x0a,
- 0x24, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f,
- 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x53, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f,
- 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x10, 0x01, 0x12, 0x2a, 0x0a, 0x26, 0x41, 0x43, 0x43, 0x4f, 0x55,
- 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53,
- 0x54, 0x45, 0x44, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44, 0x53, 0x5f, 0x4c, 0x49, 0x4d, 0x49,
- 0x54, 0x10, 0x02, 0x12, 0x2b, 0x0a, 0x27, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x53,
- 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x49, 0x4e,
- 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x03,
- 0x12, 0x27, 0x0a, 0x23, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54,
- 0x55, 0x53, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x52, 0x49, 0x41, 0x4c,
- 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x04, 0x12, 0x28, 0x0a, 0x24, 0x41, 0x43, 0x43,
- 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x43, 0x4f,
- 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x52, 0x49, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45,
- 0x44, 0x10, 0x05, 0x32, 0xa0, 0x16, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f,
- 0x75, 0x6e, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x07, 0x47, 0x65,
- 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61,
- 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
- 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x07, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65,
+ 0x6e, 0x73, 0x65, 0x22, 0x90, 0x01, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52,
+ 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x27, 0x0a, 0x0a, 0x61, 0x63,
+ 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08,
+ 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x09, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e,
+ 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x06,
+ 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x03,
+ 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+ 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x6f, 0x6c, 0x65,
+ 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65,
+ 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x92, 0x01,
+ 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x79, 0x70,
+ 0x65, 0x12, 0x21, 0x0a, 0x1d, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e,
+ 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
+ 0x45, 0x44, 0x10, 0x00, 0x12, 0x1e, 0x0a, 0x1a, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x43,
+ 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x50, 0x45, 0x52, 0x53, 0x4f, 0x4e,
+ 0x41, 0x4c, 0x10, 0x01, 0x12, 0x1a, 0x0a, 0x16, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x43,
+ 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x54, 0x45, 0x41, 0x4d, 0x10, 0x02,
+ 0x12, 0x20, 0x0a, 0x1c, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54,
+ 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x50, 0x52, 0x49, 0x53, 0x45,
+ 0x10, 0x03, 0x2a, 0xa9, 0x01, 0x0a, 0x0d, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x12, 0x1e, 0x0a, 0x1a, 0x42, 0x49, 0x4c, 0x4c, 0x49, 0x4e, 0x47, 0x5f,
+ 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
+ 0x45, 0x44, 0x10, 0x00, 0x12, 0x19, 0x0a, 0x15, 0x42, 0x49, 0x4c, 0x4c, 0x49, 0x4e, 0x47, 0x5f,
+ 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12,
+ 0x1a, 0x0a, 0x16, 0x42, 0x49, 0x4c, 0x4c, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55,
+ 0x53, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x02, 0x12, 0x1f, 0x0a, 0x1b, 0x42,
+ 0x49, 0x4c, 0x4c, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54, 0x52,
+ 0x49, 0x41, 0x4c, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x03, 0x12, 0x20, 0x0a, 0x1c,
+ 0x42, 0x49, 0x4c, 0x4c, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x54,
+ 0x52, 0x49, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x04, 0x2a, 0x8c,
+ 0x02, 0x0a, 0x0d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
+ 0x12, 0x25, 0x0a, 0x21, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54,
+ 0x55, 0x53, 0x5f, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
+ 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x28, 0x0a, 0x24, 0x41, 0x43, 0x43, 0x4f, 0x55,
+ 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x58, 0x43, 0x45, 0x45, 0x44,
+ 0x53, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x45, 0x44, 0x5f, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x10,
+ 0x01, 0x12, 0x2a, 0x0a, 0x26, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41,
+ 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45, 0x53, 0x54, 0x45, 0x44, 0x5f, 0x45, 0x58,
+ 0x43, 0x45, 0x45, 0x44, 0x53, 0x5f, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x10, 0x02, 0x12, 0x2b, 0x0a,
+ 0x27, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f,
+ 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x49, 0x4e, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52,
+ 0x45, 0x44, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x10, 0x03, 0x12, 0x27, 0x0a, 0x23, 0x41, 0x43,
+ 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x43,
+ 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x52, 0x49, 0x41, 0x4c, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56,
+ 0x45, 0x10, 0x04, 0x12, 0x28, 0x0a, 0x24, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x53,
+ 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x54, 0x52,
+ 0x49, 0x41, 0x4c, 0x5f, 0x45, 0x58, 0x50, 0x49, 0x52, 0x45, 0x44, 0x10, 0x05, 0x2a, 0x9f, 0x01,
+ 0x0a, 0x0b, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x12, 0x1c, 0x0a,
+ 0x18, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x55, 0x4e,
+ 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x41,
+ 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x52, 0x4f, 0x4c, 0x45, 0x5f, 0x41, 0x44, 0x4d, 0x49,
+ 0x4e, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x52,
+ 0x4f, 0x4c, 0x45, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x44, 0x45, 0x56, 0x45, 0x4c, 0x4f, 0x50, 0x45,
+ 0x52, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x52,
+ 0x4f, 0x4c, 0x45, 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x45, 0x52, 0x10, 0x03,
+ 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x5f, 0x52, 0x4f, 0x4c, 0x45,
+ 0x5f, 0x4a, 0x4f, 0x42, 0x5f, 0x45, 0x58, 0x45, 0x43, 0x55, 0x54, 0x4f, 0x52, 0x10, 0x04, 0x32,
+ 0xf8, 0x16, 0x0a, 0x12, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53,
+ 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65,
0x72, 0x12, 0x1d, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
- 0x31, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
- 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
- 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63,
- 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61,
- 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63,
- 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d,
- 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74,
- 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x50, 0x65, 0x72,
- 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x2e, 0x6d,
+ 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x07, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1d, 0x2e,
+ 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65,
+ 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6d,
0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74,
- 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31,
- 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e,
- 0x61, 0x6c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
- 0x65, 0x22, 0x00, 0x12, 0x89, 0x01, 0x0a, 0x1c, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x50,
- 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x54, 0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63,
- 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x32, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+ 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62,
+ 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+ 0x73, 0x12, 0x25, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+ 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+ 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
+ 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72,
+ 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x12, 0x53, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61,
+ 0x6c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
+ 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73,
+ 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
+ 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x63,
+ 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
+ 0x89, 0x01, 0x0a, 0x1c, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f,
+ 0x6e, 0x61, 0x6c, 0x54, 0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+ 0x12, 0x32, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+ 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c,
+ 0x54, 0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x50, 0x65, 0x72, 0x73,
0x6f, 0x6e, 0x61, 0x6c, 0x54, 0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
- 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
- 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x76, 0x65, 0x72, 0x74,
- 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x54, 0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63,
- 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
- 0x68, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63,
- 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
- 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41,
- 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e,
- 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72,
- 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0f, 0x49, 0x73, 0x55,
- 0x73, 0x65, 0x72, 0x49, 0x6e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x6d,
- 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x55,
- 0x73, 0x65, 0x72, 0x49, 0x6e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
- 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x41, 0x63, 0x63, 0x6f,
- 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a,
- 0x18, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f,
- 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74,
- 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63,
- 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66,
- 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6d, 0x67, 0x6d, 0x74,
- 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63,
- 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66,
- 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x18,
- 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72,
- 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
- 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f,
- 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69,
- 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
- 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f,
- 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69,
- 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x15, 0x47,
- 0x65, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x65, 0x6d,
- 0x62, 0x65, 0x72, 0x73, 0x12, 0x2b, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
- 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f,
- 0x75, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
- 0x74, 0x1a, 0x2c, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
- 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
- 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
- 0x00, 0x12, 0x7a, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41,
- 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2d, 0x2e, 0x6d,
- 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6d,
- 0x6f, 0x76, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x65,
- 0x6d, 0x62, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6d, 0x67,
- 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f,
- 0x76, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x65, 0x6d,
- 0x62, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a,
- 0x17, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x54, 0x6f, 0x54, 0x65, 0x61,
- 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2d, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
- 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x55,
- 0x73, 0x65, 0x72, 0x54, 0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+ 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x11, 0x43,
+ 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+ 0x12, 0x27, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+ 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75,
+ 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6d, 0x67, 0x6d, 0x74,
+ 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
+ 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x62, 0x0a, 0x0f, 0x49, 0x73, 0x55, 0x73, 0x65, 0x72, 0x49,
+ 0x6e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x25, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
+ 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x55, 0x73, 0x65, 0x72, 0x49,
+ 0x6e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+ 0x26, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+ 0x49, 0x73, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52,
+ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x18, 0x47, 0x65, 0x74,
+ 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61,
+ 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+ 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61,
+ 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+ 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65,
+ 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7d, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x41,
+ 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f,
+ 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+ 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54,
+ 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+ 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x54,
+ 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x54, 0x65,
+ 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73,
+ 0x12, 0x2b, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+ 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d,
+ 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e,
+ 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65,
+ 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62,
+ 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a,
+ 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75,
+ 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2d, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
+ 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54,
+ 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76,
- 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x55, 0x73,
- 0x65, 0x72, 0x54, 0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x15, 0x47, 0x65, 0x74,
- 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74,
- 0x65, 0x73, 0x12, 0x2b, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
- 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
- 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
- 0x2c, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
- 0x47, 0x65, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e,
- 0x76, 0x69, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
- 0x7a, 0x0a, 0x17, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63,
- 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x6d, 0x67, 0x6d,
- 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76,
- 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76, 0x69,
- 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74,
- 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65,
- 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74,
- 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x17, 0x41,
- 0x63, 0x63, 0x65, 0x70, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+ 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x65,
+ 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x52,
+ 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x17, 0x49, 0x6e, 0x76,
+ 0x69, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x54, 0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63,
+ 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2d, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+ 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x54,
+ 0x6f, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
+ 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x54, 0x6f,
+ 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x54, 0x65, 0x61, 0x6d,
+ 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x73, 0x12, 0x2b,
+ 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47,
+ 0x65, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76,
+ 0x69, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x6d, 0x67,
+ 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54,
+ 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65,
+ 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x17, 0x52,
+ 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31,
- 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x54, 0x65, 0x61,
+ 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x65, 0x61,
0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61,
- 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x54, 0x65, 0x61, 0x6d,
+ 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x65, 0x61, 0x6d,
0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73,
- 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x79,
- 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12,
- 0x2a, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
- 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,
- 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x67,
+ 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x7a, 0x0a, 0x17, 0x41, 0x63, 0x63, 0x65, 0x70,
+ 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76, 0x69,
+ 0x74, 0x65, 0x12, 0x2d, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
+ 0x61, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63,
+ 0x6f, 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x1a, 0x2e, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+ 0x31, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x54, 0x65, 0x61, 0x6d, 0x41, 0x63, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x49, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x22, 0x00, 0x12, 0x74, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
+ 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x2e, 0x6d, 0x67,
0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53,
0x79, 0x73, 0x74, 0x65, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e,
- 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x83, 0x01,
- 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x6e, 0x62, 0x6f,
- 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x30, 0x2e, 0x6d,
- 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74,
- 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e,
- 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31,
- 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47,
- 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64,
- 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
- 0x65, 0x22, 0x00, 0x12, 0x83, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75,
- 0x6e, 0x74, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66,
- 0x69, 0x67, 0x12, 0x30, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
- 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x6e, 0x62,
- 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71,
- 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
- 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f,
- 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
- 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74,
- 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e,
- 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65,
- 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65,
- 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61,
- 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
- 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03,
- 0x90, 0x02, 0x01, 0x12, 0x74, 0x0a, 0x14, 0x49, 0x73, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
- 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x2a, 0x2e, 0x6d, 0x67,
- 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x41, 0x63,
- 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76,
+ 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x79, 0x73, 0x74, 0x65,
+ 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x83, 0x01, 0x0a, 0x1a, 0x47, 0x65,
+ 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69,
+ 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x30, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e,
+ 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x6d, 0x67, 0x6d,
+ 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63,
+ 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x43,
+ 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
+ 0x83, 0x01, 0x0a, 0x1a, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x6e,
+ 0x62, 0x6f, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x30,
+ 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53,
+ 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x6e, 0x62, 0x6f, 0x61, 0x72, 0x64,
+ 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+ 0x1a, 0x31, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+ 0x2e, 0x53, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4f, 0x6e, 0x62, 0x6f, 0x61,
+ 0x72, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x68, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f,
+ 0x75, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x26, 0x2e, 0x6d, 0x67, 0x6d, 0x74,
+ 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63,
+ 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x1a, 0x27, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+ 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74,
+ 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12,
+ 0x74, 0x0a, 0x14, 0x49, 0x73, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74,
+ 0x75, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x12, 0x2a, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76,
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
- 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x95, 0x01, 0x0a, 0x20, 0x47, 0x65,
- 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x43,
- 0x68, 0x65, 0x63, 0x6b, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x36,
- 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47,
- 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67,
- 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52,
- 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31,
- 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
- 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6f, 0x75, 0x74,
- 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
- 0x00, 0x12, 0x8f, 0x01, 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
- 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x73,
- 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x34, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
- 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42,
- 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73,
- 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x6d, 0x67, 0x6d,
+ 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
+ 0x68, 0x61, 0x31, 0x2e, 0x49, 0x73, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x53, 0x74, 0x61,
+ 0x74, 0x75, 0x73, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x95, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63,
+ 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x65, 0x63, 0x6b,
+ 0x6f, 0x75, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x2e, 0x6d, 0x67, 0x6d,
0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63,
- 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x72, 0x74,
- 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
- 0x65, 0x22, 0x00, 0x12, 0x6e, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e,
- 0x67, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x28, 0x2e, 0x6d, 0x67, 0x6d, 0x74,
- 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x69, 0x6c,
- 0x6c, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75,
- 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
- 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x41, 0x63,
- 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03,
- 0x90, 0x02, 0x01, 0x12, 0x71, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e,
- 0x67, 0x4d, 0x65, 0x74, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x2e, 0x6d, 0x67,
- 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x42,
- 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x74, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76,
+ 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x65, 0x63,
+ 0x6b, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
+ 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x69, 0x6c,
+ 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x73, 0x73,
+ 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x8f, 0x01,
+ 0x0a, 0x1e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x69, 0x6c, 0x6c,
+ 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x12, 0x34, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+ 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69,
+ 0x6e, 0x67, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52,
+ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31,
+ 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
+ 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x53, 0x65,
+ 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12,
+ 0x6e, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63,
+ 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x28, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61,
+ 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67,
+ 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+ 0x29, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+ 0x47, 0x65, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
+ 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12,
+ 0x71, 0x0a, 0x14, 0x53, 0x65, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x74,
+ 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2a, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76,
0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69,
- 0x6e, 0x67, 0x4d, 0x65, 0x74, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70,
- 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xcc, 0x01, 0x0a, 0x11, 0x63, 0x6f, 0x6d, 0x2e, 0x6d,
- 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x10, 0x55, 0x73,
- 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
- 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x75, 0x63,
- 0x6c, 0x65, 0x75, 0x73, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6e, 0x65, 0x6f, 0x73, 0x79, 0x6e,
- 0x63, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f,
- 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6d, 0x67, 0x6d, 0x74, 0x2f, 0x76, 0x31, 0x61,
- 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x6d, 0x67, 0x6d, 0x74, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
- 0x61, 0x31, 0xa2, 0x02, 0x03, 0x4d, 0x58, 0x58, 0xaa, 0x02, 0x0d, 0x4d, 0x67, 0x6d, 0x74, 0x2e,
- 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x0d, 0x4d, 0x67, 0x6d, 0x74, 0x5c,
- 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xe2, 0x02, 0x19, 0x4d, 0x67, 0x6d, 0x74, 0x5c,
- 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61,
- 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, 0x4d, 0x67, 0x6d, 0x74, 0x3a, 0x3a, 0x56, 0x31, 0x61,
- 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x6e, 0x67, 0x4d, 0x65, 0x74, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
+ 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x42, 0x69, 0x6c, 0x6c, 0x69, 0x6e, 0x67, 0x4d, 0x65,
+ 0x74, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+ 0x22, 0x00, 0x12, 0x56, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x6f, 0x6c,
+ 0x65, 0x12, 0x21, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+ 0x31, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x65, 0x71,
+ 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+ 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x52, 0x6f, 0x6c, 0x65,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xcc, 0x01, 0x0a, 0x11, 0x63,
+ 0x6f, 0x6d, 0x2e, 0x6d, 0x67, 0x6d, 0x74, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+ 0x42, 0x10, 0x55, 0x73, 0x65, 0x72, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x72, 0x6f,
+ 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x50, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x2f, 0x6e, 0x75, 0x63, 0x6c, 0x65, 0x75, 0x73, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2f, 0x6e, 0x65,
+ 0x6f, 0x73, 0x79, 0x6e, 0x63, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x67, 0x65,
+ 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6d, 0x67, 0x6d, 0x74,
+ 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x6d, 0x67, 0x6d, 0x74, 0x76, 0x31,
+ 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x4d, 0x58, 0x58, 0xaa, 0x02, 0x0d, 0x4d,
+ 0x67, 0x6d, 0x74, 0x2e, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x0d, 0x4d,
+ 0x67, 0x6d, 0x74, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xe2, 0x02, 0x19, 0x4d,
+ 0x67, 0x6d, 0x74, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42,
+ 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, 0x4d, 0x67, 0x6d, 0x74, 0x3a,
+ 0x3a, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x33,
}
var (
@@ -3521,141 +3708,147 @@ func file_mgmt_v1alpha1_user_account_proto_rawDescGZIP() []byte {
return file_mgmt_v1alpha1_user_account_proto_rawDescData
}
-var file_mgmt_v1alpha1_user_account_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
-var file_mgmt_v1alpha1_user_account_proto_msgTypes = make([]protoimpl.MessageInfo, 53)
+var file_mgmt_v1alpha1_user_account_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
+var file_mgmt_v1alpha1_user_account_proto_msgTypes = make([]protoimpl.MessageInfo, 55)
var file_mgmt_v1alpha1_user_account_proto_goTypes = []any{
(UserAccountType)(0), // 0: mgmt.v1alpha1.UserAccountType
(BillingStatus)(0), // 1: mgmt.v1alpha1.BillingStatus
(AccountStatus)(0), // 2: mgmt.v1alpha1.AccountStatus
- (*GetUserRequest)(nil), // 3: mgmt.v1alpha1.GetUserRequest
- (*GetUserResponse)(nil), // 4: mgmt.v1alpha1.GetUserResponse
- (*SetUserRequest)(nil), // 5: mgmt.v1alpha1.SetUserRequest
- (*SetUserResponse)(nil), // 6: mgmt.v1alpha1.SetUserResponse
- (*GetUserAccountsRequest)(nil), // 7: mgmt.v1alpha1.GetUserAccountsRequest
- (*GetUserAccountsResponse)(nil), // 8: mgmt.v1alpha1.GetUserAccountsResponse
- (*UserAccount)(nil), // 9: mgmt.v1alpha1.UserAccount
- (*ConvertPersonalToTeamAccountRequest)(nil), // 10: mgmt.v1alpha1.ConvertPersonalToTeamAccountRequest
- (*ConvertPersonalToTeamAccountResponse)(nil), // 11: mgmt.v1alpha1.ConvertPersonalToTeamAccountResponse
- (*SetPersonalAccountRequest)(nil), // 12: mgmt.v1alpha1.SetPersonalAccountRequest
- (*SetPersonalAccountResponse)(nil), // 13: mgmt.v1alpha1.SetPersonalAccountResponse
- (*IsUserInAccountRequest)(nil), // 14: mgmt.v1alpha1.IsUserInAccountRequest
- (*IsUserInAccountResponse)(nil), // 15: mgmt.v1alpha1.IsUserInAccountResponse
- (*GetAccountTemporalConfigRequest)(nil), // 16: mgmt.v1alpha1.GetAccountTemporalConfigRequest
- (*GetAccountTemporalConfigResponse)(nil), // 17: mgmt.v1alpha1.GetAccountTemporalConfigResponse
- (*SetAccountTemporalConfigRequest)(nil), // 18: mgmt.v1alpha1.SetAccountTemporalConfigRequest
- (*SetAccountTemporalConfigResponse)(nil), // 19: mgmt.v1alpha1.SetAccountTemporalConfigResponse
- (*AccountTemporalConfig)(nil), // 20: mgmt.v1alpha1.AccountTemporalConfig
- (*CreateTeamAccountRequest)(nil), // 21: mgmt.v1alpha1.CreateTeamAccountRequest
- (*CreateTeamAccountResponse)(nil), // 22: mgmt.v1alpha1.CreateTeamAccountResponse
- (*AccountUser)(nil), // 23: mgmt.v1alpha1.AccountUser
- (*GetTeamAccountMembersRequest)(nil), // 24: mgmt.v1alpha1.GetTeamAccountMembersRequest
- (*GetTeamAccountMembersResponse)(nil), // 25: mgmt.v1alpha1.GetTeamAccountMembersResponse
- (*RemoveTeamAccountMemberRequest)(nil), // 26: mgmt.v1alpha1.RemoveTeamAccountMemberRequest
- (*RemoveTeamAccountMemberResponse)(nil), // 27: mgmt.v1alpha1.RemoveTeamAccountMemberResponse
- (*InviteUserToTeamAccountRequest)(nil), // 28: mgmt.v1alpha1.InviteUserToTeamAccountRequest
- (*AccountInvite)(nil), // 29: mgmt.v1alpha1.AccountInvite
- (*InviteUserToTeamAccountResponse)(nil), // 30: mgmt.v1alpha1.InviteUserToTeamAccountResponse
- (*GetTeamAccountInvitesRequest)(nil), // 31: mgmt.v1alpha1.GetTeamAccountInvitesRequest
- (*GetTeamAccountInvitesResponse)(nil), // 32: mgmt.v1alpha1.GetTeamAccountInvitesResponse
- (*RemoveTeamAccountInviteRequest)(nil), // 33: mgmt.v1alpha1.RemoveTeamAccountInviteRequest
- (*RemoveTeamAccountInviteResponse)(nil), // 34: mgmt.v1alpha1.RemoveTeamAccountInviteResponse
- (*AcceptTeamAccountInviteRequest)(nil), // 35: mgmt.v1alpha1.AcceptTeamAccountInviteRequest
- (*AcceptTeamAccountInviteResponse)(nil), // 36: mgmt.v1alpha1.AcceptTeamAccountInviteResponse
- (*GetSystemInformationRequest)(nil), // 37: mgmt.v1alpha1.GetSystemInformationRequest
- (*GetSystemInformationResponse)(nil), // 38: mgmt.v1alpha1.GetSystemInformationResponse
- (*GetAccountOnboardingConfigRequest)(nil), // 39: mgmt.v1alpha1.GetAccountOnboardingConfigRequest
- (*GetAccountOnboardingConfigResponse)(nil), // 40: mgmt.v1alpha1.GetAccountOnboardingConfigResponse
- (*SetAccountOnboardingConfigRequest)(nil), // 41: mgmt.v1alpha1.SetAccountOnboardingConfigRequest
- (*SetAccountOnboardingConfigResponse)(nil), // 42: mgmt.v1alpha1.SetAccountOnboardingConfigResponse
- (*AccountOnboardingConfig)(nil), // 43: mgmt.v1alpha1.AccountOnboardingConfig
- (*GetAccountStatusRequest)(nil), // 44: mgmt.v1alpha1.GetAccountStatusRequest
- (*GetAccountStatusResponse)(nil), // 45: mgmt.v1alpha1.GetAccountStatusResponse
- (*IsAccountStatusValidRequest)(nil), // 46: mgmt.v1alpha1.IsAccountStatusValidRequest
- (*IsAccountStatusValidResponse)(nil), // 47: mgmt.v1alpha1.IsAccountStatusValidResponse
- (*GetAccountBillingCheckoutSessionRequest)(nil), // 48: mgmt.v1alpha1.GetAccountBillingCheckoutSessionRequest
- (*GetAccountBillingCheckoutSessionResponse)(nil), // 49: mgmt.v1alpha1.GetAccountBillingCheckoutSessionResponse
- (*GetAccountBillingPortalSessionRequest)(nil), // 50: mgmt.v1alpha1.GetAccountBillingPortalSessionRequest
- (*GetAccountBillingPortalSessionResponse)(nil), // 51: mgmt.v1alpha1.GetAccountBillingPortalSessionResponse
- (*GetBillingAccountsRequest)(nil), // 52: mgmt.v1alpha1.GetBillingAccountsRequest
- (*GetBillingAccountsResponse)(nil), // 53: mgmt.v1alpha1.GetBillingAccountsResponse
- (*SetBillingMeterEventRequest)(nil), // 54: mgmt.v1alpha1.SetBillingMeterEventRequest
- (*SetBillingMeterEventResponse)(nil), // 55: mgmt.v1alpha1.SetBillingMeterEventResponse
- (*timestamppb.Timestamp)(nil), // 56: google.protobuf.Timestamp
+ (AccountRole)(0), // 3: mgmt.v1alpha1.AccountRole
+ (*GetUserRequest)(nil), // 4: mgmt.v1alpha1.GetUserRequest
+ (*GetUserResponse)(nil), // 5: mgmt.v1alpha1.GetUserResponse
+ (*SetUserRequest)(nil), // 6: mgmt.v1alpha1.SetUserRequest
+ (*SetUserResponse)(nil), // 7: mgmt.v1alpha1.SetUserResponse
+ (*GetUserAccountsRequest)(nil), // 8: mgmt.v1alpha1.GetUserAccountsRequest
+ (*GetUserAccountsResponse)(nil), // 9: mgmt.v1alpha1.GetUserAccountsResponse
+ (*UserAccount)(nil), // 10: mgmt.v1alpha1.UserAccount
+ (*ConvertPersonalToTeamAccountRequest)(nil), // 11: mgmt.v1alpha1.ConvertPersonalToTeamAccountRequest
+ (*ConvertPersonalToTeamAccountResponse)(nil), // 12: mgmt.v1alpha1.ConvertPersonalToTeamAccountResponse
+ (*SetPersonalAccountRequest)(nil), // 13: mgmt.v1alpha1.SetPersonalAccountRequest
+ (*SetPersonalAccountResponse)(nil), // 14: mgmt.v1alpha1.SetPersonalAccountResponse
+ (*IsUserInAccountRequest)(nil), // 15: mgmt.v1alpha1.IsUserInAccountRequest
+ (*IsUserInAccountResponse)(nil), // 16: mgmt.v1alpha1.IsUserInAccountResponse
+ (*GetAccountTemporalConfigRequest)(nil), // 17: mgmt.v1alpha1.GetAccountTemporalConfigRequest
+ (*GetAccountTemporalConfigResponse)(nil), // 18: mgmt.v1alpha1.GetAccountTemporalConfigResponse
+ (*SetAccountTemporalConfigRequest)(nil), // 19: mgmt.v1alpha1.SetAccountTemporalConfigRequest
+ (*SetAccountTemporalConfigResponse)(nil), // 20: mgmt.v1alpha1.SetAccountTemporalConfigResponse
+ (*AccountTemporalConfig)(nil), // 21: mgmt.v1alpha1.AccountTemporalConfig
+ (*CreateTeamAccountRequest)(nil), // 22: mgmt.v1alpha1.CreateTeamAccountRequest
+ (*CreateTeamAccountResponse)(nil), // 23: mgmt.v1alpha1.CreateTeamAccountResponse
+ (*AccountUser)(nil), // 24: mgmt.v1alpha1.AccountUser
+ (*GetTeamAccountMembersRequest)(nil), // 25: mgmt.v1alpha1.GetTeamAccountMembersRequest
+ (*GetTeamAccountMembersResponse)(nil), // 26: mgmt.v1alpha1.GetTeamAccountMembersResponse
+ (*RemoveTeamAccountMemberRequest)(nil), // 27: mgmt.v1alpha1.RemoveTeamAccountMemberRequest
+ (*RemoveTeamAccountMemberResponse)(nil), // 28: mgmt.v1alpha1.RemoveTeamAccountMemberResponse
+ (*InviteUserToTeamAccountRequest)(nil), // 29: mgmt.v1alpha1.InviteUserToTeamAccountRequest
+ (*AccountInvite)(nil), // 30: mgmt.v1alpha1.AccountInvite
+ (*InviteUserToTeamAccountResponse)(nil), // 31: mgmt.v1alpha1.InviteUserToTeamAccountResponse
+ (*GetTeamAccountInvitesRequest)(nil), // 32: mgmt.v1alpha1.GetTeamAccountInvitesRequest
+ (*GetTeamAccountInvitesResponse)(nil), // 33: mgmt.v1alpha1.GetTeamAccountInvitesResponse
+ (*RemoveTeamAccountInviteRequest)(nil), // 34: mgmt.v1alpha1.RemoveTeamAccountInviteRequest
+ (*RemoveTeamAccountInviteResponse)(nil), // 35: mgmt.v1alpha1.RemoveTeamAccountInviteResponse
+ (*AcceptTeamAccountInviteRequest)(nil), // 36: mgmt.v1alpha1.AcceptTeamAccountInviteRequest
+ (*AcceptTeamAccountInviteResponse)(nil), // 37: mgmt.v1alpha1.AcceptTeamAccountInviteResponse
+ (*GetSystemInformationRequest)(nil), // 38: mgmt.v1alpha1.GetSystemInformationRequest
+ (*GetSystemInformationResponse)(nil), // 39: mgmt.v1alpha1.GetSystemInformationResponse
+ (*GetAccountOnboardingConfigRequest)(nil), // 40: mgmt.v1alpha1.GetAccountOnboardingConfigRequest
+ (*GetAccountOnboardingConfigResponse)(nil), // 41: mgmt.v1alpha1.GetAccountOnboardingConfigResponse
+ (*SetAccountOnboardingConfigRequest)(nil), // 42: mgmt.v1alpha1.SetAccountOnboardingConfigRequest
+ (*SetAccountOnboardingConfigResponse)(nil), // 43: mgmt.v1alpha1.SetAccountOnboardingConfigResponse
+ (*AccountOnboardingConfig)(nil), // 44: mgmt.v1alpha1.AccountOnboardingConfig
+ (*GetAccountStatusRequest)(nil), // 45: mgmt.v1alpha1.GetAccountStatusRequest
+ (*GetAccountStatusResponse)(nil), // 46: mgmt.v1alpha1.GetAccountStatusResponse
+ (*IsAccountStatusValidRequest)(nil), // 47: mgmt.v1alpha1.IsAccountStatusValidRequest
+ (*IsAccountStatusValidResponse)(nil), // 48: mgmt.v1alpha1.IsAccountStatusValidResponse
+ (*GetAccountBillingCheckoutSessionRequest)(nil), // 49: mgmt.v1alpha1.GetAccountBillingCheckoutSessionRequest
+ (*GetAccountBillingCheckoutSessionResponse)(nil), // 50: mgmt.v1alpha1.GetAccountBillingCheckoutSessionResponse
+ (*GetAccountBillingPortalSessionRequest)(nil), // 51: mgmt.v1alpha1.GetAccountBillingPortalSessionRequest
+ (*GetAccountBillingPortalSessionResponse)(nil), // 52: mgmt.v1alpha1.GetAccountBillingPortalSessionResponse
+ (*GetBillingAccountsRequest)(nil), // 53: mgmt.v1alpha1.GetBillingAccountsRequest
+ (*GetBillingAccountsResponse)(nil), // 54: mgmt.v1alpha1.GetBillingAccountsResponse
+ (*SetBillingMeterEventRequest)(nil), // 55: mgmt.v1alpha1.SetBillingMeterEventRequest
+ (*SetBillingMeterEventResponse)(nil), // 56: mgmt.v1alpha1.SetBillingMeterEventResponse
+ (*SetUserRoleRequest)(nil), // 57: mgmt.v1alpha1.SetUserRoleRequest
+ (*SetUserRoleResponse)(nil), // 58: mgmt.v1alpha1.SetUserRoleResponse
+ (*timestamppb.Timestamp)(nil), // 59: google.protobuf.Timestamp
}
var file_mgmt_v1alpha1_user_account_proto_depIdxs = []int32{
- 9, // 0: mgmt.v1alpha1.GetUserAccountsResponse.accounts:type_name -> mgmt.v1alpha1.UserAccount
+ 10, // 0: mgmt.v1alpha1.GetUserAccountsResponse.accounts:type_name -> mgmt.v1alpha1.UserAccount
0, // 1: mgmt.v1alpha1.UserAccount.type:type_name -> mgmt.v1alpha1.UserAccountType
- 20, // 2: mgmt.v1alpha1.GetAccountTemporalConfigResponse.config:type_name -> mgmt.v1alpha1.AccountTemporalConfig
- 20, // 3: mgmt.v1alpha1.SetAccountTemporalConfigRequest.config:type_name -> mgmt.v1alpha1.AccountTemporalConfig
- 20, // 4: mgmt.v1alpha1.SetAccountTemporalConfigResponse.config:type_name -> mgmt.v1alpha1.AccountTemporalConfig
- 23, // 5: mgmt.v1alpha1.GetTeamAccountMembersResponse.users:type_name -> mgmt.v1alpha1.AccountUser
- 56, // 6: mgmt.v1alpha1.AccountInvite.created_at:type_name -> google.protobuf.Timestamp
- 56, // 7: mgmt.v1alpha1.AccountInvite.updated_at:type_name -> google.protobuf.Timestamp
- 56, // 8: mgmt.v1alpha1.AccountInvite.expires_at:type_name -> google.protobuf.Timestamp
- 29, // 9: mgmt.v1alpha1.InviteUserToTeamAccountResponse.invite:type_name -> mgmt.v1alpha1.AccountInvite
- 29, // 10: mgmt.v1alpha1.GetTeamAccountInvitesResponse.invites:type_name -> mgmt.v1alpha1.AccountInvite
- 9, // 11: mgmt.v1alpha1.AcceptTeamAccountInviteResponse.account:type_name -> mgmt.v1alpha1.UserAccount
- 56, // 12: mgmt.v1alpha1.GetSystemInformationResponse.build_date:type_name -> google.protobuf.Timestamp
- 43, // 13: mgmt.v1alpha1.GetAccountOnboardingConfigResponse.config:type_name -> mgmt.v1alpha1.AccountOnboardingConfig
- 43, // 14: mgmt.v1alpha1.SetAccountOnboardingConfigRequest.config:type_name -> mgmt.v1alpha1.AccountOnboardingConfig
- 43, // 15: mgmt.v1alpha1.SetAccountOnboardingConfigResponse.config:type_name -> mgmt.v1alpha1.AccountOnboardingConfig
+ 21, // 2: mgmt.v1alpha1.GetAccountTemporalConfigResponse.config:type_name -> mgmt.v1alpha1.AccountTemporalConfig
+ 21, // 3: mgmt.v1alpha1.SetAccountTemporalConfigRequest.config:type_name -> mgmt.v1alpha1.AccountTemporalConfig
+ 21, // 4: mgmt.v1alpha1.SetAccountTemporalConfigResponse.config:type_name -> mgmt.v1alpha1.AccountTemporalConfig
+ 24, // 5: mgmt.v1alpha1.GetTeamAccountMembersResponse.users:type_name -> mgmt.v1alpha1.AccountUser
+ 59, // 6: mgmt.v1alpha1.AccountInvite.created_at:type_name -> google.protobuf.Timestamp
+ 59, // 7: mgmt.v1alpha1.AccountInvite.updated_at:type_name -> google.protobuf.Timestamp
+ 59, // 8: mgmt.v1alpha1.AccountInvite.expires_at:type_name -> google.protobuf.Timestamp
+ 30, // 9: mgmt.v1alpha1.InviteUserToTeamAccountResponse.invite:type_name -> mgmt.v1alpha1.AccountInvite
+ 30, // 10: mgmt.v1alpha1.GetTeamAccountInvitesResponse.invites:type_name -> mgmt.v1alpha1.AccountInvite
+ 10, // 11: mgmt.v1alpha1.AcceptTeamAccountInviteResponse.account:type_name -> mgmt.v1alpha1.UserAccount
+ 59, // 12: mgmt.v1alpha1.GetSystemInformationResponse.build_date:type_name -> google.protobuf.Timestamp
+ 44, // 13: mgmt.v1alpha1.GetAccountOnboardingConfigResponse.config:type_name -> mgmt.v1alpha1.AccountOnboardingConfig
+ 44, // 14: mgmt.v1alpha1.SetAccountOnboardingConfigRequest.config:type_name -> mgmt.v1alpha1.AccountOnboardingConfig
+ 44, // 15: mgmt.v1alpha1.SetAccountOnboardingConfigResponse.config:type_name -> mgmt.v1alpha1.AccountOnboardingConfig
1, // 16: mgmt.v1alpha1.GetAccountStatusResponse.subscription_status:type_name -> mgmt.v1alpha1.BillingStatus
2, // 17: mgmt.v1alpha1.IsAccountStatusValidResponse.account_status:type_name -> mgmt.v1alpha1.AccountStatus
- 56, // 18: mgmt.v1alpha1.IsAccountStatusValidResponse.trial_expires_at:type_name -> google.protobuf.Timestamp
- 9, // 19: mgmt.v1alpha1.GetBillingAccountsResponse.accounts:type_name -> mgmt.v1alpha1.UserAccount
- 3, // 20: mgmt.v1alpha1.UserAccountService.GetUser:input_type -> mgmt.v1alpha1.GetUserRequest
- 5, // 21: mgmt.v1alpha1.UserAccountService.SetUser:input_type -> mgmt.v1alpha1.SetUserRequest
- 7, // 22: mgmt.v1alpha1.UserAccountService.GetUserAccounts:input_type -> mgmt.v1alpha1.GetUserAccountsRequest
- 12, // 23: mgmt.v1alpha1.UserAccountService.SetPersonalAccount:input_type -> mgmt.v1alpha1.SetPersonalAccountRequest
- 10, // 24: mgmt.v1alpha1.UserAccountService.ConvertPersonalToTeamAccount:input_type -> mgmt.v1alpha1.ConvertPersonalToTeamAccountRequest
- 21, // 25: mgmt.v1alpha1.UserAccountService.CreateTeamAccount:input_type -> mgmt.v1alpha1.CreateTeamAccountRequest
- 14, // 26: mgmt.v1alpha1.UserAccountService.IsUserInAccount:input_type -> mgmt.v1alpha1.IsUserInAccountRequest
- 16, // 27: mgmt.v1alpha1.UserAccountService.GetAccountTemporalConfig:input_type -> mgmt.v1alpha1.GetAccountTemporalConfigRequest
- 18, // 28: mgmt.v1alpha1.UserAccountService.SetAccountTemporalConfig:input_type -> mgmt.v1alpha1.SetAccountTemporalConfigRequest
- 24, // 29: mgmt.v1alpha1.UserAccountService.GetTeamAccountMembers:input_type -> mgmt.v1alpha1.GetTeamAccountMembersRequest
- 26, // 30: mgmt.v1alpha1.UserAccountService.RemoveTeamAccountMember:input_type -> mgmt.v1alpha1.RemoveTeamAccountMemberRequest
- 28, // 31: mgmt.v1alpha1.UserAccountService.InviteUserToTeamAccount:input_type -> mgmt.v1alpha1.InviteUserToTeamAccountRequest
- 31, // 32: mgmt.v1alpha1.UserAccountService.GetTeamAccountInvites:input_type -> mgmt.v1alpha1.GetTeamAccountInvitesRequest
- 33, // 33: mgmt.v1alpha1.UserAccountService.RemoveTeamAccountInvite:input_type -> mgmt.v1alpha1.RemoveTeamAccountInviteRequest
- 35, // 34: mgmt.v1alpha1.UserAccountService.AcceptTeamAccountInvite:input_type -> mgmt.v1alpha1.AcceptTeamAccountInviteRequest
- 37, // 35: mgmt.v1alpha1.UserAccountService.GetSystemInformation:input_type -> mgmt.v1alpha1.GetSystemInformationRequest
- 39, // 36: mgmt.v1alpha1.UserAccountService.GetAccountOnboardingConfig:input_type -> mgmt.v1alpha1.GetAccountOnboardingConfigRequest
- 41, // 37: mgmt.v1alpha1.UserAccountService.SetAccountOnboardingConfig:input_type -> mgmt.v1alpha1.SetAccountOnboardingConfigRequest
- 44, // 38: mgmt.v1alpha1.UserAccountService.GetAccountStatus:input_type -> mgmt.v1alpha1.GetAccountStatusRequest
- 46, // 39: mgmt.v1alpha1.UserAccountService.IsAccountStatusValid:input_type -> mgmt.v1alpha1.IsAccountStatusValidRequest
- 48, // 40: mgmt.v1alpha1.UserAccountService.GetAccountBillingCheckoutSession:input_type -> mgmt.v1alpha1.GetAccountBillingCheckoutSessionRequest
- 50, // 41: mgmt.v1alpha1.UserAccountService.GetAccountBillingPortalSession:input_type -> mgmt.v1alpha1.GetAccountBillingPortalSessionRequest
- 52, // 42: mgmt.v1alpha1.UserAccountService.GetBillingAccounts:input_type -> mgmt.v1alpha1.GetBillingAccountsRequest
- 54, // 43: mgmt.v1alpha1.UserAccountService.SetBillingMeterEvent:input_type -> mgmt.v1alpha1.SetBillingMeterEventRequest
- 4, // 44: mgmt.v1alpha1.UserAccountService.GetUser:output_type -> mgmt.v1alpha1.GetUserResponse
- 6, // 45: mgmt.v1alpha1.UserAccountService.SetUser:output_type -> mgmt.v1alpha1.SetUserResponse
- 8, // 46: mgmt.v1alpha1.UserAccountService.GetUserAccounts:output_type -> mgmt.v1alpha1.GetUserAccountsResponse
- 13, // 47: mgmt.v1alpha1.UserAccountService.SetPersonalAccount:output_type -> mgmt.v1alpha1.SetPersonalAccountResponse
- 11, // 48: mgmt.v1alpha1.UserAccountService.ConvertPersonalToTeamAccount:output_type -> mgmt.v1alpha1.ConvertPersonalToTeamAccountResponse
- 22, // 49: mgmt.v1alpha1.UserAccountService.CreateTeamAccount:output_type -> mgmt.v1alpha1.CreateTeamAccountResponse
- 15, // 50: mgmt.v1alpha1.UserAccountService.IsUserInAccount:output_type -> mgmt.v1alpha1.IsUserInAccountResponse
- 17, // 51: mgmt.v1alpha1.UserAccountService.GetAccountTemporalConfig:output_type -> mgmt.v1alpha1.GetAccountTemporalConfigResponse
- 19, // 52: mgmt.v1alpha1.UserAccountService.SetAccountTemporalConfig:output_type -> mgmt.v1alpha1.SetAccountTemporalConfigResponse
- 25, // 53: mgmt.v1alpha1.UserAccountService.GetTeamAccountMembers:output_type -> mgmt.v1alpha1.GetTeamAccountMembersResponse
- 27, // 54: mgmt.v1alpha1.UserAccountService.RemoveTeamAccountMember:output_type -> mgmt.v1alpha1.RemoveTeamAccountMemberResponse
- 30, // 55: mgmt.v1alpha1.UserAccountService.InviteUserToTeamAccount:output_type -> mgmt.v1alpha1.InviteUserToTeamAccountResponse
- 32, // 56: mgmt.v1alpha1.UserAccountService.GetTeamAccountInvites:output_type -> mgmt.v1alpha1.GetTeamAccountInvitesResponse
- 34, // 57: mgmt.v1alpha1.UserAccountService.RemoveTeamAccountInvite:output_type -> mgmt.v1alpha1.RemoveTeamAccountInviteResponse
- 36, // 58: mgmt.v1alpha1.UserAccountService.AcceptTeamAccountInvite:output_type -> mgmt.v1alpha1.AcceptTeamAccountInviteResponse
- 38, // 59: mgmt.v1alpha1.UserAccountService.GetSystemInformation:output_type -> mgmt.v1alpha1.GetSystemInformationResponse
- 40, // 60: mgmt.v1alpha1.UserAccountService.GetAccountOnboardingConfig:output_type -> mgmt.v1alpha1.GetAccountOnboardingConfigResponse
- 42, // 61: mgmt.v1alpha1.UserAccountService.SetAccountOnboardingConfig:output_type -> mgmt.v1alpha1.SetAccountOnboardingConfigResponse
- 45, // 62: mgmt.v1alpha1.UserAccountService.GetAccountStatus:output_type -> mgmt.v1alpha1.GetAccountStatusResponse
- 47, // 63: mgmt.v1alpha1.UserAccountService.IsAccountStatusValid:output_type -> mgmt.v1alpha1.IsAccountStatusValidResponse
- 49, // 64: mgmt.v1alpha1.UserAccountService.GetAccountBillingCheckoutSession:output_type -> mgmt.v1alpha1.GetAccountBillingCheckoutSessionResponse
- 51, // 65: mgmt.v1alpha1.UserAccountService.GetAccountBillingPortalSession:output_type -> mgmt.v1alpha1.GetAccountBillingPortalSessionResponse
- 53, // 66: mgmt.v1alpha1.UserAccountService.GetBillingAccounts:output_type -> mgmt.v1alpha1.GetBillingAccountsResponse
- 55, // 67: mgmt.v1alpha1.UserAccountService.SetBillingMeterEvent:output_type -> mgmt.v1alpha1.SetBillingMeterEventResponse
- 44, // [44:68] is the sub-list for method output_type
- 20, // [20:44] is the sub-list for method input_type
- 20, // [20:20] is the sub-list for extension type_name
- 20, // [20:20] is the sub-list for extension extendee
- 0, // [0:20] is the sub-list for field type_name
+ 59, // 18: mgmt.v1alpha1.IsAccountStatusValidResponse.trial_expires_at:type_name -> google.protobuf.Timestamp
+ 10, // 19: mgmt.v1alpha1.GetBillingAccountsResponse.accounts:type_name -> mgmt.v1alpha1.UserAccount
+ 3, // 20: mgmt.v1alpha1.SetUserRoleRequest.role:type_name -> mgmt.v1alpha1.AccountRole
+ 4, // 21: mgmt.v1alpha1.UserAccountService.GetUser:input_type -> mgmt.v1alpha1.GetUserRequest
+ 6, // 22: mgmt.v1alpha1.UserAccountService.SetUser:input_type -> mgmt.v1alpha1.SetUserRequest
+ 8, // 23: mgmt.v1alpha1.UserAccountService.GetUserAccounts:input_type -> mgmt.v1alpha1.GetUserAccountsRequest
+ 13, // 24: mgmt.v1alpha1.UserAccountService.SetPersonalAccount:input_type -> mgmt.v1alpha1.SetPersonalAccountRequest
+ 11, // 25: mgmt.v1alpha1.UserAccountService.ConvertPersonalToTeamAccount:input_type -> mgmt.v1alpha1.ConvertPersonalToTeamAccountRequest
+ 22, // 26: mgmt.v1alpha1.UserAccountService.CreateTeamAccount:input_type -> mgmt.v1alpha1.CreateTeamAccountRequest
+ 15, // 27: mgmt.v1alpha1.UserAccountService.IsUserInAccount:input_type -> mgmt.v1alpha1.IsUserInAccountRequest
+ 17, // 28: mgmt.v1alpha1.UserAccountService.GetAccountTemporalConfig:input_type -> mgmt.v1alpha1.GetAccountTemporalConfigRequest
+ 19, // 29: mgmt.v1alpha1.UserAccountService.SetAccountTemporalConfig:input_type -> mgmt.v1alpha1.SetAccountTemporalConfigRequest
+ 25, // 30: mgmt.v1alpha1.UserAccountService.GetTeamAccountMembers:input_type -> mgmt.v1alpha1.GetTeamAccountMembersRequest
+ 27, // 31: mgmt.v1alpha1.UserAccountService.RemoveTeamAccountMember:input_type -> mgmt.v1alpha1.RemoveTeamAccountMemberRequest
+ 29, // 32: mgmt.v1alpha1.UserAccountService.InviteUserToTeamAccount:input_type -> mgmt.v1alpha1.InviteUserToTeamAccountRequest
+ 32, // 33: mgmt.v1alpha1.UserAccountService.GetTeamAccountInvites:input_type -> mgmt.v1alpha1.GetTeamAccountInvitesRequest
+ 34, // 34: mgmt.v1alpha1.UserAccountService.RemoveTeamAccountInvite:input_type -> mgmt.v1alpha1.RemoveTeamAccountInviteRequest
+ 36, // 35: mgmt.v1alpha1.UserAccountService.AcceptTeamAccountInvite:input_type -> mgmt.v1alpha1.AcceptTeamAccountInviteRequest
+ 38, // 36: mgmt.v1alpha1.UserAccountService.GetSystemInformation:input_type -> mgmt.v1alpha1.GetSystemInformationRequest
+ 40, // 37: mgmt.v1alpha1.UserAccountService.GetAccountOnboardingConfig:input_type -> mgmt.v1alpha1.GetAccountOnboardingConfigRequest
+ 42, // 38: mgmt.v1alpha1.UserAccountService.SetAccountOnboardingConfig:input_type -> mgmt.v1alpha1.SetAccountOnboardingConfigRequest
+ 45, // 39: mgmt.v1alpha1.UserAccountService.GetAccountStatus:input_type -> mgmt.v1alpha1.GetAccountStatusRequest
+ 47, // 40: mgmt.v1alpha1.UserAccountService.IsAccountStatusValid:input_type -> mgmt.v1alpha1.IsAccountStatusValidRequest
+ 49, // 41: mgmt.v1alpha1.UserAccountService.GetAccountBillingCheckoutSession:input_type -> mgmt.v1alpha1.GetAccountBillingCheckoutSessionRequest
+ 51, // 42: mgmt.v1alpha1.UserAccountService.GetAccountBillingPortalSession:input_type -> mgmt.v1alpha1.GetAccountBillingPortalSessionRequest
+ 53, // 43: mgmt.v1alpha1.UserAccountService.GetBillingAccounts:input_type -> mgmt.v1alpha1.GetBillingAccountsRequest
+ 55, // 44: mgmt.v1alpha1.UserAccountService.SetBillingMeterEvent:input_type -> mgmt.v1alpha1.SetBillingMeterEventRequest
+ 57, // 45: mgmt.v1alpha1.UserAccountService.SetUserRole:input_type -> mgmt.v1alpha1.SetUserRoleRequest
+ 5, // 46: mgmt.v1alpha1.UserAccountService.GetUser:output_type -> mgmt.v1alpha1.GetUserResponse
+ 7, // 47: mgmt.v1alpha1.UserAccountService.SetUser:output_type -> mgmt.v1alpha1.SetUserResponse
+ 9, // 48: mgmt.v1alpha1.UserAccountService.GetUserAccounts:output_type -> mgmt.v1alpha1.GetUserAccountsResponse
+ 14, // 49: mgmt.v1alpha1.UserAccountService.SetPersonalAccount:output_type -> mgmt.v1alpha1.SetPersonalAccountResponse
+ 12, // 50: mgmt.v1alpha1.UserAccountService.ConvertPersonalToTeamAccount:output_type -> mgmt.v1alpha1.ConvertPersonalToTeamAccountResponse
+ 23, // 51: mgmt.v1alpha1.UserAccountService.CreateTeamAccount:output_type -> mgmt.v1alpha1.CreateTeamAccountResponse
+ 16, // 52: mgmt.v1alpha1.UserAccountService.IsUserInAccount:output_type -> mgmt.v1alpha1.IsUserInAccountResponse
+ 18, // 53: mgmt.v1alpha1.UserAccountService.GetAccountTemporalConfig:output_type -> mgmt.v1alpha1.GetAccountTemporalConfigResponse
+ 20, // 54: mgmt.v1alpha1.UserAccountService.SetAccountTemporalConfig:output_type -> mgmt.v1alpha1.SetAccountTemporalConfigResponse
+ 26, // 55: mgmt.v1alpha1.UserAccountService.GetTeamAccountMembers:output_type -> mgmt.v1alpha1.GetTeamAccountMembersResponse
+ 28, // 56: mgmt.v1alpha1.UserAccountService.RemoveTeamAccountMember:output_type -> mgmt.v1alpha1.RemoveTeamAccountMemberResponse
+ 31, // 57: mgmt.v1alpha1.UserAccountService.InviteUserToTeamAccount:output_type -> mgmt.v1alpha1.InviteUserToTeamAccountResponse
+ 33, // 58: mgmt.v1alpha1.UserAccountService.GetTeamAccountInvites:output_type -> mgmt.v1alpha1.GetTeamAccountInvitesResponse
+ 35, // 59: mgmt.v1alpha1.UserAccountService.RemoveTeamAccountInvite:output_type -> mgmt.v1alpha1.RemoveTeamAccountInviteResponse
+ 37, // 60: mgmt.v1alpha1.UserAccountService.AcceptTeamAccountInvite:output_type -> mgmt.v1alpha1.AcceptTeamAccountInviteResponse
+ 39, // 61: mgmt.v1alpha1.UserAccountService.GetSystemInformation:output_type -> mgmt.v1alpha1.GetSystemInformationResponse
+ 41, // 62: mgmt.v1alpha1.UserAccountService.GetAccountOnboardingConfig:output_type -> mgmt.v1alpha1.GetAccountOnboardingConfigResponse
+ 43, // 63: mgmt.v1alpha1.UserAccountService.SetAccountOnboardingConfig:output_type -> mgmt.v1alpha1.SetAccountOnboardingConfigResponse
+ 46, // 64: mgmt.v1alpha1.UserAccountService.GetAccountStatus:output_type -> mgmt.v1alpha1.GetAccountStatusResponse
+ 48, // 65: mgmt.v1alpha1.UserAccountService.IsAccountStatusValid:output_type -> mgmt.v1alpha1.IsAccountStatusValidResponse
+ 50, // 66: mgmt.v1alpha1.UserAccountService.GetAccountBillingCheckoutSession:output_type -> mgmt.v1alpha1.GetAccountBillingCheckoutSessionResponse
+ 52, // 67: mgmt.v1alpha1.UserAccountService.GetAccountBillingPortalSession:output_type -> mgmt.v1alpha1.GetAccountBillingPortalSessionResponse
+ 54, // 68: mgmt.v1alpha1.UserAccountService.GetBillingAccounts:output_type -> mgmt.v1alpha1.GetBillingAccountsResponse
+ 56, // 69: mgmt.v1alpha1.UserAccountService.SetBillingMeterEvent:output_type -> mgmt.v1alpha1.SetBillingMeterEventResponse
+ 58, // 70: mgmt.v1alpha1.UserAccountService.SetUserRole:output_type -> mgmt.v1alpha1.SetUserRoleResponse
+ 46, // [46:71] is the sub-list for method output_type
+ 21, // [21:46] 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_mgmt_v1alpha1_user_account_proto_init() }
@@ -3675,8 +3868,8 @@ func file_mgmt_v1alpha1_user_account_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_mgmt_v1alpha1_user_account_proto_rawDesc,
- NumEnums: 3,
- NumMessages: 53,
+ NumEnums: 4,
+ NumMessages: 55,
NumExtensions: 0,
NumServices: 1,
},
diff --git a/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.json.go b/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.json.go
index 392e55a0c0..d20e7714d1 100644
--- a/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.json.go
+++ b/backend/gen/go/protos/mgmt/v1alpha1/user_account.pb.json.go
@@ -536,3 +536,23 @@ func (msg *SetBillingMeterEventResponse) MarshalJSON() ([]byte, error) {
func (msg *SetBillingMeterEventResponse) UnmarshalJSON(b []byte) error {
return protojson.UnmarshalOptions{}.Unmarshal(b, msg)
}
+
+// MarshalJSON implements json.Marshaler
+func (msg *SetUserRoleRequest) MarshalJSON() ([]byte, error) {
+ return protojson.MarshalOptions{}.Marshal(msg)
+}
+
+// UnmarshalJSON implements json.Unmarshaler
+func (msg *SetUserRoleRequest) UnmarshalJSON(b []byte) error {
+ return protojson.UnmarshalOptions{}.Unmarshal(b, msg)
+}
+
+// MarshalJSON implements json.Marshaler
+func (msg *SetUserRoleResponse) MarshalJSON() ([]byte, error) {
+ return protojson.MarshalOptions{}.Marshal(msg)
+}
+
+// UnmarshalJSON implements json.Unmarshaler
+func (msg *SetUserRoleResponse) UnmarshalJSON(b []byte) error {
+ return protojson.UnmarshalOptions{}.Unmarshal(b, msg)
+}
diff --git a/backend/internal/cmds/mgmt/serve/connect/cmd.go b/backend/internal/cmds/mgmt/serve/connect/cmd.go
index d5ccbedb51..551678b2b1 100644
--- a/backend/internal/cmds/mgmt/serve/connect/cmd.go
+++ b/backend/internal/cmds/mgmt/serve/connect/cmd.go
@@ -17,6 +17,8 @@ import (
"connectrpc.com/otelconnect"
"github.com/auth0/go-jwt-middleware/v2/validator"
"github.com/go-logr/logr"
+ "github.com/jackc/pgx/v5/stdlib"
+ db_queries "github.com/nucleuscloud/neosync/backend/gen/go/db"
"github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
connectionmanager "github.com/nucleuscloud/neosync/internal/connection-manager"
"github.com/nucleuscloud/neosync/internal/connectrpc/validate"
@@ -43,9 +45,12 @@ import (
bookend_logging_interceptor "github.com/nucleuscloud/neosync/backend/internal/connect/interceptors/bookend"
logger_interceptor "github.com/nucleuscloud/neosync/backend/internal/connect/interceptors/logger"
jobhooks "github.com/nucleuscloud/neosync/backend/internal/ee/hooks/jobs"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac/enforcer"
neosync_gcp "github.com/nucleuscloud/neosync/backend/internal/gcp"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
"github.com/nucleuscloud/neosync/backend/internal/temporal/clientmanager"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
neosynclogger "github.com/nucleuscloud/neosync/backend/pkg/logger"
"github.com/nucleuscloud/neosync/backend/pkg/mongoconnect"
mssql_queries "github.com/nucleuscloud/neosync/backend/pkg/mssql-querier"
@@ -122,6 +127,11 @@ func serve(ctx context.Context) error {
}
slogger.Debug(fmt.Sprintf("neosync cloud enabled: %t", ncloudlicense.IsValid()))
+ cascadelicense := license.NewCascadeLicense(
+ ncloudlicense,
+ eelicense,
+ )
+
mux := http.NewServeMux()
services := []string{
@@ -159,15 +169,18 @@ func serve(ctx context.Context) error {
return err
}
- db, err := neosyncdb.NewFromConfig(dbconfig)
+ pool, err := neosyncdb.NewPool(dbconfig)
if err != nil {
return err
}
+ querier := db_queries.New()
+ db := neosyncdb.New(pool, querier)
+
if viper.GetBool("DB_AUTO_MIGRATE") {
schemaDir := viper.GetString("DB_SCHEMA_DIR")
if schemaDir == "" {
- return errors.New("must provide DB_SCHEMA_DIR env var to run auto db migrations")
+ return errors.New("must provide DB_SCHEMA_DIR env var to run auto db migrationssss")
}
dbMigConfig, err := getDbMigrationConfig()
if err != nil {
@@ -180,8 +193,34 @@ func serve(ctx context.Context) error {
schemaDir,
slogger,
); err != nil {
- return fmt.Errorf("unable to complete database migrations: %w", err)
+ return fmt.Errorf("unable to complete database migrationss: %w", err)
+ }
+ }
+
+ var rbacclient rbac.Interface
+ if cascadelicense.IsValid() {
+ slogger.Debug("rbac is enabled")
+ stddb := stdlib.OpenDBFromPool(pool)
+
+ rbacenforcer, err := enforcer.NewActiveEnforcer(ctx, stddb, "neosync_api.casbin_rule")
+ if err != nil {
+ return err
+ }
+ rbacenforcer.EnableAutoSave(true)
+ err = rbacenforcer.LoadPolicy()
+ if err != nil {
+ return fmt.Errorf("unable to load rbac policies: %w", err)
+ }
+ rbacdb := rbac.NewRbacDb(querier, db.Db)
+ enforcedClient := rbac.New(rbacenforcer)
+ err = enforcedClient.InitPolicies(ctx, rbacdb, slogger)
+ if err != nil {
+ return fmt.Errorf("unable to initialize rbac policies: %w", err)
}
+ rbacclient = enforcedClient
+ } else {
+ slogger.Debug("rbac is disabled")
+ rbacclient = rbac.NewAllowAllClient()
}
stdInterceptors := []connect.Interceptor{}
@@ -436,7 +475,7 @@ func serve(ctx context.Context) error {
IsAuthEnabled: isAuthEnabled,
IsNeosyncCloud: ncloudlicense.IsValid(),
DefaultMaxAllowedRecords: getDefaultMaxAllowedRecords(),
- }, db, temporalConfigProvider, authclient, authadminclient, billingClient)
+ }, db, temporalConfigProvider, authclient, authadminclient, billingClient, rbacclient)
api.Handle(
mgmtv1alpha1connect.NewUserAccountServiceHandler(
useraccountService,
@@ -446,10 +485,11 @@ func serve(ctx context.Context) error {
connect.WithRecover(recoverHandler),
),
)
+ userdataclient := userdata.NewClient(useraccountService, rbacclient)
apiKeyService := v1alpha1_apikeyservice.New(&v1alpha1_apikeyservice.Config{
IsAuthEnabled: isAuthEnabled,
- }, db, useraccountService)
+ }, db, userdataclient)
api.Handle(
mgmtv1alpha1connect.NewApiKeyServiceHandler(
apiKeyService,
@@ -473,10 +513,11 @@ func serve(ctx context.Context) error {
sql_manager.WithConnectionManagerOpts(connectionmanager.WithCloseOnRelease()),
)
mongoconnector := mongoconnect.NewConnector()
+
connectionService := v1alpha1_connectionservice.New(
&v1alpha1_connectionservice.Config{},
db,
- useraccountService,
+ userdataclient,
mongoconnector,
awsManager,
sqlmanager,
@@ -499,7 +540,7 @@ func serve(ctx context.Context) error {
jobhookService := jobhooks.New(
db,
- useraccountService,
+ userdataclient,
jobhookOpts...,
)
@@ -518,9 +559,9 @@ func serve(ctx context.Context) error {
db,
tfwfmgr,
connectionService,
- useraccountService,
sqlmanager,
jobhookService,
+ userdataclient,
)
api.Handle(
mgmtv1alpha1connect.NewJobServiceHandler(
@@ -558,7 +599,7 @@ func serve(ctx context.Context) error {
transformerService := v1alpha1_transformerservice.New(&v1alpha1_transformerservice.Config{
IsPresidioEnabled: ncloudlicense.IsValid(),
IsNeosyncCloud: ncloudlicense.IsValid(),
- }, db, useraccountService, presEntityClient)
+ }, db, presEntityClient, userdataclient)
api.Handle(
mgmtv1alpha1connect.NewTransformersServiceHandler(
transformerService,
@@ -574,7 +615,7 @@ func serve(ctx context.Context) error {
PresidioDefaultLanguage: getPresidioDefaultLanguage(),
IsAuthEnabled: isAuthEnabled,
IsNeosyncCloud: ncloudlicense.IsValid(),
- }, anonymizerMeter, useraccountService, presAnalyzeClient, presAnonClient, db)
+ }, anonymizerMeter, userdataclient, useraccountService, presAnalyzeClient, presAnonClient, db)
api.Handle(
mgmtv1alpha1connect.NewAnonymizationServiceHandler(
anonymizationService,
@@ -588,7 +629,6 @@ func serve(ctx context.Context) error {
gcpmanager := neosync_gcp.NewManager()
connectionDataService := v1alpha1_connectiondataservice.New(
&v1alpha1_connectiondataservice.Config{},
- useraccountService,
connectionService,
jobService,
awsManager,
@@ -612,7 +652,7 @@ func serve(ctx context.Context) error {
if shouldEnableMetricsService() {
metricsService := v1alpha1_metricsservice.New(
&v1alpha1_metricsservice.Config{},
- useraccountService,
+ userdataclient,
jobService,
promv1.NewAPI(promclient),
)
diff --git a/backend/internal/ee/hooks/jobs/service.go b/backend/internal/ee/hooks/jobs/service.go
index 1082ec7e6b..6dcd64d097 100644
--- a/backend/internal/ee/hooks/jobs/service.go
+++ b/backend/internal/ee/hooks/jobs/service.go
@@ -12,14 +12,16 @@ import (
"github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
logger_interceptor "github.com/nucleuscloud/neosync/backend/internal/connect/interceptors/logger"
"github.com/nucleuscloud/neosync/backend/internal/dtomaps"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
)
type Service struct {
- cfg *config
- db *neosyncdb.NeosyncDb
- useraccountService mgmtv1alpha1connect.UserAccountServiceClient
+ cfg *config
+ db *neosyncdb.NeosyncDb
+ userdataclient userdata.Interface
}
var _ Interface = (*Service)(nil)
@@ -49,7 +51,7 @@ type Option func(*config)
func New(
db *neosyncdb.NeosyncDb,
- useraccountservice mgmtv1alpha1connect.UserAccountServiceClient,
+ userdataclient userdata.Interface,
opts ...Option,
) *Service {
cfg := &config{}
@@ -57,7 +59,7 @@ func New(
opt(cfg)
}
- return &Service{cfg: cfg, db: db, useraccountService: useraccountservice}
+ return &Service{cfg: cfg, db: db, userdataclient: userdataclient}
}
func (s *Service) GetJobHooks(
@@ -71,7 +73,7 @@ func (s *Service) GetJobHooks(
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
logger = logger.With("jobId", req.GetJobId())
- verifyResp, err := s.verifyUserHasJob(ctx, req.GetJobId())
+ verifyResp, err := s.verifyUserHasJob(ctx, req.GetJobId(), rbac.JobAction_View)
if err != nil {
return nil, err
}
@@ -114,7 +116,7 @@ func (s *Service) GetJobHook(
return nil, nucleuserrors.NewNotFound("unable to find job hook by id")
}
- verifyResp, err := s.verifyUserHasJob(ctx, neosyncdb.UUIDString(hook.JobID))
+ verifyResp, err := s.verifyUserHasJob(ctx, neosyncdb.UUIDString(hook.JobID), rbac.JobAction_View)
if err != nil {
return nil, err
}
@@ -156,7 +158,7 @@ func (s *Service) DeleteJobHook(
return &mgmtv1alpha1.DeleteJobHookResponse{}, nil
}
- verifyResp, err := s.verifyUserHasJob(ctx, neosyncdb.UUIDString(hook.JobID))
+ verifyResp, err := s.verifyUserHasJob(ctx, neosyncdb.UUIDString(hook.JobID), rbac.JobAction_Delete)
if err != nil {
return nil, err
}
@@ -187,7 +189,7 @@ func (s *Service) IsJobHookNameAvailable(
if err != nil {
return nil, err
}
- verifyResp, err := s.verifyUserHasJob(ctx, req.GetJobId())
+ verifyResp, err := s.verifyUserHasJob(ctx, req.GetJobId(), rbac.JobAction_View)
if err != nil {
return nil, err
}
@@ -221,7 +223,7 @@ func (s *Service) CreateJobHook(
if err != nil {
return nil, err
}
- verifyResp, err := s.verifyUserHasJob(ctx, req.GetJobId())
+ verifyResp, err := s.verifyUserHasJob(ctx, req.GetJobId(), rbac.JobAction_Create)
if err != nil {
return nil, err
}
@@ -230,11 +232,6 @@ func (s *Service) CreateJobHook(
"jobId", neosyncdb.UUIDString(verifyResp.JobUuid),
)
- useruuid, err := s.getUserUuid(ctx)
- if err != nil {
- return nil, err
- }
-
hookReq := req.GetHook()
logger.Debug(fmt.Sprintf("attempting to create new job hook %q", hookReq.GetName()))
@@ -267,8 +264,8 @@ func (s *Service) CreateJobHook(
JobID: jobuuid,
Enabled: hookReq.GetEnabled(),
Priority: priority,
- CreatedByUserID: *useruuid,
- UpdatedByUserID: *useruuid,
+ CreatedByUserID: verifyResp.UserUuid,
+ UpdatedByUserID: verifyResp.UserUuid,
Config: config,
})
if err != nil {
@@ -295,10 +292,6 @@ func (s *Service) UpdateJobHook(
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
logger = logger.With("hookId", req.GetId())
- useruuid, err := s.getUserUuid(ctx)
- if err != nil {
- return nil, err
- }
jobuuid, err := neosyncdb.ToUuid(getResp.GetHook().GetJobId())
if err != nil {
return nil, err
@@ -332,13 +325,22 @@ func (s *Service) UpdateJobHook(
return nil, err
}
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ _, err = s.verifyUserHasJob(ctx, neosyncdb.UUIDString(jobuuid), rbac.JobAction_Edit)
+ if err != nil {
+ return nil, err
+ }
+
updatedhook, err := s.db.Q.UpdateJobHook(ctx, s.db.Db, db_queries.UpdateJobHookParams{
Name: req.GetName(),
Description: req.GetDescription(),
Config: config,
Enabled: req.GetEnabled(),
Priority: priority,
- UpdatedByUserID: *useruuid,
+ UpdatedByUserID: user.PgId(),
ID: hookuuid,
})
if err != nil {
@@ -370,10 +372,11 @@ func (s *Service) SetJobHookEnabled(
return &mgmtv1alpha1.SetJobHookEnabledResponse{Hook: getResp.GetHook()}, nil
}
- useruuid, err := s.getUserUuid(ctx)
+ verifyResp, err := s.verifyUserHasJob(ctx, getResp.GetHook().GetJobId(), rbac.JobAction_Edit)
if err != nil {
return nil, err
}
+
hookuuid, err := neosyncdb.ToUuid(getResp.GetHook().GetId())
if err != nil {
return nil, err
@@ -382,7 +385,7 @@ func (s *Service) SetJobHookEnabled(
logger.Debug(fmt.Sprintf("attempting to update job hook enabled status from %v to %v", getResp.GetHook().GetEnabled(), req.GetEnabled()))
updatedHook, err := s.db.Q.SetJobHookEnabled(ctx, s.db.Db, db_queries.SetJobHookEnabledParams{
Enabled: req.GetEnabled(),
- UpdatedByUserID: *useruuid,
+ UpdatedByUserID: verifyResp.UserUuid,
ID: hookuuid,
})
if err != nil {
@@ -408,7 +411,7 @@ func (s *Service) GetActiveJobHooksByTiming(
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
logger = logger.With("jobId", req.GetJobId())
- verifyResp, err := s.verifyUserHasJob(ctx, req.GetJobId())
+ verifyResp, err := s.verifyUserHasJob(ctx, req.GetJobId(), rbac.JobAction_View)
if err != nil {
return nil, err
}
@@ -456,9 +459,10 @@ func (s *Service) GetActiveJobHooksByTiming(
type verifyUserJobResponse struct {
JobUuid pgtype.UUID
AccountUuid pgtype.UUID
+ UserUuid pgtype.UUID
}
-func (s *Service) verifyUserHasJob(ctx context.Context, jobId string) (*verifyUserJobResponse, error) {
+func (s *Service) verifyUserHasJob(ctx context.Context, jobId string, permission rbac.JobAction) (*verifyUserJobResponse, error) {
jobuuid, err := neosyncdb.ToUuid(jobId)
if err != nil {
return nil, err
@@ -470,13 +474,20 @@ func (s *Service) verifyUserHasJob(ctx context.Context, jobId string) (*verifyUs
} else if err != nil && neosyncdb.IsNoRows(err) {
return nil, nucleuserrors.NewNotFound("unable to find job id")
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(accountUuid))
+
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
+
+ if err := user.EnforceJob(ctx, userdata.NewDbDomainEntity(accountUuid, jobuuid), permission); err != nil {
+ return nil, err
+ }
+
return &verifyUserJobResponse{
JobUuid: jobuuid,
AccountUuid: accountUuid,
+ UserUuid: user.PgId(),
}, nil
}
diff --git a/backend/internal/ee/hooks/jobs/user-account.go b/backend/internal/ee/hooks/jobs/user-account.go
deleted file mode 100644
index 95686ec33e..0000000000
--- a/backend/internal/ee/hooks/jobs/user-account.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package jobhooks
-
-import (
- "context"
-
- "connectrpc.com/connect"
- "github.com/jackc/pgx/v5/pgtype"
- mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
- "github.com/nucleuscloud/neosync/backend/internal/apikey"
- auth_apikey "github.com/nucleuscloud/neosync/backend/internal/auth/apikey"
- nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
- "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
-)
-
-func (s *Service) verifyUserInAccount(
- ctx context.Context,
- accountId string,
-) (*pgtype.UUID, error) {
- accountUuid, err := neosyncdb.ToUuid(accountId)
- if err != nil {
- return nil, err
- }
-
- if isWorkerApiKey(ctx) {
- return &accountUuid, nil
- }
-
- resp, err := s.useraccountService.IsUserInAccount(ctx, connect.NewRequest(&mgmtv1alpha1.IsUserInAccountRequest{AccountId: accountId}))
- if err != nil {
- return nil, err
- }
- if !resp.Msg.Ok {
- return nil, nucleuserrors.NewForbidden("user in not in requested account")
- }
-
- return &accountUuid, nil
-}
-
-func isWorkerApiKey(ctx context.Context) bool {
- data, err := auth_apikey.GetTokenDataFromCtx(ctx)
- if err != nil {
- return false
- }
- return data.ApiKeyType == apikey.WorkerApiKey
-}
-
-func (s *Service) getUserUuid(
- ctx context.Context,
-) (*pgtype.UUID, error) {
- user, err := s.useraccountService.GetUser(ctx, connect.NewRequest(&mgmtv1alpha1.GetUserRequest{}))
- if err != nil {
- return nil, err
- }
- userUuid, err := neosyncdb.ToUuid(user.Msg.UserId)
- if err != nil {
- return nil, err
- }
- return &userUuid, nil
-}
diff --git a/backend/internal/ee/rbac/actions.go b/backend/internal/ee/rbac/actions.go
new file mode 100644
index 0000000000..0a92793b39
--- /dev/null
+++ b/backend/internal/ee/rbac/actions.go
@@ -0,0 +1,41 @@
+package rbac
+
+type AccountAction string
+
+const (
+ AccountAction_Create AccountAction = "create"
+ AccountAction_Delete AccountAction = "delete"
+ AccountAction_View AccountAction = "view"
+ AccountAction_Edit AccountAction = "edit"
+)
+
+func (a AccountAction) String() string {
+ return string(a)
+}
+
+type ConnectionAction string
+
+const (
+ ConnectionAction_Create ConnectionAction = "create"
+ ConnectionAction_Delete ConnectionAction = "delete"
+ ConnectionAction_View ConnectionAction = "view"
+ ConnectionAction_Edit ConnectionAction = "edit"
+)
+
+func (c ConnectionAction) String() string {
+ return string(c)
+}
+
+type JobAction string
+
+const (
+ JobAction_Create JobAction = "create"
+ JobAction_Delete JobAction = "delete"
+ JobAction_Execute JobAction = "execute"
+ JobAction_View JobAction = "view"
+ JobAction_Edit JobAction = "edit"
+)
+
+func (a JobAction) String() string {
+ return string(a)
+}
diff --git a/backend/internal/ee/rbac/allow_all_client.go b/backend/internal/ee/rbac/allow_all_client.go
new file mode 100644
index 0000000000..9f7e9e4c24
--- /dev/null
+++ b/backend/internal/ee/rbac/allow_all_client.go
@@ -0,0 +1,57 @@
+package rbac
+
+import (
+ "context"
+ "log/slog"
+
+ mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
+)
+
+type AllowAllClient struct {
+}
+
+var _ Interface = (*AllowAllClient)(nil)
+
+func (a *AllowAllClient) Job(ctx context.Context, user, account, job EntityString, action JobAction) (bool, error) {
+ return true, nil
+}
+
+func (a *AllowAllClient) Connection(ctx context.Context, user, account, connection EntityString, action ConnectionAction) (bool, error) {
+ return true, nil
+}
+
+func (a *AllowAllClient) Account(ctx context.Context, user, account EntityString, action AccountAction) (bool, error) {
+ return true, nil
+}
+
+func (a *AllowAllClient) EnforceJob(ctx context.Context, user, account, job EntityString, action JobAction) error {
+ return nil
+}
+
+func (a *AllowAllClient) EnforceConnection(ctx context.Context, user, account, connection EntityString, action ConnectionAction) error {
+ return nil
+}
+
+func (a *AllowAllClient) EnforceAccount(ctx context.Context, user, account EntityString, action AccountAction) error {
+ return nil
+}
+
+func (a *AllowAllClient) SetAccountRole(ctx context.Context, user, account EntityString, role mgmtv1alpha1.AccountRole) error {
+ return nil
+}
+
+func (a *AllowAllClient) RemoveAccountRole(ctx context.Context, user, account EntityString, role mgmtv1alpha1.AccountRole) error {
+ return nil
+}
+
+func (a *AllowAllClient) RemoveAccountUser(ctx context.Context, user, account EntityString) error {
+ return nil
+}
+
+func (a *AllowAllClient) SetupNewAccount(ctx context.Context, accountId string, logger *slog.Logger) error {
+ return nil
+}
+
+func NewAllowAllClient() *AllowAllClient {
+ return &AllowAllClient{}
+}
diff --git a/backend/internal/ee/rbac/client.go b/backend/internal/ee/rbac/client.go
new file mode 100644
index 0000000000..5ada51418f
--- /dev/null
+++ b/backend/internal/ee/rbac/client.go
@@ -0,0 +1,21 @@
+package rbac
+
+import "github.com/casbin/casbin/v2"
+
+type Rbac struct {
+ e casbin.IEnforcer
+}
+
+// Combines RBAC interface that handles entity enforcement and role management
+type Interface interface {
+ EntityEnforcer
+ RoleAdmin
+}
+
+var _ Interface = (*Rbac)(nil)
+
+func New(
+ e casbin.IEnforcer,
+) *Rbac {
+ return &Rbac{e: e}
+}
diff --git a/backend/internal/ee/rbac/db.go b/backend/internal/ee/rbac/db.go
new file mode 100644
index 0000000000..7065fd2e7f
--- /dev/null
+++ b/backend/internal/ee/rbac/db.go
@@ -0,0 +1,39 @@
+package rbac
+
+import (
+ "context"
+
+ db_queries "github.com/nucleuscloud/neosync/backend/gen/go/db"
+ "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+)
+
+type RbacDb struct {
+ q db_queries.Querier
+ dbtx db_queries.DBTX
+}
+
+var _ Db = (*RbacDb)(nil)
+
+func NewRbacDb(q db_queries.Querier, dbtx db_queries.DBTX) *RbacDb {
+ return &RbacDb{q: q, dbtx: dbtx}
+}
+
+func (r *RbacDb) GetAccountIds(ctx context.Context) ([]string, error) {
+ resp, err := r.q.GetAccountIds(ctx, r.dbtx)
+ if err != nil {
+ return nil, err
+ }
+ return neosyncdb.UUIDStrings(resp), nil
+}
+
+func (r *RbacDb) GetAccountUsers(ctx context.Context, accountId string) ([]string, error) {
+ accountUuid, err := neosyncdb.ToUuid(accountId)
+ if err != nil {
+ return nil, err
+ }
+ resp, err := r.q.GetAccountUsers(ctx, r.dbtx, accountUuid)
+ if err != nil {
+ return nil, err
+ }
+ return neosyncdb.UUIDStrings(resp), nil
+}
diff --git a/backend/internal/ee/rbac/enforcer/enforcer.go b/backend/internal/ee/rbac/enforcer/enforcer.go
new file mode 100644
index 0000000000..5d4b2d9874
--- /dev/null
+++ b/backend/internal/ee/rbac/enforcer/enforcer.go
@@ -0,0 +1,53 @@
+package enforcer
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+
+ sqladapter "github.com/Blank-Xu/sql-adapter"
+ "github.com/casbin/casbin/v2"
+ "github.com/casbin/casbin/v2/model"
+ "github.com/casbin/casbin/v2/persist"
+)
+
+// The default casbin enforcer with a SQL-enabled backend
+func NewActiveEnforcer(
+ ctx context.Context,
+ db *sql.DB,
+ casbinTableName string,
+) (casbin.IEnforcer, error) {
+ adapter, err := newSqlAdapter(ctx, db, casbinTableName)
+ if err != nil {
+ return nil, err
+ }
+ return newEnforcer(adapter)
+}
+
+func newEnforcer(
+ adapter persist.Adapter,
+) (casbin.IEnforcer, error) {
+ m, err := model.NewModelFromString(neosyncRbacModel)
+ if err != nil {
+ return nil, fmt.Errorf("unable to initialize casbin model from string: %w", err)
+ }
+
+ enforcer, err := casbin.NewSyncedEnforcer(m, adapter)
+ if err != nil {
+ return nil, fmt.Errorf("unable to initialize casbin synced cached enforcer: %w", err)
+ }
+ enforcer.EnableAutoSave(true) // seems to do this automatically but it doesn't hurt
+ return enforcer, nil
+}
+
+func newSqlAdapter(
+ ctx context.Context,
+ db *sql.DB,
+ tableName string,
+) (persist.Adapter, error) {
+ adapter, err := sqladapter.NewAdapterWithContext(ctx, db, "postgres", tableName)
+ if err != nil {
+ return nil, fmt.Errorf("unable to create casbin sql adapter: %w", err)
+ }
+ return adapter, nil
+}
diff --git a/backend/internal/ee/rbac/enforcer/model.go b/backend/internal/ee/rbac/enforcer/model.go
new file mode 100644
index 0000000000..09120bd9fd
--- /dev/null
+++ b/backend/internal/ee/rbac/enforcer/model.go
@@ -0,0 +1,6 @@
+package enforcer
+
+import _ "embed"
+
+//go:embed rbac_model.conf
+var neosyncRbacModel string
diff --git a/backend/internal/ee/rbac/enforcer/rbac_model.conf b/backend/internal/ee/rbac/enforcer/rbac_model.conf
new file mode 100644
index 0000000000..487455d245
--- /dev/null
+++ b/backend/internal/ee/rbac/enforcer/rbac_model.conf
@@ -0,0 +1,14 @@
+[request_definition]
+r = sub, dom, obj, act
+
+[policy_definition]
+p = sub, dom, obj, act
+
+[role_definition]
+g = _, _, _
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = g(r.sub, p.sub, r.dom) && r.dom == p.dom && keyMatch(r.obj, p.obj) && keyMatch(r.act, p.act)
diff --git a/backend/internal/ee/rbac/entity.go b/backend/internal/ee/rbac/entity.go
new file mode 100644
index 0000000000..78ba3a804d
--- /dev/null
+++ b/backend/internal/ee/rbac/entity.go
@@ -0,0 +1,53 @@
+package rbac
+
+import (
+ "fmt"
+
+ "github.com/jackc/pgx/v5/pgtype"
+ "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+)
+
+const (
+ Wildcard = "*"
+)
+
+var (
+ JobWildcard = NewEntity("jobs", Wildcard)
+ ConnectionWildcard = NewEntity("connections", Wildcard)
+)
+
+type Entity struct {
+ prefix string
+ value string
+}
+
+type EntityString interface {
+ String() string
+}
+
+func NewEntity(prefix, value string) *Entity {
+ return &Entity{prefix: prefix, value: value}
+}
+
+func (e *Entity) String() string {
+ return fmt.Sprintf("%s/%s", e.prefix, e.value)
+}
+
+func NewAccountIdEntity(value string) *Entity {
+ return NewEntity("accounts", value)
+}
+
+func NewJobIdEntity(value string) *Entity {
+ return NewEntity("jobs", value)
+}
+
+func NewUserIdEntity(value string) *Entity {
+ return NewEntity("users", value)
+}
+func NewPgUserIdEntity(value pgtype.UUID) *Entity {
+ return NewUserIdEntity(neosyncdb.UUIDString(value))
+}
+
+func NewConnectionIdEntity(value string) *Entity {
+ return NewEntity("connections", value)
+}
diff --git a/backend/internal/ee/rbac/policy.go b/backend/internal/ee/rbac/policy.go
new file mode 100644
index 0000000000..a176f8a1ec
--- /dev/null
+++ b/backend/internal/ee/rbac/policy.go
@@ -0,0 +1,372 @@
+package rbac
+
+import (
+ "context"
+ "fmt"
+ "log/slog"
+
+ "github.com/casbin/casbin/v2"
+ mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
+ nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
+ "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+)
+
+// Interface used by rbac engine to make necessary calls to the database
+type Db interface {
+ GetAccountIds(ctx context.Context) ([]string, error)
+ GetAccountUsers(ctx context.Context, accountId string) ([]string, error)
+}
+
+// Interface that handles enforcing entity level policies
+type EntityEnforcer interface {
+ Job(ctx context.Context, user EntityString, account EntityString, job EntityString, action JobAction) (bool, error)
+ EnforceJob(ctx context.Context, user EntityString, account EntityString, job EntityString, action JobAction) error
+ Connection(ctx context.Context, user EntityString, account EntityString, connection EntityString, action ConnectionAction) (bool, error)
+ EnforceConnection(ctx context.Context, user EntityString, account EntityString, connection EntityString, action ConnectionAction) error
+ Account(ctx context.Context, user EntityString, account EntityString, action AccountAction) (bool, error)
+ EnforceAccount(ctx context.Context, user EntityString, account EntityString, action AccountAction) error
+}
+
+// Interface that handles setting and removing roles for users
+type RoleAdmin interface {
+ SetAccountRole(ctx context.Context, user EntityString, account EntityString, role mgmtv1alpha1.AccountRole) error
+ RemoveAccountRole(ctx context.Context, user EntityString, account EntityString, role mgmtv1alpha1.AccountRole) error
+ RemoveAccountUser(ctx context.Context, user EntityString, account EntityString) error
+ SetupNewAccount(ctx context.Context, accountId string, logger *slog.Logger) error
+}
+
+// Initialize default policies for existing accounts at startup
+func (r *Rbac) InitPolicies(
+ ctx context.Context,
+ db Db,
+ logger *slog.Logger,
+) error {
+ accountIds, err := db.GetAccountIds(ctx)
+ if err != nil {
+ return fmt.Errorf("unable to retrieve account ids during casbin policy init: %w", err)
+ }
+ err = setupAccountPolicies(r.e, accountIds, logger)
+ if err != nil {
+ return err
+ }
+
+ err = setupUserAssignments(ctx, db, r.e, accountIds, logger)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func setupAccountPolicies(enforcer casbin.IEnforcer, accountIds []string, logger *slog.Logger) error {
+ logger.Debug(fmt.Sprintf("found %d account ids to associate with rbac policies", len(accountIds)))
+
+ policyRules := [][]string{}
+ for _, accountId := range accountIds {
+ accountRules := getAccountPolicyRules(accountId)
+ policyRules = append(
+ policyRules,
+ accountRules...,
+ )
+ }
+
+ if len(policyRules) > 0 {
+ logger.Debug(fmt.Sprintf("adding %d policy rules to rbac engine", len(policyRules)))
+ for _, policy := range policyRules {
+ err := setPolicy(enforcer, policy)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// For the given accounts, assign users to the account admin role if the account does not currently have any role assignments
+func setupUserAssignments(ctx context.Context, db Db, enforcer casbin.IEnforcer, accountIds []string, logger *slog.Logger) error {
+ policiesByDomain, err := getGroupingPoliciesByDomain(enforcer)
+ if err != nil {
+ return err
+ }
+
+ groupedRules := [][]string{}
+ for _, accountId := range accountIds {
+ _, ok := policiesByDomain[NewAccountIdEntity(accountId).String()]
+ if ok {
+ continue
+ }
+
+ // get users in account
+ // assign them all account admin role for the account
+ users, err := db.GetAccountUsers(ctx, accountId)
+ if err != nil && !neosyncdb.IsNoRows(err) {
+ return err
+ } else if err != nil && neosyncdb.IsNoRows(err) {
+ logger.Debug(fmt.Sprintf("no users found for account %s, skipping", accountId))
+ continue
+ }
+ logger.Debug(fmt.Sprintf("found %d users for account %s, assigning all account admin role", len(users), accountId))
+ for _, user := range users {
+ groupedRules = append(groupedRules, []string{
+ NewUserIdEntity(user).String(),
+ Role_AccountAdmin.String(),
+ NewAccountIdEntity(accountId).String(),
+ })
+ }
+ }
+ if len(groupedRules) > 0 {
+ logger.Debug(fmt.Sprintf("adding %d grouping policies to rbac engine", len(groupedRules)))
+ _, err := enforcer.AddNamedGroupingPolicies("g", groupedRules)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func getGroupingPoliciesByDomain(enforcer casbin.IEnforcer) (map[string][][]string, error) {
+ // Get all grouping policies
+ allPolicies, err := enforcer.GetNamedGroupingPolicy("g")
+ if err != nil {
+ return nil, fmt.Errorf("unable to get grouping policies: %w", err)
+ }
+
+ // Create a map to store policies by domain
+ policiesByDomain := make(map[string][][]string)
+
+ // Group policies by domain (domain is the third element, index 2)
+ for _, policy := range allPolicies {
+ domain := policy[2]
+ policiesByDomain[domain] = append(policiesByDomain[domain], policy)
+ }
+
+ return policiesByDomain, nil
+}
+
+func (r *Rbac) SetupNewAccount(
+ ctx context.Context,
+ accountId string,
+ logger *slog.Logger,
+) error {
+ accountRules := getAccountPolicyRules(accountId)
+ if len(accountRules) > 0 {
+ logger.Debug(fmt.Sprintf("adding %d policy rules to rbac engine for account %s", len(accountRules), accountId))
+ for _, policy := range accountRules {
+ err := setPolicy(r.e, policy)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func getAccountPolicyRules(accountId string) [][]string {
+ accountKey := NewAccountIdEntity(accountId).String()
+ return [][]string{
+ {
+ Role_AccountAdmin.String(),
+ accountKey,
+ Wildcard, // any resource in the account
+ Wildcard, // all actions in the account
+ },
+ {
+ Role_JobDeveloper.String(),
+ accountKey,
+ JobWildcard.String(), // all jobs in the account
+ Wildcard, // all job actions
+ },
+ {
+ Role_JobDeveloper.String(),
+ accountKey,
+ ConnectionWildcard.String(), // all connections in the account
+ Wildcard, // all connection actions
+ },
+ {
+ Role_JobDeveloper.String(),
+ accountKey,
+ accountKey,
+ AccountAction_View.String(), // job developer can view the account
+ },
+ {
+ Role_JobViewer.String(),
+ accountKey,
+ JobWildcard.String(),
+ JobAction_View.String(), // job viewer can view all jobs in the account
+ },
+ {
+ Role_JobViewer.String(),
+ accountKey,
+ JobWildcard.String(),
+ JobAction_Execute.String(), // job viewer can execute all jobs in the account
+ },
+ {
+ Role_JobViewer.String(),
+ accountKey,
+ ConnectionWildcard.String(),
+ ConnectionAction_View.String(), // job viewer can view all connections in the account
+ },
+ {
+ Role_JobViewer.String(),
+ accountKey,
+ accountKey,
+ AccountAction_View.String(), // job viewer can view the account
+ },
+ }
+}
+
+// For the given user and account, removes all existing roles and replaces them with the new role
+func (r *Rbac) SetAccountRole(
+ ctx context.Context,
+ user EntityString,
+ account EntityString,
+ role mgmtv1alpha1.AccountRole,
+) error {
+ roleName, err := toRoleName(role)
+ if err != nil {
+ return err
+ }
+
+ _, err = r.e.DeleteRolesForUserInDomain(user.String(), account.String())
+ if err != nil {
+ return fmt.Errorf("unable to delete roles for user in domain: %w", err)
+ }
+
+ _, err = r.e.AddRoleForUserInDomain(user.String(), roleName, account.String())
+ return err
+}
+
+// For the given user and account, removes the given role
+func (r *Rbac) RemoveAccountRole(
+ ctx context.Context,
+ user EntityString,
+ account EntityString,
+ role mgmtv1alpha1.AccountRole,
+) error {
+ roleName, err := toRoleName(role)
+ if err != nil {
+ return err
+ }
+ _, err = r.e.DeleteRoleForUserInDomain(user.String(), roleName, account.String())
+ return err
+}
+
+// For the given user and account, removes all roles for the user
+func (r *Rbac) RemoveAccountUser(
+ ctx context.Context,
+ user EntityString,
+ account EntityString,
+) error {
+ _, err := r.e.DeleteRolesForUserInDomain(user.String(), account.String())
+ return err
+}
+
+func (r *Rbac) Job(
+ ctx context.Context,
+ user EntityString,
+ account EntityString,
+ job EntityString,
+ action JobAction,
+) (bool, error) {
+ return r.e.Enforce(user.String(), account.String(), job.String(), action.String())
+}
+
+func (r *Rbac) EnforceJob(
+ ctx context.Context,
+ user EntityString,
+ account EntityString,
+ job EntityString,
+ action JobAction,
+) error {
+ ok, err := r.Job(ctx, user, account, job, action)
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return nucleuserrors.NewForbidden(fmt.Sprintf("user does not have permission to %s job", action))
+ }
+ return nil
+}
+
+func (r *Rbac) Connection(
+ ctx context.Context,
+ user EntityString,
+ account EntityString,
+ connection EntityString,
+ action ConnectionAction,
+) (bool, error) {
+ return r.e.Enforce(user.String(), account.String(), connection.String(), action.String())
+}
+
+func (r *Rbac) EnforceConnection(
+ ctx context.Context,
+ user EntityString,
+ account EntityString,
+ connection EntityString,
+ action ConnectionAction,
+) error {
+ ok, err := r.Connection(ctx, user, account, connection, action)
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return nucleuserrors.NewForbidden(fmt.Sprintf("user does not have permission to %s connection", action))
+ }
+ return nil
+}
+
+func (r *Rbac) Account(
+ ctx context.Context,
+ user EntityString,
+ account EntityString,
+ action AccountAction,
+) (bool, error) {
+ return r.e.Enforce(user.String(), account.String(), account.String(), action.String())
+}
+
+func (r *Rbac) EnforceAccount(
+ ctx context.Context,
+ user EntityString,
+ account EntityString,
+ action AccountAction,
+) error {
+ ok, err := r.Account(ctx, user, account, action)
+ if err != nil {
+ return err
+ }
+ if !ok {
+ return nucleuserrors.NewForbidden(fmt.Sprintf("user does not have permission to %s account", action))
+ }
+ return nil
+}
+
+func toRoleName(role mgmtv1alpha1.AccountRole) (string, error) {
+ switch role {
+ case mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_ADMIN:
+ return Role_AccountAdmin.String(), nil
+ case mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_JOB_DEVELOPER:
+ return Role_JobDeveloper.String(), nil
+ case mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_JOB_VIEWER:
+ return Role_JobViewer.String(), nil
+ default:
+ return "", fmt.Errorf("account role provided has not be mapped to a casbin role name: %d", role)
+ }
+}
+
+func setPolicy(e casbin.IEnforcer, policy []string) error {
+ // AddPoliciesEx is what should be uesd here but is resulting in duplicates (and errors with unique constraint)
+ // AddPolicies handles the unique constraint but fails if even one policy already exists..
+
+ // This logic here seems to handle what I want it to do instead strangely...
+ ok, err := e.HasPolicy(policy)
+ if err != nil {
+ return fmt.Errorf("unable to check if policy exists: %w", err)
+ }
+ if !ok {
+ _, err = e.AddPolicy(policy) // always resolves to true even if it was not added, may be adapter dependent
+ if err != nil {
+ return fmt.Errorf("unable to add policy: %w", err)
+ }
+ }
+ return nil
+}
diff --git a/backend/internal/ee/rbac/rbac_integration_test.go b/backend/internal/ee/rbac/rbac_integration_test.go
new file mode 100644
index 0000000000..16b918de7e
--- /dev/null
+++ b/backend/internal/ee/rbac/rbac_integration_test.go
@@ -0,0 +1,232 @@
+package rbac
+
+import (
+ "context"
+ "testing"
+
+ "github.com/google/uuid"
+ "github.com/jackc/pgx/v5/stdlib"
+ mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac/enforcer"
+ neomigrate "github.com/nucleuscloud/neosync/internal/migrate"
+ "github.com/nucleuscloud/neosync/internal/testutil"
+ testcontainers_postgres "github.com/nucleuscloud/neosync/internal/testutil/testcontainers/postgres"
+ "github.com/stretchr/testify/require"
+)
+
+func TestRbac(t *testing.T) {
+ t.Parallel()
+ ok := testutil.ShouldRunIntegrationTest()
+ if !ok {
+ return
+ }
+
+ ctx := context.Background()
+
+ container, err := testcontainers_postgres.NewPostgresTestContainer(ctx)
+ require.NoError(t, err)
+
+ err = neomigrate.Up(ctx, container.URL, "../../../sql/postgresql/schema", testutil.GetTestLogger(t))
+ require.NoError(t, err)
+
+ rbacenforcer, err := enforcer.NewActiveEnforcer(ctx, stdlib.OpenDBFromPool(container.DB), "neosync_api.casbin_rule")
+ require.NoError(t, err)
+
+ rbacenforcer.EnableAutoSave(true)
+ err = rbacenforcer.LoadPolicy()
+ require.NoError(t, err)
+
+ rbacclient := New(rbacenforcer)
+
+ t.Run("account_admin", func(t *testing.T) {
+ t.Parallel()
+
+ accountId := uuid.NewString()
+ userId := uuid.NewString()
+ err := rbacclient.SetupNewAccount(ctx, accountId, testutil.GetTestLogger(t))
+ require.NoError(t, err)
+
+ // Set user as account admin
+ err = rbacclient.SetAccountRole(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_ADMIN)
+ require.NoError(t, err)
+
+ // Test Account permissions
+ err = rbacclient.EnforceAccount(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), AccountAction_View)
+ require.NoError(t, err)
+
+ err = rbacclient.EnforceAccount(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), AccountAction_Edit)
+ require.NoError(t, err)
+
+ // Test Job permissions
+ jobId := uuid.NewString()
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_Create)
+ require.NoError(t, err)
+
+ // Test Connection permissions
+ connectionId := uuid.NewString()
+ err = rbacclient.EnforceConnection(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), NewConnectionIdEntity(connectionId), ConnectionAction_Create)
+ require.NoError(t, err)
+ })
+
+ t.Run("job_developer", func(t *testing.T) {
+ t.Parallel()
+
+ accountId := uuid.NewString()
+ userId := uuid.NewString()
+ err := rbacclient.SetupNewAccount(ctx, accountId, testutil.GetTestLogger(t))
+ require.NoError(t, err)
+
+ // Set user as job developer
+ err = rbacclient.SetAccountRole(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_JOB_DEVELOPER)
+ require.NoError(t, err)
+
+ // Test Account permissions (should only have view)
+ err = rbacclient.EnforceAccount(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), AccountAction_View)
+ require.NoError(t, err)
+
+ err = rbacclient.EnforceAccount(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), AccountAction_Edit)
+ require.Error(t, err)
+ require.ErrorContains(t, err, "user does not have permission to edit account")
+
+ // Test Job permissions (should have all)
+ jobId := uuid.NewString()
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_Create)
+ require.NoError(t, err)
+ })
+
+ t.Run("job_viewer", func(t *testing.T) {
+ t.Parallel()
+
+ accountId := uuid.NewString()
+ userId := uuid.NewString()
+ err := rbacclient.SetupNewAccount(ctx, accountId, testutil.GetTestLogger(t))
+ require.NoError(t, err)
+
+ // Set user as job viewer
+ err = rbacclient.SetAccountRole(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_JOB_VIEWER)
+ require.NoError(t, err)
+
+ // Test Account permissions (should only have view)
+ err = rbacclient.EnforceAccount(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), AccountAction_View)
+ require.NoError(t, err)
+
+ // Test Job permissions (should only have view and execute)
+ jobId := uuid.NewString()
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_View)
+ require.NoError(t, err)
+
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_Execute)
+ require.NoError(t, err)
+
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_Create)
+ require.Error(t, err)
+ require.ErrorContains(t, err, "user does not have permission to create job")
+ })
+
+ t.Run("role_changes", func(t *testing.T) {
+ t.Parallel()
+
+ accountId := uuid.NewString()
+ userId := uuid.NewString()
+ err := rbacclient.SetupNewAccount(ctx, accountId, testutil.GetTestLogger(t))
+ require.NoError(t, err)
+
+ // Set and remove roles
+ err = rbacclient.SetAccountRole(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_ADMIN)
+ require.NoError(t, err)
+
+ err = rbacclient.RemoveAccountRole(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_ADMIN)
+ require.NoError(t, err)
+
+ // Verify permissions are removed
+ err = rbacclient.EnforceAccount(ctx, NewUserIdEntity(userId), NewAccountIdEntity(accountId), AccountAction_View)
+ require.Error(t, err)
+ require.ErrorContains(t, err, "user does not have permission to view account")
+ })
+
+ t.Run("cross_account_access", func(t *testing.T) {
+ t.Parallel()
+
+ // Setup first account and user
+ account1Id := uuid.NewString()
+ user1Id := uuid.NewString()
+ err := rbacclient.SetupNewAccount(ctx, account1Id, testutil.GetTestLogger(t))
+ require.NoError(t, err)
+ err = rbacclient.SetAccountRole(ctx, NewUserIdEntity(user1Id), NewAccountIdEntity(account1Id), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_ADMIN)
+ require.NoError(t, err)
+
+ // Setup second account and user
+ account2Id := uuid.NewString()
+ user2Id := uuid.NewString()
+ err = rbacclient.SetupNewAccount(ctx, account2Id, testutil.GetTestLogger(t))
+ require.NoError(t, err)
+ err = rbacclient.SetAccountRole(ctx, NewUserIdEntity(user2Id), NewAccountIdEntity(account2Id), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_ADMIN)
+ require.NoError(t, err)
+
+ // Verify user1 cannot access account2's resources
+ err = rbacclient.EnforceAccount(ctx, NewUserIdEntity(user1Id), NewAccountIdEntity(account2Id), AccountAction_View)
+ require.Error(t, err)
+ require.ErrorContains(t, err, "user does not have permission to view account")
+
+ // Verify user1 cannot access jobs in account2
+ jobId := uuid.NewString()
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(user1Id), NewAccountIdEntity(account2Id), NewJobIdEntity(jobId), JobAction_View)
+ require.Error(t, err)
+ require.ErrorContains(t, err, "user does not have permission to view job")
+
+ // Verify user1 cannot access connections in account2
+ connectionId := uuid.NewString()
+ err = rbacclient.EnforceConnection(ctx, NewUserIdEntity(user1Id), NewAccountIdEntity(account2Id), NewConnectionIdEntity(connectionId), ConnectionAction_View)
+ require.Error(t, err)
+ require.ErrorContains(t, err, "user does not have permission to view connection")
+ })
+
+ t.Run("mixed_roles_same_account", func(t *testing.T) {
+ t.Parallel()
+
+ accountId := uuid.NewString()
+ adminUserId := uuid.NewString()
+ viewerUserId := uuid.NewString()
+ err := rbacclient.SetupNewAccount(ctx, accountId, testutil.GetTestLogger(t))
+ require.NoError(t, err)
+
+ // Set up admin user
+ err = rbacclient.SetAccountRole(ctx, NewUserIdEntity(adminUserId), NewAccountIdEntity(accountId), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_ADMIN)
+ require.NoError(t, err)
+
+ // Set up job viewer user
+ err = rbacclient.SetAccountRole(ctx, NewUserIdEntity(viewerUserId), NewAccountIdEntity(accountId), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_JOB_VIEWER)
+ require.NoError(t, err)
+
+ jobId := uuid.NewString()
+ connectionId := uuid.NewString()
+
+ // Verify admin has full permissions
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(adminUserId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_Create)
+ require.NoError(t, err)
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(adminUserId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_Delete)
+ require.NoError(t, err)
+ err = rbacclient.EnforceConnection(ctx, NewUserIdEntity(adminUserId), NewAccountIdEntity(accountId), NewConnectionIdEntity(connectionId), ConnectionAction_Create)
+ require.NoError(t, err)
+
+ // Verify job viewer can only view and execute jobs
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(viewerUserId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_View)
+ require.NoError(t, err)
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(viewerUserId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_Execute)
+ require.NoError(t, err)
+
+ // Verify job viewer cannot perform other actions
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(viewerUserId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_Create)
+ require.Error(t, err)
+ require.ErrorContains(t, err, "user does not have permission to create job")
+
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(viewerUserId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_Delete)
+ require.Error(t, err)
+ require.ErrorContains(t, err, "user does not have permission to delete job")
+
+ err = rbacclient.EnforceJob(ctx, NewUserIdEntity(viewerUserId), NewAccountIdEntity(accountId), NewJobIdEntity(jobId), JobAction_Edit)
+ require.Error(t, err)
+ require.ErrorContains(t, err, "user does not have permission to edit job")
+ })
+
+}
diff --git a/backend/internal/ee/rbac/roles.go b/backend/internal/ee/rbac/roles.go
new file mode 100644
index 0000000000..cd23445bc0
--- /dev/null
+++ b/backend/internal/ee/rbac/roles.go
@@ -0,0 +1,13 @@
+package rbac
+
+type Role string
+
+const (
+ Role_AccountAdmin Role = "account_admin"
+ Role_JobDeveloper Role = "job_developer"
+ Role_JobViewer Role = "job_viewer"
+)
+
+func (r Role) String() string {
+ return string(r)
+}
diff --git a/backend/internal/neosyncdb/db.go b/backend/internal/neosyncdb/db.go
index 3a2a13ce63..01023cc74c 100644
--- a/backend/internal/neosyncdb/db.go
+++ b/backend/internal/neosyncdb/db.go
@@ -58,7 +58,7 @@ func New(db DBTX, q db_queries.Querier) *NeosyncDb {
}
}
-func NewFromConfig(config *ConnectConfig) (*NeosyncDb, error) {
+func NewPool(config *ConnectConfig) (*pgxpool.Pool, error) {
pgxconfig, err := pgxpool.ParseConfig(GetDbUrl(config))
if err != nil {
return nil, fmt.Errorf("unable to parse pgxpool config: %w", err)
@@ -72,6 +72,14 @@ func NewFromConfig(config *ConnectConfig) (*NeosyncDb, error) {
if err != nil {
return nil, fmt.Errorf("uanble to initialize pgx pool from configuration: %w", err)
}
+ return pool, nil
+}
+
+func NewFromConfig(config *ConnectConfig) (*NeosyncDb, error) {
+ pool, err := NewPool(config)
+ if err != nil {
+ return nil, err
+ }
return New(pool, nil), nil
}
diff --git a/backend/internal/userdata/client.go b/backend/internal/userdata/client.go
new file mode 100644
index 0000000000..0e12867368
--- /dev/null
+++ b/backend/internal/userdata/client.go
@@ -0,0 +1,68 @@
+package userdata
+
+import (
+ "context"
+ "fmt"
+
+ "connectrpc.com/connect"
+ mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
+ auth_apikey "github.com/nucleuscloud/neosync/backend/internal/auth/apikey"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
+ "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+)
+
+type UserServiceClient interface {
+ GetUser(ctx context.Context, req *connect.Request[mgmtv1alpha1.GetUserRequest]) (*connect.Response[mgmtv1alpha1.GetUserResponse], error)
+ IsUserInAccount(ctx context.Context, req *connect.Request[mgmtv1alpha1.IsUserInAccountRequest]) (*connect.Response[mgmtv1alpha1.IsUserInAccountResponse], error)
+}
+
+type Client struct {
+ userServiceClient UserServiceClient
+ enforcer rbac.EntityEnforcer
+}
+
+type Interface interface {
+ GetUser(ctx context.Context) (*User, error)
+}
+
+type GetUserResponse struct {
+ User *User
+}
+
+func NewClient(
+ userServiceClient UserServiceClient,
+ enforcer rbac.EntityEnforcer,
+) *Client {
+ return &Client{
+ userServiceClient: userServiceClient,
+ enforcer: enforcer,
+ }
+}
+
+func (c *Client) GetUser(ctx context.Context) (*User, error) {
+ resp, err := c.userServiceClient.GetUser(ctx, connect.NewRequest(&mgmtv1alpha1.GetUserRequest{}))
+ if err != nil {
+ return nil, fmt.Errorf("unable to get user: %w", err)
+ }
+ pguuid, err := neosyncdb.ToUuid(resp.Msg.GetUserId())
+ if err != nil {
+ return nil, fmt.Errorf("unable to parse user id: %w", err)
+ }
+
+ apiKeyData, _ := auth_apikey.GetTokenDataFromCtx(ctx)
+
+ user := &User{
+ id: pguuid,
+ apiKeyData: apiKeyData,
+ userAccountServiceClient: c.userServiceClient,
+ }
+ user.EntityEnforcer = &UserEntityEnforcer{
+ enforcer: c.enforcer,
+ user: rbac.NewUserIdEntity(resp.Msg.GetUserId()),
+ enforceAccountAccess: func(ctx context.Context, accountId string) error {
+ return EnforceAccountAccess(ctx, user, accountId)
+ },
+ }
+
+ return user, nil
+}
diff --git a/backend/internal/userdata/entity.go b/backend/internal/userdata/entity.go
new file mode 100644
index 0000000000..461ff7716e
--- /dev/null
+++ b/backend/internal/userdata/entity.go
@@ -0,0 +1,71 @@
+package userdata
+
+import (
+ "github.com/jackc/pgx/v5/pgtype"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
+ "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+)
+
+// Domain entity interface that mimics the domain model of the mgmt service
+type DomainEntity interface {
+ Identifier
+ GetAccountId() string
+}
+
+type DomainEntityImpl struct {
+ id string
+ accountId string
+ isWild bool
+}
+
+type Identifier interface {
+ GetId() string
+}
+
+func (j *DomainEntityImpl) GetId() string {
+ return j.id
+}
+func (j *DomainEntityImpl) GetAccountId() string {
+ return j.accountId
+}
+
+// Used for things like mgmtv1alpha1.Job, mgmtv1alpha1.Connection, etc
+func NewDomainEntity(accountId, id string) DomainEntity {
+ return &DomainEntityImpl{
+ id: id,
+ accountId: accountId,
+ }
+}
+
+// Used for things like mgmtv1alpha1.Job, mgmtv1alpha1.Connection, etc
+// But for checking wildcard or account-level access
+func NewWildcardDomainEntity(accountId string) DomainEntity {
+ return &DomainEntityImpl{
+ id: rbac.Wildcard,
+ accountId: accountId,
+ isWild: true,
+ }
+}
+
+// Helper function that can be used when dealing with the DB entities instead of the domain entities
+func NewDbDomainEntity(accountId, id pgtype.UUID) DomainEntity {
+ return &DomainEntityImpl{
+ id: neosyncdb.UUIDString(id),
+ accountId: neosyncdb.UUIDString(accountId),
+ }
+}
+
+type IdentifierImpl struct {
+ id string
+}
+
+// Helper function that creates just an identitier. Generally used when working with the account object
+func NewIdentifier(id string) Identifier {
+ return &IdentifierImpl{
+ id: id,
+ }
+}
+
+func (i *IdentifierImpl) GetId() string {
+ return i.id
+}
diff --git a/backend/internal/userdata/entity_enforcer.go b/backend/internal/userdata/entity_enforcer.go
new file mode 100644
index 0000000000..827f0d2f6e
--- /dev/null
+++ b/backend/internal/userdata/entity_enforcer.go
@@ -0,0 +1,46 @@
+package userdata
+
+import (
+ "context"
+
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
+)
+
+type UserEntityEnforcer struct {
+ enforcer rbac.EntityEnforcer
+ user rbac.EntityString
+ enforceAccountAccess func(ctx context.Context, accountId string) error
+}
+
+var _ EntityEnforcer = (*UserEntityEnforcer)(nil)
+
+// Higher level entity enforcer that slightly abstracts away the lower level rbac interface
+// The intention here is to be able to use objects that are closer to the mgmt domain model
+// rather than the lower level rbac model
+// see the entity.go file for functions that help with this
+type EntityEnforcer interface {
+ EnforceJob(ctx context.Context, job DomainEntity, action rbac.JobAction) error
+ EnforceConnection(ctx context.Context, connection DomainEntity, action rbac.ConnectionAction) error
+ EnforceAccount(ctx context.Context, account Identifier, action rbac.AccountAction) error
+}
+
+func (u *UserEntityEnforcer) EnforceJob(ctx context.Context, job DomainEntity, action rbac.JobAction) error {
+ if err := u.enforceAccountAccess(ctx, job.GetAccountId()); err != nil {
+ return err
+ }
+ return u.enforcer.EnforceJob(ctx, u.user, rbac.NewAccountIdEntity(job.GetAccountId()), rbac.NewJobIdEntity(job.GetId()), action)
+}
+
+func (u *UserEntityEnforcer) EnforceConnection(ctx context.Context, connection DomainEntity, action rbac.ConnectionAction) error {
+ if err := u.enforceAccountAccess(ctx, connection.GetAccountId()); err != nil {
+ return err
+ }
+ return u.enforcer.EnforceConnection(ctx, u.user, rbac.NewAccountIdEntity(connection.GetAccountId()), rbac.NewConnectionIdEntity(connection.GetId()), action)
+}
+
+func (u *UserEntityEnforcer) EnforceAccount(ctx context.Context, account Identifier, action rbac.AccountAction) error {
+ if err := u.enforceAccountAccess(ctx, account.GetId()); err != nil {
+ return err
+ }
+ return u.enforcer.EnforceAccount(ctx, u.user, rbac.NewAccountIdEntity(account.GetId()), action)
+}
diff --git a/backend/internal/userdata/mock_EntityEnforcer.go b/backend/internal/userdata/mock_EntityEnforcer.go
new file mode 100644
index 0000000000..0bc5b90143
--- /dev/null
+++ b/backend/internal/userdata/mock_EntityEnforcer.go
@@ -0,0 +1,181 @@
+// Code generated by mockery. DO NOT EDIT.
+
+package userdata
+
+import (
+ context "context"
+
+ rbac "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
+ mock "github.com/stretchr/testify/mock"
+)
+
+// MockEntityEnforcer is an autogenerated mock type for the EntityEnforcer type
+type MockEntityEnforcer struct {
+ mock.Mock
+}
+
+type MockEntityEnforcer_Expecter struct {
+ mock *mock.Mock
+}
+
+func (_m *MockEntityEnforcer) EXPECT() *MockEntityEnforcer_Expecter {
+ return &MockEntityEnforcer_Expecter{mock: &_m.Mock}
+}
+
+// EnforceAccount provides a mock function with given fields: ctx, account, action
+func (_m *MockEntityEnforcer) EnforceAccount(ctx context.Context, account Identifier, action rbac.AccountAction) error {
+ ret := _m.Called(ctx, account, action)
+
+ if len(ret) == 0 {
+ panic("no return value specified for EnforceAccount")
+ }
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(context.Context, Identifier, rbac.AccountAction) error); ok {
+ r0 = rf(ctx, account, action)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// MockEntityEnforcer_EnforceAccount_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnforceAccount'
+type MockEntityEnforcer_EnforceAccount_Call struct {
+ *mock.Call
+}
+
+// EnforceAccount is a helper method to define mock.On call
+// - ctx context.Context
+// - account Identifier
+// - action rbac.AccountAction
+func (_e *MockEntityEnforcer_Expecter) EnforceAccount(ctx interface{}, account interface{}, action interface{}) *MockEntityEnforcer_EnforceAccount_Call {
+ return &MockEntityEnforcer_EnforceAccount_Call{Call: _e.mock.On("EnforceAccount", ctx, account, action)}
+}
+
+func (_c *MockEntityEnforcer_EnforceAccount_Call) Run(run func(ctx context.Context, account Identifier, action rbac.AccountAction)) *MockEntityEnforcer_EnforceAccount_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(Identifier), args[2].(rbac.AccountAction))
+ })
+ return _c
+}
+
+func (_c *MockEntityEnforcer_EnforceAccount_Call) Return(_a0 error) *MockEntityEnforcer_EnforceAccount_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *MockEntityEnforcer_EnforceAccount_Call) RunAndReturn(run func(context.Context, Identifier, rbac.AccountAction) error) *MockEntityEnforcer_EnforceAccount_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// EnforceConnection provides a mock function with given fields: ctx, connection, action
+func (_m *MockEntityEnforcer) EnforceConnection(ctx context.Context, connection DomainEntity, action rbac.ConnectionAction) error {
+ ret := _m.Called(ctx, connection, action)
+
+ if len(ret) == 0 {
+ panic("no return value specified for EnforceConnection")
+ }
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(context.Context, DomainEntity, rbac.ConnectionAction) error); ok {
+ r0 = rf(ctx, connection, action)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// MockEntityEnforcer_EnforceConnection_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnforceConnection'
+type MockEntityEnforcer_EnforceConnection_Call struct {
+ *mock.Call
+}
+
+// EnforceConnection is a helper method to define mock.On call
+// - ctx context.Context
+// - connection DomainEntity
+// - action rbac.ConnectionAction
+func (_e *MockEntityEnforcer_Expecter) EnforceConnection(ctx interface{}, connection interface{}, action interface{}) *MockEntityEnforcer_EnforceConnection_Call {
+ return &MockEntityEnforcer_EnforceConnection_Call{Call: _e.mock.On("EnforceConnection", ctx, connection, action)}
+}
+
+func (_c *MockEntityEnforcer_EnforceConnection_Call) Run(run func(ctx context.Context, connection DomainEntity, action rbac.ConnectionAction)) *MockEntityEnforcer_EnforceConnection_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(DomainEntity), args[2].(rbac.ConnectionAction))
+ })
+ return _c
+}
+
+func (_c *MockEntityEnforcer_EnforceConnection_Call) Return(_a0 error) *MockEntityEnforcer_EnforceConnection_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *MockEntityEnforcer_EnforceConnection_Call) RunAndReturn(run func(context.Context, DomainEntity, rbac.ConnectionAction) error) *MockEntityEnforcer_EnforceConnection_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// EnforceJob provides a mock function with given fields: ctx, job, action
+func (_m *MockEntityEnforcer) EnforceJob(ctx context.Context, job DomainEntity, action rbac.JobAction) error {
+ ret := _m.Called(ctx, job, action)
+
+ if len(ret) == 0 {
+ panic("no return value specified for EnforceJob")
+ }
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(context.Context, DomainEntity, rbac.JobAction) error); ok {
+ r0 = rf(ctx, job, action)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// MockEntityEnforcer_EnforceJob_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'EnforceJob'
+type MockEntityEnforcer_EnforceJob_Call struct {
+ *mock.Call
+}
+
+// EnforceJob is a helper method to define mock.On call
+// - ctx context.Context
+// - job DomainEntity
+// - action rbac.JobAction
+func (_e *MockEntityEnforcer_Expecter) EnforceJob(ctx interface{}, job interface{}, action interface{}) *MockEntityEnforcer_EnforceJob_Call {
+ return &MockEntityEnforcer_EnforceJob_Call{Call: _e.mock.On("EnforceJob", ctx, job, action)}
+}
+
+func (_c *MockEntityEnforcer_EnforceJob_Call) Run(run func(ctx context.Context, job DomainEntity, action rbac.JobAction)) *MockEntityEnforcer_EnforceJob_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context), args[1].(DomainEntity), args[2].(rbac.JobAction))
+ })
+ return _c
+}
+
+func (_c *MockEntityEnforcer_EnforceJob_Call) Return(_a0 error) *MockEntityEnforcer_EnforceJob_Call {
+ _c.Call.Return(_a0)
+ return _c
+}
+
+func (_c *MockEntityEnforcer_EnforceJob_Call) RunAndReturn(run func(context.Context, DomainEntity, rbac.JobAction) error) *MockEntityEnforcer_EnforceJob_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// NewMockEntityEnforcer creates a new instance of MockEntityEnforcer. 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 NewMockEntityEnforcer(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *MockEntityEnforcer {
+ mock := &MockEntityEnforcer{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/backend/internal/userdata/mock_Interface.go b/backend/internal/userdata/mock_Interface.go
new file mode 100644
index 0000000000..85af7fb3c0
--- /dev/null
+++ b/backend/internal/userdata/mock_Interface.go
@@ -0,0 +1,94 @@
+// Code generated by mockery. DO NOT EDIT.
+
+package userdata
+
+import (
+ context "context"
+
+ mock "github.com/stretchr/testify/mock"
+)
+
+// MockInterface is an autogenerated mock type for the Interface type
+type MockInterface struct {
+ mock.Mock
+}
+
+type MockInterface_Expecter struct {
+ mock *mock.Mock
+}
+
+func (_m *MockInterface) EXPECT() *MockInterface_Expecter {
+ return &MockInterface_Expecter{mock: &_m.Mock}
+}
+
+// GetUser provides a mock function with given fields: ctx
+func (_m *MockInterface) GetUser(ctx context.Context) (*User, error) {
+ ret := _m.Called(ctx)
+
+ if len(ret) == 0 {
+ panic("no return value specified for GetUser")
+ }
+
+ var r0 *User
+ var r1 error
+ if rf, ok := ret.Get(0).(func(context.Context) (*User, error)); ok {
+ return rf(ctx)
+ }
+ if rf, ok := ret.Get(0).(func(context.Context) *User); ok {
+ r0 = rf(ctx)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*User)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(context.Context) error); ok {
+ r1 = rf(ctx)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// MockInterface_GetUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetUser'
+type MockInterface_GetUser_Call struct {
+ *mock.Call
+}
+
+// GetUser is a helper method to define mock.On call
+// - ctx context.Context
+func (_e *MockInterface_Expecter) GetUser(ctx interface{}) *MockInterface_GetUser_Call {
+ return &MockInterface_GetUser_Call{Call: _e.mock.On("GetUser", ctx)}
+}
+
+func (_c *MockInterface_GetUser_Call) Run(run func(ctx context.Context)) *MockInterface_GetUser_Call {
+ _c.Call.Run(func(args mock.Arguments) {
+ run(args[0].(context.Context))
+ })
+ return _c
+}
+
+func (_c *MockInterface_GetUser_Call) Return(_a0 *User, _a1 error) *MockInterface_GetUser_Call {
+ _c.Call.Return(_a0, _a1)
+ return _c
+}
+
+func (_c *MockInterface_GetUser_Call) RunAndReturn(run func(context.Context) (*User, error)) *MockInterface_GetUser_Call {
+ _c.Call.Return(run)
+ return _c
+}
+
+// NewMockInterface creates a new instance of MockInterface. 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 NewMockInterface(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *MockInterface {
+ mock := &MockInterface{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/backend/internal/userdata/user.go b/backend/internal/userdata/user.go
new file mode 100644
index 0000000000..70fc1d157e
--- /dev/null
+++ b/backend/internal/userdata/user.go
@@ -0,0 +1,66 @@
+package userdata
+
+import (
+ "context"
+ "fmt"
+
+ "connectrpc.com/connect"
+ "github.com/jackc/pgx/v5/pgtype"
+ mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
+ "github.com/nucleuscloud/neosync/backend/internal/apikey"
+ auth_apikey "github.com/nucleuscloud/neosync/backend/internal/auth/apikey"
+ nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
+ "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+)
+
+type UserAccountServiceClient interface {
+ IsUserInAccount(ctx context.Context, req *connect.Request[mgmtv1alpha1.IsUserInAccountRequest]) (*connect.Response[mgmtv1alpha1.IsUserInAccountResponse], error)
+}
+
+type User struct {
+ id pgtype.UUID
+
+ apiKeyData *auth_apikey.TokenContextData // Optional because we may not have an api key
+
+ userAccountServiceClient UserAccountServiceClient
+
+ EntityEnforcer
+}
+
+func (u *User) Id() string {
+ return neosyncdb.UUIDString(u.id)
+}
+func (u *User) PgId() pgtype.UUID {
+ return u.id
+}
+
+func (u *User) IsWorkerApiKey() bool {
+ return u.apiKeyData != nil && u.apiKeyData.ApiKeyType == apikey.WorkerApiKey
+}
+
+func (u *User) IsApiKey() bool {
+ return u.apiKeyData != nil
+}
+
+func EnforceAccountAccess(ctx context.Context, user *User, accountId string) error {
+ if user.IsApiKey() {
+ if user.IsWorkerApiKey() {
+ return nil
+ }
+ // We first want to check to make sure the api key is valid and that it says it's in the account
+ // However, we still want to make a DB request to ensure the DB still says it's in the account
+ if user.apiKeyData.ApiKey == nil || neosyncdb.UUIDString(user.apiKeyData.ApiKey.AccountID) != accountId {
+ return nucleuserrors.NewForbidden("api key is not valid for account")
+ }
+ }
+
+ // Note: because we are calling to the user account service here, the ctx must still contain the user data
+ inAccountResp, err := user.userAccountServiceClient.IsUserInAccount(ctx, connect.NewRequest(&mgmtv1alpha1.IsUserInAccountRequest{AccountId: accountId}))
+ if err != nil {
+ return fmt.Errorf("unable to check if user is in account: %w", err)
+ }
+ if !inAccountResp.Msg.GetOk() {
+ return nucleuserrors.NewForbidden("user is not in account")
+ }
+ return nil
+}
diff --git a/backend/pkg/integration-test/integration-test.go b/backend/pkg/integration-test/integration-test.go
index 3970a92ca8..da3106f13b 100644
--- a/backend/pkg/integration-test/integration-test.go
+++ b/backend/pkg/integration-test/integration-test.go
@@ -2,11 +2,13 @@ package integrationtests_test
import (
"context"
+ "fmt"
"net/http"
"net/http/httptest"
"testing"
"connectrpc.com/connect"
+ "github.com/jackc/pgx/v5/stdlib"
db_queries "github.com/nucleuscloud/neosync/backend/gen/go/db"
mysql_queries "github.com/nucleuscloud/neosync/backend/gen/go/db/dbschemas/mysql"
pg_queries "github.com/nucleuscloud/neosync/backend/gen/go/db/dbschemas/postgresql"
@@ -18,9 +20,12 @@ import (
"github.com/nucleuscloud/neosync/backend/internal/authmgmt"
auth_interceptor "github.com/nucleuscloud/neosync/backend/internal/connect/interceptors/auth"
jobhooks "github.com/nucleuscloud/neosync/backend/internal/ee/hooks/jobs"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac/enforcer"
neosync_gcp "github.com/nucleuscloud/neosync/backend/internal/gcp"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
clientmanager "github.com/nucleuscloud/neosync/backend/internal/temporal/clientmanager"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
"github.com/nucleuscloud/neosync/backend/internal/utils"
"github.com/nucleuscloud/neosync/backend/pkg/mongoconnect"
"github.com/nucleuscloud/neosync/backend/pkg/sqlconnect"
@@ -150,6 +155,7 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
if err != nil {
return err
}
+
s.Pgcontainer = pgcontainer
s.NeosyncQuerier = db_queries.New()
s.systemQuerier = pg_queries.New()
@@ -168,6 +174,24 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
},
}
+ err = s.InitializeTest(ctx, t)
+ if err != nil {
+ return err
+ }
+
+ permissiveRbacClient := rbac.NewAllowAllClient()
+
+ rbacenforcer, err := enforcer.NewActiveEnforcer(ctx, stdlib.OpenDBFromPool(pgcontainer.DB), "neosync_api.casbin_rule")
+ if err != nil {
+ return fmt.Errorf("unable to create rbac enforcer: %w", err)
+ }
+ rbacenforcer.EnableAutoSave(true)
+ err = rbacenforcer.LoadPolicy()
+ if err != nil {
+ return fmt.Errorf("unable to load rbac policies: %w", err)
+ }
+ enforcedRbacClient := rbac.New(rbacenforcer)
+
maxAllowed := int64(10000)
unauthdUserService := v1alpha1_useraccountservice.New(
&v1alpha1_useraccountservice.Config{IsAuthEnabled: false, IsNeosyncCloud: false, DefaultMaxAllowedRecords: &maxAllowed},
@@ -175,8 +199,10 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
s.Mocks.TemporalConfigProvider,
s.Mocks.Authclient,
s.Mocks.Authmanagerclient,
- nil,
+ nil, // billing client
+ permissiveRbacClient, // rbac client
)
+ unauthdUserClient := userdata.NewClient(unauthdUserService, permissiveRbacClient)
authdUserService := v1alpha1_useraccountservice.New(
&v1alpha1_useraccountservice.Config{IsAuthEnabled: true, IsNeosyncCloud: false},
@@ -184,15 +210,18 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
s.Mocks.TemporalConfigProvider,
s.Mocks.Authclient,
s.Mocks.Authmanagerclient,
- nil,
+ nil, // billing client
+ enforcedRbacClient, // rbac client
)
sqlmanagerclient := NewTestSqlManagerClient()
+ authdUserDataClient := userdata.NewClient(authdUserService, enforcedRbacClient)
+
authdConnectionService := v1alpha1_connectionservice.New(
&v1alpha1_connectionservice.Config{},
neosyncdb.New(pgcontainer.DB, db_queries.New()),
- authdUserService,
+ authdUserDataClient,
mongoconnect.NewConnector(),
awsmanager.New(),
sqlmanagerclient,
@@ -206,10 +235,13 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
s.Mocks.Authclient,
s.Mocks.Authmanagerclient,
s.Mocks.Billingclient,
+ enforcedRbacClient, // rbac client
)
+ neoCloudUserDataClient := userdata.NewClient(neoCloudAuthdUserService, enforcedRbacClient)
neoCloudAuthdAnonymizeService := v1alpha_anonymizationservice.New(
&v1alpha_anonymizationservice.Config{IsAuthEnabled: true, IsNeosyncCloud: true, IsPresidioEnabled: false},
- nil,
+ nil, // meter
+ neoCloudUserDataClient,
neoCloudAuthdUserService,
s.Mocks.Presidio.Analyzer,
s.Mocks.Presidio.Anonymizer,
@@ -219,7 +251,7 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
neoCloudConnectionService := v1alpha1_connectionservice.New(
&v1alpha1_connectionservice.Config{},
neosyncdb.New(pgcontainer.DB, db_queries.New()),
- neoCloudAuthdUserService,
+ neoCloudUserDataClient,
mongoconnect.NewConnector(),
awsmanager.New(),
sqlmanagerclient,
@@ -227,7 +259,7 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
)
neoCloudJobHookService := jobhooks.New(
neosyncdb.New(pgcontainer.DB, db_queries.New()),
- neoCloudAuthdUserService,
+ neoCloudUserDataClient,
jobhooks.WithEnabled(),
)
neoCloudJobService := v1alpha1_jobservice.New(
@@ -235,9 +267,9 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
neosyncdb.New(pgcontainer.DB, db_queries.New()),
s.Mocks.TemporalClientManager,
neoCloudConnectionService,
- neoCloudAuthdUserService,
sqlmanagerclient,
neoCloudJobHookService,
+ neoCloudUserDataClient,
)
unauthdTransformersService := v1alpha1_transformersservice.New(
@@ -246,23 +278,23 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
IsNeosyncCloud: false,
},
neosyncdb.New(pgcontainer.DB, db_queries.New()),
- unauthdUserService,
s.Mocks.Presidio.Entities,
+ unauthdUserClient,
)
unauthdConnectionsService := v1alpha1_connectionservice.New(
&v1alpha1_connectionservice.Config{},
neosyncdb.New(pgcontainer.DB, db_queries.New()),
- unauthdUserService,
+ unauthdUserClient,
mongoconnect.NewConnector(),
awsmanager.New(),
sqlmanagerclient,
&sqlconnect.SqlOpenConnector{},
)
- jobhookService := jobhooks.New(
+ unAuthdjobhookService := jobhooks.New(
neosyncdb.New(pgcontainer.DB, db_queries.New()),
- unauthdUserService,
+ unauthdUserClient,
)
unauthdJobsService := v1alpha1_jobservice.New(
@@ -270,14 +302,13 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
neosyncdb.New(pgcontainer.DB, db_queries.New()),
s.Mocks.TemporalClientManager,
unauthdConnectionsService,
- unauthdUserService,
sqlmanagerclient,
- jobhookService,
+ unAuthdjobhookService,
+ unauthdUserClient,
)
unauthdConnectionDataService := v1alpha1_connectiondataservice.New(
&v1alpha1_connectiondataservice.Config{},
- unauthdUserService,
unauthdConnectionsService,
unauthdJobsService,
awsmanager.New(),
@@ -294,7 +325,8 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
unauthdAnonymizationService := v1alpha_anonymizationservice.New(
&v1alpha_anonymizationservice.Config{IsPresidioEnabled: false},
- nil,
+ nil, // meter
+ unauthdUserClient,
unauthdUserService,
presAnalyzeClient, presAnonClient,
neosyncdb.New(pgcontainer.DB, db_queries.New()),
@@ -392,11 +424,6 @@ func (s *NeosyncApiTestClient) Setup(ctx context.Context, t testing.TB) error {
httpsrv: s.httpsrv,
basepath: "/ncauth",
}
-
- err = s.InitializeTest(ctx, t)
- if err != nil {
- return err
- }
return nil
}
diff --git a/backend/protos/mgmt/v1alpha1/user_account.proto b/backend/protos/mgmt/v1alpha1/user_account.proto
index 8ce64f9b4d..7f18dd5c93 100644
--- a/backend/protos/mgmt/v1alpha1/user_account.proto
+++ b/backend/protos/mgmt/v1alpha1/user_account.proto
@@ -308,6 +308,30 @@ message SetBillingMeterEventRequest {
message SetBillingMeterEventResponse {}
+message SetUserRoleRequest {
+ // The account id to apply this role to
+ string account_id = 1 [(buf.validate.field).string.uuid = true];
+ // The user that this will be applied to
+ string user_id = 2 [(buf.validate.field).string.uuid = true];
+ // The role that this user will obtain
+ AccountRole role = 3;
+}
+
+enum AccountRole {
+ // Default value, this should not be used, but will default to ACCOUNT_ROLE_JOB_VIEWER
+ ACCOUNT_ROLE_UNSPECIFIED = 0;
+ // Admin, can do anything in the account.
+ ACCOUNT_ROLE_ADMIN = 1;
+ // Can view, edit jobs and connections
+ ACCOUNT_ROLE_JOB_DEVELOPER = 2;
+ // Can view
+ ACCOUNT_ROLE_JOB_VIEWER = 3;
+ // Can view and execute
+ ACCOUNT_ROLE_JOB_EXECUTOR = 4;
+}
+
+message SetUserRoleResponse {}
+
service UserAccountService {
rpc GetUser(GetUserRequest) returns (GetUserResponse) {}
rpc SetUser(SetUserRequest) returns (SetUserResponse) {}
@@ -363,4 +387,7 @@ service UserAccountService {
// Sends a new metered event to the billing system
rpc SetBillingMeterEvent(SetBillingMeterEventRequest) returns (SetBillingMeterEventResponse) {}
+
+ // Sets the users role
+ rpc SetUserRole(SetUserRoleRequest) returns (SetUserRoleResponse) {}
}
diff --git a/backend/services/mgmt/v1alpha1/anonymization-service/anonymization.go b/backend/services/mgmt/v1alpha1/anonymization-service/anonymization.go
index 5f46f5a2f9..3765c362c0 100644
--- a/backend/services/mgmt/v1alpha1/anonymization-service/anonymization.go
+++ b/backend/services/mgmt/v1alpha1/anonymization-service/anonymization.go
@@ -12,6 +12,7 @@ import (
"github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
"github.com/nucleuscloud/neosync/backend/pkg/metrics"
jsonanonymizer "github.com/nucleuscloud/neosync/internal/json-anonymizer"
"go.opentelemetry.io/otel/attribute"
@@ -35,12 +36,21 @@ func (s *Service) AnonymizeMany(
)
}
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = userdata.EnforceAccountAccess(ctx, user, req.Msg.GetAccountId())
+ if err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
- account, err := s.db.Q.GetAccount(ctx, s.db.Db, *accountUuid)
+ account, err := s.db.Q.GetAccount(ctx, s.db.Db, accountUuid)
if err != nil {
return nil, err
}
@@ -52,7 +62,7 @@ func (s *Service) AnonymizeMany(
requestedCount := uint64(len(req.Msg.InputData))
resp, err := s.useraccountService.IsAccountStatusValid(ctx, connect.NewRequest(&mgmtv1alpha1.IsAccountStatusValidRequest{
- AccountId: neosyncdb.UUIDString(*accountUuid),
+ AccountId: req.Msg.GetAccountId(),
RequestedRecordCount: &requestedCount,
}))
if err != nil {
@@ -76,7 +86,7 @@ func (s *Service) AnonymizeMany(
var outputErrorCounter, outputCounter metric.Int64Counter
var labels []attribute.KeyValue
if s.meter != nil {
- labels = getMetricLabels(ctx, "anonymizeMany", neosyncdb.UUIDString(*accountUuid))
+ labels = getMetricLabels(ctx, "anonymizeMany", req.Msg.GetAccountId())
counter, err := s.meter.Int64Counter(inputMetricStr)
if err != nil {
return nil, err
@@ -126,12 +136,21 @@ func (s *Service) AnonymizeSingle(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.AnonymizeSingleRequest],
) (*connect.Response[mgmtv1alpha1.AnonymizeSingleResponse], error) {
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = userdata.EnforceAccountAccess(ctx, user, req.Msg.GetAccountId())
+ if err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
- account, err := s.db.Q.GetAccount(ctx, s.db.Db, *accountUuid)
+ account, err := s.db.Q.GetAccount(ctx, s.db.Db, accountUuid)
if err != nil {
return nil, err
}
@@ -151,7 +170,7 @@ func (s *Service) AnonymizeSingle(
requestedCount := uint64(len(req.Msg.InputData))
resp, err := s.useraccountService.IsAccountStatusValid(ctx, connect.NewRequest(&mgmtv1alpha1.IsAccountStatusValidRequest{
- AccountId: neosyncdb.UUIDString(*accountUuid),
+ AccountId: req.Msg.GetAccountId(),
RequestedRecordCount: &requestedCount,
}))
if err != nil {
@@ -174,7 +193,7 @@ func (s *Service) AnonymizeSingle(
var outputCounter, outputErrorCounter metric.Int64Counter
var labels []attribute.KeyValue
if s.meter != nil {
- labels = getMetricLabels(ctx, "anonymizeSingle", neosyncdb.UUIDString(*accountUuid))
+ labels = getMetricLabels(ctx, "anonymizeSingle", req.Msg.GetAccountId())
counter, err := s.meter.Int64Counter(inputMetricStr)
if err != nil {
return nil, err
diff --git a/backend/services/mgmt/v1alpha1/anonymization-service/service.go b/backend/services/mgmt/v1alpha1/anonymization-service/service.go
index 61648d91aa..adf2aca39c 100644
--- a/backend/services/mgmt/v1alpha1/anonymization-service/service.go
+++ b/backend/services/mgmt/v1alpha1/anonymization-service/service.go
@@ -3,6 +3,7 @@ package v1alpha_anonymizationservice
import (
"github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
presidioapi "github.com/nucleuscloud/neosync/internal/ee/presidio"
"go.opentelemetry.io/otel/metric"
)
@@ -10,6 +11,7 @@ import (
type Service struct {
cfg *Config
meter metric.Meter // optional
+ userdataclient userdata.Interface
useraccountService mgmtv1alpha1connect.UserAccountServiceClient
analyze presidioapi.AnalyzeInterface
anonymize presidioapi.AnonymizeInterface
@@ -26,6 +28,7 @@ type Config struct {
func New(
cfg *Config,
meter metric.Meter,
+ userdataclient userdata.Interface,
useraccountService mgmtv1alpha1connect.UserAccountServiceClient,
analyzeclient presidioapi.AnalyzeInterface,
anonymizeclient presidioapi.AnonymizeInterface,
@@ -34,6 +37,7 @@ func New(
return &Service{
cfg: cfg,
meter: meter,
+ userdataclient: userdataclient,
useraccountService: useraccountService,
analyze: analyzeclient,
anonymize: anonymizeclient,
diff --git a/backend/services/mgmt/v1alpha1/anonymization-service/user-account.go b/backend/services/mgmt/v1alpha1/anonymization-service/user-account.go
deleted file mode 100644
index 49cf33902d..0000000000
--- a/backend/services/mgmt/v1alpha1/anonymization-service/user-account.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package v1alpha_anonymizationservice
-
-import (
- "context"
-
- "connectrpc.com/connect"
- "github.com/jackc/pgx/v5/pgtype"
- mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
- "github.com/nucleuscloud/neosync/backend/internal/apikey"
- auth_apikey "github.com/nucleuscloud/neosync/backend/internal/auth/apikey"
- nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
- "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
-)
-
-func (s *Service) verifyUserInAccount(
- ctx context.Context,
- accountId string,
-) (*pgtype.UUID, error) {
- accountUuid, err := neosyncdb.ToUuid(accountId)
- if err != nil {
- return nil, err
- }
-
- if isWorkerApiKey(ctx) {
- return &accountUuid, nil
- }
-
- resp, err := s.useraccountService.IsUserInAccount(ctx, connect.NewRequest(&mgmtv1alpha1.IsUserInAccountRequest{AccountId: accountId}))
- if err != nil {
- return nil, err
- }
- if !resp.Msg.Ok {
- return nil, nucleuserrors.NewForbidden("user in not in requested account")
- }
-
- return &accountUuid, nil
-}
-
-func isWorkerApiKey(ctx context.Context) bool {
- data, err := auth_apikey.GetTokenDataFromCtx(ctx)
- if err != nil {
- return false
- }
- return data.ApiKeyType == apikey.WorkerApiKey
-}
diff --git a/backend/services/mgmt/v1alpha1/api-key-service/api-keys.go b/backend/services/mgmt/v1alpha1/api-key-service/api-keys.go
index 5f287ee1c9..03611c3a01 100644
--- a/backend/services/mgmt/v1alpha1/api-key-service/api-keys.go
+++ b/backend/services/mgmt/v1alpha1/api-key-service/api-keys.go
@@ -8,8 +8,10 @@ import (
mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
"github.com/nucleuscloud/neosync/backend/internal/apikey"
"github.com/nucleuscloud/neosync/backend/internal/dtomaps"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
pkg_utils "github.com/nucleuscloud/neosync/backend/pkg/utils"
)
@@ -17,12 +19,21 @@ func (s *Service) GetAccountApiKeys(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.GetAccountApiKeysRequest],
) (*connect.Response[mgmtv1alpha1.GetAccountApiKeysResponse], error) {
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- apiKeys, err := s.db.Q.GetAccountApiKeys(ctx, s.db.Db, *accountUuid)
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_View); err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
+ if err != nil {
+ return nil, err
+ }
+
+ apiKeys, err := s.db.Q.GetAccountApiKeys(ctx, s.db.Db, accountUuid)
if err != nil {
return nil, err
}
@@ -42,7 +53,7 @@ func (s *Service) GetAccountApiKey(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.GetAccountApiKeyRequest],
) (*connect.Response[mgmtv1alpha1.GetAccountApiKeyResponse], error) {
- apiKeyUuid, err := neosyncdb.ToUuid(req.Msg.Id)
+ apiKeyUuid, err := neosyncdb.ToUuid(req.Msg.GetId())
if err != nil {
return nil, err
}
@@ -54,10 +65,13 @@ func (s *Service) GetAccountApiKey(
return nil, nucleuserrors.NewNotFound("unable to find api key")
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(apiKey.AccountID))
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(neosyncdb.UUIDString(apiKey.AccountID)), rbac.AccountAction_View); err != nil {
+ return nil, err
+ }
return connect.NewResponse(&mgmtv1alpha1.GetAccountApiKeyResponse{
ApiKey: dtomaps.ToAccountApiKeyDto(&apiKey, nil),
@@ -68,16 +82,25 @@ func (s *Service) CreateAccountApiKey(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.CreateAccountApiKeyRequest],
) (*connect.Response[mgmtv1alpha1.CreateAccountApiKeyResponse], error) {
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- userUuid, err := s.getUserUuid(ctx)
+
+ if user.IsApiKey() {
+ return nil, nucleuserrors.NewUnauthorized("api key user cannot create api keys")
+ }
+
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_Edit); err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
- expiresAt, err := neosyncdb.ToTimestamp(req.Msg.ExpiresAt.AsTime())
+ expiresAt, err := neosyncdb.ToTimestamp(req.Msg.GetExpiresAt().AsTime())
if err != nil {
return nil, err
}
@@ -90,8 +113,8 @@ func (s *Service) CreateAccountApiKey(
newApiKey, err := s.db.CreateAccountApikey(ctx, &neosyncdb.CreateAccountApiKeyRequest{
KeyName: req.Msg.Name,
KeyValue: hashedKeyValue,
- AccountUuid: *accountUuid,
- CreatedByUserUuid: *userUuid,
+ AccountUuid: accountUuid,
+ CreatedByUserUuid: user.PgId(),
ExpiresAt: expiresAt,
})
if err != nil {
@@ -106,7 +129,7 @@ func (s *Service) RegenerateAccountApiKey(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.RegenerateAccountApiKeyRequest],
) (*connect.Response[mgmtv1alpha1.RegenerateAccountApiKeyResponse], error) {
- apiKeyUuid, err := neosyncdb.ToUuid(req.Msg.Id)
+ apiKeyUuid, err := neosyncdb.ToUuid(req.Msg.GetId())
if err != nil {
return nil, err
}
@@ -118,26 +141,31 @@ func (s *Service) RegenerateAccountApiKey(
return nil, nucleuserrors.NewNotFound("account api key not found")
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(apiKey.AccountID))
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- userUuid, err := s.getUserUuid(ctx)
- if err != nil {
+
+ if user.IsApiKey() {
+ return nil, nucleuserrors.NewUnauthorized("api key user cannot regenerate api keys")
+ }
+
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(neosyncdb.UUIDString(apiKey.AccountID)), rbac.AccountAction_Edit); err != nil {
return nil, err
}
+
clearKeyValue := apikey.NewV1AccountKey()
hashedKeyValue := pkg_utils.ToSha256(
clearKeyValue,
)
- expiresAt, err := neosyncdb.ToTimestamp(req.Msg.ExpiresAt.AsTime())
+ expiresAt, err := neosyncdb.ToTimestamp(req.Msg.GetExpiresAt().AsTime())
if err != nil {
return nil, err
}
updatedApiKey, err := s.db.Q.UpdateAccountApiKeyValue(ctx, s.db.Db, db_queries.UpdateAccountApiKeyValueParams{
KeyValue: hashedKeyValue,
ExpiresAt: expiresAt,
- UpdatedByID: *userUuid,
+ UpdatedByID: user.PgId(),
ID: apiKeyUuid,
})
if err != nil {
@@ -152,10 +180,11 @@ func (s *Service) DeleteAccountApiKey(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.DeleteAccountApiKeyRequest],
) (*connect.Response[mgmtv1alpha1.DeleteAccountApiKeyResponse], error) {
- apiKeyUuid, err := neosyncdb.ToUuid(req.Msg.Id)
+ apiKeyUuid, err := neosyncdb.ToUuid(req.Msg.GetId())
if err != nil {
return nil, err
}
+
apiKey, err := s.db.Q.GetAccountApiKeyById(ctx, s.db.Db, apiKeyUuid)
if err != nil && !neosyncdb.IsNoRows(err) {
return nil, err
@@ -163,10 +192,16 @@ func (s *Service) DeleteAccountApiKey(
return connect.NewResponse(&mgmtv1alpha1.DeleteAccountApiKeyResponse{}), nil
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(apiKey.AccountID))
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
+ if user.IsApiKey() {
+ return nil, nucleuserrors.NewUnauthorized("api key user cannot delete api keys")
+ }
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(neosyncdb.UUIDString(apiKey.AccountID)), rbac.AccountAction_Edit); err != nil {
+ return nil, err
+ }
err = s.db.Q.RemoveAccountApiKey(ctx, s.db.Db, apiKeyUuid)
if err != nil && !neosyncdb.IsNoRows(err) {
diff --git a/backend/services/mgmt/v1alpha1/api-key-service/api-keys_test.go b/backend/services/mgmt/v1alpha1/api-key-service/api-keys_test.go
index 87ab6007da..a6fc4e0961 100644
--- a/backend/services/mgmt/v1alpha1/api-key-service/api-keys_test.go
+++ b/backend/services/mgmt/v1alpha1/api-key-service/api-keys_test.go
@@ -2,6 +2,7 @@ package v1alpha1_apikeyservice
import (
"context"
+ "errors"
"testing"
"time"
@@ -11,8 +12,8 @@ import (
"github.com/jackc/pgx/v5/pgtype"
db_queries "github.com/nucleuscloud/neosync/backend/gen/go/db"
mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
- "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
pgxmock "github.com/nucleuscloud/neosync/internal/mocks/github.com/jackc/pgx/v5"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
@@ -22,9 +23,9 @@ import (
func Test_Service_GetAccountApiKeys(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
rawData := []db_queries.NeosyncApiAccountApiKey{
{
@@ -52,7 +53,7 @@ func Test_Service_GetAccountApiKeys(t *testing.T) {
}
mockQuerier.On("GetAccountApiKeys", mock.Anything, mock.Anything, mock.Anything).
Return(rawData, nil)
- mockIsUserInAccount(mockUserAccountService, true)
+ mockIsUserInAccount(t, mockUserService, true)
resp, err := svc.GetAccountApiKeys(context.Background(), connect.NewRequest(&mgmtv1alpha1.GetAccountApiKeysRequest{
AccountId: uuid.NewString(),
@@ -76,11 +77,11 @@ func Test_Service_GetAccountApiKeys(t *testing.T) {
func Test_Service_GetAccountApiKeys_ForbiddenAccount(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
- mockIsUserInAccount(mockUserAccountService, false)
+ mockIsUserInAccount(t, mockUserService, false)
resp, err := svc.GetAccountApiKeys(context.Background(), connect.NewRequest(&mgmtv1alpha1.GetAccountApiKeysRequest{
AccountId: uuid.NewString(),
@@ -92,9 +93,9 @@ func Test_Service_GetAccountApiKeys_ForbiddenAccount(t *testing.T) {
func Test_Service_GetAccountApiKey_Found(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
rawData := db_queries.NeosyncApiAccountApiKey{
ID: newPgUuid(t),
@@ -109,7 +110,7 @@ func Test_Service_GetAccountApiKey_Found(t *testing.T) {
}
mockQuerier.On("GetAccountApiKeyById", mock.Anything, mock.Anything, mock.Anything).
Return(rawData, nil)
- mockIsUserInAccount(mockUserAccountService, true)
+ mockIsUserInAccount(t, mockUserService, true)
resp, err := svc.GetAccountApiKey(context.Background(), connect.NewRequest(&mgmtv1alpha1.GetAccountApiKeyRequest{
Id: uuid.NewString(),
@@ -123,9 +124,9 @@ func Test_Service_GetAccountApiKey_Found(t *testing.T) {
func Test_Service_GetAccountApiKey_NotFound(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
mockQuerier.On("GetAccountApiKeyById", mock.Anything, mock.Anything, mock.Anything).
Return(db_queries.NeosyncApiAccountApiKey{}, pgx.ErrNoRows)
@@ -140,9 +141,9 @@ func Test_Service_GetAccountApiKey_NotFound(t *testing.T) {
func Test_Service_GetAccountApiKey_Found_ForbiddenAccount(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
rawData := db_queries.NeosyncApiAccountApiKey{
ID: newPgUuid(t),
@@ -157,7 +158,7 @@ func Test_Service_GetAccountApiKey_Found_ForbiddenAccount(t *testing.T) {
}
mockQuerier.On("GetAccountApiKeyById", mock.Anything, mock.Anything, mock.Anything).
Return(rawData, nil)
- mockIsUserInAccount(mockUserAccountService, false)
+ mockIsUserInAccount(t, mockUserService, false)
resp, err := svc.GetAccountApiKey(context.Background(), connect.NewRequest(&mgmtv1alpha1.GetAccountApiKeyRequest{
Id: uuid.NewString(),
@@ -170,12 +171,12 @@ func Test_Service_CreateAccountApiKey(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
mockTx := pgxmock.NewMockTx(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
+
+ mockIsUserInAccount(t, mockUserService, true)
- mockIsUserInAccount(mockUserAccountService, true)
- mockUserAccountCalls(mockUserAccountService, true, uuid.NewString())
mockDbtx.On("Begin", mock.Anything).Return(mockTx, nil)
mockTx.On("Commit", mock.Anything).Return(nil)
mockTx.On("Rollback", mock.Anything).Return(nil)
@@ -214,12 +215,12 @@ func Test_Service_CreateAccountApiKey(t *testing.T) {
func Test_Service_RegenerateAccountApiKey(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
+
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ mockIsUserInAccount(t, mockUserService, true)
- mockIsUserInAccount(mockUserAccountService, true)
- mockUserAccountCalls(mockUserAccountService, true, uuid.NewString())
rawData := db_queries.NeosyncApiAccountApiKey{
ID: newPgUuid(t),
AccountID: newPgUuid(t),
@@ -249,11 +250,11 @@ func Test_Service_RegenerateAccountApiKey(t *testing.T) {
func Test_Service_RegenerateAccountApiKey_ForbiddenAccount(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
- mockIsUserInAccount(mockUserAccountService, false)
+ mockIsUserInAccount(t, mockUserService, false)
rawData := db_queries.NeosyncApiAccountApiKey{
ID: newPgUuid(t),
AccountID: newPgUuid(t),
@@ -279,9 +280,9 @@ func Test_Service_RegenerateAccountApiKey_ForbiddenAccount(t *testing.T) {
func Test_Service_RegenerateAccountApiKey_NotFound(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
mockQuerier.On("GetAccountApiKeyById", mock.Anything, mock.Anything, mock.Anything).
Return(db_queries.NeosyncApiAccountApiKey{}, pgx.ErrNoRows)
@@ -297,11 +298,11 @@ func Test_Service_RegenerateAccountApiKey_NotFound(t *testing.T) {
func Test_Service_CreateAccountApiKey_ForbiddenAccount(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
- mockIsUserInAccount(mockUserAccountService, false)
+ mockIsUserInAccount(t, mockUserService, false)
resp, err := svc.CreateAccountApiKey(context.Background(), connect.NewRequest(&mgmtv1alpha1.CreateAccountApiKeyRequest{
AccountId: uuid.NewString(),
@@ -315,9 +316,9 @@ func Test_Service_CreateAccountApiKey_ForbiddenAccount(t *testing.T) {
func Test_Service_DeleteAccountApiKey_Existing(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
rawData := db_queries.NeosyncApiAccountApiKey{
ID: newPgUuid(t),
@@ -332,7 +333,7 @@ func Test_Service_DeleteAccountApiKey_Existing(t *testing.T) {
}
mockQuerier.On("GetAccountApiKeyById", mock.Anything, mock.Anything, mock.Anything).
Return(rawData, nil)
- mockIsUserInAccount(mockUserAccountService, true)
+ mockIsUserInAccount(t, mockUserService, true)
mockQuerier.On("RemoveAccountApiKey", mock.Anything, mock.Anything, mock.Anything).Return(nil)
resp, err := svc.DeleteAccountApiKey(context.Background(), connect.NewRequest(&mgmtv1alpha1.DeleteAccountApiKeyRequest{
@@ -345,9 +346,9 @@ func Test_Service_DeleteAccountApiKey_Existing(t *testing.T) {
func Test_Service_DeleteAccountApiKey_Existing_ForbiddenAccount(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
rawData := db_queries.NeosyncApiAccountApiKey{
ID: newPgUuid(t),
@@ -362,7 +363,7 @@ func Test_Service_DeleteAccountApiKey_Existing_ForbiddenAccount(t *testing.T) {
}
mockQuerier.On("GetAccountApiKeyById", mock.Anything, mock.Anything, mock.Anything).
Return(rawData, nil)
- mockIsUserInAccount(mockUserAccountService, false)
+ mockIsUserInAccount(t, mockUserService, false)
resp, err := svc.DeleteAccountApiKey(context.Background(), connect.NewRequest(&mgmtv1alpha1.DeleteAccountApiKeyRequest{
Id: uuid.NewString(),
@@ -374,9 +375,9 @@ func Test_Service_DeleteAccountApiKey_Existing_ForbiddenAccount(t *testing.T) {
func Test_Service_DeleteAccountApiKey_NotFound(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
mockQuerier.On("GetAccountApiKeyById", mock.Anything, mock.Anything, mock.Anything).
Return(db_queries.NeosyncApiAccountApiKey{}, pgx.ErrNoRows)
@@ -391,9 +392,9 @@ func Test_Service_DeleteAccountApiKey_NotFound(t *testing.T) {
func Test_Service_DeleteAccountApiKey_Existing_DeleteRace(t *testing.T) {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
- svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserAccountService)
+ svc := New(&Config{}, neosyncdb.New(mockDbtx, mockQuerier), mockUserService)
rawData := db_queries.NeosyncApiAccountApiKey{
ID: newPgUuid(t),
@@ -408,7 +409,7 @@ func Test_Service_DeleteAccountApiKey_Existing_DeleteRace(t *testing.T) {
}
mockQuerier.On("GetAccountApiKeyById", mock.Anything, mock.Anything, mock.Anything).
Return(rawData, nil)
- mockIsUserInAccount(mockUserAccountService, true)
+ mockIsUserInAccount(t, mockUserService, true)
mockQuerier.On("RemoveAccountApiKey", mock.Anything, mock.Anything, mock.Anything).Return(pgx.ErrNoRows)
resp, err := svc.DeleteAccountApiKey(context.Background(), connect.NewRequest(&mgmtv1alpha1.DeleteAccountApiKeyRequest{
@@ -426,19 +427,14 @@ func newPgUuid(t *testing.T) pgtype.UUID {
return val
}
-func mockIsUserInAccount(userAccountServiceMock *mgmtv1alpha1connect.MockUserAccountServiceClient, isInAccount bool) {
- userAccountServiceMock.On("IsUserInAccount", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.IsUserInAccountResponse{
- Ok: isInAccount,
- }), nil)
-}
-
-func mockUserAccountCalls(
- userAccountServiceMock *mgmtv1alpha1connect.MockUserAccountServiceClient,
- isInAccount bool,
- userId string,
-) {
- mockIsUserInAccount(userAccountServiceMock, isInAccount)
- userAccountServiceMock.On("GetUser", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetUserResponse{
- UserId: userId,
- }), nil)
+func mockIsUserInAccount(t testing.TB, userServiceMock *userdata.MockInterface, isInAccount bool) {
+ mockEntityEnforcer := userdata.NewMockEntityEnforcer(t)
+ if isInAccount {
+ mockEntityEnforcer.On("EnforceAccount", mock.Anything, mock.Anything, mock.Anything).Once().Return(nil)
+ } else {
+ mockEntityEnforcer.On("EnforceAccount", mock.Anything, mock.Anything, mock.Anything).Once().Return(errors.New("test: not in account"))
+ }
+ userServiceMock.On("GetUser", mock.Anything).Once().Return(&userdata.User{
+ EntityEnforcer: mockEntityEnforcer,
+ }, nil)
}
diff --git a/backend/services/mgmt/v1alpha1/api-key-service/service.go b/backend/services/mgmt/v1alpha1/api-key-service/service.go
index d6e0aaff9c..25c0b35b96 100644
--- a/backend/services/mgmt/v1alpha1/api-key-service/service.go
+++ b/backend/services/mgmt/v1alpha1/api-key-service/service.go
@@ -1,14 +1,14 @@
package v1alpha1_apikeyservice
import (
- "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
)
type Service struct {
- cfg *Config
- db *neosyncdb.NeosyncDb
- useraccountService mgmtv1alpha1connect.UserAccountServiceClient
+ cfg *Config
+ db *neosyncdb.NeosyncDb
+ userdataclient userdata.Interface
}
type Config struct {
@@ -18,7 +18,7 @@ type Config struct {
func New(
cfg *Config,
db *neosyncdb.NeosyncDb,
- useraccountService mgmtv1alpha1connect.UserAccountServiceClient,
+ userdataclient userdata.Interface,
) *Service {
- return &Service{cfg: cfg, db: db, useraccountService: useraccountService}
+ return &Service{cfg: cfg, db: db, userdataclient: userdataclient}
}
diff --git a/backend/services/mgmt/v1alpha1/api-key-service/user-account.go b/backend/services/mgmt/v1alpha1/api-key-service/user-account.go
deleted file mode 100644
index f0445e8dbc..0000000000
--- a/backend/services/mgmt/v1alpha1/api-key-service/user-account.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package v1alpha1_apikeyservice
-
-import (
- "context"
-
- "connectrpc.com/connect"
- "github.com/jackc/pgx/v5/pgtype"
- mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
- nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
- "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
-)
-
-func (s *Service) verifyUserInAccount(
- ctx context.Context,
- accountId string,
-) (*pgtype.UUID, error) {
- resp, err := s.useraccountService.IsUserInAccount(ctx, connect.NewRequest(&mgmtv1alpha1.IsUserInAccountRequest{AccountId: accountId}))
- if err != nil {
- return nil, err
- }
- if !resp.Msg.Ok {
- return nil, nucleuserrors.NewForbidden("user in not in requested account")
- }
-
- accountUuid, err := neosyncdb.ToUuid(accountId)
- if err != nil {
- return nil, err
- }
- return &accountUuid, nil
-}
-
-func (s *Service) getUserUuid(
- ctx context.Context,
-) (*pgtype.UUID, error) {
- user, err := s.useraccountService.GetUser(ctx, connect.NewRequest(&mgmtv1alpha1.GetUserRequest{}))
- if err != nil {
- return nil, err
- }
- userUuid, err := neosyncdb.ToUuid(user.Msg.UserId)
- if err != nil {
- return nil, err
- }
- return &userUuid, nil
-}
diff --git a/backend/services/mgmt/v1alpha1/connection-data-service/connection-data.go b/backend/services/mgmt/v1alpha1/connection-data-service/connection-data.go
index 852678b096..6a272d2cf7 100644
--- a/backend/services/mgmt/v1alpha1/connection-data-service/connection-data.go
+++ b/backend/services/mgmt/v1alpha1/connection-data-service/connection-data.go
@@ -75,10 +75,6 @@ func (s *Service) GetConnectionDataStream(
return err
}
connection := connResp.Msg.Connection
- _, err = s.verifyUserInAccount(ctx, connection.AccountId)
- if err != nil {
- return err
- }
connectionTimeout := uint32(5)
@@ -483,10 +479,6 @@ func (s *Service) GetConnectionSchema(
return nil, err
}
connection := connResp.Msg.Connection
- _, err = s.verifyUserInAccount(ctx, connection.AccountId)
- if err != nil {
- return nil, err
- }
switch config := connection.ConnectionConfig.Config.(type) {
case *mgmtv1alpha1.ConnectionConfig_MysqlConfig, *mgmtv1alpha1.ConnectionConfig_PgConfig, *mgmtv1alpha1.ConnectionConfig_MssqlConfig:
@@ -753,11 +745,6 @@ func (s *Service) GetConnectionForeignConstraints(
return nil, err
}
- _, err = s.verifyUserInAccount(ctx, connection.Msg.Connection.AccountId)
- if err != nil {
- return nil, err
- }
-
schemaResp, err := s.getConnectionSchema(ctx, connection.Msg.Connection, &schemaOpts{})
if err != nil {
return nil, err
@@ -816,11 +803,6 @@ func (s *Service) GetConnectionPrimaryConstraints(
return nil, err
}
- _, err = s.verifyUserInAccount(ctx, connection.Msg.Connection.AccountId)
- if err != nil {
- return nil, err
- }
-
schemaResp, err := s.getConnectionSchema(ctx, connection.Msg.Connection, &schemaOpts{})
if err != nil {
return nil, err
@@ -870,11 +852,6 @@ func (s *Service) GetConnectionInitStatements(
return nil, err
}
- _, err = s.verifyUserInAccount(ctx, connection.Msg.Connection.AccountId)
- if err != nil {
- return nil, err
- }
-
schemaResp, err := s.getConnectionSchema(ctx, connection.Msg.Connection, &schemaOpts{})
if err != nil {
return nil, err
@@ -1213,11 +1190,6 @@ func (s *Service) GetConnectionUniqueConstraints(
return nil, err
}
- _, err = s.verifyUserInAccount(ctx, connection.Msg.Connection.AccountId)
- if err != nil {
- return nil, err
- }
-
schemaResp, err := s.getConnectionSchema(ctx, connection.Msg.Connection, &schemaOpts{})
if err != nil {
return nil, err
@@ -1277,10 +1249,6 @@ func (s *Service) GetAiGeneratedData(
return nil, err
}
aiconnection := aiconnectionResp.Msg.GetConnection()
- _, err = s.verifyUserInAccount(ctx, aiconnection.GetAccountId())
- if err != nil {
- return nil, err
- }
dbconnectionResp, err := s.connectionService.GetConnection(ctx, connect.NewRequest(&mgmtv1alpha1.GetConnectionRequest{
Id: req.Msg.GetDataConnectionId(),
@@ -1372,11 +1340,6 @@ func (s *Service) GetConnectionTableConstraints(
return nil, err
}
- _, err = s.verifyUserInAccount(ctx, connection.Msg.Connection.AccountId)
- if err != nil {
- return nil, err
- }
-
schemaResp, err := s.getConnectionSchema(ctx, connection.Msg.Connection, &schemaOpts{})
if err != nil {
return nil, err
@@ -1458,11 +1421,6 @@ func (s *Service) GetTableRowCount(
return nil, err
}
- _, err = s.verifyUserInAccount(ctx, connection.Msg.Connection.AccountId)
- if err != nil {
- return nil, err
- }
-
switch connection.Msg.GetConnection().GetConnectionConfig().Config.(type) {
case *mgmtv1alpha1.ConnectionConfig_PgConfig, *mgmtv1alpha1.ConnectionConfig_MysqlConfig, *mgmtv1alpha1.ConnectionConfig_MssqlConfig:
db, err := s.sqlmanager.NewSqlConnection(ctx, connectionmanager.NewUniqueSession(), connection.Msg.GetConnection(), logger)
diff --git a/backend/services/mgmt/v1alpha1/connection-data-service/connection-data_test.go b/backend/services/mgmt/v1alpha1/connection-data-service/connection-data_test.go
index b58b1c3165..fc6b15f9e1 100644
--- a/backend/services/mgmt/v1alpha1/connection-data-service/connection-data_test.go
+++ b/backend/services/mgmt/v1alpha1/connection-data-service/connection-data_test.go
@@ -60,7 +60,6 @@ func Test_GetConnectionSchema_AwsS3(t *testing.T) {
mockKey := "workflows/7c54e1ce-3924-477c-bfa8-ab8bd36cfee2-2023-12-21T22:02:35Z/activities/public.regions/data/228.txt.gz"
path := fmt.Sprintf("workflows/%s/activities/", mockJobRunId)
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, AwsS3Mock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -138,7 +137,6 @@ func Test_GetConnectionSchema_Postgres(t *testing.T) {
m.DbMock.On("Close").Return(nil)
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, PostgresMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -187,7 +185,6 @@ func Test_GetConnectionSchema_Mysql(t *testing.T) {
}
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, MysqlMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -217,7 +214,6 @@ func Test_GetConnectionSchema_NoRows(t *testing.T) {
defer m.SqlDbMock.Close()
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, MysqlMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -244,7 +240,6 @@ func Test_GetConnectionSchema_Error(t *testing.T) {
defer m.SqlDbMock.Close()
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, MysqlMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -270,7 +265,6 @@ func Test_GetConnectionForeignConstraints_Mysql(t *testing.T) {
defer m.SqlDbMock.Close()
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, MysqlMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -321,7 +315,6 @@ func Test_GetConnectionForeignConstraints_Postgres(t *testing.T) {
)
m.DbMock.On("Close").Return(nil)
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, PostgresMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -366,7 +359,6 @@ func Test_GetConnectionPrimaryConstraints_Mysql(t *testing.T) {
defer m.SqlDbMock.Close()
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, MysqlMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -409,7 +401,6 @@ func Test_GetConnectionPrimaryConstraints_Postgres(t *testing.T) {
defer m.SqlDbMock.Close()
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, PostgresMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -453,7 +444,6 @@ func Test_GetConnectionInitStatements_Mysql_Create(t *testing.T) {
defer m.SqlDbMock.Close()
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, MysqlMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -506,7 +496,6 @@ func Test_GetConnectionInitStatements_Mysql_Truncate(t *testing.T) {
defer m.SqlDbMock.Close()
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, MysqlMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -553,7 +542,6 @@ func Test_GetConnectionInitStatements_Postgres_Create(t *testing.T) {
)
m.DbMock.On("Close").Return(nil)
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, PostgresMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -607,7 +595,6 @@ func Test_GetConnectionInitStatements_Postgres_Truncate(t *testing.T) {
)
m.DbMock.On("Close").Return(nil)
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, PostgresMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -644,29 +631,27 @@ func Test_GetConnectionInitStatements_Postgres_Truncate(t *testing.T) {
}
type serviceMocks struct {
- Service *Service
- DbtxMock *neosyncdb.MockDBTX
- QuerierMock *db_queries.MockQuerier
- UserAccountServiceMock *mgmtv1alpha1connect.MockUserAccountServiceClient
- ConnectionServiceMock *mgmtv1alpha1connect.MockConnectionServiceClient
- JobServiceMock *mgmtv1alpha1connect.MockJobServiceHandler
- SqlMock sqlmock.Sqlmock
- SqlDbMock *sql.DB
- SqlDbContainerMock *sqlconnect.MockSqlDbContainer
- PgQueierMock *pg_queries.MockQuerier
- MysqlQueierMock *mysql_queries.MockQuerier
- SqlConnectorMock *sqlconnect.MockSqlConnector
- AwsManagerMock *awsmanager.MockNeosyncAwsManagerClient
- MongoConnectorMock *mongoconnect.MockInterface
- SqlManagerMock *sqlmanager.MockSqlManagerClient
- DbMock *sqlmanager.MockSqlDatabase
- GcpManagerMock *neosync_gcp.MockManagerInterface
+ Service *Service
+ DbtxMock *neosyncdb.MockDBTX
+ QuerierMock *db_queries.MockQuerier
+ ConnectionServiceMock *mgmtv1alpha1connect.MockConnectionServiceClient
+ JobServiceMock *mgmtv1alpha1connect.MockJobServiceHandler
+ SqlMock sqlmock.Sqlmock
+ SqlDbMock *sql.DB
+ SqlDbContainerMock *sqlconnect.MockSqlDbContainer
+ PgQueierMock *pg_queries.MockQuerier
+ MysqlQueierMock *mysql_queries.MockQuerier
+ SqlConnectorMock *sqlconnect.MockSqlConnector
+ AwsManagerMock *awsmanager.MockNeosyncAwsManagerClient
+ MongoConnectorMock *mongoconnect.MockInterface
+ SqlManagerMock *sqlmanager.MockSqlManagerClient
+ DbMock *sqlmanager.MockSqlDatabase
+ GcpManagerMock *neosync_gcp.MockManagerInterface
}
func createServiceMock(t *testing.T) *serviceMocks {
mockDbtx := neosyncdb.NewMockDBTX(t)
mockQuerier := db_queries.NewMockQuerier(t)
- mockUserAccountService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
mockConnectionService := mgmtv1alpha1connect.NewMockConnectionServiceClient(t)
mockJobService := mgmtv1alpha1connect.NewMockJobServiceHandler(t)
mockPgquerier := pg_queries.NewMockQuerier(t)
@@ -685,7 +670,6 @@ func createServiceMock(t *testing.T) *serviceMocks {
service := New(
&Config{},
- mockUserAccountService,
mockConnectionService,
mockJobService,
mockAwsManager,
@@ -698,32 +682,25 @@ func createServiceMock(t *testing.T) *serviceMocks {
)
return &serviceMocks{
- Service: service,
- DbtxMock: mockDbtx,
- QuerierMock: mockQuerier,
- UserAccountServiceMock: mockUserAccountService,
- ConnectionServiceMock: mockConnectionService,
- JobServiceMock: mockJobService,
- SqlMock: sqlMock,
- SqlDbMock: sqlDbMock,
- SqlDbContainerMock: sqlconnect.NewMockSqlDbContainer(t),
- PgQueierMock: mockPgquerier,
- MysqlQueierMock: mockMysqlquerier,
- SqlConnectorMock: mockSqlConnector,
- AwsManagerMock: mockAwsManager,
- MongoConnectorMock: mockMongoConnector,
- SqlManagerMock: mockSqlManager,
- DbMock: mockSqlDb,
- GcpManagerMock: mockGcpManager,
+ Service: service,
+ DbtxMock: mockDbtx,
+ QuerierMock: mockQuerier,
+ ConnectionServiceMock: mockConnectionService,
+ JobServiceMock: mockJobService,
+ SqlMock: sqlMock,
+ SqlDbMock: sqlDbMock,
+ SqlDbContainerMock: sqlconnect.NewMockSqlDbContainer(t),
+ PgQueierMock: mockPgquerier,
+ MysqlQueierMock: mockMysqlquerier,
+ SqlConnectorMock: mockSqlConnector,
+ AwsManagerMock: mockAwsManager,
+ MongoConnectorMock: mockMongoConnector,
+ SqlManagerMock: mockSqlManager,
+ DbMock: mockSqlDb,
+ GcpManagerMock: mockGcpManager,
}
}
-func mockIsUserInAccount(userAccountServiceMock *mgmtv1alpha1connect.MockUserAccountServiceClient, isInAccount bool) { //nolint
- userAccountServiceMock.On("IsUserInAccount", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.IsUserInAccountResponse{
- Ok: isInAccount,
- }), nil)
-}
-
//nolint:all
func getConnectionMock(accountId, name string, id string, connType ConnTypeMock) *mgmtv1alpha1.Connection {
timestamp := timestamppb.New(time.Now())
@@ -909,7 +886,6 @@ func Test_GetConnectionUniqueConstraints_Mysql(t *testing.T) {
defer m.SqlDbMock.Close()
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, MysqlMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
@@ -954,7 +930,6 @@ func Test_GetConnectionUniqueConstraints_Postgres(t *testing.T) {
defer m.SqlDbMock.Close()
connection := getConnectionMock(mockAccountId, mockConnectionName, mockConnectionId, PostgresMock)
- mockIsUserInAccount(m.UserAccountServiceMock, true)
m.ConnectionServiceMock.On("GetConnection", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: connection,
}), nil)
diff --git a/backend/services/mgmt/v1alpha1/connection-data-service/service.go b/backend/services/mgmt/v1alpha1/connection-data-service/service.go
index a7f225638c..bc7ca36713 100644
--- a/backend/services/mgmt/v1alpha1/connection-data-service/service.go
+++ b/backend/services/mgmt/v1alpha1/connection-data-service/service.go
@@ -12,10 +12,9 @@ import (
)
type Service struct {
- cfg *Config
- useraccountService mgmtv1alpha1connect.UserAccountServiceClient
- connectionService mgmtv1alpha1connect.ConnectionServiceClient
- jobService mgmtv1alpha1connect.JobServiceHandler
+ cfg *Config
+ connectionService mgmtv1alpha1connect.ConnectionServiceClient
+ jobService mgmtv1alpha1connect.JobServiceHandler
awsManager awsmanager.NeosyncAwsManagerClient
@@ -33,7 +32,6 @@ type Config struct {
func New(
cfg *Config,
- useraccountService mgmtv1alpha1connect.UserAccountServiceClient,
connectionService mgmtv1alpha1connect.ConnectionServiceClient,
jobService mgmtv1alpha1connect.JobServiceHandler,
@@ -47,16 +45,15 @@ func New(
gcpmanager neosync_gcp.ManagerInterface,
) *Service {
return &Service{
- cfg: cfg,
- useraccountService: useraccountService,
- connectionService: connectionService,
- jobService: jobService,
- awsManager: awsManager,
- sqlConnector: sqlConnector,
- pgquerier: pgquerier,
- mysqlquerier: mysqlquerier,
- sqlmanager: sqlmanager,
- mongoconnector: mongoconnector,
- gcpmanager: gcpmanager,
+ cfg: cfg,
+ connectionService: connectionService,
+ jobService: jobService,
+ awsManager: awsManager,
+ sqlConnector: sqlConnector,
+ pgquerier: pgquerier,
+ mysqlquerier: mysqlquerier,
+ sqlmanager: sqlmanager,
+ mongoconnector: mongoconnector,
+ gcpmanager: gcpmanager,
}
}
diff --git a/backend/services/mgmt/v1alpha1/connection-data-service/user-account.go b/backend/services/mgmt/v1alpha1/connection-data-service/user-account.go
deleted file mode 100644
index 5ade83d05a..0000000000
--- a/backend/services/mgmt/v1alpha1/connection-data-service/user-account.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package v1alpha1_connectiondataservice
-
-import (
- "context"
-
- "connectrpc.com/connect"
- "github.com/jackc/pgx/v5/pgtype"
- mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
- "github.com/nucleuscloud/neosync/backend/internal/apikey"
- auth_apikey "github.com/nucleuscloud/neosync/backend/internal/auth/apikey"
- nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
- "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
-)
-
-func (s *Service) verifyUserInAccount(
- ctx context.Context,
- accountId string,
-) (*pgtype.UUID, error) { //nolint:unparam
- accountUuid, err := neosyncdb.ToUuid(accountId)
- if err != nil {
- return nil, err
- }
- if isWorkerApiKey(ctx) {
- return &accountUuid, nil
- }
- resp, err := s.useraccountService.IsUserInAccount(ctx, connect.NewRequest(&mgmtv1alpha1.IsUserInAccountRequest{AccountId: accountId}))
- if err != nil {
- return nil, err
- }
- if !resp.Msg.Ok {
- return nil, nucleuserrors.NewForbidden("user in not in requested account")
- }
-
- return &accountUuid, nil
-}
-
-func isWorkerApiKey(ctx context.Context) bool {
- data, err := auth_apikey.GetTokenDataFromCtx(ctx)
- if err != nil {
- return false
- }
- return data.ApiKeyType == apikey.WorkerApiKey
-}
diff --git a/backend/services/mgmt/v1alpha1/connection-service/connection.go b/backend/services/mgmt/v1alpha1/connection-service/connection.go
index c3180332e7..1ff26c4033 100644
--- a/backend/services/mgmt/v1alpha1/connection-service/connection.go
+++ b/backend/services/mgmt/v1alpha1/connection-service/connection.go
@@ -16,8 +16,10 @@ import (
mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
logger_interceptor "github.com/nucleuscloud/neosync/backend/internal/connect/interceptors/logger"
"github.com/nucleuscloud/neosync/backend/internal/dtomaps"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
dbconnectconfig "github.com/nucleuscloud/neosync/backend/pkg/dbconnect-config"
"github.com/nucleuscloud/neosync/backend/pkg/sqlconnect"
pg_models "github.com/nucleuscloud/neosync/backend/sql/postgresql/models"
@@ -237,13 +239,20 @@ func (s *Service) IsConnectionNameAvailable(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.IsConnectionNameAvailableRequest],
) (*connect.Response[mgmtv1alpha1.IsConnectionNameAvailableResponse], error) {
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userclient.GetUser(ctx)
if err != nil {
return nil, err
}
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceConnection(ctx, userdata.NewWildcardDomainEntity(req.Msg.GetAccountId()), rbac.ConnectionAction_View); err != nil {
+ return nil, err
+ }
count, err := s.db.Q.IsConnectionNameAvailable(ctx, s.db.Db, db_queries.IsConnectionNameAvailableParams{
- AccountId: *accountUuid,
+ AccountId: accountUuid,
ConnectionName: req.Msg.ConnectionName,
})
if err != nil {
@@ -259,12 +268,19 @@ func (s *Service) GetConnections(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.GetConnectionsRequest],
) (*connect.Response[mgmtv1alpha1.GetConnectionsResponse], error) {
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceConnection(ctx, userdata.NewWildcardDomainEntity(req.Msg.GetAccountId()), rbac.ConnectionAction_View); err != nil {
+ return nil, err
+ }
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
- connections, err := s.db.Q.GetConnectionsByAccount(ctx, s.db.Db, *accountUuid)
+ connections, err := s.db.Q.GetConnectionsByAccount(ctx, s.db.Db, accountUuid)
if err != nil {
return nil, err
}
@@ -300,14 +316,22 @@ func (s *Service) GetConnection(
return nil, nucleuserrors.NewNotFound("unable to find connection by id")
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(connection.AccountID))
+ dto, err := dtomaps.ToConnectionDto(&connection)
if err != nil {
return nil, err
}
- dto, err := dtomaps.ToConnectionDto(&connection)
+
+ user, err := s.userclient.GetUser(ctx)
if err != nil {
return nil, err
}
+ if err := user.EnforceConnection(ctx, userdata.NewDbDomainEntity(connection.AccountID, connection.ID), rbac.ConnectionAction_View); err != nil {
+ return nil, err
+ }
+
+ if err := user.EnforceConnection(ctx, dto, rbac.ConnectionAction_View); err != nil {
+ return nil, err
+ }
return connect.NewResponse(&mgmtv1alpha1.GetConnectionResponse{
Connection: dto,
}), nil
@@ -322,22 +346,24 @@ func (s *Service) CreateConnection(
return nil, err
}
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userclient.GetUser(ctx)
if err != nil {
return nil, err
}
-
- userUuid, err := s.getUserUuid(ctx)
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
+ if err := user.EnforceConnection(ctx, userdata.NewWildcardDomainEntity(req.Msg.GetAccountId()), rbac.ConnectionAction_Create); err != nil {
+ return nil, err
+ }
connection, err := s.db.Q.CreateConnection(ctx, s.db.Db, db_queries.CreateConnectionParams{
- AccountID: *accountUuid,
+ AccountID: accountUuid,
Name: req.Msg.Name,
ConnectionConfig: cc,
- CreatedByID: *userUuid,
- UpdatedByID: *userUuid,
+ CreatedByID: user.PgId(),
+ UpdatedByID: user.PgId(),
})
if err != nil {
return nil, err
@@ -366,13 +392,12 @@ func (s *Service) UpdateConnection(
return nil, nucleuserrors.NewNotFound("unable to find connection by id")
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(connection.AccountID))
+ user, err := s.userclient.GetUser(ctx)
if err != nil {
return nil, err
}
- userUuid, err := s.getUserUuid(ctx)
- if err != nil {
+ if err := user.EnforceConnection(ctx, userdata.NewDbDomainEntity(connection.AccountID, connection.ID), rbac.ConnectionAction_Edit); err != nil {
return nil, err
}
@@ -384,7 +409,7 @@ func (s *Service) UpdateConnection(
connection, err = s.db.Q.UpdateConnection(ctx, s.db.Db, db_queries.UpdateConnectionParams{
ID: connection.ID,
ConnectionConfig: cc,
- UpdatedByID: *userUuid,
+ UpdatedByID: user.PgId(),
Name: req.Msg.Name,
})
if err != nil {
@@ -415,11 +440,15 @@ func (s *Service) DeleteConnection(
return connect.NewResponse(&mgmtv1alpha1.DeleteConnectionResponse{}), nil
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(connection.AccountID))
+ user, err := s.userclient.GetUser(ctx)
if err != nil {
return nil, err
}
+ if err := user.EnforceConnection(ctx, userdata.NewDbDomainEntity(connection.AccountID, connection.ID), rbac.ConnectionAction_Delete); err != nil {
+ return nil, err
+ }
+
err = s.db.Q.RemoveConnectionById(ctx, s.db.Db, connection.ID)
if err != nil {
return nil, err
diff --git a/backend/services/mgmt/v1alpha1/connection-service/service.go b/backend/services/mgmt/v1alpha1/connection-service/service.go
index 46dfd9170a..c731536196 100644
--- a/backend/services/mgmt/v1alpha1/connection-service/service.go
+++ b/backend/services/mgmt/v1alpha1/connection-service/service.go
@@ -1,8 +1,8 @@
package v1alpha1_connectionservice
import (
- "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
"github.com/nucleuscloud/neosync/backend/pkg/mongoconnect"
"github.com/nucleuscloud/neosync/backend/pkg/sqlconnect"
sql_manager "github.com/nucleuscloud/neosync/backend/pkg/sqlmanager"
@@ -10,13 +10,13 @@ import (
)
type Service struct {
- cfg *Config
- db *neosyncdb.NeosyncDb
- useraccountService mgmtv1alpha1connect.UserAccountServiceClient
- sqlConnector sqlconnect.SqlConnector
- sqlmanager sql_manager.SqlManagerClient
- mongoconnector mongoconnect.Interface
- awsManager awsmanager.NeosyncAwsManagerClient
+ cfg *Config
+ db *neosyncdb.NeosyncDb
+ userclient userdata.Interface
+ sqlConnector sqlconnect.SqlConnector
+ sqlmanager sql_manager.SqlManagerClient
+ mongoconnector mongoconnect.Interface
+ awsManager awsmanager.NeosyncAwsManagerClient
}
type Config struct {
@@ -25,19 +25,19 @@ type Config struct {
func New(
cfg *Config,
db *neosyncdb.NeosyncDb,
- useraccountService mgmtv1alpha1connect.UserAccountServiceClient,
+ userclient userdata.Interface,
mongoconnector mongoconnect.Interface,
awsManager awsmanager.NeosyncAwsManagerClient,
sqlmanager sql_manager.SqlManagerClient,
sqlconnector sqlconnect.SqlConnector,
) *Service {
return &Service{
- cfg: cfg,
- db: db,
- useraccountService: useraccountService,
- sqlmanager: sqlmanager,
- mongoconnector: mongoconnector,
- awsManager: awsManager,
- sqlConnector: sqlconnector,
+ cfg: cfg,
+ db: db,
+ userclient: userclient,
+ sqlmanager: sqlmanager,
+ mongoconnector: mongoconnector,
+ awsManager: awsManager,
+ sqlConnector: sqlconnector,
}
}
diff --git a/backend/services/mgmt/v1alpha1/connection-service/user-account.go b/backend/services/mgmt/v1alpha1/connection-service/user-account.go
deleted file mode 100644
index 14637f6e05..0000000000
--- a/backend/services/mgmt/v1alpha1/connection-service/user-account.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package v1alpha1_connectionservice
-
-import (
- "context"
-
- "connectrpc.com/connect"
- "github.com/jackc/pgx/v5/pgtype"
- mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
- "github.com/nucleuscloud/neosync/backend/internal/apikey"
- auth_apikey "github.com/nucleuscloud/neosync/backend/internal/auth/apikey"
- nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
- "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
-)
-
-func (s *Service) verifyUserInAccount(
- ctx context.Context,
- accountId string,
-) (*pgtype.UUID, error) {
- accountUuid, err := neosyncdb.ToUuid(accountId)
- if err != nil {
- return nil, err
- }
-
- if isWorkerApiKey(ctx) {
- return &accountUuid, nil
- }
-
- resp, err := s.useraccountService.IsUserInAccount(ctx, connect.NewRequest(&mgmtv1alpha1.IsUserInAccountRequest{AccountId: accountId}))
- if err != nil {
- return nil, err
- }
- if !resp.Msg.Ok {
- return nil, nucleuserrors.NewForbidden("user in not in requested account")
- }
-
- return &accountUuid, nil
-}
-
-func (s *Service) getUserUuid(
- ctx context.Context,
-) (*pgtype.UUID, error) {
- user, err := s.useraccountService.GetUser(ctx, connect.NewRequest(&mgmtv1alpha1.GetUserRequest{}))
- if err != nil {
- return nil, err
- }
- userUuid, err := neosyncdb.ToUuid(user.Msg.UserId)
- if err != nil {
- return nil, err
- }
- return &userUuid, nil
-}
-
-func isWorkerApiKey(ctx context.Context) bool {
- data, err := auth_apikey.GetTokenDataFromCtx(ctx)
- if err != nil {
- return false
- }
- return data.ApiKeyType == apikey.WorkerApiKey
-}
diff --git a/backend/services/mgmt/v1alpha1/integration_tests/integration_test.go b/backend/services/mgmt/v1alpha1/integration_tests/integration_test.go
index fdde39e529..1fa360193b 100644
--- a/backend/services/mgmt/v1alpha1/integration_tests/integration_test.go
+++ b/backend/services/mgmt/v1alpha1/integration_tests/integration_test.go
@@ -20,7 +20,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.ctx = context.Background()
api, err := tcneosyncapi.NewNeosyncApiTestClient(s.ctx, s.T())
if err != nil {
- panic(err)
+ s.T().Fatalf("unable to create neosync api test client: %v", err)
}
s.NeosyncApiTestClient = *api
}
@@ -29,21 +29,21 @@ func (s *IntegrationTestSuite) SetupSuite() {
func (s *IntegrationTestSuite) SetupTest() {
err := s.InitializeTest(s.ctx, s.T())
if err != nil {
- panic(err)
+ s.T().Fatalf("unable to initialize test: %v", err)
}
}
func (s *IntegrationTestSuite) TearDownTest() {
err := s.CleanupTest(s.ctx)
if err != nil {
- panic(err)
+ s.T().Fatalf("unable to cleanup test: %v", err)
}
}
func (s *IntegrationTestSuite) TearDownSuite() {
err := s.TearDown(s.ctx)
if err != nil {
- panic(err)
+ s.T().Fatalf("unable to cleanup test: %v", err)
}
}
diff --git a/backend/services/mgmt/v1alpha1/job-service/jobs.go b/backend/services/mgmt/v1alpha1/job-service/jobs.go
index 200a3c2bd3..08c51a6618 100644
--- a/backend/services/mgmt/v1alpha1/job-service/jobs.go
+++ b/backend/services/mgmt/v1alpha1/job-service/jobs.go
@@ -14,8 +14,10 @@ import (
mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
logger_interceptor "github.com/nucleuscloud/neosync/backend/internal/connect/interceptors/logger"
"github.com/nucleuscloud/neosync/backend/internal/dtomaps"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
"github.com/nucleuscloud/neosync/backend/internal/utils"
sqlmanager_shared "github.com/nucleuscloud/neosync/backend/pkg/sqlmanager/shared"
tabledependency "github.com/nucleuscloud/neosync/backend/pkg/table-dependency"
@@ -37,12 +39,21 @@ func (s *Service) GetJobs(
) (*connect.Response[mgmtv1alpha1.GetJobsResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.GetAccountId())
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(req.Msg.GetAccountId()), rbac.JobAction_View)
if err != nil {
return nil, err
}
- jobs, err := s.db.Q.GetJobsByAccount(ctx, s.db.Db, *accountUuid)
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
+ if err != nil {
+ return nil, err
+ }
+
+ jobs, err := s.db.Q.GetJobsByAccount(ctx, s.db.Db, accountUuid)
if err != nil && !neosyncdb.IsNoRows(err) {
return nil, fmt.Errorf("unable to get jobs by account: %w", err)
} else if err != nil && neosyncdb.IsNoRows(err) {
@@ -99,7 +110,11 @@ func (s *Service) GetJob(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.GetJobRequest],
) (*connect.Response[mgmtv1alpha1.GetJobResponse], error) {
- jobUuid, err := neosyncdb.ToUuid(req.Msg.Id)
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ jobUuid, err := neosyncdb.ToUuid(req.Msg.GetId())
if err != nil {
return nil, err
}
@@ -132,8 +147,7 @@ func (s *Service) GetJob(
return nil, err
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(job.AccountID))
- if err != nil {
+ if err := user.EnforceJob(ctx, userdata.NewDbDomainEntity(job.AccountID, job.ID), rbac.JobAction_View); err != nil {
return nil, err
}
@@ -147,24 +161,13 @@ func (s *Service) GetJobStatus(
req *connect.Request[mgmtv1alpha1.GetJobStatusRequest],
) (*connect.Response[mgmtv1alpha1.GetJobStatusResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- logger = logger.With("jobId", req.Msg.JobId)
- jobUuid, err := neosyncdb.ToUuid(req.Msg.JobId)
- if err != nil {
- return nil, err
- }
- job, err := s.db.Q.GetJobById(ctx, s.db.Db, jobUuid)
- if err != nil && !neosyncdb.IsNoRows(err) {
- return nil, fmt.Errorf("unable to get job by id: %w", err)
- } else if err != nil && neosyncdb.IsNoRows(err) {
- return nil, nucleuserrors.NewNotFound("job with that id does not exist")
- }
-
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(job.AccountID))
+ logger = logger.With("jobId", req.Msg.GetJobId())
+ jobResp, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{Id: req.Msg.GetJobId()}))
if err != nil {
return nil, err
}
- schedule, err := s.temporalmgr.DescribeSchedule(ctx, neosyncdb.UUIDString(job.AccountID), neosyncdb.UUIDString(job.ID), logger)
+ schedule, err := s.temporalmgr.DescribeSchedule(ctx, jobResp.Msg.GetJob().GetAccountId(), jobResp.Msg.GetJob().GetId(), logger)
if err != nil {
return nil, fmt.Errorf("unable to describe temporal schedule when retrieving job status: %w", err)
}
@@ -180,11 +183,21 @@ func (s *Service) GetJobStatuses(
) (*connect.Response[mgmtv1alpha1.GetJobStatusesResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.GetAccountId())
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- jobs, err := s.db.Q.GetJobsByAccount(ctx, s.db.Db, *accountUuid)
+ err = user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(req.Msg.GetAccountId()), rbac.JobAction_View)
+ if err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
+ if err != nil {
+ return nil, err
+ }
+
+ jobs, err := s.db.Q.GetJobsByAccount(ctx, s.db.Db, accountUuid)
if err != nil && !neosyncdb.IsNoRows(err) {
return nil, fmt.Errorf("unable to get jobs by account: %w", err)
} else if err != nil && neosyncdb.IsNoRows(err) {
@@ -229,23 +242,13 @@ func (s *Service) GetJobRecentRuns(
) (*connect.Response[mgmtv1alpha1.GetJobRecentRunsResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
logger = logger.With("jobId", req.Msg.JobId)
- jobUuid, err := neosyncdb.ToUuid(req.Msg.JobId)
- if err != nil {
- return nil, err
- }
- job, err := s.db.Q.GetJobById(ctx, s.db.Db, jobUuid)
- if err != nil && !neosyncdb.IsNoRows(err) {
- return nil, fmt.Errorf("unable to get job by id: %w", err)
- } else if err != nil && neosyncdb.IsNoRows(err) {
- return nil, nucleuserrors.NewNotFound("job with that id does not exist")
- }
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(job.AccountID))
+ jobResp, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{Id: req.Msg.GetJobId()}))
if err != nil {
return nil, err
}
- schedule, err := s.temporalmgr.DescribeSchedule(ctx, neosyncdb.UUIDString(job.AccountID), neosyncdb.UUIDString(job.ID), logger)
+ schedule, err := s.temporalmgr.DescribeSchedule(ctx, jobResp.Msg.GetJob().GetAccountId(), jobResp.Msg.GetJob().GetId(), logger)
if err != nil {
return nil, fmt.Errorf("unable to describe temporal schedule when retrieving job recent runs: %w", err)
}
@@ -260,24 +263,14 @@ func (s *Service) GetJobNextRuns(
req *connect.Request[mgmtv1alpha1.GetJobNextRunsRequest],
) (*connect.Response[mgmtv1alpha1.GetJobNextRunsResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- logger = logger.With("jobId", req.Msg.JobId)
- jobUuid, err := neosyncdb.ToUuid(req.Msg.JobId)
- if err != nil {
- return nil, err
- }
- job, err := s.db.Q.GetJobById(ctx, s.db.Db, jobUuid)
- if err != nil && !neosyncdb.IsNoRows(err) {
- return nil, fmt.Errorf("unable to get job by id: %w", err)
- } else if err != nil && neosyncdb.IsNoRows(err) {
- return nil, nucleuserrors.NewNotFound("job with that id does not exist")
- }
+ logger = logger.With("jobId", req.Msg.GetJobId())
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(job.AccountID))
+ jobResp, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{Id: req.Msg.GetJobId()}))
if err != nil {
return nil, err
}
- schedule, err := s.temporalmgr.DescribeSchedule(ctx, neosyncdb.UUIDString(job.AccountID), neosyncdb.UUIDString(job.ID), logger)
+ schedule, err := s.temporalmgr.DescribeSchedule(ctx, jobResp.Msg.GetJob().GetAccountId(), jobResp.Msg.GetJob().GetId(), logger)
if err != nil {
return nil, fmt.Errorf("unable to describe temporal schedule when retrieving job next runs: %w", err)
}
@@ -287,7 +280,7 @@ func (s *Service) GetJobNextRuns(
}), nil
}
-type Destination struct {
+type destination struct {
ConnectionId pgtype.UUID
Options *pg_models.JobDestinationOptions
}
@@ -299,18 +292,22 @@ func (s *Service) CreateJob(
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
logger = logger.With("jobName", req.Msg.JobName, "accountId", req.Msg.AccountId)
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(req.Msg.GetAccountId()), rbac.JobAction_Create)
if err != nil {
return nil, err
}
- userUuid, err := s.getUserUuid(ctx)
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
connectionUuids := []pgtype.UUID{}
connectionIds := []string{}
- destinations := []*Destination{}
+ destinations := []*destination{}
for _, dest := range req.Msg.Destinations {
destUuid, err := neosyncdb.ToUuid(dest.ConnectionId)
if err != nil {
@@ -321,14 +318,14 @@ func (s *Service) CreateJob(
if err != nil {
return nil, err
}
- destinations = append(destinations, &Destination{ConnectionId: destUuid, Options: options})
+ destinations = append(destinations, &destination{ConnectionId: destUuid, Options: options})
connectionIds = append(connectionIds, dest.ConnectionId)
connectionUuids = append(connectionUuids, destUuid)
}
logger.Debug("verifying connections")
count, err := s.db.Q.AreConnectionsInAccount(ctx, s.db.Db, db_queries.AreConnectionsInAccountParams{
- AccountId: *accountUuid,
+ AccountId: accountUuid,
ConnectionIds: connectionUuids,
})
if err != nil {
@@ -451,14 +448,14 @@ func (s *Service) CreateJob(
cj, err := s.db.CreateJob(ctx, &db_queries.CreateJobParams{
Name: req.Msg.JobName,
- AccountID: *accountUuid,
+ AccountID: accountUuid,
Status: int16(mgmtv1alpha1.JobStatus_JOB_STATUS_ENABLED),
CronSchedule: cronText,
ConnectionOptions: connectionOptions,
Mappings: mappings,
VirtualForeignKeys: virtualForeignKeys,
- CreatedByID: *userUuid,
- UpdatedByID: *userUuid,
+ CreatedByID: user.PgId(),
+ UpdatedByID: user.PgId(),
WorkflowOptions: workflowOptions,
SyncOptions: activitySyncOptions,
}, connDestParams)
@@ -552,7 +549,11 @@ func (s *Service) DeleteJob(
return connect.NewResponse(&mgmtv1alpha1.DeleteJobResponse{}), nil
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(job.AccountID))
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceJob(ctx, userdata.NewDbDomainEntity(job.AccountID, job.ID), rbac.JobAction_Delete)
if err != nil {
return nil, err
}
@@ -593,19 +594,22 @@ func (s *Service) CreateJobDestinationConnections(
if err != nil {
return nil, err
}
- accountUuid, err := s.verifyUserInAccount(ctx, job.Msg.Job.AccountId)
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- userUuid, err := s.getUserUuid(ctx)
+ err = user.EnforceJob(ctx, job.Msg.GetJob(), rbac.JobAction_Create)
+ if err != nil {
+ return nil, err
+ }
+ accountUuid, err := neosyncdb.ToUuid(job.Msg.GetJob().GetAccountId())
if err != nil {
return nil, err
}
- logger = logger.With("userId", userUuid)
connectionIds := []string{}
connectionUuids := []pgtype.UUID{}
- destinations := []*Destination{}
+ destinations := []*destination{}
for _, dest := range req.Msg.Destinations {
destUuid, err := neosyncdb.ToUuid(dest.ConnectionId)
if err != nil {
@@ -616,7 +620,7 @@ func (s *Service) CreateJobDestinationConnections(
if err != nil {
return nil, err
}
- destinations = append(destinations, &Destination{ConnectionId: destUuid, Options: options})
+ destinations = append(destinations, &destination{ConnectionId: destUuid, Options: options})
connectionIds = append(connectionIds, dest.ConnectionId)
connectionUuids = append(connectionUuids, destUuid)
}
@@ -625,7 +629,7 @@ func (s *Service) CreateJobDestinationConnections(
return nil, nucleuserrors.NewBadRequest("connections ids are not unique")
}
- isInSameAccount, err := verifyConnectionsInAccount(ctx, s.db, connectionUuids, *accountUuid)
+ isInSameAccount, err := verifyConnectionsInAccount(ctx, s.db, connectionUuids, accountUuid)
if err != nil {
return nil, err
}
@@ -668,23 +672,20 @@ func (s *Service) UpdateJobSchedule(
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
logger = logger.With("jobId", req.Msg.Id)
logger.Debug("updating job schedule")
- jobUuid, err := neosyncdb.ToUuid(req.Msg.Id)
+
+ jobResp, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
+ Id: req.Msg.Id,
+ }))
if err != nil {
return nil, err
}
- job, err := s.db.Q.GetJobById(ctx, s.db.Db, jobUuid)
- if err != nil && !neosyncdb.IsNoRows(err) {
- return nil, fmt.Errorf("unable to get job by id: %w", err)
- } else if err != nil && neosyncdb.IsNoRows(err) {
- return nil, nucleuserrors.NewNotFound("job with that id does not exist")
- }
+ job := jobResp.Msg.GetJob()
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(job.AccountID))
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
-
- userUuid, err := s.getUserUuid(ctx)
+ err = user.EnforceJob(ctx, job, rbac.JobAction_Edit)
if err != nil {
return nil, err
}
@@ -699,11 +700,16 @@ func (s *Service) UpdateJobSchedule(
return nil, err
}
+ jobUuid, err := neosyncdb.ToUuid(job.GetId())
+ if err != nil {
+ return nil, err
+ }
+
if err := s.db.WithTx(ctx, nil, func(dbtx neosyncdb.BaseDBTX) error {
_, err = s.db.Q.UpdateJobSchedule(ctx, dbtx, db_queries.UpdateJobScheduleParams{
- ID: job.ID,
+ ID: jobUuid,
CronSchedule: cronText,
- UpdatedByID: *userUuid,
+ UpdatedByID: user.PgId(),
})
if err != nil {
return err
@@ -715,8 +721,8 @@ func (s *Service) UpdateJobSchedule(
// update temporal scheduled job
err = s.temporalmgr.UpdateSchedule(
ctx,
- neosyncdb.UUIDString(job.AccountID),
- neosyncdb.UUIDString(job.ID),
+ job.GetAccountId(),
+ job.GetId(),
&temporalclient.ScheduleUpdateOptions{
DoUpdate: func(schedule temporalclient.ScheduleUpdateInput) (*temporalclient.ScheduleUpdate, error) {
schedule.Description.Schedule.Spec = spec
@@ -753,18 +759,19 @@ func (s *Service) PauseJob(
) (*connect.Response[mgmtv1alpha1.PauseJobResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
logger = logger.With("jobId", req.Msg.Id)
- jobUuid, err := neosyncdb.ToUuid(req.Msg.Id)
+ jobResp, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
+ Id: req.Msg.Id,
+ }))
if err != nil {
return nil, err
}
- job, err := s.db.Q.GetJobById(ctx, s.db.Db, jobUuid)
- if err != nil && !neosyncdb.IsNoRows(err) {
- return nil, fmt.Errorf("unable to get job by id: %w", err)
- } else if err != nil && neosyncdb.IsNoRows(err) {
- return nil, nucleuserrors.NewNotFound("job with that id does not exist")
- }
+ job := jobResp.Msg.GetJob()
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(job.AccountID))
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceJob(ctx, job, rbac.JobAction_Edit)
if err != nil {
return nil, err
}
@@ -773,8 +780,8 @@ func (s *Service) PauseJob(
logger.Debug("pausing job")
err = s.temporalmgr.PauseSchedule(
ctx,
- neosyncdb.UUIDString(job.AccountID),
- neosyncdb.UUIDString(job.ID),
+ job.GetAccountId(),
+ job.GetId(),
&temporalclient.SchedulePauseOptions{Note: req.Msg.GetNote()},
logger,
)
@@ -785,8 +792,8 @@ func (s *Service) PauseJob(
logger.Debug("unpausing job")
err = s.temporalmgr.UnpauseSchedule(
ctx,
- neosyncdb.UUIDString(job.AccountID),
- neosyncdb.UUIDString(job.ID),
+ job.GetAccountId(),
+ job.GetId(),
&temporalclient.ScheduleUnpauseOptions{Note: req.Msg.GetNote()},
logger,
)
@@ -814,23 +821,20 @@ func (s *Service) UpdateJobSourceConnection(
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
logger = logger.With("jobId", req.Msg.Id)
logger.Debug("updating job source connection and mappings")
- jobUuid, err := neosyncdb.ToUuid(req.Msg.Id)
+
+ jobResp, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
+ Id: req.Msg.Id,
+ }))
if err != nil {
return nil, err
}
+ job := jobResp.Msg.GetJob()
- job, err := s.db.Q.GetJobById(ctx, s.db.Db, jobUuid)
- if err != nil && !neosyncdb.IsNoRows(err) {
- return nil, fmt.Errorf("unable to get job by id: %w", err)
- } else if err != nil && neosyncdb.IsNoRows(err) {
- return nil, nucleuserrors.NewNotFound("job with that id does not exist")
- }
-
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(job.AccountID))
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- userUuid, err := s.getUserUuid(ctx)
+ err = user.EnforceJob(ctx, job, rbac.JobAction_Edit)
if err != nil {
return nil, err
}
@@ -862,7 +866,7 @@ func (s *Service) UpdateJobSourceConnection(
}
// verifies that the account has access to that connection id
- if err := s.verifyConnectionInAccount(ctx, connectionIdToVerify, neosyncdb.UUIDString(job.AccountID)); err != nil {
+ if err := s.verifyConnectionInAccount(ctx, connectionIdToVerify, job.GetAccountId()); err != nil {
return nil, err
}
@@ -952,21 +956,26 @@ func (s *Service) UpdateJobSourceConnection(
vfkKeys[key] = struct{}{}
}
+ jobUuid, err := neosyncdb.ToUuid(job.GetId())
+ if err != nil {
+ return nil, err
+ }
+
if err := s.db.WithTx(ctx, nil, func(dbtx neosyncdb.BaseDBTX) error {
_, err = s.db.Q.UpdateJobSource(ctx, dbtx, db_queries.UpdateJobSourceParams{
- ID: job.ID,
+ ID: jobUuid,
ConnectionOptions: connectionOptions,
- UpdatedByID: *userUuid,
+ UpdatedByID: user.PgId(),
})
if err != nil {
return fmt.Errorf("unable to update job source: %w", err)
}
_, err = s.db.Q.UpdateJobMappings(ctx, dbtx, db_queries.UpdateJobMappingsParams{
- ID: job.ID,
+ ID: jobUuid,
Mappings: mappings,
- UpdatedByID: *userUuid,
+ UpdatedByID: user.PgId(),
})
if err != nil {
return fmt.Errorf("unable to update job mappings: %w", err)
@@ -974,8 +983,8 @@ func (s *Service) UpdateJobSourceConnection(
args := db_queries.UpdateJobVirtualForeignKeysParams{
VirtualForeignKeys: virtualForeignKeys,
- UpdatedByID: *userUuid,
- ID: job.ID,
+ UpdatedByID: user.PgId(),
+ ID: jobUuid,
}
_, err = s.db.Q.UpdateJobVirtualForeignKeys(ctx, dbtx, args)
if err != nil {
@@ -1006,36 +1015,37 @@ func (s *Service) SetJobSourceSqlConnectionSubsets(
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
logger = logger.With("jobId", req.Msg.Id)
logger.Debug("updating job source sql connection subsets")
- jobUuid, err := neosyncdb.ToUuid(req.Msg.Id)
+
+ jobResp, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
+ Id: req.Msg.Id,
+ }))
if err != nil {
return nil, err
}
- job, err := s.db.Q.GetJobById(ctx, s.db.Db, jobUuid)
- if err != nil && !neosyncdb.IsNoRows(err) {
- return nil, fmt.Errorf("unable to get job by id: %w", err)
- } else if err != nil && neosyncdb.IsNoRows(err) {
- return nil, nucleuserrors.NewNotFound("job with that id does not exist")
+ job := jobResp.Msg.GetJob()
+ jobUuid, err := neosyncdb.ToUuid(job.GetId())
+ if err != nil {
+ return nil, err
}
-
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(job.AccountID))
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- userUuid, err := s.getUserUuid(ctx)
+ err = user.EnforceJob(ctx, job, rbac.JobAction_Edit)
if err != nil {
return nil, err
}
var connectionId *string
- if job.ConnectionOptions != nil {
- if job.ConnectionOptions.MysqlOptions != nil {
- connectionId = &job.ConnectionOptions.MysqlOptions.ConnectionId
- } else if job.ConnectionOptions.PostgresOptions != nil {
- connectionId = &job.ConnectionOptions.PostgresOptions.ConnectionId
- } else if job.ConnectionOptions.DynamoDBOptions != nil {
- connectionId = &job.ConnectionOptions.DynamoDBOptions.ConnectionId
- } else if job.ConnectionOptions.MssqlOptions != nil {
- connectionId = &job.ConnectionOptions.MssqlOptions.ConnectionId
+ if job.GetSource().GetOptions() != nil {
+ if job.GetSource().GetOptions().GetMysql() != nil {
+ connectionId = &job.GetSource().GetOptions().GetMysql().ConnectionId
+ } else if job.GetSource().GetOptions().GetPostgres() != nil {
+ connectionId = &job.GetSource().GetOptions().GetPostgres().ConnectionId
+ } else if job.GetSource().GetOptions().GetDynamodb() != nil {
+ connectionId = &job.GetSource().GetOptions().GetDynamodb().ConnectionId
+ } else if job.GetSource().GetOptions().GetMssql() != nil {
+ connectionId = &job.GetSource().GetOptions().GetMssql().ConnectionId
} else {
return nil, nucleuserrors.NewBadRequest("only jobs with a valid source connection id may be subset")
}
@@ -1062,7 +1072,7 @@ func (s *Service) SetJobSourceSqlConnectionSubsets(
jobUuid,
req.Msg.Schemas,
req.Msg.SubsetByForeignKeyConstraints,
- *userUuid,
+ user.PgId(),
); err != nil {
return nil, err
}
@@ -1084,37 +1094,37 @@ func (s *Service) UpdateJobDestinationConnection(
req *connect.Request[mgmtv1alpha1.UpdateJobDestinationConnectionRequest],
) (*connect.Response[mgmtv1alpha1.UpdateJobDestinationConnectionResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- logger = logger.With("jobId", req.Msg.JobId, "connectionId", req.Msg.ConnectionId)
+ logger = logger.With("jobId", req.Msg.GetJobId(), "connectionId", req.Msg.GetConnectionId())
- jobUuid, err := neosyncdb.ToUuid(req.Msg.JobId)
+ jobUuid, err := neosyncdb.ToUuid(req.Msg.GetJobId())
if err != nil {
return nil, err
}
- destinationUuid, err := neosyncdb.ToUuid(req.Msg.DestinationId)
+ destinationUuid, err := neosyncdb.ToUuid(req.Msg.GetDestinationId())
if err != nil {
return nil, err
}
- job, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
+ jobResp, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
Id: req.Msg.JobId,
}))
if err != nil {
return nil, err
}
- _, err = s.verifyUserInAccount(ctx, job.Msg.Job.AccountId)
+ job := jobResp.Msg.GetJob()
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- userUuid, err := s.getUserUuid(ctx)
+ err = user.EnforceJob(ctx, job, rbac.JobAction_Edit)
if err != nil {
return nil, err
}
- logger = logger.With("userId", userUuid)
- connectionUuid, err := neosyncdb.ToUuid(req.Msg.ConnectionId)
+ connectionUuid, err := neosyncdb.ToUuid(req.Msg.GetConnectionId())
if err != nil {
return nil, err
}
- if err := s.verifyConnectionInAccount(ctx, req.Msg.ConnectionId, job.Msg.Job.AccountId); err != nil {
+ if err := s.verifyConnectionInAccount(ctx, req.Msg.GetConnectionId(), job.GetAccountId()); err != nil {
return nil, err
}
options := &pg_models.JobDestinationOptions{}
@@ -1146,7 +1156,7 @@ func (s *Service) UpdateJobDestinationConnection(
}
updatedJob, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
- Id: job.Msg.Job.Id,
+ Id: job.GetId(),
}))
if err != nil {
return nil, err
@@ -1162,9 +1172,9 @@ func (s *Service) DeleteJobDestinationConnection(
req *connect.Request[mgmtv1alpha1.DeleteJobDestinationConnectionRequest],
) (*connect.Response[mgmtv1alpha1.DeleteJobDestinationConnectionResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- logger = logger.With("destinationId", req.Msg.DestinationId)
+ logger = logger.With("destinationId", req.Msg.GetDestinationId())
- destinationUuid, err := neosyncdb.ToUuid(req.Msg.DestinationId)
+ destinationUuid, err := neosyncdb.ToUuid(req.Msg.GetDestinationId())
if err != nil {
return nil, err
}
@@ -1176,27 +1186,24 @@ func (s *Service) DeleteJobDestinationConnection(
return connect.NewResponse(&mgmtv1alpha1.DeleteJobDestinationConnectionResponse{}), nil
}
- job, err := s.db.Q.GetJobById(ctx, s.db.Db, destination.JobID)
- if err != nil && !neosyncdb.IsNoRows(err) {
- return nil, fmt.Errorf("unable to get job by id: %w", err)
- } else if err != nil && neosyncdb.IsNoRows(err) {
- return nil, nucleuserrors.NewNotFound("job with that id does not exist")
- }
+ jobId := neosyncdb.UUIDString(destination.JobID)
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(job.AccountID))
+ jobResp, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
+ Id: jobId,
+ }))
if err != nil {
return nil, err
}
- userUuid, err := s.getUserUuid(ctx)
+ job := jobResp.Msg.GetJob()
+
+ logger = logger.With("jobId", job.GetId())
+
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- logger = logger.With("userId", userUuid, "jobId", job.ID)
-
- if err := s.verifyConnectionInAccount(
- ctx,
- neosyncdb.UUIDString(destination.ConnectionID),
- neosyncdb.UUIDString(job.AccountID)); err != nil {
+ err = user.EnforceJob(ctx, job, rbac.JobAction_Edit)
+ if err != nil {
return nil, err
}
@@ -1215,13 +1222,22 @@ func (s *Service) IsJobNameAvailable(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.IsJobNameAvailableRequest],
) (*connect.Response[mgmtv1alpha1.IsJobNameAvailableResponse], error) {
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(req.Msg.GetAccountId()), rbac.JobAction_View); err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
count, err := s.db.Q.IsJobNameAvailable(ctx, s.db.Db, db_queries.IsJobNameAvailableParams{
- AccountId: *accountUuid,
+ AccountId: accountUuid,
JobName: req.Msg.Name,
})
if err != nil {
@@ -1287,7 +1303,7 @@ func verifyConnectionIdsUnique(connectionIds []string) bool {
return true
}
-func verifyConnectionsAreCompatible(ctx context.Context, db *neosyncdb.NeosyncDb, sourceConnId pgtype.UUID, destinations []*Destination) (bool, error) {
+func verifyConnectionsAreCompatible(ctx context.Context, db *neosyncdb.NeosyncDb, sourceConnId pgtype.UUID, destinations []*destination) (bool, error) {
var sourceConnection db_queries.NeosyncApiConnection
dests := make([]db_queries.NeosyncApiConnection, len(destinations))
group := new(errgroup.Group)
@@ -1360,7 +1376,12 @@ func (s *Service) SetJobWorkflowOptions(
if err != nil {
return nil, err
}
- _, err = s.verifyUserInAccount(ctx, job.Msg.Job.AccountId)
+
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceJob(ctx, job.Msg.GetJob(), rbac.JobAction_Edit)
if err != nil {
return nil, err
}
@@ -1374,16 +1395,13 @@ func (s *Service) SetJobWorkflowOptions(
if err != nil {
return nil, err
}
- userUuid, err := s.getUserUuid(ctx)
- if err != nil {
- return nil, err
- }
+
// update temporal scheduled job
if err := s.db.WithTx(ctx, nil, func(dbtx neosyncdb.BaseDBTX) error {
_, err = s.db.Q.SetJobWorkflowOptions(ctx, dbtx, db_queries.SetJobWorkflowOptionsParams{
ID: jobUuid,
WorkflowOptions: wfOptions,
- UpdatedByID: *userUuid,
+ UpdatedByID: user.PgId(),
})
if err != nil {
return fmt.Errorf("unable to set job workflow options: %w", err)
@@ -1437,13 +1455,17 @@ func (s *Service) SetJobSyncOptions(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.SetJobSyncOptionsRequest],
) (*connect.Response[mgmtv1alpha1.SetJobSyncOptionsResponse], error) {
- job, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
+ jobResp, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
Id: req.Msg.Id,
}))
if err != nil {
return nil, err
}
- _, err = s.verifyUserInAccount(ctx, job.Msg.Job.AccountId)
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceJob(ctx, jobResp.Msg.GetJob(), rbac.JobAction_Edit)
if err != nil {
return nil, err
}
@@ -1457,22 +1479,18 @@ func (s *Service) SetJobSyncOptions(
if err != nil {
return nil, err
}
- userUuid, err := s.getUserUuid(ctx)
- if err != nil {
- return nil, err
- }
_, err = s.db.Q.SetJobSyncOptions(ctx, s.db.Db, db_queries.SetJobSyncOptionsParams{
ID: jobUuid,
SyncOptions: syncOptions,
- UpdatedByID: *userUuid,
+ UpdatedByID: user.PgId(),
})
if err != nil {
return nil, err
}
updatedJob, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
- Id: req.Msg.Id,
+ Id: req.Msg.GetId(),
}))
if err != nil {
return nil, err
@@ -1485,15 +1503,10 @@ func (s *Service) ValidateJobMappings(
req *connect.Request[mgmtv1alpha1.ValidateJobMappingsRequest],
) (*connect.Response[mgmtv1alpha1.ValidateJobMappingsResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- logger = logger.With("accountId", req.Msg.AccountId)
-
- _, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
- if err != nil {
- return nil, err
- }
+ logger = logger.With("accountId", req.Msg.GetAccountId())
connection, err := s.connectionService.GetConnection(ctx, connect.NewRequest(&mgmtv1alpha1.GetConnectionRequest{
- Id: req.Msg.ConnectionId,
+ Id: req.Msg.GetConnectionId(),
}))
if err != nil {
return nil, err
diff --git a/backend/services/mgmt/v1alpha1/job-service/runs.go b/backend/services/mgmt/v1alpha1/job-service/runs.go
index 40460b200e..4375d557b4 100644
--- a/backend/services/mgmt/v1alpha1/job-service/runs.go
+++ b/backend/services/mgmt/v1alpha1/job-service/runs.go
@@ -12,14 +12,15 @@ import (
"time"
"connectrpc.com/connect"
- "github.com/jackc/pgx/v5/pgtype"
db_queries "github.com/nucleuscloud/neosync/backend/gen/go/db"
mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
logger_interceptor "github.com/nucleuscloud/neosync/backend/internal/connect/interceptors/logger"
"github.com/nucleuscloud/neosync/backend/internal/dtomaps"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
"github.com/nucleuscloud/neosync/backend/internal/loki"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
"go.temporal.io/api/enums/v1"
temporalclient "go.temporal.io/sdk/client"
"go.temporal.io/sdk/converter"
@@ -51,6 +52,7 @@ func (s *Service) GetJobRuns(
if err != nil {
return nil, err
}
+
accountId = neosyncdb.UUIDString(job.AccountID)
jobIds = append(jobIds, id.JobId)
case *mgmtv1alpha1.GetJobRunsRequest_AccountId:
@@ -71,10 +73,13 @@ func (s *Service) GetJobRuns(
return nil, fmt.Errorf("must provide jobId or accountId")
}
- _, err := s.verifyUserInAccount(ctx, accountId)
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
+ if err := user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(accountId), rbac.JobAction_View); err != nil {
+ return nil, err
+ }
workflows, err := s.temporalmgr.GetWorkflowExecutionsByScheduleIds(ctx, accountId, jobIds, logger)
if err != nil {
@@ -97,12 +102,22 @@ func (s *Service) GetJobRun(
) (*connect.Response[mgmtv1alpha1.GetJobRunResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
logger = logger.With("jobRunId", req.Msg.JobRunId)
+
res, err := s.temporalmgr.DescribeWorklowExecution(ctx, req.Msg.GetAccountId(), req.Msg.GetJobRunId(), logger)
if err != nil {
return nil, err
}
dto := dtomaps.ToJobRunDto(logger, res)
+
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceJob(ctx, userdata.NewDomainEntity(req.Msg.GetAccountId(), dto.GetJobId()), rbac.JobAction_View); err != nil {
+ return nil, err
+ }
+
return connect.NewResponse(&mgmtv1alpha1.GetJobRunResponse{
JobRun: dto,
}), nil
@@ -114,13 +129,24 @@ func (s *Service) GetJobRunEvents(
) (*connect.Response[mgmtv1alpha1.GetJobRunEventsResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
logger = logger.With("accountId", req.Msg.GetAccountId(), "jobRunId", req.Msg.GetJobRunId())
+
+ jrResp, err := s.GetJobRun(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRunRequest{
+ AccountId: req.Msg.GetAccountId(),
+ JobRunId: req.Msg.GetJobRunId(),
+ }))
+ if err != nil {
+ return nil, err
+ }
+
+ jobRun := jrResp.Msg.GetJobRun()
+
isRunComplete := false
activityOrder := []int64{}
activityMap := map[int64]*mgmtv1alpha1.JobRunEvent{}
iter, err := s.temporalmgr.GetWorkflowHistory(
ctx,
req.Msg.GetAccountId(),
- req.Msg.GetJobRunId(),
+ jobRun.GetId(),
logger,
)
if err != nil {
@@ -228,26 +254,27 @@ func (s *Service) CreateJobRun(
req *connect.Request[mgmtv1alpha1.CreateJobRunRequest],
) (*connect.Response[mgmtv1alpha1.CreateJobRunResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- logger = logger.With("jobId", req.Msg.JobId)
- jobUuid, err := neosyncdb.ToUuid(req.Msg.JobId)
+ logger = logger.With("jobId", req.Msg.GetJobId())
+ jobResp, err := s.GetJob(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRequest{
+ Id: req.Msg.GetJobId(),
+ }))
if err != nil {
return nil, err
}
- job, err := s.db.Q.GetJobById(ctx, s.db.Db, jobUuid)
+ job := jobResp.Msg.GetJob()
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- accountId := neosyncdb.UUIDString(job.AccountID)
- _, err = s.verifyUserInAccount(ctx, accountId)
- if err != nil {
+ if err := user.EnforceJob(ctx, userdata.NewDomainEntity(job.GetAccountId(), job.GetId()), rbac.JobAction_Execute); err != nil {
return nil, err
}
logger.Debug("creating job run by triggering temporal schedule")
err = s.temporalmgr.TriggerSchedule(
ctx,
- neosyncdb.UUIDString(job.AccountID),
- neosyncdb.UUIDString(job.ID),
+ job.GetAccountId(),
+ job.GetId(),
&temporalclient.ScheduleTriggerOptions{},
logger,
)
@@ -267,7 +294,24 @@ func (s *Service) CancelJobRun(
"accountId", req.Msg.GetAccountId(),
"jobRunId", req.Msg.GetJobRunId(),
)
- err := s.temporalmgr.CancelWorkflow(ctx, req.Msg.GetAccountId(), req.Msg.GetJobRunId(), logger)
+
+ jobRunResp, err := s.GetJobRun(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRunRequest{
+ AccountId: req.Msg.GetAccountId(),
+ JobRunId: req.Msg.GetJobRunId(),
+ }))
+ if err != nil {
+ return nil, err
+ }
+ jobRun := jobRunResp.Msg.GetJobRun()
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceJob(ctx, userdata.NewDomainEntity(req.Msg.GetAccountId(), jobRun.GetJobId()), rbac.JobAction_Execute); err != nil {
+ return nil, err
+ }
+
+ err = s.temporalmgr.CancelWorkflow(ctx, req.Msg.GetAccountId(), req.Msg.GetJobRunId(), logger)
if err != nil {
return nil, fmt.Errorf("unable to cancel job run: %w", err)
}
@@ -283,7 +327,23 @@ func (s *Service) TerminateJobRun(
"accountId", req.Msg.GetAccountId(),
"jobRunId", req.Msg.GetJobRunId(),
)
- err := s.temporalmgr.TerminateWorkflow(ctx, req.Msg.GetAccountId(), req.Msg.GetJobRunId(), logger)
+ jobRunResp, err := s.GetJobRun(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRunRequest{
+ AccountId: req.Msg.GetAccountId(),
+ JobRunId: req.Msg.GetJobRunId(),
+ }))
+ if err != nil {
+ return nil, err
+ }
+ jobRun := jobRunResp.Msg.GetJobRun()
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceJob(ctx, userdata.NewDomainEntity(req.Msg.GetAccountId(), jobRun.GetJobId()), rbac.JobAction_Execute); err != nil {
+ return nil, err
+ }
+
+ err = s.temporalmgr.TerminateWorkflow(ctx, req.Msg.GetAccountId(), req.Msg.GetJobRunId(), logger)
if err != nil {
return nil, fmt.Errorf("unable to terminate job run: %w", err)
}
@@ -299,7 +359,23 @@ func (s *Service) DeleteJobRun(
"accountId", req.Msg.GetAccountId(),
"jobRunId", req.Msg.GetJobRunId(),
)
- err := s.temporalmgr.DeleteWorkflowExecution(ctx, req.Msg.GetAccountId(), req.Msg.GetJobRunId(), logger)
+ jobRunResp, err := s.GetJobRun(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRunRequest{
+ AccountId: req.Msg.GetAccountId(),
+ JobRunId: req.Msg.GetJobRunId(),
+ }))
+ if err != nil {
+ return nil, err
+ }
+ jobRun := jobRunResp.Msg.GetJobRun()
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceJob(ctx, userdata.NewDomainEntity(req.Msg.GetAccountId(), jobRun.GetJobId()), rbac.JobAction_Delete); err != nil {
+ return nil, err
+ }
+
+ err = s.temporalmgr.DeleteWorkflowExecution(ctx, req.Msg.GetAccountId(), req.Msg.GetJobRunId(), logger)
if err != nil {
return nil, fmt.Errorf("unable to delete job run: %w", err)
}
@@ -318,12 +394,28 @@ func (s *Service) GetJobRunLogsStream(
stream *connect.ServerStream[mgmtv1alpha1.GetJobRunLogsStreamResponse],
) error {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- logger = logger.With("jobRunId", req.Msg.JobRunId)
+ logger = logger.With("jobRunId", req.Msg.GetJobRunId())
if s.cfg.RunLogConfig == nil || !s.cfg.RunLogConfig.IsEnabled || s.cfg.RunLogConfig.RunLogType == nil {
return nucleuserrors.NewNotImplemented("job run logs streaming is not enabled. please configure or contact system administrator to enable logs.")
}
+ jobRunResp, err := s.GetJobRun(ctx, connect.NewRequest(&mgmtv1alpha1.GetJobRunRequest{
+ AccountId: req.Msg.GetAccountId(),
+ JobRunId: req.Msg.GetJobRunId(),
+ }))
+ if err != nil {
+ return err
+ }
+ jobRun := jobRunResp.Msg.GetJobRun()
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return err
+ }
+ if err := user.EnforceJob(ctx, userdata.NewDomainEntity(req.Msg.GetAccountId(), jobRun.GetJobId()), rbac.JobAction_View); err != nil {
+ return err
+ }
+
switch *s.cfg.RunLogConfig.RunLogType {
case KubePodRunLogType:
return s.streamK8sWorkerPodLogs(ctx, req, stream, logger)
@@ -543,7 +635,16 @@ func (s *Service) GetRunContext(
req *connect.Request[mgmtv1alpha1.GetRunContextRequest],
) (*connect.Response[mgmtv1alpha1.GetRunContextResponse], error) {
id := req.Msg.GetId()
- accountUuid, err := s.verifyUserInAccount(ctx, id.GetAccountId())
+
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(id.GetAccountId()), rbac.JobAction_View); err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(id.GetAccountId())
if err != nil {
return nil, err
}
@@ -551,7 +652,7 @@ func (s *Service) GetRunContext(
runContext, err := s.db.Q.GetRunContextByKey(ctx, s.db.Db, db_queries.GetRunContextByKeyParams{
WorkflowId: id.GetJobRunId(),
ExternalId: id.GetExternalId(),
- AccountId: *accountUuid,
+ AccountId: accountUuid,
})
if err != nil && !neosyncdb.IsNoRows(err) {
return nil, fmt.Errorf("unable to retrieve run context by key: %w", err)
@@ -569,36 +670,31 @@ func (s *Service) SetRunContext(
req *connect.Request[mgmtv1alpha1.SetRunContextRequest],
) (*connect.Response[mgmtv1alpha1.SetRunContextResponse], error) {
id := req.Msg.GetId()
- accountUuid, err := s.verifyUserInAccount(ctx, id.GetAccountId())
+
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- if s.cfg.IsNeosyncCloud && !isWorkerApiKey(ctx) {
+ if err := user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(id.GetAccountId()), rbac.JobAction_Edit); err != nil {
+ return nil, err
+ }
+
+ if s.cfg.IsNeosyncCloud && !user.IsWorkerApiKey() {
return nil, nucleuserrors.NewUnauthenticated("must provide valid authentication credentials for this endpoint")
}
- var userId *pgtype.UUID
- if isWorkerApiKey(ctx) {
- uid, err := neosyncdb.ToUuid("00000000-0000-0000-0000-000000000000")
- if err != nil {
- return nil, err
- }
- userId = &uid
- } else {
- userUuid, err := s.getUserUuid(ctx)
- if err != nil {
- return nil, err
- }
- userId = userUuid
+ accountUuid, err := neosyncdb.ToUuid(id.GetAccountId())
+ if err != nil {
+ return nil, err
}
err = s.db.Q.SetRunContext(ctx, s.db.Db, db_queries.SetRunContextParams{
WorkflowID: id.GetJobRunId(),
ExternalID: id.GetExternalId(),
- AccountID: *accountUuid,
+ AccountID: accountUuid,
Value: req.Msg.GetValue(),
- CreatedByID: *userId,
- UpdatedByID: *userId,
+ CreatedByID: user.PgId(),
+ UpdatedByID: user.PgId(),
})
if err != nil {
return nil, fmt.Errorf("unable to set run context: %w", err)
@@ -614,36 +710,30 @@ func (s *Service) SetRunContexts(
for stream.Receive() {
req := stream.Msg()
id := req.GetId()
- accountUuid, err := s.verifyUserInAccount(ctx, id.GetAccountId())
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- if s.cfg.IsNeosyncCloud && !isWorkerApiKey(ctx) {
+ if err := user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(id.GetAccountId()), rbac.JobAction_Edit); err != nil {
+ return nil, err
+ }
+
+ if s.cfg.IsNeosyncCloud && !user.IsWorkerApiKey() {
return nil, nucleuserrors.NewUnauthenticated("must provide valid authentication credentials for this endpoint")
}
- var userId *pgtype.UUID
- if isWorkerApiKey(ctx) {
- uid, err := neosyncdb.ToUuid("00000000-0000-0000-0000-000000000000")
- if err != nil {
- return nil, err
- }
- userId = &uid
- } else {
- userUuid, err := s.getUserUuid(ctx)
- if err != nil {
- return nil, err
- }
- userId = userUuid
+ accountUuid, err := neosyncdb.ToUuid(id.GetAccountId())
+ if err != nil {
+ return nil, err
}
err = s.db.Q.SetRunContext(ctx, s.db.Db, db_queries.SetRunContextParams{
WorkflowID: id.GetJobRunId(),
ExternalID: id.GetExternalId(),
- AccountID: *accountUuid,
+ AccountID: accountUuid,
Value: req.GetValue(),
- CreatedByID: *userId,
- UpdatedByID: *userId,
+ CreatedByID: user.PgId(),
+ UpdatedByID: user.PgId(),
})
if err != nil {
return nil, fmt.Errorf("unable to set run context: %w", err)
diff --git a/backend/services/mgmt/v1alpha1/job-service/service.go b/backend/services/mgmt/v1alpha1/job-service/service.go
index c54d1140e4..6b2e3fe330 100644
--- a/backend/services/mgmt/v1alpha1/job-service/service.go
+++ b/backend/services/mgmt/v1alpha1/job-service/service.go
@@ -5,15 +5,16 @@ import (
jobhooks "github.com/nucleuscloud/neosync/backend/internal/ee/hooks/jobs"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
clientmanager "github.com/nucleuscloud/neosync/backend/internal/temporal/clientmanager"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
sql_manager "github.com/nucleuscloud/neosync/backend/pkg/sqlmanager"
)
type Service struct {
- cfg *Config
- db *neosyncdb.NeosyncDb
- connectionService mgmtv1alpha1connect.ConnectionServiceClient
- useraccountService mgmtv1alpha1connect.UserAccountServiceClient
- sqlmanager sql_manager.SqlManagerClient
+ cfg *Config
+ db *neosyncdb.NeosyncDb
+ connectionService mgmtv1alpha1connect.ConnectionServiceClient
+ userdataclient userdata.Interface
+ sqlmanager sql_manager.SqlManagerClient
temporalmgr clientmanager.Interface
@@ -59,17 +60,17 @@ func New(
db *neosyncdb.NeosyncDb,
temporalWfManager clientmanager.Interface,
connectionService mgmtv1alpha1connect.ConnectionServiceClient,
- useraccountService mgmtv1alpha1connect.UserAccountServiceClient,
sqlmanager sql_manager.SqlManagerClient,
jobhookService jobhooks.Interface,
+ userdataclient userdata.Interface,
) *Service {
return &Service{
- cfg: cfg,
- db: db,
- temporalmgr: temporalWfManager,
- connectionService: connectionService,
- useraccountService: useraccountService,
- sqlmanager: sqlmanager,
- hookService: jobhookService,
+ cfg: cfg,
+ db: db,
+ temporalmgr: temporalWfManager,
+ connectionService: connectionService,
+ sqlmanager: sqlmanager,
+ hookService: jobhookService,
+ userdataclient: userdataclient,
}
}
diff --git a/backend/services/mgmt/v1alpha1/job-service/user-account.go b/backend/services/mgmt/v1alpha1/job-service/user-account.go
deleted file mode 100644
index 1a461c0729..0000000000
--- a/backend/services/mgmt/v1alpha1/job-service/user-account.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package v1alpha1_jobservice
-
-import (
- "context"
-
- "connectrpc.com/connect"
- "github.com/jackc/pgx/v5/pgtype"
- mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
- "github.com/nucleuscloud/neosync/backend/internal/apikey"
- auth_apikey "github.com/nucleuscloud/neosync/backend/internal/auth/apikey"
- nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
- "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
-)
-
-func (s *Service) verifyUserInAccount(
- ctx context.Context,
- accountId string,
-) (*pgtype.UUID, error) {
- accountUuid, err := neosyncdb.ToUuid(accountId)
- if err != nil {
- return nil, err
- }
-
- if isWorkerApiKey(ctx) {
- return &accountUuid, nil
- }
-
- resp, err := s.useraccountService.IsUserInAccount(ctx, connect.NewRequest(&mgmtv1alpha1.IsUserInAccountRequest{AccountId: accountId}))
- if err != nil {
- return nil, err
- }
- if !resp.Msg.Ok {
- return nil, nucleuserrors.NewForbidden("user in not in requested account")
- }
-
- return &accountUuid, nil
-}
-
-func isWorkerApiKey(ctx context.Context) bool {
- data, err := auth_apikey.GetTokenDataFromCtx(ctx)
- if err != nil {
- return false
- }
- return data.ApiKeyType == apikey.WorkerApiKey
-}
-
-func (s *Service) getUserUuid(
- ctx context.Context,
-) (*pgtype.UUID, error) {
- user, err := s.useraccountService.GetUser(ctx, connect.NewRequest(&mgmtv1alpha1.GetUserRequest{}))
- if err != nil {
- return nil, err
- }
- userUuid, err := neosyncdb.ToUuid(user.Msg.UserId)
- if err != nil {
- return nil, err
- }
- return &userUuid, nil
-}
diff --git a/backend/services/mgmt/v1alpha1/metrics-service/metrics.go b/backend/services/mgmt/v1alpha1/metrics-service/metrics.go
index 4a2cb9f741..10672b01c3 100644
--- a/backend/services/mgmt/v1alpha1/metrics-service/metrics.go
+++ b/backend/services/mgmt/v1alpha1/metrics-service/metrics.go
@@ -9,7 +9,9 @@ import (
"connectrpc.com/connect"
mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
logger_interceptor "github.com/nucleuscloud/neosync/backend/internal/connect/interceptors/logger"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
"github.com/nucleuscloud/neosync/backend/pkg/metrics"
)
@@ -49,7 +51,11 @@ func (s *Service) GetDailyMetricCount(
switch identifier := req.Msg.Identifier.(type) {
case *mgmtv1alpha1.GetDailyMetricCountRequest_AccountId:
- if _, err := s.verifyUserInAccount(ctx, identifier.AccountId); err != nil {
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(identifier.AccountId), rbac.AccountAction_View); err != nil {
return nil, err
}
queryLabels = append(queryLabels, metrics.NewEqLabel(metrics.AccountIdLabel, identifier.AccountId))
@@ -130,7 +136,11 @@ func (s *Service) GetMetricCount(
switch identifier := req.Msg.Identifier.(type) {
case *mgmtv1alpha1.GetMetricCountRequest_AccountId:
- if _, err := s.verifyUserInAccount(ctx, identifier.AccountId); err != nil {
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(identifier.AccountId), rbac.AccountAction_View); err != nil {
return nil, err
}
queryLabels = append(queryLabels, metrics.NewEqLabel(metrics.AccountIdLabel, identifier.AccountId))
diff --git a/backend/services/mgmt/v1alpha1/metrics-service/metrics_test.go b/backend/services/mgmt/v1alpha1/metrics-service/metrics_test.go
index 7f23d0ab3d..af8c12e01a 100644
--- a/backend/services/mgmt/v1alpha1/metrics-service/metrics_test.go
+++ b/backend/services/mgmt/v1alpha1/metrics-service/metrics_test.go
@@ -2,12 +2,14 @@ package v1alpha1_metricsservice
import (
"context"
+ "errors"
"testing"
"time"
"connectrpc.com/connect"
mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
"github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
promapiv1mock "github.com/nucleuscloud/neosync/internal/mocks/github.com/prometheus/client_golang/api/prometheus/v1"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/assert"
@@ -50,7 +52,7 @@ var (
func Test_GetMetricCount_Empty_Matrix(t *testing.T) {
m := createServiceMock(t, &Config{})
- mockIsUserInAccount(m.UserAccountServiceMock, true)
+ mockIsUserInAccount(t, m.UserServiceMock, true)
ctx := context.Background()
@@ -89,7 +91,7 @@ func Test_GetMetricCount_InvalidIdentifier(t *testing.T) {
func Test_GetMetricCount_AccountId(t *testing.T) {
m := createServiceMock(t, &Config{})
- mockIsUserInAccount(m.UserAccountServiceMock, true)
+ mockIsUserInAccount(t, m.UserServiceMock, true)
ctx := context.Background()
@@ -283,39 +285,45 @@ func Test_GetMetricCount_No_Metric(t *testing.T) {
}
type serviceMocks struct {
- Service *Service
- UserAccountServiceMock *mgmtv1alpha1connect.MockUserAccountServiceClient
- JobServiceMock *mgmtv1alpha1connect.MockJobServiceHandler
- PromApiMock *promapiv1mock.MockAPI
+ Service *Service
+ UserServiceMock *userdata.MockInterface
+ JobServiceMock *mgmtv1alpha1connect.MockJobServiceHandler
+ PromApiMock *promapiv1mock.MockAPI
}
func createServiceMock(t testing.TB, config *Config) *serviceMocks {
t.Helper()
- mockUserAccService := mgmtv1alpha1connect.NewMockUserAccountServiceClient(t)
+ mockUserService := userdata.NewMockInterface(t)
mockJobService := mgmtv1alpha1connect.NewMockJobServiceHandler(t)
mockPromApi := promapiv1mock.NewMockAPI(t)
- service := New(config, mockUserAccService, mockJobService, mockPromApi)
+ service := New(config, mockUserService, mockJobService, mockPromApi)
return &serviceMocks{
- Service: service,
- UserAccountServiceMock: mockUserAccService,
- JobServiceMock: mockJobService,
- PromApiMock: mockPromApi,
+ Service: service,
+ UserServiceMock: mockUserService,
+ JobServiceMock: mockJobService,
+ PromApiMock: mockPromApi,
}
}
//nolint:unparam
-func mockIsUserInAccount(userAccountServiceMock *mgmtv1alpha1connect.MockUserAccountServiceClient, isInAccount bool) {
- userAccountServiceMock.On("IsUserInAccount", mock.Anything, mock.Anything).Return(connect.NewResponse(&mgmtv1alpha1.IsUserInAccountResponse{
- Ok: isInAccount,
- }), nil)
+func mockIsUserInAccount(t testing.TB, userServiceMock *userdata.MockInterface, isInAccount bool) {
+ mockEntityEnforcer := userdata.NewMockEntityEnforcer(t)
+ if isInAccount {
+ mockEntityEnforcer.On("EnforceAccount", mock.Anything, mock.Anything, mock.Anything).Once().Return(nil)
+ } else {
+ mockEntityEnforcer.On("EnforceAccount", mock.Anything, mock.Anything, mock.Anything).Once().Return(errors.New("test: not in account"))
+ }
+ userServiceMock.On("GetUser", mock.Anything).Once().Return(&userdata.User{
+ EntityEnforcer: mockEntityEnforcer,
+ }, nil)
}
func Test_GetDailyMetricCount_Empty_Matrix(t *testing.T) {
m := createServiceMock(t, &Config{})
- mockIsUserInAccount(m.UserAccountServiceMock, true)
+ mockIsUserInAccount(t, m.UserServiceMock, true)
ctx := context.Background()
@@ -356,7 +364,7 @@ func Test_GetDailyMetricCount_InvalidIdentifier(t *testing.T) {
func Test_GetDailyMetricCount_AccountId(t *testing.T) {
m := createServiceMock(t, &Config{})
- mockIsUserInAccount(m.UserAccountServiceMock, true)
+ mockIsUserInAccount(t, m.UserServiceMock, true)
ctx := context.Background()
@@ -448,7 +456,7 @@ func Test_GetDailyMetricCount_RunId(t *testing.T) {
func Test_GetDailyMetricCount_MultipleDays(t *testing.T) {
m := createServiceMock(t, &Config{})
- mockIsUserInAccount(m.UserAccountServiceMock, true)
+ mockIsUserInAccount(t, m.UserServiceMock, true)
ctx := context.Background()
@@ -486,7 +494,7 @@ func Test_GetDailyMetricCount_MultipleDays(t *testing.T) {
func Test_GetDailyMetricCount_MultipleDays_Ordering(t *testing.T) {
m := createServiceMock(t, &Config{})
- mockIsUserInAccount(m.UserAccountServiceMock, true)
+ mockIsUserInAccount(t, m.UserServiceMock, true)
ctx := context.Background()
diff --git a/backend/services/mgmt/v1alpha1/metrics-service/service.go b/backend/services/mgmt/v1alpha1/metrics-service/service.go
index 536f57f5b4..1d4cb208a9 100644
--- a/backend/services/mgmt/v1alpha1/metrics-service/service.go
+++ b/backend/services/mgmt/v1alpha1/metrics-service/service.go
@@ -2,15 +2,16 @@ package v1alpha1_metricsservice
import (
"github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
promv1 "github.com/prometheus/client_golang/api/prometheus/v1"
)
type Service struct {
cfg *Config
- useraccountservice mgmtv1alpha1connect.UserAccountServiceClient
- jobservice mgmtv1alpha1connect.JobServiceHandler
- prometheusclient promv1.API
+ userdataclient userdata.Interface
+ jobservice mgmtv1alpha1connect.JobServiceHandler
+ prometheusclient promv1.API
}
type Config struct {
@@ -19,14 +20,14 @@ type Config struct {
func New(
cfg *Config,
- useraccountservice mgmtv1alpha1connect.UserAccountServiceClient,
+ userdataclient userdata.Interface,
jobservice mgmtv1alpha1connect.JobServiceHandler,
promclient promv1.API,
) *Service {
return &Service{
- cfg: cfg,
- useraccountservice: useraccountservice,
- jobservice: jobservice,
- prometheusclient: promclient,
+ cfg: cfg,
+ userdataclient: userdataclient,
+ jobservice: jobservice,
+ prometheusclient: promclient,
}
}
diff --git a/backend/services/mgmt/v1alpha1/metrics-service/user-account.go b/backend/services/mgmt/v1alpha1/metrics-service/user-account.go
deleted file mode 100644
index fabdc23114..0000000000
--- a/backend/services/mgmt/v1alpha1/metrics-service/user-account.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package v1alpha1_metricsservice
-
-import (
- "context"
-
- "connectrpc.com/connect"
- "github.com/jackc/pgx/v5/pgtype"
- mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
- "github.com/nucleuscloud/neosync/backend/internal/apikey"
- auth_apikey "github.com/nucleuscloud/neosync/backend/internal/auth/apikey"
- nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
- "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
-)
-
-//nolint:unparam
-func (s *Service) verifyUserInAccount(
- ctx context.Context,
- accountId string,
-) (*pgtype.UUID, error) {
- accountUuid, err := neosyncdb.ToUuid(accountId)
- if err != nil {
- return nil, err
- }
-
- if isWorkerApiKey(ctx) {
- return &accountUuid, nil
- }
-
- resp, err := s.useraccountservice.IsUserInAccount(ctx, connect.NewRequest(&mgmtv1alpha1.IsUserInAccountRequest{AccountId: accountId}))
- if err != nil {
- return nil, err
- }
- if !resp.Msg.Ok {
- return nil, nucleuserrors.NewForbidden("user in not in requested account")
- }
-
- return &accountUuid, nil
-}
-
-func isWorkerApiKey(ctx context.Context) bool {
- data, err := auth_apikey.GetTokenDataFromCtx(ctx)
- if err != nil {
- return false
- }
- return data.ApiKeyType == apikey.WorkerApiKey
-}
-
-// func (s *Service) getUserUuid(
-// ctx context.Context,
-// ) (*pgtype.UUID, error) {
-// user, err := s.useraccountservice.GetUser(ctx, connect.NewRequest(&mgmtv1alpha1.GetUserRequest{}))
-// if err != nil {
-// return nil, err
-// }
-// userUuid, err := neosyncdb.ToUuid(user.Msg.UserId)
-// if err != nil {
-// return nil, err
-// }
-// return &userUuid, nil
-// }
diff --git a/backend/services/mgmt/v1alpha1/transformers-service/entities.go b/backend/services/mgmt/v1alpha1/transformers-service/entities.go
index 8d17982b27..41f665d110 100644
--- a/backend/services/mgmt/v1alpha1/transformers-service/entities.go
+++ b/backend/services/mgmt/v1alpha1/transformers-service/entities.go
@@ -8,7 +8,9 @@ import (
"connectrpc.com/connect"
mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
"github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
presidioapi "github.com/nucleuscloud/neosync/internal/ee/presidio"
)
@@ -26,7 +28,11 @@ func (s *Service) GetTransformPiiEntities(
if s.entityclient == nil {
return nil, nucleuserrors.NewInternalError("entity service is enabled but client was nil.")
}
- _, err := s.verifyUserInAccount(ctx, req.Msg.GetAccountId())
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(req.Msg.GetAccountId()), rbac.JobAction_View)
if err != nil {
return nil, err
}
diff --git a/backend/services/mgmt/v1alpha1/transformers-service/service.go b/backend/services/mgmt/v1alpha1/transformers-service/service.go
index 56e3aa144c..37dfaf266a 100644
--- a/backend/services/mgmt/v1alpha1/transformers-service/service.go
+++ b/backend/services/mgmt/v1alpha1/transformers-service/service.go
@@ -1,16 +1,16 @@
package v1alpha1_transformersservice
import (
- "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
presidioapi "github.com/nucleuscloud/neosync/internal/ee/presidio"
)
type Service struct {
- cfg *Config
- db *neosyncdb.NeosyncDb
- useraccountService mgmtv1alpha1connect.UserAccountServiceClient
- entityclient presidioapi.EntityInterface
+ cfg *Config
+ db *neosyncdb.NeosyncDb
+ entityclient presidioapi.EntityInterface
+ userdataclient userdata.Interface
}
type Config struct {
@@ -21,13 +21,13 @@ type Config struct {
func New(
cfg *Config,
db *neosyncdb.NeosyncDb,
- useraccountService mgmtv1alpha1connect.UserAccountServiceClient,
recognizerclient presidioapi.EntityInterface,
+ userdataclient userdata.Interface,
) *Service {
return &Service{
- cfg: cfg,
- db: db,
- useraccountService: useraccountService,
- entityclient: recognizerclient,
+ cfg: cfg,
+ db: db,
+ entityclient: recognizerclient,
+ userdataclient: userdataclient,
}
}
diff --git a/backend/services/mgmt/v1alpha1/transformers-service/user-account.go b/backend/services/mgmt/v1alpha1/transformers-service/user-account.go
deleted file mode 100644
index 7b37ec17c9..0000000000
--- a/backend/services/mgmt/v1alpha1/transformers-service/user-account.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package v1alpha1_transformersservice
-
-import (
- "context"
-
- "connectrpc.com/connect"
- "github.com/jackc/pgx/v5/pgtype"
- mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
- "github.com/nucleuscloud/neosync/backend/internal/apikey"
- auth_apikey "github.com/nucleuscloud/neosync/backend/internal/auth/apikey"
- nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
- "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
-)
-
-func (s *Service) verifyUserInAccount(
- ctx context.Context,
- accountId string,
-) (*pgtype.UUID, error) {
- accountUuid, err := neosyncdb.ToUuid(accountId)
- if err != nil {
- return nil, err
- }
-
- if isWorkerApiKey(ctx) {
- return &accountUuid, nil
- }
-
- resp, err := s.useraccountService.IsUserInAccount(ctx, connect.NewRequest(&mgmtv1alpha1.IsUserInAccountRequest{AccountId: accountId}))
- if err != nil {
- return nil, err
- }
- if !resp.Msg.Ok {
- return nil, nucleuserrors.NewForbidden("user in not in requested account")
- }
-
- return &accountUuid, nil
-}
-
-func (s *Service) getUserUuid(
- ctx context.Context,
-) (*pgtype.UUID, error) {
- user, err := s.useraccountService.GetUser(ctx, connect.NewRequest(&mgmtv1alpha1.GetUserRequest{}))
- if err != nil {
- return nil, err
- }
- userUuid, err := neosyncdb.ToUuid(user.Msg.UserId)
- if err != nil {
- return nil, err
- }
- return &userUuid, nil
-}
-
-func isWorkerApiKey(ctx context.Context) bool {
- data, err := auth_apikey.GetTokenDataFromCtx(ctx)
- if err != nil {
- return false
- }
- return data.ApiKeyType == apikey.WorkerApiKey
-}
diff --git a/backend/services/mgmt/v1alpha1/transformers-service/userdefined_transformers.go b/backend/services/mgmt/v1alpha1/transformers-service/userdefined_transformers.go
index 395e51094a..9159df007b 100644
--- a/backend/services/mgmt/v1alpha1/transformers-service/userdefined_transformers.go
+++ b/backend/services/mgmt/v1alpha1/transformers-service/userdefined_transformers.go
@@ -11,8 +11,10 @@ import (
mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
logger_interceptor "github.com/nucleuscloud/neosync/backend/internal/connect/interceptors/logger"
"github.com/nucleuscloud/neosync/backend/internal/dtomaps"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
pg_models "github.com/nucleuscloud/neosync/backend/sql/postgresql/models"
)
@@ -20,12 +22,20 @@ func (s *Service) GetUserDefinedTransformers(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.GetUserDefinedTransformersRequest],
) (*connect.Response[mgmtv1alpha1.GetUserDefinedTransformersResponse], error) {
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(req.Msg.GetAccountId()), rbac.JobAction_View)
+ if err != nil {
+ return nil, err
+ }
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
- transformers, err := s.db.Q.GetUserDefinedTransformersByAccount(ctx, s.db.Db, *accountUuid)
+ transformers, err := s.db.Q.GetUserDefinedTransformersByAccount(ctx, s.db.Db, accountUuid)
if err != nil {
return nil, err
}
@@ -49,7 +59,7 @@ func (s *Service) GetUserDefinedTransformerById(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.GetUserDefinedTransformerByIdRequest],
) (*connect.Response[mgmtv1alpha1.GetUserDefinedTransformerByIdResponse], error) {
- tId, err := neosyncdb.ToUuid(req.Msg.TransformerId)
+ tId, err := neosyncdb.ToUuid(req.Msg.GetTransformerId())
if err != nil {
return nil, err
}
@@ -61,14 +71,18 @@ func (s *Service) GetUserDefinedTransformerById(
return nil, nucleuserrors.NewNotFound("unable to find transformer by id")
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(transformer.AccountID))
+ dto, err := dtomaps.ToUserDefinedTransformerDto(&transformer, s.getSystemTransformerSourceMap())
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("failed to map user defined transformer %s with source %d: %w", neosyncdb.UUIDString(transformer.ID), transformer.Source, err)
}
- dto, err := dtomaps.ToUserDefinedTransformerDto(&transformer, s.getSystemTransformerSourceMap())
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
- return nil, fmt.Errorf("failed to map user defined transformer %s with source %d: %w", neosyncdb.UUIDString(transformer.ID), transformer.Source, err)
+ return nil, err
+ }
+ err = user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(dto.GetAccountId()), rbac.JobAction_View)
+ if err != nil {
+ return nil, err
}
return connect.NewResponse(&mgmtv1alpha1.GetUserDefinedTransformerByIdResponse{
@@ -77,24 +91,27 @@ func (s *Service) GetUserDefinedTransformerById(
}
func (s *Service) CreateUserDefinedTransformer(ctx context.Context, req *connect.Request[mgmtv1alpha1.CreateUserDefinedTransformerRequest]) (*connect.Response[mgmtv1alpha1.CreateUserDefinedTransformerResponse], error) {
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
-
- userUuid, err := s.getUserUuid(ctx)
+ err = user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(req.Msg.GetAccountId()), rbac.JobAction_Edit)
+ if err != nil {
+ return nil, err
+ }
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
UserDefinedTransformer := &db_queries.CreateUserDefinedTransformerParams{
- AccountID: *accountUuid,
+ AccountID: accountUuid,
Name: req.Msg.Name,
Description: req.Msg.Description,
TransformerConfig: &pg_models.TransformerConfig{},
Source: int32(req.Msg.Source),
- CreatedByID: *userUuid,
- UpdatedByID: *userUuid,
+ CreatedByID: user.PgId(),
+ UpdatedByID: user.PgId(),
}
err = UserDefinedTransformer.TransformerConfig.FromTransformerConfigDto(req.Msg.TransformerConfig)
@@ -119,9 +136,9 @@ func (s *Service) CreateUserDefinedTransformer(ctx context.Context, req *connect
func (s *Service) DeleteUserDefinedTransformer(ctx context.Context, req *connect.Request[mgmtv1alpha1.DeleteUserDefinedTransformerRequest]) (*connect.Response[mgmtv1alpha1.DeleteUserDefinedTransformerResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- logger = logger.With("transformerId", req.Msg.TransformerId)
+ logger = logger.With("transformerId", req.Msg.GetTransformerId())
- tId, err := neosyncdb.ToUuid(req.Msg.TransformerId)
+ tId, err := neosyncdb.ToUuid(req.Msg.GetTransformerId())
if err != nil {
return nil, err
}
@@ -133,7 +150,11 @@ func (s *Service) DeleteUserDefinedTransformer(ctx context.Context, req *connect
return connect.NewResponse(&mgmtv1alpha1.DeleteUserDefinedTransformerResponse{}), nil
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(transformer.AccountID))
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(neosyncdb.UUIDString(transformer.AccountID)), rbac.JobAction_Delete)
if err != nil {
return nil, err
}
@@ -160,12 +181,11 @@ func (s *Service) UpdateUserDefinedTransformer(ctx context.Context, req *connect
return nil, nucleuserrors.NewNotFound("unable to find transformer by id")
}
- _, err = s.verifyUserInAccount(ctx, neosyncdb.UUIDString(transformer.AccountID))
+ user, err := s.userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
-
- userUuid, err := s.getUserUuid(ctx)
+ err = user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(neosyncdb.UUIDString(transformer.AccountID)), rbac.JobAction_Edit)
if err != nil {
return nil, err
}
@@ -174,11 +194,11 @@ func (s *Service) UpdateUserDefinedTransformer(ctx context.Context, req *connect
Name: req.Msg.Name,
Description: req.Msg.Description,
TransformerConfig: &pg_models.TransformerConfig{},
- UpdatedByID: *userUuid,
+ UpdatedByID: user.PgId(),
ID: tUuid,
}
// todo: must verify that this updated config is valid for the configured source
- err = updateParams.TransformerConfig.FromTransformerConfigDto(req.Msg.TransformerConfig)
+ err = updateParams.TransformerConfig.FromTransformerConfigDto(req.Msg.GetTransformerConfig())
if err != nil {
return nil, err
}
@@ -199,13 +219,21 @@ func (s *Service) UpdateUserDefinedTransformer(ctx context.Context, req *connect
}
func (s *Service) IsTransformerNameAvailable(ctx context.Context, req *connect.Request[mgmtv1alpha1.IsTransformerNameAvailableRequest]) (*connect.Response[mgmtv1alpha1.IsTransformerNameAvailableResponse], error) {
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ user, err := s.userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceJob(ctx, userdata.NewWildcardDomainEntity(req.Msg.GetAccountId()), rbac.JobAction_View)
+ if err != nil {
+ return nil, err
+ }
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
count, err := s.db.Q.IsTransformerNameAvailable(ctx, s.db.Db, db_queries.IsTransformerNameAvailableParams{
- AccountId: *accountUuid,
+ AccountId: accountUuid,
TransformerName: req.Msg.TransformerName,
})
if err != nil {
diff --git a/backend/services/mgmt/v1alpha1/user-account-service/account-onboarding.go b/backend/services/mgmt/v1alpha1/user-account-service/account-onboarding.go
index 2d59472c34..36d7720e94 100644
--- a/backend/services/mgmt/v1alpha1/user-account-service/account-onboarding.go
+++ b/backend/services/mgmt/v1alpha1/user-account-service/account-onboarding.go
@@ -6,6 +6,9 @@ import (
"connectrpc.com/connect"
db_queries "github.com/nucleuscloud/neosync/backend/gen/go/db"
mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
+ "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
pg_models "github.com/nucleuscloud/neosync/backend/sql/postgresql/models"
)
@@ -13,12 +16,22 @@ func (s *Service) GetAccountOnboardingConfig(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.GetAccountOnboardingConfigRequest],
) (*connect.Response[mgmtv1alpha1.GetAccountOnboardingConfigResponse], error) {
- accountId, err := s.verifyUserInAccount(ctx, req.Msg.GetAccountId())
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_View)
+ if err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
- oc, err := s.db.Q.GetAccountOnboardingConfig(ctx, s.db.Db, *accountId)
+ oc, err := s.db.Q.GetAccountOnboardingConfig(ctx, s.db.Db, accountUuid)
if err != nil {
return nil, err
}
@@ -32,7 +45,17 @@ func (s *Service) SetAccountOnboardingConfig(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.SetAccountOnboardingConfigRequest],
) (*connect.Response[mgmtv1alpha1.SetAccountOnboardingConfigResponse], error) {
- accountId, err := s.verifyUserInAccount(ctx, req.Msg.GetAccountId())
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_Edit)
+ if err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
@@ -47,7 +70,7 @@ func (s *Service) SetAccountOnboardingConfig(
account, err := s.db.Q.UpdateAccountOnboardingConfig(ctx, s.db.Db, db_queries.UpdateAccountOnboardingConfigParams{
OnboardingConfig: onboardingConfigModel,
- AccountId: *accountId,
+ AccountId: accountUuid,
})
if err != nil {
return nil, err
diff --git a/backend/services/mgmt/v1alpha1/user-account-service/account-temporal-config.go b/backend/services/mgmt/v1alpha1/user-account-service/account-temporal-config.go
index b2206d9d4e..8c50a81179 100644
--- a/backend/services/mgmt/v1alpha1/user-account-service/account-temporal-config.go
+++ b/backend/services/mgmt/v1alpha1/user-account-service/account-temporal-config.go
@@ -6,7 +6,10 @@ import (
"connectrpc.com/connect"
db_queries "github.com/nucleuscloud/neosync/backend/gen/go/db"
mgmtv1alpha1 "github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
+ "github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
pg_models "github.com/nucleuscloud/neosync/backend/sql/postgresql/models"
)
@@ -17,7 +20,12 @@ func (s *Service) GetAccountTemporalConfig(
if s.cfg.IsNeosyncCloud {
return nil, nucleuserrors.NewNotImplemented("not enabled in Neosync Cloud")
}
- _, err := s.verifyUserInAccount(ctx, req.Msg.GetAccountId())
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ err = user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_View)
if err != nil {
return nil, err
}
@@ -39,7 +47,18 @@ func (s *Service) SetAccountTemporalConfig(
if s.cfg.IsNeosyncCloud {
return nil, nucleuserrors.NewNotImplemented("not enabled in Neosync Cloud")
}
- accountUuid, err := s.verifyUserInAccount(ctx, req.Msg.GetAccountId())
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ err = user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_Edit)
+ if err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
@@ -54,7 +73,7 @@ func (s *Service) SetAccountTemporalConfig(
_, err = s.db.Q.UpdateTemporalConfigByAccount(ctx, s.db.Db, db_queries.UpdateTemporalConfigByAccountParams{
TemporalConfig: tc,
- AccountId: *accountUuid,
+ AccountId: accountUuid,
})
if err != nil {
return nil, err
diff --git a/backend/services/mgmt/v1alpha1/user-account-service/billing.go b/backend/services/mgmt/v1alpha1/user-account-service/billing.go
index 122bcf3465..0fe63dd5f2 100644
--- a/backend/services/mgmt/v1alpha1/user-account-service/billing.go
+++ b/backend/services/mgmt/v1alpha1/user-account-service/billing.go
@@ -14,8 +14,10 @@ import (
"github.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1/mgmtv1alpha1connect"
logger_interceptor "github.com/nucleuscloud/neosync/backend/internal/connect/interceptors/logger"
"github.com/nucleuscloud/neosync/backend/internal/dtomaps"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
"github.com/nucleuscloud/neosync/internal/billing"
"github.com/stripe/stripe-go/v79"
"google.golang.org/protobuf/types/known/timestamppb"
@@ -32,16 +34,27 @@ func (s *Service) GetAccountStatus(
) (*connect.Response[mgmtv1alpha1.GetAccountStatusResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- accountId, err := s.verifyUserInAccount(ctx, req.Msg.GetAccountId())
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- logger = logger.With("accountId", accountId)
+ err = user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_View)
+ if err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
+ if err != nil {
+ return nil, err
+ }
+
+ logger = logger.With("accountId", req.Msg.GetAccountId())
if !s.cfg.IsNeosyncCloud || s.billingclient == nil {
return connect.NewResponse(&mgmtv1alpha1.GetAccountStatusResponse{}), nil
}
- account, err := s.db.Q.GetAccount(ctx, s.db.Db, *accountId)
+ account, err := s.db.Q.GetAccount(ctx, s.db.Db, accountUuid)
if err != nil {
return nil, fmt.Errorf("unable to retrieve account: %w", err)
}
@@ -212,12 +225,21 @@ func (s *Service) GetAccountBillingCheckoutSession(
return nil, nucleuserrors.NewNotImplemented(fmt.Sprintf("%s is not implemented", strings.TrimPrefix(mgmtv1alpha1connect.UserAccountServiceGetAccountBillingCheckoutSessionProcedure, "/")))
}
logger = logger.With("accountId", req.Msg.GetAccountId())
- accountId, err := s.verifyUserInAccount(ctx, req.Msg.GetAccountId())
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- user, err := s.GetUser(ctx, connect.NewRequest(&mgmtv1alpha1.GetUserRequest{}))
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
+ if err != nil {
+ return nil, err
+ }
+
+ err = user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_Edit)
+ if err != nil {
+ return nil, err
+ }
if err != nil {
return nil, err
}
@@ -225,8 +247,8 @@ func (s *Service) GetAccountBillingCheckoutSession(
// retrieve the account, creates a customer id if one doesn't already exist
account, err := s.db.UpsertStripeCustomerId(
ctx,
- *accountId,
- s.getCreateStripeAccountFunction(user.Msg.GetUserId(), logger),
+ accountUuid,
+ s.getCreateStripeAccountFunction(user.Id(), logger),
logger,
)
if err != nil {
@@ -236,7 +258,7 @@ func (s *Service) GetAccountBillingCheckoutSession(
return nil, errors.New("stripe customer id does not exist on account after creation attempt")
}
- session, err := s.generateCheckoutSession(account.StripeCustomerID.String, account.AccountSlug, user.Msg.GetUserId(), logger)
+ session, err := s.generateCheckoutSession(account.StripeCustomerID.String, account.AccountSlug, user.Id(), logger)
if err != nil {
return nil, fmt.Errorf("unable to generate billing checkout session: %w", err)
}
@@ -253,12 +275,23 @@ func (s *Service) GetAccountBillingPortalSession(
if !s.cfg.IsNeosyncCloud || s.billingclient == nil {
return nil, nucleuserrors.NewNotImplemented(fmt.Sprintf("%s is not implemented", strings.TrimPrefix(mgmtv1alpha1connect.UserAccountServiceGetAccountBillingPortalSessionProcedure, "/")))
}
- accountId, err := s.verifyUserInAccount(ctx, req.Msg.GetAccountId())
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- account, err := s.db.Q.GetAccount(ctx, s.db.Db, *accountId)
+ err = user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_Edit)
+ if err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
+ if err != nil {
+ return nil, err
+ }
+
+ account, err := s.db.Q.GetAccount(ctx, s.db.Db, accountUuid)
if err != nil {
return nil, err
}
@@ -279,7 +312,12 @@ func (s *Service) GetBillingAccounts(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.GetBillingAccountsRequest],
) (*connect.Response[mgmtv1alpha1.GetBillingAccountsResponse], error) {
- if s.cfg.IsNeosyncCloud && !isWorkerApiKey(ctx) {
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if s.cfg.IsNeosyncCloud && !user.IsWorkerApiKey() {
return nil, nucleuserrors.NewUnauthorized("must provide valid authentication credentials for this endpoint")
}
@@ -312,7 +350,12 @@ func (s *Service) SetBillingMeterEvent(
if s.billingclient == nil {
return nil, nucleuserrors.NewUnauthorized("billing is not currently enabled")
}
- if s.cfg.IsNeosyncCloud && !isWorkerApiKey(ctx) {
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if s.cfg.IsNeosyncCloud && !user.IsWorkerApiKey() {
return nil, nucleuserrors.NewUnauthorized("must provide valid authentication credentials for this endpoint")
}
diff --git a/backend/services/mgmt/v1alpha1/user-account-service/service.go b/backend/services/mgmt/v1alpha1/user-account-service/service.go
index ebc06d107f..e401ea2d67 100644
--- a/backend/services/mgmt/v1alpha1/user-account-service/service.go
+++ b/backend/services/mgmt/v1alpha1/user-account-service/service.go
@@ -3,6 +3,7 @@ package v1alpha1_useraccountservice
import (
auth_client "github.com/nucleuscloud/neosync/backend/internal/auth/client"
"github.com/nucleuscloud/neosync/backend/internal/authmgmt"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
"github.com/nucleuscloud/neosync/backend/internal/temporal/clientmanager"
"github.com/nucleuscloud/neosync/internal/billing"
@@ -15,6 +16,7 @@ type Service struct {
authclient auth_client.Interface
authadminclient authmgmt.Interface
billingclient billing.Interface
+ rbacClient rbac.Interface
}
type Config struct {
@@ -30,6 +32,7 @@ func New(
authclient auth_client.Interface,
authadminclient authmgmt.Interface,
billingclient billing.Interface,
+ rbacClient rbac.Interface,
) *Service {
return &Service{
cfg: cfg,
@@ -38,5 +41,6 @@ func New(
authclient: authclient,
authadminclient: authadminclient,
billingclient: billingclient,
+ rbacClient: rbacClient,
}
}
diff --git a/backend/services/mgmt/v1alpha1/user-account-service/users.go b/backend/services/mgmt/v1alpha1/user-account-service/users.go
index 18016b4132..1dff668945 100644
--- a/backend/services/mgmt/v1alpha1/user-account-service/users.go
+++ b/backend/services/mgmt/v1alpha1/user-account-service/users.go
@@ -17,8 +17,10 @@ import (
"github.com/nucleuscloud/neosync/backend/internal/auth/tokenctx"
logger_interceptor "github.com/nucleuscloud/neosync/backend/internal/connect/interceptors/logger"
"github.com/nucleuscloud/neosync/backend/internal/dtomaps"
+ "github.com/nucleuscloud/neosync/backend/internal/ee/rbac"
nucleuserrors "github.com/nucleuscloud/neosync/backend/internal/errors"
"github.com/nucleuscloud/neosync/backend/internal/neosyncdb"
+ "github.com/nucleuscloud/neosync/backend/internal/userdata"
"github.com/nucleuscloud/neosync/backend/internal/version"
"github.com/nucleuscloud/neosync/internal/billing"
"github.com/stripe/stripe-go/v79"
@@ -63,6 +65,10 @@ func (s *Service) GetUser(
return connect.NewResponse(&mgmtv1alpha1.GetUserResponse{
UserId: neosyncdb.UUIDString(tokenctxResp.ApiKeyContextData.ApiKey.UserID),
}), nil
+ } else if tokenctxResp.ApiKeyContextData.ApiKeyType == apikey.WorkerApiKey {
+ return connect.NewResponse(&mgmtv1alpha1.GetUserResponse{
+ UserId: "00000000-0000-0000-0000-000000000000",
+ }), nil
}
return nil, nucleuserrors.NewUnauthenticated(fmt.Sprintf("invalid api key type when calling GetUser: %s", tokenctxResp.ApiKeyContextData.ApiKeyType))
} else if tokenctxResp.JwtContextData != nil {
@@ -136,7 +142,7 @@ func (s *Service) GetUserAccounts(
if err != nil {
return nil, err
}
- userId, err := neosyncdb.ToUuid(user.Msg.UserId)
+ userId, err := neosyncdb.ToUuid(user.Msg.GetUserId())
if err != nil {
return nil, err
}
@@ -194,6 +200,28 @@ func (s *Service) ConvertPersonalToTeamAccount(
break
}
}
+ } else {
+ personalAccountUuid, err := neosyncdb.ToUuid(personalAccountId)
+ if err != nil {
+ return nil, err
+ }
+ count, err := s.db.Q.IsUserInAccount(ctx, s.db.Db, db_queries.IsUserInAccountParams{
+ AccountId: personalAccountUuid,
+ UserId: userId,
+ })
+ if err != nil {
+ return nil, err
+ }
+ if count == 0 {
+ return nil, nucleuserrors.NewNotFound("user is not in the provided account")
+ }
+ account, err := s.db.Q.GetAccount(ctx, s.db.Db, personalAccountUuid)
+ if err != nil {
+ return nil, err
+ }
+ if account.AccountType != int16(neosyncdb.AccountType_Personal) {
+ return nil, nucleuserrors.NewNotFound("account is not a personal account")
+ }
}
personalAccountUuid, err := neosyncdb.ToUuid(personalAccountId)
@@ -245,7 +273,7 @@ func (s *Service) SetPersonalAccount(
return nil, err
}
- userId, err := neosyncdb.ToUuid(user.Msg.UserId)
+ userId, err := neosyncdb.ToUuid(user.Msg.GetUserId())
if err != nil {
return nil, err
}
@@ -255,6 +283,19 @@ func (s *Service) SetPersonalAccount(
return nil, err
}
+ logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
+ logger = logger.With("accountId", neosyncdb.UUIDString(account.ID), "userId", user.Msg.GetUserId())
+
+ if err := s.rbacClient.SetupNewAccount(ctx, neosyncdb.UUIDString(account.ID), logger); err != nil {
+ // note: if this fails the account is kind of in a broken state...
+ return nil, fmt.Errorf("unable to setup new account, please reach out to support for further assistance: %w", err)
+ }
+
+ if err := s.rbacClient.SetAccountRole(ctx, rbac.NewUserIdEntity(user.Msg.GetUserId()), rbac.NewAccountIdEntity(neosyncdb.UUIDString(account.ID)), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_ADMIN); err != nil {
+ // note: if this fails the account is kind of in a broken state...
+ return nil, fmt.Errorf("unable to set account role for user, please reach out to support for further assistance: %w", err)
+ }
+
return connect.NewResponse(&mgmtv1alpha1.SetPersonalAccountResponse{
AccountId: neosyncdb.UUIDString(account.ID),
}), nil
@@ -327,6 +368,8 @@ func (s *Service) CreateTeamAccount(
return nil, err
}
+ logger = logger.With("accountId", neosyncdb.UUIDString(account.ID))
+
var checkoutSessionUrl *string
if s.cfg.IsNeosyncCloud && !account.StripeCustomerID.Valid && s.billingclient != nil {
account, err = s.db.UpsertStripeCustomerId(
@@ -346,6 +389,16 @@ func (s *Service) CreateTeamAccount(
checkoutSessionUrl = &session.URL
}
+ if err := s.rbacClient.SetupNewAccount(ctx, neosyncdb.UUIDString(account.ID), logger); err != nil {
+ // note: if this fails the account is kind of in a broken state...
+ return nil, fmt.Errorf("unable to setup new account, please reach out to support for further assistance: %w", err)
+ }
+
+ if err := s.rbacClient.SetAccountRole(ctx, rbac.NewUserIdEntity(user.Msg.GetUserId()), rbac.NewAccountIdEntity(neosyncdb.UUIDString(account.ID)), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_ADMIN); err != nil {
+ // note: if this fails the account is kind of in a broken state...
+ return nil, fmt.Errorf("unable to set account role for user, please reach out to support for further assistance: %w", err)
+ }
+
return connect.NewResponse(&mgmtv1alpha1.CreateTeamAccountResponse{
AccountId: neosyncdb.UUIDString(account.ID),
CheckoutSessionUrl: checkoutSessionUrl,
@@ -401,16 +454,26 @@ func (s *Service) GetTeamAccountMembers(
req *connect.Request[mgmtv1alpha1.GetTeamAccountMembersRequest],
) (*connect.Response[mgmtv1alpha1.GetTeamAccountMembersResponse], error) {
logger := logger_interceptor.GetLoggerFromContextOrDefault(ctx)
- accountId, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_View); err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.AccountId)
if err != nil {
return nil, err
}
- if err := s.verifyTeamAccount(ctx, *accountId); err != nil {
+ if err := s.verifyTeamAccount(ctx, accountUuid); err != nil {
return nil, err
}
- userIdentities, err := s.db.Q.GetUserIdentitiesByTeamAccount(ctx, s.db.Db, *accountId)
+ userIdentities, err := s.db.Q.GetUserIdentitiesByTeamAccount(ctx, s.db.Db, accountUuid)
if err != nil {
return nil, err
}
@@ -455,11 +518,21 @@ func (s *Service) RemoveTeamAccountMember(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.RemoveTeamAccountMemberRequest],
) (*connect.Response[mgmtv1alpha1.RemoveTeamAccountMemberResponse], error) {
- accountId, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_Edit); err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
- if err := s.verifyTeamAccount(ctx, *accountId); err != nil {
+
+ if err := s.verifyTeamAccount(ctx, accountUuid); err != nil {
return nil, err
}
memberUserId, err := neosyncdb.ToUuid(req.Msg.UserId)
@@ -467,11 +540,15 @@ func (s *Service) RemoveTeamAccountMember(
return nil, err
}
err = s.db.Q.RemoveAccountUser(ctx, s.db.Db, db_queries.RemoveAccountUserParams{
- AccountId: *accountId,
+ AccountId: accountUuid,
UserId: memberUserId,
})
if err != nil && !neosyncdb.IsNoRows(err) {
- return nil, err
+ return nil, fmt.Errorf("unable to remove account user from db: %w", err)
+ }
+
+ if err := s.rbacClient.RemoveAccountUser(ctx, rbac.NewPgUserIdEntity(memberUserId), rbac.NewAccountIdEntity(neosyncdb.UUIDString(accountUuid))); err != nil {
+ return nil, fmt.Errorf("unable to remove account user from rbac engine: %w", err)
}
return connect.NewResponse(&mgmtv1alpha1.RemoveTeamAccountMemberResponse{}), nil
@@ -481,21 +558,21 @@ func (s *Service) InviteUserToTeamAccount(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.InviteUserToTeamAccountRequest],
) (*connect.Response[mgmtv1alpha1.InviteUserToTeamAccountResponse], error) {
- user, err := s.GetUser(ctx, connect.NewRequest(&mgmtv1alpha1.GetUserRequest{}))
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
- userId, err := neosyncdb.ToUuid(user.Msg.UserId)
- if err != nil {
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_Edit); err != nil {
return nil, err
}
- accountId, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
- if err := s.verifyTeamAccount(ctx, *accountId); err != nil {
+ if err := s.verifyTeamAccount(ctx, accountUuid); err != nil {
return nil, err
}
@@ -505,7 +582,9 @@ func (s *Service) InviteUserToTeamAccount(
return nil, err
}
- invite, err := s.db.CreateTeamAccountInvite(ctx, *accountId, userId, req.Msg.Email, expiresAt)
+ // todo: this method will need the intended role for the user
+
+ invite, err := s.db.CreateTeamAccountInvite(ctx, accountUuid, user.PgId(), req.Msg.GetEmail(), expiresAt)
if err != nil {
return nil, err
}
@@ -519,16 +598,25 @@ func (s *Service) GetTeamAccountInvites(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.GetTeamAccountInvitesRequest],
) (*connect.Response[mgmtv1alpha1.GetTeamAccountInvitesResponse], error) {
- accountId, err := s.verifyUserInAccount(ctx, req.Msg.AccountId)
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_View); err != nil {
+ return nil, err
+ }
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
return nil, err
}
- if err := s.verifyTeamAccount(ctx, *accountId); err != nil {
+ if err := s.verifyTeamAccount(ctx, accountUuid); err != nil {
return nil, err
}
- invites, err := s.db.Q.GetActiveAccountInvites(ctx, s.db.Db, *accountId)
+ invites, err := s.db.Q.GetActiveAccountInvites(ctx, s.db.Db, accountUuid)
if err != nil && !neosyncdb.IsNoRows(err) {
return nil, nucleuserrors.New(err)
} else if err != nil && neosyncdb.IsNoRows(err) {
@@ -551,7 +639,7 @@ func (s *Service) RemoveTeamAccountInvite(
ctx context.Context,
req *connect.Request[mgmtv1alpha1.RemoveTeamAccountInviteRequest],
) (*connect.Response[mgmtv1alpha1.RemoveTeamAccountInviteResponse], error) {
- inviteId, err := neosyncdb.ToUuid(req.Msg.Id)
+ inviteId, err := neosyncdb.ToUuid(req.Msg.GetId())
if err != nil {
return nil, err
}
@@ -561,20 +649,23 @@ func (s *Service) RemoveTeamAccountInvite(
} else if err != nil && neosyncdb.IsNoRows(err) {
return connect.NewResponse(&mgmtv1alpha1.RemoveTeamAccountInviteResponse{}), nil
}
- accountId, err := s.verifyUserInAccount(ctx, neosyncdb.UUIDString(invite.AccountID))
+
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
if err != nil {
return nil, err
}
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(neosyncdb.UUIDString(invite.AccountID)), rbac.AccountAction_Edit); err != nil {
+ return nil, err
+ }
- if err := s.verifyTeamAccount(ctx, *accountId); err != nil {
+ if err := s.verifyTeamAccount(ctx, invite.AccountID); err != nil {
return nil, err
}
err = s.db.Q.RemoveAccountInvite(ctx, s.db.Db, inviteId)
if err != nil && !neosyncdb.IsNoRows(err) {
return nil, nucleuserrors.New(err)
- } else if err != nil && neosyncdb.IsNoRows(err) {
- return connect.NewResponse(&mgmtv1alpha1.RemoveTeamAccountInviteResponse{}), nil
}
return connect.NewResponse(&mgmtv1alpha1.RemoveTeamAccountInviteResponse{}), nil
@@ -624,6 +715,11 @@ func (s *Service) AcceptTeamAccountInvite(
return nil, err
}
+ // todo: this should be updated to set the intended role based on what was configured in the invite
+ if err := s.rbacClient.SetAccountRole(ctx, rbac.NewUserIdEntity(user.Msg.GetUserId()), rbac.NewAccountIdEntity(neosyncdb.UUIDString(accountId)), mgmtv1alpha1.AccountRole_ACCOUNT_ROLE_ADMIN); err != nil {
+ return nil, fmt.Errorf("unable to set account role for user, please reach out to support for further assistance: %w", err)
+ }
+
if err := s.verifyTeamAccount(ctx, accountId); err != nil {
return nil, err
}
@@ -638,46 +734,58 @@ func (s *Service) AcceptTeamAccountInvite(
}), nil
}
-func (s *Service) verifyTeamAccount(ctx context.Context, accountId pgtype.UUID) error {
- account, err := s.db.Q.GetAccount(ctx, s.db.Db, accountId)
+func (s *Service) SetUserRole(
+ ctx context.Context,
+ req *connect.Request[mgmtv1alpha1.SetUserRoleRequest],
+) (*connect.Response[mgmtv1alpha1.SetUserRoleResponse], error) {
+ userdataclient := userdata.NewClient(s, s.rbacClient)
+ user, err := userdataclient.GetUser(ctx)
if err != nil {
- return err
+ return nil, err
}
- if account.AccountType != 1 {
- return nucleuserrors.NewForbidden("account is not a team account")
+
+ if err := user.EnforceAccount(ctx, userdata.NewIdentifier(req.Msg.GetAccountId()), rbac.AccountAction_Edit); err != nil {
+ return nil, err
}
- return nil
-}
-func isWorkerApiKey(ctx context.Context) bool {
- data, err := auth_apikey.GetTokenDataFromCtx(ctx)
+
+ accountUuid, err := neosyncdb.ToUuid(req.Msg.GetAccountId())
if err != nil {
- return false
+ return nil, err
}
- return data.ApiKeyType == apikey.WorkerApiKey
-}
-func (s *Service) verifyUserInAccount(
- ctx context.Context,
- accountId string,
-) (*pgtype.UUID, error) {
- accountUuid, err := neosyncdb.ToUuid(accountId)
+ requestingUserUuid, err := neosyncdb.ToUuid(req.Msg.GetUserId())
if err != nil {
return nil, err
}
- if isWorkerApiKey(ctx) {
- return &accountUuid, nil
+ count, err := s.db.Q.IsUserInAccount(ctx, s.db.Db, db_queries.IsUserInAccountParams{
+ AccountId: accountUuid,
+ UserId: requestingUserUuid,
+ })
+ if err != nil {
+ return nil, err
+ }
+ if count == 0 {
+ return nil, nucleuserrors.NewBadRequest("provided user id is not in account")
}
- resp, err := s.IsUserInAccount(ctx, connect.NewRequest(&mgmtv1alpha1.IsUserInAccountRequest{AccountId: accountId}))
+ err = s.rbacClient.SetAccountRole(ctx, rbac.NewPgUserIdEntity(requestingUserUuid), rbac.NewAccountIdEntity(req.Msg.GetAccountId()), req.Msg.GetRole())
if err != nil {
return nil, err
}
- if !resp.Msg.Ok {
- return nil, nucleuserrors.NewForbidden("user in not in requested account")
- }
- return &accountUuid, nil
+ return connect.NewResponse(&mgmtv1alpha1.SetUserRoleResponse{}), nil
+}
+
+func (s *Service) verifyTeamAccount(ctx context.Context, accountId pgtype.UUID) error {
+ account, err := s.db.Q.GetAccount(ctx, s.db.Db, accountId)
+ if err != nil {
+ return err
+ }
+ if account.AccountType != int16(neosyncdb.AccountType_Team) && account.AccountType != int16(neosyncdb.AccountType_Enterprise) {
+ return nucleuserrors.NewForbidden("account is not a team account")
+ }
+ return nil
}
func (s *Service) GetSystemInformation(ctx context.Context, req *connect.Request[mgmtv1alpha1.GetSystemInformationRequest]) (*connect.Response[mgmtv1alpha1.GetSystemInformationResponse], error) {
diff --git a/backend/sql/postgresql/queries/users.sql b/backend/sql/postgresql/queries/users.sql
index 5ac3c03265..534d4cfcdb 100644
--- a/backend/sql/postgresql/queries/users.sql
+++ b/backend/sql/postgresql/queries/users.sql
@@ -28,11 +28,19 @@ INSERT INTO neosync_api.users (
RETURNING *;
-- name: GetUserIdentitiesByTeamAccount :many
-SELECT aipa.* FROM neosync_api.user_identity_provider_associations aipa
-JOIN neosync_api.account_user_associations aua ON aua.user_id = aipa.user_id
-JOIN neosync_api.accounts a ON a.id = aua.account_id
+SELECT aipa.*
+FROM neosync_api.user_identity_provider_associations aipa
+INNER JOIN neosync_api.account_user_associations aua ON aua.user_id = aipa.user_id
+INNER JOIN neosync_api.accounts a ON a.id = aua.account_id
WHERE aua.account_id = sqlc.arg('accountId') AND a.account_type = 1;
+-- name: GetAccountUsers :many
+SELECT u.id
+FROM neosync_api.users u
+INNER JOIN neosync_api.account_user_associations aua ON aua.user_id = u.id
+INNER JOIN neosync_api.accounts a ON a.id = aua.account_id
+WHERE a.id = sqlc.arg('accountId') AND u.user_type = 0;
+
-- name: GetUserIdentityByUserId :one
SELECT aipa.* FROM neosync_api.user_identity_provider_associations aipa
WHERE aipa.user_id = $1;
@@ -226,3 +234,6 @@ SET account_slug = sqlc.arg('teamName'),
max_allowed_records = NULL
WHERE id = sqlc.arg('accountId')
RETURNING *;
+
+-- name: GetAccountIds :many
+SELECT id FROM neosync_api.accounts;
diff --git a/backend/sql/postgresql/schema/20241210211459_add-rbac-casbin.down.sql b/backend/sql/postgresql/schema/20241210211459_add-rbac-casbin.down.sql
new file mode 100644
index 0000000000..88cd884ff0
--- /dev/null
+++ b/backend/sql/postgresql/schema/20241210211459_add-rbac-casbin.down.sql
@@ -0,0 +1,2 @@
+DROP INDEX IF EXISTS neosync_api.idx_casbin_rule;
+DROP TABLE IF EXISTS neosync_api.casbin_rule;
diff --git a/backend/sql/postgresql/schema/20241210211459_add-rbac-casbin.up.sql b/backend/sql/postgresql/schema/20241210211459_add-rbac-casbin.up.sql
new file mode 100644
index 0000000000..5fce06e7e4
--- /dev/null
+++ b/backend/sql/postgresql/schema/20241210211459_add-rbac-casbin.up.sql
@@ -0,0 +1,19 @@
+CREATE TABLE IF NOT EXISTS neosync_api.casbin_rule(
+ p_type VARCHAR(32) DEFAULT '' NOT NULL,
+ v0 VARCHAR(255) DEFAULT '' NOT NULL,
+ v1 VARCHAR(255) DEFAULT '' NOT NULL,
+ v2 VARCHAR(255) DEFAULT '' NOT NULL,
+ v3 VARCHAR(255) DEFAULT '' NOT NULL,
+ v4 VARCHAR(255) DEFAULT '' NOT NULL,
+ v5 VARCHAR(255) DEFAULT '' NOT NULL,
+ created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+CREATE INDEX IF NOT EXISTS "idx_neosync_api_casbin_rule" ON neosync_api.casbin_rule (p_type, v0, v1);
+CREATE UNIQUE INDEX IF NOT EXISTS "idx_neosync_api_casbin_rule_unique_policy"
+ON neosync_api.casbin_rule (p_type, v0, v1, v2, v3, v4, v5);
+
+CREATE TRIGGER update_neosync_api_casbin_rule_updated_at
+BEFORE UPDATE ON neosync_api.casbin_rule
+FOR EACH ROW
+EXECUTE FUNCTION update_updated_at_column();
diff --git a/docs/openapi/mgmt/v1alpha1/user_account.openapi.yaml b/docs/openapi/mgmt/v1alpha1/user_account.openapi.yaml
index 2a10cfaa8b..21aa9b37a7 100644
--- a/docs/openapi/mgmt/v1alpha1/user_account.openapi.yaml
+++ b/docs/openapi/mgmt/v1alpha1/user_account.openapi.yaml
@@ -851,8 +851,53 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/mgmt.v1alpha1.SetBillingMeterEventResponse'
+ /mgmt.v1alpha1.UserAccountService/SetUserRole:
+ post:
+ tags:
+ - mgmt.v1alpha1.UserAccountService
+ summary: SetUserRole
+ description: Sets the users role
+ operationId: mgmt.v1alpha1.UserAccountService.SetUserRole
+ parameters:
+ - name: Connect-Protocol-Version
+ in: header
+ required: true
+ schema:
+ $ref: '#/components/schemas/connect-protocol-version'
+ - name: Connect-Timeout-Ms
+ in: header
+ schema:
+ $ref: '#/components/schemas/connect-timeout-header'
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/mgmt.v1alpha1.SetUserRoleRequest'
+ required: true
+ responses:
+ default:
+ description: Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/connect.error'
+ "200":
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/mgmt.v1alpha1.SetUserRoleResponse'
components:
schemas:
+ mgmt.v1alpha1.AccountRole:
+ type: string
+ title: AccountRole
+ enum:
+ - ACCOUNT_ROLE_UNSPECIFIED
+ - ACCOUNT_ROLE_ADMIN
+ - ACCOUNT_ROLE_JOB_DEVELOPER
+ - ACCOUNT_ROLE_JOB_VIEWER
+ - ACCOUNT_ROLE_JOB_EXECUTOR
mgmt.v1alpha1.AccountStatus:
type: string
title: AccountStatus
@@ -1701,6 +1746,30 @@ components:
title: user_id
title: SetUserResponse
additionalProperties: false
+ mgmt.v1alpha1.SetUserRoleRequest:
+ type: object
+ properties:
+ accountId:
+ type: string
+ title: account_id
+ format: uuid
+ description: The account id to apply this role to
+ userId:
+ type: string
+ title: user_id
+ format: uuid
+ description: The user that this will be applied to
+ role:
+ allOf:
+ - title: role
+ description: The role that this user will obtain
+ - $ref: '#/components/schemas/mgmt.v1alpha1.AccountRole'
+ title: SetUserRoleRequest
+ additionalProperties: false
+ mgmt.v1alpha1.SetUserRoleResponse:
+ type: object
+ title: SetUserRoleResponse
+ additionalProperties: false
mgmt.v1alpha1.UserAccount:
type: object
properties:
diff --git a/docs/openapi/neosync.mgmt.v1alpha1.yaml b/docs/openapi/neosync.mgmt.v1alpha1.yaml
index 089a5fb018..59dba30864 100644
--- a/docs/openapi/neosync.mgmt.v1alpha1.yaml
+++ b/docs/openapi/neosync.mgmt.v1alpha1.yaml
@@ -4020,6 +4020,43 @@ paths:
schema:
$ref: '#/components/schemas/connect.error'
security: []
+ /mgmt.v1alpha1.UserAccountService/SetUserRole:
+ post:
+ tags:
+ - mgmt.v1alpha1.UserAccountService
+ summary: SetUserRole
+ description: Sets the users role
+ operationId: mgmt.v1alpha1.UserAccountService.SetUserRole
+ parameters:
+ - name: Connect-Protocol-Version
+ in: header
+ required: true
+ schema:
+ $ref: '#/components/schemas/connect-protocol-version'
+ - name: Connect-Timeout-Ms
+ in: header
+ schema:
+ $ref: '#/components/schemas/connect-timeout-header'
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/mgmt.v1alpha1.SetUserRoleRequest'
+ required: true
+ responses:
+ '200':
+ description: Success
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/mgmt.v1alpha1.SetUserRoleResponse'
+ default:
+ description: Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/connect.error'
+ security: []
components:
schemas:
mgmt.v1alpha1.GenerateEmailType:
@@ -12256,6 +12293,15 @@ components:
title: valid
title: ValidateUserRegexCodeResponse
additionalProperties: false
+ mgmt.v1alpha1.AccountRole:
+ type: string
+ title: AccountRole
+ enum:
+ - ACCOUNT_ROLE_UNSPECIFIED
+ - ACCOUNT_ROLE_ADMIN
+ - ACCOUNT_ROLE_JOB_DEVELOPER
+ - ACCOUNT_ROLE_JOB_VIEWER
+ - ACCOUNT_ROLE_JOB_EXECUTOR
mgmt.v1alpha1.AccountStatus:
type: string
title: AccountStatus
@@ -13028,6 +13074,30 @@ components:
title: user_id
title: SetUserResponse
additionalProperties: false
+ mgmt.v1alpha1.SetUserRoleRequest:
+ type: object
+ properties:
+ accountId:
+ type: string
+ title: account_id
+ format: uuid
+ description: The account id to apply this role to
+ userId:
+ type: string
+ title: user_id
+ format: uuid
+ description: The user that this will be applied to
+ role:
+ allOf:
+ - title: role
+ description: The role that this user will obtain
+ - $ref: '#/components/schemas/mgmt.v1alpha1.AccountRole'
+ title: SetUserRoleRequest
+ additionalProperties: false
+ mgmt.v1alpha1.SetUserRoleResponse:
+ type: object
+ title: SetUserRoleResponse
+ additionalProperties: false
mgmt.v1alpha1.UserAccount:
type: object
properties:
diff --git a/docs/protos/mgmt/v1alpha1/user_account.proto.mdx b/docs/protos/mgmt/v1alpha1/user_account.proto.mdx
index 275a203157..393fd9277d 100644
--- a/docs/protos/mgmt/v1alpha1/user_account.proto.mdx
+++ b/docs/protos/mgmt/v1alpha1/user_account.proto.mdx
@@ -225,23 +225,35 @@ _**package** mgmt.v1alpha1_
+### `SetUserRoleRequest`
+
+
+
+### `SetUserRoleResponse`
+
+
+
### `UserAccount`
-
+
---
## Enums
+### `AccountRole`
+
+
+
### `AccountStatus`
-
+
### `BillingStatus`
-
+
### `UserAccountType`
-
+
---
## Services
@@ -348,6 +360,10 @@ _**package** mgmt.v1alpha1_
+#### `SetUserRole`
+
+
+
---
diff --git a/docs/protos/proto_docs.json b/docs/protos/proto_docs.json
index 09fbb1a405..ece92cc186 100644
--- a/docs/protos/proto_docs.json
+++ b/docs/protos/proto_docs.json
@@ -15690,6 +15690,39 @@
"hasMessages": true,
"hasServices": true,
"enums": [
+ {
+ "name": "AccountRole",
+ "longName": "AccountRole",
+ "fullName": "mgmt.v1alpha1.AccountRole",
+ "description": "",
+ "values": [
+ {
+ "name": "ACCOUNT_ROLE_UNSPECIFIED",
+ "number": "0",
+ "description": "Default value, this should not be used, but will default to ACCOUNT_ROLE_JOB_VIEWER"
+ },
+ {
+ "name": "ACCOUNT_ROLE_ADMIN",
+ "number": "1",
+ "description": "Admin, can do anything in the account."
+ },
+ {
+ "name": "ACCOUNT_ROLE_JOB_DEVELOPER",
+ "number": "2",
+ "description": "Can view, edit jobs and connections"
+ },
+ {
+ "name": "ACCOUNT_ROLE_JOB_VIEWER",
+ "number": "3",
+ "description": "Can view"
+ },
+ {
+ "name": "ACCOUNT_ROLE_JOB_EXECUTOR",
+ "number": "4",
+ "description": "Can view and execute"
+ }
+ ]
+ },
{
"name": "AccountStatus",
"longName": "AccountStatus",
@@ -17440,6 +17473,65 @@
}
]
},
+ {
+ "name": "SetUserRoleRequest",
+ "longName": "SetUserRoleRequest",
+ "fullName": "mgmt.v1alpha1.SetUserRoleRequest",
+ "description": "",
+ "hasExtensions": false,
+ "hasFields": true,
+ "hasOneofs": false,
+ "extensions": [],
+ "fields": [
+ {
+ "name": "account_id",
+ "description": "The account id to apply this role to",
+ "label": "",
+ "type": "string",
+ "longType": "string",
+ "fullType": "string",
+ "ismap": false,
+ "isoneof": false,
+ "oneofdecl": "",
+ "defaultValue": ""
+ },
+ {
+ "name": "user_id",
+ "description": "The user that this will be applied to",
+ "label": "",
+ "type": "string",
+ "longType": "string",
+ "fullType": "string",
+ "ismap": false,
+ "isoneof": false,
+ "oneofdecl": "",
+ "defaultValue": ""
+ },
+ {
+ "name": "role",
+ "description": "The role that this user will obtain",
+ "label": "",
+ "type": "AccountRole",
+ "longType": "AccountRole",
+ "fullType": "mgmt.v1alpha1.AccountRole",
+ "ismap": false,
+ "isoneof": false,
+ "oneofdecl": "",
+ "defaultValue": ""
+ }
+ ]
+ },
+ {
+ "name": "SetUserRoleResponse",
+ "longName": "SetUserRoleResponse",
+ "fullName": "mgmt.v1alpha1.SetUserRoleResponse",
+ "description": "",
+ "hasExtensions": false,
+ "hasFields": false,
+ "hasOneofs": false,
+ "extensions": [],
+ "fields": []
+ },
{
"name": "UserAccount",
"longName": "UserAccount",
@@ -17807,6 +17899,18 @@
"responseLongType": "SetBillingMeterEventResponse",
"responseFullType": "mgmt.v1alpha1.SetBillingMeterEventResponse",
"responseStreaming": false
+ },
+ {
+ "name": "SetUserRole",
+ "description": "Sets the users role",
+ "requestType": "SetUserRoleRequest",
+ "requestLongType": "SetUserRoleRequest",
+ "requestFullType": "mgmt.v1alpha1.SetUserRoleRequest",
+ "requestStreaming": false,
+ "responseType": "SetUserRoleResponse",
+ "responseLongType": "SetUserRoleResponse",
+ "responseFullType": "mgmt.v1alpha1.SetUserRoleResponse",
+ "responseStreaming": false
}
]
}
diff --git a/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account-UserAccountService_connectquery.ts b/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account-UserAccountService_connectquery.ts
index 8e3ef4775b..501ef5d663 100644
--- a/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account-UserAccountService_connectquery.ts
+++ b/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account-UserAccountService_connectquery.ts
@@ -4,7 +4,7 @@
// @ts-nocheck
import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf";
-import { AcceptTeamAccountInviteRequest, AcceptTeamAccountInviteResponse, ConvertPersonalToTeamAccountRequest, ConvertPersonalToTeamAccountResponse, CreateTeamAccountRequest, CreateTeamAccountResponse, GetAccountBillingCheckoutSessionRequest, GetAccountBillingCheckoutSessionResponse, GetAccountBillingPortalSessionRequest, GetAccountBillingPortalSessionResponse, GetAccountOnboardingConfigRequest, GetAccountOnboardingConfigResponse, GetAccountStatusRequest, GetAccountStatusResponse, GetAccountTemporalConfigRequest, GetAccountTemporalConfigResponse, GetBillingAccountsRequest, GetBillingAccountsResponse, GetSystemInformationRequest, GetSystemInformationResponse, GetTeamAccountInvitesRequest, GetTeamAccountInvitesResponse, GetTeamAccountMembersRequest, GetTeamAccountMembersResponse, GetUserAccountsRequest, GetUserAccountsResponse, GetUserRequest, GetUserResponse, InviteUserToTeamAccountRequest, InviteUserToTeamAccountResponse, IsAccountStatusValidRequest, IsAccountStatusValidResponse, IsUserInAccountRequest, IsUserInAccountResponse, RemoveTeamAccountInviteRequest, RemoveTeamAccountInviteResponse, RemoveTeamAccountMemberRequest, RemoveTeamAccountMemberResponse, SetAccountOnboardingConfigRequest, SetAccountOnboardingConfigResponse, SetAccountTemporalConfigRequest, SetAccountTemporalConfigResponse, SetBillingMeterEventRequest, SetBillingMeterEventResponse, SetPersonalAccountRequest, SetPersonalAccountResponse, SetUserRequest, SetUserResponse } from "./user_account_pb.js";
+import { AcceptTeamAccountInviteRequest, AcceptTeamAccountInviteResponse, ConvertPersonalToTeamAccountRequest, ConvertPersonalToTeamAccountResponse, CreateTeamAccountRequest, CreateTeamAccountResponse, GetAccountBillingCheckoutSessionRequest, GetAccountBillingCheckoutSessionResponse, GetAccountBillingPortalSessionRequest, GetAccountBillingPortalSessionResponse, GetAccountOnboardingConfigRequest, GetAccountOnboardingConfigResponse, GetAccountStatusRequest, GetAccountStatusResponse, GetAccountTemporalConfigRequest, GetAccountTemporalConfigResponse, GetBillingAccountsRequest, GetBillingAccountsResponse, GetSystemInformationRequest, GetSystemInformationResponse, GetTeamAccountInvitesRequest, GetTeamAccountInvitesResponse, GetTeamAccountMembersRequest, GetTeamAccountMembersResponse, GetUserAccountsRequest, GetUserAccountsResponse, GetUserRequest, GetUserResponse, InviteUserToTeamAccountRequest, InviteUserToTeamAccountResponse, IsAccountStatusValidRequest, IsAccountStatusValidResponse, IsUserInAccountRequest, IsUserInAccountResponse, RemoveTeamAccountInviteRequest, RemoveTeamAccountInviteResponse, RemoveTeamAccountMemberRequest, RemoveTeamAccountMemberResponse, SetAccountOnboardingConfigRequest, SetAccountOnboardingConfigResponse, SetAccountTemporalConfigRequest, SetAccountTemporalConfigResponse, SetBillingMeterEventRequest, SetBillingMeterEventResponse, SetPersonalAccountRequest, SetPersonalAccountResponse, SetUserRequest, SetUserResponse, SetUserRoleRequest, SetUserRoleResponse } from "./user_account_pb.js";
/**
* @generated from rpc mgmt.v1alpha1.UserAccountService.GetUser
@@ -361,3 +361,19 @@ export const setBillingMeterEvent = {
typeName: "mgmt.v1alpha1.UserAccountService"
}
} as const;
+
+/**
+ * Sets the users role
+ *
+ * @generated from rpc mgmt.v1alpha1.UserAccountService.SetUserRole
+ */
+export const setUserRole = {
+ localName: "setUserRole",
+ name: "SetUserRole",
+ kind: MethodKind.Unary,
+ I: SetUserRoleRequest,
+ O: SetUserRoleResponse,
+ service: {
+ typeName: "mgmt.v1alpha1.UserAccountService"
+ }
+} as const;
diff --git a/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account_connect.ts b/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account_connect.ts
index ecb51ea4f1..8ec3eb5d90 100644
--- a/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account_connect.ts
+++ b/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account_connect.ts
@@ -3,7 +3,7 @@
/* eslint-disable */
// @ts-nocheck
-import { AcceptTeamAccountInviteRequest, AcceptTeamAccountInviteResponse, ConvertPersonalToTeamAccountRequest, ConvertPersonalToTeamAccountResponse, CreateTeamAccountRequest, CreateTeamAccountResponse, GetAccountBillingCheckoutSessionRequest, GetAccountBillingCheckoutSessionResponse, GetAccountBillingPortalSessionRequest, GetAccountBillingPortalSessionResponse, GetAccountOnboardingConfigRequest, GetAccountOnboardingConfigResponse, GetAccountStatusRequest, GetAccountStatusResponse, GetAccountTemporalConfigRequest, GetAccountTemporalConfigResponse, GetBillingAccountsRequest, GetBillingAccountsResponse, GetSystemInformationRequest, GetSystemInformationResponse, GetTeamAccountInvitesRequest, GetTeamAccountInvitesResponse, GetTeamAccountMembersRequest, GetTeamAccountMembersResponse, GetUserAccountsRequest, GetUserAccountsResponse, GetUserRequest, GetUserResponse, InviteUserToTeamAccountRequest, InviteUserToTeamAccountResponse, IsAccountStatusValidRequest, IsAccountStatusValidResponse, IsUserInAccountRequest, IsUserInAccountResponse, RemoveTeamAccountInviteRequest, RemoveTeamAccountInviteResponse, RemoveTeamAccountMemberRequest, RemoveTeamAccountMemberResponse, SetAccountOnboardingConfigRequest, SetAccountOnboardingConfigResponse, SetAccountTemporalConfigRequest, SetAccountTemporalConfigResponse, SetBillingMeterEventRequest, SetBillingMeterEventResponse, SetPersonalAccountRequest, SetPersonalAccountResponse, SetUserRequest, SetUserResponse } from "./user_account_pb.js";
+import { AcceptTeamAccountInviteRequest, AcceptTeamAccountInviteResponse, ConvertPersonalToTeamAccountRequest, ConvertPersonalToTeamAccountResponse, CreateTeamAccountRequest, CreateTeamAccountResponse, GetAccountBillingCheckoutSessionRequest, GetAccountBillingCheckoutSessionResponse, GetAccountBillingPortalSessionRequest, GetAccountBillingPortalSessionResponse, GetAccountOnboardingConfigRequest, GetAccountOnboardingConfigResponse, GetAccountStatusRequest, GetAccountStatusResponse, GetAccountTemporalConfigRequest, GetAccountTemporalConfigResponse, GetBillingAccountsRequest, GetBillingAccountsResponse, GetSystemInformationRequest, GetSystemInformationResponse, GetTeamAccountInvitesRequest, GetTeamAccountInvitesResponse, GetTeamAccountMembersRequest, GetTeamAccountMembersResponse, GetUserAccountsRequest, GetUserAccountsResponse, GetUserRequest, GetUserResponse, InviteUserToTeamAccountRequest, InviteUserToTeamAccountResponse, IsAccountStatusValidRequest, IsAccountStatusValidResponse, IsUserInAccountRequest, IsUserInAccountResponse, RemoveTeamAccountInviteRequest, RemoveTeamAccountInviteResponse, RemoveTeamAccountMemberRequest, RemoveTeamAccountMemberResponse, SetAccountOnboardingConfigRequest, SetAccountOnboardingConfigResponse, SetAccountTemporalConfigRequest, SetAccountTemporalConfigResponse, SetBillingMeterEventRequest, SetBillingMeterEventResponse, SetPersonalAccountRequest, SetPersonalAccountResponse, SetUserRequest, SetUserResponse, SetUserRoleRequest, SetUserRoleResponse } from "./user_account_pb.js";
import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf";
/**
@@ -248,6 +248,17 @@ export const UserAccountService = {
O: SetBillingMeterEventResponse,
kind: MethodKind.Unary,
},
+ /**
+ * Sets the users role
+ *
+ * @generated from rpc mgmt.v1alpha1.UserAccountService.SetUserRole
+ */
+ setUserRole: {
+ name: "SetUserRole",
+ I: SetUserRoleRequest,
+ O: SetUserRoleResponse,
+ kind: MethodKind.Unary,
+ },
}
} as const;
diff --git a/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account_pb.ts b/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account_pb.ts
index 7e381ff0b0..bc232ea774 100644
--- a/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account_pb.ts
+++ b/frontend/packages/sdk/src/client/mgmt/v1alpha1/user_account_pb.ts
@@ -141,6 +141,54 @@ proto3.util.setEnumType(AccountStatus, "mgmt.v1alpha1.AccountStatus", [
{ no: 5, name: "ACCOUNT_STATUS_ACCOUNT_TRIAL_EXPIRED" },
]);
+/**
+ * @generated from enum mgmt.v1alpha1.AccountRole
+ */
+export enum AccountRole {
+ /**
+ * Default value, this should not be used, but will default to ACCOUNT_ROLE_JOB_VIEWER
+ *
+ * @generated from enum value: ACCOUNT_ROLE_UNSPECIFIED = 0;
+ */
+ UNSPECIFIED = 0,
+
+ /**
+ * Admin, can do anything in the account.
+ *
+ * @generated from enum value: ACCOUNT_ROLE_ADMIN = 1;
+ */
+ ADMIN = 1,
+
+ /**
+ * Can view, edit jobs and connections
+ *
+ * @generated from enum value: ACCOUNT_ROLE_JOB_DEVELOPER = 2;
+ */
+ JOB_DEVELOPER = 2,
+
+ /**
+ * Can view
+ *
+ * @generated from enum value: ACCOUNT_ROLE_JOB_VIEWER = 3;
+ */
+ JOB_VIEWER = 3,
+
+ /**
+ * Can view and execute
+ *
+ * @generated from enum value: ACCOUNT_ROLE_JOB_EXECUTOR = 4;
+ */
+ JOB_EXECUTOR = 4,
+}
+// Retrieve enum metadata with: proto3.getEnumType(AccountRole)
+proto3.util.setEnumType(AccountRole, "mgmt.v1alpha1.AccountRole", [
+ { no: 0, name: "ACCOUNT_ROLE_UNSPECIFIED" },
+ { no: 1, name: "ACCOUNT_ROLE_ADMIN" },
+ { no: 2, name: "ACCOUNT_ROLE_JOB_DEVELOPER" },
+ { no: 3, name: "ACCOUNT_ROLE_JOB_VIEWER" },
+ { no: 4, name: "ACCOUNT_ROLE_JOB_EXECUTOR" },
+]);
+
/**
* @generated from message mgmt.v1alpha1.GetUserRequest
*/
@@ -2403,3 +2451,89 @@ export class SetBillingMeterEventResponse extends Message {
+ /**
+ * The account id to apply this role to
+ *
+ * @generated from field: string account_id = 1;
+ */
+ accountId = "";
+
+ /**
+ * The user that this will be applied to
+ *
+ * @generated from field: string user_id = 2;
+ */
+ userId = "";
+
+ /**
+ * The role that this user will obtain
+ *
+ * @generated from field: mgmt.v1alpha1.AccountRole role = 3;
+ */
+ role = AccountRole.UNSPECIFIED;
+
+ constructor(data?: PartialMessage) {
+ super();
+ proto3.util.initPartial(data, this);
+ }
+
+ static readonly runtime: typeof proto3 = proto3;
+ static readonly typeName = "mgmt.v1alpha1.SetUserRoleRequest";
+ static readonly fields: FieldList = proto3.util.newFieldList(() => [
+ { no: 1, name: "account_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
+ { no: 2, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
+ { no: 3, name: "role", kind: "enum", T: proto3.getEnumType(AccountRole) },
+ ]);
+
+ static fromBinary(bytes: Uint8Array, options?: Partial): SetUserRoleRequest {
+ return new SetUserRoleRequest().fromBinary(bytes, options);
+ }
+
+ static fromJson(jsonValue: JsonValue, options?: Partial): SetUserRoleRequest {
+ return new SetUserRoleRequest().fromJson(jsonValue, options);
+ }
+
+ static fromJsonString(jsonString: string, options?: Partial): SetUserRoleRequest {
+ return new SetUserRoleRequest().fromJsonString(jsonString, options);
+ }
+
+ static equals(a: SetUserRoleRequest | PlainMessage | undefined, b: SetUserRoleRequest | PlainMessage | undefined): boolean {
+ return proto3.util.equals(SetUserRoleRequest, a, b);
+ }
+}
+
+/**
+ * @generated from message mgmt.v1alpha1.SetUserRoleResponse
+ */
+export class SetUserRoleResponse extends Message {
+ constructor(data?: PartialMessage) {
+ super();
+ proto3.util.initPartial(data, this);
+ }
+
+ static readonly runtime: typeof proto3 = proto3;
+ static readonly typeName = "mgmt.v1alpha1.SetUserRoleResponse";
+ static readonly fields: FieldList = proto3.util.newFieldList(() => [
+ ]);
+
+ static fromBinary(bytes: Uint8Array, options?: Partial): SetUserRoleResponse {
+ return new SetUserRoleResponse().fromBinary(bytes, options);
+ }
+
+ static fromJson(jsonValue: JsonValue, options?: Partial): SetUserRoleResponse {
+ return new SetUserRoleResponse().fromJson(jsonValue, options);
+ }
+
+ static fromJsonString(jsonString: string, options?: Partial): SetUserRoleResponse {
+ return new SetUserRoleResponse().fromJsonString(jsonString, options);
+ }
+
+ static equals(a: SetUserRoleResponse | PlainMessage | undefined, b: SetUserRoleResponse | PlainMessage | undefined): boolean {
+ return proto3.util.equals(SetUserRoleResponse, a, b);
+ }
+}
+
diff --git a/go.mod b/go.mod
index c197796649..feac9768e3 100644
--- a/go.mod
+++ b/go.mod
@@ -11,6 +11,7 @@ require (
connectrpc.com/otelconnect v0.7.1
github.com/Azure/azure-sdk-for-go/sdk/ai/azopenai v0.7.1
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0
+ github.com/Blank-Xu/sql-adapter v1.1.1
github.com/DATA-DOG/go-sqlmock v1.5.2
github.com/Jeffail/gabs/v2 v2.7.0
github.com/Jeffail/shutdown v1.0.0
@@ -25,6 +26,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sts v1.33.2
github.com/aws/smithy-go v1.22.1
github.com/bufbuild/protovalidate-go v0.7.3
+ github.com/casbin/casbin/v2 v2.102.0
github.com/cenkalti/backoff/v4 v4.3.0
github.com/charmbracelet/bubbles v0.20.0
github.com/charmbracelet/bubbletea v1.2.4
@@ -176,8 +178,10 @@ require (
github.com/aymerick/douceur v0.2.0 // indirect
github.com/benhoyt/goawk v1.25.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
+ github.com/bmatcuk/doublestar/v4 v4.7.1 // indirect
github.com/bufbuild/protocompile v0.8.0 // indirect
github.com/bwmarrin/snowflake v0.3.0 // indirect
+ github.com/casbin/govaluate v1.2.0 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/x/ansi v0.4.5 // indirect
@@ -208,7 +212,7 @@ require (
github.com/eapache/go-resiliency v1.5.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect
github.com/eapache/queue v1.1.0 // indirect
- github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 // indirect
+ github.com/edsrzf/mmap-go v1.0.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/envoyproxy/go-control-plane v0.13.1 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
diff --git a/go.sum b/go.sum
index 96ccc7953d..b60de0f197 100644
--- a/go.sum
+++ b/go.sum
@@ -747,6 +747,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9s
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.3 h1:6LyjnnaLpcOKK0fbYisI+mb8CE7iNe7i89nMNQxFxs8=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/Blank-Xu/sql-adapter v1.1.1 h1:+g7QXU9sl/qT6Po97teMpf3GjAO0X9aFaqgSePXvYko=
+github.com/Blank-Xu/sql-adapter v1.1.1/go.mod h1:o2g8EZhZ3TudnYEGDkoU+3jCTCgDgx1o/Ig5ajKkaLY=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4=
@@ -977,6 +979,9 @@ github.com/bits-and-blooms/bitset v1.4.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edY
github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
+github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
+github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
+github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bobg/gcsobj v0.1.2/go.mod h1:vS49EQ1A1Ib8FgrL58C8xXYZyOCR2TgzAdopy6/ipa8=
@@ -1008,6 +1013,11 @@ github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4Ho
github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
+github.com/casbin/casbin/v2 v2.100.0/go.mod h1:LO7YPez4dX3LgoTCqSQAleQDo0S0BeZBDxYnPUl95Ng=
+github.com/casbin/casbin/v2 v2.102.0 h1:weq9iSThUSL21SH3VrwoKa2DgRsaYMfjRNX/yOU3Foo=
+github.com/casbin/casbin/v2 v2.102.0/go.mod h1:LO7YPez4dX3LgoTCqSQAleQDo0S0BeZBDxYnPUl95Ng=
+github.com/casbin/govaluate v1.2.0 h1:wXCXFmqyY+1RwiKfYo3jMKyrtZmOL3kHwaqDyCPOYak=
+github.com/casbin/govaluate v1.2.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -1154,8 +1164,8 @@ github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
-github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8=
-github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
+github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/emicklei/proto v1.10.0 h1:pDGyFRVV5RvV+nkBK9iy3q67FBy9Xa7vwrOTE+g5aGw=
diff --git a/python/src/neosync/mgmt/v1alpha1/user_account_pb2.py b/python/src/neosync/mgmt/v1alpha1/user_account_pb2.py
index 8b162f29c9..ba8817d4dc 100644
--- a/python/src/neosync/mgmt/v1alpha1/user_account_pb2.py
+++ b/python/src/neosync/mgmt/v1alpha1/user_account_pb2.py
@@ -26,7 +26,7 @@
from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n mgmt/v1alpha1/user_account.proto\x12\rmgmt.v1alpha1\x1a\x1b\x62uf/validate/validate.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x10\n\x0eGetUserRequest\"*\n\x0fGetUserResponse\x12\x17\n\x07user_id\x18\x01 \x01(\tR\x06userId\"\x10\n\x0eSetUserRequest\"*\n\x0fSetUserResponse\x12\x17\n\x07user_id\x18\x01 \x01(\tR\x06userId\"\x18\n\x16GetUserAccountsRequest\"Q\n\x17GetUserAccountsResponse\x12\x36\n\x08\x61\x63\x63ounts\x18\x01 \x03(\x0b\x32\x1a.mgmt.v1alpha1.UserAccountR\x08\x61\x63\x63ounts\"\x9a\x01\n\x0bUserAccount\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x32\n\x04type\x18\x03 \x01(\x0e\x32\x1e.mgmt.v1alpha1.UserAccountTypeR\x04type\x12\x33\n\x16has_stripe_customer_id\x18\x04 \x01(\x08R\x13hasStripeCustomerId\"\x91\x01\n#ConvertPersonalToTeamAccountRequest\x12-\n\x04name\x18\x01 \x01(\tB\x19\xbaH\x16r\x14\x32\x12^[a-z0-9-]{3,100}$R\x04name\x12,\n\naccount_id\x18\x02 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01H\x00R\taccountId\x88\x01\x01\x42\r\n\x0b_account_id\"\xcc\x01\n$ConvertPersonalToTeamAccountResponse\x12\x1d\n\naccount_id\x18\x01 \x01(\tR\taccountId\x12\x35\n\x14\x63heckout_session_url\x18\x02 \x01(\tH\x00R\x12\x63heckoutSessionUrl\x88\x01\x01\x12\x35\n\x17new_personal_account_id\x18\x03 \x01(\tR\x14newPersonalAccountIdB\x17\n\x15_checkout_session_url\"\x1b\n\x19SetPersonalAccountRequest\";\n\x1aSetPersonalAccountResponse\x12\x1d\n\naccount_id\x18\x01 \x01(\tR\taccountId\"A\n\x16IsUserInAccountRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\")\n\x17IsUserInAccountResponse\x12\x0e\n\x02ok\x18\x01 \x01(\x08R\x02ok\"J\n\x1fGetAccountTemporalConfigRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"`\n GetAccountTemporalConfigResponse\x12<\n\x06\x63onfig\x18\x01 \x01(\x0b\x32$.mgmt.v1alpha1.AccountTemporalConfigR\x06\x63onfig\"\x88\x01\n\x1fSetAccountTemporalConfigRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\x12<\n\x06\x63onfig\x18\x02 \x01(\x0b\x32$.mgmt.v1alpha1.AccountTemporalConfigR\x06\x63onfig\"`\n SetAccountTemporalConfigResponse\x12<\n\x06\x63onfig\x18\x01 \x01(\x0b\x32$.mgmt.v1alpha1.AccountTemporalConfigR\x06\x63onfig\"\x91\x01\n\x15\x41\x63\x63ountTemporalConfig\x12\x19\n\x03url\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x03url\x12%\n\tnamespace\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\tnamespace\x12\x36\n\x13sync_job_queue_name\x18\x03 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x10syncJobQueueName\"I\n\x18\x43reateTeamAccountRequest\x12-\n\x04name\x18\x01 \x01(\tB\x19\xbaH\x16r\x14\x32\x12^[a-z0-9-]{3,100}$R\x04name\"\x8a\x01\n\x19\x43reateTeamAccountResponse\x12\x1d\n\naccount_id\x18\x01 \x01(\tR\taccountId\x12\x35\n\x14\x63heckout_session_url\x18\x02 \x01(\tH\x00R\x12\x63heckoutSessionUrl\x88\x01\x01\x42\x17\n\x15_checkout_session_url\"]\n\x0b\x41\x63\x63ountUser\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x14\n\x05image\x18\x03 \x01(\tR\x05image\x12\x14\n\x05\x65mail\x18\x04 \x01(\tR\x05\x65mail\"G\n\x1cGetTeamAccountMembersRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"Q\n\x1dGetTeamAccountMembersResponse\x12\x30\n\x05users\x18\x01 \x03(\x0b\x32\x1a.mgmt.v1alpha1.AccountUserR\x05users\"l\n\x1eRemoveTeamAccountMemberRequest\x12!\n\x07user_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\x06userId\x12\'\n\naccount_id\x18\x02 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"!\n\x1fRemoveTeamAccountMemberResponse\"h\n\x1eInviteUserToTeamAccountRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\x12\x1d\n\x05\x65mail\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x05\x65mail\"\xdd\x02\n\rAccountInvite\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n\naccount_id\x18\x02 \x01(\tR\taccountId\x12$\n\x0esender_user_id\x18\x03 \x01(\tR\x0csenderUserId\x12\x14\n\x05\x65mail\x18\x04 \x01(\tR\x05\x65mail\x12\x14\n\x05token\x18\x05 \x01(\tR\x05token\x12\x1a\n\x08\x61\x63\x63\x65pted\x18\x06 \x01(\x08R\x08\x61\x63\x63\x65pted\x12\x39\n\ncreated_at\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tcreatedAt\x12\x39\n\nupdated_at\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tupdatedAt\x12\x39\n\nexpires_at\x18\t \x01(\x0b\x32\x1a.google.protobuf.TimestampR\texpiresAt\"W\n\x1fInviteUserToTeamAccountResponse\x12\x34\n\x06invite\x18\x01 \x01(\x0b\x32\x1c.mgmt.v1alpha1.AccountInviteR\x06invite\"G\n\x1cGetTeamAccountInvitesRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"W\n\x1dGetTeamAccountInvitesResponse\x12\x36\n\x07invites\x18\x01 \x03(\x0b\x32\x1c.mgmt.v1alpha1.AccountInviteR\x07invites\":\n\x1eRemoveTeamAccountInviteRequest\x12\x18\n\x02id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\x02id\"!\n\x1fRemoveTeamAccountInviteResponse\"?\n\x1e\x41\x63\x63\x65ptTeamAccountInviteRequest\x12\x1d\n\x05token\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x05token\"W\n\x1f\x41\x63\x63\x65ptTeamAccountInviteResponse\x12\x34\n\x07\x61\x63\x63ount\x18\x01 \x01(\x0b\x32\x1a.mgmt.v1alpha1.UserAccountR\x07\x61\x63\x63ount\"\x1d\n\x1bGetSystemInformationRequest\"\xc3\x01\n\x1cGetSystemInformationResponse\x12\x18\n\x07version\x18\x01 \x01(\tR\x07version\x12\x16\n\x06\x63ommit\x18\x02 \x01(\tR\x06\x63ommit\x12\x1a\n\x08\x63ompiler\x18\x03 \x01(\tR\x08\x63ompiler\x12\x1a\n\x08platform\x18\x04 \x01(\tR\x08platform\x12\x39\n\nbuild_date\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tbuildDate\"L\n!GetAccountOnboardingConfigRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"d\n\"GetAccountOnboardingConfigResponse\x12>\n\x06\x63onfig\x18\x01 \x01(\x0b\x32&.mgmt.v1alpha1.AccountOnboardingConfigR\x06\x63onfig\"\x8c\x01\n!SetAccountOnboardingConfigRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\x12>\n\x06\x63onfig\x18\x02 \x01(\x0b\x32&.mgmt.v1alpha1.AccountOnboardingConfigR\x06\x63onfig\"d\n\"SetAccountOnboardingConfigResponse\x12>\n\x06\x63onfig\x18\x01 \x01(\x0b\x32&.mgmt.v1alpha1.AccountOnboardingConfigR\x06\x63onfig\"\xbb\x02\n\x17\x41\x63\x63ountOnboardingConfig\x12\x41\n\x1dhas_created_source_connection\x18\x01 \x01(\x08R\x1ahasCreatedSourceConnection\x12K\n\"has_created_destination_connection\x18\x02 \x01(\x08R\x1fhasCreatedDestinationConnection\x12&\n\x0fhas_created_job\x18\x03 \x01(\x08R\rhasCreatedJob\x12.\n\x13has_invited_members\x18\x04 \x01(\x08R\x11hasInvitedMembers\x12\x38\n\x18has_completed_onboarding\x18\x05 \x01(\x08R\x16hasCompletedOnboarding\"B\n\x17GetAccountStatusRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"\xe5\x01\n\x18GetAccountStatusResponse\x12*\n\x11used_record_count\x18\x01 \x01(\x04R\x0fusedRecordCount\x12\x35\n\x14\x61llowed_record_count\x18\x02 \x01(\x04H\x00R\x12\x61llowedRecordCount\x88\x01\x01\x12M\n\x13subscription_status\x18\x03 \x01(\x0e\x32\x1c.mgmt.v1alpha1.BillingStatusR\x12subscriptionStatusB\x17\n\x15_allowed_record_count\"\x9c\x01\n\x1bIsAccountStatusValidRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\x12\x39\n\x16requested_record_count\x18\x02 \x01(\x04H\x00R\x14requestedRecordCount\x88\x01\x01\x42\x19\n\x17_requested_record_count\"\xa3\x03\n\x1cIsAccountStatusValidResponse\x12\x19\n\x08is_valid\x18\x01 \x01(\x08R\x07isValid\x12\x1b\n\x06reason\x18\x02 \x01(\tH\x00R\x06reason\x88\x01\x01\x12\x1f\n\x0bshould_poll\x18\x03 \x01(\x08R\nshouldPoll\x12*\n\x11used_record_count\x18\x04 \x01(\x04R\x0fusedRecordCount\x12\x35\n\x14\x61llowed_record_count\x18\x05 \x01(\x04H\x01R\x12\x61llowedRecordCount\x88\x01\x01\x12\x43\n\x0e\x61\x63\x63ount_status\x18\x06 \x01(\x0e\x32\x1c.mgmt.v1alpha1.AccountStatusR\raccountStatus\x12I\n\x10trial_expires_at\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x02R\x0etrialExpiresAt\x88\x01\x01\x42\t\n\x07_reasonB\x17\n\x15_allowed_record_countB\x13\n\x11_trial_expires_at\"R\n\'GetAccountBillingCheckoutSessionRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"\\\n(GetAccountBillingCheckoutSessionResponse\x12\x30\n\x14\x63heckout_session_url\x18\x01 \x01(\tR\x12\x63heckoutSessionUrl\"P\n%GetAccountBillingPortalSessionRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"V\n&GetAccountBillingPortalSessionResponse\x12,\n\x12portal_session_url\x18\x01 \x01(\tR\x10portalSessionUrl\"<\n\x19GetBillingAccountsRequest\x12\x1f\n\x0b\x61\x63\x63ount_ids\x18\x01 \x03(\tR\naccountIds\"T\n\x1aGetBillingAccountsResponse\x12\x36\n\x08\x61\x63\x63ounts\x18\x01 \x03(\x0b\x32\x1a.mgmt.v1alpha1.UserAccountR\x08\x61\x63\x63ounts\"\xe2\x01\n\x1bSetBillingMeterEventRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\x12&\n\nevent_name\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\teventName\x12\x1d\n\x05value\x18\x03 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x05value\x12\"\n\x08\x65vent_id\x18\x04 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x07\x65ventId\x12!\n\ttimestamp\x18\x05 \x01(\x04H\x00R\ttimestamp\x88\x01\x01\x42\x0c\n\n_timestamp\"\x1e\n\x1cSetBillingMeterEventResponse*\x92\x01\n\x0fUserAccountType\x12!\n\x1dUSER_ACCOUNT_TYPE_UNSPECIFIED\x10\x00\x12\x1e\n\x1aUSER_ACCOUNT_TYPE_PERSONAL\x10\x01\x12\x1a\n\x16USER_ACCOUNT_TYPE_TEAM\x10\x02\x12 \n\x1cUSER_ACCOUNT_TYPE_ENTERPRISE\x10\x03*\xa9\x01\n\rBillingStatus\x12\x1e\n\x1a\x42ILLING_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15\x42ILLING_STATUS_ACTIVE\x10\x01\x12\x1a\n\x16\x42ILLING_STATUS_EXPIRED\x10\x02\x12\x1f\n\x1b\x42ILLING_STATUS_TRIAL_ACTIVE\x10\x03\x12 \n\x1c\x42ILLING_STATUS_TRIAL_EXPIRED\x10\x04*\x8c\x02\n\rAccountStatus\x12%\n!ACCOUNT_STATUS_REASON_UNSPECIFIED\x10\x00\x12(\n$ACCOUNT_STATUS_EXCEEDS_ALLOWED_LIMIT\x10\x01\x12*\n&ACCOUNT_STATUS_REQUESTED_EXCEEDS_LIMIT\x10\x02\x12+\n\'ACCOUNT_STATUS_ACCOUNT_IN_EXPIRED_STATE\x10\x03\x12\'\n#ACCOUNT_STATUS_ACCOUNT_TRIAL_ACTIVE\x10\x04\x12(\n$ACCOUNT_STATUS_ACCOUNT_TRIAL_EXPIRED\x10\x05\x32\xa0\x16\n\x12UserAccountService\x12J\n\x07GetUser\x12\x1d.mgmt.v1alpha1.GetUserRequest\x1a\x1e.mgmt.v1alpha1.GetUserResponse\"\x00\x12J\n\x07SetUser\x12\x1d.mgmt.v1alpha1.SetUserRequest\x1a\x1e.mgmt.v1alpha1.SetUserResponse\"\x00\x12\x62\n\x0fGetUserAccounts\x12%.mgmt.v1alpha1.GetUserAccountsRequest\x1a&.mgmt.v1alpha1.GetUserAccountsResponse\"\x00\x12k\n\x12SetPersonalAccount\x12(.mgmt.v1alpha1.SetPersonalAccountRequest\x1a).mgmt.v1alpha1.SetPersonalAccountResponse\"\x00\x12\x89\x01\n\x1c\x43onvertPersonalToTeamAccount\x12\x32.mgmt.v1alpha1.ConvertPersonalToTeamAccountRequest\x1a\x33.mgmt.v1alpha1.ConvertPersonalToTeamAccountResponse\"\x00\x12h\n\x11\x43reateTeamAccount\x12\'.mgmt.v1alpha1.CreateTeamAccountRequest\x1a(.mgmt.v1alpha1.CreateTeamAccountResponse\"\x00\x12\x62\n\x0fIsUserInAccount\x12%.mgmt.v1alpha1.IsUserInAccountRequest\x1a&.mgmt.v1alpha1.IsUserInAccountResponse\"\x00\x12}\n\x18GetAccountTemporalConfig\x12..mgmt.v1alpha1.GetAccountTemporalConfigRequest\x1a/.mgmt.v1alpha1.GetAccountTemporalConfigResponse\"\x00\x12}\n\x18SetAccountTemporalConfig\x12..mgmt.v1alpha1.SetAccountTemporalConfigRequest\x1a/.mgmt.v1alpha1.SetAccountTemporalConfigResponse\"\x00\x12t\n\x15GetTeamAccountMembers\x12+.mgmt.v1alpha1.GetTeamAccountMembersRequest\x1a,.mgmt.v1alpha1.GetTeamAccountMembersResponse\"\x00\x12z\n\x17RemoveTeamAccountMember\x12-.mgmt.v1alpha1.RemoveTeamAccountMemberRequest\x1a..mgmt.v1alpha1.RemoveTeamAccountMemberResponse\"\x00\x12z\n\x17InviteUserToTeamAccount\x12-.mgmt.v1alpha1.InviteUserToTeamAccountRequest\x1a..mgmt.v1alpha1.InviteUserToTeamAccountResponse\"\x00\x12t\n\x15GetTeamAccountInvites\x12+.mgmt.v1alpha1.GetTeamAccountInvitesRequest\x1a,.mgmt.v1alpha1.GetTeamAccountInvitesResponse\"\x00\x12z\n\x17RemoveTeamAccountInvite\x12-.mgmt.v1alpha1.RemoveTeamAccountInviteRequest\x1a..mgmt.v1alpha1.RemoveTeamAccountInviteResponse\"\x00\x12z\n\x17\x41\x63\x63\x65ptTeamAccountInvite\x12-.mgmt.v1alpha1.AcceptTeamAccountInviteRequest\x1a..mgmt.v1alpha1.AcceptTeamAccountInviteResponse\"\x00\x12t\n\x14GetSystemInformation\x12*.mgmt.v1alpha1.GetSystemInformationRequest\x1a+.mgmt.v1alpha1.GetSystemInformationResponse\"\x03\x90\x02\x01\x12\x83\x01\n\x1aGetAccountOnboardingConfig\x12\x30.mgmt.v1alpha1.GetAccountOnboardingConfigRequest\x1a\x31.mgmt.v1alpha1.GetAccountOnboardingConfigResponse\"\x00\x12\x83\x01\n\x1aSetAccountOnboardingConfig\x12\x30.mgmt.v1alpha1.SetAccountOnboardingConfigRequest\x1a\x31.mgmt.v1alpha1.SetAccountOnboardingConfigResponse\"\x00\x12h\n\x10GetAccountStatus\x12&.mgmt.v1alpha1.GetAccountStatusRequest\x1a\'.mgmt.v1alpha1.GetAccountStatusResponse\"\x03\x90\x02\x01\x12t\n\x14IsAccountStatusValid\x12*.mgmt.v1alpha1.IsAccountStatusValidRequest\x1a+.mgmt.v1alpha1.IsAccountStatusValidResponse\"\x03\x90\x02\x01\x12\x95\x01\n GetAccountBillingCheckoutSession\x12\x36.mgmt.v1alpha1.GetAccountBillingCheckoutSessionRequest\x1a\x37.mgmt.v1alpha1.GetAccountBillingCheckoutSessionResponse\"\x00\x12\x8f\x01\n\x1eGetAccountBillingPortalSession\x12\x34.mgmt.v1alpha1.GetAccountBillingPortalSessionRequest\x1a\x35.mgmt.v1alpha1.GetAccountBillingPortalSessionResponse\"\x00\x12n\n\x12GetBillingAccounts\x12(.mgmt.v1alpha1.GetBillingAccountsRequest\x1a).mgmt.v1alpha1.GetBillingAccountsResponse\"\x03\x90\x02\x01\x12q\n\x14SetBillingMeterEvent\x12*.mgmt.v1alpha1.SetBillingMeterEventRequest\x1a+.mgmt.v1alpha1.SetBillingMeterEventResponse\"\x00\x42\xcc\x01\n\x11\x63om.mgmt.v1alpha1B\x10UserAccountProtoP\x01ZPgithub.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1;mgmtv1alpha1\xa2\x02\x03MXX\xaa\x02\rMgmt.V1alpha1\xca\x02\rMgmt\\V1alpha1\xe2\x02\x19Mgmt\\V1alpha1\\GPBMetadata\xea\x02\x0eMgmt::V1alpha1b\x06proto3')
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n mgmt/v1alpha1/user_account.proto\x12\rmgmt.v1alpha1\x1a\x1b\x62uf/validate/validate.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x10\n\x0eGetUserRequest\"*\n\x0fGetUserResponse\x12\x17\n\x07user_id\x18\x01 \x01(\tR\x06userId\"\x10\n\x0eSetUserRequest\"*\n\x0fSetUserResponse\x12\x17\n\x07user_id\x18\x01 \x01(\tR\x06userId\"\x18\n\x16GetUserAccountsRequest\"Q\n\x17GetUserAccountsResponse\x12\x36\n\x08\x61\x63\x63ounts\x18\x01 \x03(\x0b\x32\x1a.mgmt.v1alpha1.UserAccountR\x08\x61\x63\x63ounts\"\x9a\x01\n\x0bUserAccount\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x32\n\x04type\x18\x03 \x01(\x0e\x32\x1e.mgmt.v1alpha1.UserAccountTypeR\x04type\x12\x33\n\x16has_stripe_customer_id\x18\x04 \x01(\x08R\x13hasStripeCustomerId\"\x91\x01\n#ConvertPersonalToTeamAccountRequest\x12-\n\x04name\x18\x01 \x01(\tB\x19\xbaH\x16r\x14\x32\x12^[a-z0-9-]{3,100}$R\x04name\x12,\n\naccount_id\x18\x02 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01H\x00R\taccountId\x88\x01\x01\x42\r\n\x0b_account_id\"\xcc\x01\n$ConvertPersonalToTeamAccountResponse\x12\x1d\n\naccount_id\x18\x01 \x01(\tR\taccountId\x12\x35\n\x14\x63heckout_session_url\x18\x02 \x01(\tH\x00R\x12\x63heckoutSessionUrl\x88\x01\x01\x12\x35\n\x17new_personal_account_id\x18\x03 \x01(\tR\x14newPersonalAccountIdB\x17\n\x15_checkout_session_url\"\x1b\n\x19SetPersonalAccountRequest\";\n\x1aSetPersonalAccountResponse\x12\x1d\n\naccount_id\x18\x01 \x01(\tR\taccountId\"A\n\x16IsUserInAccountRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\")\n\x17IsUserInAccountResponse\x12\x0e\n\x02ok\x18\x01 \x01(\x08R\x02ok\"J\n\x1fGetAccountTemporalConfigRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"`\n GetAccountTemporalConfigResponse\x12<\n\x06\x63onfig\x18\x01 \x01(\x0b\x32$.mgmt.v1alpha1.AccountTemporalConfigR\x06\x63onfig\"\x88\x01\n\x1fSetAccountTemporalConfigRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\x12<\n\x06\x63onfig\x18\x02 \x01(\x0b\x32$.mgmt.v1alpha1.AccountTemporalConfigR\x06\x63onfig\"`\n SetAccountTemporalConfigResponse\x12<\n\x06\x63onfig\x18\x01 \x01(\x0b\x32$.mgmt.v1alpha1.AccountTemporalConfigR\x06\x63onfig\"\x91\x01\n\x15\x41\x63\x63ountTemporalConfig\x12\x19\n\x03url\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x03url\x12%\n\tnamespace\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\tnamespace\x12\x36\n\x13sync_job_queue_name\x18\x03 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x10syncJobQueueName\"I\n\x18\x43reateTeamAccountRequest\x12-\n\x04name\x18\x01 \x01(\tB\x19\xbaH\x16r\x14\x32\x12^[a-z0-9-]{3,100}$R\x04name\"\x8a\x01\n\x19\x43reateTeamAccountResponse\x12\x1d\n\naccount_id\x18\x01 \x01(\tR\taccountId\x12\x35\n\x14\x63heckout_session_url\x18\x02 \x01(\tH\x00R\x12\x63heckoutSessionUrl\x88\x01\x01\x42\x17\n\x15_checkout_session_url\"]\n\x0b\x41\x63\x63ountUser\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x14\n\x05image\x18\x03 \x01(\tR\x05image\x12\x14\n\x05\x65mail\x18\x04 \x01(\tR\x05\x65mail\"G\n\x1cGetTeamAccountMembersRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"Q\n\x1dGetTeamAccountMembersResponse\x12\x30\n\x05users\x18\x01 \x03(\x0b\x32\x1a.mgmt.v1alpha1.AccountUserR\x05users\"l\n\x1eRemoveTeamAccountMemberRequest\x12!\n\x07user_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\x06userId\x12\'\n\naccount_id\x18\x02 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"!\n\x1fRemoveTeamAccountMemberResponse\"h\n\x1eInviteUserToTeamAccountRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\x12\x1d\n\x05\x65mail\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x05\x65mail\"\xdd\x02\n\rAccountInvite\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n\naccount_id\x18\x02 \x01(\tR\taccountId\x12$\n\x0esender_user_id\x18\x03 \x01(\tR\x0csenderUserId\x12\x14\n\x05\x65mail\x18\x04 \x01(\tR\x05\x65mail\x12\x14\n\x05token\x18\x05 \x01(\tR\x05token\x12\x1a\n\x08\x61\x63\x63\x65pted\x18\x06 \x01(\x08R\x08\x61\x63\x63\x65pted\x12\x39\n\ncreated_at\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tcreatedAt\x12\x39\n\nupdated_at\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tupdatedAt\x12\x39\n\nexpires_at\x18\t \x01(\x0b\x32\x1a.google.protobuf.TimestampR\texpiresAt\"W\n\x1fInviteUserToTeamAccountResponse\x12\x34\n\x06invite\x18\x01 \x01(\x0b\x32\x1c.mgmt.v1alpha1.AccountInviteR\x06invite\"G\n\x1cGetTeamAccountInvitesRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"W\n\x1dGetTeamAccountInvitesResponse\x12\x36\n\x07invites\x18\x01 \x03(\x0b\x32\x1c.mgmt.v1alpha1.AccountInviteR\x07invites\":\n\x1eRemoveTeamAccountInviteRequest\x12\x18\n\x02id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\x02id\"!\n\x1fRemoveTeamAccountInviteResponse\"?\n\x1e\x41\x63\x63\x65ptTeamAccountInviteRequest\x12\x1d\n\x05token\x18\x01 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x05token\"W\n\x1f\x41\x63\x63\x65ptTeamAccountInviteResponse\x12\x34\n\x07\x61\x63\x63ount\x18\x01 \x01(\x0b\x32\x1a.mgmt.v1alpha1.UserAccountR\x07\x61\x63\x63ount\"\x1d\n\x1bGetSystemInformationRequest\"\xc3\x01\n\x1cGetSystemInformationResponse\x12\x18\n\x07version\x18\x01 \x01(\tR\x07version\x12\x16\n\x06\x63ommit\x18\x02 \x01(\tR\x06\x63ommit\x12\x1a\n\x08\x63ompiler\x18\x03 \x01(\tR\x08\x63ompiler\x12\x1a\n\x08platform\x18\x04 \x01(\tR\x08platform\x12\x39\n\nbuild_date\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.TimestampR\tbuildDate\"L\n!GetAccountOnboardingConfigRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"d\n\"GetAccountOnboardingConfigResponse\x12>\n\x06\x63onfig\x18\x01 \x01(\x0b\x32&.mgmt.v1alpha1.AccountOnboardingConfigR\x06\x63onfig\"\x8c\x01\n!SetAccountOnboardingConfigRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\x12>\n\x06\x63onfig\x18\x02 \x01(\x0b\x32&.mgmt.v1alpha1.AccountOnboardingConfigR\x06\x63onfig\"d\n\"SetAccountOnboardingConfigResponse\x12>\n\x06\x63onfig\x18\x01 \x01(\x0b\x32&.mgmt.v1alpha1.AccountOnboardingConfigR\x06\x63onfig\"\xbb\x02\n\x17\x41\x63\x63ountOnboardingConfig\x12\x41\n\x1dhas_created_source_connection\x18\x01 \x01(\x08R\x1ahasCreatedSourceConnection\x12K\n\"has_created_destination_connection\x18\x02 \x01(\x08R\x1fhasCreatedDestinationConnection\x12&\n\x0fhas_created_job\x18\x03 \x01(\x08R\rhasCreatedJob\x12.\n\x13has_invited_members\x18\x04 \x01(\x08R\x11hasInvitedMembers\x12\x38\n\x18has_completed_onboarding\x18\x05 \x01(\x08R\x16hasCompletedOnboarding\"B\n\x17GetAccountStatusRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"\xe5\x01\n\x18GetAccountStatusResponse\x12*\n\x11used_record_count\x18\x01 \x01(\x04R\x0fusedRecordCount\x12\x35\n\x14\x61llowed_record_count\x18\x02 \x01(\x04H\x00R\x12\x61llowedRecordCount\x88\x01\x01\x12M\n\x13subscription_status\x18\x03 \x01(\x0e\x32\x1c.mgmt.v1alpha1.BillingStatusR\x12subscriptionStatusB\x17\n\x15_allowed_record_count\"\x9c\x01\n\x1bIsAccountStatusValidRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\x12\x39\n\x16requested_record_count\x18\x02 \x01(\x04H\x00R\x14requestedRecordCount\x88\x01\x01\x42\x19\n\x17_requested_record_count\"\xa3\x03\n\x1cIsAccountStatusValidResponse\x12\x19\n\x08is_valid\x18\x01 \x01(\x08R\x07isValid\x12\x1b\n\x06reason\x18\x02 \x01(\tH\x00R\x06reason\x88\x01\x01\x12\x1f\n\x0bshould_poll\x18\x03 \x01(\x08R\nshouldPoll\x12*\n\x11used_record_count\x18\x04 \x01(\x04R\x0fusedRecordCount\x12\x35\n\x14\x61llowed_record_count\x18\x05 \x01(\x04H\x01R\x12\x61llowedRecordCount\x88\x01\x01\x12\x43\n\x0e\x61\x63\x63ount_status\x18\x06 \x01(\x0e\x32\x1c.mgmt.v1alpha1.AccountStatusR\raccountStatus\x12I\n\x10trial_expires_at\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x02R\x0etrialExpiresAt\x88\x01\x01\x42\t\n\x07_reasonB\x17\n\x15_allowed_record_countB\x13\n\x11_trial_expires_at\"R\n\'GetAccountBillingCheckoutSessionRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"\\\n(GetAccountBillingCheckoutSessionResponse\x12\x30\n\x14\x63heckout_session_url\x18\x01 \x01(\tR\x12\x63heckoutSessionUrl\"P\n%GetAccountBillingPortalSessionRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\"V\n&GetAccountBillingPortalSessionResponse\x12,\n\x12portal_session_url\x18\x01 \x01(\tR\x10portalSessionUrl\"<\n\x19GetBillingAccountsRequest\x12\x1f\n\x0b\x61\x63\x63ount_ids\x18\x01 \x03(\tR\naccountIds\"T\n\x1aGetBillingAccountsResponse\x12\x36\n\x08\x61\x63\x63ounts\x18\x01 \x03(\x0b\x32\x1a.mgmt.v1alpha1.UserAccountR\x08\x61\x63\x63ounts\"\xe2\x01\n\x1bSetBillingMeterEventRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\x12&\n\nevent_name\x18\x02 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\teventName\x12\x1d\n\x05value\x18\x03 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x05value\x12\"\n\x08\x65vent_id\x18\x04 \x01(\tB\x07\xbaH\x04r\x02\x10\x01R\x07\x65ventId\x12!\n\ttimestamp\x18\x05 \x01(\x04H\x00R\ttimestamp\x88\x01\x01\x42\x0c\n\n_timestamp\"\x1e\n\x1cSetBillingMeterEventResponse\"\x90\x01\n\x12SetUserRoleRequest\x12\'\n\naccount_id\x18\x01 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\taccountId\x12!\n\x07user_id\x18\x02 \x01(\tB\x08\xbaH\x05r\x03\xb0\x01\x01R\x06userId\x12.\n\x04role\x18\x03 \x01(\x0e\x32\x1a.mgmt.v1alpha1.AccountRoleR\x04role\"\x15\n\x13SetUserRoleResponse*\x92\x01\n\x0fUserAccountType\x12!\n\x1dUSER_ACCOUNT_TYPE_UNSPECIFIED\x10\x00\x12\x1e\n\x1aUSER_ACCOUNT_TYPE_PERSONAL\x10\x01\x12\x1a\n\x16USER_ACCOUNT_TYPE_TEAM\x10\x02\x12 \n\x1cUSER_ACCOUNT_TYPE_ENTERPRISE\x10\x03*\xa9\x01\n\rBillingStatus\x12\x1e\n\x1a\x42ILLING_STATUS_UNSPECIFIED\x10\x00\x12\x19\n\x15\x42ILLING_STATUS_ACTIVE\x10\x01\x12\x1a\n\x16\x42ILLING_STATUS_EXPIRED\x10\x02\x12\x1f\n\x1b\x42ILLING_STATUS_TRIAL_ACTIVE\x10\x03\x12 \n\x1c\x42ILLING_STATUS_TRIAL_EXPIRED\x10\x04*\x8c\x02\n\rAccountStatus\x12%\n!ACCOUNT_STATUS_REASON_UNSPECIFIED\x10\x00\x12(\n$ACCOUNT_STATUS_EXCEEDS_ALLOWED_LIMIT\x10\x01\x12*\n&ACCOUNT_STATUS_REQUESTED_EXCEEDS_LIMIT\x10\x02\x12+\n\'ACCOUNT_STATUS_ACCOUNT_IN_EXPIRED_STATE\x10\x03\x12\'\n#ACCOUNT_STATUS_ACCOUNT_TRIAL_ACTIVE\x10\x04\x12(\n$ACCOUNT_STATUS_ACCOUNT_TRIAL_EXPIRED\x10\x05*\x9f\x01\n\x0b\x41\x63\x63ountRole\x12\x1c\n\x18\x41\x43\x43OUNT_ROLE_UNSPECIFIED\x10\x00\x12\x16\n\x12\x41\x43\x43OUNT_ROLE_ADMIN\x10\x01\x12\x1e\n\x1a\x41\x43\x43OUNT_ROLE_JOB_DEVELOPER\x10\x02\x12\x1b\n\x17\x41\x43\x43OUNT_ROLE_JOB_VIEWER\x10\x03\x12\x1d\n\x19\x41\x43\x43OUNT_ROLE_JOB_EXECUTOR\x10\x04\x32\xf8\x16\n\x12UserAccountService\x12J\n\x07GetUser\x12\x1d.mgmt.v1alpha1.GetUserRequest\x1a\x1e.mgmt.v1alpha1.GetUserResponse\"\x00\x12J\n\x07SetUser\x12\x1d.mgmt.v1alpha1.SetUserRequest\x1a\x1e.mgmt.v1alpha1.SetUserResponse\"\x00\x12\x62\n\x0fGetUserAccounts\x12%.mgmt.v1alpha1.GetUserAccountsRequest\x1a&.mgmt.v1alpha1.GetUserAccountsResponse\"\x00\x12k\n\x12SetPersonalAccount\x12(.mgmt.v1alpha1.SetPersonalAccountRequest\x1a).mgmt.v1alpha1.SetPersonalAccountResponse\"\x00\x12\x89\x01\n\x1c\x43onvertPersonalToTeamAccount\x12\x32.mgmt.v1alpha1.ConvertPersonalToTeamAccountRequest\x1a\x33.mgmt.v1alpha1.ConvertPersonalToTeamAccountResponse\"\x00\x12h\n\x11\x43reateTeamAccount\x12\'.mgmt.v1alpha1.CreateTeamAccountRequest\x1a(.mgmt.v1alpha1.CreateTeamAccountResponse\"\x00\x12\x62\n\x0fIsUserInAccount\x12%.mgmt.v1alpha1.IsUserInAccountRequest\x1a&.mgmt.v1alpha1.IsUserInAccountResponse\"\x00\x12}\n\x18GetAccountTemporalConfig\x12..mgmt.v1alpha1.GetAccountTemporalConfigRequest\x1a/.mgmt.v1alpha1.GetAccountTemporalConfigResponse\"\x00\x12}\n\x18SetAccountTemporalConfig\x12..mgmt.v1alpha1.SetAccountTemporalConfigRequest\x1a/.mgmt.v1alpha1.SetAccountTemporalConfigResponse\"\x00\x12t\n\x15GetTeamAccountMembers\x12+.mgmt.v1alpha1.GetTeamAccountMembersRequest\x1a,.mgmt.v1alpha1.GetTeamAccountMembersResponse\"\x00\x12z\n\x17RemoveTeamAccountMember\x12-.mgmt.v1alpha1.RemoveTeamAccountMemberRequest\x1a..mgmt.v1alpha1.RemoveTeamAccountMemberResponse\"\x00\x12z\n\x17InviteUserToTeamAccount\x12-.mgmt.v1alpha1.InviteUserToTeamAccountRequest\x1a..mgmt.v1alpha1.InviteUserToTeamAccountResponse\"\x00\x12t\n\x15GetTeamAccountInvites\x12+.mgmt.v1alpha1.GetTeamAccountInvitesRequest\x1a,.mgmt.v1alpha1.GetTeamAccountInvitesResponse\"\x00\x12z\n\x17RemoveTeamAccountInvite\x12-.mgmt.v1alpha1.RemoveTeamAccountInviteRequest\x1a..mgmt.v1alpha1.RemoveTeamAccountInviteResponse\"\x00\x12z\n\x17\x41\x63\x63\x65ptTeamAccountInvite\x12-.mgmt.v1alpha1.AcceptTeamAccountInviteRequest\x1a..mgmt.v1alpha1.AcceptTeamAccountInviteResponse\"\x00\x12t\n\x14GetSystemInformation\x12*.mgmt.v1alpha1.GetSystemInformationRequest\x1a+.mgmt.v1alpha1.GetSystemInformationResponse\"\x03\x90\x02\x01\x12\x83\x01\n\x1aGetAccountOnboardingConfig\x12\x30.mgmt.v1alpha1.GetAccountOnboardingConfigRequest\x1a\x31.mgmt.v1alpha1.GetAccountOnboardingConfigResponse\"\x00\x12\x83\x01\n\x1aSetAccountOnboardingConfig\x12\x30.mgmt.v1alpha1.SetAccountOnboardingConfigRequest\x1a\x31.mgmt.v1alpha1.SetAccountOnboardingConfigResponse\"\x00\x12h\n\x10GetAccountStatus\x12&.mgmt.v1alpha1.GetAccountStatusRequest\x1a\'.mgmt.v1alpha1.GetAccountStatusResponse\"\x03\x90\x02\x01\x12t\n\x14IsAccountStatusValid\x12*.mgmt.v1alpha1.IsAccountStatusValidRequest\x1a+.mgmt.v1alpha1.IsAccountStatusValidResponse\"\x03\x90\x02\x01\x12\x95\x01\n GetAccountBillingCheckoutSession\x12\x36.mgmt.v1alpha1.GetAccountBillingCheckoutSessionRequest\x1a\x37.mgmt.v1alpha1.GetAccountBillingCheckoutSessionResponse\"\x00\x12\x8f\x01\n\x1eGetAccountBillingPortalSession\x12\x34.mgmt.v1alpha1.GetAccountBillingPortalSessionRequest\x1a\x35.mgmt.v1alpha1.GetAccountBillingPortalSessionResponse\"\x00\x12n\n\x12GetBillingAccounts\x12(.mgmt.v1alpha1.GetBillingAccountsRequest\x1a).mgmt.v1alpha1.GetBillingAccountsResponse\"\x03\x90\x02\x01\x12q\n\x14SetBillingMeterEvent\x12*.mgmt.v1alpha1.SetBillingMeterEventRequest\x1a+.mgmt.v1alpha1.SetBillingMeterEventResponse\"\x00\x12V\n\x0bSetUserRole\x12!.mgmt.v1alpha1.SetUserRoleRequest\x1a\".mgmt.v1alpha1.SetUserRoleResponse\"\x00\x42\xcc\x01\n\x11\x63om.mgmt.v1alpha1B\x10UserAccountProtoP\x01ZPgithub.com/nucleuscloud/neosync/backend/gen/go/protos/mgmt/v1alpha1;mgmtv1alpha1\xa2\x02\x03MXX\xaa\x02\rMgmt.V1alpha1\xca\x02\rMgmt\\V1alpha1\xe2\x02\x19Mgmt\\V1alpha1\\GPBMetadata\xea\x02\x0eMgmt::V1alpha1b\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -88,6 +88,10 @@
_globals['_SETBILLINGMETEREVENTREQUEST'].fields_by_name['value']._serialized_options = b'\272H\004r\002\020\001'
_globals['_SETBILLINGMETEREVENTREQUEST'].fields_by_name['event_id']._loaded_options = None
_globals['_SETBILLINGMETEREVENTREQUEST'].fields_by_name['event_id']._serialized_options = b'\272H\004r\002\020\001'
+ _globals['_SETUSERROLEREQUEST'].fields_by_name['account_id']._loaded_options = None
+ _globals['_SETUSERROLEREQUEST'].fields_by_name['account_id']._serialized_options = b'\272H\005r\003\260\001\001'
+ _globals['_SETUSERROLEREQUEST'].fields_by_name['user_id']._loaded_options = None
+ _globals['_SETUSERROLEREQUEST'].fields_by_name['user_id']._serialized_options = b'\272H\005r\003\260\001\001'
_globals['_USERACCOUNTSERVICE'].methods_by_name['GetSystemInformation']._loaded_options = None
_globals['_USERACCOUNTSERVICE'].methods_by_name['GetSystemInformation']._serialized_options = b'\220\002\001'
_globals['_USERACCOUNTSERVICE'].methods_by_name['GetAccountStatus']._loaded_options = None
@@ -96,12 +100,14 @@
_globals['_USERACCOUNTSERVICE'].methods_by_name['IsAccountStatusValid']._serialized_options = b'\220\002\001'
_globals['_USERACCOUNTSERVICE'].methods_by_name['GetBillingAccounts']._loaded_options = None
_globals['_USERACCOUNTSERVICE'].methods_by_name['GetBillingAccounts']._serialized_options = b'\220\002\001'
- _globals['_USERACCOUNTTYPE']._serialized_start=5798
- _globals['_USERACCOUNTTYPE']._serialized_end=5944
- _globals['_BILLINGSTATUS']._serialized_start=5947
- _globals['_BILLINGSTATUS']._serialized_end=6116
- _globals['_ACCOUNTSTATUS']._serialized_start=6119
- _globals['_ACCOUNTSTATUS']._serialized_end=6387
+ _globals['_USERACCOUNTTYPE']._serialized_start=5968
+ _globals['_USERACCOUNTTYPE']._serialized_end=6114
+ _globals['_BILLINGSTATUS']._serialized_start=6117
+ _globals['_BILLINGSTATUS']._serialized_end=6286
+ _globals['_ACCOUNTSTATUS']._serialized_start=6289
+ _globals['_ACCOUNTSTATUS']._serialized_end=6557
+ _globals['_ACCOUNTROLE']._serialized_start=6560
+ _globals['_ACCOUNTROLE']._serialized_end=6719
_globals['_GETUSERREQUEST']._serialized_start=113
_globals['_GETUSERREQUEST']._serialized_end=129
_globals['_GETUSERRESPONSE']._serialized_start=131
@@ -208,6 +214,10 @@
_globals['_SETBILLINGMETEREVENTREQUEST']._serialized_end=5763
_globals['_SETBILLINGMETEREVENTRESPONSE']._serialized_start=5765
_globals['_SETBILLINGMETEREVENTRESPONSE']._serialized_end=5795
- _globals['_USERACCOUNTSERVICE']._serialized_start=6390
- _globals['_USERACCOUNTSERVICE']._serialized_end=9238
+ _globals['_SETUSERROLEREQUEST']._serialized_start=5798
+ _globals['_SETUSERROLEREQUEST']._serialized_end=5942
+ _globals['_SETUSERROLERESPONSE']._serialized_start=5944
+ _globals['_SETUSERROLERESPONSE']._serialized_end=5965
+ _globals['_USERACCOUNTSERVICE']._serialized_start=6722
+ _globals['_USERACCOUNTSERVICE']._serialized_end=9658
# @@protoc_insertion_point(module_scope)
diff --git a/python/src/neosync/mgmt/v1alpha1/user_account_pb2.pyi b/python/src/neosync/mgmt/v1alpha1/user_account_pb2.pyi
index 4dfc1cb96f..2543c2a14a 100644
--- a/python/src/neosync/mgmt/v1alpha1/user_account_pb2.pyi
+++ b/python/src/neosync/mgmt/v1alpha1/user_account_pb2.pyi
@@ -31,6 +31,14 @@ class AccountStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
ACCOUNT_STATUS_ACCOUNT_IN_EXPIRED_STATE: _ClassVar[AccountStatus]
ACCOUNT_STATUS_ACCOUNT_TRIAL_ACTIVE: _ClassVar[AccountStatus]
ACCOUNT_STATUS_ACCOUNT_TRIAL_EXPIRED: _ClassVar[AccountStatus]
+
+class AccountRole(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
+ __slots__ = ()
+ ACCOUNT_ROLE_UNSPECIFIED: _ClassVar[AccountRole]
+ ACCOUNT_ROLE_ADMIN: _ClassVar[AccountRole]
+ ACCOUNT_ROLE_JOB_DEVELOPER: _ClassVar[AccountRole]
+ ACCOUNT_ROLE_JOB_VIEWER: _ClassVar[AccountRole]
+ ACCOUNT_ROLE_JOB_EXECUTOR: _ClassVar[AccountRole]
USER_ACCOUNT_TYPE_UNSPECIFIED: UserAccountType
USER_ACCOUNT_TYPE_PERSONAL: UserAccountType
USER_ACCOUNT_TYPE_TEAM: UserAccountType
@@ -46,6 +54,11 @@ ACCOUNT_STATUS_REQUESTED_EXCEEDS_LIMIT: AccountStatus
ACCOUNT_STATUS_ACCOUNT_IN_EXPIRED_STATE: AccountStatus
ACCOUNT_STATUS_ACCOUNT_TRIAL_ACTIVE: AccountStatus
ACCOUNT_STATUS_ACCOUNT_TRIAL_EXPIRED: AccountStatus
+ACCOUNT_ROLE_UNSPECIFIED: AccountRole
+ACCOUNT_ROLE_ADMIN: AccountRole
+ACCOUNT_ROLE_JOB_DEVELOPER: AccountRole
+ACCOUNT_ROLE_JOB_VIEWER: AccountRole
+ACCOUNT_ROLE_JOB_EXECUTOR: AccountRole
class GetUserRequest(_message.Message):
__slots__ = ()
@@ -438,3 +451,17 @@ class SetBillingMeterEventRequest(_message.Message):
class SetBillingMeterEventResponse(_message.Message):
__slots__ = ()
def __init__(self) -> None: ...
+
+class SetUserRoleRequest(_message.Message):
+ __slots__ = ("account_id", "user_id", "role")
+ ACCOUNT_ID_FIELD_NUMBER: _ClassVar[int]
+ USER_ID_FIELD_NUMBER: _ClassVar[int]
+ ROLE_FIELD_NUMBER: _ClassVar[int]
+ account_id: str
+ user_id: str
+ role: AccountRole
+ def __init__(self, account_id: _Optional[str] = ..., user_id: _Optional[str] = ..., role: _Optional[_Union[AccountRole, str]] = ...) -> None: ...
+
+class SetUserRoleResponse(_message.Message):
+ __slots__ = ()
+ def __init__(self) -> None: ...
diff --git a/python/src/neosync/mgmt/v1alpha1/user_account_pb2_grpc.py b/python/src/neosync/mgmt/v1alpha1/user_account_pb2_grpc.py
index 27ca5c04cd..fca0d3a589 100644
--- a/python/src/neosync/mgmt/v1alpha1/user_account_pb2_grpc.py
+++ b/python/src/neosync/mgmt/v1alpha1/user_account_pb2_grpc.py
@@ -134,6 +134,11 @@ def __init__(self, channel):
request_serializer=mgmt_dot_v1alpha1_dot_user__account__pb2.SetBillingMeterEventRequest.SerializeToString,
response_deserializer=mgmt_dot_v1alpha1_dot_user__account__pb2.SetBillingMeterEventResponse.FromString,
_registered_method=True)
+ self.SetUserRole = channel.unary_unary(
+ '/mgmt.v1alpha1.UserAccountService/SetUserRole',
+ request_serializer=mgmt_dot_v1alpha1_dot_user__account__pb2.SetUserRoleRequest.SerializeToString,
+ response_deserializer=mgmt_dot_v1alpha1_dot_user__account__pb2.SetUserRoleResponse.FromString,
+ _registered_method=True)
class UserAccountServiceServicer(object):
@@ -291,6 +296,13 @@ def SetBillingMeterEvent(self, request, context):
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
+ def SetUserRole(self, request, context):
+ """Sets the users role
+ """
+ context.set_code(grpc.StatusCode.UNIMPLEMENTED)
+ context.set_details('Method not implemented!')
+ raise NotImplementedError('Method not implemented!')
+
def add_UserAccountServiceServicer_to_server(servicer, server):
rpc_method_handlers = {
@@ -414,6 +426,11 @@ def add_UserAccountServiceServicer_to_server(servicer, server):
request_deserializer=mgmt_dot_v1alpha1_dot_user__account__pb2.SetBillingMeterEventRequest.FromString,
response_serializer=mgmt_dot_v1alpha1_dot_user__account__pb2.SetBillingMeterEventResponse.SerializeToString,
),
+ 'SetUserRole': grpc.unary_unary_rpc_method_handler(
+ servicer.SetUserRole,
+ request_deserializer=mgmt_dot_v1alpha1_dot_user__account__pb2.SetUserRoleRequest.FromString,
+ response_serializer=mgmt_dot_v1alpha1_dot_user__account__pb2.SetUserRoleResponse.SerializeToString,
+ ),
}
generic_handler = grpc.method_handlers_generic_handler(
'mgmt.v1alpha1.UserAccountService', rpc_method_handlers)
@@ -1072,3 +1089,30 @@ def SetBillingMeterEvent(request,
timeout,
metadata,
_registered_method=True)
+
+ @staticmethod
+ def SetUserRole(request,
+ target,
+ options=(),
+ channel_credentials=None,
+ call_credentials=None,
+ insecure=False,
+ compression=None,
+ wait_for_ready=None,
+ timeout=None,
+ metadata=None):
+ return grpc.experimental.unary_unary(
+ request,
+ target,
+ '/mgmt.v1alpha1.UserAccountService/SetUserRole',
+ mgmt_dot_v1alpha1_dot_user__account__pb2.SetUserRoleRequest.SerializeToString,
+ mgmt_dot_v1alpha1_dot_user__account__pb2.SetUserRoleResponse.FromString,
+ options,
+ channel_credentials,
+ insecure,
+ call_credentials,
+ compression,
+ wait_for_ready,
+ timeout,
+ metadata,
+ _registered_method=True)