From 719fab8d4bb1c226035565eb4b0c1aca56789541 Mon Sep 17 00:00:00 2001 From: CuriousMagpie Date: Fri, 16 Sep 2022 16:12:34 -0400 Subject: [PATCH] chore: merge develop in --- habitica-images | 2 +- test/api/unit/libs/email.test.js | 25 - test/api/unit/libs/password.test.js | 2 +- .../v3/integration/user/DELETE-user.test.js | 39 - .../DELETE-user_auth_social_network.test.js | 64 - .../user/auth/POST-user_auth_social.test.js | 141 --- .../auth/POST-user_reset_password.test.js | 13 + website/client/src/assets/svg/facebook.svg | 4 +- website/client/src/assets/svg/instagram.svg | 4 +- website/client/src/assets/svg/tumblr.svg | 3 + website/client/src/assets/svg/twitter.svg | 4 +- website/client/src/components/appFooter.vue | 1119 +++++++++++------ .../client/src/components/auth/authForm.vue | 20 - .../components/auth/registerLoginReset.vue | 4 - .../src/components/groups/groupPlan.vue | 12 +- .../src/components/payments/buyGemsModal.vue | 8 - .../client/src/components/settings/site.vue | 4 +- .../client/src/components/static/privacy.vue | 7 +- .../src/components/static/staticWrapper.vue | 104 +- website/client/vue.config.js | 1 - website/common/locales/da/backgrounds.json | 3 +- website/common/locales/da/challenge.json | 3 +- .../locales/da/communityguidelines.json | 104 +- website/common/locales/da/content.json | 2 +- website/common/locales/da/contrib.json | 5 +- website/common/locales/da/death.json | 4 +- website/common/locales/da/defaulttasks.json | 4 +- website/common/locales/da/faq.json | 36 +- website/common/locales/da/front.json | 9 +- website/common/locales/da/gear.json | 9 +- website/common/locales/da/groups.json | 7 +- website/common/locales/da/limited.json | 173 ++- website/common/locales/da/npc.json | 9 +- website/common/locales/da/overview.json | 6 +- website/common/locales/da/pets.json | 8 +- website/common/locales/da/quests.json | 12 +- website/common/locales/da/questscontent.json | 2 +- website/common/locales/da/rebirth.json | 2 +- website/common/locales/da/settings.json | 8 +- website/common/locales/da/subscriber.json | 16 +- website/common/locales/da/tasks.json | 22 +- website/common/locales/de/groups.json | 2 +- website/common/locales/en/front.json | 11 +- website/common/locales/en/npc.json | 3 +- website/common/locales/en/questsContent.json | 2 +- website/common/locales/en/subscriber.json | 2 +- website/common/locales/es/gear.json | 34 +- website/common/locales/it/backgrounds.json | 9 +- website/common/locales/it/gear.json | 6 +- website/common/locales/ja/backgrounds.json | 9 +- website/common/locales/ja/gear.json | 6 +- website/common/locales/ja/generic.json | 4 +- website/common/locales/ja/quests.json | 2 +- website/common/locales/pt_BR/backgrounds.json | 9 +- website/common/locales/pt_BR/gear.json | 6 +- website/common/locales/ru/backgrounds.json | 9 +- website/common/locales/ru/groups.json | 22 +- website/common/locales/ru/settings.json | 3 +- website/common/locales/ru/subscriber.json | 4 +- website/common/locales/uk/backgrounds.json | 9 +- website/common/locales/uk/gear.json | 18 +- website/common/locales/uk/questscontent.json | 38 +- website/common/locales/zh/backgrounds.json | 9 +- website/common/locales/zh/faq.json | 3 +- website/common/locales/zh/gear.json | 10 +- website/common/locales/zh/npc.json | 4 +- website/common/script/constants.js | 1 - website/common/script/content/bundles.js | 3 +- .../common/script/content/constants/events.js | 6 + .../script/content/shop-featuredItems.js | 14 +- website/server/libs/auth/index.js | 7 +- website/server/libs/auth/social.js | 10 +- website/server/libs/setupPassport.js | 15 - 73 files changed, 1297 insertions(+), 1017 deletions(-) create mode 100644 website/client/src/assets/svg/tumblr.svg diff --git a/habitica-images b/habitica-images index 8d63ff55e5..ce1e117dc9 160000 --- a/habitica-images +++ b/habitica-images @@ -1 +1 @@ -Subproject commit 8d63ff55e5c63872f95a3c9106d0b5f90af9ab6f +Subproject commit ce1e117dc94ac4b1757f144e6897e332ccdaf119 diff --git a/test/api/unit/libs/email.test.js b/test/api/unit/libs/email.test.js index b3fe180086..8673e03b62 100644 --- a/test/api/unit/libs/email.test.js +++ b/test/api/unit/libs/email.test.js @@ -13,11 +13,6 @@ function getUser () { username: 'username', email: 'email@email', }, - facebook: { - emails: [{ - value: 'email@facebook', - }], - }, google: { emails: [{ value: 'email@google', @@ -62,30 +57,12 @@ describe('emails', () => { expect(data).to.have.property('canSend', true); }); - it('returns correct user data [facebook users]', () => { - const attachEmail = requireAgain(pathToEmailLib); - const { getUserInfo } = attachEmail; - const user = getUser(); - delete user.profile.name; - delete user.auth.local.email; - delete user.auth.google.emails; - delete user.auth.apple.emails; - - const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']); - - expect(data).to.have.property('name', user.auth.local.username); - expect(data).to.have.property('email', user.auth.facebook.emails[0].value); - expect(data).to.have.property('_id', user._id); - expect(data).to.have.property('canSend', true); - }); - it('returns correct user data [google users]', () => { const attachEmail = requireAgain(pathToEmailLib); const { getUserInfo } = attachEmail; const user = getUser(); delete user.profile.name; delete user.auth.local.email; - delete user.auth.facebook.emails; delete user.auth.apple.emails; const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']); @@ -103,7 +80,6 @@ describe('emails', () => { delete user.profile.name; delete user.auth.local.email; delete user.auth.google.emails; - delete user.auth.facebook.emails; const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']); @@ -118,7 +94,6 @@ describe('emails', () => { const { getUserInfo } = attachEmail; const user = getUser(); delete user.auth.local.email; - delete user.auth.facebook; delete user.auth.google; delete user.auth.apple; diff --git a/test/api/unit/libs/password.test.js b/test/api/unit/libs/password.test.js index 3ca2643b6d..d9f1572ab5 100644 --- a/test/api/unit/libs/password.test.js +++ b/test/api/unit/libs/password.test.js @@ -246,7 +246,7 @@ describe('Password Utilities', () => { it('returns false if the user has no local auth', async () => { const user = await generateUser({ auth: { - facebook: {}, + google: {}, }, }); const res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({ diff --git a/test/api/v3/integration/user/DELETE-user.test.js b/test/api/v3/integration/user/DELETE-user.test.js index 18be9792ce..5c176fe141 100644 --- a/test/api/v3/integration/user/DELETE-user.test.js +++ b/test/api/v3/integration/user/DELETE-user.test.js @@ -289,45 +289,6 @@ describe('DELETE /user', () => { }); }); - context('user with Facebook auth', async () => { - beforeEach(async () => { - user = await generateUser({ - auth: { - facebook: { - id: 'facebook-id', - }, - }, - }); - }); - - it('returns an error if confirmation phrase is wrong', async () => { - await expect(user.del('/user', { - password: 'just-do-it', - })).to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('incorrectDeletePhrase', { magicWord: 'DELETE' }), - }); - }); - - it('returns an error if confirmation phrase is not supplied', async () => { - await expect(user.del('/user', { - password: '', - })).to.eventually.be.rejected.and.eql({ - code: 400, - error: 'BadRequest', - message: t('missingPassword'), - }); - }); - - it('deletes a Facebook user', async () => { - await user.del('/user', { - password: DELETE_CONFIRMATION, - }); - await expect(checkExistence('users', user._id)).to.eventually.eql(false); - }); - }); - context('user with Google auth', async () => { beforeEach(async () => { user = await generateUser({ diff --git a/test/api/v3/integration/user/auth/DELETE-user_auth_social_network.test.js b/test/api/v3/integration/user/auth/DELETE-user_auth_social_network.test.js index 12bef95459..522a49d369 100644 --- a/test/api/v3/integration/user/auth/DELETE-user_auth_social_network.test.js +++ b/test/api/v3/integration/user/auth/DELETE-user_auth_social_network.test.js @@ -20,44 +20,6 @@ describe('DELETE social registration', () => { }); }); - context('Facebook', () => { - it('fails if user does not have an alternative registration method', async () => { - await user.update({ - 'auth.facebook.id': 'some-fb-id', - 'auth.local': { ok: true }, - }); - await expect(user.del('/user/auth/social/facebook')).to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('cantDetachSocial'), - }); - }); - - it('succeeds if user has a local registration', async () => { - await user.update({ - 'auth.facebook.id': 'some-fb-id', - }); - - const response = await user.del('/user/auth/social/facebook'); - expect(response).to.eql({}); - await user.sync(); - expect(user.auth.facebook).to.be.undefined; - }); - - it('succeeds if user has a google registration', async () => { - await user.update({ - 'auth.facebook.id': 'some-fb-id', - 'auth.google.id': 'some-google-id', - 'auth.local': { ok: true }, - }); - - const response = await user.del('/user/auth/social/facebook'); - expect(response).to.eql({}); - await user.sync(); - expect(user.auth.facebook).to.be.undefined; - }); - }); - context('Google', () => { it('fails if user does not have an alternative registration method', async () => { await user.update({ @@ -81,19 +43,6 @@ describe('DELETE social registration', () => { await user.sync(); expect(user.auth.google).to.be.undefined; }); - - it('succeeds if user has a facebook registration', async () => { - await user.update({ - 'auth.google.id': 'some-google-id', - 'auth.facebook.id': 'some-facebook-id', - 'auth.local': { ok: true }, - }); - - const response = await user.del('/user/auth/social/google'); - expect(response).to.eql({}); - await user.sync(); - expect(user.auth.goodl).to.be.undefined; - }); }); context('Apple', () => { @@ -119,18 +68,5 @@ describe('DELETE social registration', () => { await user.sync(); expect(user.auth.apple).to.be.undefined; }); - - it('succeeds if user has a facebook registration', async () => { - await user.update({ - 'auth.apple.id': 'some-apple-id', - 'auth.facebook.id': 'some-facebook-id', - 'auth.local': { ok: true }, - }); - - const response = await user.del('/user/auth/social/apple'); - expect(response).to.eql({}); - await user.sync(); - expect(user.auth.goodl).to.be.undefined; - }); }); }); diff --git a/test/api/v3/integration/user/auth/POST-user_auth_social.test.js b/test/api/v3/integration/user/auth/POST-user_auth_social.test.js index c4ffe57efb..df976d0987 100644 --- a/test/api/v3/integration/user/auth/POST-user_auth_social.test.js +++ b/test/api/v3/integration/user/auth/POST-user_auth_social.test.js @@ -12,7 +12,6 @@ describe('POST /user/auth/social', () => { let user; const endpoint = '/user/auth/social'; let randomAccessToken = '123456'; - let randomFacebookId = 'facebookId'; let randomGoogleId = 'googleId'; let network = 'NoNetwork'; @@ -33,146 +32,6 @@ describe('POST /user/auth/social', () => { }); }); - describe('facebook', () => { - beforeEach(async () => { - randomFacebookId = generateUUID(); - const expectedResult = { - id: randomFacebookId, - displayName: 'a facebook user', - emails: [ - { value: `${user.auth.local.username}+facebook@example.com` }, - ], - }; - sandbox.stub(passport._strategies.facebook, 'userProfile').yields(null, expectedResult); - network = 'facebook'; - }); - - afterEach(async () => { - passport._strategies.facebook.userProfile.restore(); - }); - - it('registers a new user', async () => { - const response = await api.post(endpoint, { - authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase - network, - }); - - expect(response.apiToken).to.exist; - expect(response.id).to.exist; - expect(response.newUser).to.be.true; - expect(response.username).to.exist; - - await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a facebook user'); - await expect(getProperty('users', response.id, 'auth.local.lowerCaseUsername')).to.exist; - await expect(getProperty('users', response.id, 'auth.local.email')).to.eventually.equal(`${user.auth.local.username}+facebook@example.com`); - await expect(getProperty('users', response.id, 'auth.facebook.id')).to.eventually.equal(randomFacebookId); - }); - - it('logs an existing user in', async () => { - const registerResponse = await api.post(endpoint, { - authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase - network, - }); - - const response = await api.post(endpoint, { - authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase - network, - }); - - expect(response.apiToken).to.eql(registerResponse.apiToken); - expect(response.id).to.eql(registerResponse.id); - expect(response.newUser).to.be.false; - expect(registerResponse.newUser).to.be.true; - }); - - it('logs an existing user in if they have local auth with matching email', async () => { - passport._strategies.facebook.userProfile.restore(); - const expectedResult = { - id: randomFacebookId, - displayName: 'a facebook user', - emails: [ - { value: user.auth.local.email }, - ], - }; - sandbox.stub(passport._strategies.facebook, 'userProfile').yields(null, expectedResult); - - const response = await api.post(endpoint, { - authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase - network, - }); - - expect(response.apiToken).to.eql(user.apiToken); - expect(response.id).to.eql(user._id); - expect(response.newUser).to.be.false; - }); - - it('logs an existing user into their social account if they have local auth with matching email', 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.facebook.userProfile.restore(); - const expectedResult = { - id: randomFacebookId, - displayName: 'a facebook user', - emails: [ - { value: user.auth.local.email }, - ], - }; - sandbox.stub(passport._strategies.facebook, 'userProfile').yields(null, expectedResult); - - const response = await api.post(endpoint, { - authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase - network, - }); - - 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 - network, - }); - - expect(response.apiToken).to.eql(user.apiToken); - expect(response.id).to.eql(user._id); - expect(response.newUser).to.be.false; - }); - - it('does not log into other account if social auth already exists', async () => { - const registerResponse = await api.post(endpoint, { - authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase - network, - }); - expect(registerResponse.newUser).to.be.true; - - await expect(user.post(endpoint, { - authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase - network, - })).to.eventually.be.rejected.and.eql({ - code: 401, - error: 'NotAuthorized', - message: t('socialAlreadyExists'), - }); - }); - - xit('enrolls a new user in an A/B test', async () => { - await api.post(endpoint, { - authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase - network, - }); - - await expect(getProperty('users', user._id, '_ABtests')).to.eventually.be.a('object'); - }); - }); - describe('google', () => { beforeEach(async () => { randomGoogleId = generateUUID(); diff --git a/test/api/v3/integration/user/auth/POST-user_reset_password.test.js b/test/api/v3/integration/user/auth/POST-user_reset_password.test.js index 8603fa7cb3..da66e5fd3e 100644 --- a/test/api/v3/integration/user/auth/POST-user_reset_password.test.js +++ b/test/api/v3/integration/user/auth/POST-user_reset_password.test.js @@ -25,6 +25,19 @@ describe('POST /user/reset-password', async () => { expect(user.auth.local.hashed_password).to.not.eql(previousPassword); }); + it('resets password for social users', async () => { + const email = `${user.auth.local.username}+google@example.com`; + await user.update({ 'auth.google.emails': [{ value: email }] }); + await user.sync(); + const previousPassword = user.auth.local.passwordResetCode; + const response = await user.post(endpoint, { + email, + }); + expect(response).to.eql({ data: {}, message: t('passwordReset') }); + await user.sync(); + expect(user.auth.local.passwordResetCode).to.not.eql(previousPassword); + }); + it('same message on error as on success', async () => { const response = await user.post(endpoint, { email: 'nonExistent@email.com', diff --git a/website/client/src/assets/svg/facebook.svg b/website/client/src/assets/svg/facebook.svg index 3150f4d0f9..02315b15bf 100644 --- a/website/client/src/assets/svg/facebook.svg +++ b/website/client/src/assets/svg/facebook.svg @@ -1,3 +1,3 @@ - - + + diff --git a/website/client/src/assets/svg/instagram.svg b/website/client/src/assets/svg/instagram.svg index 80ff7254f5..c5ee2c2447 100644 --- a/website/client/src/assets/svg/instagram.svg +++ b/website/client/src/assets/svg/instagram.svg @@ -1,3 +1,3 @@ - - + + diff --git a/website/client/src/assets/svg/tumblr.svg b/website/client/src/assets/svg/tumblr.svg new file mode 100644 index 0000000000..4e8eabc27e --- /dev/null +++ b/website/client/src/assets/svg/tumblr.svg @@ -0,0 +1,3 @@ + + + diff --git a/website/client/src/assets/svg/twitter.svg b/website/client/src/assets/svg/twitter.svg index c48698bcdb..b477dbb472 100644 --- a/website/client/src/assets/svg/twitter.svg +++ b/website/client/src/assets/svg/twitter.svg @@ -1,3 +1,3 @@ - - + + diff --git a/website/client/src/components/appFooter.vue b/website/client/src/components/appFooter.vue index c3e0b81ac0..963cd6d9d9 100644 --- a/website/client/src/components/appFooter.vue +++ b/website/client/src/components/appFooter.vue @@ -1,487 +1,792 @@