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.

360 lines
8.0 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
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
  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/go-xorm/core"
  14. "github.com/go-xorm/xorm"
  15. "github.com/gogits/gogs/modules/auth/ldap"
  16. "github.com/gogits/gogs/modules/log"
  17. "github.com/gogits/gogs/modules/uuid"
  18. )
  19. type LoginType int
  20. const (
  21. NOTYPE LoginType = iota
  22. PLAIN
  23. LDAP
  24. SMTP
  25. )
  26. var (
  27. ErrAuthenticationAlreadyExist = errors.New("Authentication already exist")
  28. ErrAuthenticationNotExist = errors.New("Authentication does not exist")
  29. ErrAuthenticationUserUsed = errors.New("Authentication has been used by some users")
  30. )
  31. var LoginTypes = map[LoginType]string{
  32. LDAP: "LDAP",
  33. SMTP: "SMTP",
  34. }
  35. // Ensure structs implemented interface.
  36. var (
  37. _ core.Conversion = &LDAPConfig{}
  38. _ core.Conversion = &SMTPConfig{}
  39. )
  40. type LDAPConfig struct {
  41. ldap.Ldapsource
  42. }
  43. func (cfg *LDAPConfig) FromDB(bs []byte) error {
  44. return json.Unmarshal(bs, &cfg.Ldapsource)
  45. }
  46. func (cfg *LDAPConfig) ToDB() ([]byte, error) {
  47. return json.Marshal(cfg.Ldapsource)
  48. }
  49. type SMTPConfig struct {
  50. Auth string
  51. Host string
  52. Port int
  53. TLS bool
  54. }
  55. func (cfg *SMTPConfig) FromDB(bs []byte) error {
  56. return json.Unmarshal(bs, cfg)
  57. }
  58. func (cfg *SMTPConfig) ToDB() ([]byte, error) {
  59. return json.Marshal(cfg)
  60. }
  61. type LoginSource struct {
  62. Id int64
  63. Type LoginType
  64. Name string `xorm:"UNIQUE"`
  65. IsActived bool `xorm:"NOT NULL DEFAULT false"`
  66. Cfg core.Conversion `xorm:"TEXT"`
  67. AllowAutoRegister bool `xorm:"NOT NULL DEFAULT false"`
  68. Created time.Time `xorm:"CREATED"`
  69. Updated time.Time `xorm:"UPDATED"`
  70. }
  71. func (source *LoginSource) TypeString() string {
  72. return LoginTypes[source.Type]
  73. }
  74. func (source *LoginSource) LDAP() *LDAPConfig {
  75. return source.Cfg.(*LDAPConfig)
  76. }
  77. func (source *LoginSource) SMTP() *SMTPConfig {
  78. return source.Cfg.(*SMTPConfig)
  79. }
  80. func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
  81. if colName == "type" {
  82. ty := (*val).(int64)
  83. switch LoginType(ty) {
  84. case LDAP:
  85. source.Cfg = new(LDAPConfig)
  86. case SMTP:
  87. source.Cfg = new(SMTPConfig)
  88. }
  89. }
  90. }
  91. func CreateSource(source *LoginSource) error {
  92. _, err := x.Insert(source)
  93. return err
  94. }
  95. func GetAuths() ([]*LoginSource, error) {
  96. var auths = make([]*LoginSource, 0, 5)
  97. err := x.Find(&auths)
  98. return auths, err
  99. }
  100. func GetLoginSourceById(id int64) (*LoginSource, error) {
  101. source := new(LoginSource)
  102. has, err := x.Id(id).Get(source)
  103. if err != nil {
  104. return nil, err
  105. } else if !has {
  106. return nil, ErrAuthenticationNotExist
  107. }
  108. return source, nil
  109. }
  110. func UpdateSource(source *LoginSource) error {
  111. _, err := x.Id(source.Id).AllCols().Update(source)
  112. return err
  113. }
  114. func DelLoginSource(source *LoginSource) error {
  115. cnt, err := x.Count(&User{LoginSource: source.Id})
  116. if err != nil {
  117. return err
  118. }
  119. if cnt > 0 {
  120. return ErrAuthenticationUserUsed
  121. }
  122. _, err = x.Id(source.Id).Delete(&LoginSource{})
  123. return err
  124. }
  125. // UserSignIn validates user name and password.
  126. func UserSignIn(uname, passwd string) (*User, error) {
  127. u := new(User)
  128. if strings.Contains(uname, "@") {
  129. u = &User{Email: uname}
  130. } else {
  131. u = &User{LowerName: strings.ToLower(uname)}
  132. }
  133. has, err := x.Get(u)
  134. if err != nil {
  135. return nil, err
  136. }
  137. if u.LoginType == NOTYPE && has {
  138. u.LoginType = PLAIN
  139. }
  140. // For plain login, user must exist to reach this line.
  141. // Now verify password.
  142. if u.LoginType == PLAIN {
  143. if !u.ValidtePassword(passwd) {
  144. return nil, ErrUserNotExist
  145. }
  146. return u, nil
  147. }
  148. if !has {
  149. var sources []LoginSource
  150. if err = x.UseBool().Find(&sources,
  151. &LoginSource{IsActived: true, AllowAutoRegister: true}); err != nil {
  152. return nil, err
  153. }
  154. for _, source := range sources {
  155. if source.Type == LDAP {
  156. u, err := LoginUserLdapSource(nil, uname, passwd,
  157. source.Id, source.Cfg.(*LDAPConfig), true)
  158. if err == nil {
  159. return u, nil
  160. }
  161. log.Warn("Fail to login(%s) by LDAP(%s): %v", uname, source.Name, err)
  162. } else if source.Type == SMTP {
  163. u, err := LoginUserSMTPSource(nil, uname, passwd,
  164. source.Id, source.Cfg.(*SMTPConfig), true)
  165. if err == nil {
  166. return u, nil
  167. }
  168. log.Warn("Fail to login(%s) by SMTP(%s): %v", uname, source.Name, err)
  169. }
  170. }
  171. return nil, ErrUserNotExist
  172. }
  173. var source LoginSource
  174. hasSource, err := x.Id(u.LoginSource).Get(&source)
  175. if err != nil {
  176. return nil, err
  177. } else if !hasSource {
  178. return nil, ErrLoginSourceNotExist
  179. } else if !source.IsActived {
  180. return nil, ErrLoginSourceNotActived
  181. }
  182. switch u.LoginType {
  183. case LDAP:
  184. return LoginUserLdapSource(u, u.LoginName, passwd, source.Id, source.Cfg.(*LDAPConfig), false)
  185. case SMTP:
  186. return LoginUserSMTPSource(u, u.LoginName, passwd, source.Id, source.Cfg.(*SMTPConfig), false)
  187. }
  188. return nil, ErrUnsupportedLoginType
  189. }
  190. // Query if name/passwd can login against the LDAP directory pool
  191. // Create a local user if success
  192. // Return the same LoginUserPlain semantic
  193. // FIXME: https://github.com/gogits/gogs/issues/672
  194. func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) {
  195. name, fn, sn, mail, logged := cfg.Ldapsource.SearchEntry(name, passwd)
  196. if !logged {
  197. // User not in LDAP, do nothing
  198. return nil, ErrUserNotExist
  199. }
  200. if !autoRegister {
  201. return u, nil
  202. }
  203. // Fallback.
  204. if len(mail) == 0 {
  205. mail = uuid.NewV4().String() + "@localhost"
  206. }
  207. u = &User{
  208. Name: name,
  209. FullName: fn + " " + sn,
  210. LoginType: LDAP,
  211. LoginSource: sourceId,
  212. LoginName: name,
  213. Passwd: passwd,
  214. Email: mail,
  215. IsActive: true,
  216. }
  217. return u, CreateUser(u)
  218. }
  219. type loginAuth struct {
  220. username, password string
  221. }
  222. func LoginAuth(username, password string) smtp.Auth {
  223. return &loginAuth{username, password}
  224. }
  225. func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
  226. return "LOGIN", []byte(a.username), nil
  227. }
  228. func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
  229. if more {
  230. switch string(fromServer) {
  231. case "Username:":
  232. return []byte(a.username), nil
  233. case "Password:":
  234. return []byte(a.password), nil
  235. }
  236. }
  237. return nil, nil
  238. }
  239. var (
  240. SMTP_PLAIN = "PLAIN"
  241. SMTP_LOGIN = "LOGIN"
  242. SMTPAuths = []string{SMTP_PLAIN, SMTP_LOGIN}
  243. )
  244. func SmtpAuth(host string, port int, a smtp.Auth, useTls bool) error {
  245. c, err := smtp.Dial(fmt.Sprintf("%s:%d", host, port))
  246. if err != nil {
  247. return err
  248. }
  249. defer c.Close()
  250. if err = c.Hello("gogs"); err != nil {
  251. return err
  252. }
  253. if useTls {
  254. if ok, _ := c.Extension("STARTTLS"); ok {
  255. config := &tls.Config{ServerName: host}
  256. if err = c.StartTLS(config); err != nil {
  257. return err
  258. }
  259. } else {
  260. return errors.New("SMTP server unsupports TLS")
  261. }
  262. }
  263. if ok, _ := c.Extension("AUTH"); ok {
  264. if err = c.Auth(a); err != nil {
  265. return err
  266. }
  267. return nil
  268. }
  269. return ErrUnsupportedLoginType
  270. }
  271. // Query if name/passwd can login against the LDAP directory pool
  272. // Create a local user if success
  273. // Return the same LoginUserPlain semantic
  274. func LoginUserSMTPSource(u *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
  275. var auth smtp.Auth
  276. if cfg.Auth == SMTP_PLAIN {
  277. auth = smtp.PlainAuth("", name, passwd, cfg.Host)
  278. } else if cfg.Auth == SMTP_LOGIN {
  279. auth = LoginAuth(name, passwd)
  280. } else {
  281. return nil, errors.New("Unsupported SMTP auth type")
  282. }
  283. if err := SmtpAuth(cfg.Host, cfg.Port, auth, cfg.TLS); err != nil {
  284. if strings.Contains(err.Error(), "Username and Password not accepted") {
  285. return nil, ErrUserNotExist
  286. }
  287. return nil, err
  288. }
  289. if !autoRegister {
  290. return u, nil
  291. }
  292. var loginName = name
  293. idx := strings.Index(name, "@")
  294. if idx > -1 {
  295. loginName = name[:idx]
  296. }
  297. // fake a local user creation
  298. u = &User{
  299. LowerName: strings.ToLower(loginName),
  300. Name: strings.ToLower(loginName),
  301. LoginType: SMTP,
  302. LoginSource: sourceId,
  303. LoginName: name,
  304. IsActive: true,
  305. Passwd: passwd,
  306. Email: name,
  307. }
  308. err := CreateUser(u)
  309. return u, err
  310. }