You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

admin_auth_ldap.go 10KB

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