2ee2b05d1c
* feat(security): implement CSP * fix(csp): update helmet version to latest * Squashed commit of the following: commit cc6a35e61db07759c1f32716185543bc48bce760 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 17:27:50 2025 -0600 fix(CSP): more Amazon domains commit 985b86c29af866b2df942c21217d99390a2c6e92 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 17:18:08 2025 -0600 fix(csp): more loggly allowance commit 166bd315272f9c3a42652f3a026c27a88ed1a549 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 17:12:00 2025 -0600 fix(csp): data, inline, some refactoring commit 1a0a6c1806a53d43a7199bb2ef72cff610e908be Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 17:05:44 2025 -0600 fix(CSP): override default script-src commit 023d9886c835989da9c5901c168d66b572097dcf Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:56:24 2025 -0600 fix(CSP): unsafe-eval in default-src commit f51f0a0c93b60dfec7ce02be0ecd2587fc882fe0 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:52:14 2025 -0600 fix(CSP): move trusted list to default-src commit 83b2ba7688dea38abb651cf5c27482a7a3648374 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:38:05 2025 -0600 fix(CSP): explicit habitica/aws in script-src commit d5ca5172d5ad2fd8cec9402d7d2c9452c6ece7a1 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:31:38 2025 -0600 fix(CSP): need escaped single quotes commit c677a1ffeff5793b6da228924e68d2c2e47794b2 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:27:46 2025 -0600 fix(CSP): unsafe-eval commit 6ef35c3f7281c8426d9c333686be6bb65f00b3a8 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 16:15:07 2025 -0600 fix(CSP): might need to skip entirely in dev but try no 'self' commit 5759fb37d82fa61b474f01e9ce5e2dc461f6ceba Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 12 15:51:26 2025 -0600 fix(csp): permit AWS in default-src commit 9f238abf9373bc29715657b945b2247ef23c9224 Author: Kalista Payne <kalista@habitica.com> Date: Fri Dec 5 17:22:25 2025 -0600 fix(csp): update helmet version to latest commit 9462e90f4f3058f4014137b3178b9751c5280e97 Author: Kalista Payne <kalista@habitica.com> Date: Tue Nov 25 09:27:05 2025 -0600 feat(security): implement CSP commit72539f9ba3Author: Kalista Payne <kalista@habitica.com> Date: Wed Dec 10 14:16:53 2025 -0600 5.42.2 commitdabd466719Author: Kalista Payne <kalista@habitica.com> Date: Wed Dec 10 14:16:48 2025 -0600 Revert "Chat optimization (#15545)" This reverts commit2917955ef0. commit8bf2304330Author: Kalista Payne <kalista@habitica.com> Date: Wed Dec 10 14:15:48 2025 -0600 chore(event): G1G1 date tweaks commit6937dc4e4eAuthor: Kalista Payne <kalista@habitica.com> Date: Mon Dec 8 16:37:04 2025 -0600 fix(subscription): couple more layout tweaks * fix(csp): move unsafe-eval to default? ig? * Revert "fix(csp): move unsafe-eval to default? ig?" This reverts commit 90476cbf6c59132024d4fbaeceb7cdb19a876303. * fix(security): no unsafe! yay! * fix(packages): remove webpack * fix(lint): object destructuring * fix(csp): remove Vue-Fragment * wip(i18n): load Moment locale from cache * fix(gulp): remove unneeded cache task * fix(i18n): add Moment weekday abbrevs to translations * fix(lint): destructuring ...why is this happening here and not develop lol * fix(csp): add amplitude to whitelist --------- Co-authored-by: Phillip Thelen <phillip@thelen.space>
230 lines
5.2 KiB
Vue
230 lines
5.2 KiB
Vue
<template>
|
|
<div class="d-content">
|
|
<tr
|
|
v-if="!mixinData.inlineSettingMixin.modalVisible"
|
|
>
|
|
<td class="settings-label">
|
|
{{ $t("displayName") }}
|
|
</td>
|
|
<td class="settings-value">
|
|
{{ user.profile.name }}
|
|
</td>
|
|
<td class="settings-button">
|
|
<a
|
|
class="edit-link"
|
|
@click.prevent="openModal()"
|
|
>
|
|
{{ $t('edit') }}
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
<tr
|
|
v-if="mixinData.inlineSettingMixin.modalVisible"
|
|
class="expanded"
|
|
>
|
|
<td colspan="3">
|
|
<div
|
|
v-once
|
|
class="dialog-title"
|
|
>
|
|
{{ $t("displayName") }}
|
|
</div>
|
|
<div
|
|
v-once
|
|
class="dialog-disclaimer"
|
|
>
|
|
{{ $t("changeDisplayNameDisclaimer") }}
|
|
</div>
|
|
|
|
<div class="input-area">
|
|
<div class="settings-label">
|
|
{{ $t("displayName") }}
|
|
</div>
|
|
<div
|
|
class="form"
|
|
name="changeDisplayName"
|
|
novalidate="novalidate"
|
|
>
|
|
<div class="form-group">
|
|
<input
|
|
id="changeDisplayname"
|
|
v-model="temporaryDisplayName"
|
|
class="form-control"
|
|
type="text"
|
|
:placeholder="$t('newDisplayName')"
|
|
:class="{'is-invalid input-invalid': displayNameInvalid}"
|
|
@keyup="valuesChanged()"
|
|
>
|
|
<div
|
|
v-if="displayNameIssues.length > 0"
|
|
class="mb-3"
|
|
>
|
|
<div
|
|
v-for="issue in displayNameIssues"
|
|
:key="issue"
|
|
class="input-error"
|
|
>
|
|
{{ issue }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<save-cancel-buttons
|
|
:disable-save="displayNameCannotSubmit"
|
|
@saveClicked="changeDisplayName(temporaryDisplayName)"
|
|
@cancelClicked="requestCloseModal()"
|
|
/>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '@/assets/scss/colors.scss';
|
|
|
|
.input-group {
|
|
position: relative;
|
|
background: white;
|
|
}
|
|
|
|
input {
|
|
margin-right: 2rem;
|
|
}
|
|
|
|
.input-floating-checkmark {
|
|
position: absolute;
|
|
background: none !important;
|
|
right: 0.5rem;
|
|
top: 0.5rem;
|
|
|
|
width: 1rem;
|
|
height: 1rem;
|
|
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.input-group.is-valid {
|
|
border-color: $green-10 !important;
|
|
}
|
|
|
|
.input-group:not(.is-valid) {
|
|
.check-icon {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
.check-icon {
|
|
width: 12px;
|
|
height: 10px;
|
|
color: $green-50;
|
|
}
|
|
|
|
.form-group {
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
</style>
|
|
|
|
<script>
|
|
import axios from 'axios';
|
|
import isEmail from 'validator/es/lib/isEmail';
|
|
import debounce from 'lodash/debounce';
|
|
import { mapState } from '@/libs/store';
|
|
|
|
import checkIcon from '@/assets/svg/check.svg?raw';
|
|
import SaveCancelButtons from '../components/saveCancelButtons.vue';
|
|
import { InlineSettingMixin } from '../components/inlineSettingMixin';
|
|
import NotificationMixins from '@/mixins/notifications';
|
|
|
|
export default {
|
|
components: { SaveCancelButtons },
|
|
mixins: [InlineSettingMixin, NotificationMixins],
|
|
data () {
|
|
return {
|
|
temporaryDisplayName: '',
|
|
inputChanged: false,
|
|
displayNameIssues: [],
|
|
updates: {
|
|
newEmail: '',
|
|
password: '',
|
|
},
|
|
icons: Object.freeze({
|
|
checkIcon,
|
|
}),
|
|
};
|
|
},
|
|
computed: {
|
|
...mapState({
|
|
user: 'user.data',
|
|
}),
|
|
validEmail () {
|
|
return isEmail(this.updates.newEmail);
|
|
},
|
|
allowedToSave () {
|
|
return !this.validEmail || this.updates.password.length === 0;
|
|
},
|
|
displayNameInvalid () {
|
|
if (this.temporaryDisplayName.length <= 1) {
|
|
return true;
|
|
}
|
|
|
|
return this.displayNameIssues.length !== 0;
|
|
},
|
|
displayNameCannotSubmit () {
|
|
return this.displayNameInvalid || !this.inputChanged;
|
|
},
|
|
},
|
|
watch: {
|
|
temporaryDisplayName: {
|
|
handler () {
|
|
this.validateDisplayName(this.temporaryDisplayName);
|
|
},
|
|
deep: true,
|
|
},
|
|
},
|
|
mounted () {
|
|
this.resetControls();
|
|
},
|
|
methods: {
|
|
/**
|
|
* is a callback from the {InlineSettingMixin}
|
|
* do not remove
|
|
*/
|
|
resetControls () {
|
|
this.temporaryDisplayName = this.user.profile.name;
|
|
},
|
|
async changeDisplayName (newName) {
|
|
await axios.put('/api/v4/user/', { 'profile.name': newName });
|
|
this.text(this.$t('displayNameSuccess'));
|
|
this.user.profile.name = newName;
|
|
this.temporaryDisplayName = newName;
|
|
|
|
this.closeModal();
|
|
},
|
|
validateDisplayName: debounce(async function checkName (displayName) {
|
|
if (displayName.length <= 1 || displayName === this.user.profile.name) {
|
|
this.displayNameIssues = [];
|
|
return;
|
|
}
|
|
const res = await this.$store.dispatch('auth:verifyDisplayName', {
|
|
displayName,
|
|
});
|
|
|
|
if (res.issues !== undefined) {
|
|
this.displayNameIssues = res.issues;
|
|
} else {
|
|
this.displayNameIssues = [];
|
|
}
|
|
}, 500),
|
|
valuesChanged () {
|
|
this.inputChanged = true;
|
|
this.modalValuesChanged();
|
|
},
|
|
},
|
|
};
|
|
</script>
|