You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

login.go 7.7 kB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. // Copyright github.com/juju2013. 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. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "net/smtp"
  10. "strings"
  11. "time"
  12. "github.com/go-xorm/core"
  13. "github.com/go-xorm/xorm"
  14. "github.com/gogits/gogs/modules/auth/ldap"
  15. )
  16. // Login types.
  17. const (
  18. LT_NOTYPE = iota
  19. LT_PLAIN
  20. LT_LDAP
  21. LT_SMTP
  22. )
  23. var (
  24. ErrAuthenticationAlreadyExist = errors.New("Authentication already exist")
  25. ErrAuthenticationNotExist = errors.New("Authentication does not exist")
  26. ErrAuthenticationUserUsed = errors.New("Authentication has been used by some users")
  27. )
  28. var LoginTypes = map[int]string{
  29. LT_LDAP: "LDAP",
  30. LT_SMTP: "SMTP",
  31. }
  32. var _ core.Conversion = &LDAPConfig{}
  33. var _ core.Conversion = &SMTPConfig{}
  34. type LDAPConfig struct {
  35. ldap.Ldapsource
  36. }
  37. // implement
  38. func (cfg *LDAPConfig) FromDB(bs []byte) error {
  39. return json.Unmarshal(bs, &cfg.Ldapsource)
  40. }
  41. func (cfg *LDAPConfig) ToDB() ([]byte, error) {
  42. return json.Marshal(cfg.Ldapsource)
  43. }
  44. type SMTPConfig struct {
  45. Auth string
  46. Host string
  47. Port int
  48. TLS bool
  49. }
  50. // implement
  51. func (cfg *SMTPConfig) FromDB(bs []byte) error {
  52. return json.Unmarshal(bs, cfg)
  53. }
  54. func (cfg *SMTPConfig) ToDB() ([]byte, error) {
  55. return json.Marshal(cfg)
  56. }
  57. type LoginSource struct {
  58. Id int64
  59. Type int
  60. Name string `xorm:"unique"`
  61. IsActived bool `xorm:"not null default false"`
  62. Cfg core.Conversion `xorm:"TEXT"`
  63. Created time.Time `xorm:"created"`
  64. Updated time.Time `xorm:"updated"`
  65. AllowAutoRegisted bool `xorm:"not null default false"`
  66. }
  67. func (source *LoginSource) TypeString() string {
  68. return LoginTypes[source.Type]
  69. }
  70. func (source *LoginSource) LDAP() *LDAPConfig {
  71. return source.Cfg.(*LDAPConfig)
  72. }
  73. func (source *LoginSource) SMTP() *SMTPConfig {
  74. return source.Cfg.(*SMTPConfig)
  75. }
  76. // for xorm callback
  77. func (source *LoginSource) BeforeSet(colName string, val xorm.Cell) {
  78. if colName == "type" {
  79. ty := (*val).(int64)
  80. switch ty {
  81. case LT_LDAP:
  82. source.Cfg = new(LDAPConfig)
  83. case LT_SMTP:
  84. source.Cfg = new(SMTPConfig)
  85. }
  86. }
  87. }
  88. func GetAuths() ([]*LoginSource, error) {
  89. var auths = make([]*LoginSource, 0)
  90. err := orm.Find(&auths)
  91. return auths, err
  92. }
  93. func GetLoginSourceById(id int64) (*LoginSource, error) {
  94. source := new(LoginSource)
  95. has, err := orm.Id(id).Get(source)
  96. if err != nil {
  97. return nil, err
  98. }
  99. if !has {
  100. return nil, ErrAuthenticationNotExist
  101. }
  102. return source, nil
  103. }
  104. func AddSource(source *LoginSource) error {
  105. _, err := orm.Insert(source)
  106. return err
  107. }
  108. func UpdateSource(source *LoginSource) error {
  109. _, err := orm.AllCols().Id(source.Id).Update(source)
  110. return err
  111. }
  112. func DelLoginSource(source *LoginSource) error {
  113. cnt, err := orm.Count(&User{LoginSource: source.Id})
  114. if err != nil {
  115. return err
  116. }
  117. if cnt > 0 {
  118. return ErrAuthenticationUserUsed
  119. }
  120. _, err = orm.Id(source.Id).Delete(&LoginSource{})
  121. return err
  122. }
  123. // login a user
  124. func LoginUser(uname, passwd string) (*User, error) {
  125. var u *User
  126. var emailLogin bool
  127. if strings.Contains(uname, "@") {
  128. u = &User{Email: uname}
  129. emailLogin = true
  130. } else {
  131. u = &User{LowerName: strings.ToLower(uname)}
  132. }
  133. has, err := orm.Get(u)
  134. if err != nil {
  135. return nil, err
  136. }
  137. // if email login, then we cannot auto register
  138. if emailLogin {
  139. if !has {
  140. return nil, ErrUserNotExist
  141. }
  142. }
  143. if u.LoginType == LT_NOTYPE {
  144. u.LoginType = LT_PLAIN
  145. }
  146. // for plain login, user must have existed.
  147. if u.LoginType == LT_PLAIN {
  148. if !has {
  149. return nil, ErrUserNotExist
  150. }
  151. newUser := &User{Passwd: passwd, Salt: u.Salt}
  152. newUser.EncodePasswd()
  153. if u.Passwd != newUser.Passwd {
  154. return nil, ErrUserNotExist
  155. }
  156. return u, nil
  157. } else {
  158. if !has {
  159. var sources []LoginSource
  160. cond := &LoginSource{IsActived: true, AllowAutoRegisted: true}
  161. err = orm.UseBool().Find(&sources, cond)
  162. if err != nil {
  163. return nil, err
  164. }
  165. for _, source := range sources {
  166. if source.Type == LT_LDAP {
  167. u, err := LoginUserLdapSource(nil, u.LoginName, passwd,
  168. source.Id, source.Cfg.(*LDAPConfig), true)
  169. if err == nil {
  170. return u, err
  171. }
  172. } else if source.Type == LT_SMTP {
  173. u, err := LoginUserSMTPSource(nil, u.LoginName, passwd,
  174. source.Id, source.Cfg.(*SMTPConfig), true)
  175. if err == nil {
  176. return u, err
  177. }
  178. }
  179. }
  180. return nil, ErrUserNotExist
  181. }
  182. var source LoginSource
  183. hasSource, err := orm.Id(u.LoginSource).Get(&source)
  184. if err != nil {
  185. return nil, err
  186. }
  187. if !hasSource {
  188. return nil, ErrLoginSourceNotExist
  189. }
  190. if !source.IsActived {
  191. return nil, ErrLoginSourceNotActived
  192. }
  193. switch u.LoginType {
  194. case LT_LDAP:
  195. return LoginUserLdapSource(u, u.LoginName, passwd,
  196. source.Id, source.Cfg.(*LDAPConfig), false)
  197. case LT_SMTP:
  198. return LoginUserSMTPSource(u, u.LoginName, passwd,
  199. source.Id, source.Cfg.(*SMTPConfig), false)
  200. }
  201. return nil, ErrUnsupportedLoginType
  202. }
  203. }
  204. // Query if name/passwd can login against the LDAP direcotry pool
  205. // Create a local user if success
  206. // Return the same LoginUserPlain semantic
  207. func LoginUserLdapSource(user *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) {
  208. mail, logged := cfg.Ldapsource.SearchEntry(name, passwd)
  209. if !logged {
  210. // user not in LDAP, do nothing
  211. return nil, ErrUserNotExist
  212. }
  213. if !autoRegister {
  214. return user, nil
  215. }
  216. // fake a local user creation
  217. user = &User{
  218. LowerName: strings.ToLower(name),
  219. Name: strings.ToLower(name),
  220. LoginType: LT_LDAP,
  221. LoginSource: sourceId,
  222. LoginName: name,
  223. IsActive: true,
  224. Passwd: passwd,
  225. Email: mail,
  226. }
  227. return RegisterUser(user)
  228. }
  229. type loginAuth struct {
  230. username, password string
  231. }
  232. func LoginAuth(username, password string) smtp.Auth {
  233. return &loginAuth{username, password}
  234. }
  235. func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
  236. return "LOGIN", []byte(a.username), nil
  237. }
  238. func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
  239. if more {
  240. switch string(fromServer) {
  241. case "Username:":
  242. return []byte(a.username), nil
  243. case "Password:":
  244. return []byte(a.password), nil
  245. }
  246. }
  247. return nil, nil
  248. }
  249. var (
  250. SMTP_PLAIN = "PLAIN"
  251. SMTP_LOGIN = "LOGIN"
  252. SMTPAuths = []string{SMTP_PLAIN, SMTP_LOGIN}
  253. )
  254. func SmtpAuth(addr string, a smtp.Auth) error {
  255. c, err := smtp.Dial(addr)
  256. if err != nil {
  257. return err
  258. }
  259. defer c.Close()
  260. if ok, _ := c.Extension("STARTTLS"); ok {
  261. if err = c.StartTLS(nil); err != nil {
  262. return err
  263. }
  264. }
  265. if ok, _ := c.Extension("AUTH"); ok {
  266. if err = c.Auth(a); err != nil {
  267. return err
  268. }
  269. return nil
  270. } else {
  271. return ErrUnsupportedLoginType
  272. }
  273. }
  274. // Query if name/passwd can login against the LDAP direcotry pool
  275. // Create a local user if success
  276. // Return the same LoginUserPlain semantic
  277. func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) {
  278. var auth smtp.Auth
  279. if cfg.Auth == SMTP_PLAIN {
  280. auth = smtp.PlainAuth("", name, passwd, cfg.Host)
  281. } else if cfg.Auth == SMTP_LOGIN {
  282. auth = LoginAuth(name, passwd)
  283. }
  284. err := SmtpAuth(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), auth)
  285. if err != nil {
  286. return nil, err
  287. }
  288. if !autoRegister {
  289. return user, nil
  290. }
  291. // fake a local user creation
  292. user = &User{
  293. LowerName: strings.ToLower(name),
  294. Name: strings.ToLower(name),
  295. LoginType: LT_SMTP,
  296. LoginSource: sourceId,
  297. LoginName: name,
  298. IsActive: true,
  299. Passwd: passwd,
  300. Email: name,
  301. }
  302. return RegisterUser(user)
  303. }