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.

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