github.com/extrame/fabric-ca@v2.0.0-alpha+incompatible/lib/server/user/user.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package user 8 9 import ( 10 "database/sql" 11 "encoding/json" 12 "strings" 13 14 "github.com/cloudflare/cfssl/log" 15 "github.com/hyperledger/fabric-ca/api" 16 "github.com/hyperledger/fabric-ca/lib/spi" 17 "github.com/jmoiron/sqlx" 18 "github.com/pkg/errors" 19 "golang.org/x/crypto/bcrypt" 20 ) 21 22 // DbTxResult returns information on any affiliations and/or identities affected 23 // during a database transaction 24 type DbTxResult struct { 25 Affiliations []spi.Affiliation 26 Identities []User 27 } 28 29 // Registry is the API for retreiving users and groups 30 type Registry interface { 31 GetUser(id string, attrs []string) (User, error) 32 InsertUser(user *Info) error 33 UpdateUser(user *Info, updatePass bool) error 34 DeleteUser(id string) (User, error) 35 GetAffiliation(name string) (spi.Affiliation, error) 36 GetAllAffiliations(name string) (*sqlx.Rows, error) 37 InsertAffiliation(name string, prekey string, level int) error 38 GetUserLessThanLevel(version int) ([]User, error) 39 GetFilteredUsers(affiliation, types string) (*sqlx.Rows, error) 40 DeleteAffiliation(name string, force, identityRemoval, isRegistrar bool) (*DbTxResult, error) 41 ModifyAffiliation(oldAffiliation, newAffiliation string, force, isRegistrar bool) (*DbTxResult, error) 42 GetAffiliationTree(name string) (*DbTxResult, error) 43 } 44 45 // User is the SPI for a user 46 type User interface { 47 // Returns the enrollment ID of the user 48 GetName() string 49 // Return the type of the user 50 GetType() string 51 // Return the max enrollments of the user 52 GetMaxEnrollments() int 53 // Login the user with a password 54 Login(password string, caMaxEnrollment int) error 55 // Get the complete path for the user's affiliation. 56 GetAffiliationPath() []string 57 // GetAttribute returns the value for an attribute name 58 GetAttribute(name string) (*api.Attribute, error) 59 // GetAttributes returns the requested attributes 60 GetAttributes(attrNames []string) ([]api.Attribute, error) 61 // ModifyAttributes adds, removes, or deletes attribute 62 ModifyAttributes(attrs []api.Attribute) error 63 // LoginComplete completes the login process by incrementing the state of the user 64 LoginComplete() error 65 // Revoke will revoke the user, setting the state of the user to be -1 66 Revoke() error 67 // IsRevoked returns back true if user is revoked 68 IsRevoked() bool 69 // GetLevel returns the level of the user, level is used to verify if the user needs migration 70 GetLevel() int 71 // SetLevel sets the level of the user 72 SetLevel(level int) error 73 // IncrementIncorrectPasswordAttempts updates the incorrect password count of user 74 IncrementIncorrectPasswordAttempts() error 75 // GetFailedLoginAttempts returns the number of times the user has entered an incorrect password 76 GetFailedLoginAttempts() int 77 } 78 79 // Record defines the properties of a user 80 type Record struct { 81 Name string `db:"id"` 82 Pass []byte `db:"token"` 83 Type string `db:"type"` 84 Affiliation string `db:"affiliation"` 85 Attributes string `db:"attributes"` 86 State int `db:"state"` 87 MaxEnrollments int `db:"max_enrollments"` 88 Level int `db:"level"` 89 IncorrectPasswordAttempts int `db:"incorrect_password_attempts"` 90 } 91 92 // Info contains information about a user 93 type Info struct { 94 Name string 95 Pass string `mask:"password"` 96 Type string 97 Affiliation string 98 Attributes []api.Attribute 99 State int 100 MaxEnrollments int 101 Level int 102 IncorrectPasswordAttempts int 103 } 104 105 //go:generate counterfeiter -o mocks/userDB.go -fake-name UserDB . userDB 106 107 type userDB interface { 108 Exec(funcName, query string, args ...interface{}) (sql.Result, error) 109 Get(funcName string, dest interface{}, query string, args ...interface{}) error 110 Rebind(query string) string 111 Queryx(funcName, query string, args ...interface{}) (*sqlx.Rows, error) 112 } 113 114 // Impl is the databases representation of a user 115 type Impl struct { 116 Info 117 pass []byte 118 attrs map[string]api.Attribute 119 db userDB 120 } 121 122 // New creates a DBUser object from the DB user record 123 func New(userRec *Record, db userDB) *Impl { 124 var user = new(Impl) 125 user.Name = userRec.Name 126 user.pass = userRec.Pass 127 user.State = userRec.State 128 user.MaxEnrollments = userRec.MaxEnrollments 129 user.Affiliation = userRec.Affiliation 130 user.Type = userRec.Type 131 user.Level = userRec.Level 132 user.IncorrectPasswordAttempts = userRec.IncorrectPasswordAttempts 133 134 var attrs []api.Attribute 135 json.Unmarshal([]byte(userRec.Attributes), &attrs) 136 user.Attributes = attrs 137 138 user.attrs = make(map[string]api.Attribute) 139 for _, attr := range attrs { 140 user.attrs[attr.Name] = api.Attribute{ 141 Name: attr.Name, 142 Value: attr.Value, 143 ECert: attr.ECert, 144 } 145 } 146 147 user.db = db 148 return user 149 } 150 151 // GetName returns the enrollment ID of the user 152 func (u *Impl) GetName() string { 153 return u.Name 154 } 155 156 // GetPass returns the hashed password of the user 157 func (u *Impl) GetPass() []byte { 158 return u.pass 159 } 160 161 // GetType returns the type of the user 162 func (u *Impl) GetType() string { 163 return u.Type 164 } 165 166 // GetMaxEnrollments returns the max enrollments of the user 167 func (u *Impl) GetMaxEnrollments() int { 168 return u.MaxEnrollments 169 } 170 171 // GetLevel returns the level of the user 172 func (u *Impl) GetLevel() int { 173 return u.Level 174 } 175 176 // SetLevel sets the level of the user 177 func (u *Impl) SetLevel(level int) error { 178 return u.setLevel(nil, level) 179 } 180 181 // SetLevelTx sets the level of the user 182 func (u *Impl) SetLevelTx(tx userDB, level int) error { 183 return u.setLevel(tx, level) 184 } 185 186 func (u *Impl) setLevel(tx userDB, level int) (err error) { 187 query := "UPDATE users SET level = ? where (id = ?)" 188 id := u.GetName() 189 var res sql.Result 190 if tx != nil { 191 res, err = tx.Exec("SetLevel", tx.Rebind(query), level, id) 192 if err != nil { 193 return err 194 } 195 } else { 196 res, err = u.db.Exec("SetLevel", u.db.Rebind(query), level, id) 197 if err != nil { 198 return err 199 } 200 } 201 202 numRowsAffected, err := res.RowsAffected() 203 if err != nil { 204 return errors.Wrap(err, "Failed to get number of rows affected") 205 } 206 207 if numRowsAffected == 0 { 208 return errors.Errorf("No rows were affected when updating the state of identity %s", id) 209 } 210 211 if numRowsAffected != 1 { 212 return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, id) 213 } 214 return nil 215 } 216 217 // Login the user with a password 218 func (u *Impl) Login(pass string, caMaxEnrollments int) error { 219 log.Debugf("DB: Login user %s with max enrollments of %d and state of %d", u.Name, u.MaxEnrollments, u.State) 220 221 // Check the password by comparing to stored hash 222 err := bcrypt.CompareHashAndPassword(u.pass, []byte(pass)) 223 if err != nil { 224 err2 := u.IncrementIncorrectPasswordAttempts() 225 if err2 != nil { 226 return errors.Wrap(err2, "Failed to mark incorrect password attempt") 227 } 228 return errors.Wrap(err, "Password mismatch") 229 } 230 231 if u.MaxEnrollments == 0 { 232 return errors.Errorf("Zero is an invalid value for maximum enrollments on identity '%s'", u.Name) 233 } 234 235 if u.State == -1 { 236 return errors.Errorf("User %s is revoked; access denied", u.Name) 237 } 238 239 // If max enrollment value of user is greater than allowed by CA, using CA max enrollment value for user 240 if caMaxEnrollments != -1 && (u.MaxEnrollments > caMaxEnrollments || u.MaxEnrollments == -1) { 241 log.Debugf("Max enrollment value (%d) of identity is greater than allowed by CA, using CA max enrollment value of %d", u.MaxEnrollments, caMaxEnrollments) 242 u.MaxEnrollments = caMaxEnrollments 243 } 244 245 // If maxEnrollments is set to -1, user has unlimited enrollment 246 // If the maxEnrollments is set (i.e. >= 1), make sure we haven't exceeded this number of logins. 247 // The state variable keeps track of the number of previously successful logins. 248 if u.MaxEnrollments != -1 && u.State >= u.MaxEnrollments { 249 return errors.Errorf("The identity %s has already enrolled %d times, it has reached its maximum enrollment allowance", u.Name, u.MaxEnrollments) 250 } 251 252 log.Debugf("DB: identity %s successfully logged in", u.Name) 253 254 return u.resetIncorrectLoginAttempts() 255 } 256 257 func (u *Impl) resetIncorrectLoginAttempts() error { 258 var passAttempts int 259 err := u.db.Get("ResetIncorrectLoginAttempts", &passAttempts, u.db.Rebind("Select incorrect_password_attempts FROM users WHERE (id = ?)"), u.GetName()) 260 if err != nil { 261 return errors.Wrapf(err, "Failed to get incorrect password attempt for %s", u.Name) 262 } 263 264 // Incorrect password attempts already at zero, don't need to reset 265 if passAttempts == 0 { 266 return nil 267 } 268 269 resetSQL := "UPDATE users SET incorrect_password_attempts = 0 WHERE (id = ?)" 270 res, err := u.db.Exec("ResetIncorrectLoginAttempts", u.db.Rebind(resetSQL), u.GetName()) 271 if err != nil { 272 return errors.Wrapf(err, "Failed to update incorrect password attempt count to 0 for %s", u.Name) 273 } 274 275 numRowsAffected, err := res.RowsAffected() 276 if err != nil { 277 return errors.Wrap(err, "db.RowsAffected failed") 278 } 279 280 if numRowsAffected == 0 { 281 return errors.Errorf("No rows were affected when updating the state of identity %s", u.Name) 282 } 283 284 if numRowsAffected != 1 { 285 return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, u.Name) 286 } 287 288 return nil 289 } 290 291 // LoginComplete completes the login process by incrementing the state of the user 292 func (u *Impl) LoginComplete() error { 293 var stateUpdateSQL string 294 var args []interface{} 295 var err error 296 297 state := u.State + 1 298 args = append(args, u.Name) 299 if u.MaxEnrollments == -1 { 300 // unlimited so no state check 301 stateUpdateSQL = "UPDATE users SET state = state + 1 WHERE (id = ?)" 302 } else { 303 // state must be less than max enrollments 304 stateUpdateSQL = "UPDATE users SET state = state + 1 WHERE (id = ? AND state < ?)" 305 args = append(args, u.MaxEnrollments) 306 } 307 res, err := u.db.Exec("LoginComplete", u.db.Rebind(stateUpdateSQL), args...) 308 if err != nil { 309 return errors.Wrapf(err, "Failed to update state of identity %s to %d", u.Name, state) 310 } 311 312 numRowsAffected, err := res.RowsAffected() 313 if err != nil { 314 return errors.Wrap(err, "Failed to get number of rows affected") 315 } 316 317 if numRowsAffected == 0 { 318 return errors.Errorf("No rows were affected when updating the state of identity %s", u.Name) 319 } 320 321 if numRowsAffected != 1 { 322 return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, u.Name) 323 } 324 325 u.State = u.State + 1 326 log.Debugf("Successfully incremented state for identity %s to %d", u.Name, state) 327 return nil 328 329 } 330 331 // GetAffiliationPath returns the complete path for the user's affiliation. 332 func (u *Impl) GetAffiliationPath() []string { 333 affiliationPath := strings.Split(u.Affiliation, ".") 334 return affiliationPath 335 } 336 337 // GetAttribute returns the value for an attribute name 338 func (u *Impl) GetAttribute(name string) (*api.Attribute, error) { 339 value, hasAttr := u.attrs[name] 340 if !hasAttr { 341 return nil, errors.Errorf("User does not have attribute '%s'", name) 342 } 343 return &value, nil 344 } 345 346 // GetAttributes returns the requested attributes. Return all the user's 347 // attributes if nil is passed in 348 func (u *Impl) GetAttributes(attrNames []string) ([]api.Attribute, error) { 349 var attrs []api.Attribute 350 if attrNames == nil { 351 for _, value := range u.attrs { 352 attrs = append(attrs, value) 353 } 354 return attrs, nil 355 } 356 357 for _, name := range attrNames { 358 value, hasAttr := u.attrs[name] 359 if !hasAttr { 360 return nil, errors.Errorf("User does not have attribute '%s'", name) 361 } 362 attrs = append(attrs, value) 363 } 364 return attrs, nil 365 } 366 367 // Revoke will revoke the user, setting the state of the user to be -1 368 func (u *Impl) Revoke() error { 369 stateUpdateSQL := "UPDATE users SET state = -1 WHERE (id = ?)" 370 371 res, err := u.db.Exec("Revoke", u.db.Rebind(stateUpdateSQL), u.GetName()) 372 if err != nil { 373 return errors.Wrapf(err, "Failed to update state of identity %s to -1", u.Name) 374 } 375 376 numRowsAffected, err := res.RowsAffected() 377 if err != nil { 378 return errors.Wrap(err, "Failed to get number of rows affected") 379 } 380 381 if numRowsAffected == 0 { 382 return errors.Errorf("No rows were affected when updating the state of identity %s", u.Name) 383 } 384 385 if numRowsAffected != 1 { 386 return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, u.Name) 387 } 388 389 u.State = -1 390 log.Debugf("Successfully incremented state for identity %s to -1", u.Name) 391 return nil 392 } 393 394 // IsRevoked returns back true if user is revoked 395 func (u *Impl) IsRevoked() bool { 396 if u.State == -1 { 397 return true 398 } 399 return false 400 } 401 402 // ModifyAttributesTx adds a new attribute, modifies existing attribute, or delete attribute 403 func (u *Impl) ModifyAttributesTx(tx userDB, newAttrs []api.Attribute) error { 404 return u.modifyAttributes(tx, newAttrs) 405 } 406 407 // ModifyAttributes adds a new attribute, modifies existing attribute, or delete attribute 408 func (u *Impl) ModifyAttributes(newAttrs []api.Attribute) error { 409 return u.modifyAttributes(nil, newAttrs) 410 } 411 412 func (u *Impl) modifyAttributes(tx userDB, newAttrs []api.Attribute) error { 413 log.Debugf("Modify Attributes: %+v", newAttrs) 414 currentAttrs, _ := u.GetAttributes(nil) 415 userAttrs := GetNewAttributes(currentAttrs, newAttrs) 416 417 attrBytes, err := json.Marshal(userAttrs) 418 if err != nil { 419 return err 420 } 421 422 query := "UPDATE users SET attributes = ? WHERE (id = ?)" 423 id := u.GetName() 424 var res sql.Result 425 if tx == nil { 426 res, err = u.db.Exec("ModifyAttributes", u.db.Rebind(query), string(attrBytes), id) 427 if err != nil { 428 return err 429 } 430 } else { 431 res, err = tx.Exec("ModifyAttributes", tx.Rebind(query), string(attrBytes), id) 432 if err != nil { 433 return err 434 } 435 } 436 437 numRowsAffected, err := res.RowsAffected() 438 if err != nil { 439 return errors.Wrap(err, "Failed to get number of rows affected") 440 } 441 442 if numRowsAffected == 0 { 443 return errors.Errorf("No rows were affected when updating the state of identity %s", id) 444 } 445 446 if numRowsAffected != 1 { 447 return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, id) 448 } 449 return nil 450 } 451 452 // GetNewAttributes updates existing attribute, or add attribute if it does not already exist 453 func GetNewAttributes(modifyAttrs, newAttrs []api.Attribute) []api.Attribute { 454 var attr api.Attribute 455 for _, attr = range newAttrs { 456 log.Debugf("Attribute request: %+v", attr) 457 found := false 458 for i := range modifyAttrs { 459 if modifyAttrs[i].Name == attr.Name { 460 if attr.Value == "" { 461 log.Debugf("Deleting attribute: %+v", modifyAttrs[i]) 462 if i == len(modifyAttrs)-1 { 463 modifyAttrs = modifyAttrs[:len(modifyAttrs)-1] 464 } else { 465 modifyAttrs = append(modifyAttrs[:i], modifyAttrs[i+1:]...) 466 } 467 } else { 468 log.Debugf("Updating existing attribute from '%+v' to '%+v'", modifyAttrs[i], attr) 469 modifyAttrs[i].Value = attr.Value 470 modifyAttrs[i].ECert = attr.ECert 471 } 472 found = true 473 break 474 } 475 } 476 if !found && attr.Value != "" { 477 log.Debugf("Adding '%+v' as new attribute", attr) 478 modifyAttrs = append(modifyAttrs, attr) 479 } 480 } 481 return modifyAttrs 482 } 483 484 // IncrementIncorrectPasswordAttempts updates the incorrect password count of user 485 func (u *Impl) IncrementIncorrectPasswordAttempts() error { 486 log.Debugf("Incorrect password entered by user '%s'", u.GetName()) 487 query := "UPDATE users SET incorrect_password_attempts = incorrect_password_attempts + 1 where (id = ?)" 488 id := u.GetName() 489 res, err := u.db.Exec("IncrementIncorrectPasswordAttempts", u.db.Rebind(query), id) 490 if err != nil { 491 return err 492 } 493 numRowsAffected, err := res.RowsAffected() 494 if err != nil { 495 return errors.Wrap(err, "Failed to get number of rows affected") 496 } 497 498 if numRowsAffected == 0 { 499 return errors.Errorf("No rows were affected when updating the state of identity %s", id) 500 } 501 502 if numRowsAffected != 1 { 503 return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, id) 504 } 505 return nil 506 } 507 508 // GetFailedLoginAttempts returns the number of times the user has entered an incorrect password 509 func (u *Impl) GetFailedLoginAttempts() int { 510 return u.IncorrectPasswordAttempts 511 } 512 513 // Migrate will migrate the user to the latest version 514 func (u *Impl) Migrate(tx userDB) error { 515 currentLevel := u.GetLevel() 516 if currentLevel < 1 { 517 err := u.migrateUserToLevel1(tx) 518 if err != nil { 519 return err 520 } 521 currentLevel++ 522 } 523 return nil 524 } 525 526 func (u *Impl) migrateUserToLevel1(tx userDB) error { 527 log.Debugf("Migrating user '%s' to level 1", u.GetName()) 528 529 // Update identity to level 1 530 _, err := u.GetAttribute("hf.Registrar.Roles") // Check if user is a registrar 531 if err == nil { 532 _, err := u.GetAttribute("hf.Registrar.Attributes") // Check if user already has "hf.Registrar.Attributes" attribute 533 if err != nil { 534 newAttr := api.Attribute{Name: "hf.Registrar.Attributes", Value: "*"} 535 err := u.ModifyAttributesTx(tx, []api.Attribute{newAttr}) 536 if err != nil { 537 return errors.WithMessage(err, "Failed to set attribute") 538 } 539 u.attrs[newAttr.Name] = newAttr 540 } 541 } 542 543 err = u.setLevel(tx, 1) 544 if err != nil { 545 return errors.WithMessage(err, "Failed to update level of user") 546 } 547 548 return nil 549 } 550 551 // Affilation is interface that defines functions needed to get a user's affiliation 552 type Affilation interface { 553 GetAffiliationPath() []string 554 } 555 556 // GetAffiliation return a joined version version of the affiliation path with '.' as the seperator 557 func GetAffiliation(user Affilation) string { 558 return strings.Join(user.GetAffiliationPath(), ".") 559 } 560 561 // GetUserLessThanLevel returns all identities that are less than the level specified 562 // Otherwise, returns no users if requested level is zero 563 func GetUserLessThanLevel(tx userDB, level int) ([]*Impl, error) { 564 if level == 0 { 565 return []*Impl{}, nil 566 } 567 568 rows, err := tx.Queryx("GetUserLessThanLevel", tx.Rebind("SELECT * FROM users WHERE (level < ?) OR (level IS NULL)"), level) 569 if err != nil { 570 return nil, errors.Wrap(err, "Failed to get identities that need to be updated") 571 } 572 573 allUsers := []*Impl{} 574 if rows != nil { 575 for rows.Next() { 576 var user Record 577 rows.StructScan(&user) 578 dbUser := New(&user, nil) 579 allUsers = append(allUsers, dbUser) 580 } 581 } 582 583 return allUsers, nil 584 }