diff --git a/src/api/common.js b/src/api/common.js index ae6d4517..dffe3e30 100644 --- a/src/api/common.js +++ b/src/api/common.js @@ -12,6 +12,7 @@ import { getCurrentLangAsV1Format } from 'src/i18n' import { i18n } from 'src/boot/i18n' import saveAs from 'file-saver' +import { PATH_CHANGE_PASSWORD } from 'src/router/routes' export const LIST_DEFAULT_PAGE = 1 export const LIST_DEFAULT_ROWS = 24 export const LIST_ALL_ROWS = 1000 @@ -177,6 +178,11 @@ function handleResponseError (err) { if (code === 403 && message === 'Invalid license') { message = i18n.global.tc('Contact your administrator to activate this functionality') } + if (code === 403 && message === 'Password expired') { + message = i18n.global.tc('Password Expired') + return this.$router.push({ path: PATH_CHANGE_PASSWORD }) + } + if (code !== null && message !== null) { throw new ApiResponseError(code, message) } diff --git a/src/api/user.js b/src/api/user.js index e32484e5..bf3f970e 100644 --- a/src/api/user.js +++ b/src/api/user.js @@ -1,5 +1,6 @@ import _ from 'lodash' +import { i18n } from 'boot/i18n' import { get, post, @@ -51,6 +52,15 @@ export async function loginByExchangeToken (token) { } } +export async function getPreLoginPasswordInfo () { + try { + const res = await httpApi.get('api/platforminfo') + return res.data.security.password + } catch (err) { + throw new Error(err.response.data.message) + } +} + export async function getUserData (id) { const allPromise = Promise.all([ getSubscriberById(id), @@ -167,6 +177,29 @@ export async function getPlatformInfo () { }) } +export function changeExpiredPassword (payload) { + return new Promise((resolve, reject) => { + httpApi.post('/api/passwordchange/', { + new_password: payload.new_password + }, { + auth: { + username: payload.username, + password: payload.old_password + } + }).then((result) => { + resolve(result) + }).catch((err) => { + if (err.response.status === 401) { + reject(`Unauthorized. ${i18n.global.tc('Wrong username or password')}`) + } else if (err.response.status === 422) { + reject(_formatPasswordError(err.response.data.message)) + } else { + reject('Unexpected error') + } + }) + }) +} + export async function createAuthToken (tokenExpiringTime) { const response = await post({ resource: 'authtokens', @@ -177,3 +210,7 @@ export async function createAuthToken (tokenExpiringTime) { }) return response.token } + +function _formatPasswordError (error) { + return error.split("'").slice(-2, -1)[0].replaceAll(',', ', ') +} diff --git a/src/boot/routes.js b/src/boot/routes.js index dcccf37d..a25e9137 100644 --- a/src/boot/routes.js +++ b/src/boot/routes.js @@ -9,7 +9,7 @@ import { export default ({ app, router, store }) => { router.beforeEach((to, from, next) => { - const publicUrls = ['/login', '/recoverpassword'] + const publicUrls = ['/login', '/recoverpassword', '/changepassword'] // not authorized user if (!hasJwt()) { if (!publicUrls.includes(to.path)) { diff --git a/src/i18n/de.json b/src/i18n/de.json index e46d6895..585cd1b3 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -58,6 +58,7 @@ "Block Mode for outbounds calls": "Block Mode for outbounds calls", "Block Outgoing": "Ausgehende Anrufe blockieren", "Block anonymous inbound calls": "Block anonymous inbound calls", + "Block outgoing": "Block outgoing", "Busy Greeting": "Begrüßung, wenn besetzt", "Busy Lamp Field": "Besetztlampenfeld", "CDR": "CDR", @@ -107,7 +108,7 @@ "Conference name": "Konferenzname", "Confirm": "Bestätigen", "Contact": "Kontakt", - "Contact your administrator to activate this functionality":"Zur Aktivierung dieser Funktion kontaktieren Sie Ihren Administrator.", + "Contact your administrator to activate this functionality": "Zur Aktivierung dieser Funktion kontaktieren Sie Ihren Administrator.", "Content": "Inhalt", "Conversations": "Konversationen", "Copy link": "Link kopieren", @@ -119,6 +120,7 @@ "Create Call Queue": "Anrufwarteschlange hinzufügen", "Create Config": "Konfiguration hinzufügen", "Create List": "Liste erstellen", + "Create New Password": "Create New Password", "Create destination": "Ziel erstellen", "Create device": "Gerät hinzufügen", "Create group": "Gruppe hinzufügen", @@ -128,6 +130,7 @@ "Created device {device} successfully": "Gerät {device} hinzugefügt", "Created manager secretary config for {msConfig} successfully": "Konfiguration für {msConfig} hinzugefügt", "Created sound set {soundSet} successfully": "Sound-Set {soundSet} hinzugefügt", + "Current Password": "Current Password", "Custom Announcement": "Benutzerdefinierte Ansage", "Custom Announcements": "Individuelle Ansagen", "Custom sound": "Benutzerdefinierter Sound", @@ -300,6 +303,7 @@ "Ncos Set": "Ncos Set", "Never": "Nie", "New Messages": "Neue Nachrichten", + "New Password": "New Password", "New SIP Password": "Neues SIP-Passwort", "New SIP Password confirm": "Neues SIP-Passwort bestätigen", "New Web Password": "Neues Web-Passwort", @@ -356,10 +360,18 @@ "Parallel Ringing": "Paralleles Klingeln", "Parent": "Eltern", "Password": "Passwort", + "Password Expired": "Password Expired", "Password Retype": "Passwort-Neueingabe", "Password changed successfully": "Passwort erfolgreich geändert", "Password confirm": "Passwort bestätigen", + "Password is considered weak": "Password is considered weak", "Password is not strong enough": "Passwort ist nicht sicher genug", + "Password is not strong enough, add more digits": "Password is not strong enough, add more digits", + "Password is not strong enough, add more lowercase letters": "Password is not strong enough, add more lowercase letters", + "Password is not strong enough, add more special characters": "Password is not strong enough, add more special characters", + "Password is not strong enough, add more uppercase letters": "Password is not strong enough, add more uppercase letters", + "Password must be at least {max} characters long": "Password must be at least {max} characters long", + "Password must be at least {min} characters long": "Password must be at least {min} characters long", "Passwords must be equal": "Passwörter müssen übereinstimmen", "Phone model": "Telefonmodell", "Phone number": "Rufnummer", @@ -602,6 +614,7 @@ "Your number is visible to the callee": "Rufnummer wird dem Angerufenen angezeigt", "Your number is visible to the callee within own PBX": "Ihre Rufnummer wird dem Angerufenen bei PBX-internen Verbindungen angezeigt", "Your password has been changed successfully": "Ihr Passwort wurde erfolgreich geändert", + "Your password has expired": "Your password has expired", "Your web password has been changed successfully": "Ihr Web-Passwort wurde erfolgreich geändert", "ago": "vor", "and": "und", diff --git a/src/i18n/en.json b/src/i18n/en.json index 7226aaf3..a63caf11 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -57,6 +57,7 @@ "Block Mode for outbounds calls": "Block Mode for outbounds calls", "Block Outgoing": "Block Outgoing", "Block anonymous inbound calls": "Block anonymous inbound calls", + "Block outgoing": "Block outgoing", "Busy Greeting": "Busy Greeting", "Busy Lamp Field": "Busy Lamp Field", "CDR": "CDR", @@ -105,7 +106,7 @@ "Conference": "Conference", "Confirm": "Confirm", "Contact": "Contact", - "Contact your administrator to activate this functionality":"Contact your administrator to activate this functionality", + "Contact your administrator to activate this functionality": "Contact your administrator to activate this functionality", "Content": "Content", "Conversations": "Conversations", "Cost": "Cost", @@ -116,6 +117,7 @@ "Create Call Queue": "Create Call Queue", "Create Config": "Create Config", "Create List": "Create List", + "Create New Password": "Create New Password", "Create destination": "Create destination", "Create device": "Create device", "Create group": "Create group", @@ -125,6 +127,7 @@ "Created device {device} successfully": "Created device {device} successfully", "Created manager secretary config for {msConfig} successfully": "Created manager secretary config for {msConfig} successfully", "Created sound set {soundSet} successfully": "Created sound set {soundSet} successfully", + "Current Password": "Current Password", "Custom Announcement": "Custom Announcement", "Custom Announcements": "Custom Announcements", "Custom sound": "Custom sound", @@ -288,6 +291,7 @@ "Ncos": "Ncos", "Ncos Set": "Ncos Set", "Never": "Never", + "New Password": "New Password", "New SIP Password": "New SIP Password", "New SIP Password confirm": "New SIP Password confirm", "New Web Password": "New Web Password", @@ -342,10 +346,18 @@ "Parallel Ringing": "Parallel Ringing", "Parent": "Parent", "Password": "Password", + "Password Expired": "Password Expired", "Password Retype": "Password Retype", "Password changed successfully": "Password changed successfully", "Password confirm": "Password confirm", + "Password is considered weak": "Password is considered weak", "Password is not strong enough": "Password is not strong enough", + "Password is not strong enough, add more digits": "Password is not strong enough, add more digits", + "Password is not strong enough, add more lowercase letters": "Password is not strong enough, add more lowercase letters", + "Password is not strong enough, add more special characters": "Password is not strong enough, add more special characters", + "Password is not strong enough, add more uppercase letters": "Password is not strong enough, add more uppercase letters", + "Password must be at least {max} characters long": "Password must be at least {max} characters long", + "Password must be at least {min} characters long": "Password must be at least {min} characters long", "Passwords must be equal": "Passwords must be equal", "Phone model": "Phone model", "Phone number": "Phone number", @@ -581,6 +593,7 @@ "Your number is hidden to the callee within own PBX": "Your number is hidden to the callee within own PBX", "Your number is visible to the callee": "Your number is visible to the callee", "Your number is visible to the callee within own PBX": "Your number is visible to the callee within own PBX", + "Your password has expired": "Your password has expired", "ago": "ago", "and": "and", "and call from": "and call from", diff --git a/src/i18n/es.json b/src/i18n/es.json index dfe5e448..7508893a 100644 --- a/src/i18n/es.json +++ b/src/i18n/es.json @@ -58,6 +58,7 @@ "Block Mode for outbounds calls": "Block Mode for outbounds calls", "Block Outgoing": "Bloquear Salientes", "Block anonymous inbound calls": "Block anonymous inbound calls", + "Block outgoing": "Block outgoing", "Busy Greeting": "Saludo de Ocupado", "Busy Lamp Field": "Campo de lámpara ocupado", "CDR": "CDR", @@ -107,7 +108,7 @@ "Conference name": "Nombre de la conferencia", "Confirm": "Confirmar", "Contact": "Contacto", - "Contact your administrator to activate this functionality":"Póngase en contacto con su administrador para activar esta funcionalidad", + "Contact your administrator to activate this functionality": "Póngase en contacto con su administrador para activar esta funcionalidad", "Content": "Contenido", "Conversations": "Conversaciones", "Copy link": "Copiar enlace", @@ -119,6 +120,7 @@ "Create Call Queue": "Crear Cola de Llamadas", "Create Config": "Crear Configuración", "Create List": "Crear Lista", + "Create New Password": "Create New Password", "Create destination": "Crear destino", "Create device": "Crear dispositivo", "Create group": "Crear grupo", @@ -128,6 +130,7 @@ "Created device {device} successfully": "Dispositivo {device} creado exitosamente", "Created manager secretary config for {msConfig} successfully": "Se creó exitosamente la configuración de Jefe-Asistente para {msConfig}", "Created sound set {soundSet} successfully": "Conjunto de sonido {soundSet} creado exitosamente", + "Current Password": "Current Password", "Custom Announcement": "Anuncio Personalizado", "Custom Announcements": "Anuncios Personalizados", "Custom sound": "Sonido personalizado", @@ -301,6 +304,7 @@ "Ncos Set": "Ncos Set", "Never": "Nunca", "New Messages": "Nuevos Mensajes", + "New Password": "New Password", "New SIP Password": "New SIP Password", "New SIP Password confirm": "New SIP Password confirm", "New Web Password": "New Web Password", @@ -355,10 +359,18 @@ "Parallel Ringing": "Llamada en paralelo", "Parent": "Parent", "Password": "Contraseña", + "Password Expired": "Password Expired", "Password Retype": "Repetir contraseña", "Password changed successfully": "La contraseña ha sido cambiada correctamente.", "Password confirm": "Password confirm", + "Password is considered weak": "Password is considered weak", "Password is not strong enough": "La contraseña no es lo suficientemente fuerte", + "Password is not strong enough, add more digits": "Password is not strong enough, add more digits", + "Password is not strong enough, add more lowercase letters": "Password is not strong enough, add more lowercase letters", + "Password is not strong enough, add more special characters": "Password is not strong enough, add more special characters", + "Password is not strong enough, add more uppercase letters": "Password is not strong enough, add more uppercase letters", + "Password must be at least {max} characters long": "Password must be at least {max} characters long", + "Password must be at least {min} characters long": "Password must be at least {min} characters long", "Passwords must be equal": "Las contraseñas deben ser iguales", "Phone model": "Modelo de teléfono", "Phone number": "Número de teléfono", @@ -606,6 +618,7 @@ "Your number is visible to the callee": "Su número es visible para la persona que llama", "Your number is visible to the callee within own PBX": "Su número es visible para el receptor de la llamada en su propia centralita.", "Your password has been changed successfully": "Su contraseña ha sido cambiada exitosamente", + "Your password has expired": "Your password has expired", "ago": "atrás", "and": "y", "and call from": "y llamar desde", diff --git a/src/i18n/fr.json b/src/i18n/fr.json index 48f300e1..8342e06d 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -58,6 +58,7 @@ "Block Mode for outbounds calls": "Mode blocage pour les appels sortants", "Block Outgoing": "Bloquer les sortants", "Block anonymous inbound calls": "Bloquer les appels entrants anonymes", + "Block outgoing": "Block outgoing", "Busy Greeting": "Message d'accueil en cas d'occupation", "Busy Lamp Field": "Champ de la lampe occupée", "CDR": "CDR", @@ -107,7 +108,7 @@ "Conference name": "Nom de la conférence", "Confirm": "Confirmer", "Contact": "Contact", - "Contact your administrator to activate this functionality":"Contactez votre administrateur pour activer cette fonctionnalité", + "Contact your administrator to activate this functionality": "Contactez votre administrateur pour activer cette fonctionnalité", "Content": "Contenu", "Conversations": "Conversations", "Copy link": "Copier le lien", @@ -119,6 +120,7 @@ "Create Call Queue": "Créer une file d’attente", "Create Config": "Créer Configuration", "Create List": "Créer une liste", + "Create New Password": "Create New Password", "Create destination": "Créer une destination", "Create device": "Créer un appareil", "Create group": "Créer un groupe", @@ -128,6 +130,7 @@ "Created device {device} successfully": "Le poste {device} a été créé avec succès", "Created manager secretary config for {msConfig} successfully": "Création réussie de la configuration du secrétaire du gestionnaire pour {msConfig}", "Created sound set {soundSet} successfully": "Création du jeu de sons {soundSet} avec succès", + "Current Password": "Current Password", "Custom Announcement": "Annonce personnalisée", "Custom Announcements": "Annonces personnalisées", "Custom sound": "Son personnalisé", @@ -300,6 +303,7 @@ "Ncos Set": "Ncos Set", "Never": "Jamais", "New Messages": "Nouveaux messages", + "New Password": "New Password", "New SIP Password": "New SIP Password", "New SIP Password confirm": "New SIP Password confirm", "New Web Password": "New Web Password", @@ -356,10 +360,18 @@ "Parallel Ringing": "Sonnerie parallèle", "Parent": "Parent", "Password": "Mot de passe", + "Password Expired": "Password Expired", "Password Retype": "Retaper le mot de passe", "Password changed successfully": "Le mot de passe a été modifié avec succès", "Password confirm": "Password confirm", + "Password is considered weak": "Password is considered weak", "Password is not strong enough": "Le mot de passe n'est pas assez fort", + "Password is not strong enough, add more digits": "Password is not strong enough, add more digits", + "Password is not strong enough, add more lowercase letters": "Password is not strong enough, add more lowercase letters", + "Password is not strong enough, add more special characters": "Password is not strong enough, add more special characters", + "Password is not strong enough, add more uppercase letters": "Password is not strong enough, add more uppercase letters", + "Password must be at least {max} characters long": "Password must be at least {max} characters long", + "Password must be at least {min} characters long": "Password must be at least {min} characters long", "Passwords must be equal": "Les mots de passe doivent être égaux", "Phone model": "Modèle du poste", "Phone number": "Numéro de téléphone", @@ -602,6 +614,7 @@ "Your number is visible to the callee": "Votre numéro est visible par le destinataire de l'appel", "Your number is visible to the callee within own PBX": "Votre numéro est visible par appelant dans son propre PBX", "Your password has been changed successfully": "Votre mot de passe a été modifié avec succès", + "Your password has expired": "Your password has expired", "ago": ".", "and": "et", "and call from": "et appeler de", diff --git a/src/i18n/it.json b/src/i18n/it.json index 61bcf8aa..c6da040b 100644 --- a/src/i18n/it.json +++ b/src/i18n/it.json @@ -56,6 +56,7 @@ "Block Mode for outbounds calls": "Modalità di blocco per le chiamate in uscita", "Block Outgoing": "Blocca chiamate in uscita", "Block anonymous inbound calls": "Blocca chiamate anonime in entrata", + "Block outgoing": "Block outgoing", "Busy Greeting": "Messaggio per numero occupato", "Busy Lamp Field": "Busy Lamp Field", "CDR": "CDR", @@ -117,6 +118,7 @@ "Create Call Queue": "Crea coda chiamate", "Create Config": "Crea configurazione", "Create List": "Crea lista", + "Create New Password": "Create New Password", "Create destination": "Crea destinazione", "Create device": "Crea dispositivo", "Create group": "Crea gruppo", @@ -126,6 +128,7 @@ "Created device {device} successfully": "Dispositivo {device} creato correttamente", "Created manager secretary config for {msConfig} successfully": "Configurazione segreteria per {msConfig} creata correttamente", "Created sound set {soundSet} successfully": "Set di messaggi audio {soundSet} creato correttamente", + "Current Password": "Current Password", "Custom Announcement": "Custom Announcement", "Custom Announcements": "Custom Announcements", "Custom sound": "Audio personalizzato", @@ -292,6 +295,7 @@ "Ncos Set": "Ncos Set", "Never": "Never", "New Messages": "Nuovi messaggi", + "New Password": "New Password", "New SIP Password": "New SIP Password", "New SIP Password confirm": "New SIP Password confirm", "New Web Password": "New Web Password", @@ -348,10 +352,18 @@ "Parallel Ringing": "Squilli in parallelo", "Parent": "Parent", "Password": "Password", + "Password Expired": "Password Expired", "Password Retype": "Password Retype", "Password changed successfully": "Password changed successfully", "Password confirm": "Password confirm", + "Password is considered weak": "Password is considered weak", "Password is not strong enough": "Password is not strong enough", + "Password is not strong enough, add more digits": "Password is not strong enough, add more digits", + "Password is not strong enough, add more lowercase letters": "Password is not strong enough, add more lowercase letters", + "Password is not strong enough, add more special characters": "Password is not strong enough, add more special characters", + "Password is not strong enough, add more uppercase letters": "Password is not strong enough, add more uppercase letters", + "Password must be at least {max} characters long": "Password must be at least {max} characters long", + "Password must be at least {min} characters long": "Password must be at least {min} characters long", "Passwords must be equal": "Le passwords devono essere identiche", "Phone model": "Modello telefono", "Phone number": "Numero di telefono", @@ -590,6 +602,7 @@ "Your number is visible to the callee": "Il tuo numero è visible al chiamato", "Your number is visible to the callee within own PBX": "Your number is visible to the callee within own PBX", "Your password has been changed successfully": "Password modificata", + "Your password has expired": "Your password has expired", "ago": "fa", "and": "e", "and call from": "and call from", diff --git a/src/pages/CscChangeExpiredPassword.vue b/src/pages/CscChangeExpiredPassword.vue new file mode 100644 index 00000000..f74f005c --- /dev/null +++ b/src/pages/CscChangeExpiredPassword.vue @@ -0,0 +1,293 @@ + + + + + diff --git a/src/pages/CscPageLogin.vue b/src/pages/CscPageLogin.vue index f2859739..1a684ee8 100644 --- a/src/pages/CscPageLogin.vue +++ b/src/pages/CscPageLogin.vue @@ -157,7 +157,7 @@ export default { }, loginError (error) { if (error) { - showGlobalError(this.$t('Wrong username or password')) + showGlobalError(error) } } }, diff --git a/src/router/routes.js b/src/router/routes.js index 8c70d6bd..5a6bfcd4 100644 --- a/src/router/routes.js +++ b/src/router/routes.js @@ -8,6 +8,7 @@ import CscPageCallBlockingPrivacy from 'src/pages/CscPageCallBlockingPrivacy' import CscPageCallRecording from 'src/pages/CscPageCallRecording' import CscPageCallSettings from 'pages/CscPageCallSettings' import CscPageCf from 'pages/CscPageCf' +import CscPageChangePassword from 'src/pages/CscChangeExpiredPassword.vue' import CscPageConversations from 'src/pages/CscPageConversations' import CscPageCustomerPhonebook from 'src/pages/CscPageCustomerPhonebook' import CscPageCustomerPhonebookAdd from 'src/pages/CscPageCustomerPhonebookAdd' @@ -45,6 +46,8 @@ import CscRecoverPassword from 'src/pages/CscRecoverPassword' import CscPageCustomerPreferences from 'src/pages/CscPageCustomerPreferences' import { i18n } from 'src/boot/i18n' +export const PATH_CHANGE_PASSWORD = '/changepassword' + const getToken = (route) => { return { token: route.query.token @@ -543,6 +546,11 @@ const routes = [ } } }, + { + name: 'passwordChange', + path: PATH_CHANGE_PASSWORD, + component: CscPageChangePassword + }, { path: '/recoverpassword', component: CscLayoutLogin, diff --git a/src/store/user.js b/src/store/user.js index 8eb3f77f..29838fb5 100644 --- a/src/store/user.js +++ b/src/store/user.js @@ -6,7 +6,9 @@ import { import { login, getUserData, - createAuthToken + createAuthToken, + changeExpiredPassword, + getPreLoginPasswordInfo } from '../api/user' import { changePassword, @@ -46,6 +48,7 @@ import { httpApi, apiDownloadFile } from 'src/api/common' +import { PATH_CHANGE_PASSWORD } from 'src/router/routes' export default { namespaced: true, @@ -340,6 +343,9 @@ export default { await this.$router.push({ name: 'dashboard' }) } catch (err) { context.commit('loginFailed', err.message) + if (err.message === 'Password expired') { + this.$router.push({ path: PATH_CHANGE_PASSWORD }) + } } }, logout () { @@ -378,6 +384,16 @@ export default { } } }, + async changeExpiredPassword (context, newPassword) { + context.commit('userPasswordRequesting') + try { + await changeExpiredPassword(newPassword) + } catch (error) { + return context.commit('userPasswordFailed', error) + } + + context.commit('userPasswordSucceeded') + }, async changePassword (context, newPassword) { const subscriberId = getSubscriberId() await changePassword(subscriberId, newPassword) @@ -572,6 +588,9 @@ export default { commit('setQrCode', null) } }, + async fetchPreLoginPasswordInfo () { + return await getPreLoginPasswordInfo() + }, async generatePasswordUser () { const password = await generateGeneralPassword()