Skip to content

Commit

Permalink
Have LDAP load a config, instead of reading from db
Browse files Browse the repository at this point in the history
  • Loading branch information
betapictoris committed Apr 7, 2024
1 parent 995c566 commit d63bd2f
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 65 deletions.
26 changes: 13 additions & 13 deletions cmd/gonic/gonic.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"go.senan.xyz/gonic/infocache/artistinfocache"
"go.senan.xyz/gonic/jukebox"
"go.senan.xyz/gonic/lastfm"
"go.senan.xyz/gonic/ldap"
"go.senan.xyz/gonic/listenbrainz"
"go.senan.xyz/gonic/playlist"
"go.senan.xyz/gonic/podcast"
Expand All @@ -47,7 +48,6 @@ import (
"go.senan.xyz/gonic/tags/tagcommon"
"go.senan.xyz/gonic/tags/taglib"
"go.senan.xyz/gonic/transcode"
"go.senan.xyz/gonic/ldap"
)

func main() {
Expand Down Expand Up @@ -83,21 +83,21 @@ func main() {

confExcludePattern := flag.String("exclude-pattern", "", "regex pattern to exclude files from scan (optional)")

var ldapConfig ldap.Config
flag.StringVar(&ldapConfig.BindUser, "ldap-bind-user", "", "the bind user to bind to LDAP with (required for LDAP)")
var ldapConfig ldap.Config
flag.StringVar(&ldapConfig.BindUser, "ldap-bind-user", "", "the bind user to bind to LDAP with (required for LDAP)")
flag.StringVar(&ldapConfig.BindPass, "ldap-bind-pass", "", "the password of the LDAP bind user (required for LDAP)")
flag.StringVar(&ldapConfig.BaseDN, "ldap-base-dn", "", "the base DN for LDAP objects (required for LDAP)")
flag.StringVar(&ldapConfig.Filter, "ldap-filter", "(uid=*)", "the filter to select LDAP objects with (optional)")
flag.StringVar(&ldapConfig.Filter, "ldap-filter", "(uid=*)", "the filter to select LDAP objects with (optional)")

flag.StringVar(&ldapConfig.FQDN, "ldap-fqdn", "", "the name of the server to connect to (required for LDAP)")
flag.UintVar(&ldapConfig.Port, "ldap-port", 389, "what port the LDAP server is hosted on (optional)")
flag.BoolVar(&ldapConfig.TLS, "ldap-tls", false, "whether gonic will connect to the LDAP server using TLS (optional)")
flag.BoolVar(&ldapConfig.TLS, "ldap-tls", false, "whether gonic will connect to the LDAP server using TLS (optional)")

if ldapConfig.FQDN != "" {
if ldapConfig.BindUser == "" || ldapConfig.BindPass == "" || ldapConfig.BaseDN == "" {
log.Fatal("a server was provided for an LDAP connection, but configuration is incomplete")
}
}
if ldapConfig.FQDN != "" {
if ldapConfig.BindUser == "" || ldapConfig.BindPass == "" || ldapConfig.BaseDN == "" {
log.Fatal("a server was provided for an LDAP connection, but configuration is incomplete")
}
}

var confMultiValueGenre, confMultiValueArtist, confMultiValueAlbumArtist multiValueSetting
flag.Var(&confMultiValueGenre, "multi-value-genre", "setting for mutli-valued genre scanning (optional)")
Expand All @@ -122,7 +122,7 @@ func main() {
log.Fatalf("invalid exclude pattern: %v\n", err)
}

if len(confMusicPaths) == 0 {
if len(confMusicPaths) == -1 {
log.Fatalf("please provide a music directory")
}

Expand Down Expand Up @@ -264,11 +264,11 @@ func main() {
return path.Join(*confProxyPrefix, in)
}

ctrlAdmin, err := ctrladmin.New(dbc, sessDB, scannr, podcast, lastfmClient, resolveProxyPath)
ctrlAdmin, err := ctrladmin.New(dbc, sessDB, scannr, podcast, lastfmClient, resolveProxyPath, ldapConfig)
if err != nil {
log.Panicf("error creating admin controller: %v\n", err)
}
ctrlSubsonic, err := ctrlsubsonic.New(dbc, scannr, musicPaths, *confPodcastPath, cacheDirAudio, cacheDirCovers, jukebx, playlistStore, scrobblers, podcast, transcoder, lastfmClient, artistInfoCache, albumInfoCache, resolveProxyPath)
ctrlSubsonic, err := ctrlsubsonic.New(dbc, scannr, musicPaths, *confPodcastPath, cacheDirAudio, cacheDirCovers, jukebx, playlistStore, scrobblers, podcast, transcoder, lastfmClient, artistInfoCache, albumInfoCache, resolveProxyPath, ldapConfig)
if err != nil {
log.Panicf("error creating subsonic controller: %v\n", err)
}
Expand Down
Binary file renamed gonic.db.1711991474.bak → gonic.db.1712509531.bak
Binary file not shown.
76 changes: 30 additions & 46 deletions ldap/ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,29 @@ import (
)

type Config struct {
BindUser string
BindPass string
BaseDN string
Filter string

FQDN string
Port uint
TLS bool
BindUser string
BindPass string
BaseDN string
Filter string

FQDN string
Port uint
TLS bool
}

func CheckLDAPcreds(username string, password string, dbc *db.DB) (bool, error) {
ldapFQDN, err := dbc.GetSetting("ldap_fqdn")
func (c Config) IsSetup() bool {
// This is basically checking if LDAP is setup, if ldapFQDN isn't set we can
// assume that the user hasn't configured LDAP.
return c.FQDN != ""
}

// This checks if LDAP is setup or not.
if ldapFQDN == "" || err != nil {
return false, err
func CheckLDAPcreds(username string, password string, dbc *db.DB, config Config) (bool, error) {
if !config.IsSetup() {
return false, nil
}

// The configuration page wouldn't allow these setting to not be set
// while LDAP is enabled (a FQDN/IP is set).
ldapPort, _ := dbc.GetSetting("ldap_port")
baseDN, _ := dbc.GetSetting("ldap_base_dn")
tls, _ := dbc.GetSetting("ldap_tls")

// Now, we can try to connect to the LDAP server.
l, err := createLDAPconnection(tls, ldapFQDN, ldapPort)
l, err := createLDAPconnection(config)
if err != nil {
// Return a generic error.
log.Println("Failed to connect to LDAP server:", err)
Expand All @@ -45,14 +42,14 @@ func CheckLDAPcreds(username string, password string, dbc *db.DB) (bool, error)
defer l.Close()

// Create the user if it doesn't exist on the database already.
err = createUserFromLDAP(username, dbc, l)
err = createUserFromLDAP(username, dbc, config, l)
if err != nil {
return false, err
}

// After we have a connection, let's try binding
_, err = l.SimpleBind(&ldap.SimpleBindRequest{
Username: fmt.Sprintf("uid=%s,%s", username, baseDN),
Username: fmt.Sprintf("uid=%s,%s", username, config.BaseDN),
Password: password,
})

Expand All @@ -65,43 +62,30 @@ func CheckLDAPcreds(username string, password string, dbc *db.DB) (bool, error)
}

// Creates a user from creds
func createUserFromLDAP(username string, dbc *db.DB, l *ldap.Conn) error {
func createUserFromLDAP(username string, dbc *db.DB, config Config, l *ldap.Conn) error {
user := dbc.GetUserByName(username)
if user != nil {
return nil
}

ldapFQDN, err := dbc.GetSetting("ldap_fqdn")

// This is basically checking if LDAP is setup, if ldapFQDN isn't set we can
// assume that the user hasn't configured LDAP.
if ldapFQDN == "" {
if !config.IsSetup() {
return nil
} else if err != nil {
return err
}

// The configuration page wouldn't allow these setting to not be set
// while LDAP is enabled (a FQDN/IP is set).
bindUID, _ := dbc.GetSetting("ldap_bind_user")
bindPWD, _ := dbc.GetSetting("ldap_bind_user_password")
baseDN, _ := dbc.GetSetting("ldap_base_dn")
filter, _ := dbc.GetSetting("ldap_filter")

// After we have a connection, let's try binding
_, err = l.SimpleBind(&ldap.SimpleBindRequest{
Username: fmt.Sprintf("uid=%s,%s", bindUID, baseDN),
Password: bindPWD,
_, err := l.SimpleBind(&ldap.SimpleBindRequest{
Username: fmt.Sprintf("uid=%s,%s", config.BindUser, config.BaseDN),
Password: config.BindPass,
})
if err != nil {
log.Println("Failed to bind to LDAP:", err)
return errors.New("wrong username or password")
}

searchReq := ldap.NewSearchRequest(
baseDN,
config.BaseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&%s(uid=%s))", filter, ldap.EscapeFilter(username)),
fmt.Sprintf("(&%s(uid=%s))", config.Filter, ldap.EscapeFilter(username)),
[]string{"dn"},
nil,
)
Expand Down Expand Up @@ -131,16 +115,16 @@ func createUserFromLDAP(username string, dbc *db.DB, l *ldap.Conn) error {
}

// Creates a connection to an LDAP server.
func createLDAPconnection(tls string, fqdn string, port string) (*ldap.Conn, error) {
func createLDAPconnection(config Config) (*ldap.Conn, error) {
protocol := "ldap"
if tls == "true" {
if config.TLS {
protocol = "ldaps"
}

// Now, we can try to connect to the LDAP server.
l, err := ldap.DialURL(fmt.Sprintf("%s://%s:%s", protocol, fqdn, port))
l, err := ldap.DialURL(fmt.Sprintf("%s://%s:%d", protocol, config.FQDN, config.Port))
if err != nil {
// Warn the server and return a generic error.
// Warn the server and return the error.
log.Println("Failed to connect to LDAP server", err)
return nil, err
}
Expand Down
5 changes: 4 additions & 1 deletion server/ctrladmin/ctrl.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"go.senan.xyz/gonic/db"
"go.senan.xyz/gonic/handlerutil"
"go.senan.xyz/gonic/lastfm"
"go.senan.xyz/gonic/ldap"
"go.senan.xyz/gonic/podcast"
"go.senan.xyz/gonic/scanner"
"go.senan.xyz/gonic/server/ctrladmin/adminui"
Expand All @@ -48,11 +49,12 @@ type Controller struct {
podcasts *podcast.Podcasts
lastfmClient *lastfm.Client
resolveProxyPath ProxyPathResolver
ldapConfig ldap.Config
}

type ProxyPathResolver func(in string) string

func New(dbc *db.DB, sessDB *gormstore.Store, scanner *scanner.Scanner, podcasts *podcast.Podcasts, lastfmClient *lastfm.Client, resolveProxyPath ProxyPathResolver) (*Controller, error) {
func New(dbc *db.DB, sessDB *gormstore.Store, scanner *scanner.Scanner, podcasts *podcast.Podcasts, lastfmClient *lastfm.Client, resolveProxyPath ProxyPathResolver, ldapConfig ldap.Config) (*Controller, error) {
c := Controller{
ServeMux: http.NewServeMux(),

Expand All @@ -62,6 +64,7 @@ func New(dbc *db.DB, sessDB *gormstore.Store, scanner *scanner.Scanner, podcasts
podcasts: podcasts,
lastfmClient: lastfmClient,
resolveProxyPath: resolveProxyPath,
ldapConfig: ldapConfig,
}

resp := respHandler(adminui.TemplatesFS, resolveProxyPath)
Expand Down
2 changes: 1 addition & 1 deletion server/ctrladmin/handlers_raw.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (c *Controller) ServeLoginDo(w http.ResponseWriter, r *http.Request) {
if user == nil || password != user.Password {
// Because internal authentication failed, we can now try to use LDAP, if
// it was enabled by the user.
ok, err := ldap.CheckLDAPcreds(username, password, c.dbc)
ok, err := ldap.CheckLDAPcreds(username, password, c.dbc, c.ldapConfig)
if err != nil {
sessAddFlashW(session, []string{err.Error()})
} else if !ok {
Expand Down
8 changes: 4 additions & 4 deletions server/ctrlsubsonic/ctrl.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ type Controller struct {
resolveProxyPath ProxyPathResolver
}

func New(dbc *db.DB, scannr *scanner.Scanner, musicPaths []MusicPath, podcastsPath string, cacheAudioPath string, cacheCoverPath string, jukebox *jukebox.Jukebox, playlistStore *playlist.Store, scrobblers []scrobble.Scrobbler, podcasts *podcast.Podcasts, transcoder transcode.Transcoder, lastFMClient *lastfm.Client, artistInfoCache *artistinfocache.ArtistInfoCache, albumInfoCache *albuminfocache.AlbumInfoCache, resolveProxyPath ProxyPathResolver) (*Controller, error) {
func New(dbc *db.DB, scannr *scanner.Scanner, musicPaths []MusicPath, podcastsPath string, cacheAudioPath string, cacheCoverPath string, jukebox *jukebox.Jukebox, playlistStore *playlist.Store, scrobblers []scrobble.Scrobbler, podcasts *podcast.Podcasts, transcoder transcode.Transcoder, lastFMClient *lastfm.Client, artistInfoCache *artistinfocache.ArtistInfoCache, albumInfoCache *albuminfocache.AlbumInfoCache, resolveProxyPath ProxyPathResolver, ldapConfig ldap.Config) (*Controller, error) {
c := Controller{
ServeMux: http.NewServeMux(),

Expand All @@ -94,7 +94,7 @@ func New(dbc *db.DB, scannr *scanner.Scanner, musicPaths []MusicPath, podcastsPa
chain := handlerutil.Chain(
withParams,
withRequiredParams,
withUser(dbc),
withUser(dbc, ldapConfig),
)
chainRaw := handlerutil.Chain(
chain,
Expand Down Expand Up @@ -224,7 +224,7 @@ func withRequiredParams(next http.Handler) http.Handler {
})
}

func withUser(dbc *db.DB) handlerutil.Middleware {
func withUser(dbc *db.DB, ldapConfig ldap.Config) handlerutil.Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
params := r.Context().Value(CtxParams).(params.Params)
Expand Down Expand Up @@ -252,7 +252,7 @@ func withUser(dbc *db.DB) handlerutil.Middleware {
if !credsOk {
// Because internal authentication failed, we can now try to use LDAP,
// if it was enabled by the user.
ok, err := ldap.CheckLDAPcreds(username, password, dbc)
ok, err := ldap.CheckLDAPcreds(username, password, dbc, ldapConfig)
if err != nil {
_ = writeResp(w, r, spec.NewError(40, err.Error()))
return
Expand Down

0 comments on commit d63bd2f

Please sign in to comment.