github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/config/client.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 "github.com/masterhung0112/hk_server/v5/model" 9 ) 10 11 // GenerateClientConfig renders the given configuration for a client. 12 func GenerateClientConfig(c *model.Config, telemetryID string, license *model.License) map[string]string { 13 props := GenerateLimitedClientConfig(c, telemetryID, license) 14 15 props["SiteURL"] = strings.TrimRight(*c.ServiceSettings.SiteURL, "/") 16 props["EnableCustomUserStatuses"] = strconv.FormatBool(*c.TeamSettings.EnableCustomUserStatuses) 17 props["EnableUserDeactivation"] = strconv.FormatBool(*c.TeamSettings.EnableUserDeactivation) 18 props["RestrictDirectMessage"] = *c.TeamSettings.RestrictDirectMessage 19 props["EnableXToLeaveChannelsFromLHS"] = strconv.FormatBool(*c.TeamSettings.EnableXToLeaveChannelsFromLHS) 20 props["TeammateNameDisplay"] = *c.TeamSettings.TeammateNameDisplay 21 props["LockTeammateNameDisplay"] = strconv.FormatBool(*c.TeamSettings.LockTeammateNameDisplay) 22 props["ExperimentalPrimaryTeam"] = *c.TeamSettings.ExperimentalPrimaryTeam 23 props["ExperimentalViewArchivedChannels"] = strconv.FormatBool(*c.TeamSettings.ExperimentalViewArchivedChannels) 24 25 props["EnableBotAccountCreation"] = strconv.FormatBool(*c.ServiceSettings.EnableBotAccountCreation) 26 props["EnableOAuthServiceProvider"] = strconv.FormatBool(*c.ServiceSettings.EnableOAuthServiceProvider) 27 props["GoogleDeveloperKey"] = *c.ServiceSettings.GoogleDeveloperKey 28 props["EnableIncomingWebhooks"] = strconv.FormatBool(*c.ServiceSettings.EnableIncomingWebhooks) 29 props["EnableOutgoingWebhooks"] = strconv.FormatBool(*c.ServiceSettings.EnableOutgoingWebhooks) 30 props["EnableCommands"] = strconv.FormatBool(*c.ServiceSettings.EnableCommands) 31 props["EnablePostUsernameOverride"] = strconv.FormatBool(*c.ServiceSettings.EnablePostUsernameOverride) 32 props["EnablePostIconOverride"] = strconv.FormatBool(*c.ServiceSettings.EnablePostIconOverride) 33 props["EnableUserAccessTokens"] = strconv.FormatBool(*c.ServiceSettings.EnableUserAccessTokens) 34 props["EnableLinkPreviews"] = strconv.FormatBool(*c.ServiceSettings.EnableLinkPreviews) 35 props["EnableTesting"] = strconv.FormatBool(*c.ServiceSettings.EnableTesting) 36 props["EnableDeveloper"] = strconv.FormatBool(*c.ServiceSettings.EnableDeveloper) 37 props["PostEditTimeLimit"] = fmt.Sprintf("%v", *c.ServiceSettings.PostEditTimeLimit) 38 props["MinimumHashtagLength"] = fmt.Sprintf("%v", *c.ServiceSettings.MinimumHashtagLength) 39 props["CloseUnusedDirectMessages"] = strconv.FormatBool(*c.ServiceSettings.CloseUnusedDirectMessages) 40 props["EnablePreviewFeatures"] = strconv.FormatBool(*c.ServiceSettings.EnablePreviewFeatures) 41 props["EnableTutorial"] = strconv.FormatBool(*c.ServiceSettings.EnableTutorial) 42 props["ExperimentalEnableDefaultChannelLeaveJoinMessages"] = strconv.FormatBool(*c.ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages) 43 props["ExperimentalGroupUnreadChannels"] = *c.ServiceSettings.ExperimentalGroupUnreadChannels 44 props["EnableSVGs"] = strconv.FormatBool(*c.ServiceSettings.EnableSVGs) 45 props["EnableMarketplace"] = strconv.FormatBool(*c.PluginSettings.EnableMarketplace) 46 props["EnableLatex"] = strconv.FormatBool(*c.ServiceSettings.EnableLatex) 47 props["ExtendSessionLengthWithActivity"] = strconv.FormatBool(*c.ServiceSettings.ExtendSessionLengthWithActivity) 48 props["ManagedResourcePaths"] = *c.ServiceSettings.ManagedResourcePaths 49 50 // This setting is only temporary, so keep using the old setting name for the mobile and web apps 51 props["ExperimentalEnablePostMetadata"] = "true" 52 props["ExperimentalEnableClickToReply"] = strconv.FormatBool(*c.ExperimentalSettings.EnableClickToReply) 53 54 props["ExperimentalCloudUserLimit"] = strconv.FormatInt(*c.ExperimentalSettings.CloudUserLimit, 10) 55 props["ExperimentalCloudBilling"] = strconv.FormatBool(*c.ExperimentalSettings.CloudBilling) 56 if *c.ServiceSettings.ExperimentalChannelOrganization || *c.ServiceSettings.ExperimentalGroupUnreadChannels != model.GROUP_UNREAD_CHANNELS_DISABLED { 57 props["ExperimentalChannelOrganization"] = strconv.FormatBool(true) 58 } else { 59 props["ExperimentalChannelOrganization"] = strconv.FormatBool(false) 60 } 61 62 props["ExperimentalEnableAutomaticReplies"] = strconv.FormatBool(*c.TeamSettings.ExperimentalEnableAutomaticReplies) 63 props["ExperimentalTimezone"] = strconv.FormatBool(*c.DisplaySettings.ExperimentalTimezone) 64 65 props["SendEmailNotifications"] = strconv.FormatBool(*c.EmailSettings.SendEmailNotifications) 66 props["SendPushNotifications"] = strconv.FormatBool(*c.EmailSettings.SendPushNotifications) 67 props["RequireEmailVerification"] = strconv.FormatBool(*c.EmailSettings.RequireEmailVerification) 68 props["EnableEmailBatching"] = strconv.FormatBool(*c.EmailSettings.EnableEmailBatching) 69 props["EnablePreviewModeBanner"] = strconv.FormatBool(*c.EmailSettings.EnablePreviewModeBanner) 70 props["EmailNotificationContentsType"] = *c.EmailSettings.EmailNotificationContentsType 71 72 props["ShowEmailAddress"] = strconv.FormatBool(*c.PrivacySettings.ShowEmailAddress) 73 props["ShowFullName"] = strconv.FormatBool(*c.PrivacySettings.ShowFullName) 74 75 props["EnableFileAttachments"] = strconv.FormatBool(*c.FileSettings.EnableFileAttachments) 76 props["EnablePublicLink"] = strconv.FormatBool(*c.FileSettings.EnablePublicLink) 77 78 props["AvailableLocales"] = *c.LocalizationSettings.AvailableLocales 79 props["SQLDriverName"] = *c.SqlSettings.DriverName 80 81 props["EnableEmojiPicker"] = strconv.FormatBool(*c.ServiceSettings.EnableEmojiPicker) 82 props["EnableGifPicker"] = strconv.FormatBool(*c.ServiceSettings.EnableGifPicker) 83 props["GfycatApiKey"] = *c.ServiceSettings.GfycatApiKey 84 props["GfycatApiSecret"] = *c.ServiceSettings.GfycatApiSecret 85 props["MaxFileSize"] = strconv.FormatInt(*c.FileSettings.MaxFileSize, 10) 86 87 props["MaxNotificationsPerChannel"] = strconv.FormatInt(*c.TeamSettings.MaxNotificationsPerChannel, 10) 88 props["EnableConfirmNotificationsToChannel"] = strconv.FormatBool(*c.TeamSettings.EnableConfirmNotificationsToChannel) 89 props["TimeBetweenUserTypingUpdatesMilliseconds"] = strconv.FormatInt(*c.ServiceSettings.TimeBetweenUserTypingUpdatesMilliseconds, 10) 90 props["EnableUserTypingMessages"] = strconv.FormatBool(*c.ServiceSettings.EnableUserTypingMessages) 91 props["EnableChannelViewedMessages"] = strconv.FormatBool(*c.ServiceSettings.EnableChannelViewedMessages) 92 93 props["RunJobs"] = strconv.FormatBool(*c.JobSettings.RunJobs) 94 95 props["EnableEmailInvitations"] = strconv.FormatBool(*c.ServiceSettings.EnableEmailInvitations) 96 97 props["CloudUserLimit"] = strconv.FormatInt(*c.ExperimentalSettings.CloudUserLimit, 10) 98 99 props["EnableLegacySidebar"] = strconv.FormatBool(*c.ServiceSettings.EnableLegacySidebar) 100 101 props["EnableReliableWebSockets"] = strconv.FormatBool(*c.ServiceSettings.EnableReliableWebSockets) 102 103 // Set default values for all options that require a license. 104 props["ExperimentalHideTownSquareinLHS"] = "false" 105 props["ExperimentalTownSquareIsReadOnly"] = "false" 106 props["ExperimentalEnableAuthenticationTransfer"] = "true" 107 props["LdapNicknameAttributeSet"] = "false" 108 props["LdapFirstNameAttributeSet"] = "false" 109 props["LdapLastNameAttributeSet"] = "false" 110 props["LdapPictureAttributeSet"] = "false" 111 props["LdapPositionAttributeSet"] = "false" 112 props["EnableCompliance"] = "false" 113 props["EnableMobileFileDownload"] = "true" 114 props["EnableMobileFileUpload"] = "true" 115 props["SamlFirstNameAttributeSet"] = "false" 116 props["SamlLastNameAttributeSet"] = "false" 117 props["SamlNicknameAttributeSet"] = "false" 118 props["SamlPositionAttributeSet"] = "false" 119 props["EnableCluster"] = "false" 120 props["EnableMetrics"] = "false" 121 props["EnableBanner"] = "false" 122 props["BannerText"] = "" 123 props["BannerColor"] = "" 124 props["BannerTextColor"] = "" 125 props["AllowBannerDismissal"] = "false" 126 props["EnableThemeSelection"] = "true" 127 props["DefaultTheme"] = "" 128 props["AllowCustomThemes"] = "true" 129 props["AllowedThemes"] = "" 130 props["DataRetentionEnableMessageDeletion"] = "false" 131 props["DataRetentionMessageRetentionDays"] = "0" 132 props["DataRetentionEnableFileDeletion"] = "false" 133 props["DataRetentionFileRetentionDays"] = "0" 134 props["CWSUrl"] = "" 135 136 props["CustomUrlSchemes"] = strings.Join(c.DisplaySettings.CustomUrlSchemes, ",") 137 props["IsDefaultMarketplace"] = strconv.FormatBool(*c.PluginSettings.MarketplaceUrl == model.PLUGIN_SETTINGS_DEFAULT_MARKETPLACE_URL) 138 props["ExperimentalSharedChannels"] = "false" 139 props["CollapsedThreads"] = *c.ServiceSettings.CollapsedThreads 140 141 if license != nil { 142 props["ExperimentalHideTownSquareinLHS"] = strconv.FormatBool(*c.TeamSettings.ExperimentalHideTownSquareinLHS) 143 props["ExperimentalTownSquareIsReadOnly"] = strconv.FormatBool(*c.TeamSettings.ExperimentalTownSquareIsReadOnly) 144 props["ExperimentalEnableAuthenticationTransfer"] = strconv.FormatBool(*c.ServiceSettings.ExperimentalEnableAuthenticationTransfer) 145 146 if *license.Features.LDAP { 147 props["LdapNicknameAttributeSet"] = strconv.FormatBool(*c.LdapSettings.NicknameAttribute != "") 148 props["LdapFirstNameAttributeSet"] = strconv.FormatBool(*c.LdapSettings.FirstNameAttribute != "") 149 props["LdapLastNameAttributeSet"] = strconv.FormatBool(*c.LdapSettings.LastNameAttribute != "") 150 props["LdapPictureAttributeSet"] = strconv.FormatBool(*c.LdapSettings.PictureAttribute != "") 151 props["LdapPositionAttributeSet"] = strconv.FormatBool(*c.LdapSettings.PositionAttribute != "") 152 } 153 154 if *license.Features.Compliance { 155 props["EnableCompliance"] = strconv.FormatBool(*c.ComplianceSettings.Enable) 156 props["EnableMobileFileDownload"] = strconv.FormatBool(*c.FileSettings.EnableMobileDownload) 157 props["EnableMobileFileUpload"] = strconv.FormatBool(*c.FileSettings.EnableMobileUpload) 158 } 159 160 if *license.Features.SAML { 161 props["SamlFirstNameAttributeSet"] = strconv.FormatBool(*c.SamlSettings.FirstNameAttribute != "") 162 props["SamlLastNameAttributeSet"] = strconv.FormatBool(*c.SamlSettings.LastNameAttribute != "") 163 props["SamlNicknameAttributeSet"] = strconv.FormatBool(*c.SamlSettings.NicknameAttribute != "") 164 props["SamlPositionAttributeSet"] = strconv.FormatBool(*c.SamlSettings.PositionAttribute != "") 165 166 // do this under the correct licensed feature 167 props["ExperimentalClientSideCertEnable"] = strconv.FormatBool(*c.ExperimentalSettings.ClientSideCertEnable) 168 props["ExperimentalClientSideCertCheck"] = *c.ExperimentalSettings.ClientSideCertCheck 169 } 170 171 if *license.Features.Cluster { 172 props["EnableCluster"] = strconv.FormatBool(*c.ClusterSettings.Enable) 173 } 174 175 if *license.Features.Cluster { 176 props["EnableMetrics"] = strconv.FormatBool(*c.MetricsSettings.Enable) 177 } 178 179 if *license.Features.Announcement { 180 props["EnableBanner"] = strconv.FormatBool(*c.AnnouncementSettings.EnableBanner) 181 props["BannerText"] = *c.AnnouncementSettings.BannerText 182 props["BannerColor"] = *c.AnnouncementSettings.BannerColor 183 props["BannerTextColor"] = *c.AnnouncementSettings.BannerTextColor 184 props["AllowBannerDismissal"] = strconv.FormatBool(*c.AnnouncementSettings.AllowBannerDismissal) 185 } 186 187 if *license.Features.ThemeManagement { 188 props["EnableThemeSelection"] = strconv.FormatBool(*c.ThemeSettings.EnableThemeSelection) 189 props["DefaultTheme"] = *c.ThemeSettings.DefaultTheme 190 props["AllowCustomThemes"] = strconv.FormatBool(*c.ThemeSettings.AllowCustomThemes) 191 props["AllowedThemes"] = strings.Join(c.ThemeSettings.AllowedThemes, ",") 192 } 193 194 if *license.Features.DataRetention { 195 props["DataRetentionEnableMessageDeletion"] = strconv.FormatBool(*c.DataRetentionSettings.EnableMessageDeletion) 196 props["DataRetentionMessageRetentionDays"] = strconv.FormatInt(int64(*c.DataRetentionSettings.MessageRetentionDays), 10) 197 props["DataRetentionEnableFileDeletion"] = strconv.FormatBool(*c.DataRetentionSettings.EnableFileDeletion) 198 props["DataRetentionFileRetentionDays"] = strconv.FormatInt(int64(*c.DataRetentionSettings.FileRetentionDays), 10) 199 } 200 201 if *license.Features.Cloud { 202 props["CWSUrl"] = *c.CloudSettings.CWSUrl 203 } 204 205 if *license.Features.SharedChannels { 206 props["ExperimentalSharedChannels"] = strconv.FormatBool(*c.ExperimentalSettings.EnableSharedChannels) 207 props["ExperimentalRemoteClusterService"] = strconv.FormatBool(c.FeatureFlags.EnableRemoteClusterService && *c.ExperimentalSettings.EnableRemoteClusterService) 208 } 209 } 210 211 return props 212 } 213 214 // GenerateLimitedClientConfig renders the given configuration for an untrusted client. 215 func GenerateLimitedClientConfig(c *model.Config, telemetryID string, license *model.License) map[string]string { 216 props := make(map[string]string) 217 218 props["Version"] = model.CurrentVersion 219 props["BuildNumber"] = model.BuildNumber 220 props["BuildDate"] = model.BuildDate 221 props["BuildHash"] = model.BuildHash 222 props["BuildHashEnterprise"] = model.BuildHashEnterprise 223 props["BuildEnterpriseReady"] = model.BuildEnterpriseReady 224 225 props["EnableBotAccountCreation"] = strconv.FormatBool(*c.ServiceSettings.EnableBotAccountCreation) 226 props["EnableFile"] = strconv.FormatBool(*c.LogSettings.EnableFile) 227 props["FileLevel"] = *c.LogSettings.FileLevel 228 229 props["SiteName"] = *c.TeamSettings.SiteName 230 props["WebsocketURL"] = strings.TrimRight(*c.ServiceSettings.WebsocketURL, "/") 231 props["WebsocketPort"] = fmt.Sprintf("%v", *c.ServiceSettings.WebsocketPort) 232 props["WebsocketSecurePort"] = fmt.Sprintf("%v", *c.ServiceSettings.WebsocketSecurePort) 233 props["EnableUserCreation"] = strconv.FormatBool(*c.TeamSettings.EnableUserCreation) 234 props["EnableOpenServer"] = strconv.FormatBool(*c.TeamSettings.EnableOpenServer) 235 236 props["AndroidLatestVersion"] = c.ClientRequirements.AndroidLatestVersion 237 props["AndroidMinVersion"] = c.ClientRequirements.AndroidMinVersion 238 props["DesktopLatestVersion"] = c.ClientRequirements.DesktopLatestVersion 239 props["DesktopMinVersion"] = c.ClientRequirements.DesktopMinVersion 240 props["IosLatestVersion"] = c.ClientRequirements.IosLatestVersion 241 props["IosMinVersion"] = c.ClientRequirements.IosMinVersion 242 243 props["EnableDiagnostics"] = strconv.FormatBool(*c.LogSettings.EnableDiagnostics) 244 245 props["EnableSignUpWithEmail"] = strconv.FormatBool(*c.EmailSettings.EnableSignUpWithEmail) 246 props["EnableSignInWithEmail"] = strconv.FormatBool(*c.EmailSettings.EnableSignInWithEmail) 247 props["EnableSignInWithUsername"] = strconv.FormatBool(*c.EmailSettings.EnableSignInWithUsername) 248 249 props["EmailLoginButtonColor"] = *c.EmailSettings.LoginButtonColor 250 props["EmailLoginButtonBorderColor"] = *c.EmailSettings.LoginButtonBorderColor 251 props["EmailLoginButtonTextColor"] = *c.EmailSettings.LoginButtonTextColor 252 253 props["EnableSignUpWithGitLab"] = strconv.FormatBool(*c.GitLabSettings.Enable) 254 255 props["TermsOfServiceLink"] = *c.SupportSettings.TermsOfServiceLink 256 props["PrivacyPolicyLink"] = *c.SupportSettings.PrivacyPolicyLink 257 props["AboutLink"] = *c.SupportSettings.AboutLink 258 props["HelpLink"] = *c.SupportSettings.HelpLink 259 props["ReportAProblemLink"] = *c.SupportSettings.ReportAProblemLink 260 props["SupportEmail"] = *c.SupportSettings.SupportEmail 261 props["EnableAskCommunityLink"] = strconv.FormatBool(*c.SupportSettings.EnableAskCommunityLink) 262 263 props["DefaultClientLocale"] = *c.LocalizationSettings.DefaultClientLocale 264 265 props["EnableCustomEmoji"] = strconv.FormatBool(*c.ServiceSettings.EnableCustomEmoji) 266 props["AppDownloadLink"] = *c.NativeAppSettings.AppDownloadLink 267 props["AndroidAppDownloadLink"] = *c.NativeAppSettings.AndroidAppDownloadLink 268 props["IosAppDownloadLink"] = *c.NativeAppSettings.IosAppDownloadLink 269 270 props["DiagnosticId"] = telemetryID 271 props["TelemetryId"] = telemetryID 272 props["DiagnosticsEnabled"] = strconv.FormatBool(*c.LogSettings.EnableDiagnostics) 273 274 props["HasImageProxy"] = strconv.FormatBool(*c.ImageProxySettings.Enable) 275 276 props["PluginsEnabled"] = strconv.FormatBool(*c.PluginSettings.Enable) 277 278 props["PasswordMinimumLength"] = fmt.Sprintf("%v", *c.PasswordSettings.MinimumLength) 279 props["PasswordRequireLowercase"] = strconv.FormatBool(*c.PasswordSettings.Lowercase) 280 props["PasswordRequireUppercase"] = strconv.FormatBool(*c.PasswordSettings.Uppercase) 281 props["PasswordRequireNumber"] = strconv.FormatBool(*c.PasswordSettings.Number) 282 props["PasswordRequireSymbol"] = strconv.FormatBool(*c.PasswordSettings.Symbol) 283 284 // Set default values for all options that require a license. 285 props["EnableCustomBrand"] = "false" 286 props["CustomBrandText"] = "" 287 props["CustomDescriptionText"] = "" 288 props["EnableLdap"] = "false" 289 props["LdapLoginFieldName"] = "" 290 props["LdapLoginButtonColor"] = "" 291 props["LdapLoginButtonBorderColor"] = "" 292 props["LdapLoginButtonTextColor"] = "" 293 props["EnableSaml"] = "false" 294 props["SamlLoginButtonText"] = "" 295 props["SamlLoginButtonColor"] = "" 296 props["SamlLoginButtonBorderColor"] = "" 297 props["SamlLoginButtonTextColor"] = "" 298 props["EnableSignUpWithGoogle"] = "false" 299 props["EnableSignUpWithOffice365"] = "false" 300 props["EnableSignUpWithOpenId"] = "false" 301 props["OpenIdButtonText"] = "" 302 props["OpenIdButtonColor"] = "" 303 props["CWSUrl"] = "" 304 props["EnableCustomBrand"] = strconv.FormatBool(*c.TeamSettings.EnableCustomBrand) 305 props["CustomBrandText"] = *c.TeamSettings.CustomBrandText 306 props["CustomDescriptionText"] = *c.TeamSettings.CustomDescriptionText 307 props["EnableMultifactorAuthentication"] = strconv.FormatBool(*c.ServiceSettings.EnableMultifactorAuthentication) 308 props["EnforceMultifactorAuthentication"] = "false" 309 props["EnableGuestAccounts"] = strconv.FormatBool(*c.GuestAccountsSettings.Enable) 310 props["GuestAccountsEnforceMultifactorAuthentication"] = strconv.FormatBool(*c.GuestAccountsSettings.EnforceMultifactorAuthentication) 311 312 if license != nil { 313 if *license.Features.LDAP { 314 props["EnableLdap"] = strconv.FormatBool(*c.LdapSettings.Enable) 315 props["LdapLoginFieldName"] = *c.LdapSettings.LoginFieldName 316 props["LdapLoginButtonColor"] = *c.LdapSettings.LoginButtonColor 317 props["LdapLoginButtonBorderColor"] = *c.LdapSettings.LoginButtonBorderColor 318 props["LdapLoginButtonTextColor"] = *c.LdapSettings.LoginButtonTextColor 319 } 320 321 if *license.Features.SAML { 322 props["EnableSaml"] = strconv.FormatBool(*c.SamlSettings.Enable) 323 props["SamlLoginButtonText"] = *c.SamlSettings.LoginButtonText 324 props["SamlLoginButtonColor"] = *c.SamlSettings.LoginButtonColor 325 props["SamlLoginButtonBorderColor"] = *c.SamlSettings.LoginButtonBorderColor 326 props["SamlLoginButtonTextColor"] = *c.SamlSettings.LoginButtonTextColor 327 } 328 329 if *license.Features.GoogleOAuth { 330 props["EnableSignUpWithGoogle"] = strconv.FormatBool(*c.GoogleSettings.Enable) 331 } 332 333 if *license.Features.Office365OAuth { 334 props["EnableSignUpWithOffice365"] = strconv.FormatBool(*c.Office365Settings.Enable) 335 } 336 337 if *license.Features.OpenId { 338 props["EnableSignUpWithOpenId"] = strconv.FormatBool(*c.OpenIdSettings.Enable) 339 props["OpenIdButtonColor"] = *c.OpenIdSettings.ButtonColor 340 props["OpenIdButtonText"] = *c.OpenIdSettings.ButtonText 341 } 342 343 if *license.Features.CustomTermsOfService { 344 props["EnableCustomTermsOfService"] = strconv.FormatBool(*c.SupportSettings.CustomTermsOfServiceEnabled) 345 props["CustomTermsOfServiceReAcceptancePeriod"] = strconv.FormatInt(int64(*c.SupportSettings.CustomTermsOfServiceReAcceptancePeriod), 10) 346 } 347 348 if *license.Features.MFA { 349 props["EnforceMultifactorAuthentication"] = strconv.FormatBool(*c.ServiceSettings.EnforceMultifactorAuthentication) 350 } 351 } 352 353 for key, value := range c.FeatureFlags.ToMap() { 354 props["FeatureFlag"+key] = value 355 } 356 357 return props 358 }