login_source_files.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. package database
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. "sync"
  8. "time"
  9. "github.com/cockroachdb/errors"
  10. "gopkg.in/ini.v1"
  11. "gogs.io/gogs/internal/auth"
  12. "gogs.io/gogs/internal/auth/github"
  13. "gogs.io/gogs/internal/auth/ldap"
  14. "gogs.io/gogs/internal/auth/pam"
  15. "gogs.io/gogs/internal/auth/smtp"
  16. "gogs.io/gogs/internal/errutil"
  17. "gogs.io/gogs/internal/osutil"
  18. )
  19. // loginSourceFilesStore is the in-memory interface for login source files stored on file system.
  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 ListLoginSourceOptions) []*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. return errors.As(err, &ErrLoginSourceNotExist{})
  43. }
  44. func (err ErrLoginSourceNotExist) Error() string {
  45. return fmt.Sprintf("login source does not exist: %v", err.args)
  46. }
  47. func (ErrLoginSourceNotExist) NotFound() bool {
  48. return true
  49. }
  50. func (s *loginSourceFiles) GetByID(id int64) (*LoginSource, error) {
  51. s.RLock()
  52. defer s.RUnlock()
  53. for _, source := range s.sources {
  54. if source.ID == id {
  55. return source, nil
  56. }
  57. }
  58. return nil, ErrLoginSourceNotExist{args: errutil.Args{"id": id}}
  59. }
  60. func (s *loginSourceFiles) Len() int {
  61. s.RLock()
  62. defer s.RUnlock()
  63. return len(s.sources)
  64. }
  65. func (s *loginSourceFiles) List(opts ListLoginSourceOptions) []*LoginSource {
  66. s.RLock()
  67. defer s.RUnlock()
  68. list := make([]*LoginSource, 0, s.Len())
  69. for _, source := range s.sources {
  70. if opts.OnlyActivated && !source.IsActived {
  71. continue
  72. }
  73. list = append(list, source)
  74. }
  75. return list
  76. }
  77. func (s *loginSourceFiles) Update(source *LoginSource) {
  78. s.Lock()
  79. defer s.Unlock()
  80. source.Updated = s.clock()
  81. for _, old := range s.sources {
  82. if old.ID == source.ID {
  83. *old = *source
  84. } else if source.IsDefault {
  85. old.IsDefault = false
  86. }
  87. }
  88. }
  89. // loadLoginSourceFiles loads login sources from file system.
  90. func loadLoginSourceFiles(authdPath string, clock func() time.Time) (loginSourceFilesStore, error) {
  91. if !osutil.IsDir(authdPath) {
  92. return &loginSourceFiles{clock: clock}, nil
  93. }
  94. store := &loginSourceFiles{clock: clock}
  95. return store, filepath.Walk(authdPath, func(path string, info os.FileInfo, err error) error {
  96. if err != nil {
  97. return err
  98. }
  99. if path == authdPath || !strings.HasSuffix(path, ".conf") {
  100. return nil
  101. } else if info.IsDir() {
  102. return filepath.SkipDir
  103. }
  104. authSource, err := ini.Load(path)
  105. if err != nil {
  106. return errors.Wrap(err, "load file")
  107. }
  108. authSource.NameMapper = ini.TitleUnderscore
  109. // Set general attributes
  110. s := authSource.Section("")
  111. loginSource := &LoginSource{
  112. ID: s.Key("id").MustInt64(),
  113. Name: s.Key("name").String(),
  114. IsActived: s.Key("is_activated").MustBool(),
  115. IsDefault: s.Key("is_default").MustBool(),
  116. File: &loginSourceFile{
  117. path: path,
  118. file: authSource,
  119. },
  120. }
  121. fi, err := os.Stat(path)
  122. if err != nil {
  123. return errors.Wrap(err, "stat file")
  124. }
  125. loginSource.Updated = fi.ModTime()
  126. // Parse authentication source file
  127. authType := s.Key("type").String()
  128. cfgSection := authSource.Section("config")
  129. switch authType {
  130. case "ldap_bind_dn":
  131. var cfg ldap.Config
  132. err = cfgSection.MapTo(&cfg)
  133. if err != nil {
  134. return errors.Wrap(err, `map "config" section`)
  135. }
  136. loginSource.Type = auth.LDAP
  137. loginSource.Provider = ldap.NewProvider(false, &cfg)
  138. case "ldap_simple_auth":
  139. var cfg ldap.Config
  140. err = cfgSection.MapTo(&cfg)
  141. if err != nil {
  142. return errors.Wrap(err, `map "config" section`)
  143. }
  144. loginSource.Type = auth.DLDAP
  145. loginSource.Provider = ldap.NewProvider(true, &cfg)
  146. case "smtp":
  147. var cfg smtp.Config
  148. err = cfgSection.MapTo(&cfg)
  149. if err != nil {
  150. return errors.Wrap(err, `map "config" section`)
  151. }
  152. loginSource.Type = auth.SMTP
  153. loginSource.Provider = smtp.NewProvider(&cfg)
  154. case "pam":
  155. var cfg pam.Config
  156. err = cfgSection.MapTo(&cfg)
  157. if err != nil {
  158. return errors.Wrap(err, `map "config" section`)
  159. }
  160. loginSource.Type = auth.PAM
  161. loginSource.Provider = pam.NewProvider(&cfg)
  162. case "github":
  163. var cfg github.Config
  164. err = cfgSection.MapTo(&cfg)
  165. if err != nil {
  166. return errors.Wrap(err, `map "config" section`)
  167. }
  168. loginSource.Type = auth.GitHub
  169. loginSource.Provider = github.NewProvider(&cfg)
  170. default:
  171. return errors.Newf("unknown type %q", authType)
  172. }
  173. store.sources = append(store.sources, loginSource)
  174. return nil
  175. })
  176. }
  177. // loginSourceFileStore is the persistent interface for a login source file.
  178. type loginSourceFileStore interface {
  179. // SetGeneral sets new value to the given key in the general (default) section.
  180. SetGeneral(name, value string)
  181. // SetConfig sets new values to the "config" section.
  182. SetConfig(cfg any) error
  183. // Save persists values to file system.
  184. Save() error
  185. }
  186. var _ loginSourceFileStore = (*loginSourceFile)(nil)
  187. type loginSourceFile struct {
  188. path string
  189. file *ini.File
  190. }
  191. func (f *loginSourceFile) SetGeneral(name, value string) {
  192. f.file.Section("").Key(name).SetValue(value)
  193. }
  194. func (f *loginSourceFile) SetConfig(cfg any) error {
  195. return f.file.Section("config").ReflectFrom(cfg)
  196. }
  197. func (f *loginSourceFile) Save() error {
  198. return f.file.SaveTo(f.path)
  199. }