Skip to content

Commit 88a182d

Browse files
authored
Merge pull request #95 from mutablelogic/v5
Added LDAP plugin
2 parents b54bb47 + ffd6146 commit 88a182d

32 files changed

+2154
-254
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ API and web applications. It is designed to be extensible and modular, allowing
55
new features and functionality as needed. By default the server includes the following
66
features:
77

8-
* Authentication of users using JWT tokens and API keys
9-
* Connection to PostgreSQL databases
8+
* Anthentication of users using Cognito, Google and LDAP
9+
* Authorization of users using JWT tokens and API keys
1010
* Task queues for running background jobs
11+
* Connection to PostgreSQL databases
1112
* Ability to manage the PostgreSQL database roles, databases, schemas and connections
1213
* Prometheus metrics support
1314

cmd/server/main.go

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,36 @@ import (
1818

1919
type CLI struct {
2020
ServiceCommands
21-
pgmanager.DatabaseCommands
22-
pgmanager.SchemaCommands
23-
pgmanager.ObjectCommands
24-
pgmanager.RoleCommands
25-
pgmanager.ConnectionCommands
26-
pgmanager.TablespaceCommands
27-
pgqueue.QueueCommands
28-
pgqueue.TaskCommands
29-
pgqueue.TickerCommands
30-
certmanager.NameCommands
31-
certmanager.CertCommands
32-
auth.UserCommands
33-
auth.TokenCommands
34-
auth.AuthCommands
35-
36-
// LDAP commands
37-
LDAP struct{ ldap.ObjectCommands } `cmd:""`
21+
22+
PG struct {
23+
pgmanager.DatabaseCommands
24+
pgmanager.SchemaCommands
25+
pgmanager.ObjectCommands
26+
pgmanager.RoleCommands
27+
pgmanager.ConnectionCommands
28+
pgmanager.TablespaceCommands
29+
pgqueue.QueueCommands
30+
pgqueue.TaskCommands
31+
pgqueue.TickerCommands
32+
} `cmd:""`
33+
34+
Cert struct {
35+
certmanager.NameCommands
36+
certmanager.CertCommands
37+
} `cmd:""`
38+
39+
Auth struct {
40+
auth.UserCommands
41+
auth.TokenCommands
42+
auth.AuthCommands
43+
} `cmd:""`
44+
45+
LDAP struct {
46+
ldap.ObjectCommands
47+
ldap.AuthCommands
48+
ldap.UserCommands
49+
ldap.GroupCommands
50+
} `cmd:""`
3851

3952
VersionCommands
4053
}

cmd/server/service.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,15 @@ func (cmd *ServiceRunCommand) Run(app server.Cmd) error {
211211
ldap.Router = router
212212
}
213213

214+
// HACK
215+
ldap.UserSchema.RDN = "cn=users,cn=accounts"
216+
ldap.UserSchema.Field = "uid"
217+
ldap.UserSchema.ObjectClasses = "top,inetOrgPerson,person,posixAccount"
218+
219+
ldap.GroupSchema.RDN = "cn=groups,cn=accounts"
220+
ldap.GroupSchema.Field = "cn"
221+
ldap.GroupSchema.ObjectClasses = "top,groupOfNames,nestedGroup,posixGroup"
222+
214223
return nil
215224
}))
216225

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
github.com/golang-jwt/jwt/v5 v5.2.2
1111
github.com/mutablelogic/go-client v1.0.12
1212
github.com/stretchr/testify v1.10.0
13+
github.com/yinyin/go-ldap-schema-parser v0.0.0-20190716182935-542aadd3dcb5
1314
)
1415

