Skip to content

Commit dbecac0

Browse files
committed
Added auth
1 parent f053d79 commit dbecac0

File tree

6 files changed

+208
-12
lines changed

6 files changed

+208
-12
lines changed

cmd/server/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ type CLI struct {
4242
auth.AuthCommands
4343
} `cmd:""`
4444

45-
LDAP struct{ ldap.ObjectCommands } `cmd:""`
45+
LDAP struct {
46+
ldap.ObjectCommands
47+
ldap.AuthCommands
48+
} `cmd:""`
4649

4750
VersionCommands
4851
}

pkg/ldap/client/auth.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"net/http"
6+
7+
// Packages
8+
client "github.com/mutablelogic/go-client"
9+
schema "github.com/mutablelogic/go-server/pkg/ldap/schema"
10+
)
11+
12+
///////////////////////////////////////////////////////////////////////////////
13+
// PUBLIC METHODS
14+
15+
func (c *Client) Auth(ctx context.Context, dn, password string) (*schema.Object, error) {
16+
req, err := client.NewJSONRequest(password)
17+
if err != nil {
18+
return nil, err
19+
}
20+
21+
// Perform request
22+
var response schema.Object
23+
if err := c.DoWithContext(ctx, req, &response, client.OptPath("auth", dn)); err != nil {
24+
return nil, err
25+
}
26+
27+
// Return the responses
28+
return &response, nil
29+
}
30+
31+
func (c *Client) ChangePassword(ctx context.Context, dn, password string) (*schema.Object, error) {
32+
req, err := client.NewJSONRequestEx(http.MethodPut, password, "")
33+
if err != nil {
34+
return nil, err
35+
}
36+
37+
// Perform request
38+
// TODO: Retrieve the new password from the response
39+
var response schema.Object
40+
if err := c.DoWithContext(ctx, req, &response, client.OptPath("auth", dn)); err != nil {
41+
return nil, err
42+
}
43+
44+
// Return the responses
45+
return &response, nil
46+
}

pkg/ldap/cmd/auth.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
// Packages
8+
server "github.com/mutablelogic/go-server"
9+
client "github.com/mutablelogic/go-server/pkg/ldap/client"
10+
)
11+
12+
///////////////////////////////////////////////////////////////////////////////
13+
// TYPES
14+
15+
type AuthCommands struct {
16+
Auth AuthBindCommand `cmd:"" group:"LDAP" help:"Authenticate user"`
17+
ChangePassword AuthChangePasswordCommand `cmd:"" group:"LDAP" help:"Change password"`
18+
}
19+
20+
type AuthBindCommand struct {
21+
ObjectGetCommand
22+
Password string `required:"" help:"Password"`
23+
}
24+
25+
type AuthChangePasswordCommand struct {
26+
AuthBindCommand
27+
}
28+
29+
///////////////////////////////////////////////////////////////////////////////
30+
// PUBLIC METHODS
31+
32+
func (cmd AuthBindCommand) Run(ctx server.Cmd) error {
33+
return run(ctx, func(ctx context.Context, provider *client.Client) error {
34+
object, err := provider.Auth(ctx, cmd.DN, cmd.Password)
35+
if err != nil {
36+
return err
37+
}
38+
39+
// Print object
40+
fmt.Println(object)
41+
return nil
42+
})
43+
}
44+
45+
func (cmd AuthChangePasswordCommand) Run(ctx server.Cmd) error {
46+
return run(ctx, func(ctx context.Context, provider *client.Client) error {
47+
object, err := provider.ChangePassword(ctx, cmd.DN, cmd.Password)
48+
if err != nil {
49+
return err
50+
}
51+
52+
// Print object
53+
fmt.Println(object)
54+
return nil
55+
})
56+
}

pkg/ldap/handler/auth.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package handler
2+
3+
import (
4+
"net/http"
5+
6+
// Packages
7+
httprequest "github.com/mutablelogic/go-server/pkg/httprequest"
8+
httpresponse "github.com/mutablelogic/go-server/pkg/httpresponse"
9+
ldap "github.com/mutablelogic/go-server/pkg/ldap"
10+
schema "github.com/mutablelogic/go-server/pkg/ldap/schema"
11+
)
12+
13+
///////////////////////////////////////////////////////////////////////////////
14+
// PRIVATE METHODS
15+
16+
func authBind(w http.ResponseWriter, r *http.Request, manager *ldap.Manager, dn string) error {
17+
// Get the password from the post body
18+
var password string
19+
if err := httprequest.Read(r, &password); err != nil {
20+
return httpresponse.Error(w, err)
21+
}
22+
23+
// Return error if the password is empty
24+
if password == "" {
25+
return httpresponse.Error(w, httpresponse.ErrBadRequest.With("missing password"))
26+
}
27+
28+
// Bind the password
29+
response, err := manager.Bind(r.Context(), dn, password)
30+
if err != nil {
31+
return httpresponse.Error(w, err)
32+
}
33+
34+
// Return success
35+
return httpresponse.JSON(w, http.StatusOK, httprequest.Indent(r), response)
36+
}
37+
38+
func authChangePassword(w http.ResponseWriter, r *http.Request, manager *ldap.Manager, dn string) error {
39+
// Get the new password from the post body
40+
var password string
41+
if err := httprequest.Read(r, &password); err != nil {
42+
return httpresponse.Error(w, err)
43+
}
44+
45+
// Change the password
46+
var response struct {
47+
*schema.Object
48+
Password string `json:"password,omitempty"`
49+
}
50+
51+
// Set the new password
52+
response.Password = password
53+
54+
// Change the password
55+
user, err := manager.ChangePassword(r.Context(), dn, "", &response.Password)
56+
if err != nil {
57+
return httpresponse.Error(w, err)
58+
} else {
59+
response.Object = user
60+
}
61+
62+
// Return success
63+
return httpresponse.JSON(w, http.StatusOK, httprequest.Indent(r), response)
64+
}

