diff --git a/pkg/api/login.go b/pkg/api/login.go
index 789765ee01e..d1c571d8e0f 100644
--- a/pkg/api/login.go
+++ b/pkg/api/login.go
@@ -27,6 +27,7 @@ func LoginView(c *middleware.Context) {
viewData.Settings["googleAuthEnabled"] = setting.OAuthService.Google
viewData.Settings["githubAuthEnabled"] = setting.OAuthService.GitHub
+ viewData.Settings["grafanaNetAuthEnabled"] = setting.OAuthService.GrafanaNet
viewData.Settings["genericOAuthEnabled"] = setting.OAuthService.Generic
viewData.Settings["oauthProviderName"] = setting.OAuthService.OAuthProviderName
viewData.Settings["disableUserSignUp"] = !setting.AllowUserSignUp
diff --git a/pkg/models/models.go b/pkg/models/models.go
index 5a53cfdabb3..3f4b27ed6ab 100644
--- a/pkg/models/models.go
+++ b/pkg/models/models.go
@@ -7,4 +7,5 @@ const (
GOOGLE
TWITTER
GENERIC
+ GRAFANANET
)
diff --git a/pkg/setting/setting_oauth.go b/pkg/setting/setting_oauth.go
index 71c4ade1468..540b32ad83e 100644
--- a/pkg/setting/setting_oauth.go
+++ b/pkg/setting/setting_oauth.go
@@ -11,7 +11,7 @@ type OAuthInfo struct {
}
type OAuther struct {
- GitHub, Google, Twitter, Generic bool
+ GitHub, Google, Twitter, Generic, GrafanaNet bool
OAuthInfos map[string]*OAuthInfo
OAuthProviderName string
}
diff --git a/pkg/social/grafananet_oauth.go b/pkg/social/grafananet_oauth.go
new file mode 100644
index 00000000000..05cc7c1f397
--- /dev/null
+++ b/pkg/social/grafananet_oauth.go
@@ -0,0 +1,112 @@
+package social
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strconv"
+
+ "github.com/grafana/grafana/pkg/models"
+
+ "golang.org/x/oauth2"
+)
+
+type SocialGrafanaNet struct {
+ *oauth2.Config
+ url string
+ allowedOrganizations []string
+ allowSignup bool
+}
+
+func (s *SocialGrafanaNet) Type() int {
+ return int(models.GRAFANANET)
+}
+
+func (s *SocialGrafanaNet) IsEmailAllowed(email string) bool {
+ return true
+}
+
+func (s *SocialGrafanaNet) IsSignupAllowed() bool {
+ return s.allowSignup
+}
+
+func (s *SocialGrafanaNet) IsOrganizationMember(client *http.Client) bool {
+ if len(s.allowedOrganizations) == 0 {
+ return true
+ }
+
+ organizations, err := s.FetchOrganizations(client)
+ if err != nil {
+ return false
+ }
+
+ for _, allowedOrganization := range s.allowedOrganizations {
+ for _, organization := range organizations {
+ if organization == allowedOrganization {
+ return true
+ }
+ }
+ }
+
+ return false
+}
+
+func (s *SocialGrafanaNet) FetchOrganizations(client *http.Client) ([]string, error) {
+ type Record struct {
+ Login string `json:"login"`
+ }
+
+ url := fmt.Sprintf(s.url + "/api/oauth2/user/orgs")
+ r, err := client.Get(url)
+ if err != nil {
+ return nil, err
+ }
+
+ defer r.Body.Close()
+
+ var records []Record
+
+ if err = json.NewDecoder(r.Body).Decode(&records); err != nil {
+ return nil, err
+ }
+
+ var logins = make([]string, len(records))
+ for i, record := range records {
+ logins[i] = record.Login
+ }
+
+ return logins, nil
+}
+
+func (s *SocialGrafanaNet) UserInfo(token *oauth2.Token) (*BasicUserInfo, error) {
+ var data struct {
+ Id int `json:"id"`
+ Name string `json:"login"`
+ Email string `json:"email"`
+ }
+
+ var err error
+ client := s.Client(oauth2.NoContext, token)
+ r, err := client.Get(s.url + "/api/oauth2/user")
+ if err != nil {
+ return nil, err
+ }
+
+ defer r.Body.Close()
+
+ if err = json.NewDecoder(r.Body).Decode(&data); err != nil {
+ return nil, err
+ }
+
+ userInfo := &BasicUserInfo{
+ Identity: strconv.Itoa(data.Id),
+ Name: data.Name,
+ Email: data.Email,
+ }
+
+ if !s.IsOrganizationMember(client) {
+ return nil, ErrMissingOrganizationMembership
+ }
+
+ return userInfo, nil
+}
diff --git a/pkg/social/social.go b/pkg/social/social.go
index 66d0f5fa778..83e5aa19b43 100644
--- a/pkg/social/social.go
+++ b/pkg/social/social.go
@@ -36,7 +36,7 @@ func NewOAuthService() {
setting.OAuthService = &setting.OAuther{}
setting.OAuthService.OAuthInfos = make(map[string]*setting.OAuthInfo)
- allOauthes := []string{"github", "google", "generic_oauth"}
+ allOauthes := []string{"github", "google", "generic_oauth", "grafananet"}
for _, name := range allOauthes {
sec := setting.Cfg.Section("auth." + name)
@@ -108,5 +108,33 @@ func NewOAuthService() {
allowedOrganizations: allowedOrganizations,
}
}
+
+ if name == "grafananet" {
+ setting.OAuthService.GrafanaNet = true
+ allowedOrganizations := sec.Key("allowed_organizations").Strings(" ")
+
+ url := sec.Key("url").String()
+ if url == "" {
+ url = "https://grafana.net"
+ }
+
+ config := oauth2.Config{
+ ClientID: info.ClientId,
+ ClientSecret: info.ClientSecret,
+ Endpoint: oauth2.Endpoint{
+ AuthURL: url + "/oauth2/authorize",
+ TokenURL: url + "/api/oauth2/token",
+ },
+ RedirectURL: strings.TrimSuffix(setting.AppUrl, "/") + SocialBaseUrl + name,
+ Scopes: info.Scopes,
+ }
+
+ SocialMap["grafananet"] = &SocialGrafanaNet{
+ Config: &config,
+ url: url,
+ allowSignup: info.AllowSignup,
+ allowedOrganizations: allowedOrganizations,
+ }
+ }
}
}
diff --git a/public/app/core/controllers/login_ctrl.js b/public/app/core/controllers/login_ctrl.js
index 3f31407f454..ee5a1832420 100644
--- a/public/app/core/controllers/login_ctrl.js
+++ b/public/app/core/controllers/login_ctrl.js
@@ -17,7 +17,13 @@ function (angular, coreModule, config) {
$scope.googleAuthEnabled = config.googleAuthEnabled;
$scope.githubAuthEnabled = config.githubAuthEnabled;
- $scope.oauthEnabled = config.githubAuthEnabled || config.googleAuthEnabled || config.genericOAuthEnabled;
+ $scope.grafanaNetAuthEnabled = config.grafanaNetAuthEnabled;
+ $scope.oauthEnabled = (
+ config.githubAuthEnabled
+ || config.googleAuthEnabled
+ || config.grafanaNetAuthEnabled
+ || config.genericOAuthEnabled
+ );
$scope.allowUserPassLogin = config.allowUserPassLogin;
$scope.genericOAuthEnabled = config.genericOAuthEnabled;
$scope.oauthProviderName = config.oauthProviderName;
diff --git a/public/app/partials/login.html b/public/app/partials/login.html
index f4f5fb26d7e..d9bf7ecdb18 100644
--- a/public/app/partials/login.html
+++ b/public/app/partials/login.html
@@ -59,10 +59,13 @@
with Github
+
+ with Grafana.net
+
- with {{oauthProviderName || "OAuth 2"}}
-
+ with {{oauthProviderName || "OAuth 2"}}
+
diff --git a/public/sass/components/_search.scss b/public/sass/components/_search.scss
index 563f89919e5..2eeb34ec0b4 100644
--- a/public/sass/components/_search.scss
+++ b/public/sass/components/_search.scss
@@ -111,7 +111,7 @@
font-size: $font-size-sm;
padding-right: 7rem;
background: url(../img/grafana_net_logo.svg);
- background-size: 6.5rem 3rem;
+ background-size: 6.5rem;
background-repeat: no-repeat;
background-position: right;
position: relative;
diff --git a/public/sass/pages/_login.scss b/public/sass/pages/_login.scss
index 8f376dfae71..8641aabe1b1 100644
--- a/public/sass/pages/_login.scss
+++ b/public/sass/pages/_login.scss
@@ -112,6 +112,19 @@
background: #555;
color: white;
}
+
+ .btn-grafana-net {
+ background: url(../img/grafana_net_logo.svg);
+ background-size: 10rem;
+ background-repeat: no-repeat;
+ background-position: right 35%;
+ overflow: hidden;
+ padding-right: 10.5rem;
+
+ span {
+ display: none;
+ }
+ }
}
.password-recovery {
@@ -157,7 +170,7 @@
.invite-box {
text-align: center;
border: 1px solid $tight-form-func-bg;
- background-color: $panel-bg;
+ background-color: $panel-bg;
max-width: 800px;
margin-left: auto;
margin-right: auto;