import Model, { attr } from '@ember-data/model'; import { alias } from '@ember/object/computed'; import { set, get, computed } from '@ember/object'; import clamp from 'vault/utils/clamp'; import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; const ACTION_VALUES = { encrypt: { isSupported: 'supportsEncryption', description: 'Looks up wrapping properties for the given token', glyph: 'lock-fill', }, decrypt: { isSupported: 'supportsDecryption', description: 'Decrypts the provided ciphertext using this key', glyph: 'mail-open', }, datakey: { isSupported: 'supportsEncryption', description: 'Generates a new key and value encrypted with this key', glyph: 'key', }, rewrap: { isSupported: 'supportsEncryption', description: 'Rewraps the ciphertext using the latest version of the named key', glyph: 'reload', }, sign: { isSupported: 'supportsSigning', description: 'Get the cryptographic signature of the given data', glyph: 'pencil-tool', }, hmac: { isSupported: true, description: 'Generate a data digest using a hash algorithm', glyph: 'shuffle', }, verify: { isSupported: true, description: 'Validate the provided signature for the given data', glyph: 'check-circle', }, export: { isSupported: 'exportable', description: 'Get the named key', glyph: 'external-link', }, }; export default Model.extend({ type: attr('string', { defaultValue: 'aes256-gcm96', }), name: attr('string', { label: 'Name', fieldValue: 'id', readOnly: true, }), autoRotatePeriod: attr({ defaultValue: '0', defaultShown: 'Key is not automatically rotated', editType: 'ttl', label: 'Auto-rotation period', }), deletionAllowed: attr('boolean'), derived: attr('boolean'), exportable: attr('boolean'), minDecryptionVersion: attr('number', { defaultValue: 1, }), minEncryptionVersion: attr('number', { defaultValue: 0, }), latestVersion: attr('number'), keys: attr('object'), convergentEncryption: attr('boolean'), convergentEncryptionVersion: attr('number'), supportsSigning: attr('boolean'), supportsEncryption: attr('boolean'), supportsDecryption: attr('boolean'), supportsDerivation: attr('boolean'), setConvergentEncryption(val) { if (val === true) { set(this, 'derived', val); } set(this, 'convergentEncryption', val); }, setDerived(val) { if (val === false) { set(this, 'convergentEncryption', val); } set(this, 'derived', val); }, supportedActions: computed('type', function () { return Object.keys(ACTION_VALUES) .filter((name) => { const { isSupported } = ACTION_VALUES[name]; return typeof isSupported === 'boolean' || get(this, isSupported); }) .map((name) => { const { description, glyph } = ACTION_VALUES[name]; return { name, description, glyph }; }); }), canDelete: computed('deletionAllowed', 'lastLoadTS', function () { const deleteAttrChanged = Boolean(this.changedAttributes().deletionAllowed); return this.deletionAllowed && deleteAttrChanged === false; }), keyVersions: computed('validKeyVersions', function () { let maxVersion = Math.max(...this.validKeyVersions); const versions = []; while (maxVersion > 0) { versions.unshift(maxVersion); maxVersion--; } return versions; }), encryptionKeyVersions: computed( 'keyVerisons', 'keyVersions', 'latestVersion', 'minDecryptionVersion', function () { const { keyVersions, minDecryptionVersion } = this; return keyVersions .filter((version) => { return version >= minDecryptionVersion; }) .reverse(); } ), keysForEncryption: computed('minEncryptionVersion', 'latestVersion', function () { let { minEncryptionVersion, latestVersion } = this; const minVersion = clamp(minEncryptionVersion - 1, 0, latestVersion); const versions = []; while (latestVersion > minVersion) { versions.push(latestVersion); latestVersion--; } return versions; }), validKeyVersions: computed('keys', function () { return Object.keys(this.keys); }), exportKeyTypes: computed('exportable', 'supportsEncryption', 'supportsSigning', 'type', function () { const types = ['hmac']; if (this.supportsSigning) { types.unshift('signing'); } if (this.supportsEncryption) { types.unshift('encryption'); } return types; }), backend: attr('string'), rotatePath: lazyCapabilities(apiPath`${'backend'}/keys/${'id'}/rotate`, 'backend', 'id'), canRotate: alias('rotatePath.canUpdate'), secretPath: lazyCapabilities(apiPath`${'backend'}/keys/${'id'}`, 'backend', 'id'), canRead: alias('secretPath.canUpdate'), canEdit: alias('secretPath.canUpdate'), });