diff options
author | ngourdon <ngourdon@gmail.com> | 2019-06-17 20:32:20 +0200 |
---|---|---|
committer | techknowlogick <hello@techknowlogick.com> | 2019-06-17 14:32:20 -0400 |
commit | a618df8d8474efb9148d29c4ce6cf8ec14cfca7a (patch) | |
tree | 439f8f1c96693cba29b7a598ae5bd2ad21a4686c /cmd/admin_auth_ldap.go | |
parent | a200ca15519d6f23a0b64c062f23000c347ce7b6 (diff) | |
download | gitea-a618df8d8474efb9148d29c4ce6cf8ec14cfca7a.tar.gz gitea-a618df8d8474efb9148d29c4ce6cf8ec14cfca7a.zip |
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 <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
Diffstat (limited to 'cmd/admin_auth_ldap.go')
-rw-r--r-- | cmd/admin_auth_ldap.go | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/cmd/admin_auth_ldap.go b/cmd/admin_auth_ldap.go new file mode 100644 index 0000000000..cce3aa894f --- /dev/null +++ b/cmd/admin_auth_ldap.go @@ -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) +} |