login_source_files.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // Copyright 2020 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 db
  5. import (
  6. "fmt"
  7. "os"
  8. "path/filepath"
  9. "strings"
  10. "sync"
  11. "time"
  12. "github.com/pkg/errors"
  13. "gopkg.in/ini.v1"
  14. "gogs.io/gogs/internal/errutil"
  15. "gogs.io/gogs/internal/osutil"
  16. )
  17. // loginSourceFilesStore is the in-memory interface for login source files stored on file system.
  18. //
  19. // NOTE: All methods are sorted in alphabetical order.
  20. type loginSourceFilesStore interface {
  21. // GetByID returns a clone of login source by given ID.
  22. GetByID(id int64) (*LoginSource, error)
  23. // Len returns number of login sources.
  24. Len() int
  25. // List returns a list of login sources filtered by options.
  26. List(opts ListLoginSourceOpts) []*LoginSource
  27. // Update updates in-memory copy of the authentication source.
  28. Update(source *LoginSource)
  29. }
  30. var _ loginSourceFilesStore = (*loginSourceFiles)(nil)
  31. // loginSourceFiles contains authentication sources configured and loaded from local files.
  32. type loginSourceFiles struct {
  33. sync.RWMutex
  34. sources []*LoginSource
  35. clock func() time.Time
  36. }
  37. var _ errutil.NotFound = (*ErrLoginSourceNotExist)(nil)
  38. type ErrLoginSourceNotExist struct {
  39. args errutil.Args
  40. }
  41. func IsErrLoginSourceNotExist(err error) bool {
  42. _, ok := err.(ErrLoginSourceNotExist)
  43. return ok
  44. }
  45. func (err ErrLoginSourceNotExist) Error() string {
  46. return fmt.Sprintf("login source does not exist: %v", err.args)
  47. }
  48. func (ErrLoginSourceNotExist) NotFound() bool {
  49. return true
  50. }
  51. func (s *loginSourceFiles) GetByID(id int64) (*LoginSource, error) {
  52. s.RLock()
  53. defer s.RUnlock()
  54. for _, source := range s.sources {
  55. if source.ID == id {
  56. return source, nil
  57. }
  58. }
  59. return nil, ErrLoginSourceNotExist{args: errutil.Args{"id": id}}
  60. }
  61. func (s *loginSourceFiles) Len() int {
  62. s.RLock()
  63. defer s.RUnlock()
  64. return len(s.sources)
  65. }
  66. func (s *loginSourceFiles) List(opts ListLoginSourceOpts) []*LoginSource {
  67. s.RLock()
  68. defer s.RUnlock()
  69. list := make([]*LoginSource, 0, s.Len())
  70. for _, source := range s.sources {
  71. if opts.OnlyActivated && !source.IsActived {
  72. continue
  73. }
  74. list = append(list, source)
  75. }
  76. return list
  77. }
  78. func (s *loginSourceFiles) Update(source *LoginSource) {
  79. s.Lock()
  80. defer s.Unlock()
  81. source.Updated = s.clock()
  82. for _, old := range s.sources {
  83. if old.ID == source.ID {
  84. *old = *source
  85. } else if source.IsDefault {
  86. old.IsDefault = false
  87. }
  88. }
  89. }
  90. // loadLoginSourceFiles loads login sources from file system.
  91. func loadLoginSourceFiles(authdPath string, clock func() time.Time) (loginSourceFilesStore, error) {
  92. if !osutil.IsDir(authdPath) {
  93. return &loginSourceFiles{clock: clock}, nil
  94. }
  95. store := &loginSourceFiles{clock: clock}
  96. return store, filepath.Walk(authdPath, func(path string, info os.FileInfo, err error) error {
  97. if err != nil {
  98. return err
  99. }
  100. if path == authdPath || !strings.HasSuffix(path, ".conf") {
  101. return nil
  102. } else if info.IsDir() {
  103. return filepath.SkipDir
  104. }
  105. authSource, err := ini.Load(path)
  106. if err != nil {
  107. return errors.Wrap(err, "load file")
  108. }
  109. authSource.NameMapper = ini.TitleUnderscore
  110. // Set general attributes
  111. s := authSource.Section("")
  112. loginSource := &LoginSource{
  113. ID: s.Key("id").MustInt64(),
  114. Name: s.Key("name").String(),
  115. IsActived: s.Key("is_activated").MustBool(),
  116. IsDefault: s.Key("is_default").MustBool(),
  117. File: &loginSourceFile{
  118. path: path,
  119. file: authSource,
  120. },
  121. }
  122. fi, err := os.Stat(path)
  123. if err != nil {
  124. return errors.Wrap(err, "stat file")
  125. }
  126. loginSource.Updated = fi.ModTime()
  127. // Parse authentication source file
  128. authType := s.Key("type").String()
  129. switch authType {
  130. case "ldap_bind_dn":
  131. loginSource.Type = LoginLDAP
  132. loginSource.Config = &LDAPConfig{}
  133. case "ldap_simple_auth":
  134. loginSource.Type = LoginDLDAP
  135. loginSource.Config = &LDAPConfig{}
  136. case "smtp":
  137. loginSource.Type = LoginSMTP
  138. loginSource.Config = &SMTPConfig{}
  139. case "pam":
  140. loginSource.Type = LoginPAM
  141. loginSource.Config = &PAMConfig{}
  142. case "github":
  143. loginSource.Type = LoginGitHub
  144. loginSource.Config = &GitHubConfig{}
  145. default:
  146. return fmt.Errorf("unknown type %q", authType)
  147. }
  148. if err = authSource.Section("config").MapTo(loginSource.Config); err != nil {
  149. return errors.Wrap(err, `map "config" section`)
  150. }
  151. store.sources = append(store.sources, loginSource)
  152. return nil
  153. })
  154. }
  155. // loginSourceFileStore is the persistent interface for a login source file.
  156. type loginSourceFileStore interface {
  157. // SetGeneral sets new value to the given key in the general (default) section.
  158. SetGeneral(name, value string)
  159. // SetConfig sets new values to the "config" section.
  160. SetConfig(cfg interface{}) error
  161. // Save persists values to file system.
  162. Save() error
  163. }
  164. var _ loginSourceFileStore = (*loginSourceFile)(nil)
  165. type loginSourceFile struct {
  166. path string
  167. file *ini.File
  168. }
  169. func (f *loginSourceFile) SetGeneral(name, value string) {
  170. f.file.Section("").Key(name).SetValue(value)
  171. }
  172. func (f *loginSourceFile) SetConfig(cfg interface{}) error {
  173. return f.file.Section("config").ReflectFrom(cfg)
  174. }
  175. func (f *loginSourceFile) Save() error {
  176. return f.file.SaveTo(f.path)
  177. }