mirror of
https://github.com/HabitRPG/habitica.git
synced 2026-04-05 12:18:15 -05:00
Compare commits
6 Commits
qa/monkey
...
phillip/au
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a09c6b84d5 | ||
|
|
3ba161abbe | ||
|
|
9038572ff9 | ||
|
|
80825f3478 | ||
|
|
a254c50a8e | ||
|
|
918a769441 |
Submodule habitica-images updated: 1c6f7d65d7...aa72332019
@@ -64,6 +64,18 @@ describe('POST /user/auth/social', () => {
|
||||
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a google user');
|
||||
});
|
||||
|
||||
it('fails if allowRegister is false and user does not exist', async () => {
|
||||
await expect(api.post(endpoint, {
|
||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||
network,
|
||||
allowRegister: false,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 404,
|
||||
error: 'NotFound',
|
||||
message: t('userNotFound'),
|
||||
});
|
||||
});
|
||||
|
||||
it('logs an existing user in', async () => {
|
||||
const registerResponse = await api.post(endpoint, {
|
||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||
@@ -131,6 +143,36 @@ describe('POST /user/auth/social', () => {
|
||||
expect(response.newUser).to.be.false;
|
||||
});
|
||||
|
||||
it('logs an existing user into their social account if allowRegister is false', async () => {
|
||||
const registerResponse = await api.post(endpoint, {
|
||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||
network,
|
||||
});
|
||||
expect(registerResponse.newUser).to.be.true;
|
||||
// This is important for existing accounts before the new social handling
|
||||
passport._strategies.google.userProfile.restore();
|
||||
const expectedResult = {
|
||||
id: randomGoogleId,
|
||||
displayName: 'a google user',
|
||||
emails: [
|
||||
{ value: user.auth.local.email },
|
||||
],
|
||||
};
|
||||
sandbox.stub(passport._strategies.google, 'userProfile').yields(null, expectedResult);
|
||||
|
||||
const response = await api.post(endpoint, {
|
||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||
network,
|
||||
allowRegister: false,
|
||||
});
|
||||
|
||||
expect(response.apiToken).to.eql(registerResponse.apiToken);
|
||||
expect(response.id).to.eql(registerResponse.id);
|
||||
expect(response.apiToken).not.to.eql(user.apiToken);
|
||||
expect(response.id).not.to.eql(user._id);
|
||||
expect(response.newUser).to.be.false;
|
||||
});
|
||||
|
||||
it('add social auth to an existing user', async () => {
|
||||
const response = await user.post(endpoint, {
|
||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
||||
|
||||
56
test/api/v4/user/auth/POST-check_email.test.js
Normal file
56
test/api/v4/user/auth/POST-check_email.test.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
translate as t,
|
||||
requester,
|
||||
generateUser,
|
||||
} from '../../../../helpers/api-integration/v4';
|
||||
|
||||
const ENDPOINT = '/user/auth/check-email';
|
||||
|
||||
describe('POST /user/auth/check-email', () => {
|
||||
const email = 'SOmE-nEw-emAIl_2@example.net';
|
||||
let api;
|
||||
|
||||
beforeEach(async () => {
|
||||
api = requester();
|
||||
});
|
||||
|
||||
it('returns email if it is not used yet', async () => {
|
||||
const response = await api.post(ENDPOINT, {
|
||||
email,
|
||||
});
|
||||
expect(response.email).to.eql(email);
|
||||
});
|
||||
|
||||
it('rejects if email is not provided', async () => {
|
||||
await expect(api.post(ENDPOINT, {
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 400,
|
||||
error: 'BadRequest',
|
||||
message: 'Invalid request parameters.',
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if email is already taken', async () => {
|
||||
const user = await generateUser();
|
||||
|
||||
await expect(api.post(ENDPOINT, {
|
||||
email: user.auth.local.email,
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('emailTaken'),
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if casing is different', async () => {
|
||||
const user = await generateUser();
|
||||
|
||||
await expect(api.post(ENDPOINT, {
|
||||
email: user.auth.local.email.toUpperCase(),
|
||||
})).to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('emailTaken'),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -479,7 +479,7 @@ api.updateHero = {
|
||||
}
|
||||
|
||||
if (updateData.auth.local && updateData.auth.local.email) {
|
||||
hero.auth.local.email = updateData.auth.local.email;
|
||||
hero.auth.local.email = updateData.auth.local.email.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import {
|
||||
authWithHeaders,
|
||||
} from '../../middlewares/auth';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../../libs/errors';
|
||||
import * as authLib from '../../libs/auth';
|
||||
import { model as User } from '../../models/user';
|
||||
import { verifyUsername } from '../../libs/user/validation';
|
||||
@@ -83,4 +86,37 @@ api.registerLocal = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {put} /api/v3/user/auth/check-email Check if email is used
|
||||
* @apiDescription Check if the email is already used by another user
|
||||
* @apiName CheckEmail
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiParam (Body) {String} email The checked email address.
|
||||
*
|
||||
* @apiSuccess {String} data.email The checked email address
|
||||
*/
|
||||
api.checkEmail = {
|
||||
method: 'POST',
|
||||
url: '/user/auth/check-email',
|
||||
async handler (req, res) {
|
||||
req.checkBody({
|
||||
email: {
|
||||
notEmpty: { errorMessage: res.t('missingEmail') },
|
||||
},
|
||||
});
|
||||
|
||||
const validationErrors = req.validationErrors();
|
||||
if (validationErrors) throw validationErrors;
|
||||
|
||||
const emailAlreadyInUse = await User.findOne({
|
||||
'auth.local.email': req.body.email.toLowerCase(),
|
||||
}).select({ _id: 1 }).lean().exec();
|
||||
|
||||
if (emailAlreadyInUse) throw new NotAuthorized(res.t('emailTaken'));
|
||||
|
||||
return res.respond(200, { email: req.body.email });
|
||||
},
|
||||
};
|
||||
|
||||
export default api;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import passport from 'passport';
|
||||
import common from '../../../common';
|
||||
import { BadRequest, NotAuthorized } from '../errors';
|
||||
import { BadRequest, NotAuthorized, NotFound } from '../errors';
|
||||
import logger from '../logger';
|
||||
import {
|
||||
generateUsername,
|
||||
@@ -33,14 +33,14 @@ export async function socialEmailToLocal (user) {
|
||||
{ 'auth.local.email': socialEmail },
|
||||
{ _id: 1 },
|
||||
).exec();
|
||||
if (!conflictingUser) return socialEmail;
|
||||
if (!conflictingUser) return socialEmail.toLowerCase();
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function loginSocial (req, res) { // eslint-disable-line import/prefer-default-export
|
||||
let existingUser = res.locals.user;
|
||||
const { network } = req.body;
|
||||
const { network, allowRegister = true } = req.body;
|
||||
|
||||
const isSupportedNetwork = common.constants.SUPPORTED_SOCIAL_NETWORKS
|
||||
.find(supportedNetwork => supportedNetwork.key === network);
|
||||
@@ -74,6 +74,10 @@ export async function loginSocial (req, res) { // eslint-disable-line import/pre
|
||||
return loginRes(user, req, res);
|
||||
}
|
||||
|
||||
if (!allowRegister) {
|
||||
throw new NotFound(res.t('userNotFound'));
|
||||
}
|
||||
|
||||
let email;
|
||||
if (profile.emails && profile.emails[0] && profile.emails[0].value) {
|
||||
email = profile.emails[0].value.toLowerCase();
|
||||
|
||||
Reference in New Issue
Block a user