-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial support for OCI key manager #4
base: main
Are you sure you want to change the base?
Changes from all commits
0d898f9
87fa666
e1c0f47
a57f292
dd59238
ef1bf5f
a59226b
57fc11f
9a5b01e
c78c7f1
c753fb7
77fd552
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/* | ||
* Copyright (c) 2024 Oracle Corporation | ||
* All rights reserved. | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
|
||
package keymanager | ||
|
||
import ( | ||
"intel/kbs/v1/model" | ||
"intel/kbs/v1/ociclient" | ||
"time" | ||
|
||
"github.com/google/uuid" | ||
"github.com/pkg/errors" | ||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
type OCIManager struct { | ||
client ociclient.OCIClient | ||
} | ||
|
||
func NewOCIManager(c ociclient.OCIClient) *OCIManager { | ||
return &OCIManager{c} | ||
} | ||
|
||
func (om *OCIManager) CreateKey(keyRequest *model.KeyRequest) (*model.KeyAttributes, error) { | ||
if keyRequest.KeyInfo.OciCompartmentId == "" || keyRequest.KeyInfo.OciKeyId == "" || | ||
keyRequest.KeyInfo.OciSecretName == "" || keyRequest.KeyInfo.OciVaultId == "" { | ||
return nil, errors.New("Missing oci_compartment_id, oci_key_id, oci_secret_name, or oci_vault_id") | ||
} | ||
|
||
newUuid, err := uuid.NewRandom() | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to create new UUID") | ||
} | ||
keyAttributes := &model.KeyAttributes{ | ||
ID: newUuid, | ||
Algorithm: keyRequest.KeyInfo.Algorithm, | ||
KeyLength: keyRequest.KeyInfo.KeyLength, | ||
OciCompartmentId: keyRequest.KeyInfo.OciCompartmentId, | ||
OciKeyId: keyRequest.KeyInfo.OciKeyId, | ||
OciSecretName: keyRequest.KeyInfo.OciSecretName, | ||
OciVaultId: keyRequest.KeyInfo.OciVaultId, | ||
TransferPolicyId: keyRequest.TransferPolicyID, | ||
CreatedAt: time.Now().UTC(), | ||
} | ||
|
||
log.Infof("OCI: Creating key: algorithm = %q; secret name = %q", keyAttributes.Algorithm, keyAttributes.OciSecretName) | ||
|
||
if err := om.client.CreateKey(keyAttributes); err != nil { | ||
return nil, errors.Wrap(err, "failed to create key") | ||
} | ||
|
||
return keyAttributes, nil | ||
} | ||
|
||
func (om *OCIManager) DeleteKey(keyAttributes *model.KeyAttributes) error { | ||
err := om.client.DeleteKey(keyAttributes.OciSecretId) | ||
if err != nil { | ||
log.Errorf("Error while deleting key: %s", err.Error()) | ||
return err | ||
} | ||
|
||
log.Infof("OCI: Deleting key: algorithm = %q; secret id = %q", keyAttributes.Algorithm, keyAttributes.OciSecretId) | ||
|
||
return nil | ||
} | ||
|
||
func (om *OCIManager) RegisterKey(keyRequest *model.KeyRequest) (*model.KeyAttributes, error) { | ||
if keyRequest.KeyInfo.OciSecretId == "" { | ||
return nil, errors.New("oci_secret_id cannot be empty for register operation in OCI mode") | ||
} | ||
|
||
newUuid, err := uuid.NewRandom() | ||
if err != nil { | ||
return nil, errors.Wrap(err, "failed to create new UUID") | ||
} | ||
keyAttributes := &model.KeyAttributes{ | ||
ID: newUuid, | ||
Algorithm: keyRequest.KeyInfo.Algorithm, | ||
KeyLength: keyRequest.KeyInfo.KeyLength, | ||
OciSecretId: keyRequest.KeyInfo.OciSecretId, | ||
TransferPolicyId: keyRequest.TransferPolicyID, | ||
CreatedAt: time.Now().UTC(), | ||
} | ||
|
||
log.Infof("OCI: Registering key: algorithm = %q; secret id = %q", keyAttributes.Algorithm, keyAttributes.OciSecretId) | ||
|
||
return keyAttributes, nil | ||
} | ||
|
||
func (om *OCIManager) TransferKey(keyAttributes *model.KeyAttributes) ([]byte, error) { | ||
if keyAttributes.OciSecretId == "" { | ||
return nil, errors.New("key is not created with OCI key manager") | ||
} | ||
|
||
secretVersionNumber := int64(0) | ||
|
||
log.Infof("OCI: Transferring key: secret id = %q; secret version = %d", keyAttributes.OciSecretId, secretVersionNumber) | ||
|
||
return om.client.GetKey(keyAttributes.OciSecretId, secretVersionNumber) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,6 +60,21 @@ type KeyInfo struct { | |
// KMIP Key ID, if the key is already created in KMIP Backend | ||
// example: 7110194b-a703-4657-9d7f-3e02b62f2ed8 | ||
KmipKeyID string `json:"kmip_key_id,omitempty"` | ||
// The OCID of the compartment where you want to create the secret. | ||
// example: ocid1.test.oc1..<unique_ID>EXAMPLE-compartmentId-Value | ||
OciCompartmentId string `json:"oci_compartment_id,omitempty"` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need to return all these fields in response? if not, then we can keep these in separate struct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that's what I was thinking too. I just did this for now since the Vault and KMIP stuff was also all mixed in there. If we want to separate them out, then I can do a patch that first pulls out the Vault and KMIP stuff into separate structs, then change this patch to also be in a separate struct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we only have KmipKeyID field for KMIP which is needed in both request and response (none specific to vault), hence kept KmipKeyID in KeyInfo struct which is shared between request and response. If the OCI fields also need to be kept in both request and response, then we can leave them in KeyInfo struct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While separating out the OCI stuff, I was going over the other code. Both If so, one option for the OCI code is to just put |
||
// The OCID of the master encryption key that is used to encrypt the secret. | ||
// example: ocid1.test.oc1..<unique_ID>EXAMPLE-keyId-Value | ||
OciKeyId string `json:"oci_key_id,omitempty"` | ||
// OCI Secret ID, if the key is already created in OCI backend | ||
// example: ocid1.test.oc1..<unique_ID>EXAMPLE-secretId-Value | ||
OciSecretId string `json:"oci_secret_id,omitempty"` | ||
// A user-friendly name for the secret. | ||
// example: EXAMPLE-secretName-Value | ||
OciSecretName string `json:"oci_secret_name,omitempty"` | ||
// The OCID of the vault where you want to create the secret. | ||
// example: ocid1.test.oc1..<unique_ID>EXAMPLE-vaultId-Value | ||
OciVaultId string `json:"oci_vault_id,omitempty"` | ||
} | ||
|
||
type KeyFilterCriteria struct { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* Copyright (c) 2024 Oracle Corporation | ||
* All rights reserved. | ||
* SPDX-License-Identifier: BSD-3-Clause | ||
*/ | ||
|
||
package ociclient | ||
|
||
import ( | ||
"intel/kbs/v1/model" | ||
|
||
"github.com/stretchr/testify/mock" | ||
) | ||
|
||
// MockOCIClient is a mock of OCIClient interface | ||
type MockOCIClient struct { | ||
mock.Mock | ||
} | ||
|
||
// NewMockOCIClient creates a new mock instance | ||
func NewMockOCIClient() *MockOCIClient { | ||
return &MockOCIClient{} | ||
} | ||
|
||
// InitializeClient mocks base method | ||
func (m *MockOCIClient) InitializeClient(serverIP, serverPort, clientToken string) error { | ||
args := m.Called(serverIP, serverPort, clientToken) | ||
return args.Error(0) | ||
} | ||
|
||
// CreateKey mocks base method | ||
func (m *MockOCIClient) CreateKey(keyAttrib *model.KeyAttributes) error { | ||
args := m.Called(keyAttrib) | ||
return args.Error(0) | ||
} | ||
|
||
// DeleteKey mocks base method | ||
func (m *MockOCIClient) DeleteKey(id string) error { | ||
args := m.Called(id) | ||
return args.Error(0) | ||
} | ||
|
||
// GetKey mocks base method | ||
func (m *MockOCIClient) GetKey(id string) ([]byte, error) { | ||
args := m.Called(id) | ||
return args.Get(0).([]byte), args.Error(1) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use errors.wrap() for consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest to re-consider the usage of
github.com/pkg/errors
since it's archived and not maintained. In general, projects are trying to move away from it and use error wrapping from the std library.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll update it to errors.Wrap() for now. I was just copying the code from vault_key_manager.go:DeleteKey(). If we want to move the std library implementation, I would suggest doing that as a separate patch(set) that updates error usage everywhere.
I'm also not 100% sure what wrapping from the std library you want to do -- I don't see an errors.Wrap() function there. Do you mean using fmt.Errorf() or errors.Join()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking
%w
witherr