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.

422 lines
9.6 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
9 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. "github.com/gogits/gogs/modules/uuid"
  19. )
  20. type LoginType int
  21. const (
  22. NOTYPE LoginType = iota
  23. PLAIN
  24. LDAP
  25. SMTP
  26. PAM
  27. )
  28. var (
  29. ErrAuthenticationAlreadyExist = errors.New("Authentication already exist")
  30. ErrAuthenticationNotExist = errors.New("Authentication does not exist")
  31. ErrAuthenticationUserUsed = errors.New("Authentication has been used by some users")
  32. )
  33. var LoginTypes = map[LoginType]string{
  34. LDAP: "LDAP",
  35. SMTP: "SMTP",
  36. PAM: "PAM",
  37. }
  38. // Ensure structs implemented interface.
  39. var (
  40. _ core.Conversion = &LDAPConfig{}
  41. _ core.Conversion = &SMTPConfig{}
  42. _ core.Conversion = &PAMConfig{}
  43. )
  44. type LDAPConfig struct {
  45. ldap.Ldapsource
  46. }
  47. func (cfg *LDAPConfig) FromDB(bs []byte) error {
  48. return json.Unmarshal(bs, &cfg.Ldapsource)
  49. }
  50. func (cfg *LDAPConfig) ToDB() ([]byte, error) {
  51. return json.Marshal(cfg.Ldapsource)
  52. }
  53. type SMTPConfig struct {
  54. Auth string
  55. Host string
  56. Port int
  57. TLS bool
  58. }
  59. func (cfg *SMTPConfig) FromDB(bs []byte) error {
  60. return json.Unmarshal(bs, cfg)
  61. }
  62. func (cfg *SMTPConfig) ToDB() ([]byte, error) {
  63. return json.Marshal(cfg)
  64. }
  65. type PAMConfig struct {
  66. ServiceName string // pam service (e.g. system-auth)
  67. }
  68. func (cfg *PAMConfig) FromDB(bs []byte) error {
  69. return json.Unmarshal(bs, &cfg)
  70. }
  71. func (cfg *PAMConfig) ToDB() ([]byte, error) {
  72. return json.Marshal(cfg)
  73. }
  74. type LoginSource struct {
  75. Id int64
  76. Type LoginType
  77. Name string `xorm:"UNIQUE"`
  78. IsActived bool `xorm:"NOT NULL DEFAULT false"`
  79. Cfg core.Conversion `xorm:"TEXT"`
  80. AllowAutoRegister bool `xorm:"NOT NULL DEFAULT false"`
  81. Created time.Time `xorm:"CREATED"`
  82. Updated time.Time `xorm:"UPDATED"`
  83. }
  84. func (source *LoginSource) TypeString() string {
  85. return LoginTypes[source.Type]
  86. }
  87. func (source *LoginSource) LDAP() *LDAPConfig {
  88. return source.Cfg.(*LDAPConfig)
  89. }
  90. func (source *LoginSource) SMTP() *SMTPConfig {
  91. return source.Cfg.(*SMTPConfig)
  92. }
  93. func (source *LoginSource) PAM() *PAMConfig {
  94. return source.Cfg.(*PAMConfig)
  95. }
  96. func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
  97. if colName == "type" {
  98. ty := (*val).(int64)
  99. switch LoginType(ty) {
  100. case LDAP:
  101. source.Cfg = new(LDAPConfig)
  102. case SMTP:
  103. source.Cfg = new(SMTPConfig)
  104. case PAM:
  105. source.Cfg = new(PAMConfig)
  106. }
  107. }
  108. }
  109. func CreateSource(source *LoginSource) error {
  110. _, err := x.Insert(source)
  111. return err
  112. }
  113. func GetAuths() ([]*LoginSource, error) {
  114. var auths = make([]*LoginSource, 0, 5)
  115. err := x.Find(&auths)
  116. return auths, err
  117. }
  118. func GetLoginSourceById(id int64) (*LoginSource, error) {
  119. source := new(LoginSource)
  120. has, err := x.Id(id).Get(source)
  121. if err != nil {
  122. return nil, err
  123. } else if !has {
  124. return nil, ErrAuthenticationNotExist
  125. }
  126. return source, nil
  127. }
  128. func UpdateSource(source *LoginSource) error {
  129. _, err := x.Id(source.Id).AllCols().Update(source)
  130. return err
  131. }
  132. func DelLoginSource(source *LoginSource) error {
  133. cnt, err := x.Count(&User{LoginSource: source.Id})
  134. if err != nil {
  135. return err
  136. }
  137. if cnt > 0 {
  138. return ErrAuthenticationUserUsed
  139. }
  140. _, err = x.Id(source.Id).Delete(&LoginSource{})
  141. return err
  142. }
  143. // UserSignIn validates user name and password.
  144. func UserSignIn(uname, passwd string) (*User, error) {
  145. u := new(User)
  146. if strings.Contains(uname, "@") {
  147. u = &User{Email: uname}
  148. } else {
  149. u = &User{LowerName: strings.ToLower(uname)}
  150. }
  151. has, err := x.Get(u)
  152. if err != nil {
  153. return nil, err
  154. }
  155. if u.LoginType == NOTYPE && has {
  156. u.LoginType = PLAIN
  157. }
  158. // For plain login, user must exist to reach this line.
  159. // Now verify password.
  160. if u.LoginType == PLAIN {
  161. if !u.ValidatePassword(passwd) {
  162. return nil, ErrUserNotExist
  163. }
  164. return u, nil
  165. }
  166. if !has {
  167. var sources []LoginSource
  168. if err = x.UseBool().Find(&sources,
  169. &LoginSource{IsActived: true, AllowAutoRegister: true}); err != nil {
  170. return nil, err
  171. }
  172. for _, source := range sources {
  173. if source.Type == LDAP {
  174. u, err := LoginUserLdapSource(nil, uname, passwd,
  175. source.Id, source.Cfg.(*LDAPConfig), true)
  176. if err == nil {
  177. return u, nil
  178. }
  179. log.Warn("Fail to login(%s) by LDAP(%s): %v", uname, source.Name, err)
  180. } else if source.Type == SMTP {
  181. u, err := LoginUserSMTPSource(nil, uname, passwd,
  182. source.Id, source.Cfg.(*SMTPConfig), true)
  183. if err == nil {
  184. return u, nil
  185. }
  186. log.Warn("Fail to login(%s) by SMTP(%s): %v", uname, source.Name, err)
  187. } else if source.Type == PAM {
  188. u, err := LoginUserPAMSource(nil, uname, passwd,
  189. source.Id, source.Cfg.(*PAMConfig), true)
  190. if err == nil {
  191. return u, nil
  192. }
  193. log.Warn("Fail to login(%s) by PAM(%s): %v", uname, source.Name, err)
  194. }
  195. }
  196. return nil, ErrUserNotExist
  197. }
  198. var source LoginSource
  199. hasSource, err := x.Id(u.LoginSource).Get(&source)
  200. if err != nil {
  201. return nil, err
  202. } else if !hasSource {
  203. return nil, ErrLoginSourceNotExist
  204. } else if !source.IsActived {
  205. return nil, ErrLoginSourceNotActived
  206. }
  207. switch u.LoginType {
  208. case LDAP:
  209. return LoginUserLdapSource(u, u.LoginName, passwd, source.Id, source.Cfg.(*LDAPConfig), false)
  210. case SMTP:
  211. return LoginUserSMTPSource(u, u.LoginName, passwd, source.Id, source.Cfg.(*SMTPConfig), false)
  212. case PAM:
  213. return LoginUserPAMSource(u, u.LoginName, passwd, source.Id, source.Cfg.(*PAMConfig), false)
  214. }
  215. return nil, ErrUnsupportedLoginType
  216. }
  217. // Query if name/passwd can login against the LDAP directory pool
  218. // Create a local user if success
  219. // Return the same LoginUserPlain semantic
  220. // FIXME: https://github.com/gogits/gogs/issues/672
  221. func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) {
  222. name, fn, sn, mail, logged := cfg.Ldapsource.SearchEntry(name, passwd)
  223. if !logged {
  224. // User not in LDAP, do nothing
  225. return nil, ErrUserNotExist
  226. }
  227. if !autoRegister {
  228. return u, nil
  229. }
  230. // Fallback.
  231. if len(mail) == 0 {
  232. mail = uuid.NewV4().String() + "@localhost"
  233. }
  234. u = &User{
  235. LowerName: strings.ToLower(name),
  236. Name: name,
  237. FullName: fn + " " + sn,
  238. LoginType: LDAP,
  239. LoginSource: sourceId,
  240. LoginName: name,
  241. Passwd: passwd,
  242. Email: mail,
  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
  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
  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. }