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.

524 lines
12 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
  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. "net/textproto"
  12. "strings"
  13. "time"
  14. "github.com/Unknwon/com"
  15. "github.com/go-xorm/core"
  16. "github.com/go-xorm/xorm"
  17. "github.com/gogits/gogs/modules/auth/ldap"
  18. "github.com/gogits/gogs/modules/auth/pam"
  19. "github.com/gogits/gogs/modules/log"
  20. )
  21. type LoginType int
  22. // Note: new type must be added at the end of list to maintain compatibility.
  23. const (
  24. LOGIN_NOTYPE LoginType = iota
  25. LOGIN_PLAIN
  26. LOGIN_LDAP
  27. LOGIN_SMTP
  28. LOGIN_PAM
  29. LOGIN_DLDAP
  30. )
  31. var (
  32. ErrAuthenticationAlreadyExist = errors.New("Authentication already exist")
  33. ErrAuthenticationUserUsed = errors.New("Authentication has been used by some users")
  34. )
  35. var LoginNames = map[LoginType]string{
  36. LOGIN_LDAP: "LDAP (via BindDN)",
  37. LOGIN_DLDAP: "LDAP (simple auth)",
  38. LOGIN_SMTP: "SMTP",
  39. LOGIN_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.Source
  49. }
  50. func (cfg *LDAPConfig) FromDB(bs []byte) error {
  51. return json.Unmarshal(bs, &cfg)
  52. }
  53. func (cfg *LDAPConfig) ToDB() ([]byte, error) {
  54. return json.Marshal(cfg)
  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. Created time.Time `xorm:"CREATED"`
  86. Updated time.Time `xorm:"UPDATED"`
  87. }
  88. // Cell2Int64 converts a xorm.Cell type to int64,
  89. // and handles possible irregular cases.
  90. func Cell2Int64(val xorm.Cell) int64 {
  91. switch (*val).(type) {
  92. case []uint8:
  93. log.Trace("Cell2Int64 ([]uint8): %v", *val)
  94. return com.StrTo(string((*val).([]uint8))).MustInt64()
  95. }
  96. return (*val).(int64)
  97. }
  98. func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
  99. switch colName {
  100. case "type":
  101. switch LoginType(Cell2Int64(val)) {
  102. case LOGIN_LDAP, LOGIN_DLDAP:
  103. source.Cfg = new(LDAPConfig)
  104. case LOGIN_SMTP:
  105. source.Cfg = new(SMTPConfig)
  106. case LOGIN_PAM:
  107. source.Cfg = new(PAMConfig)
  108. default:
  109. panic("unrecognized login source type: " + com.ToStr(*val))
  110. }
  111. }
  112. }
  113. func (source *LoginSource) TypeName() string {
  114. return LoginNames[source.Type]
  115. }
  116. func (source *LoginSource) IsLDAP() bool {
  117. return source.Type == LOGIN_LDAP
  118. }
  119. func (source *LoginSource) IsDLDAP() bool {
  120. return source.Type == LOGIN_DLDAP
  121. }
  122. func (source *LoginSource) IsSMTP() bool {
  123. return source.Type == LOGIN_SMTP
  124. }
  125. func (source *LoginSource) IsPAM() bool {
  126. return source.Type == LOGIN_PAM
  127. }
  128. func (source *LoginSource) UseTLS() bool {
  129. switch source.Type {
  130. case LOGIN_LDAP, LOGIN_DLDAP:
  131. return source.LDAP().UseSSL
  132. case LOGIN_SMTP:
  133. return source.SMTP().TLS
  134. }
  135. return false
  136. }
  137. func (source *LoginSource) SkipVerify() bool {
  138. switch source.Type {
  139. case LOGIN_LDAP, LOGIN_DLDAP:
  140. return source.LDAP().SkipVerify
  141. case LOGIN_SMTP:
  142. return source.SMTP().SkipVerify
  143. }
  144. return false
  145. }
  146. func (source *LoginSource) LDAP() *LDAPConfig {
  147. return source.Cfg.(*LDAPConfig)
  148. }
  149. func (source *LoginSource) SMTP() *SMTPConfig {
  150. return source.Cfg.(*SMTPConfig)
  151. }
  152. func (source *LoginSource) PAM() *PAMConfig {
  153. return source.Cfg.(*PAMConfig)
  154. }
  155. // CountLoginSources returns number of login sources.
  156. func CountLoginSources() int64 {
  157. count, _ := x.Count(new(LoginSource))
  158. return count
  159. }
  160. func CreateSource(source *LoginSource) error {
  161. _, err := x.Insert(source)
  162. return err
  163. }
  164. func LoginSources() ([]*LoginSource, error) {
  165. auths := make([]*LoginSource, 0, 5)
  166. return auths, x.Find(&auths)
  167. }
  168. // GetLoginSourceByID returns login source by given ID.
  169. func GetLoginSourceByID(id int64) (*LoginSource, error) {
  170. source := new(LoginSource)
  171. has, err := x.Id(id).Get(source)
  172. if err != nil {
  173. return nil, err
  174. } else if !has {
  175. return nil, ErrAuthenticationNotExist{id}
  176. }
  177. return source, nil
  178. }
  179. func UpdateSource(source *LoginSource) error {
  180. _, err := x.Id(source.ID).AllCols().Update(source)
  181. return err
  182. }
  183. func DeleteSource(source *LoginSource) error {
  184. count, err := x.Count(&User{LoginSource: source.ID})
  185. if err != nil {
  186. return err
  187. } else if count > 0 {
  188. return ErrAuthenticationUserUsed
  189. }
  190. _, err = x.Id(source.ID).Delete(new(LoginSource))
  191. return err
  192. }
  193. // .____ ________ _____ __________
  194. // | | \______ \ / _ \\______ \
  195. // | | | | \ / /_\ \| ___/
  196. // | |___ | ` \/ | \ |
  197. // |_______ \/_______ /\____|__ /____|
  198. // \/ \/ \/
  199. // LoginUserLDAPSource queries if loginName/passwd can login against the LDAP directory pool,
  200. // and create a local user if success when enabled.
  201. // It returns the same LoginUserPlain semantic.
  202. func LoginUserLDAPSource(u *User, loginName, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
  203. cfg := source.Cfg.(*LDAPConfig)
  204. directBind := (source.Type == LOGIN_DLDAP)
  205. name, fn, sn, mail, admin, logged := cfg.SearchEntry(loginName, passwd, directBind)
  206. if !logged {
  207. // User not in LDAP, do nothing
  208. return nil, ErrUserNotExist{0, loginName}
  209. }
  210. if !autoRegister {
  211. return u, nil
  212. }
  213. // Fallback.
  214. if len(name) == 0 {
  215. name = loginName
  216. }
  217. if len(mail) == 0 {
  218. mail = fmt.Sprintf("%s@localhost", name)
  219. }
  220. u = &User{
  221. LowerName: strings.ToLower(name),
  222. Name: name,
  223. FullName: composeFullName(fn, sn, name),
  224. LoginType: source.Type,
  225. LoginSource: source.ID,
  226. LoginName: loginName,
  227. Email: mail,
  228. IsAdmin: admin,
  229. IsActive: true,
  230. }
  231. return u, CreateUser(u)
  232. }
  233. func composeFullName(firstName, surename, userName string) string {
  234. switch {
  235. case len(firstName) == 0 && len(surename) == 0:
  236. return userName
  237. case len(firstName) == 0:
  238. return surename
  239. case len(surename) == 0:
  240. return firstName
  241. default:
  242. return firstName + " " + surename
  243. }
  244. }
  245. // _________ __________________________
  246. // / _____/ / \__ ___/\______ \
  247. // \_____ \ / \ / \| | | ___/
  248. // / \/ Y \ | | |
  249. // /_______ /\____|__ /____| |____|
  250. // \/ \/
  251. type loginAuth struct {
  252. username, password string
  253. }
  254. func LoginAuth(username, password string) smtp.Auth {
  255. return &loginAuth{username, password}
  256. }
  257. func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
  258. return "LOGIN", []byte(a.username), nil
  259. }
  260. func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
  261. if more {
  262. switch string(fromServer) {
  263. case "Username:":
  264. return []byte(a.username), nil
  265. case "Password:":
  266. return []byte(a.password), nil
  267. }
  268. }
  269. return nil, nil
  270. }
  271. const (
  272. SMTP_PLAIN = "PLAIN"
  273. SMTP_LOGIN = "LOGIN"
  274. )
  275. var SMTPAuths = []string{SMTP_PLAIN, SMTP_LOGIN}
  276. func SMTPAuth(a smtp.Auth, cfg *SMTPConfig) error {
  277. c, err := smtp.Dial(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))
  278. if err != nil {
  279. return err
  280. }
  281. defer c.Close()
  282. if err = c.Hello("gogs"); err != nil {
  283. return err
  284. }
  285. if cfg.TLS {
  286. if ok, _ := c.Extension("STARTTLS"); ok {
  287. if err = c.StartTLS(&tls.Config{
  288. InsecureSkipVerify: cfg.SkipVerify,
  289. ServerName: cfg.Host,
  290. }); err != nil {
  291. return err
  292. }
  293. } else {
  294. return errors.New("SMTP server unsupports TLS")
  295. }
  296. }
  297. if ok, _ := c.Extension("AUTH"); ok {
  298. if err = c.Auth(a); err != nil {
  299. return err
  300. }
  301. return nil
  302. }
  303. return ErrUnsupportedLoginType
  304. }
  305. // Query if name/passwd can login against the LDAP directory pool
  306. // Create a local user if success
  307. // Return the same LoginUserPlain semantic
  308. func LoginUserSMTPSource(u *User, name, passwd string, sourceID int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
  309. // Verify allowed domains.
  310. if len(cfg.AllowedDomains) > 0 {
  311. idx := strings.Index(name, "@")
  312. if idx == -1 {
  313. return nil, ErrUserNotExist{0, name}
  314. } else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), name[idx+1:]) {
  315. return nil, ErrUserNotExist{0, name}
  316. }
  317. }
  318. var auth smtp.Auth
  319. if cfg.Auth == SMTP_PLAIN {
  320. auth = smtp.PlainAuth("", name, passwd, cfg.Host)
  321. } else if cfg.Auth == SMTP_LOGIN {
  322. auth = LoginAuth(name, passwd)
  323. } else {
  324. return nil, errors.New("Unsupported SMTP auth type")
  325. }
  326. if err := SMTPAuth(auth, cfg); err != nil {
  327. // Check standard error format first,
  328. // then fallback to worse case.
  329. tperr, ok := err.(*textproto.Error)
  330. if (ok && tperr.Code == 535) ||
  331. strings.Contains(err.Error(), "Username and Password not accepted") {
  332. return nil, ErrUserNotExist{0, name}
  333. }
  334. return nil, err
  335. }
  336. if !autoRegister {
  337. return u, nil
  338. }
  339. var loginName = name
  340. idx := strings.Index(name, "@")
  341. if idx > -1 {
  342. loginName = name[:idx]
  343. }
  344. // fake a local user creation
  345. u = &User{
  346. LowerName: strings.ToLower(loginName),
  347. Name: strings.ToLower(loginName),
  348. LoginType: LOGIN_SMTP,
  349. LoginSource: sourceID,
  350. LoginName: name,
  351. IsActive: true,
  352. Passwd: passwd,
  353. Email: name,
  354. }
  355. err := CreateUser(u)
  356. return u, err
  357. }
  358. // __________ _____ _____
  359. // \______ \/ _ \ / \
  360. // | ___/ /_\ \ / \ / \
  361. // | | / | \/ Y \
  362. // |____| \____|__ /\____|__ /
  363. // \/ \/
  364. // Query if name/passwd can login against PAM
  365. // Create a local user if success
  366. // Return the same LoginUserPlain semantic
  367. func LoginUserPAMSource(u *User, name, passwd string, sourceID int64, cfg *PAMConfig, autoRegister bool) (*User, error) {
  368. if err := pam.PAMAuth(cfg.ServiceName, name, passwd); err != nil {
  369. if strings.Contains(err.Error(), "Authentication failure") {
  370. return nil, ErrUserNotExist{0, name}
  371. }
  372. return nil, err
  373. }
  374. if !autoRegister {
  375. return u, nil
  376. }
  377. // fake a local user creation
  378. u = &User{
  379. LowerName: strings.ToLower(name),
  380. Name: name,
  381. LoginType: LOGIN_PAM,
  382. LoginSource: sourceID,
  383. LoginName: name,
  384. IsActive: true,
  385. Passwd: passwd,
  386. Email: name,
  387. }
  388. return u, CreateUser(u)
  389. }
  390. func ExternalUserLogin(u *User, name, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
  391. if !source.IsActived {
  392. return nil, ErrLoginSourceNotActived
  393. }
  394. switch source.Type {
  395. case LOGIN_LDAP, LOGIN_DLDAP:
  396. return LoginUserLDAPSource(u, name, passwd, source, autoRegister)
  397. case LOGIN_SMTP:
  398. return LoginUserSMTPSource(u, name, passwd, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
  399. case LOGIN_PAM:
  400. return LoginUserPAMSource(u, name, passwd, source.ID, source.Cfg.(*PAMConfig), autoRegister)
  401. }
  402. return nil, ErrUnsupportedLoginType
  403. }
  404. // UserSignIn validates user name and password.
  405. func UserSignIn(uname, passwd string) (*User, error) {
  406. var u *User
  407. if strings.Contains(uname, "@") {
  408. u = &User{Email: strings.ToLower(uname)}
  409. } else {
  410. u = &User{LowerName: strings.ToLower(uname)}
  411. }
  412. userExists, err := x.Get(u)
  413. if err != nil {
  414. return nil, err
  415. }
  416. if userExists {
  417. switch u.LoginType {
  418. case LOGIN_NOTYPE, LOGIN_PLAIN:
  419. if u.ValidatePassword(passwd) {
  420. return u, nil
  421. }
  422. return nil, ErrUserNotExist{u.Id, u.Name}
  423. default:
  424. var source LoginSource
  425. hasSource, err := x.Id(u.LoginSource).Get(&source)
  426. if err != nil {
  427. return nil, err
  428. } else if !hasSource {
  429. return nil, ErrLoginSourceNotExist
  430. }
  431. return ExternalUserLogin(u, u.LoginName, passwd, &source, false)
  432. }
  433. }
  434. var sources []LoginSource
  435. if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true}); err != nil {
  436. return nil, err
  437. }
  438. for _, source := range sources {
  439. u, err := ExternalUserLogin(nil, uname, passwd, &source, true)
  440. if err == nil {
  441. return u, nil
  442. }
  443. log.Warn("Failed to login '%s' via '%s': %v", uname, source.Name, err)
  444. }
  445. return nil, ErrUserNotExist{u.Id, u.Name}
  446. }