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.

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