Browse Source

feat(ldap): more unit tests for ldap to grafana user sync

Torkel Ödegaard 10 years ago
parent
commit
a7b1df34c5
6 changed files with 167 additions and 42 deletions
  1. 1 1
      pkg/auth/auth.go
  2. 25 35
      pkg/auth/ldap.go
  3. 113 0
      pkg/auth/ldap_test.go
  4. 22 0
      pkg/auth/ldap_user.go
  5. 6 5
      pkg/auth/settings.go
  6. 0 1
      pkg/setting/setting.go

+ 1 - 1
pkg/auth/auth.go

@@ -30,7 +30,7 @@ func AuthenticateUser(query *AuthenticateUserQuery) error {
 	}
 
 	if setting.LdapEnabled {
-		for _, server := range setting.LdapServers {
+		for _, server := range ldapServers {
 			auther := NewLdapAuthenticator(server)
 			err = auther.login(query)
 			if err == nil || err != ErrInvalidCredentials {

+ 25 - 35
pkg/auth/ldap.go

@@ -8,12 +8,13 @@ import (
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/log"
 	m "github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/setting"
 )
 
+var ldapServers []*LdapServerConf
+
 func init() {
-	setting.LdapServers = []*setting.LdapServerConf{
-		&setting.LdapServerConf{
+	ldapServers = []*LdapServerConf{
+		{
 			UseSSL:        false,
 			Host:          "127.0.0.1",
 			Port:          "389",
@@ -25,45 +26,20 @@ func init() {
 			AttrEmail:     "email",
 			SearchFilter:  "(cn=%s)",
 			SearchBaseDNs: []string{"dc=grafana,dc=org"},
+			LdapGroups: []*LdapGroupToOrgRole{
+				{GroupDN: "cn=users,dc=grafana,dc=org", OrgName: "Main Org.", OrgRole: "Editor"},
+			},
 		},
 	}
 }
 
 type ldapAuther struct {
-	server *setting.LdapServerConf
+	server *LdapServerConf
 	conn   *ldap.Conn
 }
 
-type ldapUserInfo struct {
-	FirstName string
-	LastName  string
-	Username  string
-	Email     string
-	MemberOf  []string
-}
-
-func (u *ldapUserInfo) isMemberOfAny(groups []string) bool {
-	for _, group := range groups {
-		if u.isMemberOf(group) {
-			return true
-		}
-	}
-	return false
-}
-
-func (u *ldapUserInfo) isMemberOf(group string) bool {
-	for _, member := range u.MemberOf {
-		if member == group {
-			return true
-		}
-	}
-	return false
-}
-
-func NewLdapAuthenticator(server *setting.LdapServerConf) *ldapAuther {
-	return &ldapAuther{
-		server: server,
-	}
+func NewLdapAuthenticator(server *LdapServerConf) *ldapAuther {
+	return &ldapAuther{server: server}
 }
 
 func (a *ldapAuther) Dial() error {
@@ -108,11 +84,26 @@ func (a *ldapAuther) login(query *AuthenticateUserQuery) error {
 }
 
 func (a *ldapAuther) getGrafanaUserFor(ldapUser *ldapUserInfo) (*m.User, error) {
+	// validate that the user has access
+	access := false
+	for _, ldapGroup := range a.server.LdapGroups {
+		if ldapUser.isMemberOf(ldapGroup.GroupDN) {
+			access = true
+		}
+	}
+
+	if !access {
+		log.Info("Ldap Auth: user %s does not belong in any of the specified ldap groups", ldapUser.Username)
+		return nil, ErrInvalidCredentials
+	}
+
 	// get user from grafana db
 	userQuery := m.GetUserByLoginQuery{LoginOrEmail: ldapUser.Username}
 	if err := bus.Dispatch(&userQuery); err != nil {
 		if err == m.ErrUserNotFound {
 			return a.createGrafanaUser(ldapUser)
+		} else {
+			return nil, err
 		}
 	}
 
@@ -221,5 +212,4 @@ func getLdapAttrArray(name string, result *ldap.SearchResult) []string {
 
 func createUserFromLdapInfo() error {
 	return nil
-
 }

+ 113 - 0
pkg/auth/ldap_test.go

@@ -0,0 +1,113 @@
+package auth
+
+import (
+	"testing"
+
+	"github.com/grafana/grafana/pkg/bus"
+	m "github.com/grafana/grafana/pkg/models"
+	. "github.com/smartystreets/goconvey/convey"
+)
+
+func TestLdapAuther(t *testing.T) {
+
+	Convey("When translating ldap user to grafana user", t, func() {
+
+		Convey("Given no ldap group map match", func() {
+			ldapAuther := NewLdapAuthenticator(&LdapServerConf{})
+			_, err := ldapAuther.getGrafanaUserFor(&ldapUserInfo{})
+
+			So(err, ShouldEqual, ErrInvalidCredentials)
+		})
+
+		var user1 = &m.User{}
+
+		ldapAutherScenario("Given wildcard group match", func(sc *scenarioContext) {
+			ldapAuther := NewLdapAuthenticator(&LdapServerConf{
+				LdapGroups: []*LdapGroupToOrgRole{
+					{GroupDN: "*", OrgRole: "Admin", OrgName: "Main"},
+				},
+			})
+
+			sc.userQueryReturns(user1)
+
+			result, err := ldapAuther.getGrafanaUserFor(&ldapUserInfo{})
+			So(err, ShouldBeNil)
+			So(result, ShouldEqual, user1)
+		})
+
+		ldapAutherScenario("Given exact group match", func(sc *scenarioContext) {
+			ldapAuther := NewLdapAuthenticator(&LdapServerConf{
+				LdapGroups: []*LdapGroupToOrgRole{
+					{GroupDN: "cn=users", OrgRole: "Admin", OrgName: "Main"},
+				},
+			})
+
+			sc.userQueryReturns(user1)
+
+			result, err := ldapAuther.getGrafanaUserFor(&ldapUserInfo{MemberOf: []string{"cn=users"}})
+			So(err, ShouldBeNil)
+			So(result, ShouldEqual, user1)
+		})
+
+		ldapAutherScenario("Given no existing grafana user", func(sc *scenarioContext) {
+			ldapAuther := NewLdapAuthenticator(&LdapServerConf{
+				LdapGroups: []*LdapGroupToOrgRole{
+					{GroupDN: "cn=users", OrgRole: "Admin", OrgName: "Main"},
+				},
+			})
+
+			sc.userQueryReturns(nil)
+
+			result, err := ldapAuther.getGrafanaUserFor(&ldapUserInfo{
+				Username: "torkelo",
+				Email:    "my@email.com",
+				MemberOf: []string{"cn=users"},
+			})
+
+			So(err, ShouldBeNil)
+
+			Convey("Should create new user", func() {
+				So(sc.createUserCmd.Login, ShouldEqual, "torkelo")
+				So(sc.createUserCmd.Email, ShouldEqual, "my@email.com")
+			})
+
+			Convey("Should return new user", func() {
+				So(result.Login, ShouldEqual, "torkelo")
+			})
+
+		})
+
+	})
+}
+
+func ldapAutherScenario(desc string, fn scenarioFunc) {
+	Convey(desc, func() {
+		defer bus.ClearBusHandlers()
+
+		sc := &scenarioContext{}
+		bus.AddHandler("test", func(cmd *m.CreateUserCommand) error {
+			sc.createUserCmd = cmd
+			sc.createUserCmd.Result = m.User{Login: cmd.Login}
+			return nil
+		})
+
+		fn(sc)
+	})
+}
+
+type scenarioContext struct {
+	createUserCmd *m.CreateUserCommand
+}
+
+func (sc *scenarioContext) userQueryReturns(user *m.User) {
+	bus.AddHandler("test", func(query *m.GetUserByLoginQuery) error {
+		if user == nil {
+			return m.ErrUserNotFound
+		} else {
+			query.Result = user
+			return nil
+		}
+	})
+}
+
+type scenarioFunc func(c *scenarioContext)

+ 22 - 0
pkg/auth/ldap_user.go

@@ -0,0 +1,22 @@
+package auth
+
+type ldapUserInfo struct {
+	FirstName string
+	LastName  string
+	Username  string
+	Email     string
+	MemberOf  []string
+}
+
+func (u *ldapUserInfo) isMemberOf(group string) bool {
+	if group == "*" {
+		return true
+	}
+
+	for _, member := range u.MemberOf {
+		if member == group {
+			return true
+		}
+	}
+	return false
+}

+ 6 - 5
pkg/setting/setting_ldap.go → pkg/auth/settings.go

@@ -1,9 +1,10 @@
-package setting
+package auth
 
 type LdapGroupToOrgRole struct {
-	LdapGroupPath string
-	OrgId         int
-	OrgRole       string
+	GroupDN string
+	OrgId   int
+	OrgName string
+	OrgRole string
 }
 
 type LdapServerConf struct {
@@ -21,5 +22,5 @@ type LdapServerConf struct {
 	SearchFilter  string
 	SearchBaseDNs []string
 
-	LdapGroups []LdapGroupToOrgRole
+	LdapGroups []*LdapGroupToOrgRole
 }

+ 0 - 1
pkg/setting/setting.go

@@ -119,7 +119,6 @@ var (
 
 	// LDAP
 	LdapEnabled bool
-	LdapServers []*LdapServerConf
 
 	// SMTP email settings
 	Smtp SmtpSettings