Files
habitica/test/api/v4/user/auth/PUT-user_update_username.test.js
Phillip Thelen ebf3b4aa47 Username announcement (#10729)
* Change update username API call

The call no longer requires a password and also validates the username.

* Implement API call to verify username without setting it

* Improve coding style

* Apply username verification to registration

* Update error messages

* Validate display names.

* Fix API early Stat Point allocation (#10680)

* Refactor hasClass check to common so it can be used in shared & server-side code

* Check that user has selected class before allocating stat points

* chore(event): end Ember Hatching Potions

* chore(analytics): reenable navigation tracking

* update bcrypt

* Point achievement modal links to main site (#10709)

* Animal ears after death (#10691)

* Animal Ears purchasable with Gold if lost in Death

* remove ears from pinned items when set is bought

* standardise css and error handling for gems and coins

* revert accidental new line

* fix client tests

* Reduce margin-bottom of checklist-item from 10px to -3px. (#10684)

* chore(i18n): update locales

* 4.61.1

* feat(content): Subscriber Items and Magic Potions

* chore(sprites): compile

* chore(i18n): update locales

* 4.62.0

* Display notification for users to confirm their username

* fix typo

* WIP(usernames): Changes to address #10694

* WIP(usernames): Further changes for #10694

* fix(usernames): don't show spurious headings

* Change verify username notification to new version

* Improve feedback for invalid usernames

* Allow user to set their username again to confirm it

* Improve validation display for usernames

* Temporarily move display name validation outside of schema

* Improve rendering banner about sleeping in the inn

See #10695

* Display settings in one column

* Position inn banner when window is resized

* Update inn banner handling

* Fix banner offset on initial load

* Fix minor issues.

* Issue: 10660 - Fixed. Changed default to Please Enter A Value (#10718)

* Issue: 10660 - Fixed. Changed default to Please Enter A Value

* Issue: 10660 - Fixed/revision 2 Changed default to Enter A Value

* chore(news): Bailey announcements

* chore(i18n): update locales

* 4.62.1

* adjust wiki link for usernameInfo string

https://github.com/HabitRPG/habitica-private/issues/7#issuecomment-425405425

* raise coverage for tasks api calls (#10029)

* - updates a group task - approval is required
- updates a group task with checklist

* add expect to test the new checklist length

* - moves tasks to a specified position out of length

* remove unused line

* website getter tasks tests

* re-add sanitizeUserChallengeTask

* change config.json.example variable to be a string not a boolean

* fix tests - pick the text / up/down props too

* fix test - remove changes on text/up/down - revert sanitize condition - revert sanitization props

* Change update username API call

The call no longer requires a password and also validates the username.

* feat(content): Subscriber Items and Magic Potions

* Re-add register call

* Fix merge issue

* Fix issue with setting username

* Implement new alert style

* Display username confirmation status in settings

* Add disclaimer to change username field

* validate username in settings

* Allow specific fields to be focused when opening site settings

* Implement requested changes.

* Fix merge issue

* Fix failing tests

* verify username when users register with username and password

* Set ID for change username notification

* Disable submit button if username is invalid

* Improve username confirmation handling

* refactor(settings): address remaining code comments on auth form

* Revert "refactor(settings): address remaining code comments on auth form"

This reverts commit 9b6609ad64.

* Social user username (#10620)

* Refactored private functions to library

* Refactored social login code

* Added username to social registration

* Changed id library

* Added new local auth check

* Fixed export error. Fixed password check error

* fix(settings): password not available on client

* refactor(settings): more sensible placement of methods

* chore(migration): script to hand out procgen usernames

* fix(migration): don't give EVERYONE new names you doofus

* fix(migration): limit data retrieved, be extra careful about updates

* fix(migration): use missing field, not migration tag, for query

* fix(migration): unused var

* fix(usernames): only generate 20 characters

* fix(migration): set lowerCaseUsername
2018-10-02 16:17:06 -05:00

225 lines
7.2 KiB
JavaScript

import {
generateUser,
translate as t,
} from '../../../../helpers/api-integration/v4';
import {
bcryptCompare,
sha1MakeSalt,
sha1Encrypt as sha1EncryptPassword,
} from '../../../../../website/server/libs/password';
const ENDPOINT = '/user/auth/update-username';
describe('PUT /user/auth/update-username', async () => {
let user;
let password = 'password'; // from habitrpg/test/helpers/api-integration/v4/object-generators.js
beforeEach(async () => {
user = await generateUser();
});
it('successfully changes username with password', async () => {
let newUsername = 'new-username';
let response = await user.put(ENDPOINT, {
username: newUsername,
password,
});
expect(response).to.eql({ username: newUsername });
await user.sync();
expect(user.auth.local.username).to.eql(newUsername);
});
it('successfully changes username without password', async () => {
let newUsername = 'new-username-nopw';
let response = await user.put(ENDPOINT, {
username: newUsername,
});
expect(response).to.eql({ username: newUsername });
await user.sync();
expect(user.auth.local.username).to.eql(newUsername);
});
it('successfully changes username containing number and underscore', async () => {
let newUsername = 'new_username9';
let response = await user.put(ENDPOINT, {
username: newUsername,
});
expect(response).to.eql({ username: newUsername });
await user.sync();
expect(user.auth.local.username).to.eql(newUsername);
});
it('sets verifiedUsername when changing username', async () => {
user.flags.verifiedUsername = false;
await user.sync();
let newUsername = 'new-username-verify';
let response = await user.put(ENDPOINT, {
username: newUsername,
});
expect(response).to.eql({ username: newUsername });
await user.sync();
expect(user.flags.verifiedUsername).to.eql(true);
});
it('converts user with SHA1 encrypted password to bcrypt encryption', async () => {
let myNewUsername = 'my-new-username';
let textPassword = 'mySecretPassword';
let salt = sha1MakeSalt();
let sha1HashedPassword = sha1EncryptPassword(textPassword, salt);
await user.update({
'auth.local.hashed_password': sha1HashedPassword,
'auth.local.passwordHashMethod': 'sha1',
'auth.local.salt': salt,
});
await user.sync();
expect(user.auth.local.passwordHashMethod).to.equal('sha1');
expect(user.auth.local.salt).to.equal(salt);
expect(user.auth.local.hashed_password).to.equal(sha1HashedPassword);
// update email
let response = await user.put(ENDPOINT, {
username: myNewUsername,
password: textPassword,
});
expect(response).to.eql({ username: myNewUsername });
await user.sync();
expect(user.auth.local.username).to.eql(myNewUsername);
expect(user.auth.local.passwordHashMethod).to.equal('bcrypt');
expect(user.auth.local.salt).to.be.undefined;
expect(user.auth.local.hashed_password).not.to.equal(sha1HashedPassword);
let isValidPassword = await bcryptCompare(textPassword, user.auth.local.hashed_password);
expect(isValidPassword).to.equal(true);
});
context('errors', async () => {
it('prevents username update if new username is already taken', async () => {
let existingUsername = 'existing-username';
await generateUser({'auth.local.username': existingUsername, 'auth.local.lowerCaseUsername': existingUsername });
await expect(user.put(ENDPOINT, {
username: existingUsername,
password,
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('usernameTaken'),
});
});
it('errors if password is wrong', async () => {
let newUsername = 'new-username';
await expect(user.put(ENDPOINT, {
username: newUsername,
password: 'wrong-password',
})).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('wrongPassword'),
});
});
it('errors if new username is not provided', async () => {
await expect(user.put(ENDPOINT, {
password,
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('invalidReqParams'),
});
});
it('errors if new username is a slur', async () => {
await expect(user.put(ENDPOINT, {
username: 'TESTPLACEHOLDERSLURWORDHERE',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
});
});
it('errors if new username contains a slur', async () => {
await expect(user.put(ENDPOINT, {
username: 'TESTPLACEHOLDERSLURWORDHERE_otherword',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
});
await expect(user.put(ENDPOINT, {
username: 'something_TESTPLACEHOLDERSLURWORDHERE',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
});
await expect(user.put(ENDPOINT, {
username: 'somethingTESTPLACEHOLDERSLURWORDHEREotherword',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: [t('usernameIssueLength'), t('usernameIssueSlur')].join(' '),
});
});
it('errors if new username is not allowed', async () => {
await expect(user.put(ENDPOINT, {
username: 'support',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('usernameIssueForbidden'),
});
});
it('errors if new username is not allowed regardless of casing', async () => {
await expect(user.put(ENDPOINT, {
username: 'SUppORT',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('usernameIssueForbidden'),
});
});
it('errors if username has incorrect length', async () => {
await expect(user.put(ENDPOINT, {
username: 'thisisaverylongusernameover20characters',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('usernameIssueLength'),
});
});
it('errors if new username contains invalid characters', async () => {
await expect(user.put(ENDPOINT, {
username: 'Eichhörnchen',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('usernameIssueInvalidCharacters'),
});
await expect(user.put(ENDPOINT, {
username: 'test.name',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('usernameIssueInvalidCharacters'),
});
await expect(user.put(ENDPOINT, {
username: '🤬',
})).to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('usernameIssueInvalidCharacters'),
});
});
});
});