Skip to content

Commit

Permalink
Merge 5d86c87 into 8c266f0
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimvh committed Aug 3, 2021
2 parents 8c266f0 + 5d86c87 commit b5d54b9
Show file tree
Hide file tree
Showing 25 changed files with 409 additions and 503 deletions.
17 changes: 16 additions & 1 deletion config/identity/handler/default.json
Expand Up @@ -23,7 +23,22 @@
"providerFactory": { "@id": "urn:solid-server:default:IdentityProviderFactory" },
"templateHandler": {
"@type": "TemplateHandler",
"templateEngine": { "@type": "EjsTemplateEngine" }
"templateEngine": {
"comment": "Renders the specific page and embeds it into the main HTML body.",
"@type": "ChainedTemplateEngine",
"renderedName": "htmlBody",
"engines": [
{
"comment": "Will be called with specific interaction templates to generate HTML snippets.",
"@type": "EjsTemplateEngine"
},
{
"comment": "Will embed the result of the first engine into the main HTML template.",
"@type": "EjsTemplateEngine",
"template": "$PACKAGE_ROOT/templates/main.html.ejs",
}
]
}
},
"interactionCompleter": {
"comment": "Responsible for finishing OIDC interactions.",
Expand Down
Expand Up @@ -7,7 +7,7 @@
"@type": "InteractionRoute",
"route": "^/forgotpassword/?$",
"viewTemplate": "$PACKAGE_ROOT/templates/identity/email-password/forgot-password.html.ejs",
"responseTemplate": "$PACKAGE_ROOT/templates/identity/email-password/email-sent.html.ejs",
"responseTemplate": "$PACKAGE_ROOT/templates/identity/email-password/forgot-password-response.html.ejs",
"handler": {
"@type": "ForgotPasswordHandler",
"args_accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" },
Expand Down
Expand Up @@ -8,7 +8,7 @@
"@type": "InteractionRoute",
"route": "^/resetpassword(/[^/]*)?$",
"viewTemplate": "$PACKAGE_ROOT/templates/identity/email-password/reset-password.html.ejs",
"responseTemplate": "$PACKAGE_ROOT/templates/identity/email-password/message.html.ejs",
"responseTemplate": "$PACKAGE_ROOT/templates/identity/email-password/reset-password-response.html.ejs",
"handler": {
"@type": "ResetPasswordHandler",
"accountStore": { "@id": "urn:solid-server:auth:password:AccountStore" }
Expand Down
Expand Up @@ -11,8 +11,8 @@
"@id": "urn:solid-server:default:MarkdownToHtmlConverter",
"@type": "MarkdownToHtmlConverter",
"templateEngine": {
"@type": "HandlebarsTemplateEngine",
"template": "$PACKAGE_ROOT/templates/main.html.hbs"
"@type": "EjsTemplateEngine",
"template": "$PACKAGE_ROOT/templates/main.html.ejs"
}
},
{
Expand Down
4 changes: 2 additions & 2 deletions src/identity/IdentityProviderHttpHandler.ts
Expand Up @@ -182,9 +182,9 @@ export class IdentityProviderHttpHandler extends HttpHandler {
throw new BadRequestHttpError(`Unsupported request: ${request.method} ${request.url}`);
}

private async handleTemplateResponse(response: HttpResponse, templateFile: string, contents: NodeJS.Dict<any>):
private async handleTemplateResponse(response: HttpResponse, templateFile: string, contents?: NodeJS.Dict<any>):
Promise<void> {
await this.templateHandler.handleSafe({ response, templateFile, contents });
await this.templateHandler.handleSafe({ response, templateFile, contents: contents ?? {}});
}

/**
Expand Down
Expand Up @@ -6,7 +6,7 @@ export type InteractionHandlerResult = InteractionResponseResult | InteractionCo

export interface InteractionResponseResult<T = NodeJS.Dict<any>> {
type: 'response';
details: T;
details?: T;
}

export interface InteractionCompleteResult {
Expand Down
Expand Up @@ -34,7 +34,7 @@ export class ResetPasswordHandler extends InteractionHandler {
assertPassword(password, confirmPassword);

await this.resetPassword(recordId, password);
return { type: 'response', details: { message: 'Your password was successfully reset.' }};
return { type: 'response' };
} catch (error: unknown) {
throwIdpInteractionError(error);
}
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Expand Up @@ -304,6 +304,7 @@ export * from './util/locking/SingleThreadedResourceLocker';
export * from './util/locking/WrappedExpiringReadWriteLocker';

// Util/Templates
export * from './util/templates/ChainedTemplateEngine';
export * from './util/templates/EjsTemplateEngine';
export * from './util/templates/HandlebarsTemplateEngine';
export * from './util/templates/TemplateEngine';
Expand Down
6 changes: 1 addition & 5 deletions src/storage/conversion/MarkdownToHtmlConverter.ts
Expand Up @@ -23,12 +23,8 @@ export class MarkdownToHtmlConverter extends TypedRepresentationConverter {

public async handle({ representation }: RepresentationConverterArgs): Promise<Representation> {
const markdown = await readableToString(representation.data);
// Try to extract the main title for use in the <title> tag
const title = /^#+\s*([^\n]+)\n/u.exec(markdown)?.[1];

// Place the rendered Markdown into the HTML template
const htmlBody = marked(markdown);
const html = await this.templateEngine.render({ htmlBody, title });
const html = await this.templateEngine.render({ htmlBody });

return new BasicRepresentation(html, representation.metadata, TEXT_HTML);
}
Expand Down
39 changes: 39 additions & 0 deletions src/util/templates/ChainedTemplateEngine.ts
@@ -0,0 +1,39 @@
import type { Template, TemplateEngine } from './TemplateEngine';
import Dict = NodeJS.Dict;

/**
* Calls the given array of {@link TemplateEngine}s in the order they appear,
* feeding the output of one into the input of the next.
*
* The first engine will be called with the provided contents and template parameters.
* All subsequent engines will be called with no template parameter.
* Contents will still be passed along and another entry will be added for the body of the previous output.
*/
export class ChainedTemplateEngine<T extends Dict<any> = Dict<any>> implements TemplateEngine<T> {
private readonly firstEngine: TemplateEngine<T>;
private readonly chainedEngines: TemplateEngine[];
private readonly renderedName: string;

/**
* @param engines - Engines will be executed in the same order as the array.
* @param renderedName - The name of the key used to pass the body of one engine to the next.
*/
public constructor(engines: TemplateEngine[], renderedName = 'body') {
if (engines.length === 0) {
throw new Error('At least 1 engine needs to be provided.');
}
this.firstEngine = engines[0];
this.chainedEngines = engines.slice(1);
this.renderedName = renderedName;
}

public async render(contents: T): Promise<string>;
public async render<TCustom = T>(contents: TCustom, template: Template): Promise<string>;
public async render<TCustom = T>(contents: TCustom, template?: Template): Promise<string> {
let body = await this.firstEngine.render(contents, template!);
for (const engine of this.chainedEngines) {
body = await engine.render({ ...contents, [this.renderedName]: body });
}
return body;
}
}
31 changes: 4 additions & 27 deletions templates/identity/email-password/confirm.html.ejs
@@ -1,27 +1,4 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Authorize</title>
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
</head>
<body>
<header>
<a href="/"><img src="/.well_known/css/images/solid.svg" alt="[Solid logo]" /></a>
<h1>Community Solid Server</h1>
</header>
<main>
<h1>Authorize</h1>
<form action="/idp/confirm" method="post">
<p class="actions"><button autofocus type="submit" name="submit" class="ids-link-filled">Continue</button></p>
</form>
</main>
<footer>
<p>
©2019–2021 <a href="https://inrupt.com/">Inrupt Inc.</a>
and <a href="https://www.imec-int.com/">imec</a>
</p>
</footer>
</body>
</html>
<h1>Authorize</h1>
<form action="/idp/confirm" method="post">
<p class="actions"><button autofocus type="submit" name="submit" class="ids-link-filled">Continue</button></p>
</form>
37 changes: 0 additions & 37 deletions templates/identity/email-password/email-sent.html.ejs

This file was deleted.

@@ -0,0 +1,13 @@
<h1>Email sent</h1>
<form action="/idp/forgotpassword" method="post">
<p>If your account exists, an email has been sent with a link to reset your password.</p>
<p>If you do not receive your email in a couple of minutes, check your spam folder or click the link below to send another email.</p>

<input type="hidden" name="email" value="<%= email %>" />

<p class="actions"><a href="/idp/login">Back to Log In</a></p>

<p class="actions">
<button type="submit" name="submit" class="link">Send Another Email</button>
</p>
</form>
55 changes: 16 additions & 39 deletions templates/identity/email-password/forgot-password.html.ejs
@@ -1,42 +1,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Forgot password</title>
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
</head>
<body>
<header>
<a href="/"><img src="/.well_known/css/images/solid.svg" alt="[Solid logo]" /></a>
<h1>Community Solid Server</h1>
</header>
<main>
<h1>Forgot password</h1>
<form action="/idp/forgotpassword" method="post">
<%if (errorMessage) { %>
<p class="error"><%= errorMessage %></p>
<% } %>
<h1>Forgot password</h1>
<form action="/idp/forgotpassword" method="post">
<%if (errorMessage) { %>
<p class="error"><%= errorMessage %></p>
<% } %>

<fieldset>
<ol>
<li>
<label for="email">Email</label>
<input id="email" type="email" name="email" autofocus>
</li>
</ol>
</fieldset>
<fieldset>
<ol>
<li>
<label for="email">Email</label>
<input id="email" type="email" name="email" autofocus>
</li>
</ol>
</fieldset>

<p class="actions"><button type="submit" name="submit">Send recovery email</button></p>
<p class="actions"><button type="submit" name="submit">Send recovery email</button></p>

<p class="actions"><a href="/idp/login" class="link">Log in</a></p>
</form>
</main>
<footer>
<p>
©2019–2021 <a href="https://inrupt.com/">Inrupt Inc.</a>
and <a href="https://www.imec-int.com/">imec</a>
</p>
</footer>
</body>
</html>
<p class="actions"><a href="/idp/login" class="link">Log in</a></p>
</form>
77 changes: 27 additions & 50 deletions templates/identity/email-password/login.html.ejs
@@ -1,53 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Log in</title>
<link rel="stylesheet" href="/.well_known/css/styles/main.css" type="text/css">
</head>
<body>
<header>
<a href="/"><img src="/.well_known/css/images/solid.svg" alt="[Solid logo]" /></a>
<h1>Community Solid Server</h1>
</header>
<main>
<h1>Log in</h1>
<form action="/idp/login" method="post">
<%if (errorMessage) { %>
<p class="error"><%= errorMessage %></p>
<% } %>
<h1>Log in</h1>
<form action="/idp/login" method="post">
<%if (errorMessage) { %>
<p class="error"><%= errorMessage %></p>
<% } %>

<fieldset>
<legend>Your account</legend>
<ol>
<li>
<label for="email">Email</label>
<input id="email" type="email" name="email" autofocus <% if (prefilled.email) { %> value="<%= prefilled.email %>" <% } %>>
</li>
<li>
<label for="password">Password</label>
<input id="password" type="password" name="password">
</li>
<li class="checkbox">
<label><input type="checkbox" name="remember" value="yes" checked>Stay logged in</label>
</li>
</ol>
</fieldset>
<fieldset>
<legend>Your account</legend>
<ol>
<li>
<label for="email">Email</label>
<input id="email" type="email" name="email" autofocus <% if (prefilled.email) { %> value="<%= prefilled.email %>" <% } %>>
</li>
<li>
<label for="password">Password</label>
<input id="password" type="password" name="password">
</li>
<li class="checkbox">
<label><input type="checkbox" name="remember" value="yes" checked>Stay logged in</label>
</li>
</ol>
</fieldset>

<p class="actions"><button type="submit" name="submit">Log in</button></p>
<p class="actions"><button type="submit" name="submit">Log in</button></p>

<ul class="actions">
<li><a href="/idp/register" class="link">Sign up</a></li>
<li><a href="/idp/forgotpassword" class="link">Forgot password</a></li>
</ul>
</form>
</main>
<footer>
<p>
©2019–2021 <a href="https://inrupt.com/">Inrupt Inc.</a>
and <a href="https://www.imec-int.com/">imec</a>
</p>
</footer>
</body>
</html>
<ul class="actions">
<li><a href="/idp/register" class="link">Sign up</a></li>
<li><a href="/idp/forgotpassword" class="link">Forgot password</a></li>
</ul>
</form>

0 comments on commit b5d54b9

Please sign in to comment.