mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-05-22 09:29:19 +05:30
Show 404 for unknown authFlow requests. Pass {path, query, params} as first argument to authFlow#handleRequest
This commit is contained in:
parent
97a0e448ed
commit
0149fc59d6
@ -31,7 +31,8 @@ export default function routesFactory(store) {
|
|||||||
authFlow.setStore(store);
|
authFlow.setStore(store);
|
||||||
|
|
||||||
const startAuthFlow = {
|
const startAuthFlow = {
|
||||||
onEnter: ({location}, replace, callback) => authFlow.handleRequest(location.pathname, replace, callback)
|
onEnter: ({location: {query, pathname: path}, params}, replace, callback) =>
|
||||||
|
authFlow.handleRequest({path, params, query}, replace, callback)
|
||||||
};
|
};
|
||||||
|
|
||||||
const userOnly = {
|
const userOnly = {
|
||||||
|
@ -9,8 +9,8 @@ export default class ActivationState extends AbstractState {
|
|||||||
if (user.isActive) {
|
if (user.isActive) {
|
||||||
context.setState(new CompleteState());
|
context.setState(new CompleteState());
|
||||||
} else {
|
} else {
|
||||||
const url = context.getCurrentPath().includes('/activation')
|
const url = context.getRequest().path.includes('/activation')
|
||||||
? context.getCurrentPath()
|
? context.getRequest().path
|
||||||
: '/activation';
|
: '/activation';
|
||||||
context.navigate(url);
|
context.navigate(url);
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,10 @@ export default class AuthFlow {
|
|||||||
|
|
||||||
setStore(store) {
|
setStore(store) {
|
||||||
this.navigate = (route) => {
|
this.navigate = (route) => {
|
||||||
if (this.currentPath !== route) {
|
if (this.getRequest().path !== route) {
|
||||||
this.currentPath = route;
|
this.currentRequest = {
|
||||||
|
path: route
|
||||||
|
};
|
||||||
|
|
||||||
if (this.replace) {
|
if (this.replace) {
|
||||||
this.replace(route);
|
this.replace(route);
|
||||||
@ -89,33 +91,40 @@ export default class AuthFlow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentPath() {
|
/**
|
||||||
return this.currentPath;
|
* @return {object} - current request object
|
||||||
}
|
*/
|
||||||
|
getRequest() {
|
||||||
getQuery() {
|
return this.currentRequest ? {...this.currentRequest} : {};
|
||||||
return this.getState().routing.location.query;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This should be called from onEnter prop of react-router Route component
|
* This should be called from onEnter prop of react-router Route component
|
||||||
*
|
*
|
||||||
* @param {string} path
|
* @param {object} request
|
||||||
|
* @param {string} request.path
|
||||||
|
* @param {object} request.params
|
||||||
|
* @param {object} request.query
|
||||||
* @param {function} replace
|
* @param {function} replace
|
||||||
* @param {function} [callback = function() {}] - an optional callback function to be called, when state will be stabilized
|
* @param {function} [callback = function() {}] - an optional callback function to be called, when state will be stabilized
|
||||||
* (state's enter function's promise resolved)
|
* (state's enter function's promise resolved)
|
||||||
*/
|
*/
|
||||||
handleRequest(path, replace, callback = function() {}) {
|
handleRequest(request, replace, callback = function() {}) {
|
||||||
|
const {path, params = {}, query = {}} = request;
|
||||||
this.replace = replace;
|
this.replace = replace;
|
||||||
this.onReady = callback;
|
this.onReady = callback;
|
||||||
|
|
||||||
if (this.currentPath === path) {
|
if (!path) {
|
||||||
|
throw new Error('The request.path is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.getRequest().path === path) {
|
||||||
// we are already handling this path
|
// we are already handling this path
|
||||||
this.onReady();
|
this.onReady();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentPath = path;
|
this.currentRequest = request;
|
||||||
|
|
||||||
if (path === '/') {
|
if (path === '/') {
|
||||||
// reset oauth data if user tried to navigate to index route
|
// reset oauth data if user tried to navigate to index route
|
||||||
@ -159,7 +168,9 @@ export default class AuthFlow {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported request: ${path}`);
|
console.log('Unsupported request', {path, query, params});
|
||||||
|
replace('/404');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import CompleteState from './CompleteState';
|
|||||||
|
|
||||||
export default class OAuthState extends AbstractState {
|
export default class OAuthState extends AbstractState {
|
||||||
enter(context) {
|
enter(context) {
|
||||||
const query = context.getQuery();
|
const {query} = context.getRequest();
|
||||||
|
|
||||||
return context.run('oAuthValidate', {
|
return context.run('oAuthValidate', {
|
||||||
clientId: query.client_id,
|
clientId: query.client_id,
|
||||||
|
@ -7,8 +7,8 @@ export default class RecoverPasswordState extends AbstractState {
|
|||||||
const {user} = context.getState();
|
const {user} = context.getState();
|
||||||
|
|
||||||
if (user.isGuest) {
|
if (user.isGuest) {
|
||||||
const url = context.getCurrentPath().includes('/recover-password')
|
const url = context.getRequest().path.includes('/recover-password')
|
||||||
? context.getCurrentPath()
|
? context.getRequest().path
|
||||||
: '/recover-password';
|
: '/recover-password';
|
||||||
context.navigate(url);
|
context.navigate(url);
|
||||||
} else {
|
} else {
|
||||||
|
@ -30,7 +30,7 @@ describe('ActivationState', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
context.getCurrentPath.returns(expectedPath);
|
context.getRequest.returns({path: expectedPath});
|
||||||
|
|
||||||
expectNavigate(mock, '/activation');
|
expectNavigate(mock, '/activation');
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ describe('ActivationState', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
context.getCurrentPath.returns(expectedPath);
|
context.getRequest.returns({path: expectedPath});
|
||||||
|
|
||||||
expectNavigate(mock, expectedPath);
|
expectNavigate(mock, expectedPath);
|
||||||
|
|
||||||
|
@ -30,10 +30,10 @@ describe('AuthFlow.functional', () => {
|
|||||||
flow = new AuthFlow(actions);
|
flow = new AuthFlow(actions);
|
||||||
flow.setStore(store);
|
flow.setStore(store);
|
||||||
|
|
||||||
navigate = function navigate(url) { // emulates router behaviour
|
navigate = function navigate(path, extra = {}) { // emulates router behaviour
|
||||||
if (navigate.lastUrl !== url) {
|
if (navigate.lastUrl !== path) {
|
||||||
navigate.lastUrl = url;
|
navigate.lastUrl = path;
|
||||||
flow.handleRequest(url, navigate);
|
flow.handleRequest({path, ...extra}, navigate);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ describe('AuthFlow.functional', () => {
|
|||||||
redirectUri: expectedRedirect
|
redirectUri: expectedRedirect
|
||||||
})});
|
})});
|
||||||
|
|
||||||
navigate('/oauth2');
|
navigate('/oauth2', {query: {}});
|
||||||
|
|
||||||
expect(flow.run, 'to have calls satisfying', [
|
expect(flow.run, 'to have calls satisfying', [
|
||||||
['oAuthValidate', {}],
|
['oAuthValidate', {}],
|
||||||
|
@ -5,7 +5,6 @@ import AbstractState from 'services/authFlow/AbstractState';
|
|||||||
|
|
||||||
import OAuthState from 'services/authFlow/OAuthState';
|
import OAuthState from 'services/authFlow/OAuthState';
|
||||||
import RegisterState from 'services/authFlow/RegisterState';
|
import RegisterState from 'services/authFlow/RegisterState';
|
||||||
import AcceptRulesState from 'services/authFlow/AcceptRulesState';
|
|
||||||
import RecoverPasswordState from 'services/authFlow/RecoverPasswordState';
|
import RecoverPasswordState from 'services/authFlow/RecoverPasswordState';
|
||||||
import ForgotPasswordState from 'services/authFlow/ForgotPasswordState';
|
import ForgotPasswordState from 'services/authFlow/ForgotPasswordState';
|
||||||
import ActivationState from 'services/authFlow/ActivationState';
|
import ActivationState from 'services/authFlow/ActivationState';
|
||||||
@ -189,7 +188,7 @@ describe('AuthFlow', () => {
|
|||||||
'/resend-activation': ResendActivationState
|
'/resend-activation': ResendActivationState
|
||||||
}).forEach(([path, type]) => {
|
}).forEach(([path, type]) => {
|
||||||
it(`should transition to ${type.name} if ${path}`, () => {
|
it(`should transition to ${type.name} if ${path}`, () => {
|
||||||
flow.handleRequest(path);
|
flow.handleRequest({path});
|
||||||
|
|
||||||
expect(flow.setState, 'was called once');
|
expect(flow.setState, 'was called once');
|
||||||
expect(flow.setState, 'to have a call satisfying', [
|
expect(flow.setState, 'to have a call satisfying', [
|
||||||
@ -199,7 +198,7 @@ describe('AuthFlow', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should run setOAuthRequest if /', () => {
|
it('should run setOAuthRequest if /', () => {
|
||||||
flow.handleRequest('/');
|
flow.handleRequest({path: '/'});
|
||||||
|
|
||||||
expect(flow.run, 'was called once');
|
expect(flow.run, 'was called once');
|
||||||
expect(flow.run, 'to have a call satisfying', ['setOAuthRequest', {}]);
|
expect(flow.run, 'to have a call satisfying', ['setOAuthRequest', {}]);
|
||||||
@ -208,7 +207,7 @@ describe('AuthFlow', () => {
|
|||||||
it('should call callback', () => {
|
it('should call callback', () => {
|
||||||
const callback = sinon.stub().named('callback');
|
const callback = sinon.stub().named('callback');
|
||||||
|
|
||||||
flow.handleRequest('/', () => {}, callback);
|
flow.handleRequest({path: '/'}, () => {}, callback);
|
||||||
|
|
||||||
expect(callback, 'was called once');
|
expect(callback, 'was called once');
|
||||||
});
|
});
|
||||||
@ -223,7 +222,7 @@ describe('AuthFlow', () => {
|
|||||||
|
|
||||||
flow.setState = AuthFlow.prototype.setState.bind(flow, state);
|
flow.setState = AuthFlow.prototype.setState.bind(flow, state);
|
||||||
|
|
||||||
flow.handleRequest('/', () => {}, callback);
|
flow.handleRequest({path: '/'}, () => {}, callback);
|
||||||
|
|
||||||
expect(resolve, 'to be', callback);
|
expect(resolve, 'to be', callback);
|
||||||
|
|
||||||
@ -236,8 +235,8 @@ describe('AuthFlow', () => {
|
|||||||
const path = '/oauth2';
|
const path = '/oauth2';
|
||||||
const callback = sinon.stub();
|
const callback = sinon.stub();
|
||||||
|
|
||||||
flow.handleRequest(path, () => {}, callback);
|
flow.handleRequest({path}, () => {}, callback);
|
||||||
flow.handleRequest(path, () => {}, callback);
|
flow.handleRequest({path}, () => {}, callback);
|
||||||
|
|
||||||
expect(flow.setState, 'was called once');
|
expect(flow.setState, 'was called once');
|
||||||
expect(flow.setState, 'to have a call satisfying', [
|
expect(flow.setState, 'to have a call satisfying', [
|
||||||
@ -247,7 +246,27 @@ describe('AuthFlow', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws if unsupported request', () => {
|
it('throws if unsupported request', () => {
|
||||||
expect(() => flow.handleRequest('/foo/bar'), 'to throw', 'Unsupported request: /foo/bar');
|
const replace = sinon.stub().named('replace');
|
||||||
|
|
||||||
|
flow.handleRequest({path: '/foo/bar'}, replace);
|
||||||
|
|
||||||
|
expect(replace, 'to have a call satisfying', ['/404']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getRequest()', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
sinon.stub(flow, 'setState').named('flow.setState');
|
||||||
|
sinon.stub(flow, 'run').named('flow.run');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a copy of current request', () => {
|
||||||
|
const request = {path: '/', query: {foo: 'bar'}, params: {baz: 'bud'}};
|
||||||
|
|
||||||
|
flow.handleRequest(request);
|
||||||
|
|
||||||
|
expect(flow.getRequest(), 'to equal', request);
|
||||||
|
expect(flow.getRequest(), 'not to be', request);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -30,7 +30,7 @@ describe('OAuthState', () => {
|
|||||||
state: 'state'
|
state: 'state'
|
||||||
};
|
};
|
||||||
|
|
||||||
context.getQuery.returns(query);
|
context.getRequest.returns({query});
|
||||||
|
|
||||||
expectRun(
|
expectRun(
|
||||||
mock,
|
mock,
|
||||||
@ -50,7 +50,7 @@ describe('OAuthState', () => {
|
|||||||
it('should transition to complete state on success', () => {
|
it('should transition to complete state on success', () => {
|
||||||
const promise = Promise.resolve();
|
const promise = Promise.resolve();
|
||||||
|
|
||||||
context.getQuery.returns({});
|
context.getRequest.returns({query: {}});
|
||||||
|
|
||||||
mock.expects('run').returns(promise);
|
mock.expects('run').returns(promise);
|
||||||
expectState(mock, CompleteState);
|
expectState(mock, CompleteState);
|
||||||
|
@ -28,7 +28,7 @@ describe('RecoverPasswordState', () => {
|
|||||||
user: {isGuest: true}
|
user: {isGuest: true}
|
||||||
});
|
});
|
||||||
|
|
||||||
context.getCurrentPath.returns(expectedPath);
|
context.getRequest.returns({path: expectedPath});
|
||||||
|
|
||||||
expectNavigate(mock, expectedPath);
|
expectNavigate(mock, expectedPath);
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ describe('RecoverPasswordState', () => {
|
|||||||
user: {isGuest: true}
|
user: {isGuest: true}
|
||||||
});
|
});
|
||||||
|
|
||||||
context.getCurrentPath.returns(expectedPath);
|
context.getRequest.returns({path: expectedPath});
|
||||||
|
|
||||||
expectNavigate(mock, expectedPath);
|
expectNavigate(mock, expectedPath);
|
||||||
|
|
||||||
|
@ -7,8 +7,7 @@ export function bootstrap() {
|
|||||||
getState: sinon.stub(),
|
getState: sinon.stub(),
|
||||||
run() {},
|
run() {},
|
||||||
setState() {},
|
setState() {},
|
||||||
getCurrentPath: sinon.stub(),
|
getRequest: sinon.stub(),
|
||||||
getQuery: sinon.stub(),
|
|
||||||
navigate() {}
|
navigate() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user