Skip to content

Commit

Permalink
VAULT-20669: Add New Authenticated Endpoint for Version (#23740)
Browse files Browse the repository at this point in the history
* add sys/internal/ui/version path

* add read capability for sys/internal/ui/version in default policy

* add changelog file

* doc: add api-docs page for sys/internal/ui/version

* add godoc for pathInternalUIVersion function

* add tests for functions in version package

* remove unreachable code

* use closure to restore version at end of test function

* use an example version in sample response

* Update website/content/api-docs/system/internal-ui-version.mdx

Co-authored-by: Sarah Chavis <[email protected]>

* Apply suggestions from code review

Co-authored-by: Sarah Chavis <[email protected]>

* Update website/content/api-docs/system/internal-ui-version.mdx

Co-authored-by: Sarah Chavis <[email protected]>

* add copyright header to version_test.go

---------

Co-authored-by: Sarah Chavis <[email protected]>
  • Loading branch information
Marc Boudreau and schavis authored Oct 26, 2023
1 parent 41cc3b3 commit 550c99a
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 3 deletions.
3 changes: 3 additions & 0 deletions changelog/23740.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
core: add sys/internal/ui/version endpoint
```
12 changes: 12 additions & 0 deletions vault/logical_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -4754,6 +4754,18 @@ func (b *SystemBackend) pathInternalUIResultantACL(ctx context.Context, req *log
return resp, nil
}

// pathInternalUIVersion is the framework.PathOperation callback function for
// the sys/internal/ui/version path. It simply returns the Vault version.
func (b *SystemBackend) pathInternalUIVersion(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
resp := &logical.Response{
Data: map[string]any{
"version": version.GetVersion().VersionNumber(),
},
}

return resp, nil
}

func (b *SystemBackend) pathInternalOpenAPI(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
// Limit output to authorized paths
resp, err := b.pathInternalUIMountsRead(ctx, req, d)
Expand Down
5 changes: 5 additions & 0 deletions vault/logical_system_integ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ func TestSystemBackend_InternalUIResultantACL(t *testing.T) {
"read",
},
},
"sys/internal/ui/version": map[string]interface{}{
"capabilities": []interface{}{
"read",
},
},
"sys/leases/lookup": map[string]interface{}{
"capabilities": []interface{}{
"update",
Expand Down
25 changes: 25 additions & 0 deletions vault/logical_system_paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -2641,6 +2641,31 @@ func (b *SystemBackend) internalPaths() []*framework.Path {
HelpSynopsis: strings.TrimSpace(sysHelp["internal-ui-resultant-acl"][0]),
HelpDescription: strings.TrimSpace(sysHelp["internal-ui-resultant-acl"][1]),
},
{
Pattern: "internal/ui/version",
DisplayAttrs: &framework.DisplayAttributes{
OperationPrefix: "internal-ui",
OperationVerb: "read",
OperationSuffix: "version",
},
Operations: map[logical.Operation]framework.OperationHandler{
logical.ReadOperation: &framework.PathOperation{
Callback: b.pathInternalUIVersion,
Summary: "Backwards compatibility is not guaranteed for this API",
Responses: map[int][]framework.Response{
http.StatusOK: {{
Description: "OK",
Fields: map[string]*framework.FieldSchema{
"version": {
Type: framework.TypeString,
Required: true,
},
},
}},
},
},
},
},
{
Pattern: "internal/counters/requests",
DisplayAttrs: &framework.DisplayAttributes{
Expand Down
6 changes: 6 additions & 0 deletions vault/policy_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ path "sys/internal/ui/resultant-acl" {
capabilities = ["read"]
}
# Allow a token to look up the Vault version. This path is not subject to
# redaction like the unauthenticated endpoints that provide the Vault version.
path "sys/internal/ui/version" {
capabilities = ["read"]
}
# Allow a token to renew a lease via lease_id in the request body; old path for
# old clients, new path for newer
path "sys/renew" {
Expand Down
3 changes: 0 additions & 3 deletions version/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ func GetVersion() *VersionInfo {
if GitDescribe != "" {
ver = GitDescribe
}
if GitDescribe == "" && rel == "" && VersionPrerelease != "" {
rel = "dev"
}

return &VersionInfo{
Revision: GitCommit,
Expand Down
111 changes: 111 additions & 0 deletions version/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package version

import (
"testing"

"github.com/stretchr/testify/assert"
)

func replaceVersion(v, vp string) func() {
origV := Version
origVP := VersionPrerelease

Version = v
VersionPrerelease = vp

return func() {
Version = origV
VersionPrerelease = origVP
}
}

func TestGetVersion(t *testing.T) {
// This test cannot be parallelized because it messes with some global
// variables that determine the version information.
restoreVersionFunc := replaceVersion("1.2.3", "")
defer restoreVersionFunc()

// Test the general case
vi := GetVersion()
assert.Equal(t, "1.2.3", vi.Version)
assert.Equal(t, "", vi.VersionPrerelease)
assert.Equal(t, "", vi.VersionMetadata)
assert.Equal(t, "", vi.Revision)
assert.Equal(t, "", vi.BuildDate)

// Test the git describe case
origGitDescribe := GitDescribe
GitDescribe = "git-describe"
vi = GetVersion()
assert.Equal(t, "git-describe", vi.Version)

GitDescribe = origGitDescribe
}

func TestVersionNumber(t *testing.T) {
// This test cannot be parallelized because it messes with some global
// variables that determine the version information.
restoreVersionFunc := replaceVersion("unknown", "unknown")
defer restoreVersionFunc()

// Test the unknown version case
vi := GetVersion()
assert.Equal(t, "(version unknown)", vi.VersionNumber())

replaceVersion("1.2.3", "")

// Test the pre-release case
vi = GetVersion()
vi.VersionPrerelease = "rc1"
assert.Equal(t, "1.2.3-rc1", vi.VersionNumber())

// Test the pre-release and metadata version case
vi.VersionMetadata = "ent"
assert.Equal(t, "1.2.3-rc1+ent", vi.VersionNumber())

// Test the metadata only version case
vi.VersionPrerelease = ""
assert.Equal(t, "1.2.3+ent", vi.VersionNumber())
}

func TestFullVersionNumber(t *testing.T) {
// This test cannot be parallelized because it messes with some global
// variables that determine the version information.
restoreVersionFunc := replaceVersion("unknown", "unknown")
defer restoreVersionFunc()

// Test the unknown version case
vi := GetVersion()
assert.Equal(t, "Vault (version unknown)", vi.FullVersionNumber(false))

// Test the no pre-release, metadata, revision, build date case
replaceVersion("1.2.3", "")
vi = GetVersion()
assert.Equal(t, "Vault v1.2.3", vi.FullVersionNumber(false))

// Test the pre-release case
vi.VersionPrerelease = "rc1"
assert.Equal(t, "Vault v1.2.3-rc1", vi.FullVersionNumber(false))

// Test the metadata case
vi.VersionPrerelease = ""
vi.VersionMetadata = "ent"
assert.Equal(t, "Vault v1.2.3+ent", vi.FullVersionNumber(false))

// Test the revision case
vi.VersionMetadata = ""
vi.Revision = "ab1234f"
assert.Equal(t, "Vault v1.2.3 (ab1234f)", vi.FullVersionNumber(true))

// Test the build date case
vi.BuildDate = "2023-10-20"
assert.Equal(t, "Vault v1.2.3, built 2023-10-20", vi.FullVersionNumber(false))

// Test the case where all of the things are set
vi.VersionPrerelease = "rc1"
vi.VersionMetadata = "ent"
assert.Equal(t, "Vault v1.2.3-rc1+ent (ab1234f), built 2023-10-20", vi.FullVersionNumber(true))
}
51 changes: 51 additions & 0 deletions website/content/api-docs/system/internal-ui-version.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
layout: api
page_title: /sys/internal/ui/version - HTTP API
description: >-
The `/sys/internal/ui/version` endpoint exposes the software version of Vault.
---

# `/sys/internal/ui/version`

The `/sys/internal/ui/version` endpoint exposes the Vault software version
so the Vault UI can display the information to logged in users.

Vault uses internal endpoints to provide information to the Vault UI
and/or Vault CLI. Internal endpoints are explicitly intended to support
Vault functionality, so we do not recommend them for general use
and do not guarantee backwards compatibility across versions.

## Get version

Return the current software version of Vault.

| Method | Path |
| :----- | :------------------------- |
| `GET` | `/sys/internal/ui/version` |

### Sample request

```shell-session
$ curl \
--header "X-Vault-Token: ..." \
--request GET \
http://127.0.0.1:8200/v1/sys/internal/ui/version
```

### Sample response

```json
{
"request_id": "d585b9be-9c6f-a05f-939b-490cf062ebbe",
"lease_id": "",
"renewable": false,
"lease_duration": 0,
"data": {
"version": "1.16.0"
},
"wrap_info": null,
"warnings": null,
"auth": null,
"mount_type": "system"
}
```
4 changes: 4 additions & 0 deletions website/data/api-docs-nav-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,10 @@
"title": "<code>/sys/internal/ui/resultant-acl</code>",
"path": "system/internal-ui-resultant-acl"
},
{
"title": "<code>/sys/internal/ui/version</code>",
"path": "system/internal-ui-version"
},
{
"title": "<code>/sys/key-status</code>",
"path": "system/key-status"
Expand Down

0 comments on commit 550c99a

Please sign in to comment.