Issue #675 to recover/change passwords done

parent 849161fa
...@@ -279,7 +279,7 @@ module.exports = { ...@@ -279,7 +279,7 @@ module.exports = {
var message = sails.__({ var message = sails.__({
phrase: 'change_password_mail', phrase: 'change_password_mail',
locale: params.lang || 'es-es' locale: params.lang || 'es-es'
}) + 'https://' + req.headers.host + '/sup/changepass/' + sailsTokenAuth.issueToken(supervisor.id, 60*24*7); // expires in 1 week }) + 'https://' + req.headers.host + '/app/#/changepass/' + sailsTokenAuth.issueToken(sup.id, 60); // expires in 1 hour
sails.log.debug("Sending request email: \n" + message); sails.log.debug("Sending request email: \n" + message);
mailService.mailer() mailService.mailer()
...@@ -304,20 +304,20 @@ module.exports = { ...@@ -304,20 +304,20 @@ module.exports = {
*/ */
change_password: function (req, res) { change_password: function (req, res) {
if (!req.params.token) if (!req.body.token)
return res.badRequest("Invalid URL"); return res.badRequest("Invalid URL");
sailsTokenAuth.verifyToken(req.params.token, function(err, token) { sailsTokenAuth.verifyToken(req.body.token, function(err, token) {
if (err) if (err)
return res.badRequest("Invalid token"); return res.badRequest("Invalid token");
Supervisor.findOne(token).then(function (supervisor) { Supervisor.findOne(token).then(function (supervisor) {
if (!supervisor) if (!supervisor)
throw new Error("Error when looking for user"); throw new Error("Error when looking for user");
supervisor.password = req.params.password; supervisor.password = req.body.password;
supervisor.save(); supervisor.save();
return res.view('passwordChanged', {sup: supervisor, login_url: 'https://' + req.headers.host + '/app'}); return res.ok();
}) })
.catch(function (err) { .catch(function (err) {
return res.serverError("Error when activating account " + err); return res.serverError("Error when changing password " + err);
}); });
}); });
}, },
......
...@@ -89,6 +89,7 @@ ...@@ -89,6 +89,7 @@
"error_downloading_offices": "Error downloading offices", "error_downloading_offices": "Error downloading offices",
"error_fetching_students": "Error when loading students", "error_fetching_students": "Error when loading students",
"error_only_support_images": "Only images are supported (JPG, PNG or GIF files)", "error_only_support_images": "Only images are supported (JPG, PNG or GIF files)",
"error_on_request": "The request has not been processed. Please, check your fields",
"error_loading_pictos": "Error loading pictos information", "error_loading_pictos": "Error loading pictos information",
"expand_navigation": "Expand navigation", "expand_navigation": "Expand navigation",
"expand_navigation": "Expand navigation", "expand_navigation": "Expand navigation",
...@@ -113,6 +114,7 @@ ...@@ -113,6 +114,7 @@
"instruction_begin": "Begin instruction", "instruction_begin": "Begin instruction",
"instruction_end": "End instruction", "instruction_end": "End instruction",
"instructions": "Instructions", "instructions": "Instructions",
"instructions_sent": "Instructions have been sent to your email address. Please, check your inbox.",
"invalid_fields": "Invalid values. Please, check fields introduced.", "invalid_fields": "Invalid values. Please, check fields introduced.",
"invalid_file_type": "Invalid file type", "invalid_file_type": "Invalid file type",
"invisible": "Invisible. Clic for enable", "invisible": "Invisible. Clic for enable",
...@@ -145,6 +147,8 @@ ...@@ -145,6 +147,8 @@
"minutes": "minutes", "minutes": "minutes",
"month_totals": "Month totals", "month_totals": "Month totals",
"monthly":"Monthly", "monthly":"Monthly",
"msg_change_password": "Please enter a new password for your account:",
"msg_request_change_password": "Enter your email to request for a password change. An e-mail will be sent to access password change form.",
"name": "Name", "name": "Name",
"new_instruction": "New instruction", "new_instruction": "New instruction",
"new_method": "New method", "new_method": "New method",
...@@ -180,6 +184,7 @@ ...@@ -180,6 +184,7 @@
"own_pictos": "Your pictograms", "own_pictos": "Your pictograms",
"pages": "Pages", "pages": "Pages",
"password": "Password", "password": "Password",
"password_changed": "Password changed sucessfully",
"password_confirm": "Repeat password", "password_confirm": "Repeat password",
"password_forgotten": "Have you forgotten your password?", "password_forgotten": "Have you forgotten your password?",
"password_match": "The passwords must match", "password_match": "The passwords must match",
...@@ -213,6 +218,7 @@ ...@@ -213,6 +218,7 @@
"register": "Sign in", "register": "Sign in",
"remember": "Remember me", "remember": "Remember me",
"reports": "Reports", "reports": "Reports",
"request_change_password": "Request change password",
"room_changed": "A partner is offline. Session paused.", "room_changed": "A partner is offline. Session paused.",
"save": "Save", "save": "Save",
"save_as_template": "Save as template", "save_as_template": "Save as template",
......
...@@ -92,6 +92,7 @@ ...@@ -92,6 +92,7 @@
"error_downloading_offices": "Error al descargar las oficinas", "error_downloading_offices": "Error al descargar las oficinas",
"error_fetching_students": "Error al cargar estudiantes", "error_fetching_students": "Error al cargar estudiantes",
"error_only_support_images": "Sólo se soportan imágenes (ficheros JPG, PNG o GIF)", "error_only_support_images": "Sólo se soportan imágenes (ficheros JPG, PNG o GIF)",
"error_on_request": "Se ha producido un error. Por favor, compruebe los valores introducidos.",
"error_loading_pictos": "Error cargando información de los pictos", "error_loading_pictos": "Error cargando información de los pictos",
"February": "Febrero", "February": "Febrero",
"feedback_picto": "Feedback al colocar un pictograma", "feedback_picto": "Feedback al colocar un pictograma",
...@@ -113,6 +114,7 @@ ...@@ -113,6 +114,7 @@
"instruction_begin": "Primer intento", "instruction_begin": "Primer intento",
"instruction_end": "Último intento", "instruction_end": "Último intento",
"instructions": "Instrucciones", "instructions": "Instrucciones",
"instructions_sent": "Se le han enviado instrucciones a su dirección de correo electrónico. Por favor, compruebe su buzón.",
"invalid_fields": "Valores inválidos. Compruebe los datos introducidos.", "invalid_fields": "Valores inválidos. Compruebe los datos introducidos.",
"invalid_file_type": "Tipo de archivo no válido", "invalid_file_type": "Tipo de archivo no válido",
"invisible": "Invisible. Clic para activar", "invisible": "Invisible. Clic para activar",
...@@ -145,6 +147,8 @@ ...@@ -145,6 +147,8 @@
"minutes": "minutos", "minutes": "minutos",
"month_totals": "Totales mes", "month_totals": "Totales mes",
"monthly":"Mensual", "monthly":"Mensual",
"msg_change_password": "Por favor, introduzca la nueva clave para su cuenta:",
"msg_request_change_password": "Introduzca su correo electrónico para solicitar el cambio de clave. Recibirá un correo con la dirección de acceso al formulario de cambio de clave.",
"name": "Nombre", "name": "Nombre",
"new_instruction": "Nueva instrucción", "new_instruction": "Nueva instrucción",
"new_method": "Nuevo método", "new_method": "Nuevo método",
...@@ -180,6 +184,7 @@ ...@@ -180,6 +184,7 @@
"own_pictos": "Propios", "own_pictos": "Propios",
"pages": "Páginas", "pages": "Páginas",
"password": "Contraseña", "password": "Contraseña",
"password_changed": "La clave ha sido modificada exitosamente.",
"password_confirm": "Repita la contraseña", "password_confirm": "Repita la contraseña",
"password_forgotten": "¿Ha olvidado su contraseña?", "password_forgotten": "¿Ha olvidado su contraseña?",
"password_match": "Las contraseñas deben coincidir", "password_match": "Las contraseñas deben coincidir",
...@@ -214,6 +219,7 @@ ...@@ -214,6 +219,7 @@
"register_button": "Registrar", "register_button": "Registrar",
"remember": "No cerrar sesión", "remember": "No cerrar sesión",
"reports": "Informes", "reports": "Informes",
"request_change_password": "Solicitar cambio de contraseña",
"room_changed":"Un participante abandonó la sesión. Sesión en pausa.", "room_changed":"Un participante abandonó la sesión. Sesión en pausa.",
"save": "Guardar", "save": "Guardar",
"save_as_template": "Guardar como plantilla", "save_as_template": "Guardar como plantilla",
......
...@@ -57,17 +57,17 @@ dashboardApp.config(function ($stateProvider, $urlRouterProvider) { ...@@ -57,17 +57,17 @@ dashboardApp.config(function ($stateProvider, $urlRouterProvider) {
controller: 'LoginCtrl' controller: 'LoginCtrl'
}) })
.state('login_office', { .state('login_office', {
url: '/login/:office', url: '/login/:office',
templateUrl: 'modules/login/views/login.html', templateUrl: 'modules/login/views/login.html',
controller: 'LoginCtrl' controller: 'LoginCtrl'
}) })
.state('login_validate', { .state('request_change_password', {
url: '/login/:code/:email', url: '/changepass',
templateUrl: 'modules/login/views/login.html', templateUrl: 'modules/login/views/login_setting_password.html',
controller: 'LoginCtrl' controller: 'LoginSettingPasswordCtrl'
}) })
.state('login_tutor_validate', { .state('change_password', {
url: '/logintutor/:code/:email', url: '/changepass/:token',
templateUrl: 'modules/login/views/login_setting_password.html', templateUrl: 'modules/login/views/login_setting_password.html',
controller: 'LoginSettingPasswordCtrl' controller: 'LoginSettingPasswordCtrl'
}) })
......
...@@ -28,23 +28,6 @@ function LoginCtrl( ...@@ -28,23 +28,6 @@ function LoginCtrl(
name : 'Pictogram' name : 'Pictogram'
}; };
// Validation of account
// if the code has been sent in the url "...app/login/code/email"
if ($stateParams.code && $stateParams.email) {
$http
.post(config.backend + '/sup/activate', $stateParams)
.success(function () {
$translate('account_activate').then(function (translation) {
ngToast.success({ content: translation });
});
})
.error(function () {
$translate('account_no_activate').then(function (translation) {
ngToast.danger({ content: translation });
});
});
}
// Corporate login, office code has been passed // Corporate login, office code has been passed
if ($stateParams.office) { if ($stateParams.office) {
$http $http
......
...@@ -3,46 +3,89 @@ ...@@ -3,46 +3,89 @@
//----------------------------------- //-----------------------------------
// Login Setting Password Controller // Login Setting Password Controller
//----------------------------------- //-----------------------------------
dashboardControllers.controller('LoginSettingPasswordCtrl', function LoginSettingPasswordCtrl($scope, $http, $window, $translate, $location, config, $stateParams, ngToast) { dashboardControllers.controller('LoginSettingPasswordCtrl', function LoginSettingPasswordCtrl(
$scope,
$http,
$window,
reCAPTCHA,
$translate,
$location,
config,
CONSTANTS,
$stateParams,
ngToast) {
$scope.login_setting_password = function () { $scope.reset = function () {
$scope.email = '';
$scope.password = '';
$scope.password_confirm = '';
$scope.captcha_chgpass = '';
$scope.captcha_reqchgpass = '';
requestChangePasswordForm.$invalid = true;
changePasswordForm.$invalid = true;
};
$scope.reset();
reCAPTCHA.setPublicKey('6LdLjh0TAAAAANblo_KUGNnmRZuIetOkdjdhj1b6');
if ($stateParams.token)
$scope.show_change_form = true;
else
$scope.show_change_form = false;
/*
* Handles user submission of request for password change
*/
$scope.request_change_password = function () {
if (!$scope.requestChangePasswordForm.$valid)
return;
if ($scope.email) {
$http
.get(config.backend+'/sup/changepass/' + $scope.email)
.success(function(data, status, headers, config) {
$scope.reset();
ngToast.warning({ content: $translate.instant('instructions_sent') });
})
.error(function(data, status, headers, config) {
$scope.reset();
ngToast.danger({ content: $translate.instant('error_on_request') });
});
}
};
$scope.change_password = function () {
if ($stateParams.token) {
// Validation of account
// if the code has been sent in the url "...app/login/code/email"
if ($stateParams.code && $stateParams.email) {
// Validate password match // Validate password match
if ($scope.password != $scope.password_confirm) { if ($scope.password !== $scope.password_confirm) {
$translate([ ngToast.danger({ content: $translate.instant('password_match') });
'validate_fail', return;
'password_match' }
], function (translations) {
ngToast.danger({ if ($scope.password.length < CONSTANTS.password_minlength) {
content: [ ngToast.danger({ content: $translate.instant('password_short', {minlength: CONSTANTS.password_minlength}) });
translations.validate_fail, return;
translations.password_match }
].join(': ')
}); if (!$scope.changePasswordForm.$valid)
}); return;
return;
}
var data = { var data = {
code: $stateParams.code, token: $stateParams.token,
email: $stateParams.email,
password: $scope.password password: $scope.password
}; };
$http $http
.post(config.backend+'/sup/activate', data) .put(config.backend+'/sup/changepass', data)
.success(function(data, status, headers, config) { .success(function(data, status, headers, config) {
$translate('validate_success').then(function (translation) { $scope.reset();
ngToast.success({ content: translation }); ngToast.success({ content: $translate.instant('password_changed') });
});
}) })
.error(function(data, status, headers, config) { .error(function(data, status, headers, config) {
$translate('validate_fail').then(function (translation) { $scope.reset();
ngToast.danger({ content: translation }); ngToast.danger({ content: $translate.instant('error_on_request') });
});
}); });
} }
}; };
......
...@@ -27,6 +27,7 @@ function SignInCtrl($scope, ...@@ -27,6 +27,7 @@ function SignInCtrl($scope,
password_confirm: '', password_confirm: '',
lang: 'es-es' lang: 'es-es'
}; };
$scope.captcha = '';
}; };
reCAPTCHA.setPublicKey('6LdLjh0TAAAAANblo_KUGNnmRZuIetOkdjdhj1b6'); reCAPTCHA.setPublicKey('6LdLjh0TAAAAANblo_KUGNnmRZuIetOkdjdhj1b6');
...@@ -51,8 +52,6 @@ function SignInCtrl($scope, ...@@ -51,8 +52,6 @@ function SignInCtrl($scope,
return; return;
} }
$scope.captcha = '';
if (!$scope.signInForm.$valid) if (!$scope.signInForm.$valid)
return; return;
......
...@@ -37,6 +37,9 @@ ...@@ -37,6 +37,9 @@
<p class="text-center"> <p class="text-center">
<a href="/app/#/signin" translate>create_an_account</a> <a href="/app/#/signin" translate>create_an_account</a>
</p> </p>
<p class="text-center">
<a href="/app/#/changepass" translate>password_forgotten</a>
</p>
</form> </form>
</div> </div>
<!-- Fin login --> <!-- Fin login -->
......
...@@ -3,23 +3,50 @@ ...@@ -3,23 +3,50 @@
<!-- Rejilla 3 elementos de igual ancho --> <!-- Rejilla 3 elementos de igual ancho -->
<div class="col-md-4">&nbsp;</div> <div class="col-md-4">&nbsp;</div>
<div class="col-md-4"> <div class="col-md-4">
<div id="login"> <div id="login">
<!-- Logo Pictogram --> <!-- Logo Pictogram -->
<p class="text-center"> <p class="text-center">
<img src="img/logo_pictogram.png" alt="Pictogram" title="Pictogram" /> <a href="/app/#/login"><img src="img/logo_pictogram.png" alt="Pictogram" title="Pictogram" /></a>
</p> </p>
<div ng-show="{{ 'showlink' }}" class="text-center"> <div class="text-center">
<a href="/app/#/login" translate>click_login</a> <a href="/app/#/login" translate>click_login</a>
</div> </div>
<!-- Formulario --> <!-- Request change password form -->
<form ng-hide="{{ 'hideform' }}" name="loginSettingPasswordForm" ng-submit="login_setting_password()"> <form ng-hide="{{ 'show_change_form' }}" name="requestChangePasswordForm" ng-submit="request_change_password()">
<p class="text-left">
{{ 'msg_request_change_password' | translate}}
</p>
<div class="form-group">
<input type="email" class="form-control" id="email" placeholder="{{ 'your_email' | translate}}" required ng-model="email"/>
</div>
<div class="form-group" ng-if="!show_change_form">
<label>Captcha*</label>
<div re-captcha ng-model="captcha_reqchgpass"></div>
</div>
<p class="text-center">
<button type="submit" class="btn btn-primary" ng-disabled="requestChangePasswordForm.$invalid" translate>request_change_password</button>
</p>
</form>
<!-- Change password form -->
<form ng-show="{{ 'show_change_form' }}" name="changePasswordForm" ng-submit="change_password()">
<p class="text-left">
{{ 'msg_change_password' | translate}}
</p>
<div class="form-group"> <div class="form-group">
<input type="password" class="form-control" id="login_password" placeholder="{{ 'password_type' | translate}}" required /> <input type="password" class="form-control" id="login_password" placeholder="{{ 'password_type' | translate}}" required ng-model="password"/>
</div> </div>
<div class="form-group"> <div class="form-group">
...@@ -28,8 +55,13 @@ ...@@ -28,8 +55,13 @@
<p class="color_red text_sm text-center" ng-show="password != password_confirm" translate>password_match</p> <p class="color_red text_sm text-center" ng-show="password != password_confirm" translate>password_match</p>
<div class="form-group" ng-if="show_change_form">
<label>Captcha*</label>
<div re-captcha ng-model="captcha_chgpass"></div>
</div>
<p class="text-center"> <p class="text-center">
<button type="submit" class="btn btn-primary" translate>send</button> <button type="submit" class="btn btn-primary" ng-disabled="changePasswordForm.$invalid" translate>send</button>
</p> </p>
</form> </form>
...@@ -38,9 +70,9 @@ ...@@ -38,9 +70,9 @@
</div> </div>
<div class="col-md-4">&nbsp;</div> <div class="col-md-4">&nbsp;</div>
</div> </div>
<!-- Fin de row --> <!-- Fin de row -->
<footer-translate></footer-translate> <footer-translate></footer-translate>
\ No newline at end of file
...@@ -102,12 +102,12 @@ module.exports.routes = { ...@@ -102,12 +102,12 @@ module.exports.routes = {
'GET /sup/:id/pic_categories/:id_cat': 'PictoController.categories', 'GET /sup/:id/pic_categories/:id_cat': 'PictoController.categories',
'GET /sup/:id/pic_fromcategory/:id_cat': 'PictoController.fromcategory', 'GET /sup/:id/pic_fromcategory/:id_cat': 'PictoController.fromcategory',
'GET /sup/email/:email': 'SupervisorController.getByEmail', 'GET /sup/email/:email': 'SupervisorController.getByEmail',
'GET /sup/changepass/:email': 'SupervisorController.request_change_password',
'PUT /sup/changepass': 'SupervisorController.change_password',
'PUT /sup/:id': 'SupervisorController.update', 'PUT /sup/:id': 'SupervisorController.update',
'POST /sup': 'SupervisorController.create', 'POST /sup': 'SupervisorController.create',
'POST /sup/login': 'SupervisorController.login', 'POST /sup/login': 'SupervisorController.login',
'GET /sup/activate/:token': 'SupervisorController.activate', 'GET /sup/activate/:token': 'SupervisorController.activate',
'GET /sup/changepass': 'SupervisorController.request_change_password',
'GET /sup/changepass/:token': 'SupervisorController.change_password',
'POST /sup/upload': 'SupervisorController.upload', 'POST /sup/upload': 'SupervisorController.upload',
'POST /sup/subscribe': 'SupervisorController.subscribe', 'POST /sup/subscribe': 'SupervisorController.subscribe',
'POST /sup/unsubscribe': 'SupervisorController.unsubscribe', 'POST /sup/unsubscribe': 'SupervisorController.unsubscribe',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment