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