Skip to content

Commit a618df8

Browse files
ngourdontechknowlogick
authored andcommitted
Add CLI commands to manage LDAP authentication source (#6681)
* add CLI commands to manage LDAP authentication source * delete Gogs copyright * remove unused return value of func parseLoginSource * fix comment Co-Authored-By: ngourdon <[email protected]> * remove config flag already present in global flags * remove config flag from ldap commands in docs * remove config flag handling
1 parent a200ca1 commit a618df8

File tree

4 files changed

+1802
-1
lines changed

4 files changed

+1802
-1
lines changed

cmd/admin.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ var (
131131
Subcommands: []cli.Command{
132132
microcmdAuthAddOauth,
133133
microcmdAuthUpdateOauth,
134+
cmdAuthAddLdapBindDn,
135+
cmdAuthUpdateLdapBindDn,
136+
cmdAuthAddLdapSimpleAuth,
137+
cmdAuthUpdateLdapSimpleAuth,
134138
microcmdAuthList,
135139
microcmdAuthDelete,
136140
},
@@ -144,7 +148,7 @@ var (
144148

145149
idFlag = cli.Int64Flag{
146150
Name: "id",
147-
Usage: "ID of OAuth authentication source",
151+
Usage: "ID of authentication source",
148152
}
149153

150154
microcmdAuthDelete = cli.Command{

cmd/admin_auth_ldap.go

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
// Copyright 2019 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package cmd
6+
7+
import (
8+
"fmt"
9+
"strings"
10+
11+
"code.gitea.io/gitea/models"
12+
"code.gitea.io/gitea/modules/auth/ldap"
13+
14+
"github.com/urfave/cli"
15+
)
16+
17+
type (
18+
authService struct {
19+
initDB func() error
20+
createLoginSource func(loginSource *models.LoginSource) error
21+
updateLoginSource func(loginSource *models.LoginSource) error
22+
getLoginSourceByID func(id int64) (*models.LoginSource, error)
23+
}
24+
)
25+
26+
var (
27+
commonLdapCLIFlags = []cli.Flag{
28+
cli.StringFlag{
29+
Name: "name",
30+
Usage: "Authentication name.",
31+
},
32+
cli.BoolFlag{
33+
Name: "not-active",
34+
Usage: "Deactivate the authentication source.",
35+
},
36+
cli.StringFlag{
37+
Name: "security-protocol",
38+
Usage: "Security protocol name.",
39+
},
40+
cli.BoolFlag{
41+
Name: "skip-tls-verify",
42+
Usage: "Disable TLS verification.",
43+
},
44+
cli.StringFlag{
45+
Name: "host",
46+
Usage: "The address where the LDAP server can be reached.",
47+
},
48+
cli.IntFlag{
49+
Name: "port",
50+
Usage: "The port to use when connecting to the LDAP server.",
51+
},
52+
cli.StringFlag{
53+
Name: "user-search-base",
54+
Usage: "The LDAP base at which user accounts will be searched for.",
55+
},
56+
cli.StringFlag{
57+
Name: "user-filter",
58+
Usage: "An LDAP filter declaring how to find the user record that is attempting to authenticate.",
59+
},
60+
cli.StringFlag{
61+
Name: "admin-filter",
62+
Usage: "An LDAP filter specifying if a user should be given administrator privileges.",
63+
},
64+
cli.StringFlag{
65+
Name: "username-attribute",
66+
Usage: "The attribute of the user’s LDAP record containing the user name.",
67+
},
68+
cli.StringFlag{
69+
Name: "firstname-attribute",
70+
Usage: "The attribute of the user’s LDAP record containing the user’s first name.",
71+
},
72+
cli.StringFlag{
73+
Name: "surname-attribute",
74+
Usage: "The attribute of the user’s LDAP record containing the user’s surname.",
75+
},
76+
cli.StringFlag{
77+
Name: "email-attribute",
78+
Usage: "The attribute of the user’s LDAP record containing the user’s email address.",
79+
},
80+
cli.StringFlag{
81+
Name: "public-ssh-key-attribute",
82+
Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.",
83+
},
84+
}
85+
86+
ldapBindDnCLIFlags = append(commonLdapCLIFlags,
87+
cli.StringFlag{
88+
Name: "bind-dn",
89+
Usage: "The DN to bind to the LDAP server with when searching for the user.",
90+
},
91+
cli.StringFlag{
92+
Name: "bind-password",
93+
Usage: "The password for the Bind DN, if any.",
94+
},
95+
cli.BoolFlag{
96+
Name: "attributes-in-bind",
97+
Usage: "Fetch attributes in bind DN context.",
98+
},
99+
cli.BoolFlag{
100+
Name: "synchronize-users",
101+
Usage: "Enable user synchronization.",
102+
},
103+
cli.UintFlag{
104+
Name: "page-size",
105+
Usage: "Search page size.",
106+
})
107+
108+
ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
109+
cli.StringFlag{
110+
Name: "user-dn",
111+
Usage: "The user’s DN.",
112+
})
113+
114+
cmdAuthAddLdapBindDn = cli.Command{
115+
Name: "add-ldap",
116+
Usage: "Add new LDAP (via Bind DN) authentication source",
117+
Action: func(c *cli.Context) error {
118+
return newAuthService().addLdapBindDn(c)
119+
},
120+
Flags: ldapBindDnCLIFlags,
121+
}
122+
123+
cmdAuthUpdateLdapBindDn = cli.Command{
124+
Name: "update-ldap",
125+
Usage: "Update existing LDAP (via Bind DN) authentication source",
126+
Action: func(c *cli.Context) error {
127+
return newAuthService().updateLdapBindDn(c)
128+
},
129+
Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
130+
}
131+
132+
cmdAuthAddLdapSimpleAuth = cli.Command{
133+
Name: "add-ldap-simple",
134+
Usage: "Add new LDAP (simple auth) authentication source",
135+
Action: func(c *cli.Context) error {
136+
return newAuthService().addLdapSimpleAuth(c)
137+
},
138+
Flags: ldapSimpleAuthCLIFlags,
139+
}
140+
141+
cmdAuthUpdateLdapSimpleAuth = cli.Command{
142+
Name: "update-ldap-simple",
143+
Usage: "Update existing LDAP (simple auth) authentication source",
144+
Action: func(c *cli.Context) error {
145+
return newAuthService().updateLdapSimpleAuth(c)
146+
},
147+
Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...),
148+
}
149+
)
150+
151+
// newAuthService creates a service with default functions.
152+
func newAuthService() *authService {
153+
return &authService{
154+
initDB: initDB,
155+
createLoginSource: models.CreateLoginSource,
156+
updateLoginSource: models.UpdateSource,
157+
getLoginSourceByID: models.GetLoginSourceByID,
158+
}
159+
}
160+
161+
// parseLoginSource assigns values on loginSource according to command line flags.
162+
func parseLoginSource(c *cli.Context, loginSource *models.LoginSource) {
163+
if c.IsSet("name") {
164+
loginSource.Name = c.String("name")
165+
}
166+
if c.IsSet("not-active") {
167+
loginSource.IsActived = !c.Bool("not-active")
168+
}
169+
if c.IsSet("synchronize-users") {
170+
loginSource.IsSyncEnabled = c.Bool("synchronize-users")
171+
}
172+
}
173+
174+
// parseLdapConfig assigns values on config according to command line flags.
175+
func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error {
176+
if c.IsSet("name") {
177+
config.Source.Name = c.String("name")
178+
}
179+
if c.IsSet("host") {
180+
config.Source.Host = c.String("host")
181+
}
182+
if c.IsSet("port") {
183+
config.Source.Port = c.Int("port")
184+
}
185+
if c.IsSet("security-protocol") {
186+
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
187+
if !ok {
188+
return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
189+
}
190+
config.Source.SecurityProtocol = p
191+
}
192+
if c.IsSet("skip-tls-verify") {
193+
config.Source.SkipVerify = c.Bool("skip-tls-verify")
194+
}
195+
if c.IsSet("bind-dn") {
196+
config.Source.BindDN = c.String("bind-dn")
197+
}
198+
if c.IsSet("user-dn") {
199+
config.Source.UserDN = c.String("user-dn")
200+
}
201+
if c.IsSet("bind-password") {
202+
config.Source.BindPassword = c.String("bind-password")
203+
}
204+
if c.IsSet("user-search-base") {
205+
config.Source.UserBase = c.String("user-search-base")
206+
}
207+
if c.IsSet("username-attribute") {
208+
config.Source.AttributeUsername = c.String("username-attribute")
209+
}
210+
if c.IsSet("firstname-attribute") {
211+
config.Source.AttributeName = c.String("firstname-attribute")
212+
}
213+
if c.IsSet("surname-attribute") {
214+
config.Source.AttributeSurname = c.String("surname-attribute")
215+
}
216+
if c.IsSet("email-attribute") {
217+
config.Source.AttributeMail = c.String("email-attribute")
218+
}
219+
if c.IsSet("attributes-in-bind") {
220+
config.Source.AttributesInBind = c.Bool("attributes-in-bind")
221+
}
222+
if c.IsSet("public-ssh-key-attribute") {
223+
config.Source.AttributeSSHPublicKey = c.String("public-ssh-key-attribute")
224+
}
225+
if c.IsSet("page-size") {
226+
config.Source.SearchPageSize = uint32(c.Uint("page-size"))
227+
}
228+
if c.IsSet("user-filter") {
229+
config.Source.Filter = c.String("user-filter")
230+
}
231+
if c.IsSet("admin-filter") {
232+
config.Source.AdminFilter = c.String("admin-filter")
233+
}
234+
return nil
235+
}
236+
237+
// findLdapSecurityProtocolByName finds security protocol by its name ignoring case.
238+
// It returns the value of the security protocol and if it was found.
239+
func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
240+
for i, n := range models.SecurityProtocolNames {
241+
if strings.EqualFold(name, n) {
242+
return i, true
243+
}
244+
}
245+
return 0, false
246+
}
247+
248+
// getLoginSource gets the login source by its id defined in the command line flags.
249+
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
250+
func (a *authService) getLoginSource(c *cli.Context, loginType models.LoginType) (*models.LoginSource, error) {
251+
if err := argsSet(c, "id"); err != nil {
252+
return nil, err
253+
}
254+
255+
loginSource, err := a.getLoginSourceByID(c.Int64("id"))
256+
if err != nil {
257+
return nil, err
258+
}
259+
260+
if loginSource.Type != loginType {
261+
return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", models.LoginNames[loginType], models.LoginNames[loginSource.Type])
262+
}
263+
264+
return loginSource, nil
265+
}
266+
267+
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
268+
func (a *authService) addLdapBindDn(c *cli.Context) error {
269+
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
270+
return err
271+
}
272+
273+
if err := a.initDB(); err != nil {
274+
return err
275+
}
276+
277+
loginSource := &models.LoginSource{
278+
Type: models.LoginLDAP,
279+
IsActived: true, // active by default
280+
Cfg: &models.LDAPConfig{
281+
Source: &ldap.Source{
282+
Enabled: true, // always true
283+
},
284+
},
285+
}
286+
287+
parseLoginSource(c, loginSource)
288+
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
289+
return err
290+
}
291+
292+
return a.createLoginSource(loginSource)
293+
}
294+
295+
// updateLdapBindDn updates a new LDAP via Bind DN authentication source.
296+
func (a *authService) updateLdapBindDn(c *cli.Context) error {
297+
if err := a.initDB(); err != nil {
298+
return err
299+
}
300+
301+
loginSource, err := a.getLoginSource(c, models.LoginLDAP)
302+
if err != nil {
303+
return err
304+
}
305+
306+
parseLoginSource(c, loginSource)
307+
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
308+
return err
309+
}
310+
311+
return a.updateLoginSource(loginSource)
312+
}
313+
314+
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
315+
func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
316+
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil {
317+
return err
318+
}
319+
320+
if err := a.initDB(); err != nil {
321+
return err
322+
}
323+
324+
loginSource := &models.LoginSource{
325+
Type: models.LoginDLDAP,
326+
IsActived: true, // active by default
327+
Cfg: &models.LDAPConfig{
328+
Source: &ldap.Source{
329+
Enabled: true, // always true
330+
},
331+
},
332+
}
333+
334+
parseLoginSource(c, loginSource)
335+
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
336+
return err
337+
}
338+
339+
return a.createLoginSource(loginSource)
340+
}
341+
342+
// updateLdapBindDn updates a new LDAP (simple auth) authentication source.
343+
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
344+
if err := a.initDB(); err != nil {
345+
return err
346+
}
347+
348+
loginSource, err := a.getLoginSource(c, models.LoginDLDAP)
349+
if err != nil {
350+
return err
351+
}
352+
353+
parseLoginSource(c, loginSource)
354+
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil {
355+
return err
356+
}
357+
358+
return a.updateLoginSource(loginSource)
359+
}

0 commit comments

Comments
 (0)