From 7c78b64a36b3513cc0c1221b3b050b3545878af7 Mon Sep 17 00:00:00 2001 From: Sven Klemm <31455525+svenklemm@users.noreply.github.com> Date: Mon, 10 Sep 2018 09:33:06 +0200 Subject: [PATCH 1/2] Document required order for time series queries (#13204) --- docs/sources/features/datasources/mssql.md | 2 ++ docs/sources/features/datasources/mysql.md | 2 ++ docs/sources/features/datasources/postgres.md | 2 ++ public/app/plugins/datasource/mssql/partials/query.editor.html | 2 ++ public/app/plugins/datasource/mysql/partials/query.editor.html | 2 ++ .../app/plugins/datasource/postgres/partials/query.editor.html | 2 ++ 6 files changed, 12 insertions(+) diff --git a/docs/sources/features/datasources/mssql.md b/docs/sources/features/datasources/mssql.md index 6bfcfd807f1..debf771ffb0 100644 --- a/docs/sources/features/datasources/mssql.md +++ b/docs/sources/features/datasources/mssql.md @@ -174,6 +174,8 @@ The resulting table panel: If you set `Format as` to `Time series`, for use in Graph panel for example, then the query must must have a column named `time` that returns either a sql datetime or any numeric datatype representing unix epoch in seconds. You may return a column named `metric` that is used as metric name for the value column. Any column except `time` and `metric` is treated as a value column. If you omit the `metric` column, the name of the value column will be the metric name. You may select multiple value columns, each will have its name as metric. If you return multiple value columns and a column named `metric` then this column is used as prefix for the series name (only available in Grafana 5.3+). +Resultsets of time series queries need to be sorted by time. + **Example database table:** ```sql diff --git a/docs/sources/features/datasources/mysql.md b/docs/sources/features/datasources/mysql.md index e13abcf80a2..d713a4b42b7 100644 --- a/docs/sources/features/datasources/mysql.md +++ b/docs/sources/features/datasources/mysql.md @@ -129,6 +129,8 @@ Any column except `time` and `metric` is treated as a value column. You may return a column named `metric` that is used as metric name for the value column. If you return multiple value columns and a column named `metric` then this column is used as prefix for the series name (only available in Grafana 5.3+). +Resultsets of time series queries need to be sorted by time. + **Example with `metric` column:** ```sql diff --git a/docs/sources/features/datasources/postgres.md b/docs/sources/features/datasources/postgres.md index 013d6342634..d67958814dd 100644 --- a/docs/sources/features/datasources/postgres.md +++ b/docs/sources/features/datasources/postgres.md @@ -129,6 +129,8 @@ Any column except `time` and `metric` is treated as a value column. You may return a column named `metric` that is used as metric name for the value column. If you return multiple value columns and a column named `metric` then this column is used as prefix for the series name (only available in Grafana 5.3+). +Resultsets of time series queries need to be sorted by time. + **Example with `metric` column:** ```sql diff --git a/public/app/plugins/datasource/mssql/partials/query.editor.html b/public/app/plugins/datasource/mssql/partials/query.editor.html index 4b0a46b6412..ba3bb6a4b82 100644 --- a/public/app/plugins/datasource/mssql/partials/query.editor.html +++ b/public/app/plugins/datasource/mssql/partials/query.editor.html @@ -45,6 +45,8 @@ Optional: - If multiple value columns are returned the metric column is used as prefix. - If no column named metric is found the column name of the value column is used as series name +Resultsets of time series queries need to be sorted by time. + Table: - return any set of columns diff --git a/public/app/plugins/datasource/mysql/partials/query.editor.html b/public/app/plugins/datasource/mysql/partials/query.editor.html index 1e829a1175d..1cc21adb22e 100644 --- a/public/app/plugins/datasource/mysql/partials/query.editor.html +++ b/public/app/plugins/datasource/mysql/partials/query.editor.html @@ -45,6 +45,8 @@ Optional: - If multiple value columns are returned the metric column is used as prefix. - If no column named metric is found the column name of the value column is used as series name +Resultsets of time series queries need to be sorted by time. + Table: - return any set of columns diff --git a/public/app/plugins/datasource/postgres/partials/query.editor.html b/public/app/plugins/datasource/postgres/partials/query.editor.html index fd944f4266c..6c3bf02cb51 100644 --- a/public/app/plugins/datasource/postgres/partials/query.editor.html +++ b/public/app/plugins/datasource/postgres/partials/query.editor.html @@ -143,6 +143,8 @@ Optional: - If multiple value columns are returned the metric column is used as prefix. - If no column named metric is found the column name of the value column is used as series name +Resultsets of time series queries need to be sorted by time. + Table: - return any set of columns From f257ff021681edf97a21df7d83d4fd4b6e1a0145 Mon Sep 17 00:00:00 2001 From: Bob Shannon Date: Mon, 10 Sep 2018 03:45:07 -0400 Subject: [PATCH 2/2] Allow oauth email attribute name to be configurable (#13006) * Allow oauth email attribute name to be configurable Signed-off-by: Bob Shannon * Document e-mail determination steps for generic oauth * Add reference to email_attribute_name * Re-add e-mail determination docs to new generic-oauth page * Inherit default e-mail attribute from defaults.ini --- conf/defaults.ini | 1 + docs/sources/auth/generic-oauth.md | 9 ++++++++- pkg/setting/setting_oauth.go | 1 + pkg/social/generic_oauth.go | 6 ++++-- pkg/social/social.go | 32 ++++++++++++++++-------------- 5 files changed, 31 insertions(+), 18 deletions(-) diff --git a/conf/defaults.ini b/conf/defaults.ini index 85d0953c6af..15b8927e65a 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -321,6 +321,7 @@ allow_sign_up = true client_id = some_id client_secret = some_secret scopes = user:email +email_attribute_name = email:primary auth_url = token_url = api_url = diff --git a/docs/sources/auth/generic-oauth.md b/docs/sources/auth/generic-oauth.md index bec5a98e04a..802424f180b 100644 --- a/docs/sources/auth/generic-oauth.md +++ b/docs/sources/auth/generic-oauth.md @@ -32,7 +32,14 @@ allowed_domains = mycompany.com mycompany.org allow_sign_up = true ``` -Set api_url to the resource that returns [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo) compatible information. +Set `api_url` to the resource that returns [OpenID UserInfo](https://connect2id.com/products/server/docs/api/userinfo) compatible information. + +Grafana will attempt to determine the user's e-mail address by querying the OAuth provider as described below in the following order until an e-mail address is found: + +1. Check for the presence of an e-mail address via the `email` field encoded in the OAuth `id_token` parameter. +2. Check for the presence of an e-mail address in the `attributes` map encoded in the OAuth `id_token` parameter. By default Grafana will perform a lookup into the attributes map using the `email:primary` key, however, this is configurable and can be adjusted by using the `email_attribute_name` configuration option. +3. Query the `/emails` endpoint of the OAuth provider's API (configured with `api_url`) and check for the presence of an e-mail address marked as a primary address. +4. If no e-mail address is found in steps (1-3), then the e-mail address of the user is set to the empty string. ## Set up OAuth2 with Okta diff --git a/pkg/setting/setting_oauth.go b/pkg/setting/setting_oauth.go index ee2e812415b..93b1ab6f101 100644 --- a/pkg/setting/setting_oauth.go +++ b/pkg/setting/setting_oauth.go @@ -5,6 +5,7 @@ type OAuthInfo struct { Scopes []string AuthUrl, TokenUrl string Enabled bool + EmailAttributeName string AllowedDomains []string HostedDomain string ApiUrl string diff --git a/pkg/social/generic_oauth.go b/pkg/social/generic_oauth.go index 8c02076096d..a97d58334c7 100644 --- a/pkg/social/generic_oauth.go +++ b/pkg/social/generic_oauth.go @@ -20,6 +20,7 @@ type SocialGenericOAuth struct { allowedOrganizations []string apiUrl string allowSignup bool + emailAttributeName string teamIds []int } @@ -264,8 +265,9 @@ func (s *SocialGenericOAuth) extractEmail(data *UserInfoJson) string { return data.Email } - if data.Attributes["email:primary"] != nil { - return data.Attributes["email:primary"][0] + emails, ok := data.Attributes[s.emailAttributeName] + if ok && len(emails) != 0 { + return emails[0] } if data.Upn != "" { diff --git a/pkg/social/social.go b/pkg/social/social.go index 2be71514629..e96b67fe031 100644 --- a/pkg/social/social.go +++ b/pkg/social/social.go @@ -60,21 +60,22 @@ func NewOAuthService() { for _, name := range allOauthes { sec := setting.Raw.Section("auth." + name) info := &setting.OAuthInfo{ - ClientId: sec.Key("client_id").String(), - ClientSecret: sec.Key("client_secret").String(), - Scopes: util.SplitString(sec.Key("scopes").String()), - AuthUrl: sec.Key("auth_url").String(), - TokenUrl: sec.Key("token_url").String(), - ApiUrl: sec.Key("api_url").String(), - Enabled: sec.Key("enabled").MustBool(), - AllowedDomains: util.SplitString(sec.Key("allowed_domains").String()), - HostedDomain: sec.Key("hosted_domain").String(), - AllowSignup: sec.Key("allow_sign_up").MustBool(), - Name: sec.Key("name").MustString(name), - TlsClientCert: sec.Key("tls_client_cert").String(), - TlsClientKey: sec.Key("tls_client_key").String(), - TlsClientCa: sec.Key("tls_client_ca").String(), - TlsSkipVerify: sec.Key("tls_skip_verify_insecure").MustBool(), + ClientId: sec.Key("client_id").String(), + ClientSecret: sec.Key("client_secret").String(), + Scopes: util.SplitString(sec.Key("scopes").String()), + AuthUrl: sec.Key("auth_url").String(), + TokenUrl: sec.Key("token_url").String(), + ApiUrl: sec.Key("api_url").String(), + Enabled: sec.Key("enabled").MustBool(), + EmailAttributeName: sec.Key("email_attribute_name").String(), + AllowedDomains: util.SplitString(sec.Key("allowed_domains").String()), + HostedDomain: sec.Key("hosted_domain").String(), + AllowSignup: sec.Key("allow_sign_up").MustBool(), + Name: sec.Key("name").MustString(name), + TlsClientCert: sec.Key("tls_client_cert").String(), + TlsClientKey: sec.Key("tls_client_key").String(), + TlsClientCa: sec.Key("tls_client_ca").String(), + TlsSkipVerify: sec.Key("tls_skip_verify_insecure").MustBool(), } if !info.Enabled { @@ -153,6 +154,7 @@ func NewOAuthService() { allowedDomains: info.AllowedDomains, apiUrl: info.ApiUrl, allowSignup: info.AllowSignup, + emailAttributeName: info.EmailAttributeName, teamIds: sec.Key("team_ids").Ints(","), allowedOrganizations: util.SplitString(sec.Key("allowed_organizations").String()), }