mirror of
https://github.com/wassname/talk.git
synced 2026-08-04 13:23:35 +08:00
Merge branch 'gdpr-username' of github.com:coralproject/talk into gdpr-email
* 'gdpr-username' of github.com:coralproject/talk: Adding translations Catching if username cant be updated fixes for tests fixed tests Fixed bug in mutation
This commit is contained in:
@@ -54,6 +54,15 @@ const withProfileQuery = withQuery(
|
||||
me {
|
||||
id
|
||||
username
|
||||
state {
|
||||
status {
|
||||
username {
|
||||
history {
|
||||
created_at
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
...${getDefinitionName(TabPanel.fragments.root)}
|
||||
${getSlotFragmentSpreads(slots, 'root')}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import get from 'lodash/get';
|
||||
import moment from 'moment';
|
||||
|
||||
/**
|
||||
* getReliability
|
||||
@@ -33,3 +34,18 @@ export const isSuspended = user => {
|
||||
export const isBanned = user => {
|
||||
return get(user, 'state.status.banned.status');
|
||||
};
|
||||
|
||||
/**
|
||||
* canUsernameBeUpdated
|
||||
* retrieves boolean whether a username can be updated or not
|
||||
*/
|
||||
|
||||
export const canUsernameBeUpdated = status => {
|
||||
const oldestEditTime = moment()
|
||||
.subtract(14, 'days')
|
||||
.toDate();
|
||||
|
||||
return !status.username.history.some(({ created_at }) =>
|
||||
moment(created_at).isAfter(oldestEditTime)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -14,25 +14,21 @@ module.exports = (user, perm) => {
|
||||
user.password.length > 0
|
||||
);
|
||||
|
||||
case types.CHANGE_USERNAME: {
|
||||
case types.CHANGE_USERNAME:
|
||||
return user.status.username.status === 'REJECTED';
|
||||
|
||||
case types.SET_USERNAME: {
|
||||
// Only users who have their usernames rejected or those users who
|
||||
// not changed their usernames within 14 days can change their usernames.
|
||||
const now = moment();
|
||||
const deadline = moment().subtract(14, 'days');
|
||||
return (
|
||||
user.status.username.status === 'REJECTED' ||
|
||||
get(user, 'status.username.history', [])
|
||||
.filter(({ status }) => status === 'CHANGED')
|
||||
.every(({ created_at }) =>
|
||||
moment(created_at)
|
||||
.add(14, 'days')
|
||||
.isAfter(now)
|
||||
)
|
||||
user.status.username.status === 'UNSET' ||
|
||||
get(user, 'status.username.history', []).every(({ created_at }) =>
|
||||
moment(created_at).isBefore(deadline)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
case types.SET_USERNAME:
|
||||
return user.status.username.status === 'UNSET';
|
||||
|
||||
case types.CREATE_COMMENT:
|
||||
case types.CREATE_ACTION:
|
||||
case types.DELETE_ACTION:
|
||||
|
||||
+15
-3
@@ -19,10 +19,20 @@ class ChangeUsernameContentDialog extends React.Component {
|
||||
confirmChanges = async () => {
|
||||
if (this.formHasError()) {
|
||||
this.showError();
|
||||
} else {
|
||||
await this.props.save();
|
||||
this.props.next();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.props.canUsernameBeUpdated) {
|
||||
this.props.notify(
|
||||
'error',
|
||||
t('talk-plugin-auth.change_username.change_username_attempt')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.props.save();
|
||||
this.props.next();
|
||||
this.props.closeDialog();
|
||||
};
|
||||
|
||||
formHasError = () =>
|
||||
@@ -97,6 +107,8 @@ ChangeUsernameContentDialog.propTypes = {
|
||||
onChange: PropTypes.func,
|
||||
formData: PropTypes.object,
|
||||
username: PropTypes.string,
|
||||
canUsernameBeUpdated: PropTypes.bool.isRequired,
|
||||
notify: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ChangeUsernameContentDialog;
|
||||
|
||||
@@ -11,6 +11,7 @@ import errorMsj from 'coral-framework/helpers/error';
|
||||
import ConfirmChangesDialog from './ConfirmChangesDialog';
|
||||
import ChangeUsernameContentDialog from './ChangeUsernameContentDialog';
|
||||
import ChangeEmailContentDialog from './ChangeEmailContentDialog';
|
||||
import { canUsernameBeUpdated } from 'coral-framework/utils/user';
|
||||
|
||||
const initialState = {
|
||||
editing: false,
|
||||
@@ -120,13 +121,10 @@ class Profile extends React.Component {
|
||||
|
||||
saveUsername = async () => {
|
||||
const { newUsername } = this.state.formData;
|
||||
const { id } = this.props;
|
||||
const { setUsername } = this.props;
|
||||
|
||||
try {
|
||||
await this.props.changeUsername({
|
||||
id,
|
||||
username: newUsername,
|
||||
});
|
||||
await setUsername(newUsername);
|
||||
this.props.notify(
|
||||
'success',
|
||||
t('talk-plugin-auth.change_username.changed_username_success_msg')
|
||||
@@ -159,8 +157,13 @@ class Profile extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { username, emailAddress } = this.props;
|
||||
const { editing, formData } = this.state;
|
||||
const {
|
||||
username,
|
||||
emailAddress,
|
||||
root: { me: { state: { status } } },
|
||||
notify,
|
||||
} = this.props;
|
||||
const { editing, formData, showDialog } = this.state;
|
||||
|
||||
return (
|
||||
<section
|
||||
@@ -169,12 +172,14 @@ class Profile extends React.Component {
|
||||
})}
|
||||
>
|
||||
<ConfirmChangesDialog
|
||||
showDialog={this.state.showDialog}
|
||||
showDialog={showDialog}
|
||||
closeDialog={this.closeDialog}
|
||||
finish={this.finish}
|
||||
>
|
||||
{username !== formData.newUsername && (
|
||||
<ChangeUsernameContentDialog
|
||||
notify={notify}
|
||||
canUsernameBeUpdated={canUsernameBeUpdated(status)}
|
||||
save={this.saveUsername}
|
||||
onChange={this.onChange}
|
||||
formData={this.state.formData}
|
||||
@@ -260,6 +265,8 @@ class Profile extends React.Component {
|
||||
Profile.propTypes = {
|
||||
updateEmailAddress: PropTypes.func.isRequired,
|
||||
changeUsername: PropTypes.func.isRequired,
|
||||
root: PropTypes.object.isRequired,
|
||||
setUsername: PropTypes.func.isRequired,
|
||||
notify: PropTypes.func.isRequired,
|
||||
username: PropTypes.string,
|
||||
emailAddress: PropTypes.string,
|
||||
|
||||
@@ -142,7 +142,7 @@ en:
|
||||
cancel: "Cancel"
|
||||
edit: "Edit"
|
||||
changed_password_msg: "Changed Password - Your password has been successfully changed"
|
||||
change_username:
|
||||
change_username:
|
||||
change_username_note: "Usernames can be changed every 14 days"
|
||||
save: "Save"
|
||||
edit_profile: "Edit Profile"
|
||||
@@ -165,6 +165,8 @@ en:
|
||||
confirm_change: "Confirm Change"
|
||||
cancel: "Cancel"
|
||||
change_email_msg: "Email Address Changed - Your email address has been successfully changed. This email address will now be used for signing in and email notifications"
|
||||
changed_username_success_msg: "Username Changed - Your username has been successfully changed. You will not be able to change your user name for 14 days."
|
||||
change_username_attempt: "Username can't be updated. Usernames can be changed every 14 days"
|
||||
de:
|
||||
talk-plugin-auth:
|
||||
login:
|
||||
@@ -266,7 +268,7 @@ es:
|
||||
cancel: "Cancelar"
|
||||
edit: "Editar"
|
||||
changed_password_msg: "Contraseña Actualizada - Tu contraseña ha sido exitosamente actualizada"
|
||||
change_username:
|
||||
change_username:
|
||||
change_username_note: "El usuario puede ser cambiado cada 14 días"
|
||||
save: "Guardar"
|
||||
edit_profile: "Editar Perfil"
|
||||
@@ -278,7 +280,8 @@ es:
|
||||
bottom_note: "Nota: No podrás cambiar tu usuario por 14 días"
|
||||
confirm_changes: "Confirmar Cambios"
|
||||
username_does_not_match: "El usuario no coincide"
|
||||
changed_username_success_msg: "Username Changed - Your username has been successfully changed. You will not be able to change your user name for 14 days."
|
||||
changed_username_success_msg: "Usuario Actualizado - Tu usuario ha sido exitosamente actualizado. No podrás cambiar el usuario por 14 días."
|
||||
change_username_attempt: "El usuario no puede ser actualizado. Los usuarios pueden ser cambiados cada 14 días."
|
||||
fr:
|
||||
talk-plugin-auth:
|
||||
login:
|
||||
|
||||
+15
-5
@@ -253,9 +253,19 @@ class Users {
|
||||
},
|
||||
{
|
||||
'status.username.status': { $in: ['APPROVED', 'SET'] },
|
||||
'status.username.history.created_at': {
|
||||
$lte: oldestEditTime,
|
||||
},
|
||||
$or: [
|
||||
{
|
||||
'status.username.history.created_at': {
|
||||
$lte: oldestEditTime,
|
||||
},
|
||||
},
|
||||
{
|
||||
'status.username.history': [],
|
||||
},
|
||||
{
|
||||
'status.username.history': { $exists: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -286,8 +296,8 @@ class Users {
|
||||
|
||||
if (
|
||||
!['UNSET', 'APPROVED', 'SET'].includes(user.status.username.status) ||
|
||||
!user.status.username.history.every(({ created_at }) =>
|
||||
oldestEditTime.isAfter(created_at)
|
||||
user.status.username.history.some(({ created_at }) =>
|
||||
moment(created_at).isAfter(oldestEditTime)
|
||||
)
|
||||
) {
|
||||
throw new ErrPermissionUpdateUsername();
|
||||
|
||||
@@ -89,7 +89,7 @@ describe('graph.mutations.changeUsername', () => {
|
||||
expect(res.data.changeUsername.errors).to.have.length(1);
|
||||
expect(res.data.changeUsername.errors[0]).to.have.property(
|
||||
'translation_key',
|
||||
'EDIT_USERNAME_NOT_AUTHORIZED'
|
||||
'NOT_AUTHORIZED'
|
||||
);
|
||||
|
||||
// Set the user to the desired status.
|
||||
|
||||
@@ -304,63 +304,63 @@ describe('services.UsersService', () => {
|
||||
await UsersService[func](user.id, user.username);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (func === 'setUsername') {
|
||||
it('should let a user set their username from UNSET', async () => {
|
||||
const user = mockUsers[0];
|
||||
if (func === 'setUsername') {
|
||||
it('should let a user set their username from UNSET', async () => {
|
||||
const user = mockUsers[0];
|
||||
|
||||
// Set the user to the desired status.
|
||||
await UsersService.setUsernameStatus(user.id, 'UNSET');
|
||||
await UsersService.setUsername(user.id, 'new_username', null);
|
||||
});
|
||||
|
||||
describe('time based', () => {
|
||||
afterEach(() => {
|
||||
timekeeper.reset();
|
||||
// Set the user to the desired status.
|
||||
await UsersService.setUsernameStatus(user.id, 'UNSET');
|
||||
await UsersService.setUsername(user.id, 'new_username', null);
|
||||
});
|
||||
|
||||
['SET', 'APPROVED'].forEach(status => {
|
||||
it(`should not allow users to change their username if it was changed within 14 of today from ${status}`, async () => {
|
||||
const user = mockUsers[0];
|
||||
describe('time based', () => {
|
||||
afterEach(() => {
|
||||
timekeeper.reset();
|
||||
});
|
||||
|
||||
// Set the user to the desired status.
|
||||
await UsersService.setUsernameStatus(user.id, status);
|
||||
['SET', 'APPROVED'].forEach(status => {
|
||||
it(`should not allow users to change their username if it was changed within 14 of today from ${status}`, async () => {
|
||||
const user = mockUsers[0];
|
||||
|
||||
timekeeper.travel(
|
||||
moment()
|
||||
.add(5, 'days')
|
||||
.toDate()
|
||||
);
|
||||
// Set the user to the desired status.
|
||||
await UsersService.setUsernameStatus(user.id, status);
|
||||
|
||||
try {
|
||||
await UsersService.setUsername(user.id, 'new_username', null);
|
||||
throw new Error('edit was processed successfully');
|
||||
} catch (err) {
|
||||
expect(err).have.property(
|
||||
'translation_key',
|
||||
'EDIT_USERNAME_NOT_AUTHORIZED'
|
||||
timekeeper.travel(
|
||||
moment()
|
||||
.add(5, 'days')
|
||||
.toDate()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it(`allows users to change their username if it was changed 14 days before today from ${status}`, async () => {
|
||||
const user = mockUsers[0];
|
||||
try {
|
||||
await UsersService.setUsername(user.id, 'new_username', null);
|
||||
throw new Error('edit was processed successfully');
|
||||
} catch (err) {
|
||||
expect(err).have.property(
|
||||
'translation_key',
|
||||
'EDIT_USERNAME_NOT_AUTHORIZED'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Set the user to the desired status.
|
||||
await UsersService.setUsernameStatus(user.id, status);
|
||||
it(`allows users to change their username if it was changed 14 days before today from ${status}`, async () => {
|
||||
const user = mockUsers[0];
|
||||
|
||||
timekeeper.travel(
|
||||
moment()
|
||||
.add(15, 'days')
|
||||
.toDate()
|
||||
);
|
||||
// Set the user to the desired status.
|
||||
await UsersService.setUsernameStatus(user.id, status);
|
||||
|
||||
await UsersService.setUsername(user.id, 'new_username', null);
|
||||
timekeeper.travel(
|
||||
moment()
|
||||
.add(15, 'days')
|
||||
.toDate()
|
||||
);
|
||||
|
||||
await UsersService.setUsername(user.id, 'new_username', null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isValidUsername', () => {
|
||||
|
||||
Reference in New Issue
Block a user