Skip to content

Commit

Permalink
backport of commit 0e8bcc1
Browse files Browse the repository at this point in the history
  • Loading branch information
akshya96 committed Jun 3, 2022
1 parent 1e8004d commit 4c967ad
Show file tree
Hide file tree
Showing 69 changed files with 1,363 additions and 4,137 deletions.
8 changes: 1 addition & 7 deletions .release/ci.hcl
Expand Up @@ -8,13 +8,7 @@ project "vault" {
github {
organization = "hashicorp"
repository = "vault"
release_branches = [
"main",
"release/1.8.x",
"release/1.9.x",
"release/1.10.x",
"release/1.11.x",
]
release_branches = ["release/1.11.x"]
}
}

Expand Down
168 changes: 8 additions & 160 deletions api/kv_v2.go
Expand Up @@ -38,39 +38,18 @@ type KVVersionMetadata struct {
Destroyed bool `mapstructure:"destroyed"`
}

// Currently supported options: WithOption, WithCheckAndSet, WithMethod
// Currently supported options: WithCheckAndSet
type KVOption func() (key string, value interface{})

const (
KVOptionCheckAndSet = "cas"
KVOptionMethod = "method"
KVMergeMethodPatch = "patch"
KVMergeMethodReadWrite = "rw"
)

// WithOption can optionally be passed to provide generic options for a
// KV request. Valid keys and values depend on the type of request.
func WithOption(key string, value interface{}) KVOption {
return func() (string, interface{}) {
return key, value
}
}

// WithCheckAndSet can optionally be passed to perform a check-and-set
// operation on a KV request. If not set, the write will be allowed.
// If cas is set to 0, a write will only be allowed if the key doesn't exist.
// If set to non-zero, the write will only be allowed if the key’s current
// version matches the version specified in the cas parameter.
// operation. If not set, the write will be allowed. If cas is set to 0, a
// write will only be allowed if the key doesn't exist. If set to non-zero,
// the write will only be allowed if the key’s current version matches the
// version specified in the cas parameter.
func WithCheckAndSet(cas int) KVOption {
return WithOption(KVOptionCheckAndSet, cas)
}

// WithMergeMethod can optionally be passed to dictate which type of
// patch to perform in a Patch request. If set to "patch", then an HTTP PATCH
// request will be issued. If set to "rw", then a read will be performed,
// then a local update, followed by a remote update. Defaults to "patch".
func WithMergeMethod(method string) KVOption {
return WithOption(KVOptionMethod, method)
return func() (string, interface{}) {
return "cas", cas
}
}

// Get returns the latest version of a secret from the KV v2 secrets engine.
Expand Down Expand Up @@ -238,53 +217,6 @@ func (kv *kvv2) Put(ctx context.Context, secretPath string, data map[string]inte
return kvSecret, nil
}

// Patch additively updates the most recent version of a key-value secret,
// differentiating it from Put which will fully overwrite the previous data.
// Only the key-value pairs that are new or changing need to be provided.
//
// The WithMethod KVOption function can optionally be passed to dictate which
// kind of patch to perform, as older Vault server versions (pre-1.9.0) may
// only be able to use the old "rw" (read-then-write) style of partial update,
// whereas newer Vault servers can use the default value of "patch" if the
// client token's policy has the "patch" capability.
func (kv *kvv2) Patch(ctx context.Context, secretPath string, newData map[string]interface{}, opts ...KVOption) (*KVSecret, error) {
// determine patch method
var patchMethod string
var ok bool
for _, opt := range opts {
k, v := opt()
if k == "method" {
patchMethod, ok = v.(string)
if !ok {
return nil, fmt.Errorf("unsupported type provided for option value; value for patch method should be string \"rw\" or \"patch\"")
}
}
}

// Determine which kind of patch to use,
// the newer HTTP Patch style or the older read-then-write style
var kvs *KVSecret
var perr error
switch patchMethod {
case "rw":
kvs, perr = readThenWrite(ctx, kv.c, kv.mountPath, secretPath, newData)
case "patch":
kvs, perr = mergePatch(ctx, kv.c, kv.mountPath, secretPath, newData, opts...)
case "":
kvs, perr = mergePatch(ctx, kv.c, kv.mountPath, secretPath, newData, opts...)
default:
return nil, fmt.Errorf("unsupported patch method provided; value for patch method should be string \"rw\" or \"patch\"")
}
if perr != nil {
return nil, fmt.Errorf("unable to perform patch: %w", perr)
}
if kvs == nil {
return nil, fmt.Errorf("no secret was written to %s", secretPath)
}

return kvs, nil
}

