mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2025-02-17 08:31:23 -05:00
Merge pull request #6034 from seotts/password-attempts-error
Too many password attempts error
This commit is contained in:
commit
2ee1c25244
4 changed files with 98 additions and 22 deletions
|
@ -101,6 +101,7 @@
|
|||
"studio.transfer.forgotPassword": "Forgot password?",
|
||||
"studio.transfer.alert.somethingWentWrong": "Something went wrong transferring this studio to a new host.",
|
||||
"studio.transfer.alert.wasntTheRightPassword": "Hmm, that wasn’t the right password.",
|
||||
"studio.transfer.alert.tooManyPasswordAttempts": "Too many password attempts. Please try again later.",
|
||||
"studio.transfer.alert.thisUserCannotBecomeHost": "This user cannot become the host — try transfering to another manager",
|
||||
|
||||
"studio.remove": "Remove",
|
||||
|
|
|
@ -11,6 +11,7 @@ const Errors = keyMirror({
|
|||
SERVER: null,
|
||||
PERMISSION: null,
|
||||
PASSWORD: null,
|
||||
PASSWORD_ATTEMPT_LIMIT: null,
|
||||
DUPLICATE: null,
|
||||
USER_MUTED: null,
|
||||
UNKNOWN_USERNAME: null,
|
||||
|
@ -34,6 +35,9 @@ const normalizeError = (err, body, res) => {
|
|||
if (res.statusCode === 401 || res.statusCode === 403) return Errors.PERMISSION;
|
||||
if (res.statusCode === 404) return Errors.UNKNOWN_USERNAME;
|
||||
if (res.statusCode === 409) return Errors.CANNOT_BE_HOST;
|
||||
if (res.statusCode === 429 && body.message === 'try again later') {
|
||||
return Errors.PASSWORD_ATTEMPT_LIMIT;
|
||||
}
|
||||
if (res.statusCode === 429) return Errors.RATE_LIMIT;
|
||||
if (res.statusCode !== 200) return Errors.SERVER;
|
||||
if (body && body.status === 'error') {
|
||||
|
|
|
@ -51,6 +51,14 @@ const TransferHostConfirmation = ({
|
|||
}
|
||||
};
|
||||
|
||||
const validationErrorToMessageId = error => {
|
||||
switch (error) {
|
||||
case Errors.PASSWORD: return 'studio.transfer.alert.wasntTheRightPassword';
|
||||
case Errors.PASSWORD_ATTEMPT_LIMIT: return 'studio.transfer.alert.tooManyPasswordAttempts';
|
||||
default: return 'studio.transfer.alert.somethingWentWrong';
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
setSubmitting(true);
|
||||
handleTransferHost(passwordInputValue, hostInfo.newHostUsername, selectedId)
|
||||
|
@ -64,7 +72,7 @@ const TransferHostConfirmation = ({
|
|||
})
|
||||
.catch(e => {
|
||||
// For password errors, show validation alert without closing the modal
|
||||
if (e === Errors.PASSWORD) {
|
||||
if (e === Errors.PASSWORD || e === Errors.PASSWORD_ATTEMPT_LIMIT) {
|
||||
setSubmitting(false);
|
||||
setValidationError(e);
|
||||
return;
|
||||
|
@ -137,7 +145,9 @@ const TransferHostConfirmation = ({
|
|||
/>
|
||||
{validationError && <ValidationMessage
|
||||
className="transfer-password-validation"
|
||||
message={intl.formatMessage({id: 'studio.transfer.alert.wasntTheRightPassword'})}
|
||||
message={intl.formatMessage({
|
||||
id: validationErrorToMessageId(validationError)
|
||||
})}
|
||||
mode="error"
|
||||
/>}
|
||||
</div>
|
||||
|
|
|
@ -293,7 +293,7 @@ describe('inviteCurator', () => {
|
|||
});
|
||||
test('error because of rate limit', async () => {
|
||||
api.mockImplementation((opts, callback) => {
|
||||
callback(null, null, {statusCode: 429});
|
||||
callback(null, {}, {statusCode: 429});
|
||||
});
|
||||
await expect(store.dispatch(inviteCurator('user2')))
|
||||
.rejects.toBe(Errors.RATE_LIMIT);
|
||||
|
@ -406,26 +406,87 @@ describe('acceptInvitation', () => {
|
|||
expect(state.studio.invited).toBe(true);
|
||||
expect(state.studio.curator).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('transferHost', () => {
|
||||
beforeEach(() => {
|
||||
store = configureStore(reducers, {
|
||||
...initialState,
|
||||
studio: {
|
||||
id: 123123,
|
||||
managers: 3
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('transfers the host on success', async () => {
|
||||
api.mockImplementation((opts, callback) => {
|
||||
callback(null, {}, {statusCode: 200});
|
||||
});
|
||||
await store.dispatch(transferHost('password', 'newHostName', 'newHostId'));
|
||||
const state = store.getState();
|
||||
expect(api.mock.calls[0][0].uri).toBe('/studios/123123/transfer/newHostName');
|
||||
expect(state.studio.owner).toBe('newHostId');
|
||||
describe('transferHost', () => {
|
||||
beforeEach(() => {
|
||||
store = configureStore(reducers, {
|
||||
...initialState,
|
||||
studio: {
|
||||
id: 123123,
|
||||
managers: 3,
|
||||
owner: 'oldHost'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('transfers the host on success', async () => {
|
||||
api.mockImplementation((opts, callback) => {
|
||||
callback(null, {}, {statusCode: 200});
|
||||
});
|
||||
await store.dispatch(transferHost('password', 'newHostName', 'newHostId'));
|
||||
const state = store.getState();
|
||||
expect(api.mock.calls[0][0].uri).toBe('/studios/123123/transfer/newHostName');
|
||||
expect(state.studio.owner).toBe('newHostId');
|
||||
});
|
||||
|
||||
test('error because of permissions issue', async () => {
|
||||
api.mockImplementation((opts, callback) => {
|
||||
callback(null, {}, {statusCode: 403});
|
||||
});
|
||||
await expect(store.dispatch(transferHost('password', 'newHostName', 'newHostId')))
|
||||
.rejects.toBe(Errors.PERMISSION);
|
||||
const state = store.getState();
|
||||
expect(state.studio.owner).toBe('oldHost');
|
||||
});
|
||||
|
||||
test('error because of authorization issue', async () => {
|
||||
api.mockImplementation((opts, callback) => {
|
||||
callback(null, {}, {statusCode: 401});
|
||||
});
|
||||
await expect(store.dispatch(transferHost('password', 'newHostName', 'newHostId')))
|
||||
.rejects.toBe(Errors.PERMISSION);
|
||||
const state = store.getState();
|
||||
expect(state.studio.owner).toBe('oldHost');
|
||||
});
|
||||
|
||||
test('error because of issue with new host', async () => {
|
||||
api.mockImplementation((opts, callback) => {
|
||||
callback(null, {}, {statusCode: 409});
|
||||
});
|
||||
await expect(store.dispatch(transferHost('password', 'newHostName', 'newHostId')))
|
||||
.rejects.toBe(Errors.CANNOT_BE_HOST);
|
||||
const state = store.getState();
|
||||
expect(state.studio.owner).toBe('oldHost');
|
||||
});
|
||||
|
||||
test('error because of incorrect password', async () => {
|
||||
api.mockImplementation((opts, callback) => {
|
||||
callback(null, {status: 'error', message: 'password incorrect'}, {statusCode: 401});
|
||||
});
|
||||
await expect(store.dispatch(transferHost('password', 'newHostName', 'newHostId')))
|
||||
.rejects.toBe(Errors.PASSWORD);
|
||||
const state = store.getState();
|
||||
expect(state.studio.owner).toBe('oldHost');
|
||||
});
|
||||
|
||||
test('error because of too many password attempts', async () => {
|
||||
api.mockImplementation((opts, callback) => {
|
||||
callback(null, {status: 'error', message: 'try again later'}, {statusCode: 429});
|
||||
});
|
||||
await expect(store.dispatch(transferHost('password', 'newHostName', 'newHostId')))
|
||||
.rejects.toBe(Errors.PASSWORD_ATTEMPT_LIMIT);
|
||||
const state = store.getState();
|
||||
expect(state.studio.owner).toBe('oldHost');
|
||||
});
|
||||
|
||||
test('error because of rate limit', async () => {
|
||||
api.mockImplementation((opts, callback) => {
|
||||
callback(null, {}, {statusCode: 429});
|
||||
});
|
||||
await expect(store.dispatch(transferHost('password', 'newHostName', 'newHostId')))
|
||||
.rejects.toBe(Errors.RATE_LIMIT);
|
||||
const state = store.getState();
|
||||
expect(state.studio.owner).toBe('oldHost');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue