diff --git a/clientapi/auth/login.go b/clientapi/auth/login.go index 5467e814d..fb4903355 100644 --- a/clientapi/auth/login.go +++ b/clientapi/auth/login.go @@ -57,8 +57,8 @@ func LoginFromJSONReader(ctx context.Context, r io.Reader, useraccountAPI uapi.U switch header.Type { case authtypes.LoginTypePassword: typ = &LoginTypePassword{ - GetAccountByPassword: useraccountAPI.QueryAccountByPassword, - Config: cfg, + UserAPI: useraccountAPI, + Config: cfg, } case authtypes.LoginTypeToken: typ = &LoginTypeToken{ diff --git a/clientapi/auth/password.go b/clientapi/auth/password.go index f2b0383ab..48a09e4bf 100644 --- a/clientapi/auth/password.go +++ b/clientapi/auth/password.go @@ -16,6 +16,10 @@ package auth import ( "context" + "database/sql" + "github.com/go-ldap/ldap/v3" + "github.com/google/uuid" + "github.com/matrix-org/gomatrixserverlib" "net/http" "strings" @@ -28,8 +32,6 @@ import ( "github.com/matrix-org/util" ) -type GetAccountByPassword func(ctx context.Context, req *api.QueryAccountByPasswordRequest, res *api.QueryAccountByPasswordResponse) error - type PasswordRequest struct { Login Password string `json:"password"` @@ -37,8 +39,8 @@ type PasswordRequest struct { // LoginTypePassword implements https://matrix.org/docs/spec/client_server/r0.6.1#password-based type LoginTypePassword struct { - GetAccountByPassword GetAccountByPassword - Config *config.ClientAPI + Config *config.ClientAPI + UserAPI api.UserLoginAPI } func (t *LoginTypePassword) Name() string { @@ -59,22 +61,21 @@ func (t *LoginTypePassword) LoginFromJSON(ctx context.Context, reqBytes []byte) return login, func(context.Context, *util.JSONResponse) {}, nil } -func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login, *util.JSONResponse) { - r := req.(*PasswordRequest) - username := r.Username() - if username == "" { +func (t *LoginTypePassword) Login(ctx context.Context, request *PasswordRequest) (*Login, *util.JSONResponse) { + fullUsername := request.Username() + if fullUsername == "" { return nil, &util.JSONResponse{ Code: http.StatusUnauthorized, JSON: jsonerror.BadJSON("A username must be supplied."), } } - if len(r.Password) == 0 { + if len(request.Password) == 0 { return nil, &util.JSONResponse{ Code: http.StatusUnauthorized, JSON: jsonerror.BadJSON("A password must be supplied."), } } - localpart, domain, err := userutil.ParseUsernameParam(username, t.Config.Matrix) + username, domain, err := userutil.ParseUsernameParam(fullUsername, t.Config.Matrix) if err != nil { return nil, &util.JSONResponse{ Code: http.StatusUnauthorized, @@ -87,12 +88,38 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login, JSON: jsonerror.InvalidUsername("The server name is not known."), } } - // Squash username to all lowercase letters + + var account *api.Account + if t.Config.Ldap.Enabled { + isAdmin, err := t.authenticateLdap(username, request.Password) + if err != nil { + return nil, err + } + acc, err := t.getOrCreateAccount(ctx, username, domain, isAdmin) + if err != nil { + return nil, err + } + account = acc + } else { + acc, err := t.authenticateDb(ctx, username, domain, request.Password) + if err != nil { + return nil, err + } + account = acc + } + + // Set the user, so login.Username() can do the right thing + request.Identifier.User = account.UserID + request.User = account.UserID + return &request.Login, nil +} + +func (t *LoginTypePassword) authenticateDb(ctx context.Context, username string, domain gomatrixserverlib.ServerName, password string) (*api.Account, *util.JSONResponse) { res := &api.QueryAccountByPasswordResponse{} - err = t.GetAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{ - Localpart: strings.ToLower(localpart), + err := t.UserAPI.QueryAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{ + Localpart: strings.ToLower(username), ServerName: domain, - PlaintextPassword: r.Password, + PlaintextPassword: password, }, res) if err != nil { return nil, &util.JSONResponse{ @@ -101,13 +128,11 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login, } } - // If we couldn't find the user by the lower cased localpart, try the provided - // localpart as is. if !res.Exists { - err = t.GetAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{ - Localpart: localpart, + err = t.UserAPI.QueryAccountByPassword(ctx, &api.QueryAccountByPasswordRequest{ + Localpart: username, ServerName: domain, - PlaintextPassword: r.Password, + PlaintextPassword: password, }, res) if err != nil { return nil, &util.JSONResponse{ @@ -115,8 +140,6 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login, JSON: jsonerror.Unknown("Unable to fetch account by password."), } } - // Technically we could tell them if the user does not exist by checking if err == sql.ErrNoRows - // but that would leak the existence of the user. if !res.Exists { return nil, &util.JSONResponse{ Code: http.StatusForbidden, @@ -124,8 +147,141 @@ func (t *LoginTypePassword) Login(ctx context.Context, req interface{}) (*Login, } } } - // Set the user, so login.Username() can do the right thing - r.Identifier.User = res.Account.UserID - r.User = res.Account.UserID - return &r.Login, nil + return res.Account, nil +} +func (t *LoginTypePassword) authenticateLdap(username, password string) (bool, *util.JSONResponse) { + var conn *ldap.Conn + conn, err := ldap.DialURL(t.Config.Ldap.Uri) + if err != nil { + return false, &util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: jsonerror.Unknown("unable to connect to ldap: " + err.Error()), + } + } + defer conn.Close() + + if t.Config.Ldap.AdminBindEnabled { + err = conn.Bind(t.Config.Ldap.AdminBindDn, t.Config.Ldap.AdminBindPassword) + if err != nil { + return false, &util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: jsonerror.Unknown("unable to bind to ldap: " + err.Error()), + } + } + filter := strings.ReplaceAll(t.Config.Ldap.SearchFilter, "{username}", username) + searchRequest := ldap.NewSearchRequest( + t.Config.Ldap.BaseDn, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, + 0, 0, false, filter, []string{t.Config.Ldap.SearchAttribute}, nil, + ) + result, err := conn.Search(searchRequest) + if err != nil { + return false, &util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: jsonerror.Unknown("unable to bind to search ldap: " + err.Error()), + } + } + if len(result.Entries) > 1 { + return false, &util.JSONResponse{ + Code: http.StatusUnauthorized, + JSON: jsonerror.BadJSON("'user' must be duplicated."), + } + } + if len(result.Entries) < 1 { + return false, &util.JSONResponse{ + Code: http.StatusUnauthorized, + JSON: jsonerror.BadJSON("'user' not found."), + } + } + + userDN := result.Entries[0].DN + err = conn.Bind(userDN, password) + if err != nil { + return false, &util.JSONResponse{ + Code: http.StatusUnauthorized, + JSON: jsonerror.InvalidUsername(err.Error()), + } + } + } else { + bindDn := strings.ReplaceAll(t.Config.Ldap.UserBindDn, "{username}", username) + err = conn.Bind(bindDn, password) + if err != nil { + return false, &util.JSONResponse{ + Code: http.StatusUnauthorized, + JSON: jsonerror.InvalidUsername(err.Error()), + } + } + } + + isAdmin, err := t.isLdapAdmin(conn, username) + if err != nil { + return false, &util.JSONResponse{ + Code: http.StatusUnauthorized, + JSON: jsonerror.InvalidUsername(err.Error()), + } + } + return isAdmin, nil +} + +func (t *LoginTypePassword) isLdapAdmin(conn *ldap.Conn, username string) (bool, error) { + searchRequest := ldap.NewSearchRequest( + t.Config.Ldap.AdminGroupDn, + ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false, + strings.ReplaceAll(t.Config.Ldap.AdminGroupFilter, "{username}", username), + []string{t.Config.Ldap.AdminGroupAttribute}, + nil) + + sr, err := conn.Search(searchRequest) + if err != nil { + return false, err + } + + if len(sr.Entries) < 1 { + return false, nil + } + return true, nil +} + +func (t *LoginTypePassword) getOrCreateAccount(ctx context.Context, username string, domain gomatrixserverlib.ServerName, admin bool) (*api.Account, *util.JSONResponse) { + var existing api.QueryAccountByLocalpartResponse + err := t.UserAPI.QueryAccountByLocalpart(ctx, &api.QueryAccountByLocalpartRequest{ + Localpart: username, + ServerName: domain, + }, &existing) + + if err == nil { + return existing.Account, nil + } + if err != sql.ErrNoRows { + return nil, &util.JSONResponse{ + Code: http.StatusUnauthorized, + JSON: jsonerror.InvalidUsername(err.Error()), + } + } + + accountType := api.AccountTypeUser + if admin { + accountType = api.AccountTypeAdmin + } + var created api.PerformAccountCreationResponse + err = t.UserAPI.PerformAccountCreation(ctx, &api.PerformAccountCreationRequest{ + AppServiceID: "ldap", + Localpart: username, + Password: uuid.New().String(), + AccountType: accountType, + OnConflict: api.ConflictAbort, + }, &created) + + if err != nil { + if _, ok := err.(*api.ErrorConflict); ok { + return nil, &util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.UserInUse("Desired user ID is already taken."), + } + } + return nil, &util.JSONResponse{ + Code: http.StatusInternalServerError, + JSON: jsonerror.Unknown("failed to create account: " + err.Error()), + } + } + return created.Account, nil } diff --git a/clientapi/auth/user_interactive.go b/clientapi/auth/user_interactive.go index 9971bf8a4..54a169376 100644 --- a/clientapi/auth/user_interactive.go +++ b/clientapi/auth/user_interactive.go @@ -113,8 +113,8 @@ type UserInteractive struct { func NewUserInteractive(userAccountAPI api.UserLoginAPI, cfg *config.ClientAPI) *UserInteractive { typePassword := &LoginTypePassword{ - GetAccountByPassword: userAccountAPI.QueryAccountByPassword, - Config: cfg, + UserAPI: userAccountAPI, + Config: cfg, } return &UserInteractive{ Flows: []userInteractiveFlow{ @@ -140,7 +140,7 @@ func (u *UserInteractive) IsSingleStageFlow(authType string) bool { return false } -func (u *UserInteractive) AddCompletedStage(sessionID, authType string) { +func (u *UserInteractive) AddCompletedStage(sessionID, _ string) { u.Lock() // TODO: Handle multi-stage flows delete(u.Sessions, sessionID) @@ -214,7 +214,7 @@ func (u *UserInteractive) ResponseWithChallenge(sessionID string, response inter // Verify returns an error/challenge response to send to the client, or nil if the user is authenticated. // `bodyBytes` is the HTTP request body which must contain an `auth` key. // Returns the login that was verified for additional checks if required. -func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, device *api.Device) (*Login, *util.JSONResponse) { +func (u *UserInteractive) Verify(ctx context.Context, bodyBytes []byte, _ *api.Device) (*Login, *util.JSONResponse) { // TODO: rate limit // "A client should first make a request with no auth parameter. The homeserver returns an HTTP 401 response, with a JSON body" diff --git a/clientapi/routing/key_crosssigning.go b/clientapi/routing/key_crosssigning.go index 267ba1dc5..31655be41 100644 --- a/clientapi/routing/key_crosssigning.go +++ b/clientapi/routing/key_crosssigning.go @@ -32,7 +32,7 @@ type crossSigningRequest struct { } func UploadCrossSigningDeviceKeys( - req *http.Request, userInteractiveAuth *auth.UserInteractive, + req *http.Request, keyserverAPI api.ClientKeyAPI, device *api.Device, accountAPI api.ClientUserAPI, cfg *config.ClientAPI, ) util.JSONResponse { @@ -62,8 +62,8 @@ func UploadCrossSigningDeviceKeys( } } typePassword := auth.LoginTypePassword{ - GetAccountByPassword: accountAPI.QueryAccountByPassword, - Config: cfg, + UserAPI: accountAPI, + Config: cfg, } if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil { return *authErr diff --git a/clientapi/routing/password.go b/clientapi/routing/password.go index f7f9da622..0eb95bde7 100644 --- a/clientapi/routing/password.go +++ b/clientapi/routing/password.go @@ -73,8 +73,8 @@ func Password( // Check if the existing password is correct. typePassword := auth.LoginTypePassword{ - GetAccountByPassword: userAPI.QueryAccountByPassword, - Config: cfg, + UserAPI: userAPI, + Config: cfg, } if _, authErr := typePassword.Login(req.Context(), &r.Auth.PasswordRequest); authErr != nil { return *authErr diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index c7d01a01d..cd18144fe 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -1363,7 +1363,7 @@ func Setup( // Cross-signing device keys postDeviceSigningKeys := httputil.MakeAuthAPI("post_device_signing_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { - return UploadCrossSigningDeviceKeys(req, userInteractiveAuth, userAPI, device, userAPI, cfg) + return UploadCrossSigningDeviceKeys(req, userAPI, device, userAPI, cfg) }) postDeviceSigningSignatures := httputil.MakeAuthAPI("post_device_signing_signatures", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { diff --git a/go.mod b/go.mod index 6d1817024..e5763529d 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/docker/docker v20.10.24+incompatible github.com/docker/go-connections v0.4.0 github.com/getsentry/sentry-go v0.14.0 + github.com/go-ldap/ldap/v3 v3.4.4 github.com/gologme/log v1.3.0 github.com/google/go-cmp v0.5.9 github.com/google/uuid v1.3.0 @@ -54,6 +55,7 @@ require ( ) require ( + github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect github.com/RoaringBitmap/roaring v1.2.3 // indirect @@ -79,6 +81,7 @@ require ( github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.0 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect diff --git a/go.sum b/go.sum index fffe51b18..0480e2287 100644 --- a/go.sum +++ b/go.sum @@ -37,6 +37,8 @@ github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979 h1:WndgpSW13S32VLQ3 github.com/Arceliar/phony v0.0.0-20210209235338-dde1a8dca979/go.mod h1:6Lkn+/zJilRMsKmbmG1RPoamiArC6HS73xbwRyp3UyI= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= +github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/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/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= @@ -159,6 +161,8 @@ github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwv github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= +github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -167,6 +171,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs= +github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= @@ -454,6 +460,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -504,6 +511,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -582,6 +590,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= diff --git a/setup/config/config_clientapi.go b/setup/config/config_clientapi.go index b6c74a75f..b1ab02ccb 100644 --- a/setup/config/config_clientapi.go +++ b/setup/config/config_clientapi.go @@ -52,9 +52,26 @@ type ClientAPI struct { RateLimiting RateLimiting `yaml:"rate_limiting"` MSCs *MSCs `yaml:"-"` + + Ldap Ldap `yaml:"ldap"` } -func (c *ClientAPI) Defaults(opts DefaultOpts) { +type Ldap struct { + Enabled bool `yaml:"enabled"` + Uri string `yaml:"uri"` + BaseDn string `yaml:"base_dn"` + SearchFilter string `yaml:"search_filter"` + SearchAttribute string `yaml:"search_attribute"` + AdminBindEnabled bool `yaml:"admin_bind_enabled"` + AdminBindDn string `yaml:"admin_bind_dn"` + AdminBindPassword string `yaml:"admin_bind_password"` + UserBindDn string `yaml:"user_bind_dn"` + AdminGroupDn string `yaml:"admin_group_dn"` + AdminGroupFilter string `yaml:"admin_group_filter"` + AdminGroupAttribute string `yaml:"admin_group_attribute"` +} + +func (c *ClientAPI) Defaults(_ DefaultOpts) { c.RegistrationSharedSecret = "" c.RecaptchaPublicKey = "" c.RecaptchaPrivateKey = "" diff --git a/userapi/api/api.go b/userapi/api/api.go index ba1c374f1..395a424fb 100644 --- a/userapi/api/api.go +++ b/userapi/api/api.go @@ -131,6 +131,8 @@ type QueryAcccessTokenAPI interface { type UserLoginAPI interface { QueryAccountByPassword(ctx context.Context, req *QueryAccountByPasswordRequest, res *QueryAccountByPasswordResponse) error + QueryAccountByLocalpart(ctx context.Context, req *QueryAccountByLocalpartRequest, res *QueryAccountByLocalpartResponse) error + PerformAccountCreation(ctx context.Context, req *PerformAccountCreationRequest, res *PerformAccountCreationResponse) error } type PerformKeyBackupRequest struct {