Commit 3e657357 by Torkel Ödegaard

Merge branch 'gnet-oauth'

parents 7a650164 e5fc4332
......@@ -9,7 +9,7 @@ app_mode = production
# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty
instance_name = ${HOSTNAME}
#################################### Paths ####################################
#################################### Paths ###############################
[paths]
# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
#
......@@ -23,7 +23,7 @@ logs = data/log
#
plugins = data/plugins
#################################### Server ####################################
#################################### Server ##############################
[server]
# Protocol (http or https)
protocol = http
......@@ -57,7 +57,7 @@ enable_gzip = false
cert_file =
cert_key =
#################################### Database ####################################
#################################### Database ############################
[database]
# You can configure the database connection by specifying type, host, name, user and password
# as seperate properties or as on string using the url propertie.
......@@ -84,7 +84,7 @@ server_cert_name =
# For "sqlite3" only, path relative to data_path setting
path = grafana.db
#################################### Session ####################################
#################################### Session #############################
[session]
# Either "memory", "file", "redis", "mysql", "postgres", "memcache", default is "file"
provider = file
......@@ -112,7 +112,7 @@ cookie_secure = false
session_life_time = 86400
gc_interval_time = 86400
#################################### Analytics ####################################
#################################### Analytics ###########################
[analytics]
# Server reporting, sends usage counters to stats.grafana.org every 24 hours.
# No ip addresses are being tracked, only simple counters to track
......@@ -133,7 +133,7 @@ google_analytics_ua_id =
# Google Tag Manager ID, only enabled if you specify an id here
google_tag_manager_id =
#################################### Security ####################################
#################################### Security ############################
[security]
# default admin user, created on startup
admin_user = admin
......@@ -193,7 +193,7 @@ default_theme = dark
# Allow users to sign in using username and password
allow_user_pass_login = true
#################################### Anonymous Auth ##########################
#################################### Anonymous Auth ######################
[auth.anonymous]
# enable anonymous access
enabled = false
......@@ -204,7 +204,7 @@ org_name = Main Org.
# specify role for unauthenticated users
org_role = Viewer
#################################### Github Auth ##########################
#################################### Github Auth #########################
[auth.github]
enabled = false
allow_sign_up = false
......@@ -217,7 +217,7 @@ api_url = https://api.github.com/user
team_ids =
allowed_organizations =
#################################### Google Auth ##########################
#################################### Google Auth #########################
[auth.google]
enabled = false
allow_sign_up = false
......@@ -229,7 +229,16 @@ token_url = https://accounts.google.com/o/oauth2/token
api_url = https://www.googleapis.com/oauth2/v1/userinfo
allowed_domains =
#################################### Generic OAuth ##########################
#################################### Grafana.net Auth ####################
[auth.grafananet]
enabled = false
allow_sign_up = false
client_id = some_id
client_secret = some_secret
scopes = user:email
allowed_organizations =
#################################### Generic OAuth #######################
[auth.generic_oauth]
enabled = false
allow_sign_up = false
......@@ -253,12 +262,12 @@ header_name = X-WEBAUTH-USER
header_property = username
auto_sign_up = true
#################################### Auth LDAP ##########################
#################################### Auth LDAP ###########################
[auth.ldap]
enabled = false
config_file = /etc/grafana/ldap.toml
#################################### SMTP / Emailing ##########################
#################################### SMTP / Emailing #####################
[smtp]
enabled = false
host = localhost:25
......@@ -273,9 +282,6 @@ from_address = admin@grafana.localhost
welcome_email_on_sign_up = false
templates_pattern = emails/*.html
[tmp.files]
rendered_image_ttl_days = 14
#################################### Logging ##########################
[log]
# Either "console", "file", "syslog". Default is console and file
......@@ -331,18 +337,18 @@ facility =
tag =
#################################### AMQP Event Publisher ##########################
#################################### AMQP Event Publisher ################
[event_publisher]
enabled = false
rabbitmq_url = amqp://localhost/
exchange = grafana_events
#################################### Dashboard JSON files ##########################
#################################### Dashboard JSON files ################
[dashboards.json]
enabled = false
path = /var/lib/grafana/dashboards
#################################### Usage Quotas ##########################
#################################### Usage Quotas ########################
[quota]
enabled = false
......@@ -377,7 +383,7 @@ global_api_key = -1
# global limit on number of logged in users.
global_session = -1
#################################### Alerting ######################################
#################################### Alerting ############################
# docs about alerting can be found in /docs/sources/alerting/
# __.-/|
# \`o_O'
......@@ -396,7 +402,7 @@ global_session = -1
[alerting]
enabled = true
#################################### Internal Grafana Metrics ##########################
#################################### Internal Grafana Metrics ############
# Metrics available at HTTP API Url /api/metrics
[metrics]
enabled = true
......@@ -411,7 +417,7 @@ prefix = prod.grafana.%(instance_name)s.
[grafana_net]
url = https://grafana.net
#################################### External image storage ##########################
#################################### External Image Storage ##############
[external_image_storage]
# You can choose between (s3, webdav)
provider = s3
......
......@@ -116,7 +116,7 @@
# in some UI views to notify that grafana or plugin update exists
# This option does not cause any auto updates, nor send any information
# only a GET request to http://grafana.net to get latest versions
check_for_updates = true
;check_for_updates = true
# Google Analytics universal tracking code, only enabled if you specify an id here
;google_analytics_ua_id =
......@@ -224,6 +224,15 @@ check_for_updates = true
;team_ids =
;allowed_organizations =
#################################### Grafana.net Auth ####################
[auth.grafananet]
;enabled = false
;allow_sign_up = false
;client_id = some_id
;client_secret = some_secret
;scopes = user:email
;allowed_organizations =
#################################### Auth Proxy ##########################
[auth.proxy]
;enabled = false
......
......@@ -25,10 +25,12 @@ func LoginView(c *middleware.Context) {
return
}
viewData.Settings["googleAuthEnabled"] = setting.OAuthService.Google
viewData.Settings["githubAuthEnabled"] = setting.OAuthService.GitHub
viewData.Settings["genericOAuthEnabled"] = setting.OAuthService.Generic
viewData.Settings["oauthProviderName"] = setting.OAuthService.OAuthProviderName
enabledOAuths := make(map[string]interface{})
for key, oauth := range setting.OAuthService.OAuthInfos {
enabledOAuths[key] = map[string]string{"name": oauth.Name}
}
viewData.Settings["oauth"] = enabledOAuths
viewData.Settings["disableUserSignUp"] = !setting.AllowUserSignUp
viewData.Settings["loginHint"] = setting.LoginHint
viewData.Settings["allowUserPassLogin"] = setting.AllowUserPassLogin
......
......@@ -82,10 +82,11 @@ func OAuthLogin(ctx *middleware.Context) {
return
}
cmd := m.CreateUserCommand{
Login: userInfo.Email,
Email: userInfo.Email,
Name: userInfo.Name,
Company: userInfo.Company,
Login: userInfo.Email,
Email: userInfo.Email,
Name: userInfo.Name,
Company: userInfo.Company,
DefaultOrgRole: userInfo.Role,
}
if err = bus.Dispatch(&cmd); err != nil {
......
......@@ -7,4 +7,5 @@ const (
GOOGLE
TWITTER
GENERIC
GRAFANANET
)
......@@ -44,15 +44,16 @@ func (u *User) NameOrFallback() string {
// COMMANDS
type CreateUserCommand struct {
Email string
Login string
Name string
Company string
OrgName string
Password string
EmailVerified bool
IsAdmin bool
SkipOrgSetup bool
Email string
Login string
Name string
Company string
OrgName string
Password string
EmailVerified bool
IsAdmin bool
SkipOrgSetup bool
DefaultOrgRole string
Result User
}
......
......@@ -128,7 +128,11 @@ func CreateUser(cmd *m.CreateUserCommand) error {
}
if setting.AutoAssignOrg && !user.IsAdmin {
orgUser.Role = m.RoleType(setting.AutoAssignOrgRole)
if len(cmd.DefaultOrgRole) > 0 {
orgUser.Role = m.RoleType(cmd.DefaultOrgRole)
} else {
orgUser.Role = m.RoleType(setting.AutoAssignOrgRole)
}
}
if _, err = sess.Insert(&orgUser); err != nil {
......
......@@ -8,12 +8,11 @@ type OAuthInfo struct {
AllowedDomains []string
ApiUrl string
AllowSignup bool
Name string
}
type OAuther struct {
GitHub, Google, Twitter, Generic bool
OAuthInfos map[string]*OAuthInfo
OAuthProviderName string
OAuthInfos map[string]*OAuthInfo
}
var OAuthService *OAuther
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"`
Role string `json:"role"`
}
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,
Role: data.Role,
}
if !s.IsOrganizationMember(client) {
return nil, ErrMissingOrganizationMembership
}
return userInfo, nil
}
......@@ -15,6 +15,7 @@ type BasicUserInfo struct {
Email string
Login string
Company string
Role string
}
type SocialConnector interface {
......@@ -36,7 +37,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)
......@@ -50,6 +51,7 @@ func NewOAuthService() {
Enabled: sec.Key("enabled").MustBool(),
AllowedDomains: sec.Key("allowed_domains").Strings(" "),
AllowSignup: sec.Key("allow_sign_up").MustBool(),
Name: sec.Key("name").MustString(name),
}
if !info.Enabled {
......@@ -70,22 +72,18 @@ func NewOAuthService() {
// GitHub.
if name == "github" {
setting.OAuthService.GitHub = true
teamIds := sec.Key("team_ids").Ints(",")
allowedOrganizations := sec.Key("allowed_organizations").Strings(" ")
SocialMap["github"] = &SocialGithub{
Config: &config,
allowedDomains: info.AllowedDomains,
apiUrl: info.ApiUrl,
allowSignup: info.AllowSignup,
teamIds: teamIds,
allowedOrganizations: allowedOrganizations,
teamIds: sec.Key("team_ids").Ints(","),
allowedOrganizations: sec.Key("allowed_organizations").Strings(" "),
}
}
// Google.
if name == "google" {
setting.OAuthService.Google = true
SocialMap["google"] = &SocialGoogle{
Config: &config, allowedDomains: info.AllowedDomains,
apiUrl: info.ApiUrl,
......@@ -95,17 +93,33 @@ func NewOAuthService() {
// Generic - Uses the same scheme as Github.
if name == "generic_oauth" {
setting.OAuthService.Generic = true
setting.OAuthService.OAuthProviderName = sec.Key("oauth_provider_name").String()
teamIds := sec.Key("team_ids").Ints(",")
allowedOrganizations := sec.Key("allowed_organizations").Strings(" ")
SocialMap["generic_oauth"] = &GenericOAuth{
Config: &config,
allowedDomains: info.AllowedDomains,
apiUrl: info.ApiUrl,
allowSignup: info.AllowSignup,
teamIds: teamIds,
allowedOrganizations: allowedOrganizations,
teamIds: sec.Key("team_ids").Ints(","),
allowedOrganizations: sec.Key("allowed_organizations").Strings(" "),
}
}
if name == "grafananet" {
config := oauth2.Config{
ClientID: info.ClientId,
ClientSecret: info.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: setting.GrafanaNetUrl + "/oauth2/authorize",
TokenURL: setting.GrafanaNetUrl + "/api/oauth2/token",
},
RedirectURL: strings.TrimSuffix(setting.AppUrl, "/") + SocialBaseUrl + name,
Scopes: info.Scopes,
}
SocialMap["grafananet"] = &SocialGrafanaNet{
Config: &config,
url: setting.GrafanaNetUrl,
allowSignup: info.AllowSignup,
allowedOrganizations: sec.Key("allowed_organizations").Strings(" "),
}
}
}
......
define([
'angular',
'lodash',
'../core_module',
'app/core/config',
],
function (angular, coreModule, config) {
function (angular, _, coreModule, config) {
'use strict';
var failCodes = {
"1000": "Required Github team membership not fulfilled",
"1001": "Required Github organization membership not fulfilled",
"1000": "Required team membership not fulfilled",
"1001": "Required organization membership not fulfilled",
"1002": "Required email domain not fulfilled",
};
......@@ -21,12 +22,10 @@ function (angular, coreModule, config) {
contextSrv.sidemenu = false;
$scope.googleAuthEnabled = config.googleAuthEnabled;
$scope.githubAuthEnabled = config.githubAuthEnabled;
$scope.oauthEnabled = config.githubAuthEnabled || config.googleAuthEnabled || config.genericOAuthEnabled;
$scope.oauth = config.oauth;
$scope.oauthEnabled = _.keys(config.oauth).length > 0;
$scope.allowUserPassLogin = config.allowUserPassLogin;
$scope.genericOAuthEnabled = config.genericOAuthEnabled;
$scope.oauthProviderName = config.oauthProviderName;
$scope.disableUserSignUp = config.disableUserSignUp;
$scope.loginHint = config.loginHint;
......
......@@ -51,18 +51,21 @@
<div class="clearfix"></div>
<div class="login-oauth text-center" ng-show="oauthEnabled">
<a class="btn btn-large btn-google" href="login/google" target="_self" ng-if="googleAuthEnabled">
<a class="btn btn-large btn-google" href="login/google" target="_self" ng-if="oauth.google">
<i class="fa fa-google"></i>
with Google
</a>
<a class="btn btn-large btn-github" href="login/github" target="_self" ng-if="githubAuthEnabled">
<a class="btn btn-large btn-github" href="login/github" target="_self" ng-if="oauth.github">
<i class="fa fa-github"></i>
with Github
</a>
<a class="btn btn-large btn-generic-oauth" href="login/generic_oauth" target="_self" ng-if="genericOAuthEnabled">
<a class="btn btn-large btn-grafana-net" href="login/grafananet" target="_self" ng-if="oauth.grafananet">
with <span>Grafana.net</span>
</a>
<a class="btn btn-large btn-generic-oauth" href="login/generic_oauth" target="_self" ng-if="oauth.generic_oauth">
<i class="fa fa-gear"></i>
with {{oauthProviderName || "OAuth 2"}}
</a>
with {{oauth.generic_oauth.name}}
</a>
</div>
</div>
......
......@@ -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;
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment