mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into panel_repeat
Conflicts: public/app/features/templating/templateValuesSrv.js
This commit is contained in:
commit
b762f56aee
@ -1,4 +1,10 @@
|
||||
# 2.0.3 (unreleased)
|
||||
# 2.1.0 (unreleased - master branch)
|
||||
|
||||
**Backend**
|
||||
- [Issue #1905](https://github.com/grafana/grafana/issues/1905). Github OAuth: You can now configure a Github team membership requirement, thx @dewski
|
||||
|
||||
|
||||
# 2.0.3 (unreleased - 2.0.x branch)
|
||||
|
||||
**Fixes**
|
||||
- [Issue #1872](https://github.com/grafana/grafana/issues/1872). Firefox/IE issue, invisible text in dashboard search fixed
|
||||
|
@ -137,18 +137,20 @@ org_role = Viewer
|
||||
#################################### Github Auth ##########################
|
||||
[auth.github]
|
||||
enabled = false
|
||||
allow_sign_up = false
|
||||
client_id = some_id
|
||||
client_secret = some_secret
|
||||
scopes = user:email
|
||||
auth_url = https://github.com/login/oauth/authorize
|
||||
token_url = https://github.com/login/oauth/access_token
|
||||
api_url = https://api.github.com/user
|
||||
team_ids =
|
||||
allowed_domains =
|
||||
allow_sign_up = false
|
||||
|
||||
#################################### Google Auth ##########################
|
||||
[auth.google]
|
||||
enabled = false
|
||||
allow_sign_up = false
|
||||
client_id = some_client_id
|
||||
client_secret = some_client_secret
|
||||
scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
|
||||
@ -156,7 +158,6 @@ auth_url = https://accounts.google.com/o/oauth2/auth
|
||||
token_url = https://accounts.google.com/o/oauth2/token
|
||||
api_url = https://www.googleapis.com/oauth2/v1/userinfo
|
||||
allowed_domains =
|
||||
allow_sign_up = false
|
||||
|
||||
#################################### Logging ##########################
|
||||
[log]
|
||||
|
@ -136,26 +136,27 @@
|
||||
#################################### Github Auth ##########################
|
||||
[auth.github]
|
||||
;enabled = false
|
||||
;allow_sign_up = false
|
||||
;client_id = some_id
|
||||
;client_secret = some_secret
|
||||
;scopes = user:email
|
||||
;auth_url = https://github.com/login/oauth/authorize
|
||||
;token_url = https://github.com/login/oauth/access_token
|
||||
;api_url = https://api.github.com/user
|
||||
# Uncomment bellow to only allow specific email domains
|
||||
; allowed_domains = mycompany.com othercompany.com
|
||||
;team_ids =
|
||||
;allowed_domains =
|
||||
|
||||
#################################### Google Auth ##########################
|
||||
[auth.google]
|
||||
;enabled = false
|
||||
;allow_sign_up = false
|
||||
;client_id = some_client_id
|
||||
;client_secret = some_client_secret
|
||||
;scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
|
||||
;auth_url = https://accounts.google.com/o/oauth2/auth
|
||||
;token_url = https://accounts.google.com/o/oauth2/token
|
||||
;api_url = https://www.googleapis.com/oauth2/v1/userinfo
|
||||
# Uncomment bellow to only allow specific email domains
|
||||
; allowed_domains = mycompany.com othercompany.com
|
||||
;allowed_domains =
|
||||
|
||||
#################################### Logging ##########################
|
||||
[log]
|
||||
|
@ -44,7 +44,7 @@ docs-test: docs-build
|
||||
$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" ./test.sh
|
||||
|
||||
docs-build:
|
||||
git fetch https://github.com/grafana/grafana.git docs-2.0 && git diff --name-status FETCH_HEAD...HEAD -- . > changed-files
|
||||
git fetch https://github.com/grafana/grafana.git docs-1.x && git diff --name-status FETCH_HEAD...HEAD -- . > changed-files
|
||||
echo "$(GIT_BRANCH)" > GIT_BRANCH
|
||||
echo "$(GITCOMMIT)" > GITCOMMIT
|
||||
docker build -t "$(DOCKER_DOCS_IMAGE)" .
|
||||
|
@ -15,10 +15,9 @@ no_toc: true
|
||||
<div class="columns medium-6">
|
||||
<h3>Episode 2 - Templated Graphite Queries</h3>
|
||||
<div class="video-container">
|
||||
<iframe height="315" src="//www.youtube.com/embed/FhNUrueWwOk?list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2" frameborder="0" allowfullscreen></iframe>
|
||||
<iframe height="215" src="//www.youtube.com/embed/FhNUrueWwOk?list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2" frameborder="0" allowfullscreen></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@ -34,7 +33,6 @@ no_toc: true
|
||||
<iframe height="215" src="https://www.youtube.com/embed/JY22EBOR9hQ?list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2" frameborder="0" allowfullscreen></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
@ -50,7 +48,6 @@ no_toc: true
|
||||
<iframe height="215" src="https://www.youtube.com/embed/9ZCMVNxUf6s?list=PLDGkOdUX1Ujo3wHw9-z5Vo12YLqXRjzg2" frameborder="0" allowfullscreen></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
@ -182,6 +182,7 @@ Client ID and a Client Secret. Specify these in the grafana config file. Example
|
||||
auth_url = https://github.com/login/oauth/authorize
|
||||
token_url = https://github.com/login/oauth/access_token
|
||||
allow_sign_up = false
|
||||
team_ids =
|
||||
|
||||
Restart the grafana backend. You should now see a github login button on the login page. You can
|
||||
now login or signup with your github accounts.
|
||||
@ -189,6 +190,21 @@ now login or signup with your github accounts.
|
||||
You may allow users to sign-up via github auth by setting allow_sign_up to true. When this option is
|
||||
set to true, any user successfully authenticating via github auth will be automatically signed up.
|
||||
|
||||
### team_ids
|
||||
Require an active team membership for at least one of the given teams on GitHub.
|
||||
If the authenticated user isn't a member of at least one the teams they will not
|
||||
be able to register or authenticate with your Grafana instance. Example:
|
||||
|
||||
[auth.github]
|
||||
enabled = true
|
||||
client_id = YOUR_GITHUB_APP_CLIENT_ID
|
||||
client_secret = YOUR_GITHUB_APP_CLIENT_SECRET
|
||||
scopes = user:email
|
||||
team_ids = 150,300
|
||||
auth_url = https://github.com/login/oauth/authorize
|
||||
token_url = https://github.com/login/oauth/access_token
|
||||
allow_sign_up = false
|
||||
|
||||
## [auth.google]
|
||||
You need to create a google project. You can do this in the [Google Developer Console](https://console.developers.google.com/project).
|
||||
When you create the project you will need to specify a callback URL. Specify this as callback:
|
||||
@ -257,5 +273,3 @@ enabled. Counters are sent every 24 hours. Default value is `true`.
|
||||
### google_analytics_ua_id
|
||||
If you want to track Grafana usage via Google analytics specify *your* Univeral Analytics ID
|
||||
here. By defualt this feature is disabled.
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@ Description | Download
|
||||
|
||||
$ wget https://grafanarel.s3.amazonaws.com/builds/grafana_2.0.2_amd64.deb
|
||||
$ sudo apt-get install -y adduser libfontconfig
|
||||
$ sudo dpkg -i grafana_2.0.1_amd64.deb
|
||||
$ sudo dpkg -i grafana_2.0.2_amd64.deb
|
||||
|
||||
## APT Repository
|
||||
Add the following line to your `/etc/apt/sources.list`
|
||||
|
@ -21,8 +21,9 @@ type CurrentUser struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
LightTheme bool `json:"lightTheme"`
|
||||
OrgRole m.RoleType `json:"orgRole"`
|
||||
OrgId int64 `json:"orgId"`
|
||||
OrgName string `json:"orgName"`
|
||||
OrgRole m.RoleType `json:"orgRole"`
|
||||
IsGrafanaAdmin bool `json:"isGrafanaAdmin"`
|
||||
GravatarUrl string `json:"gravatarUrl"`
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ func setIndexViewData(c *middleware.Context) error {
|
||||
Email: c.Email,
|
||||
Name: c.Name,
|
||||
LightTheme: c.Theme == "light",
|
||||
OrgId: c.OrgId,
|
||||
OrgName: c.OrgName,
|
||||
OrgRole: c.OrgRole,
|
||||
GravatarUrl: dtos.GetGravatarUrl(c.Email),
|
||||
|
@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
@ -45,7 +46,11 @@ func OAuthLogin(ctx *middleware.Context) {
|
||||
|
||||
userInfo, err := connect.UserInfo(token)
|
||||
if err != nil {
|
||||
ctx.Handle(500, fmt.Sprintf("login.OAuthLogin(get info from %s)", name), err)
|
||||
if err == social.ErrMissingTeamMembership {
|
||||
ctx.Redirect(setting.AppSubUrl + "/login?failedMsg=" + url.QueryEscape("Required Github team membership not fulfilled"))
|
||||
} else {
|
||||
ctx.Handle(500, fmt.Sprintf("login.OAuthLogin(get info from %s)", name), err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -54,7 +59,7 @@ func OAuthLogin(ctx *middleware.Context) {
|
||||
// validate that the email is allowed to login to grafana
|
||||
if !connect.IsEmailAllowed(userInfo.Email) {
|
||||
log.Info("OAuth login attempt with unallowed email, %s", userInfo.Email)
|
||||
ctx.Redirect(setting.AppSubUrl + "/login?email_not_allowed=1")
|
||||
ctx.Redirect(setting.AppSubUrl + "/login?failedMsg=" + url.QueryEscape("Required email domain not fulfilled"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,9 @@ package social
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -75,13 +77,24 @@ func NewOAuthService() {
|
||||
// GitHub.
|
||||
if name == "github" {
|
||||
setting.OAuthService.GitHub = true
|
||||
SocialMap["github"] = &SocialGithub{Config: &config, allowedDomains: info.AllowedDomains, ApiUrl: info.ApiUrl, allowSignup: info.AllowSignup}
|
||||
teamIds := sec.Key("team_ids").Ints(",")
|
||||
SocialMap["github"] = &SocialGithub{
|
||||
Config: &config,
|
||||
allowedDomains: info.AllowedDomains,
|
||||
apiUrl: info.ApiUrl,
|
||||
allowSignup: info.AllowSignup,
|
||||
teamIds: teamIds,
|
||||
}
|
||||
}
|
||||
|
||||
// Google.
|
||||
if name == "google" {
|
||||
setting.OAuthService.Google = true
|
||||
SocialMap["google"] = &SocialGoogle{Config: &config, allowedDomains: info.AllowedDomains, ApiUrl: info.ApiUrl, allowSignup: info.AllowSignup}
|
||||
SocialMap["google"] = &SocialGoogle{
|
||||
Config: &config, allowedDomains: info.AllowedDomains,
|
||||
apiUrl: info.ApiUrl,
|
||||
allowSignup: info.AllowSignup,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -103,10 +116,15 @@ func isEmailAllowed(email string, allowedDomains []string) bool {
|
||||
type SocialGithub struct {
|
||||
*oauth2.Config
|
||||
allowedDomains []string
|
||||
ApiUrl string
|
||||
apiUrl string
|
||||
allowSignup bool
|
||||
teamIds []int
|
||||
}
|
||||
|
||||
var (
|
||||
ErrMissingTeamMembership = errors.New("User not a member of one of the required teams")
|
||||
)
|
||||
|
||||
func (s *SocialGithub) Type() int {
|
||||
return int(models.GITHUB)
|
||||
}
|
||||
@ -119,6 +137,28 @@ func (s *SocialGithub) IsSignupAllowed() bool {
|
||||
return s.allowSignup
|
||||
}
|
||||
|
||||
func (s *SocialGithub) IsTeamMember(client *http.Client, username string, teamId int) bool {
|
||||
var data struct {
|
||||
Url string `json:"url"`
|
||||
State string `json:"state"`
|
||||
}
|
||||
|
||||
membershipUrl := fmt.Sprintf("https://api.github.com/teams/%d/memberships/%s", teamId, username)
|
||||
r, err := client.Get(membershipUrl)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
active := data.State == "active"
|
||||
return active
|
||||
}
|
||||
|
||||
func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
|
||||
var data struct {
|
||||
Id int `json:"id"`
|
||||
@ -128,7 +168,7 @@ func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
|
||||
|
||||
var err error
|
||||
client := s.Client(oauth2.NoContext, token)
|
||||
r, err := client.Get(s.ApiUrl)
|
||||
r, err := client.Get(s.apiUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -139,11 +179,23 @@ func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &BasicUserInfo{
|
||||
userInfo := &BasicUserInfo{
|
||||
Identity: strconv.Itoa(data.Id),
|
||||
Name: data.Name,
|
||||
Email: data.Email,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if len(s.teamIds) > 0 {
|
||||
for _, teamId := range s.teamIds {
|
||||
if s.IsTeamMember(client, data.Name, teamId) {
|
||||
return userInfo, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrMissingTeamMembership
|
||||
} else {
|
||||
return userInfo, nil
|
||||
}
|
||||
}
|
||||
|
||||
// ________ .__
|
||||
@ -156,7 +208,7 @@ func (s *SocialGithub) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
|
||||
type SocialGoogle struct {
|
||||
*oauth2.Config
|
||||
allowedDomains []string
|
||||
ApiUrl string
|
||||
apiUrl string
|
||||
allowSignup bool
|
||||
}
|
||||
|
||||
@ -181,7 +233,7 @@ func (s *SocialGoogle) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
|
||||
var err error
|
||||
|
||||
client := s.Client(oauth2.NoContext, token)
|
||||
r, err := client.Get(s.ApiUrl)
|
||||
r, err := client.Get(s.apiUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -376,6 +376,7 @@ function($, _, moment) {
|
||||
kbn.valueFormats.bytes = kbn.formatFuncCreator(1024, [' B', ' KiB', ' MiB', ' GiB', ' TiB', ' PiB', ' EiB', ' ZiB', ' YiB']);
|
||||
kbn.valueFormats.kbytes = kbn.formatFuncCreator(1024, [' KiB', ' MiB', ' GiB', ' TiB', ' PiB', ' EiB', ' ZiB', ' YiB']);
|
||||
kbn.valueFormats.mbytes = kbn.formatFuncCreator(1024, [' MiB', ' GiB', ' TiB', ' PiB', ' EiB', ' ZiB', ' YiB']);
|
||||
kbn.valueFormats.gbytes = kbn.formatFuncCreator(1024, [' GiB', ' TiB', ' PiB', ' EiB', ' ZiB', ' YiB']);
|
||||
kbn.valueFormats.bps = kbn.formatFuncCreator(1000, [' bps', ' Kbps', ' Mbps', ' Gbps', ' Tbps', ' Pbps', ' Ebps', ' Zbps', ' Ybps']);
|
||||
kbn.valueFormats.Bps = kbn.formatFuncCreator(1000, [' Bps', ' KBps', ' MBps', ' GBps', ' TBps', ' PBps', ' EBps', ' ZBps', ' YBps']);
|
||||
kbn.valueFormats.short = kbn.formatFuncCreator(1000, ['', ' K', ' Mil', ' Bil', ' Tri', ' Qaudr', ' Quint', ' Sext', ' Sept']);
|
||||
@ -547,6 +548,7 @@ function($, _, moment) {
|
||||
{text: 'bytes', value: 'bytes'},
|
||||
{text: 'kilobytes', value: 'kbytes'},
|
||||
{text: 'megabytes', value: 'mbytes'},
|
||||
{text: 'gigabytes', value: 'gbytes'},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -7,7 +7,7 @@ function (angular, config) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('LoginCtrl', function($scope, backendSrv, contextSrv) {
|
||||
module.controller('LoginCtrl', function($scope, backendSrv, contextSrv, $location) {
|
||||
$scope.formModel = {
|
||||
user: '',
|
||||
email: '',
|
||||
@ -28,6 +28,13 @@ function (angular, config) {
|
||||
$scope.init = function() {
|
||||
$scope.$watch("loginMode", $scope.loginModeChanged);
|
||||
$scope.passwordChanged();
|
||||
|
||||
var params = $location.search();
|
||||
if (params.failedMsg) {
|
||||
$scope.appEvent('alert-warning', ['Login Failed', params.failedMsg]);
|
||||
delete params.failedMsg;
|
||||
$location.search(params);
|
||||
}
|
||||
};
|
||||
|
||||
// build info view model
|
||||
|
@ -29,13 +29,7 @@ function (angular, _, kbn) {
|
||||
var variable = this.variables[i];
|
||||
var urlValue = queryParams['var-' + variable.name];
|
||||
if (urlValue !== void 0) {
|
||||
var option = _.findWhere(variable.options, { text: urlValue });
|
||||
option = option || { text: urlValue, value: urlValue };
|
||||
|
||||
var promise = this.setVariableValue(variable, option);
|
||||
this.updateAutoInterval(variable);
|
||||
|
||||
promises.push(promise);
|
||||
promises.push(this.setVariableFromUrl(variable, urlValue));
|
||||
}
|
||||
else if (variable.refresh) {
|
||||
promises.push(this.updateOptions(variable));
|
||||
@ -48,6 +42,25 @@ function (angular, _, kbn) {
|
||||
return $q.all(promises);
|
||||
};
|
||||
|
||||
this.setVariableFromUrl = function(variable, urlValue) {
|
||||
if (variable.refresh) {
|
||||
var self = this;
|
||||
//refresh the list of options before setting the value
|
||||
return this.updateOptions(variable).then(function() {
|
||||
var option = _.findWhere(variable.options, { text: urlValue });
|
||||
option = option || { text: urlValue, value: urlValue };
|
||||
|
||||
self.updateAutoInterval(variable);
|
||||
return self.setVariableValue(variable, option);
|
||||
});
|
||||
}
|
||||
var option = _.findWhere(variable.options, { text: urlValue });
|
||||
option = option || { text: urlValue, value: urlValue };
|
||||
|
||||
this.updateAutoInterval(variable);
|
||||
return this.setVariableValue(variable, option);
|
||||
};
|
||||
|
||||
this.updateAutoInterval = function(variable) {
|
||||
if (!variable.auto) { return; }
|
||||
|
||||
|
@ -5,7 +5,8 @@ function (_) {
|
||||
'use strict';
|
||||
|
||||
function InfluxSeries(options) {
|
||||
this.seriesList = options.seriesList;
|
||||
this.seriesList = options.seriesList && options.seriesList.results && options.seriesList.results.length > 0
|
||||
? options.seriesList.results[0].series || [] : [];
|
||||
this.alias = options.alias;
|
||||
this.annotation = options.annotation;
|
||||
}
|
||||
@ -17,12 +18,10 @@ function (_) {
|
||||
var self = this;
|
||||
|
||||
console.log(self.seriesList);
|
||||
if (!self.seriesList || !self.seriesList.results || !self.seriesList.results[0]) {
|
||||
if (self.seriesList.length === 0) {
|
||||
return output;
|
||||
}
|
||||
|
||||
this.seriesList = self.seriesList.results[0].series;
|
||||
|
||||
_.each(self.seriesList, function(series) {
|
||||
var datapoints = [];
|
||||
for (var i = 0; i < series.values.length; i++) {
|
||||
@ -63,19 +62,15 @@ function (_) {
|
||||
if (column === self.annotation.textColumn) { textCol = index; return; }
|
||||
});
|
||||
|
||||
_.each(series.points, function (point) {
|
||||
_.each(series.values, function (value) {
|
||||
var data = {
|
||||
annotation: self.annotation,
|
||||
time: point[timeCol],
|
||||
title: point[titleCol],
|
||||
tags: point[tagsCol],
|
||||
text: point[textCol]
|
||||
time: + new Date(value[timeCol]),
|
||||
title: value[titleCol],
|
||||
tags: value[tagsCol],
|
||||
text: value[textCol]
|
||||
};
|
||||
|
||||
if (tagsCol) {
|
||||
data.tags = point[tagsCol];
|
||||
}
|
||||
|
||||
list.push(data);
|
||||
});
|
||||
});
|
||||
|
@ -13,5 +13,6 @@
|
||||
"annotations": "app/plugins/datasource/influxdb/partials/annotations.editor.html"
|
||||
},
|
||||
|
||||
"metrics": true
|
||||
"metrics": true,
|
||||
"annotations": true
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user