oidc: refactor
This commit is contained in:
@@ -304,86 +304,80 @@ class StorageAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
async function renderError(error) {
|
||||
const data = {
|
||||
ICON_URL: '/api/v1/cloudron/avatar',
|
||||
NAME: 'Cloudron',
|
||||
ERROR_MESSAGE: error.error_description || error.error_detail || error.message || 'Internal error',
|
||||
FOOTER: marked.parse(await branding.renderFooter())
|
||||
};
|
||||
|
||||
debug('renderError: %o', error);
|
||||
let html = fs.readFileSync(path.join(__dirname, '/../dashboard/dist/oidc_error.html'), 'utf8');
|
||||
for (const key in data) {
|
||||
html = html.replaceAll(`##${key}##`, data[key]);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
async function renderInteractionPage(req, res) {
|
||||
try {
|
||||
const { uid, prompt, params, session } = await gOidcProvider.interactionDetails(req, res);
|
||||
const { uid, prompt, params, session } = await gOidcProvider.interactionDetails(req, res);
|
||||
|
||||
const client = await oidcClients.get(params.client_id);
|
||||
const client = await oidcClients.get(params.client_id);
|
||||
if (!client) return res.send(await renderError(new Error('Client not found')));
|
||||
|
||||
let app = null;
|
||||
if (client.appId) app = await apps.get(client.appId);
|
||||
const app = client.appId ? await apps.get(client.appId) : null;
|
||||
if (client.appId && !app) return res.send(await renderError(new Error('App not found')));
|
||||
|
||||
switch (prompt.name) {
|
||||
case 'login': {
|
||||
const options = {
|
||||
SUBMIT_URL: `${ROUTE_PREFIX}/interaction/${uid}/login`,
|
||||
ICON_URL: '/api/v1/cloudron/avatar',
|
||||
NAME: client?.name || await branding.getCloudronName(),
|
||||
FOOTER: marked.parse(await branding.renderFooter()),
|
||||
NOTE: (client.id === oidcClients.ID_WEBADMIN && constants.DEMO) ? '<div style="text-align: center;">This is a demo. Username and password is "cloudron"</div>' : ''
|
||||
};
|
||||
res.set('Content-Type', 'text/html');
|
||||
|
||||
if (app) {
|
||||
options.NAME = app.label || app.fqdn;
|
||||
options.ICON_URL = app.iconUrl;
|
||||
}
|
||||
|
||||
// great ejs replacement!
|
||||
let html = fs.readFileSync(__dirname + '/../dashboard/dist/login.html', 'utf-8');
|
||||
Object.keys(options).forEach(key => {
|
||||
html = html.replaceAll(`##${key}##`, options[key]);
|
||||
});
|
||||
|
||||
return res.send(html);
|
||||
}
|
||||
case 'consent': {
|
||||
let hasAccess = false;
|
||||
|
||||
const data = {
|
||||
ICON_URL: '/api/v1/cloudron/avatar',
|
||||
NAME: client?.name || '',
|
||||
FOOTER: marked.parse(await branding.renderFooter())
|
||||
};
|
||||
|
||||
// check if user has access to the app if client refers to an app
|
||||
if (app) {
|
||||
const user = await users.get(session.accountId);
|
||||
|
||||
data.NAME = app.label || app.fqdn;
|
||||
data.ICON_URL = app.iconUrl;
|
||||
hasAccess = apps.canAccess(app, user);
|
||||
} else {
|
||||
hasAccess = true;
|
||||
}
|
||||
|
||||
data.SUBMIT_URL = `${ROUTE_PREFIX}/interaction/${uid}/${hasAccess ? 'confirm' : 'abort'}`;
|
||||
|
||||
let html = fs.readFileSync(path.join(__dirname, hasAccess ? '/../dashboard/dist/oidc_interaction_confirm.html' : '/../dashboard/dist/oidc_interaction_abort.html'), 'utf8');
|
||||
Object.keys(data).forEach(key => {
|
||||
html = html.replaceAll(`##${key}##`, data[key]);
|
||||
});
|
||||
|
||||
return res.send(html);
|
||||
}
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
} catch (error) {
|
||||
debug('route interaction get error', error);
|
||||
|
||||
const data = {
|
||||
if (prompt.name === 'login') {
|
||||
const options = {
|
||||
SUBMIT_URL: `${ROUTE_PREFIX}/interaction/${uid}/login`,
|
||||
ICON_URL: '/api/v1/cloudron/avatar',
|
||||
NAME: 'Cloudron',
|
||||
ERROR_MESSAGE: error.error_description || 'Internal error',
|
||||
NAME: client.name || await branding.getCloudronName(),
|
||||
FOOTER: marked.parse(await branding.renderFooter()),
|
||||
NOTE: (client.id === oidcClients.ID_WEBADMIN && constants.DEMO) ? '<div style="text-align: center;">This is a demo. Username and password is "cloudron"</div>' : ''
|
||||
};
|
||||
|
||||
if (app) {
|
||||
options.NAME = app.label || app.fqdn;
|
||||
options.ICON_URL = app.iconUrl;
|
||||
}
|
||||
|
||||
let html = fs.readFileSync(__dirname + '/../dashboard/dist/login.html', 'utf-8');
|
||||
for (const key in options) {
|
||||
html = html.replaceAll(`##${key}##`, options[key]);
|
||||
}
|
||||
|
||||
return res.send(html);
|
||||
} else if (prompt.name === 'consent') {
|
||||
let hasAccess = false;
|
||||
|
||||
const options = {
|
||||
ICON_URL: '/api/v1/cloudron/avatar',
|
||||
NAME: client.name || '',
|
||||
FOOTER: marked.parse(await branding.renderFooter())
|
||||
};
|
||||
|
||||
let html = fs.readFileSync(path.join(__dirname, '/../dashboard/dist/oidc_error.html'), 'utf8');
|
||||
Object.keys(data).forEach(key => {
|
||||
html = html.replaceAll(`##${key}##`, data[key]);
|
||||
});
|
||||
// check if user has access to the app if client refers to an app
|
||||
if (app) {
|
||||
const user = await users.get(session.accountId);
|
||||
|
||||
options.NAME = app.label || app.fqdn;
|
||||
options.ICON_URL = app.iconUrl;
|
||||
hasAccess = apps.canAccess(app, user);
|
||||
} else {
|
||||
hasAccess = true;
|
||||
}
|
||||
|
||||
options.SUBMIT_URL = `${ROUTE_PREFIX}/interaction/${uid}/${hasAccess ? 'confirm' : 'abort'}`;
|
||||
|
||||
let html = fs.readFileSync(path.join(__dirname, hasAccess ? '/../dashboard/dist/oidc_interaction_confirm.html' : '/../dashboard/dist/oidc_interaction_abort.html'), 'utf8');
|
||||
for (const key in options) {
|
||||
html = html.replaceAll(`##${key}##`, options[key]);
|
||||
}
|
||||
|
||||
res.set('Content-Type', 'text/html');
|
||||
return res.send(html);
|
||||
}
|
||||
}
|
||||
@@ -401,8 +395,7 @@ async function interactionLogin(req, res, next) {
|
||||
|
||||
debug(`interactionLogin: for OpenID client ${clientId} from ${ip}`);
|
||||
|
||||
// This is the auto login via token hack
|
||||
if (req.body.autoLoginToken) {
|
||||
if (req.body.autoLoginToken) { // auto login for first admin/owner
|
||||
if (typeof req.body.autoLoginToken !== 'string') return next(new HttpError(400, 'autoLoginToken must be string if provided'));
|
||||
|
||||
const token = await tokens.getByAccessToken(req.body.autoLoginToken);
|
||||
@@ -575,24 +568,6 @@ async function getClaims(userId/*, use, scope*/) {
|
||||
return claims;
|
||||
}
|
||||
|
||||
async function renderError(ctx, out, error) {
|
||||
const data = {
|
||||
ICON_URL: '/api/v1/cloudron/avatar',
|
||||
NAME: 'Cloudron',
|
||||
ERROR_MESSAGE: error.error_description || error.error_detail || 'Unknown error',
|
||||
FOOTER: marked.parse(await branding.renderFooter())
|
||||
};
|
||||
|
||||
debug('renderError: %o', error);
|
||||
let html = fs.readFileSync(path.join(__dirname, '/../dashboard/dist/oidc_error.html'), 'utf8');
|
||||
Object.keys(data).forEach(key => {
|
||||
html = html.replaceAll(`##${key}##`, data[key]);
|
||||
});
|
||||
|
||||
ctx.type = 'html';
|
||||
ctx.body = html;
|
||||
}
|
||||
|
||||
async function start() {
|
||||
assert(gHttpServer === null, 'OIDC server already started');
|
||||
assert(gOidcProvider === null, 'OIDC provider already started');
|
||||
@@ -635,16 +610,19 @@ async function start() {
|
||||
}
|
||||
|
||||
const configuration = {
|
||||
findAccount: async function(ctx, id) {
|
||||
findAccount: async function (ctx, id) {
|
||||
return {
|
||||
accountId: id,
|
||||
claims: async (use, scope) => await getClaims(id, use, scope)
|
||||
};
|
||||
},
|
||||
renderError,
|
||||
renderError: async function (ctx, out, error) {
|
||||
ctx.type = 'html';
|
||||
ctx.body = await renderError(error);
|
||||
},
|
||||
adapter: StorageAdapter,
|
||||
interactions: {
|
||||
url: async function(ctx, interaction) {
|
||||
url: async function (ctx, interaction) {
|
||||
return `${ROUTE_PREFIX}/interaction/${interaction.uid}`;
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user