2020-09-02 07:42:03 -04:00
package kms
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/boundary/internal/db"
2020-11-05 16:54:03 -05:00
"github.com/hashicorp/boundary/internal/errors"
2020-09-02 07:42:03 -04:00
wrapping "github.com/hashicorp/go-kms-wrapping"
)
// CreateDatabaseKey inserts into the repository and returns the new database key and
// database key version. There are no valid options at this time.
2021-01-12 08:50:19 -08:00
func ( r * Repository ) CreateDatabaseKey ( ctx context . Context , rkvWrapper wrapping . Wrapper , key [] byte , _ ... Option ) ( * DatabaseKey , * DatabaseKeyVersion , error ) {
const op = "kms.(Repository).CreateDatabaseKey"
2020-09-02 07:42:03 -04:00
var returnedDk , returnedDv interface {}
_ , err := r . writer . DoTx (
ctx ,
db . StdRetryCnt ,
db . ExpBackoff {},
func ( reader db . Reader , w db . Writer ) error {
var err error
2020-09-02 14:37:19 -04:00
if returnedDk , returnedDv , err = createDatabaseKeyTx ( ctx , reader , w , rkvWrapper , key ); err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op )
2020-09-02 07:42:03 -04:00
}
return nil
},
)
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op )
2020-09-02 07:42:03 -04:00
}
2021-01-12 08:50:19 -08:00
return returnedDk .( * DatabaseKey ), returnedDv .( * DatabaseKeyVersion ), nil
2020-09-02 07:42:03 -04:00
}
2020-09-02 14:37:19 -04:00
// createDatabaseKeyTx inserts into the db (via db.Writer) and returns the new database key
2020-09-02 07:42:03 -04:00
// and database key version. This function encapsulates all the work required within
// a db.TxHandler and allows this capability to be shared with the iam repo.
2020-09-02 14:37:19 -04:00
func createDatabaseKeyTx ( ctx context . Context , r db . Reader , w db . Writer , rkvWrapper wrapping . Wrapper , key [] byte ) ( * DatabaseKey , * DatabaseKeyVersion , error ) {
2021-01-12 08:50:19 -08:00
const op = "kms.createDatabaseKeyTx"
2020-09-02 07:42:03 -04:00
if rkvWrapper == nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . New ( ctx , errors . InvalidParameter , op , "missing key wrapper" )
2020-09-02 07:42:03 -04:00
}
if len ( key ) == 0 {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . New ( ctx , errors . InvalidParameter , op , "missing key" )
2020-09-02 07:42:03 -04:00
}
rootKeyVersionId := rkvWrapper . KeyID ()
switch {
case ! strings . HasPrefix ( rootKeyVersionId , RootKeyVersionPrefix ):
2021-07-29 18:52:56 -06:00
return nil , nil , errors . New ( ctx , errors . InvalidParameter , op , fmt . Sprintf ( "root key version id %s doesn't start with prefix %s" , rootKeyVersionId , RootKeyVersionPrefix ))
2020-09-02 07:42:03 -04:00
case rootKeyVersionId == "" :
2021-07-29 18:52:56 -06:00
return nil , nil , errors . New ( ctx , errors . InvalidParameter , op , "missing root key version id" )
2020-09-02 07:42:03 -04:00
}
rv := AllocRootKeyVersion ()
rv . PrivateId = rootKeyVersionId
err := r . LookupById ( ctx , & rv )
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( fmt . Sprintf ( "unable to lookup root key version %s" , rootKeyVersionId )))
2020-09-02 07:42:03 -04:00
}
dk := AllocDatabaseKey ()
dv := AllocDatabaseKeyVersion ()
id , err := newDatabaseKeyId ()
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op )
2020-09-02 07:42:03 -04:00
}
dk . PrivateId = id
dk . RootKeyId = rv . RootKeyId
id , err = newDatabaseKeyVersionId ()
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op )
2020-09-02 07:42:03 -04:00
}
dv . PrivateId = id
dv . DatabaseKeyId = dk . PrivateId
dv . RootKeyVersionId = rootKeyVersionId
dv . Key = key
if err := dv . Encrypt ( ctx , rkvWrapper ); err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op )
2020-09-02 07:42:03 -04:00
}
// no oplog entries for keys
if err := w . Create ( ctx , & dk ); err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "keys create" ))
2020-09-02 07:42:03 -04:00
}
// no oplog entries for key versions
if err := w . Create ( ctx , & dv ); err != nil {
2021-07-29 18:52:56 -06:00
return nil , nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( "key versions create" ))
2020-09-02 07:42:03 -04:00
}
2021-01-12 08:50:19 -08:00
return & dk , & dv , nil
2020-09-02 07:42:03 -04:00
}
// LookupDatabaseKey will look up a key in the repository. If the key is not
// found, it will return nil, nil.
2021-01-12 08:50:19 -08:00
func ( r * Repository ) LookupDatabaseKey ( ctx context . Context , privateId string , _ ... Option ) ( * DatabaseKey , error ) {
const op = "kms.(Repository).LookupDatabaseKey"
2020-09-02 07:42:03 -04:00
if privateId == "" {
2021-07-29 18:52:56 -06:00
return nil , errors . New ( ctx , errors . InvalidParameter , op , "missing private id" )
2020-09-02 07:42:03 -04:00
}
k := AllocDatabaseKey ()
k . PrivateId = privateId
if err := r . reader . LookupById ( ctx , & k ); err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op , errors . WithMsg ( fmt . Sprintf ( "failed for %s" , privateId )))
2020-09-02 07:42:03 -04:00
}
return & k , nil
}
// DeleteDatabaseKey deletes the key for the provided id from the
// repository returning a count of the number of records deleted. All options
// are ignored.
2021-01-12 08:50:19 -08:00
func ( r * Repository ) DeleteDatabaseKey ( ctx context . Context , privateId string , _ ... Option ) ( int , error ) {
const op = "kms.(Repository).LookupDatabaseKey"
2020-09-02 07:42:03 -04:00
if privateId == "" {
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . New ( ctx , errors . InvalidParameter , op , "missing private id" )
2020-09-02 07:42:03 -04:00
}
k := AllocDatabaseKey ()
k . PrivateId = privateId
if err := r . reader . LookupById ( ctx , & k ); err != nil {
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . Wrap ( ctx , err , op , errors . WithMsg ( fmt . Sprintf ( "failed for %s" , privateId )))
2020-09-02 07:42:03 -04:00
}
var rowsDeleted int
_ , err := r . writer . DoTx (
ctx ,
db . StdRetryCnt ,
db . ExpBackoff {},
func ( _ db . Reader , w db . Writer ) ( err error ) {
dk := k . Clone ()
// no oplog entries for root keys
rowsDeleted , err = w . Delete ( ctx , dk )
2021-01-12 08:50:19 -08:00
if err != nil {
2021-07-29 18:52:56 -06:00
return errors . Wrap ( ctx , err , op )
2020-09-02 07:42:03 -04:00
}
2021-01-12 08:50:19 -08:00
if rowsDeleted > 1 {
2021-07-29 18:52:56 -06:00
return errors . New ( ctx , errors . MultipleRecords , op , "more than 1 resource would have been deleted" )
2021-01-12 08:50:19 -08:00
}
return nil
2020-09-02 07:42:03 -04:00
},
)
if err != nil {
2021-07-29 18:52:56 -06:00
return db . NoRowsAffected , errors . Wrap ( ctx , err , op , errors . WithMsg ( fmt . Sprintf ( "failed for %s" , privateId )))
2020-09-02 07:42:03 -04:00
}
return rowsDeleted , nil
}
// ListDatabaseKeys will list the keys. Supports the WithLimit option.
2020-09-03 13:23:46 -04:00
func ( r * Repository ) ListDatabaseKeys ( ctx context . Context , opt ... Option ) ([] Dek , error ) {
2021-01-12 08:50:19 -08:00
const op = "kms.(Repository).ListDatabaseKeys"
2020-09-02 07:42:03 -04:00
var keys [] * DatabaseKey
err := r . list ( ctx , & keys , "1=1" , nil , opt ... )
if err != nil {
2021-07-29 18:52:56 -06:00
return nil , errors . Wrap ( ctx , err , op )
2020-09-02 07:42:03 -04:00
}
2020-09-03 13:23:46 -04:00
deks := make ([] Dek , 0 , len ( keys ))
for _ , key := range keys {
deks = append ( deks , key )
}
return deks , nil
2020-09-02 07:42:03 -04:00
}