// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. package model import ( "net/http" "reflect" "strconv" ) type FeatureFlags struct { // Exists only for unit and manual testing. // When set to a value, will be returned by the ping endpoint. TestFeature string // Exists only for testing bool functionality. Boolean feature flags interpret "on" or "true" as true and // all other values as false. TestBoolFeature bool // Enable DMs and GMs for shared channels. EnableSharedChannelsDMs bool // Enable syncing all users for remote clusters in shared channels EnableSyncAllUsersForRemoteCluster bool // AppsEnabled toggles the Apps framework functionalities both in server and client side AppsEnabled bool NormalizeLdapDNs bool // Enable WYSIWYG text editor WysiwygEditor bool MoveThreadsEnabled bool NotificationMonitoring bool // Mask non-held attribute values in the policy editor for delegated admins. AttributeValueMasking bool // Enable permission policies (file upload/download ABAC policies). // // This is the umbrella flag: when off, both ChannelPermissionPolicies // and PolicySimulation are also off regardless of their individual // settings. Use the IsChannelPermissionPoliciesEnabled() and // IsPolicySimulationEnabled() helpers below rather than checking // PermissionPolicies + the sub-flag manually at every call site — // they encapsulate the dependency so a future renaming / // consolidation only has to update one place. PermissionPolicies bool // Enable permission-rule actions (upload_file_attachment, // download_file_attachment) on channel-scope policies — and, on the // frontend, the Channel Settings → Permissions Policy tab that lets // channel admins configure them. Requires PermissionPolicies. Read // via FeatureFlags.IsChannelPermissionPoliciesEnabled() so the // PermissionPolicies dependency is enforced at every call site. ChannelPermissionPolicies bool // Enable the "Simulate access" preview UX and its backing // /cel/simulate_users endpoint. Requires PermissionPolicies. Read // via FeatureFlags.IsPolicySimulationEnabled() so the // PermissionPolicies dependency is enforced at every call site. PolicySimulation bool ContentFlagging bool EnableMattermostEntry bool // DEPRECATED: Mobile SSO SAML code-exchange flow - disabled by default // This feature is deprecated and will be removed in a future release. // Mobile clients should use the direct SSO callback flow with srv parameter verification. MobileSSOCodeExchange bool // Enable the SHIFT+ESC combo to mark _all_ chats, messages, and channels as read EnableShiftEscapeToMarkAllRead bool // FEATURE_FLAG_REMOVAL: AutoTranslation - Remove this when MVP is to be released // Enable auto-translation feature for messages in channels AutoTranslation bool // Enable classification markings for banners at the system and channel level ClassificationMarkings bool // Enable the Global Attributes management page in the System Console GlobalAttributes bool // Enable burn-on-read messages that automatically delete after viewing BurnOnRead bool // FEATURE_FLAG_REMOVAL: EnableAIPluginBridge EnableAIPluginBridge bool // FEATURE_FLAG_REMOVAL: EnableAIRecaps - Remove this when GA is released EnableAIRecaps bool // FEATURE_FLAG_REMOVAL: IntegratedBoards - Remove this when GA is released // Enable the Integrated Boards feature within Mattermost channels IntegratedBoards bool // FEATURE_FLAG_REMOVAL: EnableDocs - Remove this when GA is released // Enable the Docs (spaces and pages) feature within Mattermost channels EnableDocs bool // Enable LIKE-based CJK (Chinese, Japanese, Korean) search for PostgreSQL CJKSearch bool // Collect plugin metrics and serve them on the /metrics endpoint AggregatePluginMetrics bool // ManagedChannelCategories enables server-side managed sidebar category enforcement (Enterprise). ManagedChannelCategories bool // Enable collection of request-provided session attributes (user agent, IP address, etc.). SessionAttributes bool // Gates the Post Attributes feature (post_attributes property group). PostAttributes bool // FEATURE_FLAG_REMOVAL: DiscoverableChannels - Remove this when the feature is GA. // Gates the per-channel Discoverable toggle and the channel-join-request flow that lets // non-members find a private channel in Browse Channels and request to join it. DiscoverableChannels bool // Enable Mobile Ephemeral Mode for controlling data persistence on mobile devices MobileEphemeralMode bool // FEATURE_FLAG_REMOVAL: PropertyFieldRank - Remove this when the feature is GA. // Gates the "rank" custom profile attribute type: when off, the app layer // rejects creating a rank property field or converting an existing field to // rank, and the admin console hides the rank type option. PropertyFieldRank bool TeamMembershipAccessControl bool // Enable channel attributes (Smart Labels, banners) powered by the Properties API. ChannelAttributes bool // FEATURE_FLAG_REMOVAL: ResourceAttributesInPolicies - Remove this when the // feature is GA. Gates access rules that compare a user's attributes against // the accessed channel's (resource.attributes.*): when off, the autocomplete // endpoint omits channel-object-type fields, so no editor offers them, and // saving a policy that references one is rejected. It does NOT gate // evaluation — a rule stored while the flag was on keeps being enforced, // because such rules deny on a missing channel value and silently dropping // enforcement would empty every channel the policy governs. ResourceAttributesInPolicies bool // Enable the new mm_blocks Interactive Messages framework MmBlocksEnabled bool // ClusterGracefulDrain enables waiting for peer silence before closing the gossip // socket during shutdown. Otherwise, peers keep sending messages before the gossip // leave message finishes propagating and spam the logs with errors about the peer // being unreachable. ClusterGracefulDrain bool // Enable verifying plugin signatures against the MFI public key, in addition to the // existing hard-coded Mattermost public key and any admin-configured public keys. EnableMFIPluginSignaturePublicKey bool // FEATURE_FLAG_REMOVAL: RecurringScheduledPosts - Remove this when the feature is GA. RecurringScheduledPosts bool // Gates post delivery audit logging. Enabling it requires a server restart, since it // controls whether the /api/v4/delivery_tracking routes are registered. PostDeliveryTracking bool } func (f *FeatureFlags) SetDefaults() { f.TestFeature = "off" f.TestBoolFeature = false f.ClusterGracefulDrain = true f.EnableSharedChannelsDMs = false f.EnableSyncAllUsersForRemoteCluster = false f.AppsEnabled = false f.NormalizeLdapDNs = false f.WysiwygEditor = false f.MoveThreadsEnabled = false f.NotificationMonitoring = true f.AttributeValueMasking = true f.PermissionPolicies = true f.TeamMembershipAccessControl = true f.ResourceAttributesInPolicies = false f.ChannelPermissionPolicies = true f.PolicySimulation = true f.ContentFlagging = true f.EnableMattermostEntry = true // DEPRECATED: Disabled by default - mobile clients use direct SSO callback flow f.MobileSSOCodeExchange = false f.EnableShiftEscapeToMarkAllRead = false f.AutoTranslation = true f.ClassificationMarkings = true f.BurnOnRead = true // FEATURE_FLAG_REMOVAL: EnableAIPluginBridge - Remove this default when MVP is to be released f.EnableAIPluginBridge = false f.EnableAIRecaps = false f.IntegratedBoards = false f.EnableDocs = false f.CJKSearch = true f.AggregatePluginMetrics = false f.ManagedChannelCategories = false f.SessionAttributes = false f.PostAttributes = false f.DiscoverableChannels = false f.MobileEphemeralMode = false f.PropertyFieldRank = true f.ChannelAttributes = false f.MmBlocksEnabled = true f.EnableMFIPluginSignaturePublicKey = true f.RecurringScheduledPosts = false f.PostDeliveryTracking = false } // isValid rejects feature flag combinations that are no longer supported. func (f *FeatureFlags) isValid() *AppError { // The Apps framework is being retired, so the server refuses to start // while the AppsEnabled feature flag is enabled. if f.AppsEnabled { return NewAppError("FeatureFlags.IsValid", "model.config.is_valid.feature_flags.apps_enabled.app_error", nil, "", http.StatusBadRequest) } // MoveThreadsEnabled is being retired in favor of Wrangler, so the server // refuses to start while it is enabled. if f.MoveThreadsEnabled { return NewAppError("FeatureFlags.IsValid", "model.config.is_valid.feature_flags.move_threads_enabled.app_error", nil, "", http.StatusBadRequest) } return nil } // IsChannelPermissionPoliciesEnabled reports whether channel-scope // policies may carry permission-rule actions (file upload/download) // and whether the Channel Settings → Permissions Policy tab should // be exposed. Both the sub-flag AND the PermissionPolicies umbrella // must be on — turning the umbrella off implicitly disables the // sub-feature even if its own flag is on. Centralizing the // dependency check here keeps every call site honest. func (f *FeatureFlags) IsChannelPermissionPoliciesEnabled() bool { return f.PermissionPolicies && f.ChannelPermissionPolicies } // IsPolicySimulationEnabled reports whether the "Simulate access" // preview UX and its backing /cel/simulate_users endpoint are // available. Both the sub-flag AND the PermissionPolicies umbrella // must be on — turning the umbrella off implicitly disables the // sub-feature even if its own flag is on. Centralizing the // dependency check here keeps every call site honest. func (f *FeatureFlags) IsPolicySimulationEnabled() bool { return f.PermissionPolicies && f.PolicySimulation } // ToMap returns the feature flags as a map[string]string // Supports boolean and string feature flags. func (f *FeatureFlags) ToMap() map[string]string { refStructVal := reflect.ValueOf(*f) refStructType := reflect.TypeFor[FeatureFlags]() ret := make(map[string]string) for i := range refStructVal.NumField() { refFieldVal := refStructVal.Field(i) if !refFieldVal.IsValid() { continue } refFieldType := refStructType.Field(i) switch refFieldType.Type.Kind() { case reflect.Bool: ret[refFieldType.Name] = strconv.FormatBool(refFieldVal.Bool()) default: ret[refFieldType.Name] = refFieldVal.String() } } return ret }