1516
require (

go.sum

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ
5252
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
5353
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
5454
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
55+
github.com/go-ldap/ldif v0.0.0-20180918085934-3491d58cdb60/go.mod h1:blBiFTfuR1Jrw4xZ7t3xuNObLzzBG+ce+5W/bEYwJq0=
5556
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
5657
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
5758
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -163,6 +164,8 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
163164
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
164165
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
165166
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
167+
github.com/yinyin/go-ldap-schema-parser v0.0.0-20190716182935-542aadd3dcb5 h1:siJ/5leB7JENBScgD/qG8JAGiS/2Q76qxCPK81icczU=
168+
github.com/yinyin/go-ldap-schema-parser v0.0.0-20190716182935-542aadd3dcb5/go.mod h1:Hb9db5nLRb/cT+dBKUrukgT3Z9mbtrpF3o2g8+sw7ic=
166169
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
167170
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
168171
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
@@ -238,9 +241,11 @@ google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
238241
google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
239242
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
240243
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
244+
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
241245
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
242246
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
243247
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
248+
gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk=
244249
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
245250
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
246251
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

pkg/httpresponse/error.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const (
3030
ErrInternalError = Err(http.StatusInternalServerError)
3131
ErrNotAuthorized = Err(http.StatusUnauthorized)
3232
ErrForbidden = Err(http.StatusForbidden)
33+
ErrGatewayError = Err(http.StatusBadGateway)
3334
)
3435

3536
///////////////////////////////////////////////////////////////////////////////

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/client/group.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package client
2+
3+
import (
4+
"context"
5+
6+
// Packages
7+
client "github.com/mutablelogic/go-client"
8+
schema "github.com/mutablelogic/go-server/pkg/ldap/schema"
9+
)
10+
11+
///////////////////////////////////////////////////////////////////////////////
12+
// PUBLIC METHODS
13+
14+
func (c *Client) ListGroups(ctx context.Context, opts ...Opt) (*schema.ObjectList, error) {
15+
req := client.NewRequest()
16+
17+
// Apply options
18+
opt, err := applyOpts(opts...)
19+
if err != nil {
20+
return nil, err
21+
}
22+
23+
// Perform request
24+
var response schema.ObjectList
25+
if err := c.DoWithContext(ctx, req, &response, client.OptPath("group"), client.OptQuery(opt.Values)); err != nil {
26+
return nil, err
27+
}
28+
29+
// Return the responses
30+
return &response, nil
31+
}
32+
33+
func (c *Client) CreateGroup(ctx context.Context, meta schema.Object) (*schema.Object, error) {
34+
req, err := client.NewJSONRequest(meta)
35+
if err != nil {
36+
return nil, err
37+
}
38+
39+
// Perform request
40+
var response schema.Object
41+
if err := c.DoWithContext(ctx, req, &response, client.OptPath("group")); err != nil {
42+
return nil, err
43+
}
44+
45+
// Return the responses
46+
return &response, nil
47+
}
48+
49+
func (c *Client) GetGroup(ctx context.Context, user string) (*schema.Object, error) {
50+
var resp schema.Object
51+
52+
// Perform request
53+
if err := c.DoWithContext(ctx, client.MethodGet, &resp, client.OptPath("group", user)); err != nil {
54+
return nil, err
55+
}
56+
57+
// Return the response
58+
return &resp, nil
59+
}
60+
61+
func (c *Client) DeleteGroup(ctx context.Context, user string) error {
62+
// Perform request
63+
return c.DoWithContext(ctx, client.MethodDelete, nil, client.OptPath("group", user))
64+
}

pkg/ldap/client/object.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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) ListObjects(ctx context.Context, opts ...Opt) (*schema.ObjectList, error) {
16+
req := client.NewRequest()
17+
18+
// Apply options
19+
opt, err := applyOpts(opts...)
20+
if err != nil {
21+
return nil, err
22+
}
23+
24+
// Perform request
25+
var response schema.ObjectList
26+
if err := c.DoWithContext(ctx, req, &response, client.OptPath("object"), client.OptQuery(opt.Values)); err != nil {
27+
return nil, err
28+
}
29+
30+
// Return the responses
31+
return &response, nil
32+
}
33+
34+
func (c *Client) CreateObject(ctx context.Context, meta schema.Object) (*schema.Object, error) {
35+
req, err := client.NewJSONRequest(meta)
36+
if err != nil {
37+
return nil, err
38+
}
39+
40+
// Perform request
41+
var response schema.Object
42+
if err := c.DoWithContext(ctx, req, &response, client.OptPath("object")); err != nil {
43+
return nil, err
44+
}
45+
46+
// Return the responses
47+
return &response, nil
48+
}
49+
50+
func (c *Client) GetObject(ctx context.Context, dn string) (*schema.Object, error) {
51+
var resp schema.Object
52+
53+
// Perform request
54+
if err := c.DoWithContext(ctx, client.MethodGet, &resp, client.OptPath("object", dn)); err != nil {
55+
return nil, err
56+
}
57+
58+
// Return the response
59+
return &resp, nil
60+
}
61+
62+
func (c *Client) DeleteObject(ctx context.Context, dn string) error {
63+
// Perform request
64+
return c.DoWithContext(ctx, client.MethodDelete, nil, client.OptPath("object", dn))
65+
}
66+
67+
func (c *Client) UpdateObject(ctx context.Context, meta schema.Object) (*schema.Object, error) {
68+
req, err := client.NewJSONRequestEx(http.MethodPatch, meta, "")
69+
if err != nil {
70+
return nil, err
71+
}
72+
73+
// Perform request
74+
var response schema.Object
75+
if err := c.DoWithContext(ctx, req, &response, client.OptPath("object", meta.DN)); err != nil {
76+
return nil, err
77+
}
78+
79+
// Return the responses
80+
return &response, nil
81+
}

pkg/ldap/client/objects.go

Lines changed: 0 additions & 31 deletions
This file was deleted.

pkg/ldap/client/opts.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ func WithFilter(v *string) Opt {
5353
return OptSet("filter", types.PtrString(v))
5454
}
5555

56+
// Set LDAP attributes
57+
func WithAttr(v ...string) Opt {
58+
return func(o *opt) error {
59+
if len(v) == 0 {
60+
o.Del("attr")
61+
} else {
62+
o.Values["attr"] = v
63+
}
64+
return nil
65+
}
66+
}
67+
5668
func OptSet(k, v string) Opt {
5769
return func(o *opt) error {
5870
if v == "" {

0 commit comments

Comments
 (0)