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 }