12e073e8cf
mostly because code is being autogenerated by all the AI stuff using this prefix. it's also used in the stack trace.
121 lines
4.6 KiB
JavaScript
121 lines
4.6 KiB
JavaScript
'use strict';
|
|
|
|
exports = module.exports = {
|
|
add,
|
|
get,
|
|
del,
|
|
update,
|
|
list,
|
|
|
|
validateId,
|
|
|
|
// token client ids. we categorize them so we can have different restrictions based on the client
|
|
ID_WEBADMIN: 'cid-webadmin', // dashboard
|
|
ID_DEVELOPMENT: 'cid-development', // dashboard development
|
|
ID_CLI: 'cid-cli', // cloudron cli
|
|
ID_SDK: 'cid-sdk', // created by user via dashboard
|
|
};
|
|
|
|
const assert = require('node:assert'),
|
|
BoxError = require('./boxerror.js'),
|
|
dashboard = require('./dashboard.js'),
|
|
database = require('./database.js'),
|
|
hat = require('./hat.js'),
|
|
safe = require('safetydance');
|
|
|
|
const OIDC_CLIENTS_TABLE_NAME = 'oidcClients';
|
|
const OIDC_CLIENTS_FIELDS = [ 'id', 'secret', 'name', 'appId', 'loginRedirectUri', 'tokenSignatureAlgorithm' ];
|
|
const DEFAULT_TOKEN_SIGNATURE_ALGORITHM='RS256';
|
|
|
|
function validateId(type) {
|
|
assert.strictEqual(typeof type, 'string');
|
|
|
|
const types = [ exports.ID_WEBADMIN, exports.ID_SDK, exports.ID_DEVELOPMENT, exports.ID_CLI ];
|
|
if (types.indexOf(type) === -1) return new BoxError(BoxError.BAD_FIELD, `type must be one of ${types.join(',')}`);
|
|
|
|
return null;
|
|
}
|
|
|
|
function postProcess(result) {
|
|
assert.strictEqual(typeof result, 'object');
|
|
|
|
result.tokenSignatureAlgorithm = result.tokenSignatureAlgorithm || DEFAULT_TOKEN_SIGNATURE_ALGORITHM;
|
|
|
|
return result;
|
|
}
|
|
|
|
async function add(data) {
|
|
assert.strictEqual(typeof data.loginRedirectUri, 'string');
|
|
assert.strictEqual(typeof data.name, 'string');
|
|
assert.strictEqual(typeof data.appId, 'string');
|
|
assert(data.tokenSignatureAlgorithm === 'RS256' || data.tokenSignatureAlgorithm === 'EdDSA');
|
|
|
|
const id = data.id || 'cid-' + hat(128); // oidc addon provides the id for apps as app.id
|
|
const secret = hat(256);
|
|
|
|
const query = `INSERT INTO ${OIDC_CLIENTS_TABLE_NAME} (id, secret, name, appId, loginRedirectUri, tokenSignatureAlgorithm) VALUES (?, ?, ?, ?, ?, ?)`;
|
|
const args = [ id, secret, data.name, data.appId, data.loginRedirectUri, data.tokenSignatureAlgorithm ];
|
|
|
|
const [error] = await safe(database.query(query, args));
|
|
if (error && error.code === 'ER_DUP_ENTRY') throw new BoxError(BoxError.ALREADY_EXISTS, 'client already exists');
|
|
if (error) throw error;
|
|
|
|
return { id, secret };
|
|
}
|
|
|
|
async function get(id) {
|
|
assert.strictEqual(typeof id, 'string');
|
|
|
|
if (id === exports.ID_WEBADMIN) {
|
|
const { fqdn:dashboardFqdn } = await dashboard.getLocation();
|
|
return {
|
|
id: exports.ID_WEBADMIN,
|
|
secret: 'notused',
|
|
application_type: 'web',
|
|
response_types: ['code', 'code token'],
|
|
grant_types: ['authorization_code', 'implicit'],
|
|
loginRedirectUri: `https://${dashboardFqdn}/authcallback.html`
|
|
};
|
|
} else if (id === exports.ID_DEVELOPMENT) {
|
|
return {
|
|
id: exports.ID_DEVELOPMENT,
|
|
secret: 'notused',
|
|
application_type: 'native', // have to use native here to support plaintext http, this however makes it impossible to skip consent screen
|
|
response_types: ['code', 'code token'],
|
|
grant_types: ['authorization_code', 'implicit'],
|
|
loginRedirectUri: 'http://localhost:4000/authcallback.html'
|
|
};
|
|
}
|
|
|
|
const result = await database.query(`SELECT ${OIDC_CLIENTS_FIELDS} FROM ${OIDC_CLIENTS_TABLE_NAME} WHERE id = ?`, [ id ]);
|
|
if (result.length === 0) return null;
|
|
|
|
return postProcess(result[0]);
|
|
}
|
|
|
|
async function update(id, data) {
|
|
assert.strictEqual(typeof id, 'string');
|
|
assert.strictEqual(typeof data.loginRedirectUri, 'string');
|
|
assert.strictEqual(typeof data.name, 'string');
|
|
assert.strictEqual(typeof data.appId, 'string');
|
|
assert(data.tokenSignatureAlgorithm === 'RS256' || data.tokenSignatureAlgorithm === 'EdDSA');
|
|
|
|
const result = await database.query(`UPDATE ${OIDC_CLIENTS_TABLE_NAME} SET name=?, appId=?, loginRedirectUri=?, tokenSignatureAlgorithm=? WHERE id = ?`, [ data.name, data.appId, data.loginRedirectUri, data.tokenSignatureAlgorithm, id]);
|
|
if (result.affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'client not found');
|
|
}
|
|
|
|
async function del(id) {
|
|
assert.strictEqual(typeof id, 'string');
|
|
|
|
const result = await database.query(`DELETE FROM ${OIDC_CLIENTS_TABLE_NAME} WHERE id = ?`, [ id ]);
|
|
if (result.affectedRows !== 1) throw new BoxError(BoxError.NOT_FOUND, 'client not found');
|
|
}
|
|
|
|
async function list() {
|
|
const results = await database.query(`SELECT * FROM ${OIDC_CLIENTS_TABLE_NAME} ORDER BY name ASC`, []);
|
|
|
|
results.forEach(postProcess);
|
|
|
|
return results;
|
|
}
|