diff --git a/cmd/cli/cmd/migrate.go b/cmd/cli/cmd/migrate.go index f56a554e6..57308063a 100644 --- a/cmd/cli/cmd/migrate.go +++ b/cmd/cli/cmd/migrate.go @@ -44,7 +44,6 @@ func NewMigrate() *cobra.Command { - Mattermost Limitations: - - RBAC is defaulted - Plugins are sourced from Botkube repository Use label selector to choose which Botkube pod you want to migrate. By default it's set to app=botkube. diff --git a/cmd/cli/docs/botkube_migrate.md b/cmd/cli/docs/botkube_migrate.md index ee0ba25fc..8afa05945 100644 --- a/cmd/cli/docs/botkube_migrate.md +++ b/cmd/cli/docs/botkube_migrate.md @@ -17,7 +17,6 @@ Supported Botkube bot platforms for migration: - Mattermost Limitations: -- RBAC is defaulted - Plugins are sourced from Botkube repository Use label selector to choose which Botkube pod you want to migrate. By default it's set to app=botkube. diff --git a/internal/cli/migrate/converter.go b/internal/cli/migrate/converter.go index a2894499a..d27ec7635 100644 --- a/internal/cli/migrate/converter.go +++ b/internal/cli/migrate/converter.go @@ -91,6 +91,7 @@ func (c *Converter) convertExecutors(executors map[string]bkconfig.Executors) ([ { Name: cfgName, Configuration: string(rawCfg), + Rbac: c.convertRbac(p.Context), }, }, }) @@ -121,6 +122,7 @@ func (c *Converter) convertSources(sources map[string]bkconfig.Sources) ([]*gqlM { Name: cfgName, Configuration: string(rawCfg), + Rbac: c.convertRbac(p.Context), }, }, }) @@ -130,6 +132,21 @@ func (c *Converter) convertSources(sources map[string]bkconfig.Sources) ([]*gqlM return out, errs.ErrorOrNil() } +func (c *Converter) convertRbac(ctx bkconfig.PluginContext) *gqlModel.RBACInput { + return &gqlModel.RBACInput{ + User: &gqlModel.UserPolicySubjectInput{ + Type: graphqlPolicySubjectType(ctx.RBAC.User.Type), + Static: &gqlModel.UserStaticSubjectInput{Value: ctx.RBAC.User.Static.Value}, + Prefix: &ctx.RBAC.User.Prefix, + }, + Group: &gqlModel.GroupPolicySubjectInput{ + Type: graphqlPolicySubjectType(ctx.RBAC.Group.Type), + Static: &gqlModel.GroupStaticSubjectInput{Values: ctx.RBAC.Group.Static.Values}, + Prefix: &ctx.RBAC.Group.Prefix, + }, + } +} + // ConvertPlatforms converts cloud supported platforms. func (c *Converter) ConvertPlatforms(platforms map[string]bkconfig.Communications) *gqlModel.PlatformsCreateInput { out := gqlModel.PlatformsCreateInput{} @@ -222,3 +239,14 @@ func toSlicePointers[T any](in []T) []*T { } return out } + +func graphqlPolicySubjectType(sub bkconfig.PolicySubjectType) gqlModel.PolicySubjectType { + switch sub { + case bkconfig.StaticPolicySubjectType: + return gqlModel.PolicySubjectTypeStatic + case bkconfig.ChannelNamePolicySubjectType: + return gqlModel.PolicySubjectTypeChannelName + default: + return gqlModel.PolicySubjectTypeEmpty + } +} diff --git a/internal/remote/graphql/connected_platforms.go b/internal/remote/graphql/connected_platforms.go new file mode 100644 index 000000000..a5faa685c --- /dev/null +++ b/internal/remote/graphql/connected_platforms.go @@ -0,0 +1,8 @@ +package graphql + +// OrganizationConnectedPlatforms represents connected platforms. +type OrganizationConnectedPlatforms struct { + OrganizationID string `graphql:"-"` + Slacks []*SlackWorkspace `json:"slacks"` + Slack *SlackWorkspace `json:"slack"` +} diff --git a/internal/remote/graphql/models_gen.go b/internal/remote/graphql/models_gen.go index 5f03e91f5..bfc067f39 100644 --- a/internal/remote/graphql/models_gen.go +++ b/internal/remote/graphql/models_gen.go @@ -63,6 +63,15 @@ type AddMemberForOrganizationInput struct { UserEmail *string `json:"userEmail"` } +type AddPlatformToOrganizationInput struct { + OrganizationID string `json:"organizationId"` + Slack *AddSlackToOrganizationInput `json:"slack"` +} + +type AddSlackToOrganizationInput struct { + Token string `json:"token"` +} + type Alias struct { ID string `json:"id"` Name string `json:"name"` @@ -193,6 +202,27 @@ type ChannelBindingsByNameUpdateInput struct { Bindings *BotBindingsUpdateInput `json:"bindings"` } +type CloudSlack struct { + ID string `json:"id"` + Name string `json:"name"` + TeamID string `json:"teamId"` + BotToken string `json:"botToken"` + Channels []*ChannelBindingsByName `json:"channels"` +} + +type CloudSlackCreateInput struct { + Name string `json:"name"` + TeamID string `json:"teamId"` + Channels []*ChannelBindingsByNameCreateInput `json:"channels"` +} + +type CloudSlackUpdateInput struct { + ID *string `json:"id"` + Name string `json:"name"` + TeamID string `json:"teamId"` + Channels []*ChannelBindingsByNameUpdateInput `json:"channels"` +} + type CommandExecutedEvent struct { ID string `json:"id"` Type *AuditEventType `json:"type"` @@ -214,19 +244,38 @@ func (this CommandExecutedEvent) GetCreatedAt() string { return this.Creat func (this CommandExecutedEvent) GetPluginName() string { return this.PluginName } func (this CommandExecutedEvent) GetDeployment() *Deployment { return this.Deployment } +type ConnectedPlatforms struct { + Slack *SlackWorkspace `json:"slack"` +} + +type DeleteByIDInput struct { + ID string `json:"ID"` +} + +type DeletePlatformInput struct { + SocketSlack *DeleteByIDInput `json:"socketSlack"` + CloudSlack *DeleteByIDInput `json:"cloudSlack"` + Discord *DeleteByIDInput `json:"discord"` + Mattermost *DeleteByIDInput `json:"mattermost"` + Webhook *DeleteByIDInput `json:"webhook"` + MsTeams *DeleteByIDInput `json:"msTeams"` + Elasticsearch *DeleteByIDInput `json:"elasticsearch"` +} + type Deployment struct { - ID string `json:"id"` - Name string `json:"name"` - Actions []*Action `json:"actions"` - Plugins []*Plugin `json:"plugins"` - Platforms *Platforms `json:"platforms"` - Status *DeploymentStatus `json:"status"` - APIKey *APIKey `json:"apiKey"` - YamlConfig *string `json:"yamlConfig"` - Aliases []*Alias `json:"aliases"` - HelmCommand *string `json:"helmCommand"` - ResourceVersion int `json:"resourceVersion"` - Heartbeat *Heartbeat `json:"heartbeat"` + ID string `json:"id"` + Name string `json:"name"` + Actions []*Action `json:"actions"` + Plugins []*Plugin `json:"plugins"` + Platforms *Platforms `json:"platforms"` + Status *DeploymentStatus `json:"status"` + APIKey *APIKey `json:"apiKey"` + YamlConfig *string `json:"yamlConfig"` + Aliases []*Alias `json:"aliases"` + HelmCommand *string `json:"helmCommand"` + ResourceVersion int `json:"resourceVersion"` + Heartbeat *Heartbeat `json:"heartbeat"` + InstallUpgradeInstructions []*InstallUpgradeInstructionsForPlatform `json:"installUpgradeInstructions"` } type DeploymentConfig struct { @@ -235,10 +284,12 @@ type DeploymentConfig struct { } type DeploymentCreateInput struct { - Name string `json:"name"` - Plugins []*PluginsCreateInput `json:"plugins"` - Platforms *PlatformsCreateInput `json:"platforms"` - Actions []*ActionCreateUpdateInput `json:"actions"` + Name string `json:"name"` + Plugins []*PluginsCreateInput `json:"plugins"` + Platforms *PlatformsCreateInput `json:"platforms"` + Actions []*ActionCreateUpdateInput `json:"actions"` + AttachDefaultAliases *bool `json:"attachDefaultAliases"` + AttachDefaultActions *bool `json:"attachDefaultActions"` } type DeploymentFailureInput struct { @@ -374,10 +425,51 @@ type ElasticsearchUpdateInput struct { Indices []*ElasticsearchIndexUpdateInput `json:"indices"` } +type GroupPolicySubject struct { + Type PolicySubjectType `json:"type"` + Static *GroupStaticSubject `json:"static"` + Prefix *string `json:"prefix"` +} + +type GroupPolicySubjectInput struct { + Type PolicySubjectType `json:"type"` + Static *GroupStaticSubjectInput `json:"static"` + Prefix *string `json:"prefix"` +} + +type GroupStaticSubject struct { + Values []string `json:"values"` +} + +type GroupStaticSubjectInput struct { + Values []string `json:"values"` +} + type Heartbeat struct { NodeCount *int `json:"nodeCount"` } +type HubSpotIdentificationToken struct { + Token string `json:"token"` +} + +type HubspotIdentificationTokenInput struct { + Email string `json:"email"` + FirstName *string `json:"firstName"` + LastName *string `json:"lastName"` +} + +type InstallUpgradeInstructionsForPlatform struct { + PlatformName string `json:"platformName"` + Prerequisites []*InstallUpgradePrerequisite `json:"prerequisites"` + InstallUpgradeCommand string `json:"installUpgradeCommand"` +} + +type InstallUpgradePrerequisite struct { + Description *string `json:"description"` + Command *string `json:"command"` +} + type Invoice struct { IsOnTrial bool `json:"isOnTrial"` UpcomingAmount int `json:"upcomingAmount"` @@ -465,16 +557,17 @@ type NotificationPatchDeploymentConfigInput struct { } type Organization struct { - ID string `json:"id"` - DisplayName string `json:"displayName"` - Subscription *OrganizationSubscription `json:"subscription"` - OwnerID string `json:"ownerId"` - Owner *User `json:"owner"` - Members []*User `json:"members"` - Quota *Quota `json:"quota"` - BillingHistoryAvailable bool `json:"billingHistoryAvailable"` - UpdateOperations *OrganizationUpdateOperations `json:"updateOperations"` - Usage *Usage `json:"usage"` + ID string `json:"id"` + DisplayName string `json:"displayName"` + Subscription *OrganizationSubscription `json:"subscription"` + ConnectedPlatforms *OrganizationConnectedPlatforms `json:"connectedPlatforms"` + OwnerID string `json:"ownerId"` + Owner *User `json:"owner"` + Members []*User `json:"members"` + Quota *Quota `json:"quota"` + BillingHistoryAvailable bool `json:"billingHistoryAvailable"` + UpdateOperations *OrganizationUpdateOperations `json:"updateOperations"` + Usage *Usage `json:"usage"` } type OrganizationCreateInput struct { @@ -516,6 +609,7 @@ type PatchDeploymentConfigInput struct { type PlatformsCreateInput struct { Discords []*DiscordCreateInput `json:"discords"` SocketSlacks []*SocketSlackCreateInput `json:"socketSlacks"` + CloudSlacks []*CloudSlackCreateInput `json:"cloudSlacks"` Mattermosts []*MattermostCreateInput `json:"mattermosts"` Webhooks []*WebhookCreateInput `json:"webhooks"` MsTeams []*MsTeamsCreateInput `json:"msTeams"` @@ -524,6 +618,7 @@ type PlatformsCreateInput struct { type PlatformsUpdateInput struct { SocketSlacks []*SocketSlackUpdateInput `json:"socketSlacks"` + CloudSlacks []*CloudSlackUpdateInput `json:"cloudSlacks"` Discords []*DiscordUpdateInput `json:"discords"` Mattermosts []*MattermostUpdateInput `json:"mattermosts"` Webhooks []*WebhookUpdateInput `json:"webhooks"` @@ -538,6 +633,7 @@ type Plugin struct { Type PluginType `json:"type"` ConfigurationName string `json:"configurationName"` Configuration string `json:"configuration"` + Rbac *Rbac `json:"rbac"` } type PluginConfigurationGroupInput struct { @@ -548,16 +644,24 @@ type PluginConfigurationGroupInput struct { } type PluginConfigurationGroupUpdateInput struct { - ID *string `json:"id"` - Name string `json:"name"` - DisplayName string `json:"displayName"` - Type PluginType `json:"type"` - Configurations []*PluginConfigurationInput `json:"configurations"` + ID *string `json:"id"` + Name string `json:"name"` + DisplayName string `json:"displayName"` + Type PluginType `json:"type"` + Configurations []*PluginConfigurationUpdateInput `json:"configurations"` } type PluginConfigurationInput struct { - Name string `json:"name"` - Configuration string `json:"configuration"` + Name string `json:"name"` + Configuration string `json:"configuration"` + Rbac *RBACInput `json:"rbac"` +} + +type PluginConfigurationUpdateInput struct { + ID *string `json:"id"` + Name string `json:"name"` + Configuration string `json:"configuration"` + Rbac *RBACUpdateInput `json:"rbac"` } type PluginPage struct { @@ -595,6 +699,24 @@ type Quota struct { AuditRetentionPeriod *int `json:"auditRetentionPeriod"` MemberCount *int `json:"memberCount"` NodeCount *int `json:"nodeCount"` + CloudSlackUseCount *int `json:"cloudSlackUseCount"` +} + +type Rbac struct { + ID string `json:"id"` + User *UserPolicySubject `json:"user"` + Group *GroupPolicySubject `json:"group"` +} + +type RBACInput struct { + User *UserPolicySubjectInput `json:"user"` + Group *GroupPolicySubjectInput `json:"group"` +} + +type RBACUpdateInput struct { + ID string `json:"id"` + User *UserPolicySubjectInput `json:"user"` + Group *GroupPolicySubjectInput `json:"group"` } type RemoveMemberFromOrganizationInput struct { @@ -602,6 +724,15 @@ type RemoveMemberFromOrganizationInput struct { UserID string `json:"userId"` } +type RemovePlatformFromOrganizationInput struct { + OrganizationID string `json:"organizationId"` + Slack *RemoveSlackFromOrganizationInput `json:"slack"` +} + +type RemoveSlackFromOrganizationInput struct { + ID string `json:"ID"` +} + type SinkBindings struct { Sources []string `json:"sources"` } @@ -614,6 +745,29 @@ type SinkBindingsUpdateInput struct { Sources []*string `json:"sources"` } +type SlackWorkspace struct { + ID string `json:"id"` + Name string `json:"name"` + TeamID string `json:"teamId"` + URL string `json:"url"` + Channels []*SlackWorkspaceChannel `json:"channels"` + IsReinstallRequired bool `json:"isReinstallRequired"` + ConnectedOrganizations []*SlackWorkspaceConnectedOrganizations `json:"connectedOrganizations"` +} + +type SlackWorkspaceChannel struct { + Name string `json:"name"` + IsPrivate bool `json:"isPrivate"` + IsMember bool `json:"isMember"` + Topic *string `json:"topic"` + Purpose *string `json:"purpose"` +} + +type SlackWorkspaceConnectedOrganizations struct { + ID string `json:"id"` + Name string `json:"name"` +} + type SocketSlack struct { ID string `json:"id"` Name string `json:"name"` @@ -681,6 +835,26 @@ type User struct { Email string `json:"email"` } +type UserPolicySubject struct { + Type PolicySubjectType `json:"type"` + Static *UserStaticSubject `json:"static"` + Prefix *string `json:"prefix"` +} + +type UserPolicySubjectInput struct { + Type PolicySubjectType `json:"type"` + Static *UserStaticSubjectInput `json:"static"` + Prefix *string `json:"prefix"` +} + +type UserStaticSubject struct { + Value string `json:"value"` +} + +type UserStaticSubjectInput struct { + Value string `json:"value"` +} + type Webhook struct { ID string `json:"id"` Name string `json:"name"` @@ -749,6 +923,7 @@ const ( BotPlatformDiscord BotPlatform = "DISCORD" BotPlatformMattermost BotPlatform = "MATTERMOST" BotPlatformMsTeams BotPlatform = "MS_TEAMS" + BotPlatformUnknown BotPlatform = "UNKNOWN" ) var AllBotPlatform = []BotPlatform{ @@ -756,11 +931,12 @@ var AllBotPlatform = []BotPlatform{ BotPlatformDiscord, BotPlatformMattermost, BotPlatformMsTeams, + BotPlatformUnknown, } func (e BotPlatform) IsValid() bool { switch e { - case BotPlatformSLACk, BotPlatformDiscord, BotPlatformMattermost, BotPlatformMsTeams: + case BotPlatformSLACk, BotPlatformDiscord, BotPlatformMattermost, BotPlatformMsTeams, BotPlatformUnknown: return true } return false @@ -876,3 +1052,46 @@ func (e *PluginType) UnmarshalGQL(v interface{}) error { func (e PluginType) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } + +type PolicySubjectType string + +const ( + PolicySubjectTypeStatic PolicySubjectType = "Static" + PolicySubjectTypeChannelName PolicySubjectType = "ChannelName" + PolicySubjectTypeEmpty PolicySubjectType = "Empty" +) + +var AllPolicySubjectType = []PolicySubjectType{ + PolicySubjectTypeStatic, + PolicySubjectTypeChannelName, + PolicySubjectTypeEmpty, +} + +func (e PolicySubjectType) IsValid() bool { + switch e { + case PolicySubjectTypeStatic, PolicySubjectTypeChannelName, PolicySubjectTypeEmpty: + return true + } + return false +} + +func (e PolicySubjectType) String() string { + return string(e) +} + +func (e *PolicySubjectType) UnmarshalGQL(v interface{}) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = PolicySubjectType(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid PolicySubjectType", str) + } + return nil +} + +func (e PolicySubjectType) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +}