/** * Copyright (c) HashiCorp, Inc. * SPDX-License-Identifier: BUSL-1.1 */ import Model, { attr } from '@ember-data/model'; import { withFormFields } from 'vault/decorators/model-form-fields'; import { withModelValidations } from 'vault/decorators/model-validations'; import { withExpandedAttributes } from 'vault/decorators/model-expanded-attributes'; import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; import { isPresent } from '@ember/utils'; const validations = { accountName: [ { validator(model) { const { generate, accountName } = model; // this is required when generate is true return generate && !isPresent(accountName) ? false : true; }, message: "Account name can't be blank when the key is generated by Vault.", }, { type: 'containsWhiteSpace', message: "Account name contains whitespace. If this is desired, you'll need to encode it with %20 in API requests.", level: 'warn', }, ], issuer: [ { validator(model) { const { generate, issuer } = model; // this is required when generate is true return generate && !isPresent(issuer) ? false : true; }, message: "Issuer can't be blank when when the key is generated by Vault.", }, ], key: [ { validator(model) { const { generate, key, url } = model; // this is required when generate is false and url is blank return !generate && !isPresent(url) && !isPresent(key) ? false : true; }, message: "Key can't be blank if key is being passed from another service and the URL is empty.", }, ], keySize: [{ type: 'number', message: 'Key size must be a number.' }], name: [ { type: 'presence', message: "Name can't be blank." }, { type: 'containsWhiteSpace', message: "Name contains whitespace. If this is desired, you'll need to encode it with %20 in API requests.", level: 'warn', }, ], qrSize: [{ type: 'number', message: 'QR size must be a number' }], }; @withModelValidations(validations) @withExpandedAttributes() @withFormFields() export default class TotpKeyModel extends Model { @attr('string', { readOnly: true, }) backend; @attr('string', { subText: 'Specifies the name for this key.', }) name; @attr('string', { subText: 'The name of the account associated with the key. Required for keys generated by Vault.', }) accountName; @attr('string', { possibleValues: ['SHA1', 'SHA256', 'SHA512'], defaultValue: 'SHA1', }) algorithm; @attr('number', { possibleValues: [6, 8], defaultValue: 6, }) digits; @attr('string', { subText: `The name of the key's issuing organization. Required for keys generated by Vault.`, }) issuer; @attr({ editType: 'ttl', helperTextEnabled: 'How long each generated TOTP is valid.', defaultValue: 30, // API accepts both an integer as seconds and string with unit e.g 30 || '30s' }) period; // The generate attr is a boolean. The generateString getter and setter is used only in forms to get and set the boolean via // strings values. The payload params expect the attr to be a boolean value. @attr({ label: 'Key Provider', defaultValue: true, editType: 'radio', possibleValues: ['Vault', 'Other service'], fieldValue: 'generateString', subText: 'Specifies if the key should be generated by Vault or passed from another service.', }) generate; // Used when generate is true @attr('number', { defaultValue: 20, }) keySize; @attr('number', { possibleValues: [0, 1], defaultValue: 1, }) skew; @attr('boolean', { editType: 'toggleButton', defaultValue: true, helperTextDisabled: 'Vault will not return QR code and url upon key creation.', helperTextEnabled: 'QR code and URL will be returned upon generating a key.', }) exported; @attr('number', { label: 'QR size', defaultValue: 200, }) qrSize; // Used when generate is false @attr('string', { label: 'URL', helpText: 'If a URL is provided the other fields can be left empty. E.g. otpauth://totp/Vault:test@test.com?secret=Y64VEVMBTSXCYIWRSHRNDZW62MPGVU2G&issuer=Vault', subText: 'The TOTP key url string that can be used to configure a key.', }) url; @attr('string', { subText: 'The root key used to generate a TOTP code.', }) key; // Returned when a key is created as provider @attr('string', { readOnly: true, }) barcode; get attrs() { const keys = ['accountName', 'name', 'algorithm', 'digits', 'issuer', 'period']; return keys.map((k) => this.allByKey[k]); } get generatedAttrs() { const keys = ['url']; return keys.map((k) => this.allByKey[k]); } get generateString() { return this.generate ? 'Vault' : 'Other service'; } set generateString(value) { this.generate = value === 'Vault' ? true : false; } @lazyCapabilities(apiPath`${'backend'}/keys/${'id'}`, 'backend', 'id') keyPath; get canDelete() { return this.keyPath.get('canDelete'); } get canRead() { return this.keyPath.get('canRead'); } }