fix: Remove interaction details from IdpRenderHandler

This commit is contained in:
Joachim Van Herwegen 2021-05-26 14:14:52 +02:00
parent 6bfe1bdccc
commit 998d2f49e1
11 changed files with 24 additions and 51 deletions

View File

@ -45,14 +45,13 @@ export class ForgotPasswordHandler extends InteractionHttpHandler {
} }
public async handle(input: InteractionHttpHandlerInput): Promise<void> { public async handle(input: InteractionHttpHandlerInput): Promise<void> {
const interactionDetails = await input.provider.interactionDetails(input.request, input.response);
try { try {
// Validate incoming data // Validate incoming data
const { email } = await getFormDataRequestBody(input.request); const { email } = await getFormDataRequestBody(input.request);
assert(typeof email === 'string' && email.length > 0, 'Email required'); assert(typeof email === 'string' && email.length > 0, 'Email required');
await this.resetPassword(email); await this.resetPassword(email);
await this.sendResponse(input.response, interactionDetails, email); await this.sendResponse(input.response, email);
} catch (err: unknown) { } catch (err: unknown) {
throwIdpInteractionError(err, {}); throwIdpInteractionError(err, {});
} }
@ -93,15 +92,13 @@ export class ForgotPasswordHandler extends InteractionHttpHandler {
/** /**
* Sends a response through the messageRenderHandler. * Sends a response through the messageRenderHandler.
* @param response - HttpResponse to send to. * @param response - HttpResponse to send to.
* @param details - Details of the interaction.
* @param email - Will be inserted in `prefilled` for the template. * @param email - Will be inserted in `prefilled` for the template.
*/ */
private async sendResponse(response: HttpResponse, details: { uid: string }, email: string): Promise<void> { private async sendResponse(response: HttpResponse, email: string): Promise<void> {
// Send response // Send response
await this.messageRenderHandler.handleSafe({ await this.messageRenderHandler.handleSafe({
response, response,
props: { props: {
details,
errorMessage: '', errorMessage: '',
prefilled: { prefilled: {
email, email,

View File

@ -1,14 +1,12 @@
import { RenderHandler } from '../../../server/util/RenderHandler'; import { RenderHandler } from '../../../server/util/RenderHandler';
export interface IdpRenderHandlerProps { export interface IdpRenderHandlerProps {
details: { errorMessage?: string;
uid: string; prefilled?: Record<string, any>;
};
errorMessage: string;
prefilled: Record<string, any>;
} }
/** /**
* A special Render Handler that renders an IDP form * A special Render Handler that renders an IDP form.
* Contains an error message if something was wrong and prefilled values for forms.
*/ */
export abstract class IdpRenderHandler extends RenderHandler<IdpRenderHandlerProps> {} export abstract class IdpRenderHandler extends RenderHandler<IdpRenderHandlerProps> {}

View File

@ -6,8 +6,8 @@ import { IdpInteractionError } from './IdpInteractionError';
import type { IdpRenderHandler } from './IdpRenderHandler'; import type { IdpRenderHandler } from './IdpRenderHandler';
/** /**
* Handles an IDP interaction route. All routes need to extract interaction details to render * Handles an IDP interaction route.
* the UI and accept a POST request to do some action. * All routes render their UI on a GET and accept POST requests to handle the interaction.
*/ */
export class IdpRouteController extends RouterHandler { export class IdpRouteController extends RouterHandler {
private readonly renderHandler: IdpRenderHandler; private readonly renderHandler: IdpRenderHandler;
@ -19,27 +19,25 @@ export class IdpRouteController extends RouterHandler {
/** /**
* Calls the renderHandler to render using the given response and props. * Calls the renderHandler to render using the given response and props.
* `details` typed as any since the `interactionDetails` output typings are not exposed.
*/ */
private async render(input: InteractionHttpHandlerInput, details: any, errorMessage = '', prefilled = {}): private async render(input: InteractionHttpHandlerInput, errorMessage = '', prefilled = {}):
Promise<void> { Promise<void> {
return this.renderHandler.handleSafe({ return this.renderHandler.handleSafe({
response: input.response, response: input.response,
props: { details, errorMessage, prefilled }, props: { errorMessage, prefilled },
}); });
} }
public async handle(input: InteractionHttpHandlerInput): Promise<void> { public async handle(input: InteractionHttpHandlerInput): Promise<void> {
const interactionDetails = await input.provider.interactionDetails(input.request, input.response);
if (input.request.method === 'GET') { if (input.request.method === 'GET') {
await this.render(input, interactionDetails); await this.render(input);
} else if (input.request.method === 'POST') { } else if (input.request.method === 'POST') {
try { try {
await this.handler.handleSafe(input); await this.handler.handleSafe(input);
} catch (err: unknown) { } catch (err: unknown) {
const errorMessage = isNativeError(err) ? err.message : 'An unknown error occurred'; const errorMessage = isNativeError(err) ? err.message : 'An unknown error occurred';
const prefilled = IdpInteractionError.isInstance(err) ? err.prefilled : {}; const prefilled = IdpInteractionError.isInstance(err) ? err.prefilled : {};
await this.render(input, interactionDetails, errorMessage, prefilled); await this.render(input, errorMessage, prefilled);
} }
} }
} }

View File

@ -39,7 +39,6 @@ export class InitialInteractionHandler extends InteractionHttpHandler {
await this.renderHandlerMap[name].handleSafe({ await this.renderHandlerMap[name].handleSafe({
response, response,
props: { props: {
details: interactionDetails,
errorMessage: '', errorMessage: '',
prefilled: {}, prefilled: {},
}, },

View File

@ -12,13 +12,8 @@
<h1 class="title--white">Authorize</h1> <h1 class="title--white">Authorize</h1>
<div class="login-panel"> <div class="login-panel">
<div class="panel-body"> <div class="panel-body">
<form autocomplete="off" action="<%= details.pathPrefix %>/idp/confirm" method="post"> <form autocomplete="off" action="/idp/confirm" method="post">
<button autofocus type="submit" name="submit" class="ids-link-filled">Continue</button> <button autofocus type="submit" name="submit" class="ids-link-filled">Continue</button>
<hr />
<div class="space-between">
<a href="<%= details.returnTo %>" class="link">Cancel</a>
</div>
</form> </form>
</div> </div>
</div> </div>

View File

@ -12,7 +12,7 @@
<h1 class="title--white">Forgot Password</h1> <h1 class="title--white">Forgot Password</h1>
<div class="login-panel"> <div class="login-panel">
<div class="panel-body"> <div class="panel-body">
<form autocomplete="off" action="<%= details.pathPrefix %>/idp/forgotpassword" method="post"> <form autocomplete="off" action="/idp/forgotpassword" method="post">
<%if (errorMessage) { %> <%if (errorMessage) { %>
<div class="input-wrap"> <div class="input-wrap">
@ -32,7 +32,7 @@
<hr /> <hr />
<div class="space-between"> <div class="space-between">
<a href="<%= details.pathPrefix %>/idp/login" class="link">Log In</a> <a href="/idp/login" class="link">Log In</a>
</div> </div>
</form> </form>
</div> </div>

View File

@ -12,7 +12,7 @@
<h1 class="title--white">Sign In</h1> <h1 class="title--white">Sign In</h1>
<div class="login-panel"> <div class="login-panel">
<div class="panel-body"> <div class="panel-body">
<form autocomplete="off" action="<%= details.pathPrefix %>/idp/login" method="post"> <form autocomplete="off" action="/idp/login" method="post">
<%if (errorMessage) { %> <%if (errorMessage) { %>
<div class="input-wrap"> <div class="input-wrap">

View File

@ -12,7 +12,7 @@
<h1 class="title--white">Register</h1> <h1 class="title--white">Register</h1>
<div class="login-panel"> <div class="login-panel">
<div class="panel-body"> <div class="panel-body">
<form autocomplete="off" action="<%= details.pathPrefix %>/idp/register" method="post"> <form autocomplete="off" action="/idp/register" method="post">
<%if (errorMessage) { %> <%if (errorMessage) { %>
<div class="input-wrap"> <div class="input-wrap">
@ -50,7 +50,7 @@
<hr /> <hr />
<div class="space-between"> <div class="space-between">
<a href="<%= details.pathPrefix %>/idp/login" class="link">Sign In</a> <a href="/idp/login" class="link">Sign In</a>
</div> </div>
</form> </form>
</div> </div>

View File

@ -16,10 +16,8 @@ describe('A ForgotPasswordHandler', (): void => {
const email = 'test@test.email'; const email = 'test@test.email';
const recordId = '123456'; const recordId = '123456';
const html = `<a href="/base/idp/resetpassword?rid=${recordId}">Reset Password</a>`; const html = `<a href="/base/idp/resetpassword?rid=${recordId}">Reset Password</a>`;
// `Interaction` type is not exposed const renderParams = { response, props: { errorMessage: '', prefilled: { email }}};
const details = {} as any; const provider: Provider = {} as any;
const renderParams = { response, props: { details, errorMessage: '', prefilled: { email }}};
let provider: Provider;
let messageRenderHandler: IdpRenderHandler; let messageRenderHandler: IdpRenderHandler;
let accountStore: AccountStore; let accountStore: AccountStore;
const baseUrl = 'http://test.com/base/'; const baseUrl = 'http://test.com/base/';
@ -31,10 +29,6 @@ describe('A ForgotPasswordHandler', (): void => {
beforeEach(async(): Promise<void> => { beforeEach(async(): Promise<void> => {
request = createPostFormRequest({ email }); request = createPostFormRequest({ email });
provider = {
interactionDetails: jest.fn().mockResolvedValue(details),
} as any;
messageRenderHandler = { messageRenderHandler = {
handleSafe: jest.fn(), handleSafe: jest.fn(),
} as any; } as any;

View File

@ -11,9 +11,7 @@ import type { HttpResponse } from '../../../../../src/server/HttpResponse';
describe('An IdpRouteController', (): void => { describe('An IdpRouteController', (): void => {
let request: HttpRequest; let request: HttpRequest;
const response: HttpResponse = {} as any; const response: HttpResponse = {} as any;
// `Interaction` type is not exposed const provider: Provider = {} as any;
const details = {} as any;
let provider: Provider;
let renderHandler: IdpRenderHandler; let renderHandler: IdpRenderHandler;
let postHandler: HttpHandler; let postHandler: HttpHandler;
let controller: IdpRouteController; let controller: IdpRouteController;
@ -24,10 +22,6 @@ describe('An IdpRouteController', (): void => {
method: 'GET', method: 'GET',
} as any; } as any;
provider = {
interactionDetails: jest.fn().mockResolvedValue(details),
} as any;
renderHandler = { renderHandler = {
handleSafe: jest.fn(), handleSafe: jest.fn(),
} as any; } as any;
@ -44,7 +38,7 @@ describe('An IdpRouteController', (): void => {
expect(renderHandler.handleSafe).toHaveBeenCalledTimes(1); expect(renderHandler.handleSafe).toHaveBeenCalledTimes(1);
expect(renderHandler.handleSafe).toHaveBeenLastCalledWith({ expect(renderHandler.handleSafe).toHaveBeenLastCalledWith({
response, response,
props: { details, errorMessage: '', prefilled: {}}, props: { errorMessage: '', prefilled: {}},
}); });
expect(postHandler.handleSafe).toHaveBeenCalledTimes(0); expect(postHandler.handleSafe).toHaveBeenCalledTimes(0);
}); });
@ -67,7 +61,7 @@ describe('An IdpRouteController', (): void => {
expect(renderHandler.handleSafe).toHaveBeenCalledTimes(1); expect(renderHandler.handleSafe).toHaveBeenCalledTimes(1);
expect(renderHandler.handleSafe).toHaveBeenLastCalledWith({ expect(renderHandler.handleSafe).toHaveBeenLastCalledWith({
response, response,
props: { details, errorMessage: 'bad request!', prefilled: { more: 'data!' }}, props: { errorMessage: 'bad request!', prefilled: { more: 'data!' }},
}); });
}); });
@ -80,7 +74,7 @@ describe('An IdpRouteController', (): void => {
expect(renderHandler.handleSafe).toHaveBeenCalledTimes(1); expect(renderHandler.handleSafe).toHaveBeenCalledTimes(1);
expect(renderHandler.handleSafe).toHaveBeenLastCalledWith({ expect(renderHandler.handleSafe).toHaveBeenLastCalledWith({
response, response,
props: { details, errorMessage: 'An unknown error occurred', prefilled: {}}, props: { errorMessage: 'An unknown error occurred', prefilled: {}},
}); });
}); });

View File

@ -36,7 +36,6 @@ describe('An InitialInteractionHandler', (): void => {
expect(map.test.handleSafe).toHaveBeenLastCalledWith({ expect(map.test.handleSafe).toHaveBeenLastCalledWith({
response, response,
props: { props: {
details,
errorMessage: '', errorMessage: '',
prefilled: {}, prefilled: {},
}, },
@ -53,7 +52,6 @@ describe('An InitialInteractionHandler', (): void => {
expect(map.default.handleSafe).toHaveBeenLastCalledWith({ expect(map.default.handleSafe).toHaveBeenLastCalledWith({
response, response,
props: { props: {
details,
errorMessage: '', errorMessage: '',
prefilled: {}, prefilled: {},
}, },