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

login.go 14 kB

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