Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple ldap auth #3674

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
104 changes: 93 additions & 11 deletions internal/entity/auth_session_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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()

Expand Down Expand Up @@ -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.
Expand Down