Commit 42670c27 by Torkel Ödegaard

feat(ldap): more work on org role sync

parent fe41a4e6
...@@ -27,7 +27,7 @@ func init() { ...@@ -27,7 +27,7 @@ func init() {
SearchFilter: "(cn=%s)", SearchFilter: "(cn=%s)",
SearchBaseDNs: []string{"dc=grafana,dc=org"}, SearchBaseDNs: []string{"dc=grafana,dc=org"},
LdapGroups: []*LdapGroupToOrgRole{ LdapGroups: []*LdapGroupToOrgRole{
{GroupDN: "cn=users,dc=grafana,dc=org", OrgRole: "Editor"}, {GroupDN: "cn=users,dc=grafana,dc=org", OrgRole: m.ROLE_EDITOR},
}, },
}, },
} }
...@@ -89,7 +89,9 @@ func (a *ldapAuther) login(query *AuthenticateUserQuery) error { ...@@ -89,7 +89,9 @@ func (a *ldapAuther) login(query *AuthenticateUserQuery) error {
func (a *ldapAuther) getGrafanaUserFor(ldapUser *ldapUserInfo) (*m.User, error) { func (a *ldapAuther) getGrafanaUserFor(ldapUser *ldapUserInfo) (*m.User, error) {
// validate that the user has access // validate that the user has access
access := false // if there are no ldap group mappings access is true
// otherwise a single group must match
access := len(a.server.LdapGroups) == 0
for _, ldapGroup := range a.server.LdapGroups { for _, ldapGroup := range a.server.LdapGroups {
if ldapUser.isMemberOf(ldapGroup.GroupDN) { if ldapUser.isMemberOf(ldapGroup.GroupDN) {
access = true access = true
...@@ -129,6 +131,49 @@ func (a *ldapAuther) createGrafanaUser(ldapUser *ldapUserInfo) (*m.User, error) ...@@ -129,6 +131,49 @@ func (a *ldapAuther) createGrafanaUser(ldapUser *ldapUserInfo) (*m.User, error)
} }
func (a *ldapAuther) syncOrgRoles(user *m.User, ldapUser *ldapUserInfo) error { func (a *ldapAuther) syncOrgRoles(user *m.User, ldapUser *ldapUserInfo) error {
if len(a.server.LdapGroups) == 0 {
return nil
}
orgsQuery := m.GetUserOrgListQuery{UserId: user.Id}
if err := bus.Dispatch(&orgsQuery); err != nil {
return err
}
// remove or update org roles
for _, org := range orgsQuery.Result {
for _, group := range a.server.LdapGroups {
if group.OrgId == org.OrgId && ldapUser.isMemberOf(group.GroupDN) {
if org.Role != group.OrgRole {
// update role
}
} else {
// remove role
}
}
}
for _, group := range a.server.LdapGroups {
if !ldapUser.isMemberOf(group.GroupDN) {
continue
}
match := false
for _, org := range orgsQuery.Result {
if group.OrgId == org.OrgId {
match = true
}
}
if !match {
// add role
cmd := m.AddOrgUserCommand{UserId: user.Id, Role: group.OrgRole, OrgId: group.OrgId}
if err := bus.Dispatch(&cmd); err != nil {
return err
}
}
}
return nil return nil
} }
......
...@@ -13,7 +13,9 @@ func TestLdapAuther(t *testing.T) { ...@@ -13,7 +13,9 @@ func TestLdapAuther(t *testing.T) {
Convey("When translating ldap user to grafana user", t, func() { Convey("When translating ldap user to grafana user", t, func() {
Convey("Given no ldap group map match", func() { Convey("Given no ldap group map match", func() {
ldapAuther := NewLdapAuthenticator(&LdapServerConf{}) ldapAuther := NewLdapAuthenticator(&LdapServerConf{
LdapGroups: []*LdapGroupToOrgRole{{}},
})
_, err := ldapAuther.getGrafanaUserFor(&ldapUserInfo{}) _, err := ldapAuther.getGrafanaUserFor(&ldapUserInfo{})
So(err, ShouldEqual, ErrInvalidCredentials) So(err, ShouldEqual, ErrInvalidCredentials)
...@@ -24,7 +26,7 @@ func TestLdapAuther(t *testing.T) { ...@@ -24,7 +26,7 @@ func TestLdapAuther(t *testing.T) {
ldapAutherScenario("Given wildcard group match", func(sc *scenarioContext) { ldapAutherScenario("Given wildcard group match", func(sc *scenarioContext) {
ldapAuther := NewLdapAuthenticator(&LdapServerConf{ ldapAuther := NewLdapAuthenticator(&LdapServerConf{
LdapGroups: []*LdapGroupToOrgRole{ LdapGroups: []*LdapGroupToOrgRole{
{GroupDN: "*", OrgRole: "Admin", OrgName: "Main"}, {GroupDN: "*", OrgRole: "Admin"},
}, },
}) })
...@@ -38,7 +40,7 @@ func TestLdapAuther(t *testing.T) { ...@@ -38,7 +40,7 @@ func TestLdapAuther(t *testing.T) {
ldapAutherScenario("Given exact group match", func(sc *scenarioContext) { ldapAutherScenario("Given exact group match", func(sc *scenarioContext) {
ldapAuther := NewLdapAuthenticator(&LdapServerConf{ ldapAuther := NewLdapAuthenticator(&LdapServerConf{
LdapGroups: []*LdapGroupToOrgRole{ LdapGroups: []*LdapGroupToOrgRole{
{GroupDN: "cn=users", OrgRole: "Admin", OrgName: "Main"}, {GroupDN: "cn=users", OrgRole: "Admin"},
}, },
}) })
...@@ -52,7 +54,7 @@ func TestLdapAuther(t *testing.T) { ...@@ -52,7 +54,7 @@ func TestLdapAuther(t *testing.T) {
ldapAutherScenario("Given no existing grafana user", func(sc *scenarioContext) { ldapAutherScenario("Given no existing grafana user", func(sc *scenarioContext) {
ldapAuther := NewLdapAuthenticator(&LdapServerConf{ ldapAuther := NewLdapAuthenticator(&LdapServerConf{
LdapGroups: []*LdapGroupToOrgRole{ LdapGroups: []*LdapGroupToOrgRole{
{GroupDN: "cn=users", OrgRole: "Admin", OrgName: "Main"}, {GroupDN: "cn=users", OrgRole: "Admin"},
}, },
}) })
...@@ -78,6 +80,28 @@ func TestLdapAuther(t *testing.T) { ...@@ -78,6 +80,28 @@ func TestLdapAuther(t *testing.T) {
}) })
}) })
Convey("When syncing ldap groups to grafana org roles", t, func() {
ldapAutherScenario("given no current user orgs", func(sc *scenarioContext) {
ldapAuther := NewLdapAuthenticator(&LdapServerConf{
LdapGroups: []*LdapGroupToOrgRole{
{GroupDN: "cn=users", OrgRole: "Admin"},
},
})
sc.userOrgsQueryReturns([]*m.UserOrgDTO{})
err := ldapAuther.syncOrgRoles(&m.User{}, &ldapUserInfo{
MemberOf: []string{"cn=users"},
})
Convey("Should create new org user", func() {
So(err, ShouldBeNil)
So(sc.addOrgUserCommand, ShouldNotBeNil)
So(sc.addOrgUserCommand.Role, ShouldEqual, m.ROLE_ADMIN)
})
})
})
} }
func ldapAutherScenario(desc string, fn scenarioFunc) { func ldapAutherScenario(desc string, fn scenarioFunc) {
...@@ -85,18 +109,25 @@ func ldapAutherScenario(desc string, fn scenarioFunc) { ...@@ -85,18 +109,25 @@ func ldapAutherScenario(desc string, fn scenarioFunc) {
defer bus.ClearBusHandlers() defer bus.ClearBusHandlers()
sc := &scenarioContext{} sc := &scenarioContext{}
bus.AddHandler("test", func(cmd *m.CreateUserCommand) error { bus.AddHandler("test", func(cmd *m.CreateUserCommand) error {
sc.createUserCmd = cmd sc.createUserCmd = cmd
sc.createUserCmd.Result = m.User{Login: cmd.Login} sc.createUserCmd.Result = m.User{Login: cmd.Login}
return nil return nil
}) })
bus.AddHandler("test", func(cmd *m.AddOrgUserCommand) error {
sc.addOrgUserCommand = cmd
return nil
})
fn(sc) fn(sc)
}) })
} }
type scenarioContext struct { type scenarioContext struct {
createUserCmd *m.CreateUserCommand createUserCmd *m.CreateUserCommand
addOrgUserCommand *m.AddOrgUserCommand
} }
func (sc *scenarioContext) userQueryReturns(user *m.User) { func (sc *scenarioContext) userQueryReturns(user *m.User) {
...@@ -110,4 +141,11 @@ func (sc *scenarioContext) userQueryReturns(user *m.User) { ...@@ -110,4 +141,11 @@ func (sc *scenarioContext) userQueryReturns(user *m.User) {
}) })
} }
func (sc *scenarioContext) userOrgsQueryReturns(orgs []*m.UserOrgDTO) {
bus.AddHandler("test", func(query *m.GetUserOrgListQuery) error {
query.Result = orgs
return nil
})
}
type scenarioFunc func(c *scenarioContext) type scenarioFunc func(c *scenarioContext)
package auth package auth
import m "github.com/grafana/grafana/pkg/models"
type LdapGroupToOrgRole struct { type LdapGroupToOrgRole struct {
GroupDN string GroupDN string
OrgId int OrgId int64
OrgRole string OrgRole m.RoleType
} }
type LdapServerConf struct { type LdapServerConf struct {
......
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