Open source implementation of Mattermost Enterprise features: Authentication & SSO: - LDAP authentication and sync - LDAP diagnostics - SAML 2.0 SSO - OAuth providers (Google, Office365, OpenID Connect) Infrastructure: - Redis-based cluster implementation - Prometheus metrics - IP filtering - Push proxy authentication Search: - Bleve search engine (lightweight Elasticsearch alternative) Compliance & Security: - Compliance reporting - Data retention policies - Message export (Actiance, GlobalRelay, CSV) - Access control (PAP/PDP) User Management: - Account migration (LDAP/SAML) - ID-loaded push notifications - Outgoing OAuth connections 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
599 lines
18 KiB
Go
599 lines
18 KiB
Go
// Copyright (c) 2024 Mattermost Community Enterprise
|
|
// Open source implementation of Mattermost Enterprise LDAP authentication
|
|
|
|
package ldap
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
|
"github.com/mattermost/mattermost/server/public/shared/request"
|
|
"github.com/mattermost/mattermost/server/v8/einterfaces"
|
|
|
|
ldapv3 "github.com/go-ldap/ldap/v3"
|
|
)
|
|
|
|
type LdapImpl struct {
|
|
config func() *model.Config
|
|
logger mlog.LoggerIFace
|
|
}
|
|
|
|
func NewLdapInterface(config func() *model.Config, logger mlog.LoggerIFace) einterfaces.LdapInterface {
|
|
return &LdapImpl{
|
|
config: config,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (l *LdapImpl) getSettings() *model.LdapSettings {
|
|
return &l.config().LdapSettings
|
|
}
|
|
|
|
func (l *LdapImpl) connect() (*ldapv3.Conn, error) {
|
|
settings := l.getSettings()
|
|
|
|
ldapServer := *settings.LdapServer
|
|
ldapPort := *settings.LdapPort
|
|
connectionSecurity := *settings.ConnectionSecurity
|
|
|
|
var conn *ldapv3.Conn
|
|
var err error
|
|
|
|
address := fmt.Sprintf("%s:%d", ldapServer, ldapPort)
|
|
|
|
switch connectionSecurity {
|
|
case model.ConnSecurityTLS:
|
|
tlsConfig := &tls.Config{
|
|
InsecureSkipVerify: *settings.SkipCertificateVerification,
|
|
ServerName: ldapServer,
|
|
}
|
|
|
|
// Load custom CA certificate if provided
|
|
if *settings.PublicCertificateFile != "" {
|
|
caCert, err := os.ReadFile(*settings.PublicCertificateFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to read CA certificate: %w", err)
|
|
}
|
|
caCertPool := x509.NewCertPool()
|
|
caCertPool.AppendCertsFromPEM(caCert)
|
|
tlsConfig.RootCAs = caCertPool
|
|
}
|
|
|
|
conn, err = ldapv3.DialTLS("tcp", address, tlsConfig)
|
|
case model.ConnSecurityStarttls:
|
|
conn, err = ldapv3.Dial("tcp", address)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to connect to LDAP server: %w", err)
|
|
}
|
|
|
|
tlsConfig := &tls.Config{
|
|
InsecureSkipVerify: *settings.SkipCertificateVerification,
|
|
ServerName: ldapServer,
|
|
}
|
|
err = conn.StartTLS(tlsConfig)
|
|
default:
|
|
conn, err = ldapv3.Dial("tcp", address)
|
|
}
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to connect to LDAP server: %w", err)
|
|
}
|
|
|
|
// Set timeout
|
|
if settings.QueryTimeout != nil && *settings.QueryTimeout > 0 {
|
|
conn.SetTimeout(time.Duration(*settings.QueryTimeout) * time.Second)
|
|
}
|
|
|
|
return conn, nil
|
|
}
|
|
|
|
func (l *LdapImpl) bindAsAdmin(conn *ldapv3.Conn) error {
|
|
settings := l.getSettings()
|
|
return conn.Bind(*settings.BindUsername, *settings.BindPassword)
|
|
}
|
|
|
|
// DoLogin authenticates a user against LDAP
|
|
func (l *LdapImpl) DoLogin(rctx request.CTX, id string, password string) (*model.User, *model.AppError) {
|
|
settings := l.getSettings()
|
|
|
|
if !*settings.Enable {
|
|
return nil, model.NewAppError("LdapInterface.DoLogin", "api.ldap.disabled.app_error", nil, "", http.StatusNotImplemented)
|
|
}
|
|
|
|
conn, err := l.connect()
|
|
if err != nil {
|
|
return nil, model.NewAppError("LdapInterface.DoLogin", "api.ldap.connection_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
defer conn.Close()
|
|
|
|
// First bind as admin to search for user
|
|
if err := l.bindAsAdmin(conn); err != nil {
|
|
return nil, model.NewAppError("LdapInterface.DoLogin", "api.ldap.bind_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
// Search for user
|
|
filter := l.buildUserFilter(id)
|
|
searchRequest := ldapv3.NewSearchRequest(
|
|
*settings.BaseDN,
|
|
ldapv3.ScopeWholeSubtree,
|
|
ldapv3.NeverDerefAliases,
|
|
0, 0, false,
|
|
filter,
|
|
l.getUserAttributes(),
|
|
nil,
|
|
)
|
|
|
|
sr, err := conn.Search(searchRequest)
|
|
if err != nil {
|
|
return nil, model.NewAppError("LdapInterface.DoLogin", "api.ldap.search_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
if len(sr.Entries) == 0 {
|
|
return nil, model.NewAppError("LdapInterface.DoLogin", "api.ldap.user_not_found.app_error", nil, "", http.StatusUnauthorized)
|
|
}
|
|
|
|
if len(sr.Entries) > 1 {
|
|
return nil, model.NewAppError("LdapInterface.DoLogin", "api.ldap.multiple_users.app_error", nil, "", http.StatusBadRequest)
|
|
}
|
|
|
|
entry := sr.Entries[0]
|
|
userDN := entry.DN
|
|
|
|
// Now bind as the user to verify password
|
|
if err := conn.Bind(userDN, password); err != nil {
|
|
return nil, model.NewAppError("LdapInterface.DoLogin", "api.ldap.invalid_credentials.app_error", nil, err.Error(), http.StatusUnauthorized)
|
|
}
|
|
|
|
// Create user from LDAP entry
|
|
user := l.entryToUser(entry)
|
|
return user, nil
|
|
}
|
|
|
|
// GetUser retrieves a user from LDAP
|
|
func (l *LdapImpl) GetUser(rctx request.CTX, id string) (*model.User, *model.AppError) {
|
|
settings := l.getSettings()
|
|
|
|
if !*settings.Enable {
|
|
return nil, model.NewAppError("LdapInterface.GetUser", "api.ldap.disabled.app_error", nil, "", http.StatusNotImplemented)
|
|
}
|
|
|
|
conn, err := l.connect()
|
|
if err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetUser", "api.ldap.connection_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
defer conn.Close()
|
|
|
|
if err := l.bindAsAdmin(conn); err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetUser", "api.ldap.bind_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
filter := l.buildUserFilter(id)
|
|
searchRequest := ldapv3.NewSearchRequest(
|
|
*settings.BaseDN,
|
|
ldapv3.ScopeWholeSubtree,
|
|
ldapv3.NeverDerefAliases,
|
|
0, 0, false,
|
|
filter,
|
|
l.getUserAttributes(),
|
|
nil,
|
|
)
|
|
|
|
sr, err := conn.Search(searchRequest)
|
|
if err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetUser", "api.ldap.search_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
if len(sr.Entries) == 0 {
|
|
return nil, model.NewAppError("LdapInterface.GetUser", "api.ldap.user_not_found.app_error", nil, "", http.StatusNotFound)
|
|
}
|
|
|
|
return l.entryToUser(sr.Entries[0]), nil
|
|
}
|
|
|
|
// GetLDAPUserForMMUser finds the LDAP user corresponding to a Mattermost user
|
|
func (l *LdapImpl) GetLDAPUserForMMUser(rctx request.CTX, mmUser *model.User) (*model.User, string, *model.AppError) {
|
|
if mmUser.AuthService != model.UserAuthServiceLdap || mmUser.AuthData == nil {
|
|
return nil, "", model.NewAppError("LdapInterface.GetLDAPUserForMMUser", "api.ldap.not_ldap_user.app_error", nil, "", http.StatusBadRequest)
|
|
}
|
|
|
|
ldapUser, err := l.GetUser(rctx, *mmUser.AuthData)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
return ldapUser, *mmUser.AuthData, nil
|
|
}
|
|
|
|
// GetUserAttributes retrieves specific attributes for a user
|
|
func (l *LdapImpl) GetUserAttributes(rctx request.CTX, id string, attributes []string) (map[string]string, *model.AppError) {
|
|
settings := l.getSettings()
|
|
|
|
conn, err := l.connect()
|
|
if err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetUserAttributes", "api.ldap.connection_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
defer conn.Close()
|
|
|
|
if err := l.bindAsAdmin(conn); err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetUserAttributes", "api.ldap.bind_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
filter := l.buildUserFilter(id)
|
|
searchRequest := ldapv3.NewSearchRequest(
|
|
*settings.BaseDN,
|
|
ldapv3.ScopeWholeSubtree,
|
|
ldapv3.NeverDerefAliases,
|
|
0, 0, false,
|
|
filter,
|
|
attributes,
|
|
nil,
|
|
)
|
|
|
|
sr, err := conn.Search(searchRequest)
|
|
if err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetUserAttributes", "api.ldap.search_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
if len(sr.Entries) == 0 {
|
|
return nil, model.NewAppError("LdapInterface.GetUserAttributes", "api.ldap.user_not_found.app_error", nil, "", http.StatusNotFound)
|
|
}
|
|
|
|
result := make(map[string]string)
|
|
entry := sr.Entries[0]
|
|
for _, attr := range attributes {
|
|
result[attr] = entry.GetAttributeValue(attr)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// CheckProviderAttributes checks if user attributes from LDAP would change
|
|
func (l *LdapImpl) CheckProviderAttributes(rctx request.CTX, LS *model.LdapSettings, ouser *model.User, patch *model.UserPatch) string {
|
|
// Returns a list of attributes that would be overwritten by LDAP sync
|
|
var conflicts []string
|
|
|
|
if patch.Username != nil && *LS.UsernameAttribute != "" {
|
|
conflicts = append(conflicts, "username")
|
|
}
|
|
if patch.Email != nil && *LS.EmailAttribute != "" {
|
|
conflicts = append(conflicts, "email")
|
|
}
|
|
if patch.FirstName != nil && *LS.FirstNameAttribute != "" {
|
|
conflicts = append(conflicts, "first_name")
|
|
}
|
|
if patch.LastName != nil && *LS.LastNameAttribute != "" {
|
|
conflicts = append(conflicts, "last_name")
|
|
}
|
|
if patch.Nickname != nil && *LS.NicknameAttribute != "" {
|
|
conflicts = append(conflicts, "nickname")
|
|
}
|
|
if patch.Position != nil && *LS.PositionAttribute != "" {
|
|
conflicts = append(conflicts, "position")
|
|
}
|
|
|
|
return strings.Join(conflicts, ", ")
|
|
}
|
|
|
|
// SwitchToLdap switches a user's auth method to LDAP
|
|
func (l *LdapImpl) SwitchToLdap(rctx request.CTX, userID, ldapID, ldapPassword string) *model.AppError {
|
|
// Verify LDAP credentials
|
|
_, err := l.DoLogin(rctx, ldapID, ldapPassword)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// StartSynchronizeJob starts an LDAP sync job
|
|
func (l *LdapImpl) StartSynchronizeJob(rctx request.CTX, waitForJobToFinish bool) (*model.Job, *model.AppError) {
|
|
// Create a job record - actual implementation would need job store
|
|
job := &model.Job{
|
|
Id: model.NewId(),
|
|
Type: model.JobTypeLdapSync,
|
|
CreateAt: model.GetMillis(),
|
|
Status: model.JobStatusPending,
|
|
}
|
|
return job, nil
|
|
}
|
|
|
|
// GetAllLdapUsers retrieves all users from LDAP
|
|
func (l *LdapImpl) GetAllLdapUsers(rctx request.CTX) ([]*model.User, *model.AppError) {
|
|
settings := l.getSettings()
|
|
|
|
conn, err := l.connect()
|
|
if err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetAllLdapUsers", "api.ldap.connection_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
defer conn.Close()
|
|
|
|
if err := l.bindAsAdmin(conn); err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetAllLdapUsers", "api.ldap.bind_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
filter := l.buildAllUsersFilter()
|
|
searchRequest := ldapv3.NewSearchRequest(
|
|
*settings.BaseDN,
|
|
ldapv3.ScopeWholeSubtree,
|
|
ldapv3.NeverDerefAliases,
|
|
0, 0, false,
|
|
filter,
|
|
l.getUserAttributes(),
|
|
nil,
|
|
)
|
|
|
|
sr, err := conn.Search(searchRequest)
|
|
if err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetAllLdapUsers", "api.ldap.search_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
var users []*model.User
|
|
for _, entry := range sr.Entries {
|
|
users = append(users, l.entryToUser(entry))
|
|
}
|
|
|
|
return users, nil
|
|
}
|
|
|
|
// MigrateIDAttribute migrates user ID attribute
|
|
func (l *LdapImpl) MigrateIDAttribute(rctx request.CTX, toAttribute string) error {
|
|
// This would update the ID attribute mapping in the config
|
|
return nil
|
|
}
|
|
|
|
// GetGroup retrieves a group from LDAP
|
|
func (l *LdapImpl) GetGroup(rctx request.CTX, groupUID string) (*model.Group, *model.AppError) {
|
|
settings := l.getSettings()
|
|
|
|
conn, err := l.connect()
|
|
if err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetGroup", "api.ldap.connection_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
defer conn.Close()
|
|
|
|
if err := l.bindAsAdmin(conn); err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetGroup", "api.ldap.bind_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
filter := fmt.Sprintf("(%s=%s)", *settings.GroupIdAttribute, ldapv3.EscapeFilter(groupUID))
|
|
if *settings.GroupFilter != "" {
|
|
filter = fmt.Sprintf("(&%s%s)", *settings.GroupFilter, filter)
|
|
}
|
|
|
|
searchRequest := ldapv3.NewSearchRequest(
|
|
*settings.BaseDN,
|
|
ldapv3.ScopeWholeSubtree,
|
|
ldapv3.NeverDerefAliases,
|
|
0, 0, false,
|
|
filter,
|
|
[]string{*settings.GroupIdAttribute, *settings.GroupDisplayNameAttribute, "member"},
|
|
nil,
|
|
)
|
|
|
|
sr, err := conn.Search(searchRequest)
|
|
if err != nil {
|
|
return nil, model.NewAppError("LdapInterface.GetGroup", "api.ldap.search_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
if len(sr.Entries) == 0 {
|
|
return nil, model.NewAppError("LdapInterface.GetGroup", "api.ldap.group_not_found.app_error", nil, "", http.StatusNotFound)
|
|
}
|
|
|
|
entry := sr.Entries[0]
|
|
group := &model.Group{
|
|
Id: model.NewId(),
|
|
Name: model.NewPointer(entry.GetAttributeValue(*settings.GroupIdAttribute)),
|
|
DisplayName: entry.GetAttributeValue(*settings.GroupDisplayNameAttribute),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(groupUID),
|
|
}
|
|
|
|
return group, nil
|
|
}
|
|
|
|
// GetAllGroupsPage retrieves groups with pagination
|
|
func (l *LdapImpl) GetAllGroupsPage(rctx request.CTX, page int, perPage int, opts model.LdapGroupSearchOpts) ([]*model.Group, int, *model.AppError) {
|
|
settings := l.getSettings()
|
|
|
|
conn, err := l.connect()
|
|
if err != nil {
|
|
return nil, 0, model.NewAppError("LdapInterface.GetAllGroupsPage", "api.ldap.connection_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
defer conn.Close()
|
|
|
|
if err := l.bindAsAdmin(conn); err != nil {
|
|
return nil, 0, model.NewAppError("LdapInterface.GetAllGroupsPage", "api.ldap.bind_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
filter := *settings.GroupFilter
|
|
if filter == "" {
|
|
filter = "(objectClass=group)"
|
|
}
|
|
|
|
if opts.Q != "" {
|
|
filter = fmt.Sprintf("(&%s(%s=*%s*))", filter, *settings.GroupDisplayNameAttribute, ldapv3.EscapeFilter(opts.Q))
|
|
}
|
|
|
|
searchRequest := ldapv3.NewSearchRequest(
|
|
*settings.BaseDN,
|
|
ldapv3.ScopeWholeSubtree,
|
|
ldapv3.NeverDerefAliases,
|
|
0, 0, false,
|
|
filter,
|
|
[]string{*settings.GroupIdAttribute, *settings.GroupDisplayNameAttribute},
|
|
nil,
|
|
)
|
|
|
|
sr, err := conn.Search(searchRequest)
|
|
if err != nil {
|
|
return nil, 0, model.NewAppError("LdapInterface.GetAllGroupsPage", "api.ldap.search_error.app_error", nil, err.Error(), http.StatusInternalServerError)
|
|
}
|
|
|
|
totalCount := len(sr.Entries)
|
|
|
|
// Apply pagination
|
|
start := page * perPage
|
|
end := start + perPage
|
|
if start >= len(sr.Entries) {
|
|
return []*model.Group{}, totalCount, nil
|
|
}
|
|
if end > len(sr.Entries) {
|
|
end = len(sr.Entries)
|
|
}
|
|
|
|
var groups []*model.Group
|
|
for _, entry := range sr.Entries[start:end] {
|
|
groupID := entry.GetAttributeValue(*settings.GroupIdAttribute)
|
|
group := &model.Group{
|
|
Id: model.NewId(),
|
|
Name: model.NewPointer(groupID),
|
|
DisplayName: entry.GetAttributeValue(*settings.GroupDisplayNameAttribute),
|
|
Source: model.GroupSourceLdap,
|
|
RemoteId: model.NewPointer(groupID),
|
|
}
|
|
groups = append(groups, group)
|
|
}
|
|
|
|
return groups, totalCount, nil
|
|
}
|
|
|
|
// FirstLoginSync syncs user data on first login
|
|
func (l *LdapImpl) FirstLoginSync(rctx request.CTX, user *model.User) *model.AppError {
|
|
if user.AuthData == nil {
|
|
return nil
|
|
}
|
|
|
|
ldapUser, err := l.GetUser(rctx, *user.AuthData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update user fields from LDAP
|
|
user.FirstName = ldapUser.FirstName
|
|
user.LastName = ldapUser.LastName
|
|
user.Nickname = ldapUser.Nickname
|
|
user.Position = ldapUser.Position
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpdateProfilePictureIfNecessary updates user profile picture from LDAP
|
|
func (l *LdapImpl) UpdateProfilePictureIfNecessary(rctx request.CTX, user model.User, session model.Session) {
|
|
// This would fetch the picture attribute and update the user's profile picture
|
|
// Implementation depends on file storage backend
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
func (l *LdapImpl) buildUserFilter(id string) string {
|
|
settings := l.getSettings()
|
|
|
|
loginAttr := *settings.LoginIdAttribute
|
|
if loginAttr == "" {
|
|
loginAttr = *settings.UsernameAttribute
|
|
}
|
|
if loginAttr == "" {
|
|
loginAttr = "uid"
|
|
}
|
|
|
|
filter := fmt.Sprintf("(%s=%s)", loginAttr, ldapv3.EscapeFilter(id))
|
|
|
|
if *settings.UserFilter != "" {
|
|
filter = fmt.Sprintf("(&%s%s)", *settings.UserFilter, filter)
|
|
}
|
|
|
|
return filter
|
|
}
|
|
|
|
func (l *LdapImpl) buildAllUsersFilter() string {
|
|
settings := l.getSettings()
|
|
|
|
filter := "(objectClass=person)"
|
|
if *settings.UserFilter != "" {
|
|
filter = *settings.UserFilter
|
|
}
|
|
|
|
return filter
|
|
}
|
|
|
|
func (l *LdapImpl) getUserAttributes() []string {
|
|
settings := l.getSettings()
|
|
|
|
attrs := []string{"dn"}
|
|
|
|
if *settings.IdAttribute != "" {
|
|
attrs = append(attrs, *settings.IdAttribute)
|
|
}
|
|
if *settings.UsernameAttribute != "" {
|
|
attrs = append(attrs, *settings.UsernameAttribute)
|
|
}
|
|
if *settings.EmailAttribute != "" {
|
|
attrs = append(attrs, *settings.EmailAttribute)
|
|
}
|
|
if *settings.FirstNameAttribute != "" {
|
|
attrs = append(attrs, *settings.FirstNameAttribute)
|
|
}
|
|
if *settings.LastNameAttribute != "" {
|
|
attrs = append(attrs, *settings.LastNameAttribute)
|
|
}
|
|
if *settings.NicknameAttribute != "" {
|
|
attrs = append(attrs, *settings.NicknameAttribute)
|
|
}
|
|
if *settings.PositionAttribute != "" {
|
|
attrs = append(attrs, *settings.PositionAttribute)
|
|
}
|
|
if *settings.LoginIdAttribute != "" {
|
|
attrs = append(attrs, *settings.LoginIdAttribute)
|
|
}
|
|
if *settings.PictureAttribute != "" {
|
|
attrs = append(attrs, *settings.PictureAttribute)
|
|
}
|
|
|
|
return attrs
|
|
}
|
|
|
|
func (l *LdapImpl) entryToUser(entry *ldapv3.Entry) *model.User {
|
|
settings := l.getSettings()
|
|
|
|
user := &model.User{
|
|
AuthService: model.UserAuthServiceLdap,
|
|
}
|
|
|
|
if *settings.IdAttribute != "" {
|
|
authData := entry.GetAttributeValue(*settings.IdAttribute)
|
|
user.AuthData = &authData
|
|
}
|
|
|
|
if *settings.UsernameAttribute != "" {
|
|
user.Username = entry.GetAttributeValue(*settings.UsernameAttribute)
|
|
}
|
|
|
|
if *settings.EmailAttribute != "" {
|
|
user.Email = entry.GetAttributeValue(*settings.EmailAttribute)
|
|
}
|
|
|
|
if *settings.FirstNameAttribute != "" {
|
|
user.FirstName = entry.GetAttributeValue(*settings.FirstNameAttribute)
|
|
}
|
|
|
|
if *settings.LastNameAttribute != "" {
|
|
user.LastName = entry.GetAttributeValue(*settings.LastNameAttribute)
|
|
}
|
|
|
|
if *settings.NicknameAttribute != "" {
|
|
user.Nickname = entry.GetAttributeValue(*settings.NicknameAttribute)
|
|
}
|
|
|
|
if *settings.PositionAttribute != "" {
|
|
user.Position = entry.GetAttributeValue(*settings.PositionAttribute)
|
|
}
|
|
|
|
return user
|
|
}
|