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