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.

558 lines
13 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. // Copyright 2014 The Gogs 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 models
  5. import (
  6. "crypto/tls"
  7. "encoding/json"
  8. "errors"
  9. "fmt"
  10. "net/smtp"
  11. "net/textproto"
  12. "strings"
  13. "time"
  14. "github.com/Unknwon/com"
  15. "github.com/go-macaron/binding"
  16. "github.com/go-xorm/core"
  17. "github.com/go-xorm/xorm"
  18. "github.com/go-gitea/gitea/modules/auth/ldap"
  19. "github.com/go-gitea/gitea/modules/auth/pam"
  20. "github.com/go-gitea/gitea/modules/log"
  21. )
  22. type LoginType int
  23. // Note: new type must append to the end of list to maintain compatibility.
  24. const (
  25. LOGIN_NOTYPE LoginType = iota
  26. LOGIN_PLAIN // 1
  27. LOGIN_LDAP // 2
  28. LOGIN_SMTP // 3
  29. LOGIN_PAM // 4
  30. LOGIN_DLDAP // 5
  31. )
  32. var LoginNames = map[LoginType]string{
  33. LOGIN_LDAP: "LDAP (via BindDN)",
  34. LOGIN_DLDAP: "LDAP (simple auth)", // Via direct bind
  35. LOGIN_SMTP: "SMTP",
  36. LOGIN_PAM: "PAM",
  37. }
  38. var SecurityProtocolNames = map[ldap.SecurityProtocol]string{
  39. ldap.SECURITY_PROTOCOL_UNENCRYPTED: "Unencrypted",
  40. ldap.SECURITY_PROTOCOL_LDAPS: "LDAPS",
  41. ldap.SECURITY_PROTOCOL_START_TLS: "StartTLS",
  42. }
  43. // Ensure structs implemented interface.
  44. var (
  45. _ core.Conversion = &LDAPConfig{}
  46. _ core.Conversion = &SMTPConfig{}
  47. _ core.Conversion = &PAMConfig{}
  48. )
  49. type LDAPConfig struct {
  50. *ldap.Source
  51. }
  52. func (cfg *LDAPConfig) FromDB(bs []byte) error {
  53. return json.Unmarshal(bs, &cfg)
  54. }
  55. func (cfg *LDAPConfig) ToDB() ([]byte, error) {
  56. return json.Marshal(cfg)
  57. }
  58. func (cfg *LDAPConfig) SecurityProtocolName() string {
  59. return SecurityProtocolNames[cfg.SecurityProtocol]
  60. }
  61. type SMTPConfig struct {
  62. Auth string
  63. Host string
  64. Port int
  65. AllowedDomains string `xorm:"TEXT"`
  66. TLS bool
  67. SkipVerify bool
  68. }
  69. func (cfg *SMTPConfig) FromDB(bs []byte) error {
  70. return json.Unmarshal(bs, cfg)
  71. }
  72. func (cfg *SMTPConfig) ToDB() ([]byte, error) {
  73. return json.Marshal(cfg)
  74. }
  75. type PAMConfig struct {
  76. ServiceName string // pam service (e.g. system-auth)
  77. }
  78. func (cfg *PAMConfig) FromDB(bs []byte) error {
  79. return json.Unmarshal(bs, &cfg)
  80. }
  81. func (cfg *PAMConfig) ToDB() ([]byte, error) {
  82. return json.Marshal(cfg)
  83. }
  84. // LoginSource represents an external way for authorizing users.
  85. type LoginSource struct {
  86. ID int64 `xorm:"pk autoincr"`
  87. Type LoginType
  88. Name string `xorm:"UNIQUE"`
  89. IsActived bool `xorm:"NOT NULL DEFAULT false"`
  90. Cfg core.Conversion `xorm:"TEXT"`
  91. Created time.Time `xorm:"-"`
  92. CreatedUnix int64
  93. Updated time.Time `xorm:"-"`
  94. UpdatedUnix int64
  95. }
  96. func (s *LoginSource) BeforeInsert() {
  97. s.CreatedUnix = time.Now().Unix()
  98. s.UpdatedUnix = s.CreatedUnix
  99. }
  100. func (s *LoginSource) BeforeUpdate() {
  101. s.UpdatedUnix = time.Now().Unix()
  102. }
  103. // Cell2Int64 converts a xorm.Cell type to int64,
  104. // and handles possible irregular cases.
  105. func Cell2Int64(val xorm.Cell) int64 {
  106. switch (*val).(type) {
  107. case []uint8:
  108. log.Trace("Cell2Int64 ([]uint8): %v", *val)
  109. return com.StrTo(string((*val).([]uint8))).MustInt64()
  110. }
  111. return (*val).(int64)
  112. }
  113. func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
  114. switch colName {
  115. case "type":
  116. switch LoginType(Cell2Int64(val)) {
  117. case LOGIN_LDAP, LOGIN_DLDAP:
  118. source.Cfg = new(LDAPConfig)
  119. case LOGIN_SMTP:
  120. source.Cfg = new(SMTPConfig)
  121. case LOGIN_PAM:
  122. source.Cfg = new(PAMConfig)
  123. default:
  124. panic("unrecognized login source type: " + com.ToStr(*val))
  125. }
  126. }
  127. }
  128. func (s *LoginSource) AfterSet(colName string, _ xorm.Cell) {
  129. switch colName {
  130. case "created_unix":
  131. s.Created = time.Unix(s.CreatedUnix, 0).Local()
  132. case "updated_unix":
  133. s.Updated = time.Unix(s.UpdatedUnix, 0).Local()
  134. }
  135. }
  136. func (source *LoginSource) TypeName() string {
  137. return LoginNames[source.Type]
  138. }
  139. func (source *LoginSource) IsLDAP() bool {
  140. return source.Type == LOGIN_LDAP
  141. }
  142. func (source *LoginSource) IsDLDAP() bool {
  143. return source.Type == LOGIN_DLDAP
  144. }
  145. func (source *LoginSource) IsSMTP() bool {
  146. return source.Type == LOGIN_SMTP
  147. }
  148. func (source *LoginSource) IsPAM() bool {
  149. return source.Type == LOGIN_PAM
  150. }
  151. func (source *LoginSource) HasTLS() bool {
  152. return ((source.IsLDAP() || source.IsDLDAP()) &&
  153. source.LDAP().SecurityProtocol > ldap.SECURITY_PROTOCOL_UNENCRYPTED) ||
  154. source.IsSMTP()
  155. }
  156. func (source *LoginSource) UseTLS() bool {
  157. switch source.Type {
  158. case LOGIN_LDAP, LOGIN_DLDAP:
  159. return source.LDAP().SecurityProtocol != ldap.SECURITY_PROTOCOL_UNENCRYPTED
  160. case LOGIN_SMTP:
  161. return source.SMTP().TLS
  162. }
  163. return false
  164. }
  165. func (source *LoginSource) SkipVerify() bool {
  166. switch source.Type {
  167. case LOGIN_LDAP, LOGIN_DLDAP:
  168. return source.LDAP().SkipVerify
  169. case LOGIN_SMTP:
  170. return source.SMTP().SkipVerify
  171. }
  172. return false
  173. }
  174. func (source *LoginSource) LDAP() *LDAPConfig {
  175. return source.Cfg.(*LDAPConfig)
  176. }
  177. func (source *LoginSource) SMTP() *SMTPConfig {
  178. return source.Cfg.(*SMTPConfig)
  179. }
  180. func (source *LoginSource) PAM() *PAMConfig {
  181. return source.Cfg.(*PAMConfig)
  182. }
  183. func CreateLoginSource(source *LoginSource) error {
  184. has, err := x.Get(&LoginSource{Name: source.Name})
  185. if err != nil {
  186. return err
  187. } else if has {
  188. return ErrLoginSourceAlreadyExist{source.Name}
  189. }
  190. _, err = x.Insert(source)
  191. return err
  192. }
  193. func LoginSources() ([]*LoginSource, error) {
  194. auths := make([]*LoginSource, 0, 5)
  195. return auths, x.Find(&auths)
  196. }
  197. // GetLoginSourceByID returns login source by given ID.
  198. func GetLoginSourceByID(id int64) (*LoginSource, error) {
  199. source := new(LoginSource)
  200. has, err := x.Id(id).Get(source)
  201. if err != nil {
  202. return nil, err
  203. } else if !has {
  204. return nil, ErrLoginSourceNotExist{id}
  205. }
  206. return source, nil
  207. }
  208. func UpdateSource(source *LoginSource) error {
  209. _, err := x.Id(source.ID).AllCols().Update(source)
  210. return err
  211. }
  212. func DeleteSource(source *LoginSource) error {
  213. count, err := x.Count(&User{LoginSource: source.ID})
  214. if err != nil {
  215. return err
  216. } else if count > 0 {
  217. return ErrLoginSourceInUse{source.ID}
  218. }
  219. _, err = x.Id(source.ID).Delete(new(LoginSource))
  220. return err
  221. }
  222. // CountLoginSources returns number of login sources.
  223. func CountLoginSources() int64 {
  224. count, _ := x.Count(new(LoginSource))
  225. return count
  226. }
  227. // .____ ________ _____ __________
  228. // | | \______ \ / _ \\______ \
  229. // | | | | \ / /_\ \| ___/
  230. // | |___ | ` \/ | \ |
  231. // |_______ \/_______ /\____|__ /____|
  232. // \/ \/ \/
  233. func composeFullName(firstname, surname, username string) string {
  234. switch {
  235. case len(firstname) == 0 && len(surname) == 0:
  236. return username
  237. case len(firstname) == 0:
  238. return surname
  239. case len(surname) == 0:
  240. return firstname
  241. default:
  242. return firstname + " " + surname
  243. }
  244. }
  245. // LoginViaLDAP queries if login/password is valid against the LDAP directory pool,
  246. // and create a local user if success when enabled.
  247. func LoginViaLDAP(user *User, login, passowrd string, source *LoginSource, autoRegister bool) (*User, error) {
  248. username, fn, sn, mail, isAdmin, succeed := source.Cfg.(*LDAPConfig).SearchEntry(login, passowrd, source.Type == LOGIN_DLDAP)
  249. if !succeed {
  250. // User not in LDAP, do nothing
  251. return nil, ErrUserNotExist{0, login}
  252. }
  253. if !autoRegister {
  254. return user, nil
  255. }
  256. // Fallback.
  257. if len(username) == 0 {
  258. username = login
  259. }
  260. // Validate username make sure it satisfies requirement.
  261. if binding.AlphaDashDotPattern.MatchString(username) {
  262. return nil, fmt.Errorf("Invalid pattern for attribute 'username' [%s]: must be valid alpha or numeric or dash(-_) or dot characters", username)
  263. }
  264. if len(mail) == 0 {
  265. mail = fmt.Sprintf("%s@localhost", username)
  266. }
  267. user = &User{
  268. LowerName: strings.ToLower(username),
  269. Name: username,
  270. FullName: composeFullName(fn, sn, username),
  271. Email: mail,
  272. LoginType: source.Type,
  273. LoginSource: source.ID,
  274. LoginName: login,
  275. IsActive: true,
  276. IsAdmin: isAdmin,
  277. }
  278. return user, CreateUser(user)
  279. }
  280. // _________ __________________________
  281. // / _____/ / \__ ___/\______ \
  282. // \_____ \ / \ / \| | | ___/
  283. // / \/ Y \ | | |
  284. // /_______ /\____|__ /____| |____|
  285. // \/ \/
  286. type smtpLoginAuth struct {
  287. username, password string
  288. }
  289. func (auth *smtpLoginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
  290. return "LOGIN", []byte(auth.username), nil
  291. }
  292. func (auth *smtpLoginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
  293. if more {
  294. switch string(fromServer) {
  295. case "Username:":
  296. return []byte(auth.username), nil
  297. case "Password:":
  298. return []byte(auth.password), nil
  299. }
  300. }
  301. return nil, nil
  302. }
  303. const (
  304. SMTP_PLAIN = "PLAIN"
  305. SMTP_LOGIN = "LOGIN"
  306. )
  307. var SMTPAuths = []string{SMTP_PLAIN, SMTP_LOGIN}
  308. func SMTPAuth(a smtp.Auth, cfg *SMTPConfig) error {
  309. c, err := smtp.Dial(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))
  310. if err != nil {
  311. return err
  312. }
  313. defer c.Close()
  314. if err = c.Hello("gogs"); err != nil {
  315. return err
  316. }
  317. if cfg.TLS {
  318. if ok, _ := c.Extension("STARTTLS"); ok {
  319. if err = c.StartTLS(&tls.Config{
  320. InsecureSkipVerify: cfg.SkipVerify,
  321. ServerName: cfg.Host,
  322. }); err != nil {
  323. return err
  324. }
  325. } else {
  326. return errors.New("SMTP server unsupports TLS")
  327. }
  328. }
  329. if ok, _ := c.Extension("AUTH"); ok {
  330. if err = c.Auth(a); err != nil {
  331. return err
  332. }
  333. return nil
  334. }
  335. return ErrUnsupportedLoginType
  336. }
  337. // LoginViaSMTP queries if login/password is valid against the SMTP,
  338. // and create a local user if success when enabled.
  339. func LoginViaSMTP(user *User, login, password string, sourceID int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
  340. // Verify allowed domains.
  341. if len(cfg.AllowedDomains) > 0 {
  342. idx := strings.Index(login, "@")
  343. if idx == -1 {
  344. return nil, ErrUserNotExist{0, login}
  345. } else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), login[idx+1:]) {
  346. return nil, ErrUserNotExist{0, login}
  347. }
  348. }
  349. var auth smtp.Auth
  350. if cfg.Auth == SMTP_PLAIN {
  351. auth = smtp.PlainAuth("", login, password, cfg.Host)
  352. } else if cfg.Auth == SMTP_LOGIN {
  353. auth = &smtpLoginAuth{login, password}
  354. } else {
  355. return nil, errors.New("Unsupported SMTP auth type")
  356. }
  357. if err := SMTPAuth(auth, cfg); err != nil {
  358. // Check standard error format first,
  359. // then fallback to worse case.
  360. tperr, ok := err.(*textproto.Error)
  361. if (ok && tperr.Code == 535) ||
  362. strings.Contains(err.Error(), "Username and Password not accepted") {
  363. return nil, ErrUserNotExist{0, login}
  364. }
  365. return nil, err
  366. }
  367. if !autoRegister {
  368. return user, nil
  369. }
  370. username := login
  371. idx := strings.Index(login, "@")
  372. if idx > -1 {
  373. username = login[:idx]
  374. }
  375. user = &User{
  376. LowerName: strings.ToLower(username),
  377. Name: strings.ToLower(username),
  378. Email: login,
  379. Passwd: password,
  380. LoginType: LOGIN_SMTP,
  381. LoginSource: sourceID,
  382. LoginName: login,
  383. IsActive: true,
  384. }
  385. return user, CreateUser(user)
  386. }
  387. // __________ _____ _____
  388. // \______ \/ _ \ / \
  389. // | ___/ /_\ \ / \ / \
  390. // | | / | \/ Y \
  391. // |____| \____|__ /\____|__ /
  392. // \/ \/
  393. // LoginViaPAM queries if login/password is valid against the PAM,
  394. // and create a local user if success when enabled.
  395. func LoginViaPAM(user *User, login, password string, sourceID int64, cfg *PAMConfig, autoRegister bool) (*User, error) {
  396. if err := pam.PAMAuth(cfg.ServiceName, login, password); err != nil {
  397. if strings.Contains(err.Error(), "Authentication failure") {
  398. return nil, ErrUserNotExist{0, login}
  399. }
  400. return nil, err
  401. }
  402. if !autoRegister {
  403. return user, nil
  404. }
  405. user = &User{
  406. LowerName: strings.ToLower(login),
  407. Name: login,
  408. Email: login,
  409. Passwd: password,
  410. LoginType: LOGIN_PAM,
  411. LoginSource: sourceID,
  412. LoginName: login,
  413. IsActive: true,
  414. }
  415. return user, CreateUser(user)
  416. }
  417. func ExternalUserLogin(user *User, login, password string, source *LoginSource, autoRegister bool) (*User, error) {
  418. if !source.IsActived {
  419. return nil, ErrLoginSourceNotActived
  420. }
  421. switch source.Type {
  422. case LOGIN_LDAP, LOGIN_DLDAP:
  423. return LoginViaLDAP(user, login, password, source, autoRegister)
  424. case LOGIN_SMTP:
  425. return LoginViaSMTP(user, login, password, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
  426. case LOGIN_PAM:
  427. return LoginViaPAM(user, login, password, source.ID, source.Cfg.(*PAMConfig), autoRegister)
  428. }
  429. return nil, ErrUnsupportedLoginType
  430. }
  431. // UserSignIn validates user name and password.
  432. func UserSignIn(username, passowrd string) (*User, error) {
  433. var user *User
  434. if strings.Contains(username, "@") {
  435. user = &User{Email: strings.ToLower(username)}
  436. } else {
  437. user = &User{LowerName: strings.ToLower(username)}
  438. }
  439. hasUser, err := x.Get(user)
  440. if err != nil {
  441. return nil, err
  442. }
  443. if hasUser {
  444. switch user.LoginType {
  445. case LOGIN_NOTYPE, LOGIN_PLAIN:
  446. if user.ValidatePassword(passowrd) {
  447. return user, nil
  448. }
  449. return nil, ErrUserNotExist{user.ID, user.Name}
  450. default:
  451. var source LoginSource
  452. hasSource, err := x.Id(user.LoginSource).Get(&source)
  453. if err != nil {
  454. return nil, err
  455. } else if !hasSource {
  456. return nil, ErrLoginSourceNotExist{user.LoginSource}
  457. }
  458. return ExternalUserLogin(user, user.LoginName, passowrd, &source, false)
  459. }
  460. }
  461. sources := make([]*LoginSource, 0, 3)
  462. if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true}); err != nil {
  463. return nil, err
  464. }
  465. for _, source := range sources {
  466. authUser, err := ExternalUserLogin(nil, username, passowrd, source, true)
  467. if err == nil {
  468. return authUser, nil
  469. }
  470. log.Warn("Failed to login '%s' via '%s': %v", username, source.Name, err)
  471. }
  472. return nil, ErrUserNotExist{user.ID, user.Name}
  473. }