|
|
@@ -24,10 +24,9 @@ type ILdapConn interface {
|
|
|
}
|
|
|
|
|
|
type ILdapAuther interface {
|
|
|
- Login(query *LoginUserQuery) error
|
|
|
- SyncSignedInUser(signedInUser *m.SignedInUser) error
|
|
|
- GetGrafanaUserFor(ldapUser *LdapUserInfo) (*m.User, error)
|
|
|
- SyncOrgRoles(user *m.User, ldapUser *LdapUserInfo) error
|
|
|
+ Login(query *m.LoginUserQuery) error
|
|
|
+ SyncUser(query *m.LoginUserQuery) error
|
|
|
+ GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LdapUserInfo) (*m.User, error)
|
|
|
}
|
|
|
|
|
|
type ldapAuther struct {
|
|
|
@@ -89,7 +88,8 @@ func (a *ldapAuther) Dial() error {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
-func (a *ldapAuther) Login(query *LoginUserQuery) error {
|
|
|
+func (a *ldapAuther) Login(query *m.LoginUserQuery) error {
|
|
|
+ // connect to ldap server
|
|
|
if err := a.Dial(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
@@ -101,206 +101,105 @@ func (a *ldapAuther) Login(query *LoginUserQuery) error {
|
|
|
}
|
|
|
|
|
|
// find user entry & attributes
|
|
|
- if ldapUser, err := a.searchForUser(query.Username); err != nil {
|
|
|
+ ldapUser, err := a.searchForUser(query.Username)
|
|
|
+ if err != nil {
|
|
|
return err
|
|
|
- } else {
|
|
|
- a.log.Debug("Ldap User found", "info", spew.Sdump(ldapUser))
|
|
|
+ }
|
|
|
|
|
|
- // check if a second user bind is needed
|
|
|
- if a.requireSecondBind {
|
|
|
- if err := a.secondBind(ldapUser, query.Password); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
+ a.log.Debug("Ldap User found", "info", spew.Sdump(ldapUser))
|
|
|
|
|
|
- if grafanaUser, err := a.GetGrafanaUserFor(ldapUser); err != nil {
|
|
|
+ // check if a second user bind is needed
|
|
|
+ if a.requireSecondBind {
|
|
|
+ err = a.secondBind(ldapUser, query.Password)
|
|
|
+ if err != nil {
|
|
|
return err
|
|
|
- } else {
|
|
|
- if syncErr := a.syncInfoAndOrgRoles(grafanaUser, ldapUser); syncErr != nil {
|
|
|
- return syncErr
|
|
|
- }
|
|
|
- query.User = grafanaUser
|
|
|
- return nil
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-func (a *ldapAuther) SyncSignedInUser(signedInUser *m.SignedInUser) error {
|
|
|
- grafanaUser := m.User{
|
|
|
- Id: signedInUser.UserId,
|
|
|
- Login: signedInUser.Login,
|
|
|
- Email: signedInUser.Email,
|
|
|
- Name: signedInUser.Name,
|
|
|
+ grafanaUser, err := a.GetGrafanaUserFor(query.ReqContext, ldapUser)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- if err := a.Dial(); err != nil {
|
|
|
+ query.User = grafanaUser
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (a *ldapAuther) SyncUser(query *m.LoginUserQuery) error {
|
|
|
+ // connect to ldap server
|
|
|
+ err := a.Dial()
|
|
|
+ if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
-
|
|
|
defer a.conn.Close()
|
|
|
- if err := a.serverBind(); err != nil {
|
|
|
+
|
|
|
+ err = a.serverBind()
|
|
|
+ if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- if ldapUser, err := a.searchForUser(signedInUser.Login); err != nil {
|
|
|
+ // find user entry & attributes
|
|
|
+ ldapUser, err := a.searchForUser(query.Username)
|
|
|
+ if err != nil {
|
|
|
a.log.Error("Failed searching for user in ldap", "error", err)
|
|
|
-
|
|
|
return err
|
|
|
- } else {
|
|
|
- if err := a.syncInfoAndOrgRoles(&grafanaUser, ldapUser); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- a.log.Debug("Got Ldap User Info", "user", spew.Sdump(ldapUser))
|
|
|
}
|
|
|
|
|
|
- return nil
|
|
|
-}
|
|
|
+ a.log.Debug("Ldap User found", "info", spew.Sdump(ldapUser))
|
|
|
|
|
|
-// Sync info for ldap user and grafana user
|
|
|
-func (a *ldapAuther) syncInfoAndOrgRoles(user *m.User, ldapUser *LdapUserInfo) error {
|
|
|
- // sync user details
|
|
|
- if err := a.syncUserInfo(user, ldapUser); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- // sync org roles
|
|
|
- if err := a.SyncOrgRoles(user, ldapUser); err != nil {
|
|
|
+ grafanaUser, err := a.GetGrafanaUserFor(query.ReqContext, ldapUser)
|
|
|
+ if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
+ query.User = grafanaUser
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (a *ldapAuther) GetGrafanaUserFor(ldapUser *LdapUserInfo) (*m.User, error) {
|
|
|
- // validate that the user has access
|
|
|
- // 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 {
|
|
|
- if ldapUser.isMemberOf(ldapGroup.GroupDN) {
|
|
|
- access = true
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if !access {
|
|
|
- a.log.Info("Ldap Auth: user does not belong in any of the specified ldap groups", "username", ldapUser.Username, "groups", ldapUser.MemberOf)
|
|
|
- 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 && setting.LdapAllowSignup {
|
|
|
- return a.createGrafanaUser(ldapUser)
|
|
|
- } else if err == m.ErrUserNotFound {
|
|
|
- a.log.Warn("Not allowing LDAP login, user not found in internal user database, and ldap allow signup = false")
|
|
|
- return nil, ErrInvalidCredentials
|
|
|
- } else {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return userQuery.Result, nil
|
|
|
-
|
|
|
-}
|
|
|
-func (a *ldapAuther) createGrafanaUser(ldapUser *LdapUserInfo) (*m.User, error) {
|
|
|
- cmd := m.CreateUserCommand{
|
|
|
- Login: ldapUser.Username,
|
|
|
- Email: ldapUser.Email,
|
|
|
- Name: fmt.Sprintf("%s %s", ldapUser.FirstName, ldapUser.LastName),
|
|
|
- }
|
|
|
-
|
|
|
- if err := bus.Dispatch(&cmd); err != nil {
|
|
|
- return nil, err
|
|
|
- }
|
|
|
-
|
|
|
- return &cmd.Result, nil
|
|
|
-}
|
|
|
-
|
|
|
-func (a *ldapAuther) syncUserInfo(user *m.User, ldapUser *LdapUserInfo) error {
|
|
|
- var name = fmt.Sprintf("%s %s", ldapUser.FirstName, ldapUser.LastName)
|
|
|
- if user.Email == ldapUser.Email && user.Name == name {
|
|
|
- return nil
|
|
|
- }
|
|
|
-
|
|
|
- a.log.Debug("Syncing user info", "username", ldapUser.Username)
|
|
|
- updateCmd := m.UpdateUserCommand{}
|
|
|
- updateCmd.UserId = user.Id
|
|
|
- updateCmd.Login = user.Login
|
|
|
- updateCmd.Email = ldapUser.Email
|
|
|
- updateCmd.Name = fmt.Sprintf("%s %s", ldapUser.FirstName, ldapUser.LastName)
|
|
|
- return bus.Dispatch(&updateCmd)
|
|
|
-}
|
|
|
-
|
|
|
-func (a *ldapAuther) SyncOrgRoles(user *m.User, ldapUser *LdapUserInfo) error {
|
|
|
- if len(a.server.LdapGroups) == 0 {
|
|
|
- a.log.Warn("No group mappings defined")
|
|
|
- return nil
|
|
|
+func (a *ldapAuther) GetGrafanaUserFor(ctx *m.ReqContext, ldapUser *LdapUserInfo) (*m.User, error) {
|
|
|
+ extUser := &m.ExternalUserInfo{
|
|
|
+ AuthModule: "ldap",
|
|
|
+ AuthId: ldapUser.DN,
|
|
|
+ Name: fmt.Sprintf("%s %s", ldapUser.FirstName, ldapUser.LastName),
|
|
|
+ Login: ldapUser.Username,
|
|
|
+ Email: ldapUser.Email,
|
|
|
+ OrgRoles: map[int64]m.RoleType{},
|
|
|
}
|
|
|
|
|
|
- orgsQuery := m.GetUserOrgListQuery{UserId: user.Id}
|
|
|
- if err := bus.Dispatch(&orgsQuery); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
-
|
|
|
- handledOrgIds := map[int64]bool{}
|
|
|
-
|
|
|
- // update or remove org roles
|
|
|
- for _, org := range orgsQuery.Result {
|
|
|
- match := false
|
|
|
- handledOrgIds[org.OrgId] = true
|
|
|
-
|
|
|
- for _, group := range a.server.LdapGroups {
|
|
|
- if org.OrgId != group.OrgId {
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- if ldapUser.isMemberOf(group.GroupDN) {
|
|
|
- match = true
|
|
|
- if org.Role != group.OrgRole {
|
|
|
- // update role
|
|
|
- cmd := m.UpdateOrgUserCommand{OrgId: org.OrgId, UserId: user.Id, Role: group.OrgRole}
|
|
|
- if err := bus.Dispatch(&cmd); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
- // ignore subsequent ldap group mapping matches
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // remove role if no mappings match
|
|
|
- if !match {
|
|
|
- cmd := m.RemoveOrgUserCommand{OrgId: org.OrgId, UserId: user.Id}
|
|
|
- if err := bus.Dispatch(&cmd); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // add missing org roles
|
|
|
for _, group := range a.server.LdapGroups {
|
|
|
- if !ldapUser.isMemberOf(group.GroupDN) {
|
|
|
+ // only use the first match for each org
|
|
|
+ if extUser.OrgRoles[group.OrgId] != "" {
|
|
|
continue
|
|
|
}
|
|
|
|
|
|
- if _, exists := handledOrgIds[group.OrgId]; exists {
|
|
|
- continue
|
|
|
+ if ldapUser.isMemberOf(group.GroupDN) {
|
|
|
+ extUser.OrgRoles[group.OrgId] = group.OrgRole
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- // add role
|
|
|
- cmd := m.AddOrgUserCommand{UserId: user.Id, Role: group.OrgRole, OrgId: group.OrgId}
|
|
|
- err := bus.Dispatch(&cmd)
|
|
|
- if err != nil && err != m.ErrOrgNotFound {
|
|
|
- return err
|
|
|
- }
|
|
|
+ // validate that the user has access
|
|
|
+ // if there are no ldap group mappings access is true
|
|
|
+ // otherwise a single group must match
|
|
|
+ if len(a.server.LdapGroups) > 0 && len(extUser.OrgRoles) < 1 {
|
|
|
+ a.log.Info(
|
|
|
+ "Ldap Auth: user does not belong in any of the specified ldap groups",
|
|
|
+ "username", ldapUser.Username,
|
|
|
+ "groups", ldapUser.MemberOf)
|
|
|
+ return nil, ErrInvalidCredentials
|
|
|
+ }
|
|
|
|
|
|
- // mark this group has handled so we do not process it again
|
|
|
- handledOrgIds[group.OrgId] = true
|
|
|
+ // add/update user in grafana
|
|
|
+ userQuery := &m.UpsertUserCommand{
|
|
|
+ ReqContext: ctx,
|
|
|
+ ExternalUser: extUser,
|
|
|
+ SignupAllowed: setting.LdapAllowSignup,
|
|
|
+ }
|
|
|
+ err := bus.Dispatch(userQuery)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
}
|
|
|
|
|
|
- return nil
|
|
|
+ return userQuery.Result, nil
|
|
|
}
|
|
|
|
|
|
func (a *ldapAuther) serverBind() error {
|
|
|
@@ -403,8 +302,7 @@ func (a *ldapAuther) searchForUser(username string) (*LdapUserInfo, error) {
|
|
|
// If we are using a POSIX LDAP schema it won't support memberOf, so we manually search the groups
|
|
|
var groupSearchResult *ldap.SearchResult
|
|
|
for _, groupSearchBase := range a.server.GroupSearchBaseDNs {
|
|
|
- var filter_replace string
|
|
|
- filter_replace = getLdapAttr(a.server.GroupSearchFilterUserAttribute, searchResult)
|
|
|
+ filter_replace := getLdapAttr(a.server.GroupSearchFilterUserAttribute, searchResult)
|
|
|
if a.server.GroupSearchFilterUserAttribute == "" {
|
|
|
filter_replace = getLdapAttr(a.server.Attr.Username, searchResult)
|
|
|
}
|
|
|
@@ -470,7 +368,3 @@ func getLdapAttrArray(name string, result *ldap.SearchResult) []string {
|
|
|
}
|
|
|
return []string{}
|
|
|
}
|
|
|
-
|
|
|
-func createUserFromLdapInfo() error {
|
|
|
- return nil
|
|
|
-}
|