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.

494 lines
11 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
  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. NOTYPE LoginType = iota
  24. PLAIN
  25. LDAP
  26. SMTP
  27. PAM
  28. DLDAP
  29. )
  30. var (
  31. ErrAuthenticationAlreadyExist = errors.New("Authentication already exist")
  32. ErrAuthenticationNotExist = errors.New("Authentication does not exist")
  33. ErrAuthenticationUserUsed = errors.New("Authentication has been used by some users")
  34. )
  35. var LoginNames = map[LoginType]string{
  36. LDAP: "LDAP (via BindDN)",
  37. DLDAP: "LDAP (simple auth)",
  38. SMTP: "SMTP",
  39. 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. func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
  89. switch colName {
  90. case "type":
  91. switch LoginType((*val).(int64)) {
  92. case LDAP, DLDAP:
  93. source.Cfg = new(LDAPConfig)
  94. case SMTP:
  95. source.Cfg = new(SMTPConfig)
  96. case PAM:
  97. source.Cfg = new(PAMConfig)
  98. default:
  99. panic("unrecognized login source type: " + com.ToStr(*val))
  100. }
  101. }
  102. }
  103. func (source *LoginSource) TypeName() string {
  104. return LoginNames[source.Type]
  105. }
  106. func (source *LoginSource) IsLDAP() bool {
  107. return source.Type == LDAP
  108. }
  109. func (source *LoginSource) IsDLDAP() bool {
  110. return source.Type == DLDAP
  111. }
  112. func (source *LoginSource) IsSMTP() bool {
  113. return source.Type == SMTP
  114. }
  115. func (source *LoginSource) IsPAM() bool {
  116. return source.Type == PAM
  117. }
  118. func (source *LoginSource) UseTLS() bool {
  119. switch source.Type {
  120. case LDAP, DLDAP:
  121. return source.LDAP().UseSSL
  122. case SMTP:
  123. return source.SMTP().TLS
  124. }
  125. return false
  126. }
  127. func (source *LoginSource) SkipVerify() bool {
  128. switch source.Type {
  129. case LDAP, DLDAP:
  130. return source.LDAP().SkipVerify
  131. case SMTP:
  132. return source.SMTP().SkipVerify
  133. }
  134. return false
  135. }
  136. func (source *LoginSource) LDAP() *LDAPConfig {
  137. return source.Cfg.(*LDAPConfig)
  138. }
  139. func (source *LoginSource) SMTP() *SMTPConfig {
  140. return source.Cfg.(*SMTPConfig)
  141. }
  142. func (source *LoginSource) PAM() *PAMConfig {
  143. return source.Cfg.(*PAMConfig)
  144. }
  145. // CountLoginSources returns number of login sources.
  146. func CountLoginSources() int64 {
  147. count, _ := x.Count(new(LoginSource))
  148. return count
  149. }
  150. func CreateSource(source *LoginSource) error {
  151. _, err := x.Insert(source)
  152. return err
  153. }
  154. func LoginSources() ([]*LoginSource, error) {
  155. auths := make([]*LoginSource, 0, 5)
  156. return auths, x.Find(&auths)
  157. }
  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
  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. // Query if name/passwd can login against the LDAP directory pool
  189. // Create a local user if success
  190. // Return the same LoginUserPlain semantic
  191. // FIXME: https://github.com/gogits/gogs/issues/672
  192. func LoginUserLDAPSource(u *User, name, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
  193. cfg := source.Cfg.(*LDAPConfig)
  194. directBind := (source.Type == DLDAP)
  195. fn, sn, mail, admin, logged := cfg.SearchEntry(name, passwd, directBind)
  196. if !logged {
  197. // User not in LDAP, do nothing
  198. return nil, ErrUserNotExist{0, name}
  199. }
  200. if !autoRegister {
  201. return u, nil
  202. }
  203. // Fallback.
  204. if len(mail) == 0 {
  205. mail = fmt.Sprintf("%s@localhost", name)
  206. }
  207. u = &User{
  208. LowerName: strings.ToLower(name),
  209. Name: name,
  210. FullName: strings.TrimSpace(fn + " " + sn),
  211. LoginType: source.Type,
  212. LoginSource: source.ID,
  213. LoginName: name,
  214. Email: mail,
  215. IsAdmin: admin,
  216. IsActive: true,
  217. }
  218. return u, CreateUser(u)
  219. }
  220. // _________ __________________________
  221. // / _____/ / \__ ___/\______ \
  222. // \_____ \ / \ / \| | | ___/
  223. // / \/ Y \ | | |
  224. // /_______ /\____|__ /____| |____|
  225. // \/ \/
  226. type loginAuth struct {
  227. username, password string
  228. }
  229. func LoginAuth(username, password string) smtp.Auth {
  230. return &loginAuth{username, password}
  231. }
  232. func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
  233. return "LOGIN", []byte(a.username), nil
  234. }
  235. func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
  236. if more {
  237. switch string(fromServer) {
  238. case "Username:":
  239. return []byte(a.username), nil
  240. case "Password:":
  241. return []byte(a.password), nil
  242. }
  243. }
  244. return nil, nil
  245. }
  246. const (
  247. SMTP_PLAIN = "PLAIN"
  248. SMTP_LOGIN = "LOGIN"
  249. )
  250. var SMTPAuths = []string{SMTP_PLAIN, SMTP_LOGIN}
  251. func SMTPAuth(a smtp.Auth, cfg *SMTPConfig) error {
  252. c, err := smtp.Dial(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))
  253. if err != nil {
  254. return err
  255. }
  256. defer c.Close()
  257. if err = c.Hello("gogs"); err != nil {
  258. return err
  259. }
  260. if cfg.TLS {
  261. if ok, _ := c.Extension("STARTTLS"); ok {
  262. if err = c.StartTLS(&tls.Config{
  263. InsecureSkipVerify: cfg.SkipVerify,
  264. ServerName: cfg.Host,
  265. }); err != nil {
  266. return err
  267. }
  268. } else {
  269. return errors.New("SMTP server unsupports TLS")
  270. }
  271. }
  272. if ok, _ := c.Extension("AUTH"); ok {
  273. if err = c.Auth(a); err != nil {
  274. return err
  275. }
  276. return nil
  277. }
  278. return ErrUnsupportedLoginType
  279. }
  280. // Query if name/passwd can login against the LDAP directory pool
  281. // Create a local user if success
  282. // Return the same LoginUserPlain semantic
  283. func LoginUserSMTPSource(u *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
  284. // Verify allowed domains.
  285. if len(cfg.AllowedDomains) > 0 {
  286. idx := strings.Index(name, "@")
  287. if idx == -1 {
  288. return nil, ErrUserNotExist{0, name}
  289. } else if !com.IsSliceContainsStr(strings.Split(cfg.AllowedDomains, ","), name[idx+1:]) {
  290. return nil, ErrUserNotExist{0, name}
  291. }
  292. }
  293. var auth smtp.Auth
  294. if cfg.Auth == SMTP_PLAIN {
  295. auth = smtp.PlainAuth("", name, passwd, cfg.Host)
  296. } else if cfg.Auth == SMTP_LOGIN {
  297. auth = LoginAuth(name, passwd)
  298. } else {
  299. return nil, errors.New("Unsupported SMTP auth type")
  300. }
  301. if err := SMTPAuth(auth, cfg); err != nil {
  302. if strings.Contains(err.Error(), "Username and Password not accepted") {
  303. return nil, ErrUserNotExist{0, name}
  304. }
  305. return nil, err
  306. }
  307. if !autoRegister {
  308. return u, nil
  309. }
  310. var loginName = name
  311. idx := strings.Index(name, "@")
  312. if idx > -1 {
  313. loginName = name[:idx]
  314. }
  315. // fake a local user creation
  316. u = &User{
  317. LowerName: strings.ToLower(loginName),
  318. Name: strings.ToLower(loginName),
  319. LoginType: SMTP,
  320. LoginSource: sourceId,
  321. LoginName: name,
  322. IsActive: true,
  323. Passwd: passwd,
  324. Email: name,
  325. }
  326. err := CreateUser(u)
  327. return u, err
  328. }
  329. // __________ _____ _____
  330. // \______ \/ _ \ / \
  331. // | ___/ /_\ \ / \ / \
  332. // | | / | \/ Y \
  333. // |____| \____|__ /\____|__ /
  334. // \/ \/
  335. // Query if name/passwd can login against PAM
  336. // Create a local user if success
  337. // Return the same LoginUserPlain semantic
  338. func LoginUserPAMSource(u *User, name, passwd string, sourceId int64, cfg *PAMConfig, autoRegister bool) (*User, error) {
  339. if err := pam.PAMAuth(cfg.ServiceName, name, passwd); err != nil {
  340. if strings.Contains(err.Error(), "Authentication failure") {
  341. return nil, ErrUserNotExist{0, name}
  342. }
  343. return nil, err
  344. }
  345. if !autoRegister {
  346. return u, nil
  347. }
  348. // fake a local user creation
  349. u = &User{
  350. LowerName: strings.ToLower(name),
  351. Name: strings.ToLower(name),
  352. LoginType: PAM,
  353. LoginSource: sourceId,
  354. LoginName: name,
  355. IsActive: true,
  356. Passwd: passwd,
  357. Email: name,
  358. }
  359. err := CreateUser(u)
  360. return u, err
  361. }
  362. func ExternalUserLogin(u *User, name, passwd string, source *LoginSource, autoRegister bool) (*User, error) {
  363. if !source.IsActived {
  364. return nil, ErrLoginSourceNotActived
  365. }
  366. switch source.Type {
  367. case LDAP, DLDAP:
  368. return LoginUserLDAPSource(u, name, passwd, source, autoRegister)
  369. case SMTP:
  370. return LoginUserSMTPSource(u, name, passwd, source.ID, source.Cfg.(*SMTPConfig), autoRegister)
  371. case PAM:
  372. return LoginUserPAMSource(u, name, passwd, source.ID, source.Cfg.(*PAMConfig), autoRegister)
  373. }
  374. return nil, ErrUnsupportedLoginType
  375. }
  376. // UserSignIn validates user name and password.
  377. func UserSignIn(uname, passwd string) (*User, error) {
  378. var u *User
  379. if strings.Contains(uname, "@") {
  380. u = &User{Email: uname}
  381. } else {
  382. u = &User{LowerName: strings.ToLower(uname)}
  383. }
  384. userExists, err := x.Get(u)
  385. if err != nil {
  386. return nil, err
  387. }
  388. if userExists {
  389. switch u.LoginType {
  390. case NOTYPE, PLAIN:
  391. if u.ValidatePassword(passwd) {
  392. return u, nil
  393. }
  394. return nil, ErrUserNotExist{u.Id, u.Name}
  395. default:
  396. var source LoginSource
  397. hasSource, err := x.Id(u.LoginSource).Get(&source)
  398. if err != nil {
  399. return nil, err
  400. } else if !hasSource {
  401. return nil, ErrLoginSourceNotExist
  402. }
  403. return ExternalUserLogin(u, u.LoginName, passwd, &source, false)
  404. }
  405. }
  406. var sources []LoginSource
  407. if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true}); err != nil {
  408. return nil, err
  409. }
  410. for _, source := range sources {
  411. u, err := ExternalUserLogin(nil, uname, passwd, &source, true)
  412. if err == nil {
  413. return u, nil
  414. }
  415. log.Warn("Failed to login '%s' via '%s': %v", uname, source.Name, err)
  416. }
  417. return nil, ErrUserNotExist{u.Id, u.Name}
  418. }