diff --git a/go.mod b/go.mod index a3eeb9c00..19bac175b 100644 --- a/go.mod +++ b/go.mod @@ -95,6 +95,7 @@ require ( cloud.google.com/go/monitoring v1.21.2 // indirect cloud.google.com/go/spanner v1.73.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect @@ -115,8 +116,10 @@ require ( github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect github.com/go-chi/chi/v5 v5.1.0 // indirect github.com/go-jose/go-jose/v4 v4.0.2 // indirect + github.com/go-ldap/ldap/v3 v3.4.10 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.20.2 // indirect diff --git a/go.sum b/go.sum index c0434c281..43efe1271 100644 --- a/go.sum +++ b/go.sum @@ -623,6 +623,8 @@ gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4Lxi github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw= github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0= github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= +github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4= @@ -649,6 +651,7 @@ github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= @@ -783,6 +786,8 @@ github.com/getsentry/sentry-go v0.31.1/go.mod h1:CYNcMMz73YigoHljQRG+qPF+eMq8gG7 github.com/getsentry/sentry-go/echo v0.31.1 h1:bGY2QrNq5PovERoQBwyfJtQixjptHC06gLiAlF0WUPc= github.com/getsentry/sentry-go/echo v0.31.1/go.mod h1:2gHa20EVxDNNTJY+Cq4Eqr8A0Z6UEULh4ImSsVMSRUg= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk= +github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= @@ -804,6 +809,8 @@ github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiK github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= +github.com/go-ldap/ldap/v3 v3.4.10 h1:ot/iwPOhfpNVgB1o+AVXljizWZ9JTp7YF5oeyONmcJU= +github.com/go-ldap/ldap/v3 v3.4.10/go.mod h1:JXh4Uxgi40P6E9rdsYqpUtbW46D9UTjJ9QSwGRznplY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= @@ -967,6 +974,8 @@ github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+ github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= @@ -975,6 +984,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -1050,6 +1061,12 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= @@ -1392,6 +1409,7 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1677,6 +1695,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= @@ -1693,6 +1712,7 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/config/config.go b/pkg/config/config.go index a0d54b0c8..4801c3231 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -78,6 +78,16 @@ const ( AuthOpenIDEnabled Key = `auth.openid.enabled` AuthOpenIDProviders Key = `auth.openid.providers` + AuthLdapEnabled Key = `auth.ldap.enabled` + AuthLdapHost Key = `auth.ldap.host` + AuthLdapPort Key = `auth.ldap.port` + AuthLdapBaseDN Key = `auth.ldap.basedn` + AuthLdapUseTLS Key = `auth.ldap.usetls` + AuthLdapVerifyTLS Key = `auth.ldap.verifytls` + AuthLdapBindDN Key = `auth.ldap.binddn` + // #nosec G101 + AuthLdapBindPassword Key = `auth.ldap.bindpassword` + LegalImprintURL Key = `legal.imprinturl` LegalPrivacyURL Key = `legal.privacyurl` @@ -333,6 +343,12 @@ func InitDefaultConfig() { AuthLocalEnabled.setDefault(true) AuthOpenIDEnabled.setDefault(false) + AuthLdapEnabled.setDefault(false) + AuthLdapHost.setDefault("localhost") + AuthLdapPort.setDefault(389) + AuthLdapUseTLS.setDefault(true) + AuthLdapVerifyTLS.setDefault(true) + // Database DatabaseType.setDefault("sqlite") DatabaseHost.setDefault("localhost") diff --git a/pkg/initialize/init.go b/pkg/initialize/init.go index 8b9f1c0d5..998dc7c06 100644 --- a/pkg/initialize/init.go +++ b/pkg/initialize/init.go @@ -27,6 +27,7 @@ import ( "code.vikunja.io/api/pkg/mail" "code.vikunja.io/api/pkg/migration" "code.vikunja.io/api/pkg/models" + "code.vikunja.io/api/pkg/modules/auth/ldap" "code.vikunja.io/api/pkg/modules/auth/openid" "code.vikunja.io/api/pkg/modules/keyvalue" migrationHandler "code.vikunja.io/api/pkg/modules/migration/handler" @@ -79,6 +80,13 @@ func FullInitWithoutAsync() { // Start the mail daemon mail.StartMailDaemon() + + // Connect to ldap if enabled + l, err := ldap.ConnectAndBindToLDAPDirectory() + if err != nil { + log.Fatalf("Could not bind to LDAP server: %s", err) + } + _ = l.Close() } // FullInit initializes all kinds of things in the right order diff --git a/pkg/modules/auth/ldap/ldap.go b/pkg/modules/auth/ldap/ldap.go new file mode 100644 index 000000000..1209f1815 --- /dev/null +++ b/pkg/modules/auth/ldap/ldap.go @@ -0,0 +1,154 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public Licensee as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public Licensee for more details. +// +// You should have received a copy of the GNU Affero General Public Licensee +// along with this program. If not, see . + +package ldap + +import ( + "fmt" + "strings" + + petname "github.com/dustinkirkland/golang-petname" + "xorm.io/xorm" + + "code.vikunja.io/api/pkg/config" + "code.vikunja.io/api/pkg/log" + "code.vikunja.io/api/pkg/models" + "code.vikunja.io/api/pkg/user" + + "github.com/go-ldap/ldap/v3" +) + +func ConnectAndBindToLDAPDirectory() (l *ldap.Conn, err error) { + if !config.AuthLdapEnabled.GetBool() { + return + } + + var protocol = "ldap" + if config.AuthLdapUseTLS.GetBool() { + protocol = "ldaps" + } + url := fmt.Sprintf( + "%s://%s:%d", + protocol, + config.AuthLdapHost.GetString(), + config.AuthLdapPort.GetInt(), + ) + l, err = ldap.DialURL(url) + if err != nil { + log.Fatalf("Could not connect to LDAP server: %s", err) + } + + err = l.Bind( + config.AuthLdapBindDN.GetString(), + config.AuthLdapBindPassword.GetString(), + ) + return +} + +func AuthenticateUserInLDAP(s *xorm.Session, username, password string) (u *user.User, err error) { + if password == "" || username == "" { + return nil, user.ErrNoUsernamePassword{} + } + + l, err := ConnectAndBindToLDAPDirectory() + if err != nil { + log.Errorf("Could not bind to LDAP server: %s", err) + return + } + defer l.Close() + + log.Debugf("Connected to LDAP server") + + searchRequest := ldap.NewSearchRequest( + config.AuthLdapBaseDN.GetString(), + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + fmt.Sprintf("(&(objectClass=inetOrgPerson)(uid=%s))", username), + []string{"dn", "uid", "mail", "displayName"}, + nil, + ) + + sr, err := l.Search(searchRequest) + if err != nil { + return + } + + if len(sr.Entries) > 1 || len(sr.Entries) == 0 { + log.Debugf("Found %d entries for username %s", len(sr.Entries), username) + return nil, user.ErrWrongUsernameOrPassword{} + } + + userdn := sr.Entries[0].DN + + // Bind as the user to verify their password + err = l.Bind(userdn, password) + if err != nil { + return + } + + return getOrCreateLdapUser(s, sr.Entries[0]) +} + +func getOrCreateLdapUser(s *xorm.Session, entry *ldap.Entry) (u *user.User, err error) { + username := entry.GetAttributeValue("uid") + email := entry.GetAttributeValue("mail") + name := entry.GetAttributeValue("displayName") + + u, err = user.GetUserWithEmail(s, &user.User{ + Issuer: user.IssuerLDAP, + Subject: username, + }) + if err != nil && !user.IsErrUserDoesNotExist(err) { + return nil, err + } + + // If no user exists, create one with the preferred username if it is not already taken + if user.IsErrUserDoesNotExist(err) { + uu := &user.User{ + Username: strings.ReplaceAll(username, " ", "-"), + Email: email, + Name: name, + Status: user.StatusActive, + Issuer: user.IssuerLDAP, + Subject: username, + } + + // Check if we actually have a preferred username and generate a random one right away if we don't + if uu.Username == "" { + uu.Username = petname.Generate(3, "-") + } + + // TODO abstract this away an use in openid auth and here + u, err = user.CreateUser(s, uu) + if err != nil && !user.IsErrUsernameExists(err) { + return nil, err + } + + // If their preferred username is already taken, generate a random one + if user.IsErrUsernameExists(err) { + uu.Username = petname.Generate(3, "-") + u, err = user.CreateUser(s, uu) + if err != nil { + return nil, err + } + } + + // And create their project + err = models.CreateNewProjectForUser(s, u) + return + } + + return +} diff --git a/pkg/routes/api/v1/login.go b/pkg/routes/api/v1/login.go index fdc7be0b9..c00ac25a0 100644 --- a/pkg/routes/api/v1/login.go +++ b/pkg/routes/api/v1/login.go @@ -19,6 +19,9 @@ package v1 import ( "net/http" + "code.vikunja.io/api/pkg/config" + "code.vikunja.io/api/pkg/modules/auth/ldap" + "code.vikunja.io/api/pkg/modules/keyvalue" "code.vikunja.io/api/pkg/db" @@ -43,7 +46,7 @@ import ( // @Failure 412 {object} models.Message "Invalid totp passcode." // @Failure 403 {object} models.Message "Invalid username or password." // @Router /login [post] -func Login(c echo.Context) error { +func Login(c echo.Context) (err error) { u := user2.Login{} if err := c.Bind(&u); err != nil { return c.JSON(http.StatusBadRequest, models.Message{Message: "Please provide a username and password."}) @@ -52,8 +55,12 @@ func Login(c echo.Context) error { s := db.NewSession() defer s.Close() - // Check user - user, err := user2.CheckUserCredentials(s, &u) + var user *user2.User + if config.AuthLdapEnabled.GetBool() { + user, err = ldap.AuthenticateUserInLDAP(s, u.Username, u.Password) + } else { + user, err = user2.CheckUserCredentials(s, &u) + } if err != nil { _ = s.Rollback() return handler.HandleHTTPError(err) diff --git a/pkg/routes/routes.go b/pkg/routes/routes.go index 173cafe6c..3cfe46cc4 100644 --- a/pkg/routes/routes.go +++ b/pkg/routes/routes.go @@ -246,14 +246,16 @@ func registerAPIRoutes(a *echo.Group) { ur.Use(RateLimit(rateLimiter, "ip")) if config.AuthLocalEnabled.GetBool() { - // User stuff - ur.POST("/login", apiv1.Login) ur.POST("/register", apiv1.RegisterUser) ur.POST("/user/password/token", apiv1.UserRequestResetPasswordToken) ur.POST("/user/password/reset", apiv1.UserResetPassword) ur.POST("/user/confirm", apiv1.UserConfirmEmail) } + if config.AuthLdapEnabled.GetBool() { + ur.POST("/login", apiv1.Login) + } + if config.AuthOpenIDEnabled.GetBool() { ur.POST("/auth/openid/:provider/callback", openid.HandleCallback) } diff --git a/pkg/user/user_create.go b/pkg/user/user_create.go index eddd2135b..eec8044d6 100644 --- a/pkg/user/user_create.go +++ b/pkg/user/user_create.go @@ -26,7 +26,10 @@ import ( "xorm.io/xorm" ) -const IssuerLocal = `local` +const ( + IssuerLocal = `local` + IssuerLDAP = `ldap` +) // CreateUser creates a new user and inserts it into the database func CreateUser(s *xorm.Session, user *User) (newUser *User, err error) {