github.com/adecaro/fabric-ca@v2.0.0-alpha+incompatible/lib/server/idemix/creddbaccessor.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package idemix
     8  
     9  import (
    10  	"fmt"
    11  	"reflect"
    12  	"time"
    13  
    14  	"github.com/cloudflare/cfssl/log"
    15  	"github.com/hyperledger/fabric-ca/lib/server/db"
    16  	"github.com/kisielk/sqlstruct"
    17  	"github.com/pkg/errors"
    18  )
    19  
    20  const (
    21  	// InsertCredentialSQL is the SQL to add a credential to database
    22  	InsertCredentialSQL = `
    23  INSERT INTO credentials (id, revocation_handle, cred, ca_label, status, reason, expiry, revoked_at, level)
    24  	VALUES (:id, :revocation_handle, :cred, :ca_label, :status, :reason, :expiry, :revoked_at, :level);`
    25  
    26  	// SelectCredentialByIDSQL is the SQL for getting credentials of a user
    27  	SelectCredentialByIDSQL = `
    28  SELECT %s FROM credentials
    29  WHERE (id = ?);`
    30  
    31  	// SelectCredentialSQL is the SQL for getting a credential given a revocation handle
    32  	SelectCredentialSQL = `
    33  SELECT %s FROM credentials
    34  WHERE (revocation_handle = ?);`
    35  
    36  	// SelectRevokedCredentialSQL is the SQL for getting revoked credentials
    37  	SelectRevokedCredentialSQL = `
    38  SELECT %s FROM credentials
    39  WHERE (status = 'revoked');`
    40  
    41  	// UpdateRevokeCredentialSQL is the SQL for updating status of a credential to revoked
    42  	UpdateRevokeCredentialSQL = `
    43  UPDATE credentials
    44  SET status='revoked', revoked_at=CURRENT_TIMESTAMP, reason=:reason
    45  WHERE (id = :id AND status != 'revoked');`
    46  
    47  	// DeleteCredentialbyID is the SQL for deleting credential of a user
    48  	DeleteCredentialbyID = `
    49  DELETE FROM credentials
    50  		WHERE (id = ?);`
    51  )
    52  
    53  // CredRecord represents a credential database record
    54  type CredRecord struct {
    55  	ID               string    `db:"id"`
    56  	RevocationHandle string    `db:"revocation_handle"`
    57  	Cred             string    `db:"cred"`
    58  	CALabel          string    `db:"ca_label"`
    59  	Status           string    `db:"status"`
    60  	Reason           int       `db:"reason"`
    61  	Expiry           time.Time `db:"expiry"`
    62  	RevokedAt        time.Time `db:"revoked_at"`
    63  	Level            int       `db:"level"`
    64  }
    65  
    66  //go:generate mockery -name CredDBAccessor -case underscore
    67  
    68  // CredDBAccessor is the accessor for credentials database table
    69  type CredDBAccessor interface {
    70  	// Sets reference to datastore object
    71  	SetDB(db db.FabricCADB)
    72  	// InsertCredential inserts specified Idemix credential record into database
    73  	InsertCredential(cr CredRecord) error
    74  	// GetCredential returns Idemix credential associated with the specified revocation
    75  	// handle
    76  	GetCredential(revocationHandle string) (*CredRecord, error)
    77  	// GetCredentialsByID returns Idemix credentials associated with the specified
    78  	// enrollment ID
    79  	GetCredentialsByID(id string) ([]CredRecord, error)
    80  	// GetRevokedCredentials returns revoked credentials
    81  	GetRevokedCredentials() ([]CredRecord, error)
    82  }
    83  
    84  // CredentialAccessor implements IdemixCredDBAccessor interface
    85  type CredentialAccessor struct {
    86  	level int
    87  	db    db.FabricCADB
    88  }
    89  
    90  // NewCredentialAccessor returns a new CredentialAccessor.
    91  func NewCredentialAccessor(db db.FabricCADB, level int) CredDBAccessor {
    92  	ac := new(CredentialAccessor)
    93  	ac.db = db
    94  	ac.level = level
    95  	return ac
    96  }
    97  
    98  // SetDB changes the underlying sql.DB object Accessor is manipulating.
    99  func (ac *CredentialAccessor) SetDB(db db.FabricCADB) {
   100  	ac.db = db
   101  }
   102  
   103  // InsertCredential puts a CredentialRecord into db.
   104  func (ac *CredentialAccessor) InsertCredential(cr CredRecord) error {
   105  	log.Debug("DB: Insert Credential")
   106  	err := ac.checkDB()
   107  	if err != nil {
   108  		return err
   109  	}
   110  	cr.Level = ac.level
   111  	res, err := ac.db.NamedExec("InsertCredential", InsertCredentialSQL, cr)
   112  	if err != nil {
   113  		return errors.Wrap(err, "Failed to insert credential into datastore")
   114  	}
   115  
   116  	numRowsAffected, err := res.RowsAffected()
   117  
   118  	if numRowsAffected == 0 {
   119  		return errors.New("Failed to insert the credential record; no rows affected")
   120  	}
   121  
   122  	if numRowsAffected != 1 {
   123  		return errors.Errorf("Expected to affect 1 entry in credentials table but affected %d",
   124  			numRowsAffected)
   125  	}
   126  
   127  	return err
   128  }
   129  
   130  // GetCredentialsByID gets a CredentialRecord indexed by id.
   131  func (ac *CredentialAccessor) GetCredentialsByID(id string) ([]CredRecord, error) {
   132  	log.Debugf("DB: Get credentials by ID '%s'", id)
   133  	err := ac.checkDB()
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	crs := []CredRecord{}
   138  	err = ac.db.Select("GetCredentialsByID", &crs, fmt.Sprintf(ac.db.Rebind(SelectCredentialByIDSQL), sqlstruct.Columns(CredRecord{})), id)
   139  	if err != nil {
   140  		return nil, errors.Wrapf(err, "Failed to get credentials for identity '%s' from datastore", id)
   141  	}
   142  
   143  	return crs, nil
   144  }
   145  
   146  // GetCredential gets a CredentialRecord indexed by revocationHandle.
   147  func (ac *CredentialAccessor) GetCredential(revocationHandle string) (*CredRecord, error) {
   148  	log.Debugf("DB: Get credential by revocation handle '%s'", revocationHandle)
   149  	err := ac.checkDB()
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	cr := &CredRecord{}
   154  	err = ac.db.Select("GetCredential", cr, fmt.Sprintf(ac.db.Rebind(SelectCredentialSQL), sqlstruct.Columns(CredRecord{})), revocationHandle)
   155  	if err != nil {
   156  		return nil, errors.Wrapf(err, "Failed to get credential associated with revocation handle '%s' from datastore", revocationHandle)
   157  	}
   158  
   159  	return cr, nil
   160  }
   161  
   162  // GetRevokedCredentials returns revoked certificates
   163  func (ac *CredentialAccessor) GetRevokedCredentials() ([]CredRecord, error) {
   164  	err := ac.checkDB()
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	crs := []CredRecord{}
   169  	err = ac.db.Select("GetRevokedCredentials", &crs, fmt.Sprintf(ac.db.Rebind(SelectRevokedCredentialSQL), sqlstruct.Columns(CredRecord{})))
   170  	if err != nil {
   171  		return crs, errors.Wrap(err, "Failed to get revoked credentials from datastore")
   172  	}
   173  	return crs, nil
   174  }
   175  
   176  func (ac *CredentialAccessor) checkDB() error {
   177  	if ac.db == nil || reflect.ValueOf(ac.db).IsNil() {
   178  		return errors.New("Database is not set")
   179  	}
   180  	return nil
   181  }