'use strict'; // SharedSecret — a secret the owner has published to the shared namespace so it // can be shared with other users and/or downstream apps. // // The secret DATA lives in OpenBao at `secret/shared//` (KV-v2), // never in the DB. This row is metadata only (owner + slug + description) and is // the source of truth for the UI (which shares exist). ACCESS CONTROL is enforced // entirely by OpenBao ACL policies: the owner's `user-` policy grants full // R/W on `secret/shared//*`, and each grantee's policy content is // edited to add `read` on the exact shared path (see vault_broker.js — policy // content is parsed live at token use, so a grant takes effect immediately with // no token re-mint). `secretId` on SharedSecretGrant links grantees to this row. // // `slug` is unique and immutable in practice — it is embedded in the shared path // and in grantee policy rules, so changing it would require rewriting policies. // Like PluginInstance, there is no ORM auto-timestamp hook: route handlers stamp // created_by/on + updated_by/on on every write. `id` (uuid) is generated by the // ORM on create. const { Model } = require('@simpleworkjs/orm'); class SharedSecret extends Model { static fields = { id: { type: 'uuid', primaryKey: true }, // Human slug embedded in the OpenBao path: secret/shared//. // Unique so two owners can't collide on the same shared path. slug: { type: 'string', isRequired: true, unique: true, min: 1, max: 64 }, // The publishing user's uid — also the shared path's namespace segment. ownerUid: { type: 'string', isRequired: true, min: 1, max: 64 }, // Optional human description shown in the Shared tab. description: { type: 'text' }, // Audit stamps (set by the route handler, not by an ORM hook). created_by: { type: 'string' }, created_on: { type: 'integer' }, updated_by: { type: 'string' }, updated_on: { type: 'integer' }, }; // Full OpenBao KV-v2 path for this shared secret (logical path, no data/metadata). static pathFor(ownerUid, slug) { return `shared/${ownerUid}/${slug}`; } path() { return SharedSecret.pathFor(this.ownerUid, this.slug); } // Look up by slug (unique). Returns the row or null. static async getBySlug(slug) { const rows = await this.list({ where: { slug } }); return rows[0] || null; } } module.exports = { SharedSecret };