/** * Copyright (c) HashiCorp, Inc. * SPDX-License-Identifier: MPL-2.0 */ // Low level service that allows users to input paths to make requests to vault // this service provides the UI synecdote to the cli commands read, write, delete, and list import { filterBy } from '@ember/object/computed'; import Service from '@ember/service'; import { getOwner } from '@ember/application'; import { computed } from '@ember/object'; import { shiftCommandIndex } from 'vault/lib/console-helpers'; import { encodePath } from 'vault/utils/path-encoding-helpers'; export function sanitizePath(path) { //remove whitespace + remove trailing and leading slashes return path.trim().replace(/^\/+|\/+$/g, ''); } export function ensureTrailingSlash(path) { return path.replace(/(\w+[^/]$)/g, '$1/'); } const VERBS = { read: 'GET', list: 'GET', write: 'POST', delete: 'DELETE', }; export default Service.extend({ isOpen: false, adapter() { return getOwner(this).lookup('adapter:console'); }, commandHistory: filterBy('log', 'type', 'command'), log: computed(function () { return []; }), commandIndex: null, shiftCommandIndex(keyCode, setCommandFn = () => {}) { const [newIndex, newCommand] = shiftCommandIndex(keyCode, this.commandHistory, this.commandIndex); if (newCommand !== undefined && newIndex !== undefined) { this.set('commandIndex', newIndex); setCommandFn(newCommand); } }, clearLog(clearAll = false) { const log = this.log; let history; if (!clearAll) { history = this.commandHistory.slice(); history.setEach('hidden', true); } log.clear(); if (history) { log.addObjects(history); } }, logAndOutput(command, logContent) { const log = this.log; if (command) { log.pushObject({ type: 'command', content: command }); this.set('commandIndex', null); } if (logContent) { log.pushObject(logContent); } }, ajax(operation, path, options = {}) { const verb = VERBS[operation]; const adapter = this.adapter(); const url = adapter.buildURL(encodePath(path)); const { data, wrapTTL } = options; return adapter.ajax(url, verb, { data, wrapTTL, }); }, kvGet(path, data, flags = {}) { const { wrapTTL, metadata } = flags; // Split on first / to find backend and secret path const pathSegment = metadata ? 'metadata' : 'data'; const [backend, secretPath] = path.split(/\/(.+)?/); const kvPath = `${backend}/${pathSegment}/${secretPath}`; return this.ajax('read', sanitizePath(kvPath), { wrapTTL }); }, read(path, data, flags) { const wrapTTL = flags?.wrapTTL; return this.ajax('read', sanitizePath(path), { wrapTTL }); }, write(path, data, flags) { const wrapTTL = flags?.wrapTTL; return this.ajax('write', sanitizePath(path), { data, wrapTTL }); }, delete(path) { return this.ajax('delete', sanitizePath(path)); }, list(path, data, flags) { const wrapTTL = flags?.wrapTTL; const listPath = ensureTrailingSlash(sanitizePath(path)); return this.ajax('list', listPath, { data: { list: true, }, wrapTTL, }); }, });