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.

source.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package auth
  5. import (
  6. "fmt"
  7. "reflect"
  8. "code.gitea.io/gitea/models/db"
  9. "code.gitea.io/gitea/modules/log"
  10. "code.gitea.io/gitea/modules/timeutil"
  11. "code.gitea.io/gitea/modules/util"
  12. "xorm.io/xorm"
  13. "xorm.io/xorm/convert"
  14. )
  15. // Type represents an login type.
  16. type Type int
  17. // Note: new type must append to the end of list to maintain compatibility.
  18. const (
  19. NoType Type = iota
  20. Plain // 1
  21. LDAP // 2
  22. SMTP // 3
  23. PAM // 4
  24. DLDAP // 5
  25. OAuth2 // 6
  26. SSPI // 7
  27. )
  28. // String returns the string name of the LoginType
  29. func (typ Type) String() string {
  30. return Names[typ]
  31. }
  32. // Int returns the int value of the LoginType
  33. func (typ Type) Int() int {
  34. return int(typ)
  35. }
  36. // Names contains the name of LoginType values.
  37. var Names = map[Type]string{
  38. LDAP: "LDAP (via BindDN)",
  39. DLDAP: "LDAP (simple auth)", // Via direct bind
  40. SMTP: "SMTP",
  41. PAM: "PAM",
  42. OAuth2: "OAuth2",
  43. SSPI: "SPNEGO with SSPI",
  44. }
  45. // Config represents login config as far as the db is concerned
  46. type Config interface {
  47. convert.Conversion
  48. }
  49. // SkipVerifiable configurations provide a IsSkipVerify to check if SkipVerify is set
  50. type SkipVerifiable interface {
  51. IsSkipVerify() bool
  52. }
  53. // HasTLSer configurations provide a HasTLS to check if TLS can be enabled
  54. type HasTLSer interface {
  55. HasTLS() bool
  56. }
  57. // UseTLSer configurations provide a HasTLS to check if TLS is enabled
  58. type UseTLSer interface {
  59. UseTLS() bool
  60. }
  61. // SSHKeyProvider configurations provide ProvidesSSHKeys to check if they provide SSHKeys
  62. type SSHKeyProvider interface {
  63. ProvidesSSHKeys() bool
  64. }
  65. // RegisterableSource configurations provide RegisterSource which needs to be run on creation
  66. type RegisterableSource interface {
  67. RegisterSource() error
  68. UnregisterSource() error
  69. }
  70. var registeredConfigs = map[Type]func() Config{}
  71. // RegisterTypeConfig register a config for a provided type
  72. func RegisterTypeConfig(typ Type, exemplar Config) {
  73. if reflect.TypeOf(exemplar).Kind() == reflect.Ptr {
  74. // Pointer:
  75. registeredConfigs[typ] = func() Config {
  76. return reflect.New(reflect.ValueOf(exemplar).Elem().Type()).Interface().(Config)
  77. }
  78. return
  79. }
  80. // Not a Pointer
  81. registeredConfigs[typ] = func() Config {
  82. return reflect.New(reflect.TypeOf(exemplar)).Elem().Interface().(Config)
  83. }
  84. }
  85. // SourceSettable configurations can have their authSource set on them
  86. type SourceSettable interface {
  87. SetAuthSource(*Source)
  88. }
  89. // Source represents an external way for authorizing users.
  90. type Source struct {
  91. ID int64 `xorm:"pk autoincr"`
  92. Type Type
  93. Name string `xorm:"UNIQUE"`
  94. IsActive bool `xorm:"INDEX NOT NULL DEFAULT false"`
  95. IsSyncEnabled bool `xorm:"INDEX NOT NULL DEFAULT false"`
  96. Cfg convert.Conversion `xorm:"TEXT"`
  97. CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
  98. UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
  99. }
  100. // TableName xorm will read the table name from this method
  101. func (Source) TableName() string {
  102. return "login_source"
  103. }
  104. func init() {
  105. db.RegisterModel(new(Source))
  106. }
  107. // BeforeSet is invoked from XORM before setting the value of a field of this object.
  108. func (source *Source) BeforeSet(colName string, val xorm.Cell) {
  109. if colName == "type" {
  110. typ := Type(db.Cell2Int64(val))
  111. constructor, ok := registeredConfigs[typ]
  112. if !ok {
  113. return
  114. }
  115. source.Cfg = constructor()
  116. if settable, ok := source.Cfg.(SourceSettable); ok {
  117. settable.SetAuthSource(source)
  118. }
  119. }
  120. }
  121. // TypeName return name of this login source type.
  122. func (source *Source) TypeName() string {
  123. return Names[source.Type]
  124. }
  125. // IsLDAP returns true of this source is of the LDAP type.
  126. func (source *Source) IsLDAP() bool {
  127. return source.Type == LDAP
  128. }
  129. // IsDLDAP returns true of this source is of the DLDAP type.
  130. func (source *Source) IsDLDAP() bool {
  131. return source.Type == DLDAP
  132. }
  133. // IsSMTP returns true of this source is of the SMTP type.
  134. func (source *Source) IsSMTP() bool {
  135. return source.Type == SMTP
  136. }
  137. // IsPAM returns true of this source is of the PAM type.
  138. func (source *Source) IsPAM() bool {
  139. return source.Type == PAM
  140. }
  141. // IsOAuth2 returns true of this source is of the OAuth2 type.
  142. func (source *Source) IsOAuth2() bool {
  143. return source.Type == OAuth2
  144. }
  145. // IsSSPI returns true of this source is of the SSPI type.
  146. func (source *Source) IsSSPI() bool {
  147. return source.Type == SSPI
  148. }
  149. // HasTLS returns true of this source supports TLS.
  150. func (source *Source) HasTLS() bool {
  151. hasTLSer, ok := source.Cfg.(HasTLSer)
  152. return ok && hasTLSer.HasTLS()
  153. }
  154. // UseTLS returns true of this source is configured to use TLS.
  155. func (source *Source) UseTLS() bool {
  156. useTLSer, ok := source.Cfg.(UseTLSer)
  157. return ok && useTLSer.UseTLS()
  158. }
  159. // SkipVerify returns true if this source is configured to skip SSL
  160. // verification.
  161. func (source *Source) SkipVerify() bool {
  162. skipVerifiable, ok := source.Cfg.(SkipVerifiable)
  163. return ok && skipVerifiable.IsSkipVerify()
  164. }
  165. // CreateSource inserts a AuthSource in the DB if not already
  166. // existing with the given name.
  167. func CreateSource(source *Source) error {
  168. has, err := db.GetEngine(db.DefaultContext).Where("name=?", source.Name).Exist(new(Source))
  169. if err != nil {
  170. return err
  171. } else if has {
  172. return ErrSourceAlreadyExist{source.Name}
  173. }
  174. // Synchronization is only available with LDAP for now
  175. if !source.IsLDAP() {
  176. source.IsSyncEnabled = false
  177. }
  178. _, err = db.GetEngine(db.DefaultContext).Insert(source)
  179. if err != nil {
  180. return err
  181. }
  182. if !source.IsActive {
  183. return nil
  184. }
  185. if settable, ok := source.Cfg.(SourceSettable); ok {
  186. settable.SetAuthSource(source)
  187. }
  188. registerableSource, ok := source.Cfg.(RegisterableSource)
  189. if !ok {
  190. return nil
  191. }
  192. err = registerableSource.RegisterSource()
  193. if err != nil {
  194. // remove the AuthSource in case of errors while registering configuration
  195. if _, err := db.GetEngine(db.DefaultContext).Delete(source); err != nil {
  196. log.Error("CreateSource: Error while wrapOpenIDConnectInitializeError: %v", err)
  197. }
  198. }
  199. return err
  200. }
  201. // Sources returns a slice of all login sources found in DB.
  202. func Sources() ([]*Source, error) {
  203. auths := make([]*Source, 0, 6)
  204. return auths, db.GetEngine(db.DefaultContext).Find(&auths)
  205. }
  206. // SourcesByType returns all sources of the specified type
  207. func SourcesByType(loginType Type) ([]*Source, error) {
  208. sources := make([]*Source, 0, 1)
  209. if err := db.GetEngine(db.DefaultContext).Where("type = ?", loginType).Find(&sources); err != nil {
  210. return nil, err
  211. }
  212. return sources, nil
  213. }
  214. // AllActiveSources returns all active sources
  215. func AllActiveSources() ([]*Source, error) {
  216. sources := make([]*Source, 0, 5)
  217. if err := db.GetEngine(db.DefaultContext).Where("is_active = ?", true).Find(&sources); err != nil {
  218. return nil, err
  219. }
  220. return sources, nil
  221. }
  222. // ActiveSources returns all active sources of the specified type
  223. func ActiveSources(tp Type) ([]*Source, error) {
  224. sources := make([]*Source, 0, 1)
  225. if err := db.GetEngine(db.DefaultContext).Where("is_active = ? and type = ?", true, tp).Find(&sources); err != nil {
  226. return nil, err
  227. }
  228. return sources, nil
  229. }
  230. // IsSSPIEnabled returns true if there is at least one activated login
  231. // source of type LoginSSPI
  232. func IsSSPIEnabled() bool {
  233. if !db.HasEngine {
  234. return false
  235. }
  236. sources, err := ActiveSources(SSPI)
  237. if err != nil {
  238. log.Error("ActiveSources: %v", err)
  239. return false
  240. }
  241. return len(sources) > 0
  242. }
  243. // GetSourceByID returns login source by given ID.
  244. func GetSourceByID(id int64) (*Source, error) {
  245. source := new(Source)
  246. if id == 0 {
  247. source.Cfg = registeredConfigs[NoType]()
  248. // Set this source to active
  249. // FIXME: allow disabling of db based password authentication in future
  250. source.IsActive = true
  251. return source, nil
  252. }
  253. has, err := db.GetEngine(db.DefaultContext).ID(id).Get(source)
  254. if err != nil {
  255. return nil, err
  256. } else if !has {
  257. return nil, ErrSourceNotExist{id}
  258. }
  259. return source, nil
  260. }
  261. // UpdateSource updates a Source record in DB.
  262. func UpdateSource(source *Source) error {
  263. var originalSource *Source
  264. if source.IsOAuth2() {
  265. // keep track of the original values so we can restore in case of errors while registering OAuth2 providers
  266. var err error
  267. if originalSource, err = GetSourceByID(source.ID); err != nil {
  268. return err
  269. }
  270. }
  271. _, err := db.GetEngine(db.DefaultContext).ID(source.ID).AllCols().Update(source)
  272. if err != nil {
  273. return err
  274. }
  275. if !source.IsActive {
  276. return nil
  277. }
  278. if settable, ok := source.Cfg.(SourceSettable); ok {
  279. settable.SetAuthSource(source)
  280. }
  281. registerableSource, ok := source.Cfg.(RegisterableSource)
  282. if !ok {
  283. return nil
  284. }
  285. err = registerableSource.RegisterSource()
  286. if err != nil {
  287. // restore original values since we cannot update the provider it self
  288. if _, err := db.GetEngine(db.DefaultContext).ID(source.ID).AllCols().Update(originalSource); err != nil {
  289. log.Error("UpdateSource: Error while wrapOpenIDConnectInitializeError: %v", err)
  290. }
  291. }
  292. return err
  293. }
  294. // CountSources returns number of login sources.
  295. func CountSources() int64 {
  296. count, _ := db.GetEngine(db.DefaultContext).Count(new(Source))
  297. return count
  298. }
  299. // ErrSourceNotExist represents a "SourceNotExist" kind of error.
  300. type ErrSourceNotExist struct {
  301. ID int64
  302. }
  303. // IsErrSourceNotExist checks if an error is a ErrSourceNotExist.
  304. func IsErrSourceNotExist(err error) bool {
  305. _, ok := err.(ErrSourceNotExist)
  306. return ok
  307. }
  308. func (err ErrSourceNotExist) Error() string {
  309. return fmt.Sprintf("login source does not exist [id: %d]", err.ID)
  310. }
  311. // Unwrap unwraps this as a ErrNotExist err
  312. func (err ErrSourceNotExist) Unwrap() error {
  313. return util.ErrNotExist
  314. }
  315. // ErrSourceAlreadyExist represents a "SourceAlreadyExist" kind of error.
  316. type ErrSourceAlreadyExist struct {
  317. Name string
  318. }
  319. // IsErrSourceAlreadyExist checks if an error is a ErrSourceAlreadyExist.
  320. func IsErrSourceAlreadyExist(err error) bool {
  321. _, ok := err.(ErrSourceAlreadyExist)
  322. return ok
  323. }
  324. func (err ErrSourceAlreadyExist) Error() string {
  325. return fmt.Sprintf("login source already exists [name: %s]", err.Name)
  326. }
  327. // Unwrap unwraps this as a ErrExist err
  328. func (err ErrSourceAlreadyExist) Unwrap() error {
  329. return util.ErrAlreadyExist
  330. }
  331. // ErrSourceInUse represents a "SourceInUse" kind of error.
  332. type ErrSourceInUse struct {
  333. ID int64
  334. }
  335. // IsErrSourceInUse checks if an error is a ErrSourceInUse.
  336. func IsErrSourceInUse(err error) bool {
  337. _, ok := err.(ErrSourceInUse)
  338. return ok
  339. }
  340. func (err ErrSourceInUse) Error() string {
  341. return fmt.Sprintf("login source is still used by some users [id: %d]", err.ID)
  342. }