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  }