github.com/hxx258456/fabric-ca-gm@v0.0.3-0.20221111064038-a268ad7e3a37/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  	log "gitee.com/zhaochuninhefei/zcgolog/zclog"
    15  	"github.com/hxx258456/fabric-ca-gm/internal/pkg/api"
    16  	"github.com/hxx258456/fabric-ca-gm/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  	return u.State == -1
   397  }
   398  
   399  // ModifyAttributesTx adds a new attribute, modifies existing attribute, or delete attribute
   400  func (u *Impl) ModifyAttributesTx(tx userDB, newAttrs []api.Attribute) error {
   401  	return u.modifyAttributes(tx, newAttrs)
   402  }
   403  
   404  // ModifyAttributes adds a new attribute, modifies existing attribute, or delete attribute
   405  func (u *Impl) ModifyAttributes(newAttrs []api.Attribute) error {
   406  	return u.modifyAttributes(nil, newAttrs)
   407  }
   408  
   409  func (u *Impl) modifyAttributes(tx userDB, newAttrs []api.Attribute) error {
   410  	log.Debugf("Modify Attributes: %+v", newAttrs)
   411  	currentAttrs, _ := u.GetAttributes(nil)
   412  	userAttrs := GetNewAttributes(currentAttrs, newAttrs)
   413  
   414  	attrBytes, err := json.Marshal(userAttrs)
   415  	if err != nil {
   416  		return err
   417  	}
   418  
   419  	query := "UPDATE users SET attributes = ? WHERE (id = ?)"
   420  	id := u.GetName()
   421  	var res sql.Result
   422  	if tx == nil {
   423  		res, err = u.db.Exec("ModifyAttributes", u.db.Rebind(query), string(attrBytes), id)
   424  		if err != nil {
   425  			return err
   426  		}
   427  	} else {
   428  		res, err = tx.Exec("ModifyAttributes", tx.Rebind(query), string(attrBytes), id)
   429  		if err != nil {
   430  			return err
   431  		}
   432  	}
   433  
   434  	numRowsAffected, err := res.RowsAffected()
   435  	if err != nil {
   436  		return errors.Wrap(err, "Failed to get number of rows affected")
   437  	}
   438  
   439  	if numRowsAffected == 0 {
   440  		return errors.Errorf("No rows were affected when updating the state of identity %s", id)
   441  	}
   442  
   443  	if numRowsAffected != 1 {
   444  		return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, id)
   445  	}
   446  	return nil
   447  }
   448  
   449  // GetNewAttributes updates existing attribute, or add attribute if it does not already exist
   450  func GetNewAttributes(modifyAttrs, newAttrs []api.Attribute) []api.Attribute {
   451  	var attr api.Attribute
   452  	for _, attr = range newAttrs {
   453  		log.Debugf("Attribute request: %+v", attr)
   454  		found := false
   455  		for i := range modifyAttrs {
   456  			if modifyAttrs[i].Name == attr.Name {
   457  				if attr.Value == "" {
   458  					log.Debugf("Deleting attribute: %+v", modifyAttrs[i])
   459  					if i == len(modifyAttrs)-1 {
   460  						modifyAttrs = modifyAttrs[:len(modifyAttrs)-1]
   461  					} else {
   462  						modifyAttrs = append(modifyAttrs[:i], modifyAttrs[i+1:]...)
   463  					}
   464  				} else {
   465  					log.Debugf("Updating existing attribute from '%+v' to '%+v'", modifyAttrs[i], attr)
   466  					modifyAttrs[i].Value = attr.Value
   467  					modifyAttrs[i].ECert = attr.ECert
   468  				}
   469  				found = true
   470  				break
   471  			}
   472  		}
   473  		if !found && attr.Value != "" {
   474  			log.Debugf("Adding '%+v' as new attribute", attr)
   475  			modifyAttrs = append(modifyAttrs, attr)
   476  		}
   477  	}
   478  	return modifyAttrs
   479  }
   480  
   481  // IncrementIncorrectPasswordAttempts updates the incorrect password count of user
   482  func (u *Impl) IncrementIncorrectPasswordAttempts() error {
   483  	log.Debugf("Incorrect password entered by user '%s'", u.GetName())
   484  	query := "UPDATE users SET incorrect_password_attempts = incorrect_password_attempts + 1 where (id = ?)"
   485  	id := u.GetName()
   486  	res, err := u.db.Exec("IncrementIncorrectPasswordAttempts", u.db.Rebind(query), id)
   487  	if err != nil {
   488  		return err
   489  	}
   490  	numRowsAffected, err := res.RowsAffected()
   491  	if err != nil {
   492  		return errors.Wrap(err, "Failed to get number of rows affected")
   493  	}
   494  
   495  	if numRowsAffected == 0 {
   496  		return errors.Errorf("No rows were affected when updating the state of identity %s", id)
   497  	}
   498  
   499  	if numRowsAffected != 1 {
   500  		return errors.Errorf("%d rows were affected when updating the state of identity %s", numRowsAffected, id)
   501  	}
   502  	return nil
   503  }
   504  
   505  // GetFailedLoginAttempts returns the number of times the user has entered an incorrect password
   506  func (u *Impl) GetFailedLoginAttempts() int {
   507  	return u.IncorrectPasswordAttempts
   508  }
   509  
   510  // Migrate will migrate the user to the latest version
   511  func (u *Impl) Migrate(tx userDB) error {
   512  	switch u.GetLevel() {
   513  	case 0:
   514  		err := u.migrateUserToLevel1(tx)
   515  		if err != nil {
   516  			return err
   517  		}
   518  		fallthrough
   519  
   520  	case 1:
   521  		err := u.migrateUserToLevel2(tx)
   522  		if err != nil {
   523  			return err
   524  		}
   525  		fallthrough
   526  
   527  	default:
   528  		return nil
   529  	}
   530  }
   531  
   532  func (u *Impl) migrateUserToLevel1(tx userDB) error {
   533  	log.Debugf("Migrating user '%s' to level 1", u.GetName())
   534  
   535  	// Update identity to level 1
   536  	_, err := u.GetAttribute("hf.Registrar.Roles") // Check if user is a registrar
   537  	if err == nil {
   538  		_, err := u.GetAttribute("hf.Registrar.Attributes") // Check if user already has "hf.Registrar.Attributes" attribute
   539  		if err != nil {
   540  			newAttr := api.Attribute{Name: "hf.Registrar.Attributes", Value: "*"}
   541  			err := u.ModifyAttributesTx(tx, []api.Attribute{newAttr})
   542  			if err != nil {
   543  				return errors.WithMessage(err, "Failed to set attribute")
   544  			}
   545  			u.attrs[newAttr.Name] = newAttr
   546  		}
   547  	}
   548  
   549  	err = u.setLevel(tx, 1)
   550  	if err != nil {
   551  		return errors.WithMessage(err, "Failed to update level of user")
   552  	}
   553  
   554  	return nil
   555  }
   556  
   557  func (u *Impl) migrateUserToLevel2(tx userDB) error {
   558  	log.Debugf("Migrating user '%s' to level 2", u.GetName())
   559  
   560  	// Update identity to level 2
   561  	// Only give these attributes to a registrar user
   562  	_, err := u.GetAttribute("hf.Registrar.Roles") // Check if user is a registrar
   563  	if err == nil {
   564  		_, err := u.GetAttribute("hf.AffiliationMgr") // Check if user already has "hf.AffiliationMgr" attribute
   565  		if err != nil {
   566  			newAttr := api.Attribute{Name: "hf.AffiliationMgr", Value: "true"}
   567  			err := u.ModifyAttributesTx(tx, []api.Attribute{newAttr})
   568  			if err != nil {
   569  				return errors.WithMessage(err, "Failed to set attribute")
   570  			}
   571  			u.attrs[newAttr.Name] = newAttr
   572  		}
   573  
   574  		_, err = u.GetAttribute("hf.GenCRL") // Check if user already has "hf.GenCRL" attribute
   575  		if err != nil {
   576  			newAttr := api.Attribute{Name: "hf.GenCRL", Value: "true"}
   577  			err := u.ModifyAttributesTx(tx, []api.Attribute{newAttr})
   578  			if err != nil {
   579  				return errors.WithMessage(err, "Failed to set attribute")
   580  			}
   581  			u.attrs[newAttr.Name] = newAttr
   582  		}
   583  	}
   584  
   585  	err = u.setLevel(tx, 2)
   586  	if err != nil {
   587  		return errors.WithMessage(err, "Failed to update level of user")
   588  	}
   589  
   590  	return nil
   591  }
   592  
   593  // Affiliation is interface that defines functions needed to get a user's affiliation
   594  type Affiliation interface {
   595  	GetAffiliationPath() []string
   596  }
   597  
   598  // GetAffiliation return a joined version version of the affiliation path with '.' as the seperator
   599  func GetAffiliation(user Affiliation) string {
   600  	return strings.Join(user.GetAffiliationPath(), ".")
   601  }
   602  
   603  // GetUserLessThanLevel returns all identities that are less than the level specified
   604  // Otherwise, returns no users if requested level is zero
   605  func GetUserLessThanLevel(tx userDB, level int) ([]*Impl, error) {
   606  	if level == 0 {
   607  		return []*Impl{}, nil
   608  	}
   609  
   610  	rows, err := tx.Queryx("GetUserLessThanLevel", tx.Rebind("SELECT * FROM users WHERE (level < ?) OR (level IS NULL)"), level)
   611  	if err != nil {
   612  		return nil, errors.Wrap(err, "Failed to get identities that need to be updated")
   613  	}
   614  
   615  	allUsers := []*Impl{}
   616  	if rows != nil {
   617  		for rows.Next() {
   618  			var user Record
   619  			rows.StructScan(&user)
   620  			dbUser := New(&user, nil)
   621  			allUsers = append(allUsers, dbUser)
   622  		}
   623  	}
   624  
   625  	return allUsers, nil
   626  }