summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/admin.go6
-rw-r--r--cmd/admin_auth_ldap.go359
-rw-r--r--cmd/admin_auth_ldap_test.go1350
-rw-r--r--docs/content/doc/usage/command-line.en-us.md88
4 files changed, 1802 insertions, 1 deletions
diff --git a/cmd/admin.go b/cmd/admin.go
index 6234ab828d..4c4d6f9b66 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -131,6 +131,10 @@ var (
Subcommands: []cli.Command{
microcmdAuthAddOauth,
microcmdAuthUpdateOauth,
+ cmdAuthAddLdapBindDn,
+ cmdAuthUpdateLdapBindDn,
+ cmdAuthAddLdapSimpleAuth,
+ cmdAuthUpdateLdapSimpleAuth,
microcmdAuthList,
microcmdAuthDelete,
},
@@ -144,7 +148,7 @@ var (
idFlag = cli.Int64Flag{
Name: "id",
- Usage: "ID of OAuth authentication source",
+ Usage: "ID of authentication source",
}
microcmdAuthDelete = cli.Command{
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)
+}
diff --git a/cmd/admin_auth_ldap_test.go b/cmd/admin_auth_ldap_test.go
new file mode 100644
index 0000000000..4af9f167c3
--- /dev/null
+++ b/cmd/admin_auth_ldap_test.go
@@ -0,0 +1,1350 @@
+// 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 (
+ "testing"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/auth/ldap"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/urfave/cli"
+)
+
+func TestAddLdapBindDn(t *testing.T) {
+ // Mock cli functions to do not exit on error
+ var osExiter = cli.OsExiter
+ defer func() { cli.OsExiter = osExiter }()
+ cli.OsExiter = func(code int) {}
+
+ // Test cases
+ var cases = []struct {
+ args []string
+ loginSource *models.LoginSource
+ errMsg string
+ }{
+ // case 0
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (via Bind DN) source full",
+ "--not-active",
+ "--security-protocol", "ldaps",
+ "--skip-tls-verify",
+ "--host", "ldap-bind-server full",
+ "--port", "9876",
+ "--user-search-base", "ou=Users,dc=full-domain-bind,dc=org",
+ "--user-filter", "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)",
+ "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
+ "--username-attribute", "uid-bind full",
+ "--firstname-attribute", "givenName-bind full",
+ "--surname-attribute", "sn-bind full",
+ "--email-attribute", "mail-bind full",
+ "--public-ssh-key-attribute", "publickey-bind full",
+ "--bind-dn", "cn=readonly,dc=full-domain-bind,dc=org",
+ "--bind-password", "secret-bind-full",
+ "--attributes-in-bind",
+ "--synchronize-users",
+ "--page-size", "99",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Name: "ldap (via Bind DN) source full",
+ IsActived: false,
+ IsSyncEnabled: true,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Name: "ldap (via Bind DN) source full",
+ Host: "ldap-bind-server full",
+ Port: 9876,
+ SecurityProtocol: ldap.SecurityProtocol(1),
+ SkipVerify: true,
+ BindDN: "cn=readonly,dc=full-domain-bind,dc=org",
+ BindPassword: "secret-bind-full",
+ UserBase: "ou=Users,dc=full-domain-bind,dc=org",
+ AttributeUsername: "uid-bind full",
+ AttributeName: "givenName-bind full",
+ AttributeSurname: "sn-bind full",
+ AttributeMail: "mail-bind full",
+ AttributesInBind: true,
+ AttributeSSHPublicKey: "publickey-bind full",
+ SearchPageSize: 99,
+ Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)",
+ AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
+ Enabled: true,
+ },
+ },
+ },
+ },
+ // case 1
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (via Bind DN) source min",
+ "--security-protocol", "unencrypted",
+ "--host", "ldap-bind-server min",
+ "--port", "1234",
+ "--user-search-base", "ou=Users,dc=min-domain-bind,dc=org",
+ "--user-filter", "(memberOf=cn=user-group,ou=example,dc=min-domain-bind,dc=org)",
+ "--email-attribute", "mail-bind min",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Name: "ldap (via Bind DN) source min",
+ IsActived: true,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Name: "ldap (via Bind DN) source min",
+ Host: "ldap-bind-server min",
+ Port: 1234,
+ SecurityProtocol: ldap.SecurityProtocol(0),
+ UserBase: "ou=Users,dc=min-domain-bind,dc=org",
+ AttributeMail: "mail-bind min",
+ Filter: "(memberOf=cn=user-group,ou=example,dc=min-domain-bind,dc=org)",
+ Enabled: true,
+ },
+ },
+ },
+ },
+ // case 2
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (via Bind DN) source",
+ "--security-protocol", "zzzzz",
+ "--host", "ldap-server",
+ "--port", "1234",
+ "--user-search-base", "ou=Users,dc=domain,dc=org",
+ "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
+ "--email-attribute", "mail",
+ },
+ errMsg: "Unknown security protocol name: zzzzz",
+ },
+ // case 3
+ {
+ args: []string{
+ "ldap-test",
+ "--security-protocol", "unencrypted",
+ "--host", "ldap-server",
+ "--port", "1234",
+ "--user-search-base", "ou=Users,dc=domain,dc=org",
+ "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
+ "--email-attribute", "mail",
+ },
+ errMsg: "name is not set",
+ },
+ // case 4
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (via Bind DN) source",
+ "--host", "ldap-server",
+ "--port", "1234",
+ "--user-search-base", "ou=Users,dc=domain,dc=org",
+ "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
+ "--email-attribute", "mail",
+ },
+ errMsg: "security-protocol is not set",
+ },
+ // case 5
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (via Bind DN) source",
+ "--security-protocol", "unencrypted",
+ "--port", "1234",
+ "--user-search-base", "ou=Users,dc=domain,dc=org",
+ "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
+ "--email-attribute", "mail",
+ },
+ errMsg: "host is not set",
+ },
+ // case 6
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (via Bind DN) source",
+ "--security-protocol", "unencrypted",
+ "--host", "ldap-server",
+ "--user-search-base", "ou=Users,dc=domain,dc=org",
+ "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
+ "--email-attribute", "mail",
+ },
+ errMsg: "port is not set",
+ },
+ // case 7
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (via Bind DN) source",
+ "--security-protocol", "unencrypted",
+ "--host", "ldap-server",
+ "--port", "1234",
+ "--user-search-base", "ou=Users,dc=domain,dc=org",
+ "--email-attribute", "mail",
+ },
+ errMsg: "user-filter is not set",
+ },
+ // case 8
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (via Bind DN) source",
+ "--security-protocol", "unencrypted",
+ "--host", "ldap-server",
+ "--port", "1234",
+ "--user-search-base", "ou=Users,dc=domain,dc=org",
+ "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
+ },
+ errMsg: "email-attribute is not set",
+ },
+ }
+
+ for n, c := range cases {
+ // Mock functions.
+ var createdLoginSource *models.LoginSource
+ service := &authService{
+ initDB: func() error {
+ return nil
+ },
+ createLoginSource: func(loginSource *models.LoginSource) error {
+ createdLoginSource = loginSource
+ return nil
+ },
+ updateLoginSource: func(loginSource *models.LoginSource) error {
+ assert.FailNow(t, "case %d: should not call updateLoginSource", n)
+ return nil
+ },
+ getLoginSourceByID: func(id int64) (*models.LoginSource, error) {
+ assert.FailNow(t, "case %d: should not call getLoginSourceByID", n)
+ return nil, nil
+ },
+ }
+
+ // Create a copy of command to test
+ app := cli.NewApp()
+ app.Flags = cmdAuthAddLdapBindDn.Flags
+ app.Action = service.addLdapBindDn
+
+ // Run it
+ err := app.Run(c.args)
+ if c.errMsg != "" {
+ assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
+ } else {
+ assert.NoError(t, err, "case %d: should have no errors", n)
+ assert.Equal(t, c.loginSource, createdLoginSource, "case %d: wrong loginSource", n)
+ }
+ }
+}
+
+func TestAddLdapSimpleAuth(t *testing.T) {
+ // Mock cli functions to do not exit on error
+ var osExiter = cli.OsExiter
+ defer func() { cli.OsExiter = osExiter }()
+ cli.OsExiter = func(code int) {}
+
+ // Test cases
+ var cases = []struct {
+ args []string
+ loginSource *models.LoginSource
+ errMsg string
+ }{
+ // case 0
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (simple auth) source full",
+ "--not-active",
+ "--security-protocol", "starttls",
+ "--skip-tls-verify",
+ "--host", "ldap-simple-server full",
+ "--port", "987",
+ "--user-search-base", "ou=Users,dc=full-domain-simple,dc=org",
+ "--user-filter", "(&(objectClass=posixAccount)(full-simple-cn=%s))",
+ "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)",
+ "--username-attribute", "uid-simple full",
+ "--firstname-attribute", "givenName-simple full",
+ "--surname-attribute", "sn-simple full",
+ "--email-attribute", "mail-simple full",
+ "--public-ssh-key-attribute", "publickey-simple full",
+ "--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Name: "ldap (simple auth) source full",
+ IsActived: false,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Name: "ldap (simple auth) source full",
+ Host: "ldap-simple-server full",
+ Port: 987,
+ SecurityProtocol: ldap.SecurityProtocol(2),
+ SkipVerify: true,
+ UserDN: "cn=%s,ou=Users,dc=full-domain-simple,dc=org",
+ UserBase: "ou=Users,dc=full-domain-simple,dc=org",
+ AttributeUsername: "uid-simple full",
+ AttributeName: "givenName-simple full",
+ AttributeSurname: "sn-simple full",
+ AttributeMail: "mail-simple full",
+ AttributeSSHPublicKey: "publickey-simple full",
+ Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))",
+ AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)",
+ Enabled: true,
+ },
+ },
+ },
+ },
+ // case 1
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (simple auth) source min",
+ "--security-protocol", "unencrypted",
+ "--host", "ldap-simple-server min",
+ "--port", "123",
+ "--user-filter", "(&(objectClass=posixAccount)(min-simple-cn=%s))",
+ "--email-attribute", "mail-simple min",
+ "--user-dn", "cn=%s,ou=Users,dc=min-domain-simple,dc=org",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Name: "ldap (simple auth) source min",
+ IsActived: true,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Name: "ldap (simple auth) source min",
+ Host: "ldap-simple-server min",
+ Port: 123,
+ SecurityProtocol: ldap.SecurityProtocol(0),
+ UserDN: "cn=%s,ou=Users,dc=min-domain-simple,dc=org",
+ AttributeMail: "mail-simple min",
+ Filter: "(&(objectClass=posixAccount)(min-simple-cn=%s))",
+ Enabled: true,
+ },
+ },
+ },
+ },
+ // case 2
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (simple auth) source",
+ "--security-protocol", "zzzzz",
+ "--host", "ldap-server",
+ "--port", "123",
+ "--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
+ "--email-attribute", "mail",
+ "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
+ },
+ errMsg: "Unknown security protocol name: zzzzz",
+ },
+ // case 3
+ {
+ args: []string{
+ "ldap-test",
+ "--security-protocol", "unencrypted",
+ "--host", "ldap-server",
+ "--port", "123",
+ "--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
+ "--email-attribute", "mail",
+ "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
+ },
+ errMsg: "name is not set",
+ },
+ // case 4
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (simple auth) source",
+ "--host", "ldap-server",
+ "--port", "123",
+ "--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
+ "--email-attribute", "mail",
+ "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
+ },
+ errMsg: "security-protocol is not set",
+ },
+ // case 5
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (simple auth) source",
+ "--security-protocol", "unencrypted",
+ "--port", "123",
+ "--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
+ "--email-attribute", "mail",
+ "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
+ },
+ errMsg: "host is not set",
+ },
+ // case 6
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (simple auth) source",
+ "--security-protocol", "unencrypted",
+ "--host", "ldap-server",
+ "--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
+ "--email-attribute", "mail",
+ "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
+ },
+ errMsg: "port is not set",
+ },
+ // case 7
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (simple auth) source",
+ "--security-protocol", "unencrypted",
+ "--host", "ldap-server",
+ "--port", "123",
+ "--email-attribute", "mail",
+ "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
+ },
+ errMsg: "user-filter is not set",
+ },
+ // case 8
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (simple auth) source",
+ "--security-protocol", "unencrypted",
+ "--host", "ldap-server",
+ "--port", "123",
+ "--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
+ "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
+ },
+ errMsg: "email-attribute is not set",
+ },
+ // case 9
+ {
+ args: []string{
+ "ldap-test",
+ "--name", "ldap (simple auth) source",
+ "--security-protocol", "unencrypted",
+ "--host", "ldap-server",
+ "--port", "123",
+ "--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
+ "--email-attribute", "mail",
+ },
+ errMsg: "user-dn is not set",
+ },
+ }
+
+ for n, c := range cases {
+ // Mock functions.
+ var createdLoginSource *models.LoginSource
+ service := &authService{
+ initDB: func() error {
+ return nil
+ },
+ createLoginSource: func(loginSource *models.LoginSource) error {
+ createdLoginSource = loginSource
+ return nil
+ },
+ updateLoginSource: func(loginSource *models.LoginSource) error {
+ assert.FailNow(t, "case %d: should not call updateLoginSource", n)
+ return nil
+ },
+ getLoginSourceByID: func(id int64) (*models.LoginSource, error) {
+ assert.FailNow(t, "case %d: should not call getLoginSourceByID", n)
+ return nil, nil
+ },
+ }
+
+ // Create a copy of command to test
+ app := cli.NewApp()
+ app.Flags = cmdAuthAddLdapSimpleAuth.Flags
+ app.Action = service.addLdapSimpleAuth
+
+ // Run it
+ err := app.Run(c.args)
+ if c.errMsg != "" {
+ assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
+ } else {
+ assert.NoError(t, err, "case %d: should have no errors", n)
+ assert.Equal(t, c.loginSource, createdLoginSource, "case %d: wrong loginSource", n)
+ }
+ }
+}
+
+func TestUpdateLdapBindDn(t *testing.T) {
+ // Mock cli functions to do not exit on error
+ var osExiter = cli.OsExiter
+ defer func() { cli.OsExiter = osExiter }()
+ cli.OsExiter = func(code int) {}
+
+ // Test cases
+ var cases = []struct {
+ args []string
+ id int64
+ existingLoginSource *models.LoginSource
+ loginSource *models.LoginSource
+ errMsg string
+ }{
+ // case 0
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "23",
+ "--name", "ldap (via Bind DN) source full",
+ "--not-active",
+ "--security-protocol", "LDAPS",
+ "--skip-tls-verify",
+ "--host", "ldap-bind-server full",
+ "--port", "9876",
+ "--user-search-base", "ou=Users,dc=full-domain-bind,dc=org",
+ "--user-filter", "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)",
+ "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
+ "--username-attribute", "uid-bind full",
+ "--firstname-attribute", "givenName-bind full",
+ "--surname-attribute", "sn-bind full",
+ "--email-attribute", "mail-bind full",
+ "--public-ssh-key-attribute", "publickey-bind full",
+ "--bind-dn", "cn=readonly,dc=full-domain-bind,dc=org",
+ "--bind-password", "secret-bind-full",
+ "--synchronize-users",
+ "--page-size", "99",
+ },
+ id: 23,
+ existingLoginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ IsActived: true,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Enabled: true,
+ },
+ },
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Name: "ldap (via Bind DN) source full",
+ IsActived: false,
+ IsSyncEnabled: true,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Name: "ldap (via Bind DN) source full",
+ Host: "ldap-bind-server full",
+ Port: 9876,
+ SecurityProtocol: ldap.SecurityProtocol(1),
+ SkipVerify: true,
+ BindDN: "cn=readonly,dc=full-domain-bind,dc=org",
+ BindPassword: "secret-bind-full",
+ UserBase: "ou=Users,dc=full-domain-bind,dc=org",
+ AttributeUsername: "uid-bind full",
+ AttributeName: "givenName-bind full",
+ AttributeSurname: "sn-bind full",
+ AttributeMail: "mail-bind full",
+ AttributesInBind: false,
+ AttributeSSHPublicKey: "publickey-bind full",
+ SearchPageSize: 99,
+ Filter: "(memberOf=cn=user-group,ou=example,dc=full-domain-bind,dc=org)",
+ AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
+ Enabled: true,
+ },
+ },
+ },
+ },
+ // case 1
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{},
+ },
+ },
+ },
+ // case 2
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--name", "ldap (via Bind DN) source",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Name: "ldap (via Bind DN) source",
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Name: "ldap (via Bind DN) source",
+ },
+ },
+ },
+ },
+ // case 3
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--not-active",
+ },
+ existingLoginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ IsActived: true,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{},
+ },
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ IsActived: false,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{},
+ },
+ },
+ },
+ // case 4
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--security-protocol", "LDAPS",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ SecurityProtocol: ldap.SecurityProtocol(1),
+ },
+ },
+ },
+ },
+ // case 5
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--skip-tls-verify",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ SkipVerify: true,
+ },
+ },
+ },
+ },
+ // case 6
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--host", "ldap-server",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Host: "ldap-server",
+ },
+ },
+ },
+ },
+ // case 7
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--port", "389",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Port: 389,
+ },
+ },
+ },
+ },
+ // case 8
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--user-search-base", "ou=Users,dc=domain,dc=org",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ UserBase: "ou=Users,dc=domain,dc=org",
+ },
+ },
+ },
+ },
+ // case 9
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Filter: "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
+ },
+ },
+ },
+ },
+ // case 10
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
+ },
+ },
+ },
+ },
+ // case 11
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--username-attribute", "uid",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AttributeUsername: "uid",
+ },
+ },
+ },
+ },
+ // case 12
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--firstname-attribute", "givenName",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AttributeName: "givenName",
+ },
+ },
+ },
+ },
+ // case 13
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--surname-attribute", "sn",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AttributeSurname: "sn",
+ },
+ },
+ },
+ },
+ // case 14
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--email-attribute", "mail",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AttributeMail: "mail",
+ },
+ },
+ },
+ },
+ // case 15
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--attributes-in-bind",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AttributesInBind: true,
+ },
+ },
+ },
+ },
+ // case 16
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--public-ssh-key-attribute", "publickey",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AttributeSSHPublicKey: "publickey",
+ },
+ },
+ },
+ },
+ // case 17
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--bind-dn", "cn=readonly,dc=domain,dc=org",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ BindDN: "cn=readonly,dc=domain,dc=org",
+ },
+ },
+ },
+ },
+ // case 18
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--bind-password", "secret",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ BindPassword: "secret",
+ },
+ },
+ },
+ },
+ // case 19
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--synchronize-users",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ IsSyncEnabled: true,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{},
+ },
+ },
+ },
+ // case 20
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--page-size", "12",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ SearchPageSize: 12,
+ },
+ },
+ },
+ },
+ // case 21
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--security-protocol", "xxxxx",
+ },
+ errMsg: "Unknown security protocol name: xxxxx",
+ },
+ // case 22
+ {
+ args: []string{
+ "ldap-test",
+ },
+ errMsg: "id is not set",
+ },
+ // case 23
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ },
+ existingLoginSource: &models.LoginSource{
+ Type: models.LoginOAuth2,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{},
+ },
+ },
+ errMsg: "Invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2",
+ },
+ }
+
+ for n, c := range cases {
+ // Mock functions.
+ var updatedLoginSource *models.LoginSource
+ service := &authService{
+ initDB: func() error {
+ return nil
+ },
+ createLoginSource: func(loginSource *models.LoginSource) error {
+ assert.FailNow(t, "case %d: should not call createLoginSource", n)
+ return nil
+ },
+ updateLoginSource: func(loginSource *models.LoginSource) error {
+ updatedLoginSource = loginSource
+ return nil
+ },
+ getLoginSourceByID: func(id int64) (*models.LoginSource, error) {
+ if c.id != 0 {
+ assert.Equal(t, c.id, id, "case %d: wrong id", n)
+ }
+ if c.existingLoginSource != nil {
+ return c.existingLoginSource, nil
+ }
+ return &models.LoginSource{
+ Type: models.LoginLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{},
+ },
+ }, nil
+ },
+ }
+
+ // Create a copy of command to test
+ app := cli.NewApp()
+ app.Flags = cmdAuthUpdateLdapBindDn.Flags
+ app.Action = service.updateLdapBindDn
+
+ // Run it
+ err := app.Run(c.args)
+ if c.errMsg != "" {
+ assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
+ } else {
+ assert.NoError(t, err, "case %d: should have no errors", n)
+ assert.Equal(t, c.loginSource, updatedLoginSource, "case %d: wrong loginSource", n)
+ }
+ }
+}
+
+func TestUpdateLdapSimpleAuth(t *testing.T) {
+ // Mock cli functions to do not exit on error
+ var osExiter = cli.OsExiter
+ defer func() { cli.OsExiter = osExiter }()
+ cli.OsExiter = func(code int) {}
+
+ // Test cases
+ var cases = []struct {
+ args []string
+ id int64
+ existingLoginSource *models.LoginSource
+ loginSource *models.LoginSource
+ errMsg string
+ }{
+ // case 0
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "7",
+ "--name", "ldap (simple auth) source full",
+ "--not-active",
+ "--security-protocol", "starttls",
+ "--skip-tls-verify",
+ "--host", "ldap-simple-server full",
+ "--port", "987",
+ "--user-search-base", "ou=Users,dc=full-domain-simple,dc=org",
+ "--user-filter", "(&(objectClass=posixAccount)(full-simple-cn=%s))",
+ "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)",
+ "--username-attribute", "uid-simple full",
+ "--firstname-attribute", "givenName-simple full",
+ "--surname-attribute", "sn-simple full",
+ "--email-attribute", "mail-simple full",
+ "--public-ssh-key-attribute", "publickey-simple full",
+ "--user-dn", "cn=%s,ou=Users,dc=full-domain-simple,dc=org",
+ },
+ id: 7,
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Name: "ldap (simple auth) source full",
+ IsActived: false,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Name: "ldap (simple auth) source full",
+ Host: "ldap-simple-server full",
+ Port: 987,
+ SecurityProtocol: ldap.SecurityProtocol(2),
+ SkipVerify: true,
+ UserDN: "cn=%s,ou=Users,dc=full-domain-simple,dc=org",
+ UserBase: "ou=Users,dc=full-domain-simple,dc=org",
+ AttributeUsername: "uid-simple full",
+ AttributeName: "givenName-simple full",
+ AttributeSurname: "sn-simple full",
+ AttributeMail: "mail-simple full",
+ AttributeSSHPublicKey: "publickey-simple full",
+ Filter: "(&(objectClass=posixAccount)(full-simple-cn=%s))",
+ AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-simple,dc=org)",
+ },
+ },
+ },
+ },
+ // case 1
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{},
+ },
+ },
+ },
+ // case 2
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--name", "ldap (simple auth) source",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Name: "ldap (simple auth) source",
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Name: "ldap (simple auth) source",
+ },
+ },
+ },
+ },
+ // case 3
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--not-active",
+ },
+ existingLoginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ IsActived: true,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{},
+ },
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ IsActived: false,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{},
+ },
+ },
+ },
+ // case 4
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--security-protocol", "starttls",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ SecurityProtocol: ldap.SecurityProtocol(2),
+ },
+ },
+ },
+ },
+ // case 5
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--skip-tls-verify",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ SkipVerify: true,
+ },
+ },
+ },
+ },
+ // case 6
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--host", "ldap-server",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Host: "ldap-server",
+ },
+ },
+ },
+ },
+ // case 7
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--port", "987",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Port: 987,
+ },
+ },
+ },
+ },
+ // case 8
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--user-search-base", "ou=Users,dc=domain,dc=org",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ UserBase: "ou=Users,dc=domain,dc=org",
+ },
+ },
+ },
+ },
+ // case 9
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ Filter: "(&(objectClass=posixAccount)(cn=%s))",
+ },
+ },
+ },
+ },
+ // case 10
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
+ },
+ },
+ },
+ },
+ // case 11
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--username-attribute", "uid",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AttributeUsername: "uid",
+ },
+ },
+ },
+ },
+ // case 12
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--firstname-attribute", "givenName",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AttributeName: "givenName",
+ },
+ },
+ },
+ },
+ // case 13
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--surname-attribute", "sn",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AttributeSurname: "sn",
+ },
+ },
+ },
+ },
+ // case 14
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--email-attribute", "mail",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AttributeMail: "mail",
+ },
+ },
+ },
+ },
+ // case 15
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--public-ssh-key-attribute", "publickey",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ AttributeSSHPublicKey: "publickey",
+ },
+ },
+ },
+ },
+ // case 16
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
+ },
+ loginSource: &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{
+ UserDN: "cn=%s,ou=Users,dc=domain,dc=org",
+ },
+ },
+ },
+ },
+ // case 17
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ "--security-protocol", "xxxxx",
+ },
+ errMsg: "Unknown security protocol name: xxxxx",
+ },
+ // case 18
+ {
+ args: []string{
+ "ldap-test",
+ },
+ errMsg: "id is not set",
+ },
+ // case 19
+ {
+ args: []string{
+ "ldap-test",
+ "--id", "1",
+ },
+ existingLoginSource: &models.LoginSource{
+ Type: models.LoginPAM,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{},
+ },
+ },
+ errMsg: "Invalid authentication type. expected: LDAP (simple auth), actual: PAM",
+ },
+ }
+
+ for n, c := range cases {
+ // Mock functions.
+ var updatedLoginSource *models.LoginSource
+ service := &authService{
+ initDB: func() error {
+ return nil
+ },
+ createLoginSource: func(loginSource *models.LoginSource) error {
+ assert.FailNow(t, "case %d: should not call createLoginSource", n)
+ return nil
+ },
+ updateLoginSource: func(loginSource *models.LoginSource) error {
+ updatedLoginSource = loginSource
+ return nil
+ },
+ getLoginSourceByID: func(id int64) (*models.LoginSource, error) {
+ if c.id != 0 {
+ assert.Equal(t, c.id, id, "case %d: wrong id", n)
+ }
+ if c.existingLoginSource != nil {
+ return c.existingLoginSource, nil
+ }
+ return &models.LoginSource{
+ Type: models.LoginDLDAP,
+ Cfg: &models.LDAPConfig{
+ Source: &ldap.Source{},
+ },
+ }, nil
+ },
+ }
+
+ // Create a copy of command to test
+ app := cli.NewApp()
+ app.Flags = cmdAuthUpdateLdapSimpleAuth.Flags
+ app.Action = service.updateLdapSimpleAuth
+
+ // Run it
+ err := app.Run(c.args)
+ if c.errMsg != "" {
+ assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
+ } else {
+ assert.NoError(t, err, "case %d: should have no errors", n)
+ assert.Equal(t, c.loginSource, updatedLoginSource, "case %d: wrong loginSource", n)
+ }
+ }
+}
diff --git a/docs/content/doc/usage/command-line.en-us.md b/docs/content/doc/usage/command-line.en-us.md
index 516e46ff0b..0955680af2 100644
--- a/docs/content/doc/usage/command-line.en-us.md
+++ b/docs/content/doc/usage/command-line.en-us.md
@@ -123,6 +123,94 @@ Admin operations:
- `--custom-email-url`: Use a custom Email URL (option for GitHub).
- Examples:
- `gitea admin auth update-oauth --id 1 --name external-github-updated`
+ - `add-ldap`: Add new LDAP (via Bind DN) authentication source
+ - Options:
+ - `--name value`: Authentication name. Required.
+ - `--not-active`: Deactivate the authentication source.
+ - `--security-protocol value`: Security protocol name. Required.
+ - `--skip-tls-verify`: Disable TLS verification.
+ - `--host value`: The address where the LDAP server can be reached. Required.
+ - `--port value`: The port to use when connecting to the LDAP server. Required.
+ - `--user-search-base value`: The LDAP base at which user accounts will be searched for. Required.
+ - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. Required.
+ - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
+ - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
+ - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
+ - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
+ - `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address. Required.
+ - `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
+ - `--bind-dn value`: The DN to bind to the LDAP server with when searching for the user.
+ - `--bind-password value`: The password for the Bind DN, if any.
+ - `--attributes-in-bind`: Fetch attributes in bind DN context.
+ - `--synchronize-users`: Enable user synchronization.
+ - `--page-size value`: Search page size.
+ - Examples:
+ - `gitea admin auth add-ldap --name ldap --security-protocol unencrypted --host mydomain.org --port 389 --user-search-base "ou=Users,dc=mydomain,dc=org" --user-filter "(&(objectClass=posixAccount)(uid=%s))" --email-attribute mail`
+ - `update-ldap`: Update existing LDAP (via Bind DN) authentication source
+ - Options:
+ - `--id value`: ID of authentication source. Required.
+ - `--name value`: Authentication name.
+ - `--not-active`: Deactivate the authentication source.
+ - `--security-protocol value`: Security protocol name.
+ - `--skip-tls-verify`: Disable TLS verification.
+ - `--host value`: The address where the LDAP server can be reached.
+ - `--port value`: The port to use when connecting to the LDAP server.
+ - `--user-search-base value`: The LDAP base at which user accounts will be searched for.
+ - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate.
+ - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
+ - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
+ - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
+ - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
+ - `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address.
+ - `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
+ - `--bind-dn value`: The DN to bind to the LDAP server with when searching for the user.
+ - `--bind-password value`: The password for the Bind DN, if any.
+ - `--attributes-in-bind`: Fetch attributes in bind DN context.
+ - `--synchronize-users`: Enable user synchronization.
+ - `--page-size value`: Search page size.
+ - Examples:
+ - `gitea admin auth update-ldap --id 1 --name "my ldap auth source"`
+ - `gitea admin auth update-ldap --id 1 --username-attribute uid --firstname-attribute givenName --surname-attribute sn`
+ - `add-ldap-simple`: Add new LDAP (simple auth) authentication source
+ - Options:
+ - `--name value`: Authentication name. Required.
+ - `--not-active`: Deactivate the authentication source.
+ - `--security-protocol value`: Security protocol name. Required.
+ - `--skip-tls-verify`: Disable TLS verification.
+ - `--host value`: The address where the LDAP server can be reached. Required.
+ - `--port value`: The port to use when connecting to the LDAP server. Required.
+ - `--user-search-base value`: The LDAP base at which user accounts will be searched for.
+ - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate. Required.
+ - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
+ - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
+ - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
+ - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
+ - `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address. Required.
+ - `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
+ - `--user-dn value`: The user’s DN. Required.
+ - Examples:
+ - `gitea admin auth add-ldap-simple --name ldap --security-protocol unencrypted --host mydomain.org --port 389 --user-dn "cn=%s,ou=Users,dc=mydomain,dc=org" --user-filter "(&(objectClass=posixAccount)(cn=%s))" --email-attribute mail`
+ - `update-ldap-simple`: Update existing LDAP (simple auth) authentication source
+ - Options:
+ - `--id value`: ID of authentication source. Required.
+ - `--name value`: Authentication name.
+ - `--not-active`: Deactivate the authentication source.
+ - `--security-protocol value`: Security protocol name.
+ - `--skip-tls-verify`: Disable TLS verification.
+ - `--host value`: The address where the LDAP server can be reached.
+ - `--port value`: The port to use when connecting to the LDAP server.
+ - `--user-search-base value`: The LDAP base at which user accounts will be searched for.
+ - `--user-filter value`: An LDAP filter declaring how to find the user record that is attempting to authenticate.
+ - `--admin-filter value`: An LDAP filter specifying if a user should be given administrator privileges.
+ - `--username-attribute value`: The attribute of the user’s LDAP record containing the user name.
+ - `--firstname-attribute value`: The attribute of the user’s LDAP record containing the user’s first name.
+ - `--surname-attribute value`: The attribute of the user’s LDAP record containing the user’s surname.
+ - `--email-attribute value`: The attribute of the user’s LDAP record containing the user’s email address.
+ - `--public-ssh-key-attribute value`: The attribute of the user’s LDAP record containing the user’s public ssh key.
+ - `--user-dn value`: The user’s DN.
+ - Examples:
+ - `gitea admin auth update-ldap-simple --id 1 --name "my ldap auth source"`
+ - `gitea admin auth update-ldap-simple --id 1 --username-attribute uid --firstname-attribute givenName --surname-attribute sn`
#### cert