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