2018-05-25 20:33:22 +00:00
import { module , test } from 'qunit' ;
import {
parseCommand ,
extractDataAndFlags ,
logFromResponse ,
logFromError ,
logErrorFromInput ,
} from 'vault/lib/console-helpers' ;
2021-12-17 03:44:29 +00:00
module ( 'Unit | Lib | console helpers' , function ( ) {
2018-09-25 16:28:26 +00:00
const testCommands = [
{
name : 'write with data' ,
command : ` vault write aws/config/root \
access _key = AKIAJWVN5Z4FOFT7NLNA \
secret _key = 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 \
region = us - east - 1 ` ,
expected : [
'write' ,
[ ] ,
'aws/config/root' ,
[
'access_key=AKIAJWVN5Z4FOFT7NLNA' ,
'secret_key=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' ,
'region=us-east-1' ,
] ,
2018-05-25 20:33:22 +00:00
] ,
2018-09-25 16:28:26 +00:00
} ,
2019-05-22 21:07:42 +00:00
{
name : 'write with space in a value' ,
command : ` vault write \
auth / ldap / config \
url = ldap : //ldap.example.com:3268 \
binddn = "CN=ServiceViewDev,OU=Service Accounts,DC=example,DC=com" \
bindpass = "xxxxxxxxxxxxxxxxxxxxxxxxxx" \
userdn = "DC=example,DC=com" \
groupdn = "DC=example,DC=com" \
insecure _tls = true \
starttls = false
` ,
expected : [
'write' ,
[ ] ,
'auth/ldap/config' ,
[
'url=ldap://ldap.example.com:3268' ,
'binddn=CN=ServiceViewDev,OU=Service Accounts,DC=example,DC=com' ,
'bindpass=xxxxxxxxxxxxxxxxxxxxxxxxxx' ,
'userdn=DC=example,DC=com' ,
'groupdn=DC=example,DC=com' ,
'insecure_tls=true' ,
'starttls=false' ,
] ,
] ,
} ,
{
name : 'write with double quotes' ,
command : ` vault write \
auth / token / create \
policies = "foo"
` ,
expected : [ 'write' , [ ] , 'auth/token/create' , [ 'policies=foo' ] ] ,
} ,
{
name : 'write with single quotes' ,
command : ` vault write \
auth / token / create \
policies = 'foo'
` ,
expected : [ 'write' , [ ] , 'auth/token/create' , [ 'policies=foo' ] ] ,
} ,
{
name : 'write with unmatched quotes' ,
command : ` vault write \
auth / token / create \
2019-08-01 22:12:04 +00:00
policies = "'foo"
2019-05-22 21:07:42 +00:00
` ,
expected : [ 'write' , [ ] , 'auth/token/create' , [ "policies='foo" ] ] ,
} ,
2019-08-01 22:12:04 +00:00
{
name : 'write with shell characters' ,
/* eslint-disable no-useless-escape */
command : ` vault write database/roles/api-prod db_name=apiprod creation_statements="CREATE ROLE \" {{name}} \" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \" {{name}} \" ;" default_ttl=1h max_ttl=24h
` ,
expected : [
'write' ,
[ ] ,
'database/roles/api-prod' ,
[
'db_name=apiprod' ,
` creation_statements=CREATE ROLE {{name}} WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO {{name}}; ` ,
'default_ttl=1h' ,
'max_ttl=24h' ,
] ,
] ,
} ,
2018-09-25 16:28:26 +00:00
{
name : 'read with field' ,
command : ` vault read -field=access_key aws/creds/my-role ` ,
expected : [ 'read' , [ '-field=access_key' ] , 'aws/creds/my-role' , [ ] ] ,
} ,
] ;
2018-05-25 20:33:22 +00:00
2021-12-17 03:44:29 +00:00
testCommands . forEach ( function ( testCase ) {
test ( ` #parseCommand: ${ testCase . name } ` , function ( assert ) {
2018-09-25 16:28:26 +00:00
let result = parseCommand ( testCase . command ) ;
assert . deepEqual ( result , testCase . expected ) ;
} ) ;
2018-05-25 20:33:22 +00:00
} ) ;
2021-12-17 03:44:29 +00:00
test ( '#parseCommand: invalid commands' , function ( assert ) {
2018-09-25 16:28:26 +00:00
let command = 'vault kv get foo' ;
let result = parseCommand ( command ) ;
assert . equal ( result , false , 'parseCommand returns false by default' ) ;
2018-05-25 20:33:22 +00:00
2018-09-25 16:28:26 +00:00
assert . throws (
( ) => {
parseCommand ( command , true ) ;
} ,
/invalid command/ ,
'throws on invalid command when `shouldThrow` is true'
) ;
} ) ;
2018-05-25 20:33:22 +00:00
2018-09-25 16:28:26 +00:00
const testExtractCases = [
{
name : 'data fields' ,
input : [
[
'access_key=AKIAJWVN5Z4FOFT7NLNA' ,
'secret_key=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' ,
'region=us-east-1' ,
] ,
[ ] ,
2018-05-25 20:33:22 +00:00
] ,
2018-09-25 16:28:26 +00:00
expected : {
data : {
access _key : 'AKIAJWVN5Z4FOFT7NLNA' ,
secret _key : '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' ,
region : 'us-east-1' ,
} ,
flags : { } ,
2018-05-25 20:33:22 +00:00
} ,
} ,
2018-09-25 16:28:26 +00:00
{
name : 'repeated data and a flag' ,
input : [ [ 'allowed_domains=example.com' , 'allowed_domains=foo.example.com' ] , [ '-wrap-ttl=2h' ] ] ,
expected : {
data : {
allowed _domains : [ 'example.com' , 'foo.example.com' ] ,
} ,
flags : {
wrapTTL : '2h' ,
} ,
2018-05-25 20:33:22 +00:00
} ,
} ,
2018-09-25 16:28:26 +00:00
{
name : 'data with more than one equals sign' ,
input : [ [ 'foo=bar=baz' , 'foo=baz=bop' , 'some=value=val' ] , [ ] ] ,
expected : {
data : {
foo : [ 'bar=baz' , 'baz=bop' ] ,
some : 'value=val' ,
} ,
flags : { } ,
2018-05-25 20:33:22 +00:00
} ,
} ,
2020-02-06 18:37:38 +00:00
{
name : 'data with empty values' ,
input : [ [ ` foo= ` , 'some=thing' ] , [ ] ] ,
expected : {
data : {
foo : '' ,
some : 'thing' ,
} ,
flags : { } ,
} ,
} ,
2018-09-25 16:28:26 +00:00
] ;
2018-05-25 20:33:22 +00:00
2021-12-17 03:44:29 +00:00
testExtractCases . forEach ( function ( testCase ) {
test ( ` #extractDataAndFlags: ${ testCase . name } ` , function ( assert ) {
2018-09-25 16:28:26 +00:00
let { data , flags } = extractDataAndFlags ( ... testCase . input ) ;
assert . deepEqual ( data , testCase . expected . data , 'has expected data' ) ;
assert . deepEqual ( flags , testCase . expected . flags , 'has expected flags' ) ;
} ) ;
2018-05-25 20:33:22 +00:00
} ) ;
2018-09-25 16:28:26 +00:00
let testResponseCases = [
{
name : 'write response, no content' ,
args : [ null , 'foo/bar' , 'write' , { } ] ,
expectedData : {
type : 'success' ,
content : 'Success! Data written to: foo/bar' ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'delete response, no content' ,
args : [ null , 'foo/bar' , 'delete' , { } ] ,
expectedData : {
type : 'success' ,
content : 'Success! Data deleted (if it existed) at: foo/bar' ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2019-01-18 20:02:11 +00:00
{
name : 'read, no data, auth, wrap_info' ,
args : [ { foo : 'bar' , one : 'two' } , 'foo/bar' , 'read' , { } ] ,
expectedData : {
type : 'object' ,
content : { foo : 'bar' , one : 'two' } ,
} ,
} ,
{
name : 'read with -format=json flag, no data, auth, wrap_info' ,
args : [ { foo : 'bar' , one : 'two' } , 'foo/bar' , 'read' , { format : 'json' } ] ,
expectedData : {
type : 'json' ,
content : { foo : 'bar' , one : 'two' } ,
} ,
} ,
{
name : 'read with -field flag, no data, auth, wrap_info' ,
args : [ { foo : 'bar' , one : 'two' } , 'foo/bar' , 'read' , { field : 'one' } ] ,
expectedData : {
type : 'text' ,
content : 'two' ,
} ,
} ,
2018-09-25 16:28:26 +00:00
{
name : 'write, with content' ,
args : [ { data : { one : 'two' } } , 'foo/bar' , 'write' , { } ] ,
expectedData : {
type : 'object' ,
content : { one : 'two' } ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with wrap-ttl flag' ,
args : [ { wrap _info : { one : 'two' } } , 'foo/bar' , 'read' , { wrapTTL : '1h' } ] ,
expectedData : {
type : 'object' ,
content : { one : 'two' } ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with -format=json flag and wrap-ttl flag' ,
args : [ { foo : 'bar' , wrap _info : { one : 'two' } } , 'foo/bar' , 'read' , { format : 'json' , wrapTTL : '1h' } ] ,
expectedData : {
type : 'json' ,
content : { foo : 'bar' , wrap _info : { one : 'two' } } ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with -format=json and -field flags' ,
args : [ { foo : 'bar' , data : { one : 'two' } } , 'foo/bar' , 'read' , { format : 'json' , field : 'one' } ] ,
expectedData : {
type : 'json' ,
content : 'two' ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with -format=json and -field, and -wrap-ttl flags' ,
args : [
{ foo : 'bar' , wrap _info : { one : 'two' } } ,
'foo/bar' ,
'read' ,
{ format : 'json' , wrapTTL : '1h' , field : 'one' } ,
] ,
expectedData : {
type : 'json' ,
content : 'two' ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with string field flag and wrap-ttl flag' ,
args : [ { foo : 'bar' , wrap _info : { one : 'two' } } , 'foo/bar' , 'read' , { field : 'one' , wrapTTL : '1h' } ] ,
expectedData : {
type : 'text' ,
content : 'two' ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with object field flag and wrap-ttl flag' ,
args : [
{ foo : 'bar' , wrap _info : { one : { two : 'three' } } } ,
'foo/bar' ,
'read' ,
{ field : 'one' , wrapTTL : '1h' } ,
] ,
expectedData : {
type : 'object' ,
content : { two : 'three' } ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with response data and string field flag' ,
args : [ { foo : 'bar' , data : { one : 'two' } } , 'foo/bar' , 'read' , { field : 'one' , wrapTTL : '1h' } ] ,
expectedData : {
type : 'text' ,
content : 'two' ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with response data and object field flag ' ,
args : [
{ foo : 'bar' , data : { one : { two : 'three' } } } ,
'foo/bar' ,
'read' ,
{ field : 'one' , wrapTTL : '1h' } ,
] ,
expectedData : {
type : 'object' ,
content : { two : 'three' } ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'response with data' ,
args : [ { foo : 'bar' , data : { one : 'two' } } , 'foo/bar' , 'read' , { } ] ,
expectedData : {
type : 'object' ,
content : { one : 'two' } ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with response data, field flag, and field missing' ,
args : [ { foo : 'bar' , data : { one : 'two' } } , 'foo/bar' , 'read' , { field : 'foo' } ] ,
expectedData : {
type : 'error' ,
content : 'Field "foo" not present in secret' ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with response data and auth block' ,
args : [ { data : { one : 'two' } , auth : { three : 'four' } } , 'auth/token/create' , 'write' , { } ] ,
expectedData : {
type : 'object' ,
content : { three : 'four' } ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with -field and -format with an object field' ,
args : [ { data : { one : { three : 'two' } } } , 'sys/mounts' , 'read' , { field : 'one' , format : 'json' } ] ,
expectedData : {
type : 'json' ,
content : { three : 'two' } ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
{
name : 'with -field and -format with a string field' ,
args : [ { data : { one : 'two' } } , 'sys/mounts' , 'read' , { field : 'one' , format : 'json' } ] ,
expectedData : {
type : 'json' ,
content : 'two' ,
} ,
2018-05-25 20:33:22 +00:00
} ,
2018-09-25 16:28:26 +00:00
] ;
2018-05-25 20:33:22 +00:00
2021-12-17 03:44:29 +00:00
testResponseCases . forEach ( function ( testCase ) {
test ( ` #logFromResponse: ${ testCase . name } ` , function ( assert ) {
2018-09-25 16:28:26 +00:00
let data = logFromResponse ( ... testCase . args ) ;
assert . deepEqual ( data , testCase . expectedData ) ;
} ) ;
2018-05-25 20:33:22 +00:00
} ) ;
2018-09-25 16:28:26 +00:00
let testErrorCases = [
{
name : 'AdapterError write' ,
args : [ { httpStatus : 404 , path : 'v1/sys/foo' , errors : [ { } ] } , 'sys/foo' , 'write' ] ,
expectedContent : 'Error writing to: sys/foo.\nURL: v1/sys/foo\nCode: 404' ,
} ,
{
name : 'AdapterError read' ,
args : [ { httpStatus : 404 , path : 'v1/sys/foo' , errors : [ { } ] } , 'sys/foo' , 'read' ] ,
expectedContent : 'Error reading from: sys/foo.\nURL: v1/sys/foo\nCode: 404' ,
} ,
{
name : 'AdapterError list' ,
args : [ { httpStatus : 404 , path : 'v1/sys/foo' , errors : [ { } ] } , 'sys/foo' , 'list' ] ,
expectedContent : 'Error listing: sys/foo.\nURL: v1/sys/foo\nCode: 404' ,
} ,
{
name : 'AdapterError delete' ,
args : [ { httpStatus : 404 , path : 'v1/sys/foo' , errors : [ { } ] } , 'sys/foo' , 'delete' ] ,
expectedContent : 'Error deleting at: sys/foo.\nURL: v1/sys/foo\nCode: 404' ,
} ,
{
name : 'VaultError single error' ,
args : [ { httpStatus : 404 , path : 'v1/sys/foo' , errors : [ 'no client token' ] } , 'sys/foo' , 'delete' ] ,
expectedContent : 'Error deleting at: sys/foo.\nURL: v1/sys/foo\nCode: 404\nErrors:\n no client token' ,
} ,
{
name : 'VaultErrors multiple errors' ,
args : [
{ httpStatus : 404 , path : 'v1/sys/foo' , errors : [ 'no client token' , 'this is an error' ] } ,
'sys/foo' ,
'delete' ,
] ,
expectedContent :
'Error deleting at: sys/foo.\nURL: v1/sys/foo\nCode: 404\nErrors:\n no client token\n this is an error' ,
} ,
] ;
2018-05-25 20:33:22 +00:00
2021-12-17 03:44:29 +00:00
testErrorCases . forEach ( function ( testCase ) {
test ( ` #logFromError: ${ testCase . name } ` , function ( assert ) {
2018-09-25 16:28:26 +00:00
let data = logFromError ( ... testCase . args ) ;
assert . deepEqual (
data ,
{ type : 'error' , content : testCase . expectedContent } ,
'returns the expected data'
) ;
} ) ;
2018-05-25 20:33:22 +00:00
} ) ;
2018-09-25 16:28:26 +00:00
const testCommandCases = [
{
name : 'errors when command does not include a path' ,
args : [ ] ,
expectedContent : 'A path is required to make a request.' ,
} ,
{
name : 'errors when write command does not include data and does not have force tag' ,
args : [ 'foo/bar' , 'write' , { } , [ ] ] ,
expectedContent : 'Must supply data or use -force' ,
} ,
] ;
2018-05-25 20:33:22 +00:00
2021-12-17 03:44:29 +00:00
testCommandCases . forEach ( function ( testCase ) {
test ( ` #logErrorFromInput: ${ testCase . name } ` , function ( assert ) {
2018-09-25 16:28:26 +00:00
let data = logErrorFromInput ( ... testCase . args ) ;
2018-05-25 20:33:22 +00:00
2018-09-25 16:28:26 +00:00
assert . deepEqual (
data ,
{ type : 'error' , content : testCase . expectedContent } ,
'returns the pcorrect data'
) ;
} ) ;
2018-05-25 20:33:22 +00:00
} ) ;
} ) ;