// Delete deletes the most recent version of a secret from the KV v2
// secrets engine. To delete an older version, use DeleteVersions.
func (kv *kvv2) Delete(ctx context.Context, secretPath string) error {
Expand Down Expand Up @@ -467,87 +399,3 @@ func extractFullMetadata(secret *Secret) (*KVMetadata, error) {

return metadata, nil
}

func mergePatch(ctx context.Context, client *Client, mountPath string, secretPath string, newData map[string]interface{}, opts ...KVOption) (*KVSecret, error) {
pathToMergePatch := fmt.Sprintf("%s/data/%s", mountPath, secretPath)

// take any other additional options provided
// and pass them along to the patch request
wrappedData := map[string]interface{}{
"data": newData,
}
options := make(map[string]interface{})
for _, opt := range opts {
k, v := opt()
options[k] = v
}
if len(opts) > 0 {
wrappedData["options"] = options
}

secret, err := client.Logical().JSONMergePatch(ctx, pathToMergePatch, wrappedData)
if err != nil {
// If it's a 405, that probably means the server is running a pre-1.9
// Vault version that doesn't support the HTTP PATCH method.
// Fall back to the old way of doing it.
if re, ok := err.(*ResponseError); ok && re.StatusCode == 405 {
return readThenWrite(ctx, client, mountPath, secretPath, newData)
}

if re, ok := err.(*ResponseError); ok && re.StatusCode == 403 {
return nil, fmt.Errorf("received 403 from Vault server; please ensure that token's policy has \"patch\" capability: %w", err)
}

return nil, fmt.Errorf("error performing merge patch to %s: %s", pathToMergePatch, err)
}

metadata, err := extractVersionMetadata(secret)
if err != nil {
return nil, fmt.Errorf("secret was written successfully, but unable to view version metadata from response: %w", err)
}

kvSecret := &KVSecret{
Data: nil, // secret.Data in this case is the metadata
VersionMetadata: metadata,
Raw: secret,
}

cm, err := extractCustomMetadata(secret)
if err != nil {
return nil, fmt.Errorf("error reading custom metadata for secret %s: %w", secretPath, err)
}
kvSecret.CustomMetadata = cm

return kvSecret, nil
}

func readThenWrite(ctx context.Context, client *Client, mountPath string, secretPath string, newData map[string]interface{}) (*KVSecret, error) {
// First, read the secret.
existingVersion, err := client.KVv2(mountPath).Get(ctx, secretPath)
if err != nil {
return nil, fmt.Errorf("error reading secret as part of read-then-write patch operation: %w", err)
}

// Make sure the secret already exists
if existingVersion == nil || existingVersion.Data == nil {
return nil, fmt.Errorf("no existing secret was found at %s when doing read-then-write patch operation: %w", secretPath, err)
}

// Verify existing secret has metadata
if existingVersion.VersionMetadata == nil {
return nil, fmt.Errorf("no metadata found at %s; patch can only be used on existing data", secretPath)
}

// Copy new data over with existing data
combinedData := existingVersion.Data
for k, v := range newData {
combinedData[k] = v
}

updatedSecret, err := client.KVv2(mountPath).Put(ctx, secretPath, combinedData, WithCheckAndSet(existingVersion.VersionMetadata.Version))
if err != nil {
return nil, fmt.Errorf("error writing secret to %s: %w", secretPath, err)
}

return updatedSecret, nil
}
6 changes: 0 additions & 6 deletions changelog/15552.txt

This file was deleted.

3 changes: 0 additions & 3 deletions changelog/15583.txt

This file was deleted.

3 changes: 0 additions & 3 deletions changelog/15638.txt

This file was deleted.

3 changes: 0 additions & 3 deletions changelog/15681.txt

This file was deleted.

2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -120,7 +120,7 @@ require (
github.com/hashicorp/vault/api v1.6.0
github.com/hashicorp/vault/api/auth/approle v0.1.0
github.com/hashicorp/vault/api/auth/userpass v0.1.0
github.com/hashicorp/vault/sdk v0.5.0
github.com/hashicorp/vault/sdk v0.5.1-0.20220525201813-b7078c543d70
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab
github.com/jackc/pgx/v4 v4.15.0
github.com/jcmturner/gokrb5/v8 v8.4.2
Expand Down
4 changes: 2 additions & 2 deletions sdk/version/version_base.go
Expand Up @@ -11,7 +11,7 @@ var (
// Whether cgo is enabled or not; set at build time
CgoEnabled bool

Version = "1.12.0"
VersionPrerelease = "dev1"
Version = "1.11.0"
VersionPrerelease = "rc1"
VersionMetadata = ""
)
25 changes: 9 additions & 16 deletions ui/app/components/alert-popup.js
@@ -1,4 +1,4 @@
import Component from '@glimmer/component';
import OuterHTML from './outer-html';

/**
* @module AlertPopup
Expand All @@ -8,21 +8,14 @@ import Component from '@glimmer/component';
* // All properties are passed in from the flashMessage service.
* <AlertPopup @type={{message-types flash.type}} @message={{flash.message}} @close={{close}}/>```
*
* @param {string} type=null - The alert type. This comes from the message-types helper.
* @param {string} [message=null] - The alert message.
* @param {function} close=null - The close action which will close the alert.
* @param {boolean} isPreformatted - if true modifies class.
* @param type=null {String} - The alert type. This comes from the message-types helper.
* @param [message=null] {String} - The alert message.
* @param close=null {Func} - The close action which will close the alert.
*
*/

export default class AlertPopup extends Component {
get type() {
return this.args.type || null;
}
get message() {
return this.args.message || null;
}
get close() {
return this.args.close || null;
}
}
export default OuterHTML.extend({
type: null,
message: null,
close: null,
});
1 change: 0 additions & 1 deletion ui/app/components/auth-jwt.js
@@ -1,6 +1,5 @@
import Ember from 'ember';
import { inject as service } from '@ember/service';
// ARG NOTE: Once you remove outer-html after glimmerizing you can remove the outer-html component
import Component from './outer-html';
import { later } from '@ember/runloop';
import { task, timeout, waitForEvent } from 'ember-concurrency';
Expand Down
2 changes: 2 additions & 0 deletions ui/app/components/block-error.js
@@ -0,0 +1,2 @@
import OuterHTML from './outer-html';
export default OuterHTML.extend();
11 changes: 3 additions & 8 deletions ui/app/components/clients/line-chart.js
Expand Up @@ -82,13 +82,8 @@ export default class LineChart extends Component {
const xAxis = axisBottom(xScale).tickSize(0);
yAxis(chartSvg.append('g').attr('data-test-line-chart', 'y-axis-labels'));
xAxis(
chartSvg
.append('g')
.attr('transform', `translate(0, ${SVG_DIMENSIONS.height + 10})`)
.attr('data-test-line-chart', 'x-axis-labels')
);
yAxis(chartSvg.append('g'));
xAxis(chartSvg.append('g').attr('transform', `translate(0, ${SVG_DIMENSIONS.height + 10})`));
chartSvg.selectAll('.domain').remove();
Expand Down Expand Up @@ -130,7 +125,7 @@ export default class LineChart extends Component {
.data(filteredData)
.enter()
.append('circle')
.attr('data-test-line-chart', 'plot-point')
.attr('class', 'data-plot')
.attr('cy', (d) => `${100 - yScale(d[this.yKey])}%`)
.attr('cx', (d) => xScale(d[this.xKey]))
.attr('r', 3.5)
Expand Down
1 change: 1 addition & 0 deletions ui/app/components/logo-splash.js
@@ -0,0 +1 @@
export { default } from './outer-html';
37 changes: 15 additions & 22 deletions ui/app/components/pki-cert-popup.js
@@ -1,24 +1,17 @@
import Component from '@glimmer/component';
import { action } from '@ember/object';
import Component from '@ember/component';

/**
* @module PkiCertPopup
* PkiCertPopup component is the hotdog menu button that allows you to see details or revoke a certificate.
*
* @example
* ```js
* <PkiCertPopup @item={{@item}}/>
* ```
* @param {class} item - the PKI cert in question.
*/
export default Component.extend({
/*
* @public
* @param DS.Model
*
* the pki-certificate model
*/
item: null,

export default class PkiCertPopup extends Component {
get item() {
return this.args.item || null;
}

@action
delete(item) {
item.save({ adapterOptions: { method: 'revoke' } });
}
}
actions: {
delete(item) {
item.save({ adapterOptions: { method: 'revoke' } });
},
},
});

0 comments on commit 4c967ad

Please sign in to comment.