pkg/ldap/handler/handler.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717

1818
func Register(ctx context.Context, router server.HTTPRouter, manager *ldap.Manager) {
1919
registerObject(ctx, router, schema.APIPrefix, manager)
20+
registerAuth(ctx, router, schema.APIPrefix, manager)
2021
}
2122

2223
func registerObject(ctx context.Context, router server.HTTPRouter, prefix string, manager *ldap.Manager) {
@@ -54,3 +55,21 @@ func registerObject(ctx context.Context, router server.HTTPRouter, prefix string
5455
}
5556
})
5657
}
58+
59+
func registerAuth(ctx context.Context, router server.HTTPRouter, prefix string, manager *ldap.Manager) {
60+
router.HandleFunc(ctx, types.JoinPath(prefix, "auth/{dn...}"), func(w http.ResponseWriter, r *http.Request) {
61+
defer r.Body.Close()
62+
httpresponse.Cors(w, r, router.Origin(), http.MethodPost, http.MethodPut)
63+
64+
switch r.Method {
65+
case http.MethodOptions:
66+
_ = httpresponse.Empty(w, http.StatusOK)
67+
case http.MethodPost:
68+
_ = authBind(w, r, manager, r.PathValue("dn"))
69+
case http.MethodPut:
70+
_ = authChangePassword(w, r, manager, r.PathValue("dn"))
71+
default:
72+
_ = httpresponse.Error(w, httpresponse.Err(http.StatusMethodNotAllowed), r.Method)
73+
}
74+
})
75+
}

pkg/ldap/manager.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io"
99
"math/rand"
10+
"net/http"
1011
"net/url"
1112
"strconv"
1213
"sync"
@@ -348,7 +349,7 @@ func (manager *Manager) Get(ctx context.Context, dn string) (*schema.Object, err
348349
}
349350

350351
// Make absolute DN
351-
absdn, err := manager.absdn(dn, manager.dn)
352+
absdn, err := manager.absdn(dn)
352353
if err != nil {
353354
return nil, err
354355
}
@@ -385,7 +386,7 @@ func (manager *Manager) Create(ctx context.Context, dn string, attr url.Values)
385386
}
386387

387388
// Make absolute DN
388-
absdn, err := manager.absdn(dn, manager.dn)
389+
absdn, err := manager.absdn(dn)
389390
if err != nil {
390391
return nil, err
391392
}
@@ -418,7 +419,7 @@ func (manager *Manager) Delete(ctx context.Context, dn string) (*schema.Object,
418419
}
419420

420421
// Make absolute DN
421-
absdn, err := manager.absdn(dn, manager.dn)
422+
absdn, err := manager.absdn(dn)
422423
if err != nil {
423424
return nil, err
424425
}
@@ -450,19 +451,24 @@ func (manager *Manager) Bind(ctx context.Context, dn, password string) (*schema.
450451
}
451452

452453
// Make absolute DN
453-
absdn, err := manager.absdn(dn, manager.dn)
454+
absdn, err := manager.absdn(dn)
454455
if err != nil {
455456
return nil, err
456457
}
457458

458-
// Bind
459-
if err := manager.conn.Bind(absdn.String(), password); err != nil {
460-
return nil, ldaperr(err)
459+
// Bind - which may result in invalid credentials
460+
var errs error
461+
if err := manager.conn.Bind(absdn.String(), password); ldapErrorCode(err) == ldap.LDAPResultInvalidCredentials {
462+
errs = ldaperr(err)
463+
} else if err != nil {
464+
return nil, err
461465
}
462466

463467
// Rebind with this user
464468
if err := ldapBind(manager.conn, manager.User(), manager.pass); err != nil {
465-
return nil, ldaperr(err)
469+
return nil, errors.Join(errs, ldaperr(err))
470+
} else if errs != nil {
471+
return nil, errs
466472
}
467473

468474
// Return the user
@@ -487,7 +493,7 @@ func (manager *Manager) ChangePassword(ctx context.Context, dn, old string, new
487493
}
488494

489495
// Make absolute DN
490-
absdn, err := manager.absdn(dn, manager.dn)
496+
absdn, err := manager.absdn(dn)
491497
if err != nil {
492498
return nil, err
493499
}
@@ -515,7 +521,7 @@ func (manager *Manager) Update(ctx context.Context, dn string, attr url.Values)
515521
}
516522

517523
// Make absolute DN
518-
absdn, err := manager.absdn(dn, manager.dn)
524+
absdn, err := manager.absdn(dn)
519525
if err != nil {
520526
return nil, err
521527
}
@@ -887,13 +893,15 @@ func ldaperr(err error) error {
887893
return httpresponse.ErrNotFound.With(err.Error())
888894
case ldap.LDAPResultConstraintViolation:
889895
return httpresponse.ErrConflict.With(err.Error())
896+
case ldap.LDAPResultUnwillingToPerform:
897+
return httpresponse.Err(http.StatusServiceUnavailable).With(err.Error())
890898
default:
891899
return httpresponse.ErrInternalError.With(err)
892900
}
893901
}
894902

895903
// Make the DN absolute
896-
func (manager *Manager) absdn(dn string, base *schema.DN) (*schema.DN, error) {
904+
func (manager *Manager) absdn(dn string) (*schema.DN, error) {
897905
rdn, err := schema.NewDN(dn)
898906
if err != nil {
899907
return nil, httpresponse.ErrBadRequest.Withf("Invalid DN: %v", err.Error())

0 commit comments

Comments
 (0)