From 1b9b9c89d619882ae4338e75c6fa5adcbcbe5e08 Mon Sep 17 00:00:00 2001 From: Boris Rybalkin Date: Mon, 27 May 2024 21:57:20 +0100 Subject: [PATCH] simple ldap auth --- internal/entity/auth_session_login.go | 104 +++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 11 deletions(-) diff --git a/internal/entity/auth_session_login.go b/internal/entity/auth_session_login.go index d94b50cb7c9..08a326daeda 100644 --- a/internal/entity/auth_session_login.go +++ b/internal/entity/auth_session_login.go @@ -2,7 +2,11 @@ package entity import ( "fmt" + "github.com/go-ldap/ldap/v3" + "github.com/jinzhu/gorm" "net/http" + "os" + "strings" "time" "github.com/gin-gonic/gin" @@ -20,19 +24,30 @@ import ( // Auth checks if the credentials are valid and returns the user and authentication provider. var Auth = func(f form.Login, s *Session, c *gin.Context) (user *User, provider authn.ProviderType, method authn.MethodType, err error) { - // Get sanitized username from login form. - nameName := f.CleanUsername() - - // Find registered user account. - user = FindUserByName(nameName) - - // Try local authentication. - provider, method, err = AuthLocal(user, f, s, c) + username := f.CleanUsername() + provider = authn.ProviderNone + method = authn.MethodDefault + if os.Getenv("PHOTOPRISM_LDAP_ENABLED") == "true" { + provider = authn.ProviderLDAP + isAdmin, err := AuthLdap(username, f.Password) + if err != nil { + return nil, provider, method, err + } + user = FindUserByName(username) + if user == nil { + user, err = CreateUser(username, isAdmin) + if err != nil { + return nil, provider, method, err + } + } + } else { + user = FindUserByName(username) + provider, method, err = AuthLocal(user, f, s, c) + if err != nil { + return user, provider, method, err + } - if err != nil { - return user, provider, method, err } - // Update login timestamp. user.UpdateLoginTime() @@ -68,6 +83,73 @@ func AuthSession(f form.Login, c *gin.Context) (sess *Session, user *User, err e return sess, sess.User(), nil } +func CreateUser(username string, isAdmin bool) (*User, error) { + user := NewUser() + user.UserName = username + user.SuperAdmin = isAdmin + user.CanLogin = true + user.WebDAV = true + user.CanInvite = true + if isAdmin { + user.UserRole = acl.RoleAdmin.String() + } else { + user.UserRole = os.Getenv("PHOTOPRISM_LDAP_DEFAULT_USER_ROLE") + } + err := Db().Transaction(func(tx *gorm.DB) error { + if err := tx.Create(user).Error; err != nil { + return err + } + log.Infof("successfully added user %s", clean.LogQuote(user.Username())) + return nil + }) + if err != nil { + log.Errorln("user save error", err) + return nil, err + } + return user, nil +} + +func AuthLdap(username, password string) (bool, error) { + conn, err := ldap.DialURL(os.Getenv("PHOTOPRISM_LDAP_URI")) + if err != nil { + log.Errorln("ldap dial error", err) + return false, i18n.Error(i18n.ErrInvalidCredentials) + } + defer conn.Close() + + bindDn := strings.ReplaceAll(os.Getenv("PHOTOPRISM_LDAP_BIND_DN"), "{username}", username) + err = conn.Bind(bindDn, password) + if err != nil { + log.Errorln("ldap bind error", err) + return false, i18n.Error(i18n.ErrInvalidCredentials) + } + isAdmin, err := isLdapAdmin(conn, username) + if err != nil { + return false, i18n.Error(i18n.ErrInvalidCredentials) + } + return isAdmin, nil +} + +func isLdapAdmin(conn *ldap.Conn, username string) (bool, error) { + searchRequest := ldap.NewSearchRequest( + os.Getenv("PHOTOPRISM_LDAP_ADMIN_GROUP_DN"), + ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false, + strings.ReplaceAll(os.Getenv("PHOTOPRISM_LDAP_ADMIN_GROUP_FILTER"), "{username}", username), + []string{os.Getenv("PHOTOPRISM_LDAP_ADMIN_GROUP_ATTRIBUTE")}, + nil) + + sr, err := conn.Search(searchRequest) + if err != nil { + log.Errorln("admin search error", err) + return false, err + } + + if len(sr.Entries) < 1 { + return false, nil + } + return true, nil +} + // AuthLocal authenticates against the local user database with the specified username and password. func AuthLocal(user *User, f form.Login, s *Session, c *gin.Context) (provider authn.ProviderType, method authn.MethodType, err error) { // Set defaults.