grafana/pkg/setting/setting.go

2063 lines
67 KiB
Go
Raw Normal View History

2014-10-05 09:50:04 -05:00
// Copyright 2014 Unknwon
// Copyright 2014 Torkel Ödegaard
2014-10-04 06:33:20 -05:00
package setting
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io/fs"
"net/http"
2014-10-04 06:33:20 -05:00
"net/url"
"os"
"path"
"path/filepath"
"regexp"
"strconv"
2014-10-04 06:33:20 -05:00
"strings"
"time"
"github.com/gobwas/glob"
"github.com/prometheus/common/model"
"gopkg.in/ini.v1"
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
"github.com/grafana/grafana-azure-sdk-go/v2/azsettings"
2021-09-21 06:08:52 -05:00
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/osutil"
2014-10-04 06:33:20 -05:00
)
type Scheme string
const (
HTTPScheme Scheme = "http"
HTTPSScheme Scheme = "https"
HTTP2Scheme Scheme = "h2"
SocketScheme Scheme = "socket"
2014-10-04 06:33:20 -05:00
)
2014-12-16 05:04:08 -06:00
const (
RedactedPassword = "*********"
DefaultHTTPAddr = "0.0.0.0"
Dev = "development"
Prod = "production"
ApplicationName = "Grafana"
2014-12-16 05:04:08 -06:00
)
// zoneInfo names environment variable for setting the path to look for the timezone database in go
const zoneInfo = "ZONEINFO"
2014-10-04 06:33:20 -05:00
var (
customInitPath = "conf/custom.ini"
2014-10-04 06:33:20 -05:00
// App settings.
Env = Dev
AppUrl string
AppSubUrl string
2014-10-04 06:33:20 -05:00
// build
BuildVersion string
BuildCommit string
EnterpriseBuildCommit string
BuildBranch string
BuildStamp int64
IsEnterprise bool
2018-11-15 07:42:09 -06:00
// packaging
Packaging = "unknown"
CookieSecure bool
CookieSameSiteDisabled bool
CookieSameSiteMode http.SameSite
2014-10-04 06:33:20 -05:00
)
2018-10-12 00:55:36 -05:00
// TODO move all global vars to this struct
type Cfg struct {
Target []string
Raw *ini.File
Logger log.Logger
// for logging purposes
configFiles []string
appliedCommandLineProperties []string
appliedEnvOverrides []string
2018-10-12 00:55:36 -05:00
// HTTP Server Settings
CertFile string
KeyFile string
CertPassword string
CertWatchInterval time.Duration
HTTPAddr string
HTTPPort string
Env string
AppURL string
AppSubURL string
InstanceName string
ServeFromSubPath bool
StaticRootPath string
Protocol Scheme
SocketGid int
SocketMode int
SocketPath string
RouterLogging bool
Domain string
CDNRootURL *url.URL
ReadTimeout time.Duration
EnableGzip bool
EnforceDomain bool
MinTLSVersion string
2018-10-12 00:55:36 -05:00
// Security settings
SecretKey string
EmailCodeValidMinutes int
// build
BuildVersion string
BuildCommit string
EnterpriseBuildCommit string
BuildBranch string
BuildStamp int64
IsEnterprise bool
// packaging
Packaging string
// Paths
HomePath string
ProvisioningPath string
DataPath string
LogsPath string
PluginsPath string
BundledPluginsPath string
EnterpriseLicensePath string
// SMTP email settings
Smtp SmtpSettings
// Rendering
ImagesDir string
CSVsDir string
PDFsDir string
RendererUrl string
RendererCallbackUrl string
RendererAuthToken string
RendererConcurrentRequestLimit int
RendererRenderKeyLifeTime time.Duration
Image Rendering: Add settings for default width, height and scale (#82040) * Add Image width & height * ability to change default width, height and scale * default ini * Update conf/defaults.ini Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update pkg/setting/setting.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update pkg/setting/setting.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Added docs, changed frontend * Update conf/defaults.ini Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update conf/defaults.ini Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update conf/defaults.ini Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update docs/sources/setup-grafana/configure-grafana/_index.md Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update pkg/api/dtos/frontend_settings.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update pkg/api/frontendsettings.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update pkg/api/render.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * add query float 64 * Update packages/grafana-runtime/src/config.ts Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update public/app/features/dashboard/components/ShareModal/utils.ts Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * spacing * fix tests * Update docs/sources/setup-grafana/configure-grafana/_index.md Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> * Update docs/sources/setup-grafana/configure-grafana/_index.md Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> * Update docs/sources/setup-grafana/configure-grafana/_index.md Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> --------- Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
2024-02-26 06:27:34 -06:00
RendererDefaultImageWidth int
RendererDefaultImageHeight int
RendererDefaultImageScale float64
// Security
DisableInitAdminCreation bool
DisableBruteForceLoginProtection bool
CookieSecure bool
CookieSameSiteDisabled bool
CookieSameSiteMode http.SameSite
AllowEmbedding bool
XSSProtectionHeader bool
ContentTypeProtectionHeader bool
StrictTransportSecurity bool
StrictTransportSecurityMaxAge int
StrictTransportSecurityPreload bool
StrictTransportSecuritySubDomains bool
// CSPEnabled toggles Content Security Policy support.
CSPEnabled bool
// CSPTemplate contains the Content Security Policy template.
CSPTemplate string
// CSPReportEnabled toggles Content Security Policy Report Only support.
CSPReportOnlyEnabled bool
// CSPReportOnlyTemplate contains the Content Security Policy Report Only template.
CSPReportOnlyTemplate string
AngularSupportEnabled bool
EnableFrontendSandboxForPlugins []string
DisableGravatar bool
DataProxyWhiteList map[string]bool
ActionsAllowPostURL string
TempDataLifetime time.Duration
// Plugins
PluginsEnableAlpha bool
PluginsAppsSkipVerifyTLS bool
PluginSettings PluginSettings
PluginsAllowUnsigned []string
PluginCatalogURL string
PluginCatalogHiddenPlugins []string
PluginAdminEnabled bool
PluginAdminExternalManageEnabled bool
PluginForcePublicKeyDownload bool
PluginSkipPublicKeyDownload bool
DisablePlugins []string
HideAngularDeprecation []string
PluginInstallToken string
ForwardHostEnvVars []string
PreinstallPlugins []InstallPlugin
PreinstallPluginsAsync bool
PluginsCDNURLTemplate string
PluginLogBackendRequests bool
Plugins: Allow loading panel plugins from a CDN (#59096) * POC: Plugins CDN reverse proxy * CDN proxy POC: changed env var names * Add authorization: false for /public path in frontend plugin loader * Moved CDN settings to Cfg, add some comments * Fix error 500 in asset fetch if plugin is not using CDN * Fix EnterpriseLicensePath declared twice * Fix linter complaining about whitespaces * Plugins CDN: Skip signature verification for CDN plugins * Plugins CDN: Skip manifest and signature check for cdn plugins * Plugins: use IsValid() and IsInternal() rather than equality checks * Plugins CDN: remove comment * Plugins CDN: Fix seeker can't seek when serving plugins from local fs * Plugins CDN: add back error codes in getLocalPluginAssets * Plugins CDN: call asset.Close() rather than asset.readSeekCloser.Close() * Plugins CDN: Fix panic in JsonApiErr when errorMessageCoder wraps a nil error * Plugins CDN: Add error handling to proxyCDNPluginAsset * Plugins CDN: replace errorMessageCoder with errutil * Plugins CDN POC: expose cdn plugin paths to frontend for system.js * Plugins CDN: Fix cdn plugins showing as unsigned in frontend * WIP: Add support for formatted URL * Fix missing cdnPluginsBaseURLs in GrafanaConfig * Plugins CDN: Remove reverse proxy mode and reverse proxy references * Plugins CDN: Simplify asset serving logic * Plugins CDN: sanitize redirect path * Plugins CDN: Removed unused pluginAsset type * Plugins CDN: Removed system.js changes * Plugins CDN: Return different system.js baseURL and module for cdn plugins * Plugins CDN: Ensure CDN is disabled for non-external plugins * lint * Plugins CDN: serve images and screenshots from CDN, refactoring * Lint * Plugins CDN: Fix URLs for system.js (baseUrl and module) * Plugins CDN: Add more tests for RelativeURLForSystemJS * Plugins CDN: Iterate only on apps when preloading * Plugins CDN: Refactoring * Plugins CDN: Add comments to url_constructor.go * Plugins CDN: Update defaultHGPluginsCDNBaseURL * Plugins CDN: undo extract meta from system js config * refactor(plugins): migrate systemjs css plugin to typescript * feat(plugins): introduce systemjs cdn loader plugin * feat(plugins): add systemjs load type * Plugins CDN: Removed RelativeURLForSystemJS * Plugins CDN: Log backend redirect hits along with plugin info * Plugins CDN: Add pluginsCDNBasePath to getFrontendSettingsMap * feat(plugins): introduce cdn loading for angular plugins * refactor(plugins): move systemjs cache buster into systemjsplugins directory * Plugins CDN: Rename pluginsCDNBasePath to pluginsCDNBaseURL * refactor(plugins): introduce pluginsCDNBaseURL to the frontend * Plugins CDN: Renamed "cdn base path" to "cdn url template" in backend * Plugins CDN: lint * merge with main * Instrumentation: Add prometheus counter for backend hits, log from Info to Warn * Config: Changed key from plugins_cdn.url to plugins.plugins_cdn_base_url * CDN: Add backend tests * Lint: goimports * Default CDN URL to empty string, * Do not use CDN in setImages and module if the url template is empty * CDN: Backend: Add test for frontend settings * CDN: Do not log missing module.js warn if plugin is being loaded from CDN * CDN: Add backend test for CDN plugin loader * Removed 'cdn' signature level, switch to 'valid' * Fix pfs.TestParseTreeTestdata for cdn plugin testdata dir * Fix TestLoader_Load * Fix gocyclo complexity of loadPlugins * Plugins CDN: Moved prometheus metric to api package, removed asset_path label * Fix missing in config * Changes after review * Add pluginscdn.Service * Fix tests * Refactoring * Moved all remaining CDN checks inside pluginscdn.Service * CDN url constructor: Renamed stringURLFor to stringPath * CDN: Moved asset URL functionality to assetpath service * CDN: Renamed HasCDN() to IsEnabled() * CDN: Replace assert with require * CDN: Changes after review * Assetpath: Handle url.Parse error * Fix plugin_resource_test * CDN: Change fallback redirect from 302 to 307 * goimports * Fix tests * Switch to contextmodel.ReqContext in plugins.go Co-authored-by: Will Browne <will.browne@grafana.com> Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com>
2023-01-27 08:08:17 -06:00
// Panels
DisableSanitizeHtml bool
// Metrics
MetricsEndpointEnabled bool
MetricsEndpointBasicAuthUsername string
MetricsEndpointBasicAuthPassword string
MetricsEndpointDisableTotalStats bool
// MetricsIncludeTeamLabel configures grafana to set a label for
// the team responsible for the code at Grafana labs. We don't expect anyone else to
// use this setting.
MetricsIncludeTeamLabel bool
MetricsTotalStatsIntervalSeconds int
MetricsGrafanaEnvironmentInfo map[string]string
// Dashboards
DashboardVersionsToKeep int
MinRefreshInterval string
DefaultHomeDashboardPath string
// Auth
LoginCookieName string
LoginMaxInactiveLifetime time.Duration
LoginMaxLifetime time.Duration
TokenRotationIntervalMinutes int
SigV4AuthEnabled bool
SigV4VerboseLogging bool
AzureAuthEnabled bool
AzureSkipOrgRoleSync bool
BasicAuthEnabled bool
BasicAuthStrongPasswordPolicy bool
AdminUser string
AdminPassword string
DisableLogin bool
AdminEmail string
DisableLoginForm bool
SignoutRedirectUrl string
IDResponseHeaderEnabled bool
IDResponseHeaderPrefix string
IDResponseHeaderNamespaces map[string]struct{}
ManagedServiceAccountsEnabled bool
AWS: Add aws plugin configuration (#31312) * add new conf and make sure its passed to frontend * change auth provider name * goimports * fixed after feedback * more updates after feedback * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update conf/sample.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update docs/sources/administration/configuration.md Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/setting/setting.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * updates after pr feedback * Update conf/defaults.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update conf/defaults.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update conf/sample.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2021-02-24 11:08:13 -06:00
// AWS Plugin Auth
AWSAllowedAuthProviders []string
AWSAssumeRoleEnabled bool
AWSSessionDuration string
AWSExternalId string
AWSListMetricsPageLimit int
AWSForwardSettingsPlugins []string
AWS: Add aws plugin configuration (#31312) * add new conf and make sure its passed to frontend * change auth provider name * goimports * fixed after feedback * more updates after feedback * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update conf/sample.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update docs/sources/administration/configuration.md Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/setting/setting.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * updates after pr feedback * Update conf/defaults.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update conf/defaults.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update conf/sample.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2021-02-24 11:08:13 -06:00
// Azure Cloud settings
Azure *azsettings.AzureSettings
// Auth proxy settings
AuthProxy AuthProxySettings
// OAuth
OAuthAutoLogin bool
OAuthLoginErrorMessage string
OAuthCookieMaxAge int
OAuthAllowInsecureEmailLookup bool
OAuthRefreshTokenServerLockMinWaitMs int64
JWTAuth AuthJWTSettings
ExtJWTAuth ExtJWTSettings
Auth: Passwordless Login Option Using Magic Links (#95436) * initial passwordless client * passwordless login page * Working basic e2e flow * Add todo comments * Improve the passwordless login flow * improved passwordless login, backend for passwordless signup * add expiration to emails * update email templates & render username & name fields on signup * improve email templates * change login page text while awaiting passwordless code * fix merge conflicts * use claims.TypeUser * add initial passwordless tests * better error messages * simplified error name * remove completed TODOs * linting & minor test improvements & rename passwordless routes * more linting fixes * move code generation to its own func, use locationService to get query params * fix ampersand in email templates & use passwordless api routes in LoginCtrl * txt emails more closely match html email copy * move passwordless auth behind experimental feature toggle * fix PasswordlessLogin property failing typecheck * make update-workspace * user correct placeholder * Update emails/templates/passwordless_verify_existing_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_existing_user.mjml Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_new_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_new_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_new_user.mjml Co-authored-by: Dan Cech <dcech@grafana.com> * use &amp; in email templates * Update emails/templates/passwordless_verify_existing_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * remove IP address validation * struct for passwordless settings * revert go.work.sum changes * mock locationService.getSearch in failing test --------- Co-authored-by: Mihaly Gyongyosi <mgyongyosi@users.noreply.github.com> Co-authored-by: Dan Cech <dcech@grafana.com>
2024-11-14 07:50:55 -06:00
PasswordlessMagicLinkAuth AuthPasswordlessMagicLinkSettings
// SSO Settings Auth
SSOSettingsReloadInterval time.Duration
SSOSettingsConfigurableProviders map[string]bool
// Dataproxy
SendUserHeader bool
DataProxyLogging bool
DataProxyTimeout int
DataProxyDialTimeout int
DataProxyTLSHandshakeTimeout int
DataProxyExpectContinueTimeout int
DataProxyMaxConnsPerHost int
DataProxyMaxIdleConns int
DataProxyKeepAlive int
DataProxyIdleConnTimeout int
ResponseLimit int64
DataProxyRowLimit int64
DataProxyUserAgent string
2019-03-03 14:48:00 -06:00
// DistributedCache
RemoteCacheOptions *RemoteCacheSettings
2019-03-12 01:32:47 -05:00
ViewersCanEdit bool
2019-03-12 01:32:47 -05:00
EditorsCanAdmin bool
Auth: Allow expiration of API keys (#17678) * Modify backend to allow expiration of API Keys * Add middleware test for expired api keys * Modify frontend to enable expiration of API Keys * Fix frontend tests * Fix migration and add index for `expires` field * Add api key tests for database access * Substitude time.Now() by a mock for test usage * Front-end modifications * Change input label to `Time to live` * Change input behavior to comply with the other similar * Add tooltip * Modify AddApiKey api call response Expiration should be *time.Time instead of string * Present expiration date in the selected timezone * Use kbn for transforming intervals to seconds * Use `assert` library for tests * Frontend fixes Add checks for empty/undefined/null values * Change expires column from datetime to integer * Restrict api key duration input It should be interval not number * AddApiKey must complain if SecondsToLive is negative * Declare ErrInvalidApiKeyExpiration * Move configuration to auth section * Update docs * Eliminate alias for models in modified files * Omit expiration from api response if empty * Eliminate Goconvey from test file * Fix test Do not sleep, use mocked timeNow() instead * Remove index for expires from api_key table The index should be anyway on both org_id and expires fields. However this commit eliminates completely the index for now since not many rows are expected to be in this table. * Use getTimeZone function * Minor change in api key listing The frontend should display a message instead of empty string if the key does not expire.
2019-06-26 01:47:03 -05:00
ApiKeyMaxSecondsToLive int64
// Check if a feature toggle is enabled
// Deprecated: use featuremgmt.FeatureFlags
IsFeatureToggleEnabled func(key string) bool // filled in dynamically
AnonymousEnabled bool
AnonymousOrgName string
AnonymousOrgRole string
AnonymousHideVersion bool
AnonymousDeviceLimit int64
DateFormats DateFormats
// User
UserInviteMaxLifetime time.Duration
HiddenUsers map[string]struct{}
CaseInsensitiveLogin bool // Login and Email will be considered case insensitive
UserLastSeenUpdateInterval time.Duration
VerificationEmailMaxLifetime time.Duration
// Service Accounts
SATokenExpirationDayLimit int
// Annotations
AnnotationCleanupJobBatchSize int64
AnnotationMaximumTagsLength int64
AlertingAnnotationCleanupSetting AnnotationCleanupSettings
DashboardAnnotationCleanupSettings AnnotationCleanupSettings
APIAnnotationCleanupSettings AnnotationCleanupSettings
// GrafanaJavascriptAgent config
GrafanaJavascriptAgent GrafanaJavascriptAgent
// Data sources
DataSourceLimit int
// Number of queries to be executed concurrently. Only for the datasource supports concurrency.
ConcurrentQueryCount int
// IP range access control
IPRangeACEnabled bool
IPRangeACAllowedURLs []*url.URL
IPRangeACSecretKey string
// SQL Data sources
SqlDatasourceMaxOpenConnsDefault int
SqlDatasourceMaxIdleConnsDefault int
SqlDatasourceMaxConnLifetimeDefault int
// Snapshots
SnapshotEnabled bool
ExternalSnapshotUrl string
ExternalSnapshotName string
ExternalEnabled bool
// Only used in https://snapshots.raintank.io/
SnapshotPublicMode bool
ErrTemplateName string
StackID string
Slug string
LocalFileSystemAvailable bool
// Analytics
CheckForGrafanaUpdates bool
CheckForPluginUpdates bool
ReportingDistributor string
ReportingEnabled bool
ApplicationInsightsConnectionString string
ApplicationInsightsEndpointUrl string
FeedbackLinksEnabled bool
ReportingStaticContext map[string]string
// Frontend analytics
GoogleAnalyticsID string
GoogleAnalytics4ID string
GoogleAnalytics4SendManualPageViews bool
GoogleTagManagerID string
RudderstackDataPlaneURL string
RudderstackWriteKey string
RudderstackSDKURL string
RudderstackConfigURL string
RudderstackIntegrationsURL string
IntercomSecret string
FrontendAnalyticsConsoleReporting bool
// LDAP
LDAPAuthEnabled bool
LDAPSkipOrgRoleSync bool
LDAPConfigFilePath string
LDAPAllowSignup bool
LDAPActiveSyncEnabled bool
LDAPSyncCron string
DefaultTheme string
DefaultLanguage string
HomePage string
Quota QuotaSettings
// User settings
AllowUserSignUp bool
AllowUserOrgCreate bool
VerifyEmailEnabled bool
LoginHint string
PasswordHint string
DisableSignoutMenu bool
ExternalUserMngLinkUrl string
ExternalUserMngLinkName string
ExternalUserMngInfo string
AutoAssignOrg bool
AutoAssignOrgId int
AutoAssignOrgRole string
LoginDefaultOrgId int64
OAuthSkipOrgRoleUpdateSync bool
// ExpressionsEnabled specifies whether expressions are enabled.
ExpressionsEnabled bool
ImageUploadProvider string
// LiveMaxConnections is a maximum number of WebSocket connections to
// Grafana Live ws endpoint (per Grafana server instance). 0 disables
// Live, -1 means unlimited connections.
LiveMaxConnections int
// LiveHAEngine is a type of engine to use to achieve HA with Grafana Live.
// Zero value means in-memory single node setup.
LiveHAEngine string
2024-09-25 15:20:35 -05:00
// LiveHAPRefix is a prefix for HA engine keys.
LiveHAPrefix string
// LiveHAEngineAddress is a connection address for Live HA engine.
LiveHAEngineAddress string
LiveHAEnginePassword string
// LiveAllowedOrigins is a set of origins accepted by Live. If not provided
// then Live uses AppURL as the only allowed origin.
LiveAllowedOrigins []string
// Grafana.com URL, used for OAuth redirect.
GrafanaComURL string
// Grafana.com API URL. Can be set separately to GrafanaComURL
// in case API is not publicly accessible.
// Defaults to GrafanaComURL setting + "/api" if unset.
GrafanaComAPIURL string
// Grafana.com SSO API token used for Unified SSO between instances and Grafana.com.
GrafanaComSSOAPIToken string
// Geomap base layer config
GeomapDefaultBaseLayerConfig map[string]any
GeomapEnableCustomBaseLayers bool
// Unified Alerting
UnifiedAlerting UnifiedAlertingSettings
// Query history
QueryHistoryEnabled bool
Storage StorageSettings
Search SearchSettings
SecureSocksDSProxy SecureSocksDSProxySettings
// SAML Auth
SAMLAuthEnabled bool
SAMLSkipOrgRoleSync bool
SAMLRoleValuesGrafanaAdmin string
AuthN: Embed an OAuth2 server for external service authentication (#68086) * Moving POC files from #64283 to a new branch Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Adding missing permission definition Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Force the service instantiation while client isn't merged Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Merge conf with main Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Leave go-sqlite3 version unchanged Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * tidy Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * User SearchUserPermissions instead of SearchUsersPermissions * Replace DummyKeyService with signingkeys.Service * Use user:id:<id> as subject * Fix introspection endpoint issue * Add X-Grafana-Org-Id to get_resources.bash script * Regenerate toggles_gen.go * Fix basic.go * Add GetExternalService tests * Add GetPublicKeyScopes tests * Add GetScopesOnUser tests * Add GetScopes tests * Add ParsePublicKeyPem tests * Add database test for GetByName * re-add comments * client tests added * Add GetExternalServicePublicKey tests * Add other test case to GetExternalServicePublicKey * client_credentials grant test * Add test to jwtbearer grant * Test Comments * Add handleKeyOptions tests * Add RSA key generation test * Add ECDSA by default to EmbeddedSigningKeysService * Clean up org id scope and audiences * Add audiences to the DB * Fix check on Audience * Fix double import * Add AC Store mock and align oauthserver tests * Fix test after rebase * Adding missing store function to mock * Fix double import * Add CODEOWNER * Fix some linting errors * errors don't need type assertion * Typo codeowners * use mockery for oauthserver store * Add feature toggle check * Fix db tests to handle the feature flag * Adding call to DeleteExternalServiceRole * Fix flaky test * Re-organize routes comments and plan futur work * Add client_id check to Extended JWT client * Clean up * Fix * Remove background service registry instantiation of the OAuth server * Comment cleanup * Remove unused client function * Update go.mod to use the latest ory/fosite commit * Remove oauth2_server related configs from defaults.ini * Add audiences to DTO * Fix flaky test * Remove registration endpoint and demo scripts. Document code * Rename packages * Remove the OAuthService vs OAuthServer confusion * fix incorrect import ext_jwt_test * Comments and order * Comment basic auth * Remove unecessary todo * Clean api * Moving ParsePublicKeyPem to utils * re ordering functions in service.go * Fix comment * comment on the redirect uri * Add RBAC actions, not only scopes * Fix tests * re-import featuremgmt in migrations * Fix wire * Fix scopes in test * Fix flaky test * Remove todo, the intersection should always return the minimal set * Remove unecessary check from intersection code * Allow env overrides on settings * remove the term app name * Remove app keyword for client instead and use Name instead of ExternalServiceName * LogID remove ExternalService ref * Use Name instead of ExternalServiceName * Imports order * Inline * Using ExternalService and ExternalServiceDTO * Remove xorm tags * comment * Rename client files * client -> external service * comments * Move test to correct package * slimmer test * cachedUser -> cachedExternalService * Fix aggregate store test * PluginAuthSession -> AuthSession * Revert the nil cehcks * Remove unecessary extra * Removing custom session * fix typo in test * Use constants for tests * Simplify HandleToken tests * Refactor the HandleTokenRequest test * test message * Review test * Prevent flacky test on client as well * go imports * Revert changes from 526e48ad4550fed7e2b753b9d0a0cc6097155f58 * AuthN: Change the External Service registration form (#68649) * AuthN: change the External Service registration form * Gen default permissions * Change demo script registration form * Remove unecessary comment * Nit. * Reduce cyclomatic complexity * Remove demo_scripts * Handle case with no service account * Comments * Group key gen * Nit. * Check the SaveExternalService test * Rename cachedUser to cachedClient in test * One more test case to database test * Comments * Remove last org scope Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Update pkg/services/oauthserver/utils/utils_test.go * Update pkg/services/sqlstore/migrations/oauthserver/migrations.go Remove comment * Update pkg/setting/setting.go Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> --------- Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com>
2023-05-25 08:38:30 -05:00
// OAuth2 Server
OAuth2ServerEnabled bool
// OAuth2Server supports the two recommended key types from the RFC https://www.rfc-editor.org/rfc/rfc7518#section-3.1: RS256 and ES256
OAuth2ServerGeneratedKeyTypeForClient string
OAuth2ServerAccessTokenLifespan time.Duration
RBAC RBACSettings
Zanzana ZanzanaSettings
// GRPC Server.
GRPCServerNetwork string
GRPCServerAddress string
GRPCServerTLSConfig *tls.Config
GRPCServerEnableLogging bool // log request and response of each unary gRPC call
GRPCServerMaxRecvMsgSize int
GRPCServerMaxSendMsgSize int
CustomResponseHeaders map[string]string
// This is used to override the general error message shown to users when we want to obfuscate a sensitive backend error
UserFacingDefaultError string
// DatabaseInstrumentQueries is used to decide if database queries
// should be instrumented with metrics, logs and traces.
// This needs to be on the global object since its used in the
// sqlstore package and HTTP middlewares.
DatabaseInstrumentQueries bool
// Public dashboards
PublicDashboardsEnabled bool
// Cloud Migration
CloudMigration CloudMigrationSettings
// Feature Management Settings
FeatureManagement FeatureMgmtSettings
// Alerting
AlertingEvaluationTimeout time.Duration
AlertingNotificationTimeout time.Duration
AlertingMaxAttempts int
AlertingMinInterval int64
// Explore UI
ExploreEnabled bool
ExploreDefaultTimeOffset string
// Help UI
HelpEnabled bool
// Profile UI
ProfileEnabled bool
// News Feed
NewsFeedEnabled bool
// Experimental scope settings
ScopesListScopesURL string
ScopesListDashboardsURL string
//Short Links
ShortLinkExpiration int
// Unified Storage
UnifiedStorage map[string]UnifiedStorageConfig
IndexPath string
IndexWorkers int
IndexMaxBatchSize int
IndexListLimit int
}
type UnifiedStorageConfig struct {
DualWriterMode rest.DualWriterMode
DualWriterPeriodicDataSyncJobEnabled bool
}
type InstallPlugin struct {
ID string `json:"id"`
Version string `json:"version"`
}
// AddChangePasswordLink returns if login form is disabled or not since
// the same intention can be used to hide both features.
func (cfg *Cfg) AddChangePasswordLink() bool {
return !(cfg.DisableLoginForm || cfg.DisableLogin)
}
type CommandLineArgs struct {
Config string
HomePath string
Args []string
}
func (cfg *Cfg) parseAppUrlAndSubUrl(section *ini.Section) (string, string, error) {
appUrl := valueAsString(section, "root_url", "http://localhost:3000/")
if appUrl[len(appUrl)-1] != '/' {
appUrl += "/"
}
// Check if has app suburl.
2015-01-30 07:21:32 -06:00
url, err := url.Parse(appUrl)
if err != nil {
cfg.Logger.Error("Invalid root_url.", "url", appUrl, "error", err)
2021-10-26 10:36:24 -05:00
os.Exit(1)
}
appSubUrl := strings.TrimSuffix(url.Path, "/")
return appUrl, appSubUrl, nil
}
func ToAbsUrl(relativeUrl string) string {
return AppUrl + relativeUrl
}
func RedactedValue(key, value string) string {
if value == "" {
return ""
}
uppercased := strings.ToUpper(key)
// Sensitive information: password, secrets etc
for _, pattern := range []string{
"PASSWORD",
"SECRET",
"PROVIDER_CONFIG",
"PRIVATE_KEY",
"SECRET_KEY",
"CERTIFICATE",
"ACCOUNT_KEY",
"ENCRYPTION_KEY",
"VAULT_TOKEN",
"CLIENT_SECRET",
"ENTERPRISE_LICENSE",
"GF_ENTITY_API_DB_PASS",
} {
if match, err := regexp.MatchString(pattern, uppercased); match && err == nil {
return RedactedPassword
}
}
for _, exception := range []string{
"RUDDERSTACK",
"APPLICATION_INSIGHTS",
"SENTRY",
} {
if strings.Contains(uppercased, exception) {
return value
}
}
if u, err := RedactedURL(value); err == nil {
return u
}
return value
}
func RedactedURL(value string) (string, error) {
// Value could be a list of URLs
chunks := util.SplitString(value)
for i, chunk := range chunks {
var hasTmpPrefix bool
const tmpPrefix = "http://"
if !strings.Contains(chunk, "://") {
chunk = tmpPrefix + chunk
hasTmpPrefix = true
}
u, err := url.Parse(chunk)
if err != nil {
return "", err
}
redacted := u.Redacted()
if hasTmpPrefix {
redacted = strings.Replace(redacted, tmpPrefix, "", 1)
}
chunks[i] = redacted
}
if strings.Contains(value, ",") {
return strings.Join(chunks, ","), nil
}
return strings.Join(chunks, " "), nil
}
func (cfg *Cfg) applyEnvVariableOverrides(file *ini.File) error {
cfg.appliedEnvOverrides = make([]string, 0)
for _, section := range file.Sections() {
for _, key := range section.Keys() {
envKey := EnvKey(section.Name(), key.Name())
envValue := os.Getenv(envKey)
if len(envValue) > 0 {
key.SetValue(envValue)
cfg.appliedEnvOverrides = append(cfg.appliedEnvOverrides, fmt.Sprintf("%s=%s", envKey, RedactedValue(envKey, envValue)))
}
}
}
return nil
}
func (cfg *Cfg) readGrafanaEnvironmentMetrics() error {
environmentMetricsSection := cfg.Raw.Section("metrics.environment_info")
keys := environmentMetricsSection.Keys()
cfg.MetricsGrafanaEnvironmentInfo = make(map[string]string, len(keys))
cfg.MetricsGrafanaEnvironmentInfo["version"] = cfg.BuildVersion
cfg.MetricsGrafanaEnvironmentInfo["commit"] = cfg.BuildCommit
if cfg.EnterpriseBuildCommit != "NA" && cfg.EnterpriseBuildCommit != "" {
cfg.MetricsGrafanaEnvironmentInfo["enterprise_commit"] = cfg.EnterpriseBuildCommit
}
for _, key := range keys {
labelName := model.LabelName(key.Name())
labelValue := model.LabelValue(key.Value())
if !labelName.IsValid() {
return fmt.Errorf("invalid label name in [metrics.environment_info] configuration. name %q", labelName)
}
if !labelValue.IsValid() {
return fmt.Errorf("invalid label value in [metrics.environment_info] configuration. name %q value %q", labelName, labelValue)
}
cfg.MetricsGrafanaEnvironmentInfo[string(labelName)] = string(labelValue)
}
return nil
}
func (cfg *Cfg) readAnnotationSettings() error {
section := cfg.Raw.Section("annotations")
cfg.AnnotationCleanupJobBatchSize = section.Key("cleanupjob_batchsize").MustInt64(100)
cfg.AnnotationMaximumTagsLength = section.Key("tags_length").MustInt64(500)
switch {
case cfg.AnnotationMaximumTagsLength > 4096:
// ensure that the configuration does not exceed the respective column size
return fmt.Errorf("[annotations.tags_length] configuration exceeds the maximum allowed (4096)")
case cfg.AnnotationMaximumTagsLength > 500:
cfg.Logger.Info("[annotations.tags_length] has been increased from its default value; this may affect the performance", "tagLength", cfg.AnnotationMaximumTagsLength)
case cfg.AnnotationMaximumTagsLength < 500:
cfg.Logger.Warn("[annotations.tags_length] is too low; the minimum allowed (500) is enforced")
cfg.AnnotationMaximumTagsLength = 500
}
dashboardAnnotation := cfg.Raw.Section("annotations.dashboard")
apiIAnnotation := cfg.Raw.Section("annotations.api")
var newAnnotationCleanupSettings = func(section *ini.Section, maxAgeField string) AnnotationCleanupSettings {
maxAge, err := gtime.ParseDuration(section.Key(maxAgeField).MustString(""))
if err != nil {
maxAge = 0
}
return AnnotationCleanupSettings{
MaxAge: maxAge,
MaxCount: section.Key("max_annotations_to_keep").MustInt64(0),
}
}
alertingAnnotations := cfg.Raw.Section("unified_alerting.state_history.annotations")
if alertingAnnotations.Key("max_age").Value() == "" && section.Key("max_annotations_to_keep").Value() == "" {
// Although this section is not documented anymore, we decided to keep it to avoid potential data-loss when user upgrades Grafana and does not change the setting.
// TODO delete some time after Grafana 11.
alertingSection := cfg.Raw.Section("alerting")
cleanup := newAnnotationCleanupSettings(alertingSection, "max_annotation_age")
if cleanup.MaxCount > 0 || cleanup.MaxAge > 0 {
cfg.Logger.Warn("settings 'max_annotations_to_keep' and 'max_annotation_age' in section [alerting] are deprecated. Please use settings 'max_annotations_to_keep' and 'max_age' in section [unified_alerting.state_history.annotations]")
}
cfg.AlertingAnnotationCleanupSetting = cleanup
} else {
cfg.AlertingAnnotationCleanupSetting = newAnnotationCleanupSettings(alertingAnnotations, "max_age")
}
cfg.DashboardAnnotationCleanupSettings = newAnnotationCleanupSettings(dashboardAnnotation, "max_age")
cfg.APIAnnotationCleanupSettings = newAnnotationCleanupSettings(apiIAnnotation, "max_age")
return nil
}
func (cfg *Cfg) readExpressionsSettings() {
expressions := cfg.Raw.Section("expressions")
cfg.ExpressionsEnabled = expressions.Key("enabled").MustBool(true)
}
type AnnotationCleanupSettings struct {
MaxAge time.Duration
MaxCount int64
}
func EnvKey(sectionName string, keyName string) string {
sN := strings.ToUpper(strings.ReplaceAll(sectionName, ".", "_"))
sN = strings.ReplaceAll(sN, "-", "_")
kN := strings.ToUpper(strings.ReplaceAll(keyName, ".", "_"))
envKey := fmt.Sprintf("GF_%s_%s", sN, kN)
return envKey
}
func (cfg *Cfg) applyCommandLineDefaultProperties(props map[string]string, file *ini.File) {
cfg.appliedCommandLineProperties = make([]string, 0)
for _, section := range file.Sections() {
for _, key := range section.Keys() {
keyString := fmt.Sprintf("default.%s.%s", section.Name(), key.Name())
value, exists := props[keyString]
if exists {
key.SetValue(value)
cfg.appliedCommandLineProperties = append(cfg.appliedCommandLineProperties,
fmt.Sprintf("%s=%s", keyString, RedactedValue(keyString, value)))
}
}
}
}
func (cfg *Cfg) applyCommandLineProperties(props map[string]string, file *ini.File) {
for _, section := range file.Sections() {
sectionName := section.Name() + "."
if section.Name() == ini.DefaultSection {
sectionName = ""
}
for _, key := range section.Keys() {
keyString := sectionName + key.Name()
value, exists := props[keyString]
if exists {
cfg.appliedCommandLineProperties = append(cfg.appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value))
key.SetValue(value)
}
}
}
}
func (cfg *Cfg) getCommandLineProperties(args []string) map[string]string {
props := make(map[string]string)
2014-10-04 06:33:20 -05:00
for _, arg := range args {
if !strings.HasPrefix(arg, "cfg:") {
continue
}
trimmed := strings.TrimPrefix(arg, "cfg:")
parts := strings.Split(trimmed, "=")
if len(parts) != 2 {
cfg.Logger.Error("Invalid command line argument.", "argument", arg)
2021-10-26 10:36:24 -05:00
os.Exit(1)
}
props[parts[0]] = parts[1]
}
return props
}
func makeAbsolute(path string, root string) string {
if filepath.IsAbs(path) {
return path
}
return filepath.Join(root, path)
}
func (cfg *Cfg) loadSpecifiedConfigFile(configFile string, masterFile *ini.File) error {
if configFile == "" {
configFile = filepath.Join(cfg.HomePath, customInitPath)
// return without error if custom file does not exist
if !pathExists(configFile) {
return nil
}
}
userConfig, err := ini.Load(configFile)
if err != nil {
return fmt.Errorf("failed to parse %q: %w", configFile, err)
}
// micro-optimization since we don't need to share this ini file. In
// general, prefer to leave this flag as true as it is by default to prevent
// data races
userConfig.BlockMode = false
for _, section := range userConfig.Sections() {
for _, key := range section.Keys() {
if key.Value() == "" {
continue
}
defaultSec, err := masterFile.GetSection(section.Name())
if err != nil {
defaultSec, _ = masterFile.NewSection(section.Name())
}
defaultKey, err := defaultSec.GetKey(key.Name())
if err != nil {
defaultKey, _ = defaultSec.NewKey(key.Name(), key.Value())
}
defaultKey.SetValue(key.Value())
}
}
cfg.configFiles = append(cfg.configFiles, configFile)
return nil
}
func (cfg *Cfg) loadConfiguration(args CommandLineArgs) (*ini.File, error) {
// load config defaults
defaultConfigFile := path.Join(cfg.HomePath, "conf/defaults.ini")
cfg.configFiles = append(cfg.configFiles, defaultConfigFile)
// check if config file exists
if _, err := os.Stat(defaultConfigFile); os.IsNotExist(err) {
fmt.Println("Grafana-server Init Failed: Could not find config defaults, make sure homepath command line parameter is set or working directory is homepath")
os.Exit(1)
}
// load defaults
parsedFile, err := ini.Load(defaultConfigFile)
if err != nil {
fmt.Printf("Failed to parse defaults.ini, %v\n", err)
os.Exit(1)
return nil, err
}
// command line props
commandLineProps := cfg.getCommandLineProperties(args.Args)
// load default overrides
cfg.applyCommandLineDefaultProperties(commandLineProps, parsedFile)
// load specified config file
err = cfg.loadSpecifiedConfigFile(args.Config, parsedFile)
if err != nil {
err2 := cfg.initLogging(parsedFile)
if err2 != nil {
return nil, err2
}
cfg.Logger.Error(err.Error())
2021-10-26 10:36:24 -05:00
os.Exit(1)
}
2014-10-04 06:33:20 -05:00
// apply environment overrides
err = cfg.applyEnvVariableOverrides(parsedFile)
if err != nil {
return nil, err
}
// apply command line overrides
cfg.applyCommandLineProperties(commandLineProps, parsedFile)
// evaluate config values containing environment variables
err = expandConfig(parsedFile)
if err != nil {
return nil, err
}
// update data path and logging config
dataPath := valueAsString(parsedFile.Section("paths"), "data", "")
cfg.DataPath = makeAbsolute(dataPath, cfg.HomePath)
err = cfg.initLogging(parsedFile)
if err != nil {
return nil, err
}
2022-02-03 09:20:02 -06:00
cfg.Logger.Info(fmt.Sprintf("Starting %s", ApplicationName), "version", BuildVersion, "commit", BuildCommit, "branch", BuildBranch, "compiled", time.Unix(BuildStamp, 0))
return parsedFile, err
}
func pathExists(path string) bool {
_, err := os.Stat(path)
if err == nil {
return true
}
if os.IsNotExist(err) {
return false
}
return false
}
func (cfg *Cfg) setHomePath(args CommandLineArgs) {
if args.HomePath != "" {
cfg.HomePath = args.HomePath
return
}
var err error
cfg.HomePath, err = filepath.Abs(".")
if err != nil {
panic(err)
}
// check if homepath is correct
if pathExists(filepath.Join(cfg.HomePath, "conf/defaults.ini")) {
return
}
// try down one path
if pathExists(filepath.Join(cfg.HomePath, "../conf/defaults.ini")) {
cfg.HomePath = filepath.Join(cfg.HomePath, "../")
}
}
Remove redundancy in variable declarations (golint) This commit fixes the following golint warnings: pkg/api/avatar/avatar.go:229:12: should omit type *http.Client from declaration of var client; it will be inferred from the right-hand side pkg/login/brute_force_login_protection.go:13:26: should omit type time.Duration from declaration of var loginAttemptsWindow; it will be inferred from the right-hand side pkg/metrics/graphitebridge/graphite.go:58:26: should omit type []string from declaration of var metricCategoryPrefix; it will be inferred from the right-hand side pkg/metrics/graphitebridge/graphite.go:69:22: should omit type []string from declaration of var trimMetricPrefix; it will be inferred from the right-hand side pkg/models/alert.go:37:36: should omit type error from declaration of var ErrCannotChangeStateOnPausedAlert; it will be inferred from the right-hand side pkg/models/alert.go:38:36: should omit type error from declaration of var ErrRequiresNewState; it will be inferred from the right-hand side pkg/models/datasource.go:61:28: should omit type map[string]bool from declaration of var knownDatasourcePlugins; it will be inferred from the right-hand side pkg/plugins/update_checker.go:16:13: should omit type http.Client from declaration of var httpClient; it will be inferred from the right-hand side pkg/services/alerting/engine.go:103:24: should omit type time.Duration from declaration of var unfinishedWorkTimeout; it will be inferred from the right-hand side pkg/services/alerting/engine.go:105:19: should omit type time.Duration from declaration of var alertTimeout; it will be inferred from the right-hand side pkg/services/alerting/engine.go:106:19: should omit type int from declaration of var alertMaxAttempts; it will be inferred from the right-hand side pkg/services/alerting/notifier.go:143:23: should omit type map[string]*NotifierPlugin from declaration of var notifierFactories; it will be inferred from the right-hand side pkg/services/alerting/rule.go:136:24: should omit type map[string]ConditionFactory from declaration of var conditionFactories; it will be inferred from the right-hand side pkg/services/alerting/conditions/evaluator.go:12:15: should omit type []string from declaration of var defaultTypes; it will be inferred from the right-hand side pkg/services/alerting/conditions/evaluator.go:13:15: should omit type []string from declaration of var rangedTypes; it will be inferred from the right-hand side pkg/services/alerting/notifiers/opsgenie.go:44:19: should omit type string from declaration of var opsgenieAlertURL; it will be inferred from the right-hand side pkg/services/alerting/notifiers/pagerduty.go:43:23: should omit type string from declaration of var pagerdutyEventApiUrl; it will be inferred from the right-hand side pkg/services/alerting/notifiers/telegram.go:21:17: should omit type string from declaration of var telegramApiUrl; it will be inferred from the right-hand side pkg/services/provisioning/dashboards/config_reader_test.go:11:24: should omit type string from declaration of var simpleDashboardConfig; it will be inferred from the right-hand side pkg/services/provisioning/dashboards/config_reader_test.go:12:24: should omit type string from declaration of var oldVersion; it will be inferred from the right-hand side pkg/services/provisioning/dashboards/config_reader_test.go:13:24: should omit type string from declaration of var brokenConfigs; it will be inferred from the right-hand side pkg/services/provisioning/dashboards/file_reader.go:22:30: should omit type time.Duration from declaration of var checkDiskForChangesInterval; it will be inferred from the right-hand side pkg/services/provisioning/dashboards/file_reader.go:24:23: should omit type error from declaration of var ErrFolderNameMissing; it will be inferred from the right-hand side pkg/services/provisioning/datasources/config_reader_test.go:15:34: should omit type string from declaration of var twoDatasourcesConfig; it will be inferred from the right-hand side pkg/services/provisioning/datasources/config_reader_test.go:16:34: should omit type string from declaration of var twoDatasourcesConfigPurgeOthers; it will be inferred from the right-hand side pkg/services/provisioning/datasources/config_reader_test.go:17:34: should omit type string from declaration of var doubleDatasourcesConfig; it will be inferred from the right-hand side pkg/services/provisioning/datasources/config_reader_test.go:18:34: should omit type string from declaration of var allProperties; it will be inferred from the right-hand side pkg/services/provisioning/datasources/config_reader_test.go:19:34: should omit type string from declaration of var versionZero; it will be inferred from the right-hand side pkg/services/provisioning/datasources/config_reader_test.go:20:34: should omit type string from declaration of var brokenYaml; it will be inferred from the right-hand side pkg/services/sqlstore/stats.go:16:25: should omit type time.Duration from declaration of var activeUserTimeLimit; it will be inferred from the right-hand side pkg/services/sqlstore/migrator/mysql_dialect.go:69:14: should omit type bool from declaration of var hasLen1; it will be inferred from the right-hand side pkg/services/sqlstore/migrator/mysql_dialect.go:70:14: should omit type bool from declaration of var hasLen2; it will be inferred from the right-hand side pkg/services/sqlstore/migrator/postgres_dialect.go:95:14: should omit type bool from declaration of var hasLen1; it will be inferred from the right-hand side pkg/services/sqlstore/migrator/postgres_dialect.go:96:14: should omit type bool from declaration of var hasLen2; it will be inferred from the right-hand side pkg/setting/setting.go:42:15: should omit type string from declaration of var Env; it will be inferred from the right-hand side pkg/setting/setting.go:161:18: should omit type bool from declaration of var LdapAllowSignup; it will be inferred from the right-hand side pkg/setting/setting.go:473:30: should omit type bool from declaration of var skipStaticRootValidation; it will be inferred from the right-hand side pkg/tsdb/interval.go:14:21: should omit type time.Duration from declaration of var defaultMinInterval; it will be inferred from the right-hand side pkg/tsdb/interval.go:15:21: should omit type time.Duration from declaration of var year; it will be inferred from the right-hand side pkg/tsdb/interval.go:16:21: should omit type time.Duration from declaration of var day; it will be inferred from the right-hand side pkg/tsdb/cloudwatch/credentials.go:26:24: should omit type map[string]cache from declaration of var awsCredentialCache; it will be inferred from the right-hand side pkg/tsdb/influxdb/query.go:15:27: should omit type *regexp.Regexp from declaration of var regexpOperatorPattern; it will be inferred from the right-hand side pkg/tsdb/influxdb/query.go:16:27: should omit type *regexp.Regexp from declaration of var regexpMeasurementPattern; it will be inferred from the right-hand side pkg/tsdb/mssql/mssql_test.go:25:14: should omit type string from declaration of var serverIP; it will be inferred from the right-hand side
2018-04-27 15:14:36 -05:00
var skipStaticRootValidation = false
func NewCfg() *Cfg {
return &Cfg{
Env: Dev,
Target: []string{"all"},
Logger: log.New("settings"),
Raw: ini.Empty(),
Azure: &azsettings.AzureSettings{},
// Avoid nil pointer
IsFeatureToggleEnabled: func(_ string) bool {
return false
},
}
}
// Deprecated: Avoid using IsFeatureToggleEnabled from settings. If you need to access
// feature flags, read them from the FeatureToggle (or FeatureManager) interface
func NewCfgWithFeatures(features func(string) bool) *Cfg {
cfg := NewCfg()
cfg.IsFeatureToggleEnabled = features
return cfg
}
func NewCfgFromArgs(args CommandLineArgs) (*Cfg, error) {
cfg := NewCfg()
if err := cfg.Load(args); err != nil {
return nil, err
}
return cfg, nil
}
// NewCfgFromBytes specialized function to create a new Cfg from bytes (INI file).
func NewCfgFromBytes(bytes []byte) (*Cfg, error) {
parsedFile, err := ini.Load(bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse bytes as INI file: %w", err)
}
return NewCfgFromINIFile(parsedFile)
}
// NewCfgFromINIFile specialized function to create a new Cfg from an ini.File.
func NewCfgFromINIFile(iniFile *ini.File) (*Cfg, error) {
cfg := NewCfg()
if err := cfg.parseINIFile(iniFile); err != nil {
return nil, fmt.Errorf("failed to parse setting from INI file: %w", err)
}
return cfg, nil
}
func (cfg *Cfg) validateStaticRootPath() error {
if skipStaticRootValidation {
return nil
}
if _, err := os.Stat(path.Join(cfg.StaticRootPath, "build")); err != nil {
cfg.Logger.Error("Failed to detect generated javascript files in public/build")
}
return nil
}
func (cfg *Cfg) Load(args CommandLineArgs) error {
cfg.setHomePath(args)
// Fix for missing IANA db on Windows or Alpine
_, zoneInfoSet := os.LookupEnv(zoneInfo)
if !zoneInfoSet {
if err := os.Setenv(zoneInfo, filepath.Join(cfg.HomePath, "tools", "zoneinfo.zip")); err != nil {
cfg.Logger.Error("Can't set ZONEINFO environment variable", "err", err)
}
}
2018-10-12 00:55:36 -05:00
iniFile, err := cfg.loadConfiguration(args)
if err != nil {
return err
}
err = cfg.parseINIFile(iniFile)
if err != nil {
return err
}
cfg.LogConfigSources()
return nil
}
// nolint:gocyclo
func (cfg *Cfg) parseINIFile(iniFile *ini.File) error {
cfg.Raw = iniFile
cfg.BuildVersion = BuildVersion
cfg.BuildCommit = BuildCommit
cfg.EnterpriseBuildCommit = EnterpriseBuildCommit
cfg.BuildStamp = BuildStamp
cfg.BuildBranch = BuildBranch
cfg.IsEnterprise = IsEnterprise
cfg.Packaging = Packaging
cfg.ErrTemplateName = "error"
Target := valueAsString(iniFile.Section(""), "target", "all")
if Target != "" {
cfg.Target = util.SplitString(Target)
}
cfg.Env = valueAsString(iniFile.Section(""), "app_mode", "development")
cfg.StackID = valueAsString(iniFile.Section("environment"), "stack_id", "")
cfg.Slug = valueAsString(iniFile.Section("environment"), "stack_slug", "")
cfg.LocalFileSystemAvailable = iniFile.Section("environment").Key("local_file_system_available").MustBool(true)
cfg.InstanceName = valueAsString(iniFile.Section(""), "instance_name", "unknown_instance_name")
plugins := valueAsString(iniFile.Section("paths"), "plugins", "")
cfg.PluginsPath = makeAbsolute(plugins, cfg.HomePath)
cfg.BundledPluginsPath = makeAbsolute("plugins-bundled", cfg.HomePath)
provisioning := valueAsString(iniFile.Section("paths"), "provisioning", "")
cfg.ProvisioningPath = makeAbsolute(provisioning, cfg.HomePath)
if err := cfg.readServerSettings(iniFile); err != nil {
return err
}
if err := readDataProxySettings(iniFile, cfg); err != nil {
return err
}
if err := readSecuritySettings(iniFile, cfg); err != nil {
return err
}
if err := readSnapshotsSettings(cfg, iniFile); err != nil {
return err
}
if err := readGRPCServerSettings(cfg, iniFile); err != nil {
return err
}
// read dashboard settings
dashboards := iniFile.Section("dashboards")
cfg.DashboardVersionsToKeep = dashboards.Key("versions_to_keep").MustInt(20)
cfg.MinRefreshInterval = valueAsString(dashboards, "min_refresh_interval", "5s")
cfg.DefaultHomeDashboardPath = dashboards.Key("default_home_dashboard_path").MustString("")
2014-10-04 06:33:20 -05:00
if err := readUserSettings(iniFile, cfg); err != nil {
return err
}
if err := readServiceAccountSettings(iniFile, cfg); err != nil {
return err
}
if err := readAuthSettings(iniFile, cfg); err != nil {
return err
}
AuthN: Embed an OAuth2 server for external service authentication (#68086) * Moving POC files from #64283 to a new branch Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Adding missing permission definition Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Force the service instantiation while client isn't merged Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Merge conf with main Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Leave go-sqlite3 version unchanged Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * tidy Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * User SearchUserPermissions instead of SearchUsersPermissions * Replace DummyKeyService with signingkeys.Service * Use user:id:<id> as subject * Fix introspection endpoint issue * Add X-Grafana-Org-Id to get_resources.bash script * Regenerate toggles_gen.go * Fix basic.go * Add GetExternalService tests * Add GetPublicKeyScopes tests * Add GetScopesOnUser tests * Add GetScopes tests * Add ParsePublicKeyPem tests * Add database test for GetByName * re-add comments * client tests added * Add GetExternalServicePublicKey tests * Add other test case to GetExternalServicePublicKey * client_credentials grant test * Add test to jwtbearer grant * Test Comments * Add handleKeyOptions tests * Add RSA key generation test * Add ECDSA by default to EmbeddedSigningKeysService * Clean up org id scope and audiences * Add audiences to the DB * Fix check on Audience * Fix double import * Add AC Store mock and align oauthserver tests * Fix test after rebase * Adding missing store function to mock * Fix double import * Add CODEOWNER * Fix some linting errors * errors don't need type assertion * Typo codeowners * use mockery for oauthserver store * Add feature toggle check * Fix db tests to handle the feature flag * Adding call to DeleteExternalServiceRole * Fix flaky test * Re-organize routes comments and plan futur work * Add client_id check to Extended JWT client * Clean up * Fix * Remove background service registry instantiation of the OAuth server * Comment cleanup * Remove unused client function * Update go.mod to use the latest ory/fosite commit * Remove oauth2_server related configs from defaults.ini * Add audiences to DTO * Fix flaky test * Remove registration endpoint and demo scripts. Document code * Rename packages * Remove the OAuthService vs OAuthServer confusion * fix incorrect import ext_jwt_test * Comments and order * Comment basic auth * Remove unecessary todo * Clean api * Moving ParsePublicKeyPem to utils * re ordering functions in service.go * Fix comment * comment on the redirect uri * Add RBAC actions, not only scopes * Fix tests * re-import featuremgmt in migrations * Fix wire * Fix scopes in test * Fix flaky test * Remove todo, the intersection should always return the minimal set * Remove unecessary check from intersection code * Allow env overrides on settings * remove the term app name * Remove app keyword for client instead and use Name instead of ExternalServiceName * LogID remove ExternalService ref * Use Name instead of ExternalServiceName * Imports order * Inline * Using ExternalService and ExternalServiceDTO * Remove xorm tags * comment * Rename client files * client -> external service * comments * Move test to correct package * slimmer test * cachedUser -> cachedExternalService * Fix aggregate store test * PluginAuthSession -> AuthSession * Revert the nil cehcks * Remove unecessary extra * Removing custom session * fix typo in test * Use constants for tests * Simplify HandleToken tests * Refactor the HandleTokenRequest test * test message * Review test * Prevent flacky test on client as well * go imports * Revert changes from 526e48ad4550fed7e2b753b9d0a0cc6097155f58 * AuthN: Change the External Service registration form (#68649) * AuthN: change the External Service registration form * Gen default permissions * Change demo script registration form * Remove unecessary comment * Nit. * Reduce cyclomatic complexity * Remove demo_scripts * Handle case with no service account * Comments * Group key gen * Nit. * Check the SaveExternalService test * Rename cachedUser to cachedClient in test * One more test case to database test * Comments * Remove last org scope Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Update pkg/services/oauthserver/utils/utils_test.go * Update pkg/services/sqlstore/migrations/oauthserver/migrations.go Remove comment * Update pkg/setting/setting.go Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> --------- Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com>
2023-05-25 08:38:30 -05:00
readOAuth2ServerSettings(cfg)
cfg.readRBACSettings()
cfg.readZanzanaSettings()
if err := cfg.readRenderingSettings(iniFile); err != nil {
return err
}
cfg.TempDataLifetime = iniFile.Section("paths").Key("temp_data_lifetime").MustDuration(time.Second * 3600 * 24)
cfg.MetricsEndpointEnabled = iniFile.Section("metrics").Key("enabled").MustBool(true)
cfg.MetricsEndpointBasicAuthUsername = valueAsString(iniFile.Section("metrics"), "basic_auth_username", "")
cfg.MetricsEndpointBasicAuthPassword = valueAsString(iniFile.Section("metrics"), "basic_auth_password", "")
cfg.MetricsEndpointDisableTotalStats = iniFile.Section("metrics").Key("disable_total_stats").MustBool(false)
cfg.MetricsIncludeTeamLabel = iniFile.Section("metrics").Key("include_team_label").MustBool(false)
cfg.MetricsTotalStatsIntervalSeconds = iniFile.Section("metrics").Key("total_stats_collector_interval_seconds").MustInt(1800)
analytics := iniFile.Section("analytics")
cfg.CheckForGrafanaUpdates = analytics.Key("check_for_updates").MustBool(true)
cfg.CheckForPluginUpdates = analytics.Key("check_for_plugin_updates").MustBool(true)
cfg.GoogleAnalyticsID = analytics.Key("google_analytics_ua_id").String()
cfg.GoogleAnalytics4ID = analytics.Key("google_analytics_4_id").String()
cfg.GoogleAnalytics4SendManualPageViews = analytics.Key("google_analytics_4_send_manual_page_views").MustBool(false)
cfg.GoogleTagManagerID = analytics.Key("google_tag_manager_id").String()
cfg.RudderstackWriteKey = analytics.Key("rudderstack_write_key").String()
cfg.RudderstackDataPlaneURL = analytics.Key("rudderstack_data_plane_url").String()
cfg.RudderstackSDKURL = analytics.Key("rudderstack_sdk_url").String()
cfg.RudderstackConfigURL = analytics.Key("rudderstack_config_url").String()
cfg.RudderstackIntegrationsURL = analytics.Key("rudderstack_integrations_url").String()
cfg.IntercomSecret = analytics.Key("intercom_secret").String()
cfg.FrontendAnalyticsConsoleReporting = analytics.Key("browser_console_reporter").MustBool(false)
cfg.ReportingEnabled = analytics.Key("reporting_enabled").MustBool(true)
cfg.ReportingDistributor = analytics.Key("reporting_distributor").MustString("grafana-labs")
if len(cfg.ReportingDistributor) >= 100 {
cfg.ReportingDistributor = cfg.ReportingDistributor[:100]
}
cfg.ApplicationInsightsConnectionString = analytics.Key("application_insights_connection_string").String()
cfg.ApplicationInsightsEndpointUrl = analytics.Key("application_insights_endpoint_url").String()
cfg.FeedbackLinksEnabled = analytics.Key("feedback_links_enabled").MustBool(true)
// parse reporting static context string of key=value, key=value pairs into an object
cfg.ReportingStaticContext = make(map[string]string)
for _, pair := range strings.Split(analytics.Key("reporting_static_context").String(), ",") {
kv := strings.Split(pair, "=")
if len(kv) == 2 {
cfg.ReportingStaticContext[strings.TrimSpace("_static_context_"+kv[0])] = strings.TrimSpace(kv[1])
}
}
if err := cfg.readAlertingSettings(iniFile); err != nil {
return err
}
explore := iniFile.Section("explore")
cfg.ExploreEnabled = explore.Key("enabled").MustBool(true)
exploreDefaultTimeOffset := valueAsString(explore, "defaultTimeOffset", "1h")
// we want to ensure the value parses as a duration, but we send it forward as a string to the frontend
if _, err := gtime.ParseDuration(exploreDefaultTimeOffset); err != nil {
return err
} else {
cfg.ExploreDefaultTimeOffset = exploreDefaultTimeOffset
}
help := iniFile.Section("help")
cfg.HelpEnabled = help.Key("enabled").MustBool(true)
profile := iniFile.Section("profile")
cfg.ProfileEnabled = profile.Key("enabled").MustBool(true)
news := iniFile.Section("news")
cfg.NewsFeedEnabled = news.Key("news_feed_enabled").MustBool(true)
queryHistory := iniFile.Section("query_history")
cfg.QueryHistoryEnabled = queryHistory.Key("enabled").MustBool(true)
shortLinks := iniFile.Section("short_links")
cfg.ShortLinkExpiration = shortLinks.Key("expire_time").MustInt(7)
if cfg.ShortLinkExpiration > 365 {
cfg.Logger.Warn("short_links expire_time must be less than 366 days. Setting to 365 days")
cfg.ShortLinkExpiration = 365
}
panelsSection := iniFile.Section("panels")
cfg.DisableSanitizeHtml = panelsSection.Key("disable_sanitize_html").MustBool(false)
if err := cfg.readPluginSettings(iniFile); err != nil {
return err
}
// nolint:staticcheck
if err := cfg.readFeatureToggles(iniFile); err != nil {
return err
}
if err := cfg.ReadUnifiedAlertingSettings(iniFile); err != nil {
return err
}
// check old location for this option
if panelsSection.Key("enable_alpha").MustBool(false) {
cfg.PluginsEnableAlpha = true
}
cfg.readSAMLConfig()
cfg.readLDAPConfig()
cfg.handleAWSConfig()
cfg.readAzureSettings()
cfg.readAuthJWTSettings()
cfg.readAuthExtJWTSettings()
cfg.readAuthProxySettings()
cfg.readSessionConfig()
Auth: Passwordless Login Option Using Magic Links (#95436) * initial passwordless client * passwordless login page * Working basic e2e flow * Add todo comments * Improve the passwordless login flow * improved passwordless login, backend for passwordless signup * add expiration to emails * update email templates & render username & name fields on signup * improve email templates * change login page text while awaiting passwordless code * fix merge conflicts * use claims.TypeUser * add initial passwordless tests * better error messages * simplified error name * remove completed TODOs * linting & minor test improvements & rename passwordless routes * more linting fixes * move code generation to its own func, use locationService to get query params * fix ampersand in email templates & use passwordless api routes in LoginCtrl * txt emails more closely match html email copy * move passwordless auth behind experimental feature toggle * fix PasswordlessLogin property failing typecheck * make update-workspace * user correct placeholder * Update emails/templates/passwordless_verify_existing_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_existing_user.mjml Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_new_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_new_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * Update emails/templates/passwordless_verify_new_user.mjml Co-authored-by: Dan Cech <dcech@grafana.com> * use &amp; in email templates * Update emails/templates/passwordless_verify_existing_user.txt Co-authored-by: Dan Cech <dcech@grafana.com> * remove IP address validation * struct for passwordless settings * revert go.work.sum changes * mock locationService.getSearch in failing test --------- Co-authored-by: Mihaly Gyongyosi <mgyongyosi@users.noreply.github.com> Co-authored-by: Dan Cech <dcech@grafana.com>
2024-11-14 07:50:55 -06:00
cfg.readPasswordlessMagicLinkSettings()
if err := cfg.readSmtpSettings(); err != nil {
return err
}
if err := cfg.readAnnotationSettings(); err != nil {
return err
}
cfg.readQuotaSettings()
cfg.readExpressionsSettings()
if err := cfg.readGrafanaEnvironmentMetrics(); err != nil {
return err
}
cfg.readDataSourcesSettings()
cfg.readDataSourceSecuritySettings()
cfg.readSqlDataSourceSettings()
cfg.Storage = readStorageSettings(iniFile)
cfg.Search = readSearchSettings(iniFile)
var err error
cfg.SecureSocksDSProxy, err = readSecureSocksDSProxySettings(iniFile)
if err != nil {
// if the proxy is misconfigured, disable it rather than crashing
cfg.SecureSocksDSProxy.Enabled = false
cfg.Logger.Error("secure_socks_datasource_proxy unable to start up", "err", err.Error())
}
if cfg.VerifyEmailEnabled && !cfg.Smtp.Enabled {
cfg.Logger.Warn("require_email_validation is enabled but smtp is disabled")
}
// check old key name
grafanaComUrl := valueAsString(iniFile.Section("grafana_net"), "url", "")
if grafanaComUrl == "" {
grafanaComUrl = valueAsString(iniFile.Section("grafana_com"), "url", "https://grafana.com")
}
cfg.GrafanaComURL = grafanaComUrl
cfg.GrafanaComAPIURL = valueAsString(iniFile.Section("grafana_com"), "api_url", grafanaComUrl+"/api")
cfg.GrafanaComSSOAPIToken = valueAsString(iniFile.Section("grafana_com"), "sso_api_token", "")
imageUploadingSection := iniFile.Section("external_image_storage")
cfg.ImageUploadProvider = valueAsString(imageUploadingSection, "provider", "")
enterprise := iniFile.Section("enterprise")
cfg.EnterpriseLicensePath = valueAsString(enterprise, "license_path", filepath.Join(cfg.DataPath, "license.jwt"))
geomapSection := iniFile.Section("geomap")
basemapJSON := valueAsString(geomapSection, "default_baselayer_config", "")
if basemapJSON != "" {
layer := make(map[string]any)
err := json.Unmarshal([]byte(basemapJSON), &layer)
if err != nil {
cfg.Logger.Error("Error reading json from default_baselayer_config", "error", err)
} else {
cfg.GeomapDefaultBaseLayerConfig = layer
}
}
cfg.GeomapEnableCustomBaseLayers = geomapSection.Key("enable_custom_baselayers").MustBool(true)
cfg.readRemoteCacheSettings()
cfg.readDateFormats()
cfg.readGrafanaJavascriptAgentConfig()
if err := cfg.readLiveSettings(iniFile); err != nil {
return err
}
databaseSection := iniFile.Section("database")
cfg.DatabaseInstrumentQueries = databaseSection.Key("instrument_queries").MustBool(false)
logSection := iniFile.Section("log")
cfg.UserFacingDefaultError = logSection.Key("user_facing_default_error").MustString("please inspect Grafana server log for details")
cfg.readFeatureManagementConfig()
cfg.readPublicDashboardsSettings()
cfg.readCloudMigrationSettings()
// read experimental scopes settings.
scopesSection := iniFile.Section("scopes")
cfg.ScopesListScopesURL = scopesSection.Key("list_scopes_endpoint").MustString("")
cfg.ScopesListDashboardsURL = scopesSection.Key("list_dashboards_endpoint").MustString("")
2024-10-22 13:25:08 -05:00
// unified storage config
cfg.setUnifiedStorageConfig()
return nil
}
func valueAsString(section *ini.Section, keyName string, defaultValue string) string {
return section.Key(keyName).MustString(defaultValue)
}
func (cfg *Cfg) readSAMLConfig() {
samlSec := cfg.Raw.Section("auth.saml")
cfg.SAMLAuthEnabled = samlSec.Key("enabled").MustBool(false)
cfg.SAMLSkipOrgRoleSync = samlSec.Key("skip_org_role_sync").MustBool(false)
cfg.SAMLRoleValuesGrafanaAdmin = samlSec.Key("role_values_grafana_admin").MustString("")
}
func (cfg *Cfg) readLDAPConfig() {
ldapSec := cfg.Raw.Section("auth.ldap")
cfg.LDAPConfigFilePath = ldapSec.Key("config_file").String()
cfg.LDAPSyncCron = ldapSec.Key("sync_cron").String()
cfg.LDAPAuthEnabled = ldapSec.Key("enabled").MustBool(false)
cfg.LDAPSkipOrgRoleSync = ldapSec.Key("skip_org_role_sync").MustBool(false)
cfg.LDAPActiveSyncEnabled = ldapSec.Key("active_sync_enabled").MustBool(false)
cfg.LDAPAllowSignup = ldapSec.Key("allow_sign_up").MustBool(true)
}
func (cfg *Cfg) handleAWSConfig() {
AWS: Add aws plugin configuration (#31312) * add new conf and make sure its passed to frontend * change auth provider name * goimports * fixed after feedback * more updates after feedback * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update conf/sample.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update docs/sources/administration/configuration.md Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/setting/setting.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * updates after pr feedback * Update conf/defaults.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update conf/defaults.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update conf/sample.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2021-02-24 11:08:13 -06:00
awsPluginSec := cfg.Raw.Section("aws")
cfg.AWSAssumeRoleEnabled = awsPluginSec.Key("assume_role_enabled").MustBool(true)
allowedAuthProviders := awsPluginSec.Key("allowed_auth_providers").MustString("default,keys,credentials")
AWS: Add aws plugin configuration (#31312) * add new conf and make sure its passed to frontend * change auth provider name * goimports * fixed after feedback * more updates after feedback * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update conf/sample.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update docs/sources/administration/configuration.md Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/setting/setting.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * updates after pr feedback * Update conf/defaults.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update conf/defaults.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update conf/sample.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2021-02-24 11:08:13 -06:00
for _, authProvider := range strings.Split(allowedAuthProviders, ",") {
authProvider = strings.TrimSpace(authProvider)
if authProvider != "" {
cfg.AWSAllowedAuthProviders = append(cfg.AWSAllowedAuthProviders, authProvider)
}
}
cfg.AWSListMetricsPageLimit = awsPluginSec.Key("list_metrics_page_limit").MustInt(500)
cfg.AWSExternalId = awsPluginSec.Key("external_id").Value()
cfg.AWSSessionDuration = awsPluginSec.Key("session_duration").Value()
cfg.AWSForwardSettingsPlugins = util.SplitString(awsPluginSec.Key("forward_settings_to_plugins").String())
// Also set environment variables that can be used by core plugins
err := os.Setenv(awsds.AssumeRoleEnabledEnvVarKeyName, strconv.FormatBool(cfg.AWSAssumeRoleEnabled))
if err != nil {
cfg.Logger.Error(fmt.Sprintf("could not set environment variable '%s'", awsds.AssumeRoleEnabledEnvVarKeyName), err)
}
err = os.Setenv(awsds.AllowedAuthProvidersEnvVarKeyName, allowedAuthProviders)
if err != nil {
cfg.Logger.Error(fmt.Sprintf("could not set environment variable '%s'", awsds.AllowedAuthProvidersEnvVarKeyName), err)
}
err = os.Setenv(awsds.ListMetricsPageLimitKeyName, strconv.Itoa(cfg.AWSListMetricsPageLimit))
if err != nil {
cfg.Logger.Error(fmt.Sprintf("could not set environment variable '%s'", awsds.ListMetricsPageLimitKeyName), err)
}
err = os.Setenv(awsds.GrafanaAssumeRoleExternalIdKeyName, cfg.AWSExternalId)
if err != nil {
cfg.Logger.Error(fmt.Sprintf("could not set environment variable '%s'", awsds.GrafanaAssumeRoleExternalIdKeyName), err)
}
err = os.Setenv(awsds.SessionDurationEnvVarKeyName, cfg.AWSSessionDuration)
if err != nil {
cfg.Logger.Error(fmt.Sprintf("could not set environment variable '%s'", awsds.SessionDurationEnvVarKeyName), err)
}
AWS: Add aws plugin configuration (#31312) * add new conf and make sure its passed to frontend * change auth provider name * goimports * fixed after feedback * more updates after feedback * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update docs/sources/administration/configuration.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Update conf/sample.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update docs/sources/administration/configuration.md Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update pkg/setting/setting.go Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * updates after pr feedback * Update conf/defaults.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update conf/defaults.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> * Update conf/sample.ini Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com> Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2021-02-24 11:08:13 -06:00
}
func (cfg *Cfg) readSessionConfig() {
sec, _ := cfg.Raw.GetSection("session")
if sec != nil {
cfg.Logger.Warn(
"[Removed] Session setting was removed in v6.2, use remote_cache option instead",
)
}
}
func (cfg *Cfg) initLogging(file *ini.File) error {
logModeStr := valueAsString(file.Section("log"), "mode", "console")
// split on comma
logModes := strings.Split(logModeStr, ",")
// also try space
if len(logModes) == 1 {
logModes = strings.Split(logModeStr, " ")
}
logsPath := valueAsString(file.Section("paths"), "logs", "")
cfg.LogsPath = makeAbsolute(logsPath, cfg.HomePath)
return log.ReadLoggingConfig(logModes, cfg.LogsPath, file)
}
func (cfg *Cfg) LogConfigSources() {
var text bytes.Buffer
for _, file := range cfg.configFiles {
cfg.Logger.Info("Config loaded from", "file", file)
}
if len(cfg.appliedCommandLineProperties) > 0 {
for _, prop := range cfg.appliedCommandLineProperties {
cfg.Logger.Info("Config overridden from command line", "arg", prop)
}
}
if len(cfg.appliedEnvOverrides) > 0 {
text.WriteString("\tEnvironment variables used:\n")
for _, prop := range cfg.appliedEnvOverrides {
cfg.Logger.Info("Config overridden from Environment variable", "var", prop)
}
}
cfg.Logger.Info("Target", "target", cfg.Target)
cfg.Logger.Info("Path Home", "path", cfg.HomePath)
cfg.Logger.Info("Path Data", "path", cfg.DataPath)
cfg.Logger.Info("Path Logs", "path", cfg.LogsPath)
cfg.Logger.Info("Path Plugins", "path", cfg.PluginsPath)
cfg.Logger.Info("Path Provisioning", "path", cfg.ProvisioningPath)
cfg.Logger.Info("App mode " + cfg.Env)
}
type DynamicSection struct {
section *ini.Section
Logger log.Logger
env osutil.Env
}
// Key dynamically overrides keys with environment variables.
// As a side effect, the value of the setting key will be updated if an environment variable is present.
func (s *DynamicSection) Key(k string) *ini.Key {
envKey := EnvKey(s.section.Name(), k)
envValue := s.env.Getenv(envKey)
key := s.section.Key(k)
if len(envValue) == 0 {
return key
}
key.SetValue(envValue)
s.Logger.Info("Config overridden from Environment variable", "var", fmt.Sprintf("%s=%s", envKey, RedactedValue(envKey, envValue)))
return key
}
func (s *DynamicSection) KeysHash() map[string]string {
hash := s.section.KeysHash()
for k := range hash {
envKey := EnvKey(s.section.Name(), k)
envValue := s.env.Getenv(envKey)
if len(envValue) > 0 {
hash[k] = envValue
}
}
return hash
}
// SectionWithEnvOverrides dynamically overrides keys with environment variables.
// As a side effect, the value of the setting key will be updated if an environment variable is present.
func (cfg *Cfg) SectionWithEnvOverrides(s string) *DynamicSection {
return &DynamicSection{
section: cfg.Raw.Section(s),
Logger: cfg.Logger,
env: osutil.RealEnv{},
}
}
func readSecuritySettings(iniFile *ini.File, cfg *Cfg) error {
security := iniFile.Section("security")
cfg.SecretKey = valueAsString(security, "secret_key", "")
cfg.DisableGravatar = security.Key("disable_gravatar").MustBool(true)
cfg.DisableBruteForceLoginProtection = security.Key("disable_brute_force_login_protection").MustBool(false)
CookieSecure = security.Key("cookie_secure").MustBool(false)
cfg.CookieSecure = CookieSecure
samesiteString := valueAsString(security, "cookie_samesite", "lax")
if samesiteString == "disabled" {
CookieSameSiteDisabled = true
cfg.CookieSameSiteDisabled = CookieSameSiteDisabled
} else {
validSameSiteValues := map[string]http.SameSite{
"lax": http.SameSiteLaxMode,
"strict": http.SameSiteStrictMode,
"none": http.SameSiteNoneMode,
}
if samesite, ok := validSameSiteValues[samesiteString]; ok {
CookieSameSiteMode = samesite
cfg.CookieSameSiteMode = CookieSameSiteMode
} else {
CookieSameSiteMode = http.SameSiteLaxMode
cfg.CookieSameSiteMode = CookieSameSiteMode
}
}
cfg.AllowEmbedding = security.Key("allow_embedding").MustBool(false)
cfg.ContentTypeProtectionHeader = security.Key("x_content_type_options").MustBool(true)
cfg.XSSProtectionHeader = security.Key("x_xss_protection").MustBool(true)
cfg.ActionsAllowPostURL = security.Key("actions_allow_post_url").MustString("")
cfg.StrictTransportSecurity = security.Key("strict_transport_security").MustBool(false)
cfg.StrictTransportSecurityMaxAge = security.Key("strict_transport_security_max_age_seconds").MustInt(86400)
cfg.StrictTransportSecurityPreload = security.Key("strict_transport_security_preload").MustBool(false)
cfg.StrictTransportSecuritySubDomains = security.Key("strict_transport_security_subdomains").MustBool(false)
cfg.AngularSupportEnabled = security.Key("angular_support_enabled").MustBool(false)
cfg.CSPEnabled = security.Key("content_security_policy").MustBool(false)
cfg.CSPTemplate = security.Key("content_security_policy_template").MustString("")
cfg.CSPReportOnlyEnabled = security.Key("content_security_policy_report_only").MustBool(false)
cfg.CSPReportOnlyTemplate = security.Key("content_security_policy_report_only_template").MustString("")
enableFrontendSandboxForPlugins := security.Key("enable_frontend_sandbox_for_plugins").MustString("")
for _, plug := range strings.Split(enableFrontendSandboxForPlugins, ",") {
plug = strings.TrimSpace(plug)
cfg.EnableFrontendSandboxForPlugins = append(cfg.EnableFrontendSandboxForPlugins, plug)
}
if cfg.CSPEnabled && cfg.CSPTemplate == "" {
return fmt.Errorf("enabling content_security_policy requires a content_security_policy_template configuration")
}
if cfg.CSPReportOnlyEnabled && cfg.CSPReportOnlyTemplate == "" {
return fmt.Errorf("enabling content_security_policy_report_only requires a content_security_policy_report_only_template configuration")
}
// read data source proxy whitelist
cfg.DataProxyWhiteList = make(map[string]bool)
securityStr := valueAsString(security, "data_source_proxy_whitelist", "")
for _, hostAndIP := range util.SplitString(securityStr) {
cfg.DataProxyWhiteList[hostAndIP] = true
}
2015-01-27 08:45:27 -06:00
// admin
cfg.DisableInitAdminCreation = security.Key("disable_initial_admin_creation").MustBool(false)
cfg.AdminUser = valueAsString(security, "admin_user", "")
cfg.AdminPassword = valueAsString(security, "admin_password", "")
cfg.AdminEmail = valueAsString(security, "admin_email", fmt.Sprintf("%s@localhost", cfg.AdminUser))
2015-01-27 08:45:27 -06:00
return nil
}
func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
auth := iniFile.Section("auth")
cfg.LoginCookieName = valueAsString(auth, "login_cookie_name", "grafana_session")
const defaultMaxInactiveLifetime = "7d"
maxInactiveDurationVal := valueAsString(auth, "login_maximum_inactive_lifetime_duration", defaultMaxInactiveLifetime)
cfg.LoginMaxInactiveLifetime, err = gtime.ParseDuration(maxInactiveDurationVal)
if err != nil {
return err
}
cfg.OAuthAllowInsecureEmailLookup = auth.Key("oauth_allow_insecure_email_lookup").MustBool(false)
const defaultMaxLifetime = "30d"
maxLifetimeDurationVal := valueAsString(auth, "login_maximum_lifetime_duration", defaultMaxLifetime)
cfg.LoginMaxLifetime, err = gtime.ParseDuration(maxLifetimeDurationVal)
if err != nil {
return err
}
Auth: Allow expiration of API keys (#17678) * Modify backend to allow expiration of API Keys * Add middleware test for expired api keys * Modify frontend to enable expiration of API Keys * Fix frontend tests * Fix migration and add index for `expires` field * Add api key tests for database access * Substitude time.Now() by a mock for test usage * Front-end modifications * Change input label to `Time to live` * Change input behavior to comply with the other similar * Add tooltip * Modify AddApiKey api call response Expiration should be *time.Time instead of string * Present expiration date in the selected timezone * Use kbn for transforming intervals to seconds * Use `assert` library for tests * Frontend fixes Add checks for empty/undefined/null values * Change expires column from datetime to integer * Restrict api key duration input It should be interval not number * AddApiKey must complain if SecondsToLive is negative * Declare ErrInvalidApiKeyExpiration * Move configuration to auth section * Update docs * Eliminate alias for models in modified files * Omit expiration from api response if empty * Eliminate Goconvey from test file * Fix test Do not sleep, use mocked timeNow() instead * Remove index for expires from api_key table The index should be anyway on both org_id and expires fields. However this commit eliminates completely the index for now since not many rows are expected to be in this table. * Use getTimeZone function * Minor change in api key listing The frontend should display a message instead of empty string if the key does not expire.
2019-06-26 01:47:03 -05:00
cfg.ApiKeyMaxSecondsToLive = auth.Key("api_key_max_seconds_to_live").MustInt64(-1)
cfg.TokenRotationIntervalMinutes = auth.Key("token_rotation_interval_minutes").MustInt(10)
if cfg.TokenRotationIntervalMinutes < 2 {
cfg.TokenRotationIntervalMinutes = 2
}
cfg.DisableLoginForm = auth.Key("disable_login_form").MustBool(false)
cfg.DisableSignoutMenu = auth.Key("disable_signout_menu").MustBool(false)
// Deprecated
cfg.OAuthAutoLogin = auth.Key("oauth_auto_login").MustBool(false)
if cfg.OAuthAutoLogin {
cfg.Logger.Warn("[Deprecated] The oauth_auto_login configuration setting is deprecated. Please use auto_login inside auth provider section instead.")
}
// Default to the translation key used in the frontend
cfg.OAuthLoginErrorMessage = valueAsString(auth, "oauth_login_error_message", "oauth.login.error")
cfg.OAuthCookieMaxAge = auth.Key("oauth_state_cookie_max_age").MustInt(600)
cfg.OAuthRefreshTokenServerLockMinWaitMs = auth.Key("oauth_refresh_token_server_lock_min_wait_ms").MustInt64(1000)
cfg.SignoutRedirectUrl = valueAsString(auth, "signout_redirect_url", "")
// Deprecated
cfg.OAuthSkipOrgRoleUpdateSync = false
cfg.DisableLogin = auth.Key("disable_login").MustBool(false)
// SigV4
cfg.SigV4AuthEnabled = auth.Key("sigv4_auth_enabled").MustBool(false)
cfg.SigV4VerboseLogging = auth.Key("sigv4_verbose_logging").MustBool(false)
// Azure Auth
cfg.AzureAuthEnabled = auth.Key("azure_auth_enabled").MustBool(false)
// ID response header
cfg.IDResponseHeaderEnabled = auth.Key("id_response_header_enabled").MustBool(false)
cfg.IDResponseHeaderPrefix = auth.Key("id_response_header_prefix").MustString("X-Grafana")
idHeaderNamespaces := util.SplitString(auth.Key("id_response_header_namespaces").MustString(""))
cfg.IDResponseHeaderNamespaces = make(map[string]struct{}, len(idHeaderNamespaces))
for _, namespace := range idHeaderNamespaces {
cfg.IDResponseHeaderNamespaces[namespace] = struct{}{}
}
2015-01-27 08:45:27 -06:00
// anonymous access
anonSection := iniFile.Section("auth.anonymous")
cfg.AnonymousEnabled = anonSection.Key("enabled").MustBool(false)
cfg.AnonymousOrgName = valueAsString(anonSection, "org_name", "")
cfg.AnonymousOrgRole = valueAsString(anonSection, "org_role", "")
cfg.AnonymousHideVersion = anonSection.Key("hide_version").MustBool(false)
cfg.AnonymousDeviceLimit = anonSection.Key("device_limit").MustInt64(0)
// basic auth
authBasic := iniFile.Section("auth.basic")
cfg.BasicAuthEnabled = authBasic.Key("enabled").MustBool(true)
cfg.BasicAuthStrongPasswordPolicy = authBasic.Key("password_policy").MustBool(false)
// SSO Settings
ssoSettings := iniFile.Section("sso_settings")
cfg.SSOSettingsReloadInterval = ssoSettings.Key("reload_interval").MustDuration(1 * time.Minute)
providers := ssoSettings.Key("configurable_providers").String()
cfg.SSOSettingsConfigurableProviders = make(map[string]bool)
for _, provider := range util.SplitString(providers) {
cfg.SSOSettingsConfigurableProviders[provider] = true
}
// Managed Service Accounts
cfg.ManagedServiceAccountsEnabled = auth.Key("managed_service_accounts_enabled").MustBool(false)
return nil
}
AuthN: Embed an OAuth2 server for external service authentication (#68086) * Moving POC files from #64283 to a new branch Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Adding missing permission definition Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Force the service instantiation while client isn't merged Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Merge conf with main Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Leave go-sqlite3 version unchanged Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * tidy Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * User SearchUserPermissions instead of SearchUsersPermissions * Replace DummyKeyService with signingkeys.Service * Use user:id:<id> as subject * Fix introspection endpoint issue * Add X-Grafana-Org-Id to get_resources.bash script * Regenerate toggles_gen.go * Fix basic.go * Add GetExternalService tests * Add GetPublicKeyScopes tests * Add GetScopesOnUser tests * Add GetScopes tests * Add ParsePublicKeyPem tests * Add database test for GetByName * re-add comments * client tests added * Add GetExternalServicePublicKey tests * Add other test case to GetExternalServicePublicKey * client_credentials grant test * Add test to jwtbearer grant * Test Comments * Add handleKeyOptions tests * Add RSA key generation test * Add ECDSA by default to EmbeddedSigningKeysService * Clean up org id scope and audiences * Add audiences to the DB * Fix check on Audience * Fix double import * Add AC Store mock and align oauthserver tests * Fix test after rebase * Adding missing store function to mock * Fix double import * Add CODEOWNER * Fix some linting errors * errors don't need type assertion * Typo codeowners * use mockery for oauthserver store * Add feature toggle check * Fix db tests to handle the feature flag * Adding call to DeleteExternalServiceRole * Fix flaky test * Re-organize routes comments and plan futur work * Add client_id check to Extended JWT client * Clean up * Fix * Remove background service registry instantiation of the OAuth server * Comment cleanup * Remove unused client function * Update go.mod to use the latest ory/fosite commit * Remove oauth2_server related configs from defaults.ini * Add audiences to DTO * Fix flaky test * Remove registration endpoint and demo scripts. Document code * Rename packages * Remove the OAuthService vs OAuthServer confusion * fix incorrect import ext_jwt_test * Comments and order * Comment basic auth * Remove unecessary todo * Clean api * Moving ParsePublicKeyPem to utils * re ordering functions in service.go * Fix comment * comment on the redirect uri * Add RBAC actions, not only scopes * Fix tests * re-import featuremgmt in migrations * Fix wire * Fix scopes in test * Fix flaky test * Remove todo, the intersection should always return the minimal set * Remove unecessary check from intersection code * Allow env overrides on settings * remove the term app name * Remove app keyword for client instead and use Name instead of ExternalServiceName * LogID remove ExternalService ref * Use Name instead of ExternalServiceName * Imports order * Inline * Using ExternalService and ExternalServiceDTO * Remove xorm tags * comment * Rename client files * client -> external service * comments * Move test to correct package * slimmer test * cachedUser -> cachedExternalService * Fix aggregate store test * PluginAuthSession -> AuthSession * Revert the nil cehcks * Remove unecessary extra * Removing custom session * fix typo in test * Use constants for tests * Simplify HandleToken tests * Refactor the HandleTokenRequest test * test message * Review test * Prevent flacky test on client as well * go imports * Revert changes from 526e48ad4550fed7e2b753b9d0a0cc6097155f58 * AuthN: Change the External Service registration form (#68649) * AuthN: change the External Service registration form * Gen default permissions * Change demo script registration form * Remove unecessary comment * Nit. * Reduce cyclomatic complexity * Remove demo_scripts * Handle case with no service account * Comments * Group key gen * Nit. * Check the SaveExternalService test * Rename cachedUser to cachedClient in test * One more test case to database test * Comments * Remove last org scope Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com> * Update pkg/services/oauthserver/utils/utils_test.go * Update pkg/services/sqlstore/migrations/oauthserver/migrations.go Remove comment * Update pkg/setting/setting.go Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> --------- Co-authored-by: Mihály Gyöngyösi <mgyongyosi@users.noreply.github.com>
2023-05-25 08:38:30 -05:00
func readOAuth2ServerSettings(cfg *Cfg) {
oauth2Srv := cfg.SectionWithEnvOverrides("oauth2_server")
cfg.OAuth2ServerEnabled = oauth2Srv.Key("enabled").MustBool(false)
cfg.OAuth2ServerGeneratedKeyTypeForClient = strings.ToUpper(oauth2Srv.Key("generated_key_type_for_client").In("ECDSA", []string{"RSA", "ECDSA"}))
cfg.OAuth2ServerAccessTokenLifespan = oauth2Srv.Key("access_token_lifespan").MustDuration(time.Minute * 3)
}
func readUserSettings(iniFile *ini.File, cfg *Cfg) error {
users := iniFile.Section("users")
cfg.AllowUserSignUp = users.Key("allow_sign_up").MustBool(true)
cfg.AllowUserOrgCreate = users.Key("allow_org_create").MustBool(true)
cfg.AutoAssignOrg = users.Key("auto_assign_org").MustBool(true)
cfg.AutoAssignOrgId = users.Key("auto_assign_org_id").MustInt(1)
cfg.LoginDefaultOrgId = users.Key("login_default_org_id").MustInt64(-1)
cfg.AutoAssignOrgRole = users.Key("auto_assign_org_role").In(
string(identity.RoleViewer), []string{
string(identity.RoleNone),
string(identity.RoleViewer),
string(identity.RoleEditor),
string(identity.RoleAdmin)})
cfg.VerifyEmailEnabled = users.Key("verify_email_enabled").MustBool(false)
// Deprecated
// cfg.CaseInsensitiveLogin = users.Key("case_insensitive_login").MustBool(true)
cfg.CaseInsensitiveLogin = true
cfg.LoginHint = valueAsString(users, "login_hint", "")
cfg.PasswordHint = valueAsString(users, "password_hint", "")
cfg.DefaultTheme = valueAsString(users, "default_theme", "")
cfg.DefaultLanguage = valueAsString(users, "default_language", "")
cfg.HomePage = valueAsString(users, "home_page", "")
cfg.ExternalUserMngLinkUrl = valueAsString(users, "external_manage_link_url", "")
cfg.ExternalUserMngLinkName = valueAsString(users, "external_manage_link_name", "")
cfg.ExternalUserMngInfo = valueAsString(users, "external_manage_info", "")
cfg.ViewersCanEdit = users.Key("viewers_can_edit").MustBool(false)
cfg.EditorsCanAdmin = users.Key("editors_can_admin").MustBool(false)
userInviteMaxLifetimeVal := valueAsString(users, "user_invite_max_lifetime_duration", "24h")
userInviteMaxLifetimeDuration, err := gtime.ParseDuration(userInviteMaxLifetimeVal)
if err != nil {
return err
}
cfg.UserInviteMaxLifetime = userInviteMaxLifetimeDuration
if cfg.UserInviteMaxLifetime < time.Minute*15 {
return errors.New("the minimum supported value for the `user_invite_max_lifetime_duration` configuration is 15m (15 minutes)")
}
cfg.UserLastSeenUpdateInterval, err = gtime.ParseDuration(valueAsString(users, "last_seen_update_interval", "15m"))
if err != nil {
return err
}
if cfg.UserLastSeenUpdateInterval < time.Minute*5 {
cfg.Logger.Warn("the minimum supported value for the `last_seen_update_interval` configuration is 5m (5 minutes)")
cfg.UserLastSeenUpdateInterval = time.Minute * 5
} else if cfg.UserLastSeenUpdateInterval > time.Hour*1 {
cfg.Logger.Warn("the maximum supported value for the `last_seen_update_interval` configuration is 1h (1 hour)")
cfg.UserLastSeenUpdateInterval = time.Hour * 1
}
cfg.HiddenUsers = make(map[string]struct{})
hiddenUsers := users.Key("hidden_users").MustString("")
for _, user := range strings.Split(hiddenUsers, ",") {
user = strings.TrimSpace(user)
if user != "" {
cfg.HiddenUsers[user] = struct{}{}
}
}
verificationEmailMaxLifetimeVal := valueAsString(users, "verification_email_max_lifetime_duration", "1h")
verificationEmailMaxLifetimeDuration, err := gtime.ParseDuration(verificationEmailMaxLifetimeVal)
if err != nil {
return err
}
cfg.VerificationEmailMaxLifetime = verificationEmailMaxLifetimeDuration
return nil
}
func readServiceAccountSettings(iniFile *ini.File, cfg *Cfg) error {
serviceAccount := iniFile.Section("service_accounts")
cfg.SATokenExpirationDayLimit = serviceAccount.Key("token_expiration_day_limit").MustInt(-1)
return nil
}
func (cfg *Cfg) readRenderingSettings(iniFile *ini.File) error {
renderSec := iniFile.Section("rendering")
cfg.RendererUrl = valueAsString(renderSec, "server_url", "")
cfg.RendererCallbackUrl = valueAsString(renderSec, "callback_url", "")
cfg.RendererAuthToken = valueAsString(renderSec, "renderer_token", "-")
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
2018-09-04 06:42:55 -05:00
if cfg.RendererCallbackUrl == "" {
cfg.RendererCallbackUrl = AppUrl
} else {
if cfg.RendererCallbackUrl[len(cfg.RendererCallbackUrl)-1] != '/' {
cfg.RendererCallbackUrl += "/"
}
_, err := url.Parse(cfg.RendererCallbackUrl)
if err != nil {
// XXX: Should return an error?
cfg.Logger.Error("Invalid callback_url.", "url", cfg.RendererCallbackUrl, "error", err)
2021-10-26 10:36:24 -05:00
os.Exit(1)
improve remote image rendering (#13102) * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * improve remote image rendering - determine "domain" during Init() so we are not re-parsing settings on every request - if using http-mode via a rednererUrl, then use the AppUrl for the page that the renderer loads. When in http-mode the renderer is likely running on another server so trying to use the localhost or even the specific IP:PORT grafana is listening on wont work. - apply the request timeout via a context rather then directly on the http client. - use a global http client so we can take advantage of connection re-use - log and handle errors better. * ensure imagesDir exists * allow users to define callback_url for remote rendering - allow users to define the url that a remote rendering service should use for connecting back to the grafana instance. By default the "root_url" is used. * rendering: fixed issue with renderKey where userId and orgId was in mixed up, added test for RenderCallbackUrl reading logic
2018-09-04 06:42:55 -05:00
}
}
cfg.RendererConcurrentRequestLimit = renderSec.Key("concurrent_render_request_limit").MustInt(30)
cfg.RendererRenderKeyLifeTime = renderSec.Key("render_key_lifetime").MustDuration(5 * time.Minute)
Image Rendering: Add settings for default width, height and scale (#82040) * Add Image width & height * ability to change default width, height and scale * default ini * Update conf/defaults.ini Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update pkg/setting/setting.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update pkg/setting/setting.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Added docs, changed frontend * Update conf/defaults.ini Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update conf/defaults.ini Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update conf/defaults.ini Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update docs/sources/setup-grafana/configure-grafana/_index.md Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update pkg/api/dtos/frontend_settings.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update pkg/api/frontendsettings.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update pkg/api/render.go Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * add query float 64 * Update packages/grafana-runtime/src/config.ts Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * Update public/app/features/dashboard/components/ShareModal/utils.ts Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> * spacing * fix tests * Update docs/sources/setup-grafana/configure-grafana/_index.md Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> * Update docs/sources/setup-grafana/configure-grafana/_index.md Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> * Update docs/sources/setup-grafana/configure-grafana/_index.md Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> --------- Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com> Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
2024-02-26 06:27:34 -06:00
cfg.RendererDefaultImageWidth = renderSec.Key("default_image_width").MustInt(1000)
cfg.RendererDefaultImageHeight = renderSec.Key("default_image_height").MustInt(500)
cfg.RendererDefaultImageScale = renderSec.Key("default_image_scale").MustFloat64(1)
2018-10-12 00:55:36 -05:00
cfg.ImagesDir = filepath.Join(cfg.DataPath, "png")
cfg.CSVsDir = filepath.Join(cfg.DataPath, "csv")
cfg.PDFsDir = filepath.Join(cfg.DataPath, "pdf")
2014-11-14 10:13:33 -06:00
return nil
}
func (cfg *Cfg) readAlertingSettings(iniFile *ini.File) error {
// This check is kept to prevent users that upgrade to Grafana 11 with the legacy alerting enabled. This should prevent them from accidentally upgrading without migration to Unified Alerting.
alerting := iniFile.Section("alerting")
enabled, err := alerting.Key("enabled").Bool()
if err == nil && enabled {
cfg.Logger.Error("Option '[alerting].enabled' cannot be true. Legacy Alerting is removed. It is no longer deployed, enhanced, or supported. Delete '[alerting].enabled' and use '[unified_alerting].enabled' to enable Grafana Alerting. For more information, refer to the documentation on upgrading to Grafana Alerting (https://grafana.com/docs/grafana/v10.4/alerting/set-up/migrating-alerts)")
return fmt.Errorf("invalid setting [alerting].enabled")
}
return nil
}
func readGRPCServerSettings(cfg *Cfg, iniFile *ini.File) error {
server := iniFile.Section("grpc_server")
errPrefix := "grpc_server:"
useTLS := server.Key("use_tls").MustBool(false)
certFile := server.Key("cert_file").String()
keyFile := server.Key("cert_key").String()
if useTLS {
serverCert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return fmt.Errorf("%s error loading X509 key pair: %w", errPrefix, err)
}
cfg.GRPCServerTLSConfig = &tls.Config{
Certificates: []tls.Certificate{serverCert},
ClientAuth: tls.NoClientCert,
}
}
cfg.GRPCServerNetwork = valueAsString(server, "network", "tcp")
cfg.GRPCServerAddress = valueAsString(server, "address", "")
cfg.GRPCServerEnableLogging = server.Key("enable_logging").MustBool(false)
cfg.GRPCServerMaxRecvMsgSize = server.Key("max_recv_msg_size").MustInt(0)
cfg.GRPCServerMaxSendMsgSize = server.Key("max_send_msg_size").MustInt(0)
switch cfg.GRPCServerNetwork {
case "unix":
if cfg.GRPCServerAddress != "" {
// Explicitly provided path for unix domain socket.
if stat, err := os.Stat(cfg.GRPCServerAddress); os.IsNotExist(err) {
// File does not exist - nice, nothing to do.
} else if err != nil {
return fmt.Errorf("%s error getting stat for a file: %s", errPrefix, cfg.GRPCServerAddress)
} else {
if stat.Mode()&fs.ModeSocket == 0 {
return fmt.Errorf("%s file %s already exists and is not a unix domain socket", errPrefix, cfg.GRPCServerAddress)
}
// Unix domain socket file, should be safe to remove.
err := os.Remove(cfg.GRPCServerAddress)
if err != nil {
return fmt.Errorf("%s can't remove unix socket file: %s", errPrefix, cfg.GRPCServerAddress)
}
}
} else {
// Use temporary file path for a unix domain socket.
tf, err := os.CreateTemp("", "gf_grpc_server_api")
if err != nil {
return fmt.Errorf("%s error creating tmp file: %v", errPrefix, err)
}
unixPath := tf.Name()
if err := tf.Close(); err != nil {
return fmt.Errorf("%s error closing tmp file: %v", errPrefix, err)
}
if err := os.Remove(unixPath); err != nil {
return fmt.Errorf("%s error removing tmp file: %v", errPrefix, err)
}
cfg.GRPCServerAddress = unixPath
}
case "tcp":
if cfg.GRPCServerAddress == "" {
cfg.GRPCServerAddress = "127.0.0.1:10000"
}
default:
return fmt.Errorf("%s unsupported network %s", errPrefix, cfg.GRPCServerNetwork)
}
return nil
}
func readSnapshotsSettings(cfg *Cfg, iniFile *ini.File) error {
snapshots := iniFile.Section("snapshots")
cfg.SnapshotEnabled = snapshots.Key("enabled").MustBool(true)
cfg.ExternalSnapshotUrl = valueAsString(snapshots, "external_snapshot_url", "")
cfg.ExternalSnapshotName = valueAsString(snapshots, "external_snapshot_name", "")
cfg.ExternalEnabled = snapshots.Key("external_enabled").MustBool(true)
cfg.SnapshotPublicMode = snapshots.Key("public_mode").MustBool(false)
return nil
}
func (cfg *Cfg) readServerSettings(iniFile *ini.File) error {
server := iniFile.Section("server")
var err error
AppUrl, AppSubUrl, err = cfg.parseAppUrlAndSubUrl(server)
if err != nil {
return err
}
cfg.AppURL = AppUrl
cfg.AppSubURL = AppSubUrl
cfg.Protocol = HTTPScheme
cfg.ServeFromSubPath = server.Key("serve_from_sub_path").MustBool(false)
cfg.CertWatchInterval = server.Key("certs_watch_interval").MustDuration(0)
protocolStr := valueAsString(server, "protocol", "http")
if protocolStr == "https" {
cfg.Protocol = HTTPSScheme
cfg.CertFile = server.Key("cert_file").String()
cfg.KeyFile = server.Key("cert_key").String()
cfg.CertPassword = server.Key("cert_pass").String()
}
if protocolStr == "h2" {
cfg.Protocol = HTTP2Scheme
cfg.CertFile = server.Key("cert_file").String()
cfg.KeyFile = server.Key("cert_key").String()
cfg.CertPassword = server.Key("cert_pass").String()
2019-03-03 14:48:00 -06:00
}
if protocolStr == "socket" {
cfg.Protocol = SocketScheme
cfg.SocketGid = server.Key("socket_gid").MustInt(-1)
cfg.SocketMode = server.Key("socket_mode").MustInt(0660)
cfg.SocketPath = server.Key("socket").String()
}
cfg.MinTLSVersion = valueAsString(server, "min_tls_version", "TLS1.2")
if cfg.MinTLSVersion == "TLS1.0" || cfg.MinTLSVersion == "TLS1.1" {
return fmt.Errorf("TLS version not configured correctly:%v, allowed values are TLS1.2 and TLS1.3", cfg.MinTLSVersion)
}
cfg.Domain = valueAsString(server, "domain", "localhost")
cfg.HTTPAddr = valueAsString(server, "http_addr", DefaultHTTPAddr)
cfg.HTTPPort = valueAsString(server, "http_port", "3000")
cfg.RouterLogging = server.Key("router_logging").MustBool(false)
cfg.EnableGzip = server.Key("enable_gzip").MustBool(false)
cfg.EnforceDomain = server.Key("enforce_domain").MustBool(false)
staticRoot := valueAsString(server, "static_root_path", "")
cfg.StaticRootPath = makeAbsolute(staticRoot, cfg.HomePath)
if err := cfg.validateStaticRootPath(); err != nil {
return err
}
cdnURL := valueAsString(server, "cdn_url", "")
if cdnURL != "" {
cfg.CDNRootURL, err = url.Parse(cdnURL)
if err != nil {
return err
}
}
cfg.ReadTimeout = server.Key("read_timeout").MustDuration(0)
headersSection := cfg.Raw.Section("server.custom_response_headers")
keys := headersSection.Keys()
cfg.CustomResponseHeaders = make(map[string]string, len(keys))
for _, key := range keys {
cfg.CustomResponseHeaders[key.Name()] = key.Value()
}
return nil
}
// GetContentDeliveryURL returns full content delivery URL with /<edition>/<version> added to URL
func (cfg *Cfg) GetContentDeliveryURL(prefix string) (string, error) {
if cfg.CDNRootURL == nil {
return "", nil
}
if cfg.BuildVersion == "" {
return "", errors.New("BuildVersion is not set")
}
url := *cfg.CDNRootURL
url.Path = path.Join(url.Path, prefix, cfg.BuildVersion)
return url.String() + "/", nil
}
func (cfg *Cfg) readDataSourcesSettings() {
datasources := cfg.Raw.Section("datasources")
cfg.DataSourceLimit = datasources.Key("datasource_limit").MustInt(5000)
cfg.ConcurrentQueryCount = datasources.Key("concurrent_query_count").MustInt(10)
}
func (cfg *Cfg) readDataSourceSecuritySettings() {
datasources := cfg.Raw.Section("datasources.ip_range_security")
cfg.IPRangeACEnabled = datasources.Key("enabled").MustBool(false)
cfg.IPRangeACSecretKey = datasources.Key("secret_key").MustString("")
if cfg.IPRangeACEnabled && cfg.IPRangeACSecretKey == "" {
cfg.Logger.Error("IP range access control is enabled but no secret key is set")
}
allowedURLString := datasources.Key("allow_list").MustString("")
for _, urlString := range util.SplitString(allowedURLString) {
allowedURL, err := url.Parse(urlString)
if err != nil {
cfg.Logger.Error("Error parsing allowed URL for IP range access control", "error", err)
continue
} else {
cfg.IPRangeACAllowedURLs = append(cfg.IPRangeACAllowedURLs, allowedURL)
}
}
}
func (cfg *Cfg) readSqlDataSourceSettings() {
sqlDatasources := cfg.Raw.Section("sql_datasources")
cfg.SqlDatasourceMaxOpenConnsDefault = sqlDatasources.Key("max_open_conns_default").MustInt(100)
cfg.SqlDatasourceMaxIdleConnsDefault = sqlDatasources.Key("max_idle_conns_default").MustInt(100)
cfg.SqlDatasourceMaxConnLifetimeDefault = sqlDatasources.Key("max_conn_lifetime_default").MustInt(14400)
}
func GetAllowedOriginGlobs(originPatterns []string) ([]glob.Glob, error) {
allowedOrigins := originPatterns
originGlobs := make([]glob.Glob, 0, len(allowedOrigins))
for _, originPattern := range allowedOrigins {
g, err := glob.Compile(originPattern)
if err != nil {
return nil, fmt.Errorf("error parsing origin pattern: %v", err)
}
originGlobs = append(originGlobs, g)
}
return originGlobs, nil
}
func (cfg *Cfg) readLiveSettings(iniFile *ini.File) error {
section := iniFile.Section("live")
cfg.LiveMaxConnections = section.Key("max_connections").MustInt(100)
if cfg.LiveMaxConnections < -1 {
return fmt.Errorf("unexpected value %d for [live] max_connections", cfg.LiveMaxConnections)
}
cfg.LiveHAEngine = section.Key("ha_engine").MustString("")
switch cfg.LiveHAEngine {
case "", "redis":
default:
return fmt.Errorf("unsupported live HA engine type: %s", cfg.LiveHAEngine)
}
2024-09-25 15:20:35 -05:00
cfg.LiveHAPrefix = section.Key("ha_prefix").MustString("")
cfg.LiveHAEngineAddress = section.Key("ha_engine_address").MustString("127.0.0.1:6379")
cfg.LiveHAEnginePassword = section.Key("ha_engine_password").MustString("")
allowedOrigins := section.Key("allowed_origins").MustString("")
origins := strings.Split(allowedOrigins, ",")
originPatterns := make([]string, 0, len(origins))
for _, originPattern := range origins {
originPattern = strings.TrimSpace(originPattern)
if originPattern == "" {
continue
}
originPatterns = append(originPatterns, originPattern)
}
_, err := GetAllowedOriginGlobs(originPatterns)
if err != nil {
return err
}
cfg.LiveAllowedOrigins = originPatterns
return nil
}
func (cfg *Cfg) readPublicDashboardsSettings() {
publicDashboards := cfg.Raw.Section("public_dashboards")
cfg.PublicDashboardsEnabled = publicDashboards.Key("enabled").MustBool(true)
}
func (cfg *Cfg) DefaultOrgID() int64 {
if cfg.AutoAssignOrg && cfg.AutoAssignOrgId > 0 {
return int64(cfg.AutoAssignOrgId)
}
return int64(1)
}