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 11 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
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  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.Ldapsource
  49. }
  50. func (cfg *LDAPConfig) FromDB(bs []byte) error {
  51. return json.Unmarshal(bs, &cfg.Ldapsource)
  52. }
  53. func (cfg *LDAPConfig) ToDB() ([]byte, error) {
  54. return json.Marshal(cfg.Ldapsource)
  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. AllowAutoRegister bool `xorm:"NOT NULL DEFAULT false"`
  86. Created time.Time `xorm:"CREATED"`
  87. Updated time.Time `xorm:"UPDATED"`
  88. }
  89. func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
  90. switch colName {
  91. case "type":
  92. switch LoginType((*val).(int64)) {
  93. case LDAP, DLDAP:
  94. source.Cfg = new(LDAPConfig)
  95. case SMTP:
  96. source.Cfg = new(SMTPConfig)
  97. case PAM:
  98. source.Cfg = new(PAMConfig)
  99. default:
  100. panic("unrecognized login source type: " + com.ToStr(*val))
  101. }
  102. }
  103. }
  104. func (source *LoginSource) TypeName() string {
  105. return LoginNames[source.Type]
  106. }
  107. func (source *LoginSource) IsLDAP() bool {
  108. return source.Type == LDAP
  109. }
  110. func (source *LoginSource) IsDLDAP() bool {
  111. return source.Type == DLDAP
  112. }
  113. func (source *LoginSource) IsSMTP() bool {
  114. return source.Type == SMTP
  115. }
  116. func (source *LoginSource) IsPAM() bool {
  117. return source.Type == PAM
  118. }
  119. func (source *LoginSource) UseTLS() bool {
  120. switch source.Type {
  121. case LDAP, DLDAP:
  122. return source.LDAP().UseSSL
  123. case SMTP:
  124. return source.SMTP().TLS
  125. }
  126. return false
  127. }
  128. func (source *LoginSource) LDAP() *LDAPConfig {
  129. return source.Cfg.(*LDAPConfig)
  130. }
  131. func (source *LoginSource) SMTP() *SMTPConfig {
  132. return source.Cfg.(*SMTPConfig)
  133. }
  134. func (source *LoginSource) PAM() *PAMConfig {
  135. return source.Cfg.(*PAMConfig)
  136. }
  137. // CountLoginSources returns number of login sources.
  138. func CountLoginSources() int64 {
  139. count, _ := x.Count(new(LoginSource))
  140. return count
  141. }
  142. func CreateSource(source *LoginSource) error {
  143. _, err := x.Insert(source)
  144. return err
  145. }
  146. func GetAuths() ([]*LoginSource, error) {
  147. auths := make([]*LoginSource, 0, 5)
  148. return auths, x.Find(&auths)
  149. }
  150. func GetLoginSourceByID(id int64) (*LoginSource, error) {
  151. source := new(LoginSource)
  152. has, err := x.Id(id).Get(source)
  153. if err != nil {
  154. return nil, err
  155. } else if !has {
  156. return nil, ErrAuthenticationNotExist
  157. }
  158. return source, nil
  159. }
  160. func UpdateSource(source *LoginSource) error {
  161. _, err := x.Id(source.ID).AllCols().Update(source)
  162. return err
  163. }
  164. func DeleteSource(source *LoginSource) error {
  165. count, err := x.Count(&User{LoginSource: source.ID})
  166. if err != nil {
  167. return err
  168. } else if count > 0 {
  169. return ErrAuthenticationUserUsed
  170. }
  171. _, err = x.Id(source.ID).Delete(new(LoginSource))
  172. return err
  173. }
  174. // UserSignIn validates user name and password.
  175. func UserSignIn(uname, passwd string) (*User, error) {
  176. var u *User
  177. if strings.Contains(uname, "@") {
  178. u = &User{Email: uname}
  179. } else {
  180. u = &User{LowerName: strings.ToLower(uname)}
  181. }
  182. userExists, err := x.Get(u)
  183. if err != nil {
  184. return nil, err
  185. }
  186. if userExists {
  187. switch u.LoginType {
  188. case NOTYPE:
  189. fallthrough
  190. case PLAIN:
  191. if u.ValidatePassword(passwd) {
  192. return u, nil
  193. }
  194. return nil, ErrUserNotExist{u.Id, u.Name}
  195. default:
  196. var source LoginSource
  197. hasSource, err := x.Id(u.LoginSource).Get(&source)
  198. if err != nil {
  199. return nil, err
  200. } else if !hasSource {
  201. return nil, ErrLoginSourceNotExist
  202. }
  203. return ExternalUserLogin(u, u.LoginName, passwd, &source, false)
  204. }
  205. }
  206. var sources []LoginSource
  207. if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true, AllowAutoRegister: true}); err != nil {
  208. return nil, err
  209. }
  210. for _, source := range sources {
  211. u, err := ExternalUserLogin(nil, uname, passwd, &source, true)
  212. if err == nil {
  213. return u, nil
  214. }
  215. log.Warn("Failed to login '%s' via '%s': %v", uname, source.Name, err)
  216. }
  217. return nil, ErrUserNotExist{u.Id, u.Name}
  218. }
  219. func ExternalUserLogin(u *User, name, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
  220. if !source.IsActived {
  221. return nil, ErrLoginSourceNotActived
  222. }
  223. switch source.Type {
  224. case LDAP, DLDAP:
  225. return LoginUserLdapSource(u, name, passwd, source, autoRegister)
  226. case SMTP:
  227. return LoginUserSMTPSource(u, name, passwd, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
  228. case PAM:
  229. return LoginUserPAMSource(u, name, passwd, source.ID, source.Cfg.(*PAMConfig), autoRegister)
  230. }
  231. return nil, ErrUnsupportedLoginType
  232. }
  233. // Query if name/passwd can login against the LDAP directory pool
  234. // Create a local user if success
  235. // Return the same LoginUserPlain semantic
  236. // FIXME: https://github.com/gogits/gogs/issues/672
  237. func LoginUserLdapSource(u *User, name, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
  238. cfg := source.Cfg.(*LDAPConfig)
  239. directBind := (source.Type == DLDAP)
  240. fn, sn, mail, admin, logged := cfg.Ldapsource.SearchEntry(name, passwd, directBind)
  241. if !logged {
  242. // User not in LDAP, do nothing
  243. return nil, ErrUserNotExist{0, name}
  244. }
  245. if !autoRegister {
  246. return u, nil
  247. }
  248. // Fallback.
  249. if len(mail) == 0 {
  250. mail = fmt.Sprintf("%s@localhost", name)
  251. }
  252. u = &User{
  253. LowerName: strings.ToLower(name),
  254. Name: name,
  255. FullName: fn + " " + sn,
  256. LoginType: source.Type,
  257. LoginSource: source.ID,
  258. LoginName: name,
  259. Passwd: passwd,
  260. Email: mail,
  261. IsAdmin: admin,
  262. IsActive: true,
  263. }
  264. return u, CreateUser(u)
  265. }
  266. type loginAuth struct {
  267. username, password string
  268. }
  269. func LoginAuth(username, password string) smtp.Auth {
  270. return &loginAuth{username, password}
  271. }
  272. func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
  273. return "LOGIN", []byte(a.username), nil
  274. }
  275. func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
  276. if more {
  277. switch string(fromServer) {
  278. case "Username:":
  279. return []byte(a.username), nil
  280. case "Password:":
  281. return []byte(a.password), nil
  282. }
  283. }
  284. return nil, nil
  285. }
  286. const (
  287. SMTP_PLAIN = "PLAIN"
  288. SMTP_LOGIN = "LOGIN"
  289. )
  290. var SMTPAuths = []string{SMTP_PLAIN, SMTP_LOGIN}
  291. func SMTPAuth(a smtp.Auth, cfg *SMTPConfig) error {
  292. c, err := smtp.Dial(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))
  293. if err != nil {
  294. return err
  295. }
  296. defer c.Close()
  297. if err = c.Hello("gogs"); err != nil {
  298. return err
  299. }
  300. if cfg.TLS {
  301. if ok, _ := c.Extension("STARTTLS"); ok {
  302. if err = c.StartTLS(&tls.Config{
  303. InsecureSkipVerify: cfg.SkipVerify,
  304. ServerName: cfg.Host,
  305. }); err != nil {
  306. return err
  307. }
  308. } else {
  309. return errors.New("SMTP server unsupports TLS")
  310. }
  311. }
  312. if ok, _ := c.Extension("AUTH"); ok {
  313. if err = c.Auth(a); err != nil {
  314. return err
  315. }
  316. return nil
  317. }
  318. return ErrUnsupportedLoginType
  319. }
  320. // Query if name/passwd can login against the LDAP directory pool
  321. // Create a local user if success
  322. // Return the same LoginUserPlain semantic
  323. func LoginUserSMTPSource(u *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
  324. // Verify allowed domains.
  325. if len(cfg.AllowedDomains) > 0 {
  326. idx := strings.Index(name, "@")
  327. if idx == -1 {
  328. return nil, ErrUserNotExist{0, name}
  329. } else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), name[idx+1:]) {
  330. return nil, ErrUserNotExist{0, name}
  331. }
  332. }
  333. var auth smtp.Auth
  334. if cfg.Auth == SMTP_PLAIN {
  335. auth = smtp.PlainAuth("", name, passwd, cfg.Host)
  336. } else if cfg.Auth == SMTP_LOGIN {
  337. auth = LoginAuth(name, passwd)
  338. } else {
  339. return nil, errors.New("Unsupported SMTP auth type")
  340. }
  341. if err := SMTPAuth(auth, cfg); err != nil {
  342. if strings.Contains(err.Error(), "Username and Password not accepted") {
  343. return nil, ErrUserNotExist{0, name}
  344. }
  345. return nil, err
  346. }
  347. if !autoRegister {
  348. return u, nil
  349. }
  350. var loginName = name
  351. idx := strings.Index(name, "@")
  352. if idx > -1 {
  353. loginName = name[:idx]
  354. }
  355. // fake a local user creation
  356. u = &User{
  357. LowerName: strings.ToLower(loginName),
  358. Name: strings.ToLower(loginName),
  359. LoginType: SMTP,
  360. LoginSource: sourceId,
  361. LoginName: name,
  362. IsActive: true,
  363. Passwd: passwd,
  364. Email: name,
  365. }
  366. err := CreateUser(u)
  367. return u, err
  368. }
  369. // Query if name/passwd can login against PAM
  370. // Create a local user if success
  371. // Return the same LoginUserPlain semantic
  372. func LoginUserPAMSource(u *User, name, passwd string, sourceId int64, cfg *PAMConfig, autoRegister bool) (*User, error) {
  373. if err := pam.PAMAuth(cfg.ServiceName, name, passwd); err != nil {
  374. if strings.Contains(err.Error(), "Authentication failure") {
  375. return nil, ErrUserNotExist{u.Id, u.Name}
  376. }
  377. return nil, err
  378. }
  379. if !autoRegister {
  380. return u, nil
  381. }
  382. // fake a local user creation
  383. u = &User{
  384. LowerName: strings.ToLower(name),
  385. Name: strings.ToLower(name),
  386. LoginType: PAM,
  387. LoginSource: sourceId,
  388. LoginName: name,
  389. IsActive: true,
  390. Passwd: passwd,
  391. Email: name,
  392. }
  393. err := CreateUser(u)
  394. return u, err
  395. }