github.com/silveraid/fabric-ca@v1.1.0-preview.0.20180127000700-71974f53ab08/lib/dbaccessor.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package lib 18 19 import ( 20 "encoding/json" 21 "strings" 22 23 "github.com/hyperledger/fabric-ca/lib/attr" 24 25 "github.com/pkg/errors" 26 27 "github.com/cloudflare/cfssl/log" 28 "github.com/hyperledger/fabric-ca/api" 29 "github.com/hyperledger/fabric-ca/lib/spi" 30 "golang.org/x/crypto/bcrypt" 31 "golang.org/x/crypto/ocsp" 32 33 "github.com/jmoiron/sqlx" 34 "github.com/kisielk/sqlstruct" 35 _ "github.com/mattn/go-sqlite3" // Needed to support sqlite 36 ) 37 38 // Match to sqlx 39 func init() { 40 sqlstruct.TagName = "db" 41 } 42 43 const ( 44 insertUser = ` 45 INSERT INTO users (id, token, type, affiliation, attributes, state, max_enrollments, level) 46 VALUES (:id, :token, :type, :affiliation, :attributes, :state, :max_enrollments, :level);` 47 48 deleteUser = ` 49 DELETE FROM users 50 WHERE (id = ?);` 51 52 updateUser = ` 53 UPDATE users 54 SET token = :token, type = :type, affiliation = :affiliation, attributes = :attributes, state = :state, max_enrollments = :max_enrollments, level = :level 55 WHERE (id = :id);` 56 57 getUser = ` 58 SELECT * FROM users 59 WHERE (id = ?)` 60 61 insertAffiliation = ` 62 INSERT INTO affiliations (name, prekey, level) 63 VALUES (?, ?, ?)` 64 65 deleteAffiliation = ` 66 DELETE FROM affiliations 67 WHERE (name = ?)` 68 69 getAffiliationQuery = ` 70 SELECT * FROM affiliations 71 WHERE (name = ?)` 72 73 getAllAffiliationsQuery = ` 74 SELECT * FROM affiliations 75 WHERE ((name = ?) OR (name LIKE ?))` 76 ) 77 78 // UserRecord defines the properties of a user 79 type UserRecord struct { 80 Name string `db:"id"` 81 Pass []byte `db:"token"` 82 Type string `db:"type"` 83 Affiliation string `db:"affiliation"` 84 Attributes string `db:"attributes"` 85 State int `db:"state"` 86 MaxEnrollments int `db:"max_enrollments"` 87 Level int `db:"level"` 88 } 89 90 // AffiliationRecord defines the properties of an affiliation 91 type AffiliationRecord struct { 92 Name string `db:"name"` 93 Prekey string `db:"prekey"` 94 Level int `db:"level"` 95 } 96 97 // Accessor implements db.Accessor interface. 98 type Accessor struct { 99 db *sqlx.DB 100 } 101 102 // NewDBAccessor is a constructor for the database API 103 func NewDBAccessor(db *sqlx.DB) *Accessor { 104 return &Accessor{ 105 db: db, 106 } 107 } 108 109 func (d *Accessor) checkDB() error { 110 if d.db == nil { 111 return errors.New("Failed to correctly setup database connection") 112 } 113 return nil 114 } 115 116 // SetDB changes the underlying sql.DB object Accessor is manipulating. 117 func (d *Accessor) SetDB(db *sqlx.DB) { 118 d.db = db 119 } 120 121 // InsertUser inserts user into database 122 func (d *Accessor) InsertUser(user *spi.UserInfo) error { 123 if user == nil { 124 return errors.New("User is not defined") 125 } 126 log.Debugf("DB: Add identity %s", user.Name) 127 128 err := d.checkDB() 129 if err != nil { 130 return err 131 } 132 133 attrBytes, err := json.Marshal(user.Attributes) 134 if err != nil { 135 return err 136 } 137 138 // Hash the password before storing it 139 pwd := []byte(user.Pass) 140 pwd, err = bcrypt.GenerateFromPassword(pwd, bcrypt.DefaultCost) 141 if err != nil { 142 return errors.Wrap(err, "Failed to hash password") 143 } 144 145 // Store the user record in the DB 146 res, err := d.db.NamedExec(insertUser, &UserRecord{ 147 Name: user.Name, 148 Pass: pwd, 149 Type: user.Type, 150 Affiliation: user.Affiliation, 151 Attributes: string(attrBytes), 152 State: user.State, 153 MaxEnrollments: user.MaxEnrollments, 154 Level: user.Level, 155 }) 156 157 if err != nil { 158 return errors.Wrapf(err, "Error adding identity '%s' to the database", user.Name) 159 } 160 161 numRowsAffected, err := res.RowsAffected() 162 if err != nil { 163 return err 164 } 165 166 if numRowsAffected == 0 { 167 return errors.Errorf("Failed to add identity %s to the database", user.Name) 168 } 169 170 if numRowsAffected != 1 { 171 return errors.Errorf("Expected to add one record to the database, but %d records were added", numRowsAffected) 172 } 173 174 log.Debugf("Successfully added identity %s to the database", user.Name) 175 176 return nil 177 178 } 179 180 // DeleteUser deletes user from database 181 func (d *Accessor) DeleteUser(id string) (spi.User, error) { 182 log.Debugf("DB: Delete identity %s", id) 183 184 result, err := d.doTransaction(d.deleteUserTx, id, ocsp.CessationOfOperation) // 5 (cessationofoperation) reason for certificate revocation 185 if err != nil { 186 return nil, err 187 } 188 189 userRec := result.(*UserRecord) 190 user := d.newDBUser(userRec) 191 192 return user, nil 193 } 194 195 func (d *Accessor) deleteUserTx(tx *sqlx.Tx, args ...interface{}) (interface{}, error) { 196 id := args[0].(string) 197 reason := args[1].(int) 198 199 var userRec UserRecord 200 err := tx.Get(&userRec, tx.Rebind(getUser), id) 201 if err != nil { 202 return nil, getError(err, "User") 203 } 204 205 _, err = tx.Exec(tx.Rebind(deleteUser), id) 206 if err != nil { 207 return nil, newHTTPErr(500, ErrDBDeleteUser, "Error deleting identity '%s': %s", id, err) 208 } 209 210 record := &CertRecord{ 211 ID: id, 212 } 213 record.Reason = reason 214 215 _, err = tx.NamedExec(tx.Rebind(updateRevokeSQL), record) 216 if err != nil { 217 return nil, newHTTPErr(500, ErrDBDeleteUser, "Error encountered while revoking certificates for identity '%s' that is being deleted: %s", id, err) 218 } 219 220 return &userRec, nil 221 } 222 223 // UpdateUser updates user in database 224 func (d *Accessor) UpdateUser(user *spi.UserInfo, updatePass bool) error { 225 if user == nil { 226 return errors.New("User is not defined") 227 } 228 229 log.Debugf("DB: Update identity %s", user.Name) 230 err := d.checkDB() 231 if err != nil { 232 return err 233 } 234 235 attributes, err := json.Marshal(user.Attributes) 236 if err != nil { 237 return errors.Wrap(err, "Failed to marshal user attributes") 238 } 239 240 // Hash the password before storing it 241 pwd := []byte(user.Pass) 242 if updatePass { 243 pwd, err = bcrypt.GenerateFromPassword(pwd, bcrypt.DefaultCost) 244 if err != nil { 245 return errors.Wrap(err, "Failed to hash password") 246 } 247 } 248 249 // Store the updated user entry 250 res, err := d.db.NamedExec(updateUser, &UserRecord{ 251 Name: user.Name, 252 Pass: pwd, 253 Type: user.Type, 254 Affiliation: user.Affiliation, 255 Attributes: string(attributes), 256 State: user.State, 257 MaxEnrollments: user.MaxEnrollments, 258 Level: user.Level, 259 }) 260 261 if err != nil { 262 return errors.Wrap(err, "Failed to update identity record") 263 } 264 265 numRowsAffected, err := res.RowsAffected() 266 267 if numRowsAffected == 0 { 268 return errors.New("Failed to update any identity records") 269 } 270 271 if numRowsAffected != 1 { 272 return errors.Errorf("Expected one identity record to be updated, but %d records were updated", numRowsAffected) 273 } 274 275 return err 276 277 } 278 279 // GetUser gets user from database 280 func (d *Accessor) GetUser(id string, attrs []string) (spi.User, error) { 281 log.Debugf("DB: Getting identity %s", id) 282 283 err := d.checkDB() 284 if err != nil { 285 return nil, err 286 } 287 288 var userRec UserRecord 289 err = d.db.Get(&userRec, d.db.Rebind(getUser), id) 290 if err != nil { 291 return nil, getError(err, "User") 292 } 293 294 return d.newDBUser(&userRec), nil 295 } 296 297 // InsertAffiliation inserts affiliation into database 298 func (d *Accessor) InsertAffiliation(name string, prekey string, level int) error { 299 log.Debugf("DB: Add affiliation %s", name) 300 err := d.checkDB() 301 if err != nil { 302 return err 303 } 304 dbType := d.db.DriverName() 305 // InnoDB store engine for MySQL does not allow more than 767 bytes 306 // in a 'UNIQUE' column. To work around this, the UNIQUE constraint was removed 307 // from the 'name' column in the affiliations table for MySQL to allow for up to 1024 308 // characters to be stored. In doing this, a check is needed on MySQL to check 309 // if the affiliation exists before adding it to prevent duplicate entries. 310 if dbType == "mysql" { 311 aff, _ := d.GetAffiliation(name) 312 if aff != nil { 313 log.Debugf("Affiliation '%s' already exists", name) 314 return nil 315 } 316 } 317 _, err = d.db.Exec(d.db.Rebind(insertAffiliation), name, prekey, level) 318 if err != nil { 319 if (!strings.Contains(err.Error(), "UNIQUE constraint failed") && dbType == "sqlite3") || (!strings.Contains(err.Error(), "duplicate key value") && dbType == "postgres") { 320 return err 321 } 322 log.Debugf("Affiliation '%s' already exists", name) 323 return nil 324 } 325 log.Debugf("Affiliation '%s' added", name) 326 327 return nil 328 } 329 330 // DeleteAffiliation deletes affiliation from database. Using the force option with identity removal allowed 331 // this will also delete the identities associated with removed affiliations, and also delete the certificates 332 // for the identities removed 333 func (d *Accessor) DeleteAffiliation(name string, force, identityRemoval, isRegistrar bool) (*spi.DbTxResult, error) { 334 log.Debugf("DB: Delete affiliation %s", name) 335 336 _, err := d.GetAffiliation(name) 337 if err != nil { 338 return nil, err 339 } 340 341 result, err := d.doTransaction(d.deleteAffiliationTx, name, force, identityRemoval, isRegistrar) 342 if err != nil { 343 return nil, err 344 } 345 346 deletedInfo := result.(*spi.DbTxResult) 347 348 return deletedInfo, nil 349 } 350 351 func (d *Accessor) deleteAffiliationTx(tx *sqlx.Tx, args ...interface{}) (interface{}, error) { 352 var err error 353 354 name := args[0].(string) 355 force := args[1].(bool) 356 identityRemoval := args[2].(bool) 357 isRegistar := args[3].(bool) 358 359 query := "SELECT * FROM users WHERE (affiliation = ?)" 360 ids := []UserRecord{} 361 err = tx.Select(&ids, tx.Rebind(query), name) 362 if err != nil { 363 return nil, newHTTPErr(500, ErrRemoveAffDB, "Failed to select users with affiliation '%s': %s", name, err) 364 } 365 366 subAffName := name + ".%" 367 query = "SELECT * FROM users WHERE (affiliation LIKE ?)" 368 subAffIds := []UserRecord{} 369 err = tx.Select(&subAffIds, tx.Rebind(query), subAffName) 370 if err != nil { 371 return nil, newHTTPErr(500, ErrRemoveAffDB, "Failed to select users with sub-affiliation of '%s': %s", name, err) 372 } 373 374 ids = append(ids, subAffIds...) 375 idNames := []string{} 376 for _, id := range ids { 377 idNames = append(idNames, id.Name) 378 } 379 idNamesStr := strings.Join(idNames, ",") 380 381 if len(ids) > 0 { 382 if !isRegistar { 383 return nil, newAuthErr(ErrUpdateConfigRemoveAff, "Removing affiliation affects identities, but caller is not a registrar") 384 } 385 386 // Force enabled, delete any associated identities and certificates 387 if force { 388 log.Debugf("IDs '%s' to removed based on affiliation '%s' removal", idNamesStr, name) 389 390 if !identityRemoval { 391 return nil, newAuthErr(ErrUpdateConfigRemoveAff, "Identity removal is not allowed on server") 392 } 393 394 // Delete all the identities in one database request 395 query := "DELETE FROM users WHERE (id IN (?))" 396 inQuery, args, err := sqlx.In(query, idNames) 397 if err != nil { 398 return nil, newHTTPErr(500, ErrRemoveAffDB, "Failed to construct query '%s': %s", query, err) 399 } 400 _, err = tx.Exec(tx.Rebind(inQuery), args...) 401 if err != nil { 402 return nil, newHTTPErr(500, ErrRemoveAffDB, "Failed to execute query '%s' for multiple identity removal: %s", query, err) 403 } 404 405 // Revoke all the certificates associated with the removed identities above with reason of "affiliationchange" (3) 406 query = "UPDATE certificates SET status='revoked', revoked_at=CURRENT_TIMESTAMP, reason = ? WHERE (id IN (?) AND status != 'revoked')" 407 inQuery, args, err = sqlx.In(query, ocsp.AffiliationChanged, idNames) 408 if err != nil { 409 return nil, newHTTPErr(500, ErrRemoveAffDB, "Failed to construct query '%s': %s", query, err) 410 } 411 _, err = tx.Exec(tx.Rebind(inQuery), args...) 412 if err != nil { 413 return nil, newHTTPErr(500, ErrRemoveAffDB, "Failed to execute query '%s' for multiple certificate removal: %s", query, err) 414 } 415 } else { 416 // If force option is not specified, only delete affiliation if there are no identities that have that affiliation 417 return nil, newAuthErr(ErrUpdateConfigRemoveAff, "The request to remove affiliation '%s' has the following identities associated: %s. Need to use 'force' to remove identities and affiliation", name, idNamesStr) 418 } 419 } 420 421 aff := AffiliationRecord{} 422 err = tx.Get(&aff, tx.Rebind(getAffiliationQuery), name) 423 if err != nil { 424 return nil, getError(err, "Affiliation") 425 } 426 // Getting all the sub-affiliations that are going to be deleted 427 allAffs := []AffiliationRecord{} 428 err = tx.Select(&allAffs, tx.Rebind("Select * FROM affiliations where (name LIKE ?)"), subAffName) 429 if err != nil { 430 return nil, newHTTPErr(500, ErrRemoveAffDB, "Failed to select sub-affiliations of '%s': %s", allAffs, err) 431 } 432 allAffs = append(allAffs, aff) 433 log.Debug("All affiliations to be removed: ", allAffs) 434 435 // Delete the requested affiliation 436 _, err = tx.Exec(tx.Rebind(deleteAffiliation), name) 437 if err != nil { 438 return nil, newHTTPErr(500, ErrRemoveAffDB, "Failed to delete affiliation '%s': %s", name, err) 439 } 440 // Delete all the sub-affiliations 441 _, err = tx.Exec(tx.Rebind("DELETE FROM affiliations where (name LIKE ?)"), subAffName) 442 if err != nil { 443 return nil, newHTTPErr(500, ErrRemoveAffDB, "Failed to delete affiliations: %s", err) 444 } 445 446 // Return the identities and affiliations that were removed 447 result := d.getResult(ids, allAffs) 448 449 return result, nil 450 } 451 452 // GetAffiliation gets affiliation from database 453 func (d *Accessor) GetAffiliation(name string) (spi.Affiliation, error) { 454 log.Debugf("DB: Get affiliation %s", name) 455 err := d.checkDB() 456 if err != nil { 457 return nil, err 458 } 459 460 var affiliationRecord AffiliationRecord 461 462 err = d.db.Get(&affiliationRecord, d.db.Rebind(getAffiliationQuery), name) 463 if err != nil { 464 return nil, getError(err, "Affiliation") 465 } 466 467 affiliation := spi.NewAffiliation(affiliationRecord.Name, affiliationRecord.Prekey, affiliationRecord.Level) 468 469 return affiliation, nil 470 } 471 472 // GetAffiliationTree returns the requested affiliation and affiliations below 473 func (d *Accessor) GetAffiliationTree(name string) (*spi.DbTxResult, error) { 474 log.Debugf("DB: Get affiliation tree for '%s'", name) 475 476 if name != "" { 477 _, err := d.GetAffiliation(name) 478 if err != nil { 479 return nil, err 480 } 481 } 482 483 result, err := d.doTransaction(d.getAffiliationTreeTx, name) 484 if err != nil { 485 return nil, err 486 } 487 488 getResult := result.(*spi.DbTxResult) 489 490 return getResult, nil 491 } 492 493 // GetAffiliation gets affiliation from database 494 func (d *Accessor) getAffiliationTreeTx(tx *sqlx.Tx, args ...interface{}) (interface{}, error) { 495 name := args[0].(string) 496 497 log.Debugf("DB: Get affiliation tree for %s", name) 498 err := d.checkDB() 499 if err != nil { 500 return nil, err 501 } 502 503 // Getting affiliations 504 allAffs := []AffiliationRecord{} 505 if name == "" { // Requesting all affiliations 506 err = tx.Select(&allAffs, tx.Rebind("SELECT * FROM affiliations")) 507 if err != nil { 508 return nil, newHTTPErr(500, ErrGettingAffiliation, "Failed to get affiliation tree for '%s': %s", name, err) 509 } 510 } else { 511 err = tx.Select(&allAffs, tx.Rebind("Select * FROM affiliations where (name LIKE ?) OR (name = ?)"), name+".%", name) 512 if err != nil { 513 return nil, newHTTPErr(500, ErrGettingAffiliation, "Failed to get affiliation tree for '%s': %s", name, err) 514 } 515 } 516 517 ids := []UserRecord{} // TODO: Return identities associated with these affiliations 518 result := d.getResult(ids, allAffs) 519 return result, nil 520 } 521 522 // GetProperties returns the properties from the database 523 func (d *Accessor) GetProperties(names []string) (map[string]string, error) { 524 log.Debugf("DB: Get properties %s", names) 525 err := d.checkDB() 526 if err != nil { 527 return nil, err 528 } 529 530 type property struct { 531 Name string `db:"property"` 532 Value string `db:"value"` 533 } 534 535 properties := []property{} 536 537 query := "SELECT * FROM properties WHERE (property IN (?))" 538 inQuery, args, err := sqlx.In(query, names) 539 if err != nil { 540 return nil, errors.Wrapf(err, "Failed to construct query '%s' for properties '%s'", query, names) 541 } 542 err = d.db.Select(&properties, d.db.Rebind(inQuery), args...) 543 if err != nil { 544 return nil, getError(err, "Properties") 545 } 546 547 propertiesMap := make(map[string]string) 548 for _, prop := range properties { 549 propertiesMap[prop.Name] = prop.Value 550 } 551 552 return propertiesMap, nil 553 } 554 555 // GetUserLessThanLevel returns all identities that are less than the level specified 556 // Otherwise, returns no users if requested level is zero 557 func (d *Accessor) GetUserLessThanLevel(level int) ([]spi.User, error) { 558 if level == 0 { 559 return []spi.User{}, nil 560 } 561 562 rows, err := d.db.Queryx(d.db.Rebind("SELECT * FROM users WHERE (level < ?) OR (level IS NULL)"), level) 563 if err != nil { 564 return nil, errors.Wrap(err, "Failed to get identities that need to be updated") 565 } 566 567 allUsers := []spi.User{} 568 569 for rows.Next() { 570 var user UserRecord 571 rows.StructScan(&user) 572 dbUser := d.newDBUser(&user) 573 allUsers = append(allUsers, dbUser) 574 } 575 576 return allUsers, nil 577 } 578 579 // GetAllAffiliations gets the requested affiliation and any sub affiliations from the database 580 func (d *Accessor) GetAllAffiliations(name string) (*sqlx.Rows, error) { 581 log.Debugf("DB: Get affiliation %s", name) 582 err := d.checkDB() 583 if err != nil { 584 return nil, err 585 } 586 587 if name == "" { // Requesting all affiliations 588 rows, err := d.db.Queryx(d.db.Rebind("SELECT * FROM affiliations")) 589 if err != nil { 590 return nil, err 591 } 592 return rows, nil 593 } 594 595 rows, err := d.db.Queryx(d.db.Rebind(getAllAffiliationsQuery), name, name+".%") 596 if err != nil { 597 return nil, err 598 } 599 600 return rows, nil 601 } 602 603 // GetFilteredUsers returns all identities that fall under the affiliation and types 604 func (d *Accessor) GetFilteredUsers(affiliation, types string) (*sqlx.Rows, error) { 605 log.Debugf("DB: Get all identities per affiliation '%s' and types '%s'", affiliation, types) 606 err := d.checkDB() 607 if err != nil { 608 return nil, err 609 } 610 611 typesArray := strings.Split(types, ",") 612 for i := range typesArray { 613 typesArray[i] = strings.TrimSpace(typesArray[i]) 614 } 615 616 if affiliation == "" { 617 query := "SELECT * FROM users WHERE (type IN (?))" 618 query, args, err := sqlx.In(query, typesArray) 619 if err != nil { 620 return nil, errors.Wrapf(err, "Failed to construct query '%s' for affiliation '%s' and types '%s'", query, affiliation, types) 621 } 622 rows, err := d.db.Queryx(d.db.Rebind(query), args...) 623 if err != nil { 624 return nil, errors.Wrapf(err, "Failed to execute query '%s' for affiliation '%s' and types '%s'", query, affiliation, types) 625 } 626 return rows, nil 627 } 628 629 subAffiliation := affiliation + ".%" 630 query := "SELECT * FROM users WHERE ((affiliation = ?) OR (affiliation LIKE ?)) AND (type IN (?))" 631 inQuery, args, err := sqlx.In(query, affiliation, subAffiliation, typesArray) 632 if err != nil { 633 return nil, errors.Wrapf(err, "Failed to construct query '%s' for affiliation '%s' and types '%s'", query, affiliation, types) 634 } 635 rows, err := d.db.Queryx(d.db.Rebind(inQuery), args...) 636 if err != nil { 637 return nil, errors.Wrapf(err, "Failed to execute query '%s' for affiliation '%s' and types '%s'", query, affiliation, types) 638 } 639 640 return rows, nil 641 642 } 643 644 // ModifyAffiliation renames the affiliation and updates all identities to use the new affiliation depending on 645 // the value of the "force" parameter 646 func (d *Accessor) ModifyAffiliation(oldAffiliation, newAffiliation string, force, isRegistrar bool) (*spi.DbTxResult, error) { 647 log.Debugf("DB: Modify affiliation from '%s' to '%s'", oldAffiliation, newAffiliation) 648 err := d.checkDB() 649 if err != nil { 650 return nil, err 651 } 652 653 // Check to see if the affiliation being modifies exists in the affiliation table 654 _, err = d.GetAffiliation(oldAffiliation) 655 if err != nil { 656 return nil, err 657 } 658 659 // Check to see if the new affiliation being requested exists in the affiliation table 660 _, err = d.GetAffiliation(newAffiliation) 661 if err == nil { 662 return nil, newHTTPErr(400, ErrUpdateConfigModifyAff, "Affiliation '%s' already exists", newAffiliation) 663 } 664 665 result, err := d.doTransaction(d.modifyAffiliationTx, oldAffiliation, newAffiliation, force, isRegistrar) 666 if err != nil { 667 return nil, err 668 } 669 670 modifiedInfo := result.(*spi.DbTxResult) 671 672 return modifiedInfo, nil 673 } 674 675 func (d *Accessor) modifyAffiliationTx(tx *sqlx.Tx, args ...interface{}) (interface{}, error) { 676 oldAffiliation := args[0].(string) 677 newAffiliation := args[1].(string) 678 force := args[2].(bool) 679 isRegistar := args[3].(bool) 680 681 // Get the affiliation record 682 query := "SELECT name, prekey FROM affiliations WHERE (name = ?)" 683 var oldAffiliationRecord AffiliationRecord 684 err := tx.Get(&oldAffiliationRecord, tx.Rebind(query), oldAffiliation) 685 if err != nil { 686 return nil, err 687 } 688 689 // Get the affiliation records for all sub affiliations 690 query = "SELECT name, prekey FROM affiliations WHERE (name LIKE ?)" 691 var allOldAffiliations []AffiliationRecord 692 err = tx.Select(&allOldAffiliations, tx.Rebind(query), oldAffiliation+".%") 693 if err != nil { 694 return nil, err 695 } 696 697 allOldAffiliations = append(allOldAffiliations, oldAffiliationRecord) 698 699 log.Debugf("Affiliations to be modified %+v", allOldAffiliations) 700 701 // Iterate through all the affiliations found and update to use new affiliation path 702 idsUpdated := []string{} 703 for _, affiliation := range allOldAffiliations { 704 var idsWithOldAff []UserRecord 705 oldPath := affiliation.Name 706 oldParentPath := affiliation.Prekey 707 newPath := strings.Replace(oldPath, oldAffiliation, newAffiliation, 1) 708 newParentPath := strings.Replace(oldParentPath, oldAffiliation, newAffiliation, 1) 709 log.Debugf("oldPath: %s, newPath: %s, oldParentPath: %s, newParentPath: %s", oldPath, newPath, oldParentPath, newParentPath) 710 711 // Select all users that are using the old affiliation 712 query = "SELECT * FROM users WHERE (affiliation = ?)" 713 err = tx.Select(&idsWithOldAff, tx.Rebind(query), oldPath) 714 if err != nil { 715 return nil, err 716 } 717 if len(idsWithOldAff) > 0 { 718 if !isRegistar { 719 return nil, newAuthErr(ErrMissingRegAttr, "Modifying affiliation affects identities, but caller is not a registrar") 720 } 721 // Get the list of names of the identities that need to be updated to use new affiliation 722 ids := []string{} 723 for _, id := range idsWithOldAff { 724 ids = append(ids, id.Name) 725 } 726 727 if force { 728 log.Debugf("Identities %s to be updated to use new affiliation of '%s' from '%s'", ids, newPath, oldPath) 729 730 query := "Update users SET affiliation = ? WHERE (id IN (?))" 731 inQuery, args, err := sqlx.In(query, newPath, ids) 732 if err != nil { 733 return nil, errors.Wrapf(err, "Failed to construct query '%s'", query) 734 } 735 _, err = tx.Exec(tx.Rebind(inQuery), args...) 736 if err != nil { 737 return nil, errors.Wrapf(err, "Failed to execute query '%s' for multiple certificate removal", query) 738 } 739 740 // If user's affiliation is being updated, need to also update 'hf.Affiliation' attribute of user 741 for _, userRec := range idsWithOldAff { 742 user := d.newDBUser(&userRec) 743 currentAttrs, _ := user.GetAttributes(nil) // Get all current user attributes 744 userAff := GetUserAffiliation(user) // Get the current affiliation 745 newAff := strings.Replace(userAff, oldAffiliation, newAffiliation, 1) // Replace old affiliation with new affiliation 746 userAttrs := getNewAttributes(currentAttrs, []api.Attribute{ // Generate the new set of attributes for user 747 api.Attribute{ 748 Name: attr.Affiliation, 749 Value: newAff, 750 }, 751 }) 752 753 attrBytes, err := json.Marshal(userAttrs) 754 if err != nil { 755 return nil, err 756 } 757 758 // Update attributes 759 query := "UPDATE users SET attributes = ? where (id = ?)" 760 id := user.GetName() 761 res, err := tx.Exec(tx.Rebind(query), string(attrBytes), id) 762 if err != nil { 763 return nil, err 764 } 765 766 numRowsAffected, err := res.RowsAffected() 767 if err != nil { 768 return nil, errors.Wrap(err, "Failed to get number of rows affected") 769 } 770 771 if numRowsAffected == 0 { 772 return nil, errors.Errorf("No rows were affected when updating the state of identity %s", id) 773 } 774 775 if numRowsAffected != 1 { 776 return nil, errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, id) 777 } 778 } 779 } else { 780 // If force option is not specified, can only modify affiliation if there are no identities that have that affiliation 781 idNamesStr := strings.Join(ids, ",") 782 return nil, newHTTPErr(400, ErrUpdateConfigModifyAff, "The request to modify affiliation '%s' has the following identities associated: %s. Need to use 'force' to remove identities and affiliation", oldAffiliation, idNamesStr) 783 } 784 785 idsUpdated = append(idsUpdated, ids...) 786 } 787 788 // Update the affiliation record in the database to use new affiliation path 789 query = "Update affiliations SET name = ?, prekey = ? WHERE (name = ?)" 790 res := tx.MustExec(tx.Rebind(query), newPath, newParentPath, oldPath) 791 numRowsAffected, err := res.RowsAffected() 792 if err != nil { 793 return nil, errors.Errorf("Failed to get number of rows affected") 794 } 795 if numRowsAffected == 0 { 796 return nil, errors.Errorf("Failed to update any affiliation records for '%s'", oldPath) 797 } 798 } 799 800 // Generate the result set that has all identities with their new affiliation and all renamed affiliations 801 var idsWithNewAff []UserRecord 802 if len(idsUpdated) > 0 { 803 query = "Select * FROM users WHERE (id IN (?))" 804 inQuery, args, err := sqlx.In(query, idsUpdated) 805 if err != nil { 806 return nil, errors.Wrapf(err, "Failed to construct query '%s'", query) 807 } 808 err = tx.Select(&idsWithNewAff, tx.Rebind(inQuery), args...) 809 if err != nil { 810 return nil, errors.Wrapf(err, "Failed to execute query '%s' for getting users with new affiliation", query) 811 } 812 } 813 814 allNewAffs := []AffiliationRecord{} 815 err = tx.Select(&allNewAffs, tx.Rebind("Select * FROM affiliations where (name LIKE ?) OR (name = ?)"), newAffiliation+".%", newAffiliation) 816 if err != nil { 817 return nil, newHTTPErr(500, ErrGettingAffiliation, "Failed to get affiliation tree for '%s': %s", newAffiliation, err) 818 } 819 820 // Return the identities and affiliations that were modified 821 result := d.getResult(idsWithNewAff, allNewAffs) 822 823 return result, nil 824 } 825 826 func (d *Accessor) doTransaction(doit func(tx *sqlx.Tx, args ...interface{}) (interface{}, error), args ...interface{}) (interface{}, error) { 827 err := d.checkDB() 828 if err != nil { 829 return nil, err 830 } 831 tx := d.db.MustBegin() 832 result, err := doit(tx, args...) 833 if err != nil { 834 err2 := tx.Rollback() 835 if err2 != nil { 836 log.Errorf("Error encounted while rolling back transaction: %s", err2) 837 return nil, err 838 } 839 return nil, err 840 } 841 842 err = tx.Commit() 843 if err != nil { 844 return nil, errors.Wrap(err, "Error encountered while committing transaction") 845 } 846 847 return result, nil 848 } 849 850 // Returns the identities and affiliations that were modified 851 func (d *Accessor) getResult(ids []UserRecord, affs []AffiliationRecord) *spi.DbTxResult { 852 // Collect all the identities that were modified 853 identities := []spi.User{} 854 for _, id := range ids { 855 identities = append(identities, d.newDBUser(&id)) 856 } 857 858 // Collect the name of all affiliations that were modified 859 affiliations := []spi.Affiliation{} 860 for _, aff := range affs { 861 newAff := spi.NewAffiliation(aff.Name, aff.Prekey, aff.Level) 862 affiliations = append(affiliations, newAff) 863 } 864 865 return &spi.DbTxResult{ 866 Affiliations: affiliations, 867 Identities: identities, 868 } 869 } 870 871 // Creates a DBUser object from the DB user record 872 func (d *Accessor) newDBUser(userRec *UserRecord) *DBUser { 873 var user = new(DBUser) 874 user.Name = userRec.Name 875 user.pass = userRec.Pass 876 user.State = userRec.State 877 user.MaxEnrollments = userRec.MaxEnrollments 878 user.Affiliation = userRec.Affiliation 879 user.Type = userRec.Type 880 user.Level = userRec.Level 881 882 var attrs []api.Attribute 883 json.Unmarshal([]byte(userRec.Attributes), &attrs) 884 user.Attributes = attrs 885 886 user.attrs = make(map[string]api.Attribute) 887 for _, attr := range attrs { 888 user.attrs[attr.Name] = api.Attribute{ 889 Name: attr.Name, 890 Value: attr.Value, 891 ECert: attr.ECert, 892 } 893 } 894 895 user.db = d.db 896 return user 897 } 898 899 // DBUser is the databases representation of a user 900 type DBUser struct { 901 spi.UserInfo 902 pass []byte 903 attrs map[string]api.Attribute 904 db *sqlx.DB 905 } 906 907 // GetName returns the enrollment ID of the user 908 func (u *DBUser) GetName() string { 909 return u.Name 910 } 911 912 // GetType returns the type of the user 913 func (u *DBUser) GetType() string { 914 return u.Type 915 } 916 917 // GetMaxEnrollments returns the max enrollments of the user 918 func (u *DBUser) GetMaxEnrollments() int { 919 return u.MaxEnrollments 920 } 921 922 // GetLevel returns the level of the user 923 func (u *DBUser) GetLevel() int { 924 return u.Level 925 } 926 927 // SetLevel sets the level of the user 928 func (u *DBUser) SetLevel(level int) error { 929 query := "UPDATE users SET level = ? where (id = ?)" 930 id := u.GetName() 931 res, err := u.db.Exec(u.db.Rebind(query), level, id) 932 if err != nil { 933 return err 934 } 935 numRowsAffected, err := res.RowsAffected() 936 if err != nil { 937 return errors.Wrap(err, "Failed to get number of rows affected") 938 } 939 940 if numRowsAffected == 0 { 941 return errors.Errorf("No rows were affected when updating the state of identity %s", id) 942 } 943 944 if numRowsAffected != 1 { 945 return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, id) 946 } 947 return nil 948 } 949 950 // Login the user with a password 951 func (u *DBUser) Login(pass string, caMaxEnrollments int) error { 952 log.Debugf("DB: Login user %s with max enrollments of %d and state of %d", u.Name, u.MaxEnrollments, u.State) 953 954 // Check the password by comparing to stored hash 955 err := bcrypt.CompareHashAndPassword(u.pass, []byte(pass)) 956 if err != nil { 957 return errors.Wrap(err, "Password mismatch") 958 } 959 960 if u.MaxEnrollments == 0 { 961 return errors.Errorf("Zero is an invalid value for maximum enrollments on identity '%s'", u.Name) 962 } 963 964 if u.State == -1 { 965 return errors.Errorf("User %s is revoked; access denied", u.Name) 966 } 967 968 // If max enrollment value of user is greater than allowed by CA, using CA max enrollment value for user 969 if caMaxEnrollments != -1 && (u.MaxEnrollments > caMaxEnrollments || u.MaxEnrollments == -1) { 970 log.Debugf("Max enrollment value (%d) of identity is greater than allowed by CA, using CA max enrollment value of %d", u.MaxEnrollments, caMaxEnrollments) 971 u.MaxEnrollments = caMaxEnrollments 972 } 973 974 // If maxEnrollments is set to -1, user has unlimited enrollment 975 // If the maxEnrollments is set (i.e. >= 1), make sure we haven't exceeded this number of logins. 976 // The state variable keeps track of the number of previously successful logins. 977 if u.MaxEnrollments != -1 && u.State >= u.MaxEnrollments { 978 return errors.Errorf("The identity %s has already enrolled %d times, it has reached its maximum enrollment allowance", u.Name, u.MaxEnrollments) 979 } 980 981 log.Debugf("DB: identity %s successfully logged in", u.Name) 982 983 return nil 984 985 } 986 987 // LoginComplete completes the login process by incrementing the state of the user 988 func (u *DBUser) LoginComplete() error { 989 var stateUpdateSQL string 990 var args []interface{} 991 var err error 992 993 state := u.State + 1 994 args = append(args, u.Name) 995 if u.MaxEnrollments == -1 { 996 // unlimited so no state check 997 stateUpdateSQL = "UPDATE users SET state = state + 1 WHERE (id = ?)" 998 } else { 999 // state must be less than max enrollments 1000 stateUpdateSQL = "UPDATE users SET state = state + 1 WHERE (id = ? AND state < ?)" 1001 args = append(args, u.MaxEnrollments) 1002 } 1003 res, err := u.db.Exec(u.db.Rebind(stateUpdateSQL), args...) 1004 if err != nil { 1005 return errors.Wrapf(err, "Failed to update state of identity %s to %d", u.Name, state) 1006 } 1007 1008 numRowsAffected, err := res.RowsAffected() 1009 if err != nil { 1010 return errors.Wrap(err, "db.RowsAffected failed") 1011 } 1012 1013 if numRowsAffected == 0 { 1014 return errors.Errorf("No rows were affected when updating the state of identity %s", u.Name) 1015 } 1016 1017 if numRowsAffected != 1 { 1018 return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, u.Name) 1019 } 1020 1021 log.Debugf("Successfully incremented state for identity %s to %d", u.Name, state) 1022 return nil 1023 1024 } 1025 1026 // GetAffiliationPath returns the complete path for the user's affiliation. 1027 func (u *DBUser) GetAffiliationPath() []string { 1028 affiliationPath := strings.Split(u.Affiliation, ".") 1029 return affiliationPath 1030 } 1031 1032 // GetAttribute returns the value for an attribute name 1033 func (u *DBUser) GetAttribute(name string) (*api.Attribute, error) { 1034 value, hasAttr := u.attrs[name] 1035 if !hasAttr { 1036 return nil, errors.Errorf("User does not have attribute '%s'", name) 1037 } 1038 return &value, nil 1039 } 1040 1041 // GetAttributes returns the requested attributes. Return all the user's 1042 // attributes if nil is passed in 1043 func (u *DBUser) GetAttributes(attrNames []string) ([]api.Attribute, error) { 1044 var attrs []api.Attribute 1045 if attrNames == nil { 1046 for _, value := range u.attrs { 1047 attrs = append(attrs, value) 1048 } 1049 return attrs, nil 1050 } 1051 1052 for _, name := range attrNames { 1053 value, hasAttr := u.attrs[name] 1054 if !hasAttr { 1055 return nil, errors.Errorf("User does not have attribute '%s'", name) 1056 } 1057 attrs = append(attrs, value) 1058 } 1059 return attrs, nil 1060 } 1061 1062 // Revoke will revoke the user, setting the state of the user to be -1 1063 func (u *DBUser) Revoke() error { 1064 stateUpdateSQL := "UPDATE users SET state = -1 WHERE (id = ?)" 1065 1066 res, err := u.db.Exec(u.db.Rebind(stateUpdateSQL), u.GetName()) 1067 if err != nil { 1068 return errors.Wrapf(err, "Failed to update state of identity %s to -1", u.Name) 1069 } 1070 1071 numRowsAffected, err := res.RowsAffected() 1072 if err != nil { 1073 return errors.Wrap(err, "db.RowsAffected failed") 1074 } 1075 1076 if numRowsAffected == 0 { 1077 return errors.Errorf("No rows were affected when updating the state of identity %s", u.Name) 1078 } 1079 1080 if numRowsAffected != 1 { 1081 return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, u.Name) 1082 } 1083 1084 log.Debugf("Successfully incremented state for identity %s to -1", u.Name) 1085 1086 return nil 1087 } 1088 1089 // ModifyAttributes adds a new attribute, modifies existing attribute, or delete attribute 1090 func (u *DBUser) ModifyAttributes(newAttrs []api.Attribute) error { 1091 log.Debugf("Modify Attributes: %+v", newAttrs) 1092 currentAttrs, _ := u.GetAttributes(nil) 1093 userAttrs := getNewAttributes(currentAttrs, newAttrs) 1094 1095 attrBytes, err := json.Marshal(userAttrs) 1096 if err != nil { 1097 return err 1098 } 1099 1100 query := "UPDATE users SET attributes = ? where (id = ?)" 1101 id := u.GetName() 1102 res, err := u.db.Exec(u.db.Rebind(query), string(attrBytes), id) 1103 if err != nil { 1104 return err 1105 } 1106 1107 numRowsAffected, err := res.RowsAffected() 1108 if err != nil { 1109 return errors.Wrap(err, "Failed to get number of rows affected") 1110 } 1111 1112 if numRowsAffected == 0 { 1113 return errors.Errorf("No rows were affected when updating the state of identity %s", id) 1114 } 1115 1116 if numRowsAffected != 1 { 1117 return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, id) 1118 } 1119 return nil 1120 } 1121 1122 func getError(err error, getType string) error { 1123 if err.Error() == "sql: no rows in result set" { 1124 return newHTTPErr(404, ErrDBGet, "Failed to get %s: %s", getType, err) 1125 } 1126 return newHTTPErr(504, ErrConnectingDB, "Failed to process database request: %s", err) 1127 }