Просмотр исходного кода

Merge branch 'master' into panel_repeat

Conflicts:
	public/app/features/templating/templateValuesSrv.js
Torkel Ödegaard 10 лет назад
Родитель
Сommit
b762f56aee

+ 7 - 1
CHANGELOG.md

@@ -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

+ 3 - 2
conf/defaults.ini

@@ -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]

+ 5 - 4
conf/sample.ini

@@ -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]

+ 1 - 1
docs/Makefile

@@ -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)" .

+ 1 - 4
docs/sources/guides/screencasts.md

@@ -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">

+ 16 - 2
docs/sources/installation/configuration.md

@@ -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.
-
-

+ 1 - 1
docs/sources/installation/debian.md

@@ -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`

+ 2 - 1
pkg/api/dtos/models.go

@@ -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"`
 }

+ 1 - 0
pkg/api/index.go

@@ -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),

+ 7 - 2
pkg/api/login_oauth.go

@@ -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
 	}
 

+ 60 - 8
pkg/social/social.go

@@ -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
 	}

+ 2 - 0
public/app/components/kbn.js

@@ -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'},
         ]
       },
       {

+ 8 - 1
public/app/controllers/loginCtrl.js

@@ -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

+ 20 - 7
public/app/features/templating/templateValuesSrv.js

@@ -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; }
 

+ 8 - 13
public/app/plugins/datasource/influxdb/influxSeries.js

@@ -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);
       });
     });

+ 2 - 1
public/app/plugins/datasource/influxdb/plugin.json

@@ -13,5 +13,6 @@
     "annotations": "app/plugins/datasource/influxdb/partials/annotations.editor.html"
   },
 
-  "metrics": true
+  "metrics": true,
+  "annotations": true
 }