2019-06-17 21:32:20 +03:00
// 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." ,
} ,
2020-03-05 09:30:33 +03:00
cli . StringFlag {
Name : "restricted-filter" ,
Usage : "An LDAP filter specifying if a user should be given restricted status." ,
} ,
2020-01-20 06:47:39 +03:00
cli . BoolFlag {
Name : "allow-deactivate-all" ,
Usage : "Allow empty search results to deactivate all users." ,
} ,
2019-06-17 21:32:20 +03:00
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" )
}
2020-03-05 09:30:33 +03:00
if c . IsSet ( "restricted-filter" ) {
config . Source . RestrictedFilter = c . String ( "restricted-filter" )
}
2020-01-20 06:47:39 +03:00
if c . IsSet ( "allow-deactivate-all" ) {
config . Source . AllowDeactivateAll = c . Bool ( "allow-deactivate-all" )
}
2019-06-17 21:32:20 +03:00
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 )
}