forked from go-gitea/gitea
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CLI commands to manage LDAP authentication source (go-gitea#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 <31291059+ngourdon@users.noreply.github.com> * remove config flag already present in global flags * remove config flag from ldap commands in docs * remove config flag handling
- Loading branch information
Showing
4 changed files
with
1,802 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,359 @@ | ||
// Copyright 2019 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"code.gitea.io/gitea/models" | ||
"code.gitea.io/gitea/modules/auth/ldap" | ||
|
||
"github.com/urfave/cli" | ||
) | ||
|
||
type ( | ||
authService struct { | ||
initDB func() error | ||
createLoginSource func(loginSource *models.LoginSource) error | ||
updateLoginSource func(loginSource *models.LoginSource) error | ||
getLoginSourceByID func(id int64) (*models.LoginSource, error) | ||
} | ||
) | ||
|
||
var ( | ||
commonLdapCLIFlags = []cli.Flag{ | ||
cli.StringFlag{ | ||
Name: "name", | ||
Usage: "Authentication name.", | ||
}, | ||
cli.BoolFlag{ | ||
Name: "not-active", | ||
Usage: "Deactivate the authentication source.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "security-protocol", | ||
Usage: "Security protocol name.", | ||
}, | ||
cli.BoolFlag{ | ||
Name: "skip-tls-verify", | ||
Usage: "Disable TLS verification.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "host", | ||
Usage: "The address where the LDAP server can be reached.", | ||
}, | ||
cli.IntFlag{ | ||
Name: "port", | ||
Usage: "The port to use when connecting to the LDAP server.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "user-search-base", | ||
Usage: "The LDAP base at which user accounts will be searched for.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "user-filter", | ||
Usage: "An LDAP filter declaring how to find the user record that is attempting to authenticate.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "admin-filter", | ||
Usage: "An LDAP filter specifying if a user should be given administrator privileges.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "username-attribute", | ||
Usage: "The attribute of the user’s LDAP record containing the user name.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "firstname-attribute", | ||
Usage: "The attribute of the user’s LDAP record containing the user’s first name.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "surname-attribute", | ||
Usage: "The attribute of the user’s LDAP record containing the user’s surname.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "email-attribute", | ||
Usage: "The attribute of the user’s LDAP record containing the user’s email address.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "public-ssh-key-attribute", | ||
Usage: "The attribute of the user’s LDAP record containing the user’s public ssh key.", | ||
}, | ||
} | ||
|
||
ldapBindDnCLIFlags = append(commonLdapCLIFlags, | ||
cli.StringFlag{ | ||
Name: "bind-dn", | ||
Usage: "The DN to bind to the LDAP server with when searching for the user.", | ||
}, | ||
cli.StringFlag{ | ||
Name: "bind-password", | ||
Usage: "The password for the Bind DN, if any.", | ||
}, | ||
cli.BoolFlag{ | ||
Name: "attributes-in-bind", | ||
Usage: "Fetch attributes in bind DN context.", | ||
}, | ||
cli.BoolFlag{ | ||
Name: "synchronize-users", | ||
Usage: "Enable user synchronization.", | ||
}, | ||
cli.UintFlag{ | ||
Name: "page-size", | ||
Usage: "Search page size.", | ||
}) | ||
|
||
ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags, | ||
cli.StringFlag{ | ||
Name: "user-dn", | ||
Usage: "The user’s DN.", | ||
}) | ||
|
||
cmdAuthAddLdapBindDn = cli.Command{ | ||
Name: "add-ldap", | ||
Usage: "Add new LDAP (via Bind DN) authentication source", | ||
Action: func(c *cli.Context) error { | ||
return newAuthService().addLdapBindDn(c) | ||
}, | ||
Flags: ldapBindDnCLIFlags, | ||
} | ||
|
||
cmdAuthUpdateLdapBindDn = cli.Command{ | ||
Name: "update-ldap", | ||
Usage: "Update existing LDAP (via Bind DN) authentication source", | ||
Action: func(c *cli.Context) error { | ||
return newAuthService().updateLdapBindDn(c) | ||
}, | ||
Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...), | ||
} | ||
|
||
cmdAuthAddLdapSimpleAuth = cli.Command{ | ||
Name: "add-ldap-simple", | ||
Usage: "Add new LDAP (simple auth) authentication source", | ||
Action: func(c *cli.Context) error { | ||
return newAuthService().addLdapSimpleAuth(c) | ||
}, | ||
Flags: ldapSimpleAuthCLIFlags, | ||
} | ||
|
||
cmdAuthUpdateLdapSimpleAuth = cli.Command{ | ||
Name: "update-ldap-simple", | ||
Usage: "Update existing LDAP (simple auth) authentication source", | ||
Action: func(c *cli.Context) error { | ||
return newAuthService().updateLdapSimpleAuth(c) | ||
}, | ||
Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...), | ||
} | ||
) | ||
|
||
// newAuthService creates a service with default functions. | ||
func newAuthService() *authService { | ||
return &authService{ | ||
initDB: initDB, | ||
createLoginSource: models.CreateLoginSource, | ||
updateLoginSource: models.UpdateSource, | ||
getLoginSourceByID: models.GetLoginSourceByID, | ||
} | ||
} | ||
|
||
// parseLoginSource assigns values on loginSource according to command line flags. | ||
func parseLoginSource(c *cli.Context, loginSource *models.LoginSource) { | ||
if c.IsSet("name") { | ||
loginSource.Name = c.String("name") | ||
} | ||
if c.IsSet("not-active") { | ||
loginSource.IsActived = !c.Bool("not-active") | ||
} | ||
if c.IsSet("synchronize-users") { | ||
loginSource.IsSyncEnabled = c.Bool("synchronize-users") | ||
} | ||
} | ||
|
||
// parseLdapConfig assigns values on config according to command line flags. | ||
func parseLdapConfig(c *cli.Context, config *models.LDAPConfig) error { | ||
if c.IsSet("name") { | ||
config.Source.Name = c.String("name") | ||
} | ||
if c.IsSet("host") { | ||
config.Source.Host = c.String("host") | ||
} | ||
if c.IsSet("port") { | ||
config.Source.Port = c.Int("port") | ||
} | ||
if c.IsSet("security-protocol") { | ||
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol")) | ||
if !ok { | ||
return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol")) | ||
} | ||
config.Source.SecurityProtocol = p | ||
} | ||
if c.IsSet("skip-tls-verify") { | ||
config.Source.SkipVerify = c.Bool("skip-tls-verify") | ||
} | ||
if c.IsSet("bind-dn") { | ||
config.Source.BindDN = c.String("bind-dn") | ||
} | ||
if c.IsSet("user-dn") { | ||
config.Source.UserDN = c.String("user-dn") | ||
} | ||
if c.IsSet("bind-password") { | ||
config.Source.BindPassword = c.String("bind-password") | ||
} | ||
if c.IsSet("user-search-base") { | ||
config.Source.UserBase = c.String("user-search-base") | ||
} | ||
if c.IsSet("username-attribute") { | ||
config.Source.AttributeUsername = c.String("username-attribute") | ||
} | ||
if c.IsSet("firstname-attribute") { | ||
config.Source.AttributeName = c.String("firstname-attribute") | ||
} | ||
if c.IsSet("surname-attribute") { | ||
config.Source.AttributeSurname = c.String("surname-attribute") | ||
} | ||
if c.IsSet("email-attribute") { | ||
config.Source.AttributeMail = c.String("email-attribute") | ||
} | ||
if c.IsSet("attributes-in-bind") { | ||
config.Source.AttributesInBind = c.Bool("attributes-in-bind") | ||
} | ||
if c.IsSet("public-ssh-key-attribute") { | ||
config.Source.AttributeSSHPublicKey = c.String("public-ssh-key-attribute") | ||
} | ||
if c.IsSet("page-size") { | ||
config.Source.SearchPageSize = uint32(c.Uint("page-size")) | ||
} | ||
if c.IsSet("user-filter") { | ||
config.Source.Filter = c.String("user-filter") | ||
} | ||
if c.IsSet("admin-filter") { | ||
config.Source.AdminFilter = c.String("admin-filter") | ||
} | ||
return nil | ||
} | ||
|
||
// findLdapSecurityProtocolByName finds security protocol by its name ignoring case. | ||
// It returns the value of the security protocol and if it was found. | ||
func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) { | ||
for i, n := range models.SecurityProtocolNames { | ||
if strings.EqualFold(name, n) { | ||
return i, true | ||
} | ||
} | ||
return 0, false | ||
} | ||
|
||
// getLoginSource gets the login source by its id defined in the command line flags. | ||
// It returns an error if the id is not set, does not match any source or if the source is not of expected type. | ||
func (a *authService) getLoginSource(c *cli.Context, loginType models.LoginType) (*models.LoginSource, error) { | ||
if err := argsSet(c, "id"); err != nil { | ||
return nil, err | ||
} | ||
|
||
loginSource, err := a.getLoginSourceByID(c.Int64("id")) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if loginSource.Type != loginType { | ||
return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", models.LoginNames[loginType], models.LoginNames[loginSource.Type]) | ||
} | ||
|
||
return loginSource, nil | ||
} | ||
|
||
// addLdapBindDn adds a new LDAP via Bind DN authentication source. | ||
func (a *authService) addLdapBindDn(c *cli.Context) error { | ||
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil { | ||
return err | ||
} | ||
|
||
if err := a.initDB(); err != nil { | ||
return err | ||
} | ||
|
||
loginSource := &models.LoginSource{ | ||
Type: models.LoginLDAP, | ||
IsActived: true, // active by default | ||
Cfg: &models.LDAPConfig{ | ||
Source: &ldap.Source{ | ||
Enabled: true, // always true | ||
}, | ||
}, | ||
} | ||
|
||
parseLoginSource(c, loginSource) | ||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil { | ||
return err | ||
} | ||
|
||
return a.createLoginSource(loginSource) | ||
} | ||
|
||
// updateLdapBindDn updates a new LDAP via Bind DN authentication source. | ||
func (a *authService) updateLdapBindDn(c *cli.Context) error { | ||
if err := a.initDB(); err != nil { | ||
return err | ||
} | ||
|
||
loginSource, err := a.getLoginSource(c, models.LoginLDAP) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
parseLoginSource(c, loginSource) | ||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil { | ||
return err | ||
} | ||
|
||
return a.updateLoginSource(loginSource) | ||
} | ||
|
||
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source. | ||
func (a *authService) addLdapSimpleAuth(c *cli.Context) error { | ||
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil { | ||
return err | ||
} | ||
|
||
if err := a.initDB(); err != nil { | ||
return err | ||
} | ||
|
||
loginSource := &models.LoginSource{ | ||
Type: models.LoginDLDAP, | ||
IsActived: true, // active by default | ||
Cfg: &models.LDAPConfig{ | ||
Source: &ldap.Source{ | ||
Enabled: true, // always true | ||
}, | ||
}, | ||
} | ||
|
||
parseLoginSource(c, loginSource) | ||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil { | ||
return err | ||
} | ||
|
||
return a.createLoginSource(loginSource) | ||
} | ||
|
||
// updateLdapBindDn updates a new LDAP (simple auth) authentication source. | ||
func (a *authService) updateLdapSimpleAuth(c *cli.Context) error { | ||
if err := a.initDB(); err != nil { | ||
return err | ||
} | ||
|
||
loginSource, err := a.getLoginSource(c, models.LoginDLDAP) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
parseLoginSource(c, loginSource) | ||
if err := parseLdapConfig(c, loginSource.LDAP()); err != nil { | ||
return err | ||
} | ||
|
||
return a.updateLoginSource(loginSource) | ||
} |
Oops, something went wrong.