Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62cdaa2cdd | |||
| 13a02e6850 | |||
| baba3a414f | |||
| d049b2de49 | |||
| b0e8104790 | |||
| 3cc769cc3c | |||
| 94ff5c76eb | |||
| d74e3168ed | |||
| 9029de825c | |||
| b50a1de76f |
+4
-1
@@ -1,4 +1,7 @@
|
|||||||
# v1.13.2
|
## v1.13.3
|
||||||
|
- fix: remove missing DEPLOYMENT.md and docs/ from Docker build context
|
||||||
|
|
||||||
|
## v1.13.2
|
||||||
- chore: Update CI pipeline integration
|
- chore: Update CI pipeline integration
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|||||||
@@ -115,8 +115,6 @@ COPY nodejs/api.md ./api.md
|
|||||||
# docs/ mirrors the repo's own top-level docs/ folder.
|
# docs/ mirrors the repo's own top-level docs/ folder.
|
||||||
COPY README.md /README.md
|
COPY README.md /README.md
|
||||||
COPY CHANGELOG.md /CHANGELOG.md
|
COPY CHANGELOG.md /CHANGELOG.md
|
||||||
COPY DEPLOYMENT.md /DEPLOYMENT.md
|
|
||||||
COPY docs /docs
|
|
||||||
|
|
||||||
# Baked commit hash from the gitinfo stage (see build_info.js).
|
# Baked commit hash from the gitinfo stage (see build_info.js).
|
||||||
COPY --from=gitinfo /commit.txt ./.build_commit
|
COPY --from=gitinfo /commit.txt ./.build_commit
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
|
|
||||||
const conf = require('@simpleworkjs/conf');
|
const conf = require('@simpleworkjs/conf');
|
||||||
|
const baoConf = require('@simpleworkjs/bao-conf');
|
||||||
const Table = require('.');
|
const Table = require('.');
|
||||||
const ModelPs = require('../utils/model_pubsub');
|
const ModelPs = require('../utils/model_pubsub');
|
||||||
|
|
||||||
@@ -139,11 +140,26 @@ class DnsProvider extends Table{
|
|||||||
let __intraModel = this.__intraModel(data.dnsProvider);
|
let __intraModel = this.__intraModel(data.dnsProvider);
|
||||||
Provider = __intraModel.Provider;
|
Provider = __intraModel.Provider;
|
||||||
|
|
||||||
|
if (!data.id) data.id = crypto.randomBytes(8).toString("hex");
|
||||||
|
|
||||||
|
let secrets = {};
|
||||||
|
for (let key in Provider._keyMap) {
|
||||||
|
if (Provider._keyMap[key].isPrivate && data[key] !== undefined) {
|
||||||
|
secrets[key] = data[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This is here test if the given API key is valid
|
// This is here test if the given API key is valid
|
||||||
let provider = new __intraModel.Provider(data, ...args);
|
let provider = new __intraModel.Provider(data, ...args);
|
||||||
let domains = await provider.listDomains();
|
let domains = await provider.listDomains();
|
||||||
|
|
||||||
|
for (let key in secrets) data[key] = '********';
|
||||||
|
|
||||||
let instance = await super.create.call(__intraModel, data, ...args);
|
let instance = await super.create.call(__intraModel, data, ...args);
|
||||||
|
|
||||||
|
if (Object.keys(secrets).length > 0) {
|
||||||
|
await baoConf.set(`proxy/dns-providers/${instance.id}`, secrets);
|
||||||
|
}
|
||||||
try{
|
try{
|
||||||
await instance.updateDomains(domains);
|
await instance.updateDomains(domains);
|
||||||
}catch(updateError){
|
}catch(updateError){
|
||||||
@@ -189,7 +205,63 @@ class DnsProvider extends Table{
|
|||||||
let instance = await super.get(data, ...args);
|
let instance = await super.get(data, ...args);
|
||||||
let __intraModel = this.__intraModel(instance.dnsProvider);
|
let __intraModel = this.__intraModel(instance.dnsProvider);
|
||||||
|
|
||||||
return await super.get.call(__intraModel, data, ...args);
|
let resolved = await super.get.call(__intraModel, data, ...args);
|
||||||
|
try {
|
||||||
|
let secrets = await baoConf.get(`proxy/dns-providers/${resolved.id}`);
|
||||||
|
if (secrets) Object.assign(resolved, secrets);
|
||||||
|
} catch(e) {}
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async findall(...args){
|
||||||
|
let instances = await super.findall(...args);
|
||||||
|
for (let inst of instances) {
|
||||||
|
try {
|
||||||
|
let secrets = await baoConf.get(`proxy/dns-providers/${inst.id}`);
|
||||||
|
if (secrets) Object.assign(inst, secrets);
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
return instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async find(...args){
|
||||||
|
let instances = await super.find(...args);
|
||||||
|
for (let inst of instances) {
|
||||||
|
try {
|
||||||
|
let secrets = await baoConf.get(`proxy/dns-providers/${inst.id}`);
|
||||||
|
if (secrets) Object.assign(inst, secrets);
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
return instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
async update(data){
|
||||||
|
let Provider = this.constructor.Provider || providers[this.dnsProvider];
|
||||||
|
let secrets = {};
|
||||||
|
if (Provider) {
|
||||||
|
for (let key in Provider._keyMap) {
|
||||||
|
if (Provider._keyMap[key].isPrivate && data[key] !== undefined && data[key] !== '********') {
|
||||||
|
secrets[key] = data[key];
|
||||||
|
data[key] = '********';
|
||||||
|
} else if (Provider._keyMap[key].isPrivate && data[key] === '********') {
|
||||||
|
delete data[key]; // Do not update the masked value if it's sent back
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = await super.update(data);
|
||||||
|
|
||||||
|
if (Object.keys(secrets).length > 0) {
|
||||||
|
let existing = await baoConf.get(`proxy/dns-providers/${this.id}`) || {};
|
||||||
|
await baoConf.set(`proxy/dns-providers/${this.id}`, { ...existing, ...secrets });
|
||||||
|
Object.assign(this, secrets);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(...args){
|
||||||
|
await baoConf.request('DELETE', `proxy/dns-providers/${this.id}`).catch(()=>{});
|
||||||
|
return await super.remove(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static listProviders(){
|
static listProviders(){
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "proxy-api",
|
"name": "proxy-api",
|
||||||
"version": "1.13.2",
|
"version": "1.13.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "proxy-api",
|
"name": "proxy-api",
|
||||||
"version": "1.13.2",
|
"version": "1.13.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^7.3.0",
|
"@fortawesome/fontawesome-free": "^7.3.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "proxy-api",
|
"name": "proxy-api",
|
||||||
"version": "1.13.2",
|
"version": "1.14.3",
|
||||||
"author": [
|
"author": [
|
||||||
{
|
{
|
||||||
"name": "William Mantly",
|
"name": "William Mantly",
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
const { describe, test, beforeEach, afterEach, after, mock } = require('node:test');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
|
||||||
|
const baoConf = require('@simpleworkjs/bao-conf');
|
||||||
|
const Table = require('../../models/index');
|
||||||
|
const DnsProvider = Table.models.DnsProvider;
|
||||||
|
const DuckDns = require('../../models/dns_provider/duckdns');
|
||||||
|
|
||||||
|
describe('DnsProvider Vault Integration', () => {
|
||||||
|
let originalSet, originalGet, originalRequest;
|
||||||
|
|
||||||
|
after(async () => {
|
||||||
|
if (Table._redis && Table._redis.quit) {
|
||||||
|
await Table._redis.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// Mock baoConf
|
||||||
|
originalSet = baoConf.set;
|
||||||
|
originalGet = baoConf.get;
|
||||||
|
originalRequest = baoConf.request;
|
||||||
|
|
||||||
|
const vaultStore = {};
|
||||||
|
baoConf.set = mock.fn(async (path, data) => { vaultStore[path] = data; return true; });
|
||||||
|
baoConf.get = mock.fn(async (path) => vaultStore[path] || {});
|
||||||
|
baoConf.request = mock.fn(async () => ({}));
|
||||||
|
|
||||||
|
mock.method(DuckDns.prototype, 'listDomains', async () => []);
|
||||||
|
mock.method(DnsProvider.prototype, 'updateDomains', async () => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
baoConf.set = originalSet;
|
||||||
|
baoConf.get = originalGet;
|
||||||
|
baoConf.request = originalRequest;
|
||||||
|
mock.restoreAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create() writes isPrivate keys to OpenBao and get() retrieves them', async () => {
|
||||||
|
const payload = {
|
||||||
|
name: 'My Duck',
|
||||||
|
dnsProvider: 'DuckDns',
|
||||||
|
token: 'super-secret-vault-token',
|
||||||
|
subdomains: 'myduck',
|
||||||
|
created_by: 'admin'
|
||||||
|
};
|
||||||
|
|
||||||
|
const instance = await DnsProvider.create(payload);
|
||||||
|
|
||||||
|
// 1. Should have called OpenBao set
|
||||||
|
assert.strictEqual(baoConf.set.mock.callCount(), 1);
|
||||||
|
const [path, secrets] = baoConf.set.mock.calls[0].arguments;
|
||||||
|
|
||||||
|
assert.strictEqual(path, `proxy/dns-providers/${instance.id}`);
|
||||||
|
assert.deepStrictEqual(secrets, { token: 'super-secret-vault-token' });
|
||||||
|
|
||||||
|
// 2. The returned instance should have the secret injected back
|
||||||
|
assert.strictEqual(instance.token, 'super-secret-vault-token');
|
||||||
|
|
||||||
|
// 3. get() should fetch public data from Redis and merge secrets from OpenBao
|
||||||
|
// (baoConf.get is already mocked to return from vaultStore)
|
||||||
|
const fetched = await DnsProvider.get(instance.id);
|
||||||
|
assert.strictEqual(fetched.token, 'super-secret-vault-token');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user