feat(credential/static): Implement CRUDL for UsernamePasswordDomain
Co-authored-by: Hugo <10965479+hugoghx@users.noreply.github.com> Co-authored-by: Andrew Gaffney <109917432+anGaffney@users.noreply.github.com>
This commit is contained in:
1 parent
141225baad
commit
34b4028d31
11 files changed
+1417
-20
No files matched your search
@@ -22,6 +22,7 @@ type listCredentialResult struct {
|
||||
Name string
|
||||
Description string
|
||||
Username string
|
||||
Domain string
|
||||
KeyId string
|
||||
Hmac1 string
|
||||
Hmac2 string
|
||||
@@ -71,6 +72,26 @@ func (c *listCredentialResult) toCredential(ctx context.Context) (credential.Sta
|
||||
cred.PasswordHmac = []byte(c.Hmac1)
|
||||
}
|
||||
return cred, nil
|
||||
case "upd":
|
||||
cred := &UsernamePasswordDomainCredential{
|
||||
UsernamePasswordDomainCredential: &store.UsernamePasswordDomainCredential{
|
||||
PublicId: c.PublicId,
|
||||
StoreId: c.StoreId,
|
||||
Name: c.Name,
|
||||
Description: c.Description,
|
||||
CreateTime: c.CreateTime,
|
||||
UpdateTime: c.UpdateTime,
|
||||
Version: uint32(c.Version),
|
||||
Username: c.Username,
|
||||
Domain: c.Domain,
|
||||
KeyId: c.KeyId,
|
||||
},
|
||||
}
|
||||
// Assign byte slices only if the string isn't empty
|
||||
if c.Hmac1 != "" {
|
||||
cred.PasswordHmac = []byte(c.Hmac1)
|
||||
}
|
||||
return cred, nil
|
||||
case "ssh":
|
||||
cred := &SshPrivateKeyCredential{
|
||||
SshPrivateKeyCredential: &store.SshPrivateKeyCredential{
|
||||
|
||||
@@ -9,6 +9,7 @@ const (
|
||||
descriptionField = "Description"
|
||||
usernameField = "Username"
|
||||
passwordField = "Password"
|
||||
domainField = "Domain"
|
||||
privateKeyField = "PrivateKey"
|
||||
PrivateKeyPassphraseField = "PrivateKeyPassphrase"
|
||||
objectField = "Object"
|
||||
|
||||
@@ -55,6 +55,7 @@ select sum(reltuples::bigint) as estimate
|
||||
where oid in (
|
||||
'credential_static_json_credential'::regclass,
|
||||
'credential_static_username_password_credential'::regclass,
|
||||
'credential_static_username_password_domain_credential'::regclass,
|
||||
'credential_static_ssh_private_key_credential'::regclass
|
||||
)
|
||||
`
|
||||
@@ -77,6 +78,11 @@ upw_creds as (
|
||||
from credential_static_username_password_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
upd_creds as (
|
||||
select *
|
||||
from credential_static_username_password_domain_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
ssh_creds as (
|
||||
select *
|
||||
from credential_static_ssh_private_key_credential
|
||||
@@ -92,6 +98,7 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
null as username, -- Add this to make the union uniform
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
object_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
@@ -107,11 +114,28 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
'upw' as type
|
||||
from upw_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
domain,
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
'upd' as type
|
||||
from upd_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
@@ -122,6 +146,7 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
private_key_hmac as hmac1,
|
||||
private_key_passphrase_hmac as hmac2,
|
||||
@@ -152,6 +177,11 @@ upw_creds as (
|
||||
from credential_static_username_password_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
upd_creds as (
|
||||
select *
|
||||
from credential_static_username_password_domain_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
ssh_creds as (
|
||||
select *
|
||||
from credential_static_ssh_private_key_credential
|
||||
@@ -167,6 +197,7 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
null as username, -- Add this to make the union uniform
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
object_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
@@ -182,6 +213,7 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
@@ -197,6 +229,23 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
domain,
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
'upd' as type
|
||||
from upd_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
private_key_hmac as hmac1,
|
||||
private_key_passphrase_hmac as hmac2,
|
||||
@@ -227,6 +276,11 @@ upw_creds as (
|
||||
from credential_static_username_password_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
upd_creds as (
|
||||
select *
|
||||
from credential_static_username_password_domain_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
ssh_creds as (
|
||||
select *
|
||||
from credential_static_ssh_private_key_credential
|
||||
@@ -242,6 +296,7 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
null as username, -- Add this to make the union uniform
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
object_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
@@ -257,6 +312,7 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
@@ -272,6 +328,23 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
domain,
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
'upd' as type
|
||||
from upd_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
private_key_hmac as hmac1,
|
||||
private_key_passphrase_hmac as hmac2,
|
||||
@@ -303,6 +376,11 @@ upw_creds as (
|
||||
from credential_static_username_password_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
upd_creds as (
|
||||
select *
|
||||
from credential_static_username_password_domain_credential
|
||||
where public_id in (select public_id from credentials)
|
||||
),
|
||||
ssh_creds as (
|
||||
select *
|
||||
from credential_static_ssh_private_key_credential
|
||||
@@ -318,6 +396,7 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
null as username, -- Add this to make the union uniform
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
object_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
@@ -333,6 +412,7 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
@@ -348,6 +428,23 @@ final as (
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
domain,
|
||||
key_id,
|
||||
password_hmac as hmac1,
|
||||
null::bytea as hmac2, -- Add this to make the union uniform
|
||||
'upd' as type
|
||||
from upd_creds
|
||||
union
|
||||
select public_id,
|
||||
store_id,
|
||||
project_id,
|
||||
name,
|
||||
description,
|
||||
create_time,
|
||||
update_time,
|
||||
version,
|
||||
username,
|
||||
null as domain, -- Add this to make the union uniform
|
||||
key_id,
|
||||
private_key_hmac as hmac1,
|
||||
private_key_passphrase_hmac as hmac2,
|
||||
|
||||
@@ -107,6 +107,95 @@ func (r *Repository) CreateUsernamePasswordCredential(
|
||||
return newCred, nil
|
||||
}
|
||||
|
||||
// CreateUsernamePasswordDomainCredential inserts c into the repository and returns a new
|
||||
// UsernamePasswordDomainCredential containing the credential's PublicId. c is not
|
||||
// changed. c must not contain a PublicId. The PublicId is generated and
|
||||
// assigned by this method. c must contain a valid StoreId.
|
||||
//
|
||||
// The password is encrypted and a HmacSha256 of the password is calculated. Only the
|
||||
// PasswordHmac is returned, the plain-text and encrypted password is not returned.
|
||||
//
|
||||
// Both c.Name and c.Description are optional. If c.Name is set, it must
|
||||
// be unique within c.ProjectId. Both c.CreateTime and c.UpdateTime are
|
||||
// ignored.
|
||||
func (r *Repository) CreateUsernamePasswordDomainCredential(
|
||||
ctx context.Context,
|
||||
projectId string,
|
||||
c *UsernamePasswordDomainCredential,
|
||||
_ ...Option,
|
||||
) (*UsernamePasswordDomainCredential, error) {
|
||||
const op = "static.(Repository).CreateUsernamePasswordDomainCredential"
|
||||
if c == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing credential")
|
||||
}
|
||||
if c.UsernamePasswordDomainCredential == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing embedded credential")
|
||||
}
|
||||
if projectId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing project id")
|
||||
}
|
||||
if c.Username == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing username")
|
||||
}
|
||||
if c.Domain == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing domain")
|
||||
}
|
||||
if c.Password == nil {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing password")
|
||||
}
|
||||
if c.StoreId == "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing store id")
|
||||
}
|
||||
if c.PublicId != "" {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "public id not empty")
|
||||
}
|
||||
|
||||
c = c.clone()
|
||||
id, err := credential.NewUsernamePasswordDomainCredentialId(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
c.PublicId = id
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
// encrypt
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
if err := c.encrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
var newCred *UsernamePasswordDomainCredential
|
||||
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
newCred = c.clone()
|
||||
if err := w.Create(ctx, newCred,
|
||||
db.WithOplog(oplogWrapper, newCred.oplog(oplog.OpType_OP_TYPE_CREATE))); err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if errors.IsUniqueError(err) {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in store: %s: name %s already exists", c.StoreId, c.Name)))
|
||||
}
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("in store: %s", c.StoreId)))
|
||||
}
|
||||
|
||||
// Clear password fields, only PasswordHmac should be returned
|
||||
newCred.CtPassword = nil
|
||||
newCred.Password = nil
|
||||
|
||||
return newCred, nil
|
||||
}
|
||||
|
||||
// CreateSshPrivateKeyCredential inserts c into the repository and returns a new
|
||||
// SshPrivateKeyCredential containing the credential's PublicId. c is not
|
||||
// changed. c must not contain a PublicId. The PublicId is generated and
|
||||
@@ -308,6 +397,20 @@ func (r *Repository) LookupCredential(ctx context.Context, publicId string, _ ..
|
||||
upCred.Password = nil
|
||||
cred = upCred
|
||||
|
||||
case credential.UsernamePasswordDomainSubtype:
|
||||
updCred := allocUsernamePasswordDomainCredential()
|
||||
updCred.PublicId = publicId
|
||||
if err := r.reader.LookupByPublicId(ctx, updCred); err != nil {
|
||||
if errors.IsNotFoundError(err) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg(fmt.Sprintf("failed for: %s", publicId)))
|
||||
}
|
||||
// Clear password fields, only passwordHmac should be returned
|
||||
updCred.CtPassword = nil
|
||||
updCred.Password = nil
|
||||
cred = updCred
|
||||
|
||||
case credential.SshPrivateKeySubtype:
|
||||
spkCred := allocSshPrivateKeyCredential()
|
||||
spkCred.PublicId = publicId
|
||||
@@ -457,6 +560,122 @@ func (r *Repository) UpdateUsernamePasswordCredential(ctx context.Context,
|
||||
return returnedCredential, rowsUpdated, nil
|
||||
}
|
||||
|
||||
// UpdateUsernamePasswordDomainCredential updates the repository entry for c.PublicId with
|
||||
// the values in c for the fields listed in fieldMaskPaths. It returns a
|
||||
// new UsernamePasswordDomainCredential containing the updated values and a count of the
|
||||
// number of records updated. c is not changed.
|
||||
//
|
||||
// c must contain a valid PublicId. Only Name, Description, Username, Password, and Domain can be
|
||||
// changed. If c.Name is set to a non-empty string, it must be unique within c.ProjectId.
|
||||
//
|
||||
// An attribute of c will be set to NULL in the database if the attribute
|
||||
// in c is the zero value and it is included in fieldMaskPaths.
|
||||
func (r *Repository) UpdateUsernamePasswordDomainCredential(ctx context.Context,
|
||||
projectId string,
|
||||
c *UsernamePasswordDomainCredential,
|
||||
version uint32,
|
||||
fieldMaskPaths []string,
|
||||
_ ...Option,
|
||||
) (*UsernamePasswordDomainCredential, int, error) {
|
||||
const op = "static.(Repository).UpdateUsernamePasswordDomainCredential"
|
||||
if c == nil {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing credential")
|
||||
}
|
||||
if c.UsernamePasswordDomainCredential == nil {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing embedded credential")
|
||||
}
|
||||
if c.PublicId == "" {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidPublicId, op, "missing public id")
|
||||
}
|
||||
if version == 0 {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing version")
|
||||
}
|
||||
if projectId == "" {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing project id")
|
||||
}
|
||||
if c.StoreId == "" {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidParameter, op, "missing store id")
|
||||
}
|
||||
c = c.clone()
|
||||
|
||||
for _, f := range fieldMaskPaths {
|
||||
switch {
|
||||
case strings.EqualFold(nameField, f):
|
||||
case strings.EqualFold(descriptionField, f):
|
||||
case strings.EqualFold(usernameField, f):
|
||||
case strings.EqualFold(passwordField, f):
|
||||
case strings.EqualFold(domainField, f):
|
||||
default:
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.InvalidFieldMask, op, f)
|
||||
}
|
||||
}
|
||||
dbMask, nullFields := dbw.BuildUpdatePaths(
|
||||
map[string]any{
|
||||
nameField: c.Name,
|
||||
descriptionField: c.Description,
|
||||
usernameField: c.Username,
|
||||
passwordField: c.Password,
|
||||
domainField: c.Domain,
|
||||
},
|
||||
fieldMaskPaths,
|
||||
nil,
|
||||
)
|
||||
if len(dbMask) == 0 && len(nullFields) == 0 {
|
||||
return nil, db.NoRowsAffected, errors.New(ctx, errors.EmptyFieldMask, op, "missing field mask")
|
||||
}
|
||||
|
||||
for _, f := range fieldMaskPaths {
|
||||
if strings.EqualFold(passwordField, f) {
|
||||
// Password has been updated, re-encrypt and recalculate hmac
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
if err := c.encrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, db.NoRowsAffected, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
// Set PasswordHmac and CtPassword masks for update.
|
||||
dbMask = append(dbMask, "PasswordHmac", "CtPassword", "KeyId")
|
||||
}
|
||||
}
|
||||
|
||||
oplogWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeOplog)
|
||||
if err != nil {
|
||||
return nil, db.NoRowsAffected,
|
||||
errors.Wrap(ctx, err, op, errors.WithMsg("unable to get oplog wrapper"))
|
||||
}
|
||||
|
||||
var rowsUpdated int
|
||||
var returnedCredential *UsernamePasswordDomainCredential
|
||||
_, err = r.writer.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, w db.Writer) error {
|
||||
returnedCredential = c.clone()
|
||||
var err error
|
||||
rowsUpdated, err = w.Update(ctx, returnedCredential,
|
||||
dbMask, nullFields,
|
||||
db.WithOplog(oplogWrapper, returnedCredential.oplog(oplog.OpType_OP_TYPE_UPDATE)),
|
||||
db.WithVersion(&version))
|
||||
if err != nil {
|
||||
return errors.Wrap(ctx, err, op)
|
||||
}
|
||||
if rowsUpdated > 1 {
|
||||
return errors.New(ctx, errors.MultipleRecords, op, "more than 1 resource would have been updated")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, db.NoRowsAffected, err
|
||||
}
|
||||
|
||||
// Clear password fields, only PasswordHmac should be returned
|
||||
returnedCredential.CtPassword = nil
|
||||
returnedCredential.Password = nil
|
||||
|
||||
return returnedCredential, rowsUpdated, nil
|
||||
}
|
||||
|
||||
// UpdateSshPrivateKeyCredential updates the repository entry for c.PublicId
|
||||
// with the values in c for the fields listed in fieldMaskPaths. It returns a
|
||||
// new SshPrivateKeyCredential containing the updated values and a count of the
|
||||
@@ -865,6 +1084,11 @@ func (r *Repository) DeleteCredential(ctx context.Context, projectId, id string,
|
||||
c.PublicId = id
|
||||
input = c
|
||||
md = c.oplog(oplog.OpType_OP_TYPE_DELETE)
|
||||
case credential.UsernamePasswordDomainSubtype:
|
||||
c := allocUsernamePasswordDomainCredential()
|
||||
c.PublicId = id
|
||||
input = c
|
||||
md = c.oplog(oplog.OpType_OP_TYPE_DELETE)
|
||||
case credential.SshPrivateKeySubtype:
|
||||
c := allocSshPrivateKeyCredential()
|
||||
c.PublicId = id
|
||||
@@ -946,6 +1170,13 @@ func (r *Repository) ListDeletedCredentialIds(ctx context.Context, since time.Ti
|
||||
for _, cl := range deletedUsernamePasswordCredentials {
|
||||
credentialStoreIds = append(credentialStoreIds, cl.PublicId)
|
||||
}
|
||||
var deletedUsernamePasswordDomainCredentials []*deletedUsernamePasswordDomainCredential
|
||||
if err := r.SearchWhere(ctx, &deletedUsernamePasswordDomainCredentials, "delete_time >= ?", []any{since}); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to query deleted username password domain credentials"))
|
||||
}
|
||||
for _, cl := range deletedUsernamePasswordDomainCredentials {
|
||||
credentialStoreIds = append(credentialStoreIds, cl.PublicId)
|
||||
}
|
||||
var deletedSSHPrivateKeyCredentials []*deletedSSHPrivateKeyCredential
|
||||
if err := r.SearchWhere(ctx, &deletedSSHPrivateKeyCredentials, "delete_time >= ?", []any{since}); err != nil {
|
||||
return errors.Wrap(ctx, err, op, errors.WithMsg("failed to query deleted ssh private key credentials"))
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -25,6 +25,11 @@ func (r *Repository) Retrieve(ctx context.Context, projectId string, ids []strin
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
var updCreds []*UsernamePasswordDomainCredential
|
||||
err = r.reader.SearchWhere(ctx, &updCreds, "public_id in (?)", []any{ids})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
var spkCreds []*SshPrivateKeyCredential
|
||||
err = r.reader.SearchWhere(ctx, &spkCreds, "public_id in (?)", []any{ids})
|
||||
if err != nil {
|
||||
@@ -36,7 +41,7 @@ func (r *Repository) Retrieve(ctx context.Context, projectId string, ids []strin
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
if len(upCreds)+len(spkCreds)+len(jsonCreds) != len(ids) {
|
||||
if len(upCreds)+len(updCreds)+len(spkCreds)+len(jsonCreds) != len(ids) {
|
||||
return nil, errors.New(ctx, errors.NotSpecificIntegrity, op,
|
||||
fmt.Sprintf("mismatch between creds and number of ids requested, expected %d got %d", len(ids), len(upCreds)+len(spkCreds)+len(jsonCreds)))
|
||||
}
|
||||
@@ -55,6 +60,19 @@ func (r *Repository) Retrieve(ctx context.Context, projectId string, ids []strin
|
||||
out = append(out, c)
|
||||
}
|
||||
|
||||
for _, c := range updCreds {
|
||||
// decrypt credential
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op, errors.WithMsg("unable to get database wrapper"))
|
||||
}
|
||||
if err := c.decrypt(ctx, databaseWrapper); err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
|
||||
out = append(out, c)
|
||||
}
|
||||
|
||||
for _, c := range spkCreds {
|
||||
// decrypt credential
|
||||
databaseWrapper, err := r.kms.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
|
||||
@@ -37,6 +37,8 @@ func TestRepository_Retrieve(t *testing.T) {
|
||||
staticStore := TestCredentialStore(t, conn, wrapper, prj.GetPublicId())
|
||||
upCred1 := TestUsernamePasswordCredential(t, conn, wrapper, "user", "pass", staticStore.GetPublicId(), prj.GetPublicId())
|
||||
upCred2 := TestUsernamePasswordCredential(t, conn, wrapper, "different user", "better password", staticStore.GetPublicId(), prj.GetPublicId())
|
||||
updCred1 := TestUsernamePasswordDomainCredential(t, conn, wrapper, "user", "pass", "domain.com", staticStore.GetPublicId(), prj.GetPublicId())
|
||||
updCred2 := TestUsernamePasswordDomainCredential(t, conn, wrapper, "different user", "better password", "new-domain.com", staticStore.GetPublicId(), prj.GetPublicId())
|
||||
spkCred1 := TestSshPrivateKeyCredential(t, conn, wrapper, "final user", string(testdata.PEMBytes["ed25519"]), staticStore.GetPublicId(), prj.GetPublicId())
|
||||
spkCred2 := TestSshPrivateKeyCredential(t, conn, wrapper, "last user", string(testdata.PEMBytes["rsa-openssh-format"]), staticStore.GetPublicId(), prj.GetPublicId())
|
||||
spkCredWithPass := TestSshPrivateKeyCredential(t, conn, wrapper, "another last user",
|
||||
@@ -103,6 +105,26 @@ func TestRepository_Retrieve(t *testing.T) {
|
||||
upCred1, upCred2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-one-upd-cred",
|
||||
args: args{
|
||||
projectId: prj.GetPublicId(),
|
||||
credIds: []string{updCred1.GetPublicId()},
|
||||
},
|
||||
wantCreds: []credential.Static{
|
||||
updCred1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-multiple-upd-creds",
|
||||
args: args{
|
||||
projectId: prj.GetPublicId(),
|
||||
credIds: []string{updCred1.GetPublicId(), updCred2.GetPublicId()},
|
||||
},
|
||||
wantCreds: []credential.Static{
|
||||
updCred1, updCred2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid-ssh-pk-cred",
|
||||
args: args{
|
||||
@@ -147,10 +169,10 @@ func TestRepository_Retrieve(t *testing.T) {
|
||||
name: "valid-mixed-creds",
|
||||
args: args{
|
||||
projectId: prj.GetPublicId(),
|
||||
credIds: []string{upCred1.GetPublicId(), spkCred1.GetPublicId(), spkCredWithPass.GetPublicId(), spkCred2.GetPublicId(), upCred2.GetPublicId(), jsonCred1.GetPublicId(), jsonCred2.GetPublicId()},
|
||||
credIds: []string{upCred1.GetPublicId(), spkCred1.GetPublicId(), spkCredWithPass.GetPublicId(), spkCred2.GetPublicId(), upCred2.GetPublicId(), jsonCred1.GetPublicId(), jsonCred2.GetPublicId(), updCred1.GetPublicId(), updCred2.GetPublicId()},
|
||||
},
|
||||
wantCreds: []credential.Static{
|
||||
upCred1, spkCred1, spkCredWithPass, spkCred2, upCred2, jsonCred1, jsonCred2,
|
||||
upCred1, spkCred1, spkCredWithPass, spkCred2, upCred2, jsonCred1, jsonCred2, updCred1, updCred2,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -170,6 +192,7 @@ func TestRepository_Retrieve(t *testing.T) {
|
||||
gotCreds,
|
||||
cmpopts.IgnoreUnexported(
|
||||
UsernamePasswordCredential{}, store.UsernamePasswordCredential{},
|
||||
UsernamePasswordDomainCredential{}, store.UsernamePasswordDomainCredential{},
|
||||
SshPrivateKeyCredential{}, store.SshPrivateKeyCredential{},
|
||||
JsonCredential{}, store.JsonCredential{}),
|
||||
cmpopts.IgnoreTypes(×tamp.Timestamp{}),
|
||||
|
||||
@@ -284,7 +284,8 @@ func TestRewrap_credStaticUsernamePasswordDomainRewrapFn(t *testing.T) {
|
||||
|
||||
_, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
cs := TestCredentialStore(t, conn, wrapper, prj.PublicId)
|
||||
cred, err := NewUsernamePasswordDomainCredential(cs.GetPublicId(), "username", "domain", "password")
|
||||
|
||||
cred, err := NewUsernamePasswordDomainCredential(cs.GetPublicId(), "username", "password", "domain")
|
||||
assert.NoError(t, err)
|
||||
|
||||
cred.PublicId, err = credential.NewUsernamePasswordCredentialId(ctx)
|
||||
|
||||
@@ -213,6 +213,77 @@ func TestUsernamePasswordCredentials(
|
||||
return creds
|
||||
}
|
||||
|
||||
// TestUsernamePasswordDomainCredential creates a username password domain credential in the provided DB with
|
||||
// the provided project id and any values passed in through.
|
||||
// If any errors are encountered during the creation of the store, the test will fail.
|
||||
func TestUsernamePasswordDomainCredential(
|
||||
t testing.TB,
|
||||
conn *db.DB,
|
||||
wrapper wrapping.Wrapper,
|
||||
username, password, domain, storeId, projectId string,
|
||||
opts ...Option,
|
||||
) *UsernamePasswordDomainCredential {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
w := db.New(conn)
|
||||
|
||||
opt := getOpts(opts...)
|
||||
|
||||
databaseWrapper, err := kmsCache.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, databaseWrapper)
|
||||
|
||||
cred, err := NewUsernamePasswordDomainCredential(storeId, username, credential.Password(password), domain, opts...)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cred)
|
||||
|
||||
id := opt.withPublicId
|
||||
if id == "" {
|
||||
id, err = credential.NewUsernamePasswordDomainCredentialId(ctx)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
cred.PublicId = id
|
||||
|
||||
err = cred.encrypt(ctx, databaseWrapper)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err2 := w.DoTx(ctx, db.StdRetryCnt, db.ExpBackoff{},
|
||||
func(_ db.Reader, iw db.Writer) error {
|
||||
require.NoError(t, iw.Create(ctx, cred))
|
||||
return nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err2)
|
||||
|
||||
return cred
|
||||
}
|
||||
|
||||
// TestUsernamePasswordDomainCredentials creates count number of username password domain credentials in
|
||||
// the provided DB with the provided project id. If any errors are
|
||||
// encountered during the creation of the credentials, the test will fail.
|
||||
func TestUsernamePasswordDomainCredentials(
|
||||
t testing.TB,
|
||||
conn *db.DB,
|
||||
wrapper wrapping.Wrapper,
|
||||
username, password, domain, storeId, projectId string,
|
||||
count int,
|
||||
) []*UsernamePasswordDomainCredential {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
databaseWrapper, err := kmsCache.GetWrapper(ctx, projectId, kms.KeyPurposeDatabase)
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, databaseWrapper)
|
||||
|
||||
creds := make([]*UsernamePasswordDomainCredential, 0, count)
|
||||
for i := 0; i < count; i++ {
|
||||
creds = append(creds, TestUsernamePasswordDomainCredential(t, conn, wrapper, username, password, domain, storeId, projectId))
|
||||
}
|
||||
return creds
|
||||
}
|
||||
|
||||
// TestSshPrivateKeyCredential creates an ssh private key credential in the
|
||||
// provided DB with the provided project and any values passed in through. If any
|
||||
// errors are encountered during the creation of the store, the test will fail.
|
||||
|
||||
@@ -33,8 +33,9 @@ type UsernamePasswordDomainCredential struct {
|
||||
func NewUsernamePasswordDomainCredential(
|
||||
storeId string,
|
||||
username string,
|
||||
domain string,
|
||||
password credential.Password,
|
||||
domain string,
|
||||
|
||||
opt ...Option,
|
||||
) (*UsernamePasswordDomainCredential, error) {
|
||||
opts := getOpts(opt...)
|
||||
|
||||
@@ -173,7 +173,8 @@ func TestUsernamePasswordDomainCredential_New(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
ctx := context.Background()
|
||||
got, err := NewUsernamePasswordDomainCredential(tt.args.storeId, tt.args.username, tt.args.domain, tt.args.password, tt.args.options...)
|
||||
|
||||
got, err := NewUsernamePasswordDomainCredential(tt.args.storeId, tt.args.username, tt.args.password, tt.args.domain, tt.args.options...)
|
||||
require.NoError(err)
|
||||
require.NotNil(got)
|
||||
assert.Emptyf(got.PublicId, "PublicId set")
|
||||
|
||||
Reference in new issue
Block a user