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.

428 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
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. SkipVerify 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 `xorm:"pk autoincr"`
  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) BeforeSet(colName string, val xorm.Cell) {
  85. switch colName {
  86. case "type":
  87. switch LoginType((*val).(int64)) {
  88. case LDAP:
  89. source.Cfg = new(LDAPConfig)
  90. case SMTP:
  91. source.Cfg = new(SMTPConfig)
  92. case PAM:
  93. source.Cfg = new(PAMConfig)
  94. }
  95. }
  96. }
  97. func (source *LoginSource) TypeString() string {
  98. return LoginTypes[source.Type]
  99. }
  100. func (source *LoginSource) LDAP() *LDAPConfig {
  101. return source.Cfg.(*LDAPConfig)
  102. }
  103. func (source *LoginSource) SMTP() *SMTPConfig {
  104. return source.Cfg.(*SMTPConfig)
  105. }
  106. func (source *LoginSource) PAM() *PAMConfig {
  107. return source.Cfg.(*PAMConfig)
  108. }
  109. func CreateSource(source *LoginSource) error {
  110. _, err := x.Insert(source)
  111. return err
  112. }
  113. func GetAuths() ([]*LoginSource, error) {
  114. auths := make([]*LoginSource, 0, 5)
  115. return auths, x.Find(&auths)
  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. const (
  268. SMTP_PLAIN = "PLAIN"
  269. SMTP_LOGIN = "LOGIN"
  270. )
  271. var (
  272. SMTPAuths = []string{SMTP_PLAIN, SMTP_LOGIN}
  273. )
  274. func SMTPAuth(a smtp.Auth, cfg *SMTPConfig) error {
  275. c, err := smtp.Dial(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))
  276. if err != nil {
  277. return err
  278. }
  279. defer c.Close()
  280. if err = c.Hello("gogs"); err != nil {
  281. return err
  282. }
  283. if cfg.TLS {
  284. if ok, _ := c.Extension("STARTTLS"); ok {
  285. if err = c.StartTLS(&tls.Config{
  286. InsecureSkipVerify: cfg.SkipVerify,
  287. ServerName: cfg.Host,
  288. }); err != nil {
  289. return err
  290. }
  291. } else {
  292. return errors.New("SMTP server unsupports TLS")
  293. }
  294. }
  295. if ok, _ := c.Extension("AUTH"); ok {
  296. if err = c.Auth(a); err != nil {
  297. return err
  298. }
  299. return nil
  300. }
  301. return ErrUnsupportedLoginType
  302. }
  303. // Query if name/passwd can login against the LDAP directory pool
  304. // Create a local user if success
  305. // Return the same LoginUserPlain semantic
  306. func LoginUserSMTPSource(u *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
  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{u.Id, u.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. // Query if name/passwd can login against PAM
  344. // Create a local user if success
  345. // Return the same LoginUserPlain semantic
  346. func LoginUserPAMSource(u *User, name, passwd string, sourceId int64, cfg *PAMConfig, autoRegister bool) (*User, error) {
  347. if err := pam.PAMAuth(cfg.ServiceName, name, passwd); err != nil {
  348. if strings.Contains(err.Error(), "Authentication failure") {
  349. return nil, ErrUserNotExist{u.Id, u.Name}
  350. }
  351. return nil, err
  352. }
  353. if !autoRegister {
  354. return u, nil
  355. }
  356. // fake a local user creation
  357. u = &User{
  358. LowerName: strings.ToLower(name),
  359. Name: strings.ToLower(name),
  360. LoginType: PAM,
  361. LoginSource: sourceId,
  362. LoginName: name,
  363. IsActive: true,
  364. Passwd: passwd,
  365. Email: name,
  366. }
  367. err := CreateUser(u)
  368. return u, err
  369. }