Commit 3fa2ec07 by Torkel Ödegaard

Merge branch 'ldap-grafana-admin'

parents c63f1425 c189262b
...@@ -72,6 +72,8 @@ email = "email" ...@@ -72,6 +72,8 @@ email = "email"
[[servers.group_mappings]] [[servers.group_mappings]]
group_dn = "cn=admins,dc=grafana,dc=org" group_dn = "cn=admins,dc=grafana,dc=org"
org_role = "Admin" org_role = "Admin"
# To make user a instance admin (Grafana Admin) uncomment line below
# grafana_admin = true
# The Grafana organization database id, optional, if left out the default org (id 1) will be used # The Grafana organization database id, optional, if left out the default org (id 1) will be used
# org_id = 1 # org_id = 1
......
...@@ -23,8 +23,9 @@ specific configuration file (default: `/etc/grafana/ldap.toml`). ...@@ -23,8 +23,9 @@ specific configuration file (default: `/etc/grafana/ldap.toml`).
### Example config ### Example config
```toml ```toml
# Set to true to log user information returned from LDAP # To troubleshoot and get more log info enable ldap debug logging in grafana.ini
verbose_logging = false # [log]
# filters = ldap:debug
[[servers]] [[servers]]
# Ldap server host (specify multiple hosts space separated) # Ldap server host (specify multiple hosts space separated)
...@@ -73,6 +74,8 @@ email = "email" ...@@ -73,6 +74,8 @@ email = "email"
[[servers.group_mappings]] [[servers.group_mappings]]
group_dn = "cn=admins,dc=grafana,dc=org" group_dn = "cn=admins,dc=grafana,dc=org"
org_role = "Admin" org_role = "Admin"
# To make user a instance admin (Grafana Admin) uncomment line below
# grafana_admin = true
# The Grafana organization database id, optional, if left out the default org (id 1) will be used. Setting this allows for multiple group_dn's to be assigned to the same org_role provided the org_id differs # The Grafana organization database id, optional, if left out the default org (id 1) will be used. Setting this allows for multiple group_dn's to be assigned to the same org_role provided the org_id differs
# org_id = 1 # org_id = 1
...@@ -132,6 +135,10 @@ Users page, this change will be reset the next time the user logs in. If you ...@@ -132,6 +135,10 @@ Users page, this change will be reset the next time the user logs in. If you
change the LDAP groups of a user, the change will take effect the next change the LDAP groups of a user, the change will take effect the next
time the user logs in. time the user logs in.
### Grafana Admin
with a servers.group_mappings section you can set grafana_admin = true or false to sync Grafana Admin permission. A Grafana server admin has admin access over all orgs &
users.
### Priority ### Priority
The first group mapping that an LDAP user is matched to will be used for the sync. If you have LDAP users that fit multiple mappings, the topmost mapping in the TOML config will be used. The first group mapping that an LDAP user is matched to will be used for the sync. If you have LDAP users that fit multiple mappings, the topmost mapping in the TOML config will be used.
......
...@@ -72,6 +72,13 @@ func UpsertUser(cmd *m.UpsertUserCommand) error { ...@@ -72,6 +72,13 @@ func UpsertUser(cmd *m.UpsertUserCommand) error {
return err return err
} }
// Sync isGrafanaAdmin permission
if extUser.IsGrafanaAdmin != nil && *extUser.IsGrafanaAdmin != cmd.Result.IsAdmin {
if err := bus.Dispatch(&m.UpdateUserPermissionsCommand{UserId: cmd.Result.Id, IsGrafanaAdmin: *extUser.IsGrafanaAdmin}); err != nil {
return err
}
}
err = bus.Dispatch(&m.SyncTeamsCommand{ err = bus.Dispatch(&m.SyncTeamsCommand{
User: cmd.Result, User: cmd.Result,
ExternalUser: extUser, ExternalUser: extUser,
......
...@@ -175,6 +175,7 @@ func (a *ldapAuther) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LdapUserInfo ...@@ -175,6 +175,7 @@ func (a *ldapAuther) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LdapUserInfo
if ldapUser.isMemberOf(group.GroupDN) { if ldapUser.isMemberOf(group.GroupDN) {
extUser.OrgRoles[group.OrgId] = group.OrgRole extUser.OrgRoles[group.OrgId] = group.OrgRole
extUser.IsGrafanaAdmin = group.IsGrafanaAdmin
} }
} }
...@@ -190,18 +191,18 @@ func (a *ldapAuther) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LdapUserInfo ...@@ -190,18 +191,18 @@ func (a *ldapAuther) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LdapUserInfo
} }
// add/update user in grafana // add/update user in grafana
userQuery := &m.UpsertUserCommand{ upsertUserCmd := &m.UpsertUserCommand{
ReqContext: ctx, ReqContext: ctx,
ExternalUser: extUser, ExternalUser: extUser,
SignupAllowed: setting.LdapAllowSignup, SignupAllowed: setting.LdapAllowSignup,
} }
err := bus.Dispatch(userQuery) err := bus.Dispatch(upsertUserCmd)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return userQuery.Result, nil return upsertUserCmd.Result, nil
} }
func (a *ldapAuther) serverBind() error { func (a *ldapAuther) serverBind() error {
......
...@@ -44,9 +44,10 @@ type LdapAttributeMap struct { ...@@ -44,9 +44,10 @@ type LdapAttributeMap struct {
} }
type LdapGroupToOrgRole struct { type LdapGroupToOrgRole struct {
GroupDN string `toml:"group_dn"` GroupDN string `toml:"group_dn"`
OrgId int64 `toml:"org_id"` OrgId int64 `toml:"org_id"`
OrgRole m.RoleType `toml:"org_role"` IsGrafanaAdmin *bool `toml:"grafana_admin"` // This is a pointer to know if it was set or not (for backwards compatability)
OrgRole m.RoleType `toml:"org_role"`
} }
var LdapCfg LdapConfig var LdapCfg LdapConfig
......
...@@ -98,6 +98,10 @@ func TestLdapAuther(t *testing.T) { ...@@ -98,6 +98,10 @@ func TestLdapAuther(t *testing.T) {
So(result.Login, ShouldEqual, "torkelo") So(result.Login, ShouldEqual, "torkelo")
}) })
Convey("Should set isGrafanaAdmin to false by default", func() {
So(result.IsAdmin, ShouldBeFalse)
})
}) })
}) })
...@@ -223,8 +227,32 @@ func TestLdapAuther(t *testing.T) { ...@@ -223,8 +227,32 @@ func TestLdapAuther(t *testing.T) {
So(sc.addOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN) So(sc.addOrgUserCmd.Role, ShouldEqual, m.ROLE_ADMIN)
So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1) So(sc.setUsingOrgCmd.OrgId, ShouldEqual, 1)
}) })
Convey("Should not update permissions unless specified", func() {
So(err, ShouldBeNil)
So(sc.updateUserPermissionsCmd, ShouldBeNil)
})
}) })
ldapAutherScenario("given ldap groups with grafana_admin=true", func(sc *scenarioContext) {
trueVal := true
ldapAuther := NewLdapAuthenticator(&LdapServerConf{
LdapGroups: []*LdapGroupToOrgRole{
{GroupDN: "cn=admins", OrgId: 1, OrgRole: "Admin", IsGrafanaAdmin: &trueVal},
},
})
sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
_, err := ldapAuther.GetGrafanaUserFor(nil, &LdapUserInfo{
MemberOf: []string{"cn=admins"},
})
Convey("Should create user with admin set to true", func() {
So(err, ShouldBeNil)
So(sc.updateUserPermissionsCmd.IsGrafanaAdmin, ShouldBeTrue)
})
})
}) })
Convey("When calling SyncUser", t, func() { Convey("When calling SyncUser", t, func() {
...@@ -332,6 +360,11 @@ func ldapAutherScenario(desc string, fn scenarioFunc) { ...@@ -332,6 +360,11 @@ func ldapAutherScenario(desc string, fn scenarioFunc) {
return nil return nil
}) })
bus.AddHandlerCtx("test", func(ctx context.Context, cmd *m.UpdateUserPermissionsCommand) error {
sc.updateUserPermissionsCmd = cmd
return nil
})
bus.AddHandler("test", func(cmd *m.GetUserByAuthInfoQuery) error { bus.AddHandler("test", func(cmd *m.GetUserByAuthInfoQuery) error {
sc.getUserByAuthInfoQuery = cmd sc.getUserByAuthInfoQuery = cmd
sc.getUserByAuthInfoQuery.Result = &m.User{Login: cmd.Login} sc.getUserByAuthInfoQuery.Result = &m.User{Login: cmd.Login}
...@@ -379,14 +412,15 @@ func ldapAutherScenario(desc string, fn scenarioFunc) { ...@@ -379,14 +412,15 @@ func ldapAutherScenario(desc string, fn scenarioFunc) {
} }
type scenarioContext struct { type scenarioContext struct {
getUserByAuthInfoQuery *m.GetUserByAuthInfoQuery getUserByAuthInfoQuery *m.GetUserByAuthInfoQuery
getUserOrgListQuery *m.GetUserOrgListQuery getUserOrgListQuery *m.GetUserOrgListQuery
createUserCmd *m.CreateUserCommand createUserCmd *m.CreateUserCommand
addOrgUserCmd *m.AddOrgUserCommand addOrgUserCmd *m.AddOrgUserCommand
updateOrgUserCmd *m.UpdateOrgUserCommand updateOrgUserCmd *m.UpdateOrgUserCommand
removeOrgUserCmd *m.RemoveOrgUserCommand removeOrgUserCmd *m.RemoveOrgUserCommand
updateUserCmd *m.UpdateUserCommand updateUserCmd *m.UpdateUserCommand
setUsingOrgCmd *m.SetUsingOrgCommand setUsingOrgCmd *m.SetUsingOrgCommand
updateUserPermissionsCmd *m.UpdateUserPermissionsCommand
} }
func (sc *scenarioContext) userQueryReturns(user *m.User) { func (sc *scenarioContext) userQueryReturns(user *m.User) {
......
...@@ -13,14 +13,15 @@ type UserAuth struct { ...@@ -13,14 +13,15 @@ type UserAuth struct {
} }
type ExternalUserInfo struct { type ExternalUserInfo struct {
AuthModule string AuthModule string
AuthId string AuthId string
UserId int64 UserId int64
Email string Email string
Login string Login string
Name string Name string
Groups []string Groups []string
OrgRoles map[int64]RoleType OrgRoles map[int64]RoleType
IsGrafanaAdmin *bool // This is a pointer to know if we should sync this or not (nil = ignore sync)
} }
// --------------------- // ---------------------
......
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