github.com/silveraid/fabric-ca@v1.1.0-preview.0.20180127000700-71974f53ab08/lib/certdbaccessor.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  	"fmt"
    21  	"math/big"
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/pkg/errors"
    26  
    27  	"github.com/cloudflare/cfssl/certdb"
    28  	certsql "github.com/cloudflare/cfssl/certdb/sql"
    29  	"github.com/cloudflare/cfssl/log"
    30  	"github.com/hyperledger/fabric-ca/util"
    31  	"github.com/kisielk/sqlstruct"
    32  
    33  	"github.com/jmoiron/sqlx"
    34  )
    35  
    36  const (
    37  	insertSQL = `
    38  INSERT INTO certificates (id, serial_number, authority_key_identifier, ca_label, status, reason, expiry, revoked_at, pem, level)
    39  	VALUES (:id, :serial_number, :authority_key_identifier, :ca_label, :status, :reason, :expiry, :revoked_at, :pem, :level);`
    40  
    41  	selectSQLbyID = `
    42  SELECT %s FROM certificates
    43  WHERE (id = ?);`
    44  
    45  	selectSQL = `
    46  SELECT %s FROM certificates
    47  WHERE (serial_number = ? AND authority_key_identifier = ?);`
    48  
    49  	updateRevokeSQL = `
    50  UPDATE certificates
    51  SET status='revoked', revoked_at=CURRENT_TIMESTAMP, reason=:reason
    52  WHERE (id = :id AND status != 'revoked');`
    53  
    54  	deleteCertificatebyID = `
    55  DELETE FROM certificates
    56  		WHERE (ID = ?);`
    57  )
    58  
    59  // CertRecord extends CFSSL CertificateRecord by adding an enrollment ID to the record
    60  type CertRecord struct {
    61  	ID    string `db:"id"`
    62  	Level int    `db:"level"`
    63  	certdb.CertificateRecord
    64  }
    65  
    66  // CertDBAccessor implements certdb.Accessor interface.
    67  type CertDBAccessor struct {
    68  	level    int
    69  	accessor certdb.Accessor
    70  	db       *sqlx.DB
    71  }
    72  
    73  // NewCertDBAccessor returns a new Accessor.
    74  func NewCertDBAccessor(db *sqlx.DB, level int) *CertDBAccessor {
    75  	cffslAcc := new(CertDBAccessor)
    76  	cffslAcc.db = db
    77  	cffslAcc.accessor = certsql.NewAccessor(db)
    78  	cffslAcc.level = level
    79  	return cffslAcc
    80  }
    81  
    82  func (d *CertDBAccessor) checkDB() error {
    83  	if d.db == nil {
    84  		return errors.New("Database is not set")
    85  	}
    86  	return nil
    87  }
    88  
    89  // SetDB changes the underlying sql.DB object Accessor is manipulating.
    90  func (d *CertDBAccessor) SetDB(db *sqlx.DB) {
    91  	d.db = db
    92  }
    93  
    94  // InsertCertificate puts a CertificateRecord into db.
    95  func (d *CertDBAccessor) InsertCertificate(cr certdb.CertificateRecord) error {
    96  
    97  	log.Debug("DB: Insert Certificate")
    98  
    99  	err := d.checkDB()
   100  	if err != nil {
   101  		return err
   102  	}
   103  	id, err := util.GetEnrollmentIDFromPEM([]byte(cr.PEM))
   104  	if err != nil {
   105  		return err
   106  	}
   107  
   108  	ip := new(big.Int)
   109  	ip.SetString(cr.Serial, 10) //base 10
   110  
   111  	serial := util.GetSerialAsHex(ip)
   112  	aki := strings.TrimLeft(cr.AKI, "0")
   113  
   114  	log.Debug("Saved serial number as hex ", serial)
   115  
   116  	var record = new(CertRecord)
   117  	record.ID = id
   118  	record.Serial = serial
   119  	record.AKI = aki
   120  	record.CALabel = cr.CALabel
   121  	record.Status = cr.Status
   122  	record.Reason = cr.Reason
   123  	record.Expiry = cr.Expiry.UTC()
   124  	record.RevokedAt = cr.RevokedAt.UTC()
   125  	record.PEM = cr.PEM
   126  	record.Level = d.level
   127  
   128  	res, err := d.db.NamedExec(insertSQL, record)
   129  	if err != nil {
   130  		return errors.Wrap(err, "Failed to insert record into database")
   131  	}
   132  
   133  	numRowsAffected, err := res.RowsAffected()
   134  
   135  	if numRowsAffected == 0 {
   136  		return errors.New("Failed to insert the certificate record; no rows affected")
   137  	}
   138  
   139  	if numRowsAffected != 1 {
   140  		return errors.Errorf("Expected to affect 1 entry in certificate database but affected %d",
   141  			numRowsAffected)
   142  	}
   143  
   144  	return err
   145  }
   146  
   147  // GetCertificatesByID gets a CertificateRecord indexed by id.
   148  func (d *CertDBAccessor) GetCertificatesByID(id string) (crs []CertRecord, err error) {
   149  	log.Debugf("DB: Get certificate by ID (%s)", id)
   150  	err = d.checkDB()
   151  	if err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	err = d.db.Select(&crs, fmt.Sprintf(d.db.Rebind(selectSQLbyID), sqlstruct.Columns(CertRecord{})), id)
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	return crs, nil
   161  }
   162  
   163  // GetCertificate gets a CertificateRecord indexed by serial.
   164  func (d *CertDBAccessor) GetCertificate(serial, aki string) (crs []certdb.CertificateRecord, err error) {
   165  	log.Debugf("DB: Get certificate by serial (%s) and aki (%s)", serial, aki)
   166  	crs, err = d.accessor.GetCertificate(serial, aki)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  
   171  	return crs, nil
   172  }
   173  
   174  // GetCertificateWithID gets a CertificateRecord indexed by serial and returns user too.
   175  func (d *CertDBAccessor) GetCertificateWithID(serial, aki string) (crs CertRecord, err error) {
   176  	log.Debugf("DB: Get certificate by serial (%s) and aki (%s)", serial, aki)
   177  
   178  	err = d.checkDB()
   179  	if err != nil {
   180  		return crs, err
   181  	}
   182  
   183  	err = d.db.Get(&crs, fmt.Sprintf(d.db.Rebind(selectSQL), sqlstruct.Columns(CertRecord{})), serial, aki)
   184  	if err != nil {
   185  		return crs, getError(err, "Certificate")
   186  	}
   187  
   188  	return crs, nil
   189  }
   190  
   191  // GetUnexpiredCertificates gets all unexpired certificate from db.
   192  func (d *CertDBAccessor) GetUnexpiredCertificates() (crs []certdb.CertificateRecord, err error) {
   193  	crs, err = d.accessor.GetUnexpiredCertificates()
   194  	if err != nil {
   195  		return nil, err
   196  	}
   197  	return crs, err
   198  }
   199  
   200  // GetRevokedCertificates returns revoked certificates
   201  func (d *CertDBAccessor) GetRevokedCertificates(expiredAfter, expiredBefore, revokedAfter, revokedBefore time.Time) ([]certdb.CertificateRecord, error) {
   202  	log.Debugf("DB: Get revoked certificates that were revoked after %s and before %s that are expired after %s and before %s",
   203  		revokedAfter, revokedBefore, expiredAfter, expiredBefore)
   204  	err := d.checkDB()
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  	var crs []certdb.CertificateRecord
   209  	revokedSQL := "SELECT %s FROM certificates WHERE (WHERE_CLAUSE);"
   210  	whereConds := []string{"status='revoked' AND expiry > ? AND revoked_at > ?"}
   211  	args := []interface{}{expiredAfter, revokedAfter}
   212  	if !expiredBefore.IsZero() {
   213  		whereConds = append(whereConds, "expiry < ?")
   214  		args = append(args, expiredBefore)
   215  	}
   216  	if !revokedBefore.IsZero() {
   217  		whereConds = append(whereConds, "revoked_at < ?")
   218  		args = append(args, revokedBefore)
   219  	}
   220  	whereClause := strings.Join(whereConds, " AND ")
   221  	revokedSQL = strings.Replace(revokedSQL, "WHERE_CLAUSE", whereClause, 1)
   222  	err = d.db.Select(&crs, fmt.Sprintf(d.db.Rebind(revokedSQL),
   223  		sqlstruct.Columns(certdb.CertificateRecord{})), args...)
   224  	if err != nil {
   225  		return crs, getError(err, "Certificate")
   226  	}
   227  	return crs, nil
   228  }
   229  
   230  // GetRevokedAndUnexpiredCertificates returns revoked and unexpired certificates
   231  func (d *CertDBAccessor) GetRevokedAndUnexpiredCertificates() ([]certdb.CertificateRecord, error) {
   232  	crs, err := d.accessor.GetRevokedAndUnexpiredCertificates()
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  	return crs, err
   237  }
   238  
   239  // GetRevokedAndUnexpiredCertificatesByLabel returns revoked and unexpired certificates matching the label
   240  func (d *CertDBAccessor) GetRevokedAndUnexpiredCertificatesByLabel(label string) ([]certdb.CertificateRecord, error) {
   241  	crs, err := d.accessor.GetRevokedAndUnexpiredCertificatesByLabel(label)
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  	return crs, err
   246  }
   247  
   248  // RevokeCertificatesByID updates all certificates for a given ID and marks them revoked.
   249  func (d *CertDBAccessor) RevokeCertificatesByID(id string, reasonCode int) (crs []CertRecord, err error) {
   250  	log.Debugf("DB: Revoke certificate by ID (%s)", id)
   251  
   252  	err = d.checkDB()
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  
   257  	var record = new(CertRecord)
   258  	record.ID = id
   259  	record.Reason = reasonCode
   260  
   261  	err = d.db.Select(&crs, d.db.Rebind("SELECT * FROM certificates WHERE (id = ? AND status != 'revoked')"), id)
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  
   266  	_, err = d.db.NamedExec(updateRevokeSQL, record)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  
   271  	return crs, err
   272  }
   273  
   274  // RevokeCertificate updates a certificate with a given serial number and marks it revoked.
   275  func (d *CertDBAccessor) RevokeCertificate(serial, aki string, reasonCode int) error {
   276  	log.Debugf("DB: Revoke certificate by serial (%s) and aki (%s)", serial, aki)
   277  
   278  	err := d.accessor.RevokeCertificate(serial, aki, reasonCode)
   279  	return err
   280  }
   281  
   282  // InsertOCSP puts a new certdb.OCSPRecord into the db.
   283  func (d *CertDBAccessor) InsertOCSP(rr certdb.OCSPRecord) error {
   284  	return d.accessor.InsertOCSP(rr)
   285  }
   286  
   287  // GetOCSP retrieves a certdb.OCSPRecord from db by serial.
   288  func (d *CertDBAccessor) GetOCSP(serial, aki string) (ors []certdb.OCSPRecord, err error) {
   289  	return d.accessor.GetOCSP(serial, aki)
   290  }
   291  
   292  // GetUnexpiredOCSPs retrieves all unexpired certdb.OCSPRecord from db.
   293  func (d *CertDBAccessor) GetUnexpiredOCSPs() (ors []certdb.OCSPRecord, err error) {
   294  	return d.accessor.GetUnexpiredOCSPs()
   295  }
   296  
   297  // UpdateOCSP updates a ocsp response record with a given serial number.
   298  func (d *CertDBAccessor) UpdateOCSP(serial, aki, body string, expiry time.Time) error {
   299  	return d.accessor.UpdateOCSP(serial, aki, body, expiry)
   300  }
   301  
   302  // UpsertOCSP update a ocsp response record with a given serial number,
   303  // or insert the record if it doesn't yet exist in the db
   304  func (d *CertDBAccessor) UpsertOCSP(serial, aki, body string, expiry time.Time) error {
   305  	return d.accessor.UpsertOCSP(serial, aki, body, expiry)
   306  }