diff --git a/package.json b/package.json index 90bd003..044b193 100644 --- a/package.json +++ b/package.json @@ -15,15 +15,15 @@ "npm": "3.x" }, "dependencies": { + "angular-jwt": "latest", + "meanio-circles": "^0.1.x", + "nodemailer": "latest", "passport-facebook": "latest", "passport-github": "latest", "passport-google-oauth": "latest", "passport-linkedin": "latest", "passport-local": "latest", - "passport-twitter": "latest", - "nodemailer": "latest", - "meanio-circles": "^0.1.x", - "angular-jwt" : "latest" + "passport-twitter": "latest" }, "license": "MIT" } diff --git a/public/controllers/meanUser.js b/public/controllers/meanUser.js index 9e2c72c..b6838f1 100644 --- a/public/controllers/meanUser.js +++ b/public/controllers/meanUser.js @@ -1,26 +1,42 @@ 'use strict'; angular.module('mean.users') - .controller('AuthCtrl', ['$scope', '$rootScope', '$http', '$state', 'Global', - function($scope, $rootScope, $http, $state, Global) { + .controller('AuthCtrl', ['$scope', '$rootScope', '$http', '$state', 'Global', 'RestApi', '$location', + function ($scope, $rootScope, $http, $state, Global, RestApi, $location) { // This object will contain list of available social buttons to authorize $scope.socialButtonsCounter = 0; $scope.global = Global; $scope.$state = $state; + RestApi.getRequestServerIsAvailable() + .then(function (response) { + }) + .catch(function (response) { + $location.path('/'); + }); + $http.get('/api/get-config') - .then(function(response) { + .then(function (response) { var config = response.data; - if(config.hasOwnProperty('local')) delete config.local; // Only non-local passport strategies + if (config.hasOwnProperty('local')) delete config.local; // Only non-local passport strategies $scope.socialButtons = config; $scope.socialButtonsCounter = Object.keys(config).length; }); } ]) - .controller('LoginCtrl', ['$rootScope', 'MeanUser', - function($rootScope, MeanUser) { + .controller('LoginCtrl', ['$rootScope', 'MeanUser', 'RestApi', '$location', + function ($rootScope, MeanUser, RestApi, $location) { var vm = this; + vm.gRecaptchaResponse = ''; + + RestApi.getRequestServerIsAvailable() + .then(function (response) { + }) + .catch(function (response) { + $location.path('/'); + }); + // This object will be filled by the form vm.user = {}; @@ -32,27 +48,38 @@ angular.module('mean.users') tooltipText: 'Show password' }; - vm.togglePasswordVisible = function() { + vm.togglePasswordVisible = function () { vm.input.type = vm.input.type === 'text' ? 'password' : 'text'; vm.input.placeholder = vm.input.placeholder === 'Password' ? 'Visible Password' : 'Password'; vm.input.iconClass = vm.input.iconClass === 'icon_hide_password' ? '' : 'icon_hide_password'; vm.input.tooltipText = vm.input.tooltipText === 'Show password' ? 'Hide password' : 'Show password'; }; - $rootScope.$on('loginfailed', function(){ + $rootScope.$on('loginfailed', function () { vm.loginError = MeanUser.loginError; }); // Register the login() function - vm.login = function() { - MeanUser.login(this.user); + vm.login = function ($rootScope) { + if (vm.gRecaptchaResponse.length > 1) { + MeanUser.login(this.user); + } else { + vm.loginError = "Passe pelo CAPTCHA para logar."; + } }; } ]) - .controller('RegisterCtrl', ['$rootScope', 'MeanUser', - function($rootScope, MeanUser) { + .controller('RegisterCtrl', ['$rootScope', 'MeanUser', 'RestApi', '$location', + function ($rootScope, MeanUser, RestApi, $location) { var vm = this; + RestApi.getRequestServerIsAvailable() + .then(function (response) { + }) + .catch(function (response) { + $location.path('/'); + }); + vm.user = {}; vm.registerForm = MeanUser.registerForm = true; @@ -66,13 +93,13 @@ angular.module('mean.users') tooltipTextConfirmPass: 'Show password' }; - vm.togglePasswordVisible = function() { + vm.togglePasswordVisible = function () { vm.input.type = vm.input.type === 'text' ? 'password' : 'text'; vm.input.placeholder = vm.input.placeholder === 'Password' ? 'Visible Password' : 'Password'; vm.input.iconClass = vm.input.iconClass === 'icon_hide_password' ? '' : 'icon_hide_password'; vm.input.tooltipText = vm.input.tooltipText === 'Show password' ? 'Hide password' : 'Show password'; }; - vm.togglePasswordConfirmVisible = function() { + vm.togglePasswordConfirmVisible = function () { vm.input.type = vm.input.type === 'text' ? 'password' : 'text'; vm.input.placeholderConfirmPass = vm.input.placeholderConfirmPass === 'Repeat Password' ? 'Visible Password' : 'Repeat Password'; vm.input.iconClassConfirmPass = vm.input.iconClassConfirmPass === 'icon_hide_password' ? '' : 'icon_hide_password'; @@ -80,35 +107,98 @@ angular.module('mean.users') }; // Register the register() function - vm.register = function() { + vm.register = function () { MeanUser.register(this.user); }; - $rootScope.$on('registerfailed', function(){ + $rootScope.$on('registerfailed', function () { + vm.registerError = MeanUser.registerError; + }); + } + ]) + .controller('UpdateCtrl', ['$rootScope', 'MeanUser', 'RestApi', '$location', + function ($rootScope, MeanUser, RestApi, $location) { + var vm = this; + + RestApi.getRequestServerIsAvailable() + .then(function (response) { + }) + .catch(function (response) { + $location.path('/'); + }); + + vm.user = MeanUser.user; + + vm.registerForm = MeanUser.registerForm = false; + + // Register the update() function + vm.update = function () { + MeanUser.update(this.user); + }; + + $rootScope.$on('registerfailed', function () { vm.registerError = MeanUser.registerError; }); } ]) - .controller('ForgotPasswordCtrl', ['MeanUser', '$rootScope', - function(MeanUser, $rootScope) { + .controller('ForgotPasswordCtrl', ['MeanUser', '$rootScope', 'RestApi', '$location', + function ($rootScope, MeanUser, RestApi, $location) { var vm = this; + + RestApi.getRequestServerIsAvailable() + .then(function (response) { + }) + .catch(function (response) { + $location.path('/'); + }); + vm.user = {}; vm.registerForm = MeanUser.registerForm = false; - vm.forgotpassword = function() { + vm.forgotpassword = function () { MeanUser.forgotpassword(this.user); }; - $rootScope.$on('forgotmailsent', function(event, args){ + $rootScope.$on('forgotmailsent', function (event, args) { vm.response = args; }); } ]) - .controller('ResetPasswordCtrl', ['MeanUser', - function(MeanUser) { + .controller('ResetPasswordCtrl', ['MeanUser', 'RestApi', '$location', + function (MeanUser, RestApi, $location) { var vm = this; + + RestApi.getRequestServerIsAvailable() + .then(function (response) { + }) + .catch(function (response) { + $location.path('/'); + }); + vm.user = {}; vm.registerForm = MeanUser.registerForm = false; - vm.resetpassword = function() { + vm.resetpassword = function () { MeanUser.resetpassword(this.user); }; } + ]) + .controller('ChangePasswordCtrl', ['MeanUser', '$rootScope', 'RestApi', '$location', + function (MeanUser, $rootScope, RestApi, $location) { + var vm = this; + + RestApi.getRequestServerIsAvailable() + .then(function (response) { + }) + .catch(function (response) { + $location.path('/'); + }); + + vm.user = {}; + vm.registerForm = MeanUser.registerForm = false; + vm.changepassword = function () { + MeanUser.changepassword(this.user); + }; + + $rootScope.$on('registerfailed', function () { + vm.registerError = MeanUser.registerError; + }); + } ]); diff --git a/public/services/encoderDataUtil.js b/public/services/encoderDataUtil.js new file mode 100644 index 0000000..68e79b4 --- /dev/null +++ b/public/services/encoderDataUtil.js @@ -0,0 +1,22 @@ +'use strict'; + +angular.module('mean.users') + + .factory('EncoderDataUtil', function () { + + function EncoderDataUtilKlass() { + + } + + var EncoderDataUtil = new EncoderDataUtilKlass(); + + EncoderDataUtilKlass.prototype.encodeURIToBase64 = function (pathRoute) { + return encodeURI(Buffer.from(pathRoute).toString('base64')); + } + + EncoderDataUtilKlass.prototype.encodeDataToBase64 = function (data) { + var dataStringifyed = JSON.stringify(data); + return Buffer.from(dataStringifyed).toString('base64'); + } + return EncoderDataUtil; + }) \ No newline at end of file diff --git a/public/services/meanUser.js b/public/services/meanUser.js index 6cafd2d..107d8b9 100644 --- a/public/services/meanUser.js +++ b/public/services/meanUser.js @@ -1,8 +1,8 @@ 'use strict'; -angular.module('mean.users').factory('MeanUser', [ '$rootScope', '$http', '$location', '$stateParams', - '$cookies', '$q', '$timeout', '$meanConfig', 'Global', - function($rootScope, $http, $location, $stateParams, $cookies, $q, $timeout, $meanConfig, Global) { +angular.module('mean.users').factory('MeanUser', ['$rootScope', '$http', '$location', '$stateParams', '$state', + '$cookies', '$q', '$timeout', '$meanConfig', 'Global', 'RestApi', 'EncoderDataUtil', + function ($rootScope, $http, $location, $stateParams, $state, $cookies, $q, $timeout, $meanConfig, Global, RestApi, EncoderDataUtil) { var self; @@ -15,8 +15,8 @@ angular.module('mean.users').factory('MeanUser', [ '$rootScope', '$http', '$loca .replace(/>/g, '>'); } - function b64_to_utf8( str ) { - return decodeURIComponent(escape(window.atob( str ))); + function b64_to_utf8(str) { + return decodeURIComponent(escape(window.atob(str))); } /*function url_base64_decode(str) { @@ -36,7 +36,7 @@ angular.module('mean.users').factory('MeanUser', [ '$rootScope', '$http', '$loca return window.atob(output); //polifyll https://github.com/davidchambers/Base64.js }*/ - function MeanUserKlass(){ + function MeanUserKlass() { this.name = 'users'; this.user = {}; this.acl = {}; @@ -49,8 +49,8 @@ angular.module('mean.users').factory('MeanUser', [ '$rootScope', '$http', '$loca this.resetpassworderror = null; this.validationError = null; self = this; - $http.get('/api/users/me').then(function(response) { - if(!response.data && $cookies.get('token') && $cookies.get('redirect')) { + $http.get('/api/users/me').then(function (response) { + if (!response.data && $cookies.get('token') && $cookies.get('redirect')) { self.onIdentity.bind(self)({ token: $cookies.get('token'), redirect: $cookies.get('redirect').replace(/^"|"$/g, '') @@ -63,7 +63,8 @@ angular.module('mean.users').factory('MeanUser', [ '$rootScope', '$http', '$loca }); } - MeanUserKlass.prototype.onIdentity = function(response) { + MeanUserKlass.prototype.onIdentity = function (response) { + if (!response) return; // Workaround for Angular 1.6.x @@ -85,7 +86,7 @@ angular.module('mean.users').factory('MeanUser', [ '$rootScope', '$http', '$loca var userObj = this.user; var self = this; // Add circles info to user - $http.get('/api/circles/mine').then(function(response) { + $http.get('/api/circles/mine').then(function (response) { self.acl = response.data; if (destination) { $location.path(destination); @@ -102,7 +103,7 @@ angular.module('mean.users').factory('MeanUser', [ '$rootScope', '$http', '$loca response = response.data; $location.path(response.redirect); - this.loginError = 'Authentication failed.'; + this.loginError = 'Erro na autenticação.'; this.registerError = response; this.validationError = response.msg; this.resetpassworderror = response.msg; @@ -113,87 +114,199 @@ angular.module('mean.users').factory('MeanUser', [ '$rootScope', '$http', '$loca var MeanUser = new MeanUserKlass(); MeanUserKlass.prototype.login = function (user) { - // this is an ugly hack due to mean-admin needs var destination = $location.path().indexOf('/login') === -1 ? $location.absUrl() : false; $http.post('/api/login', { - email: user.email, - password: user.password, - redirect: destination - }) - .then(this.onIdentity.bind(this)) - .catch(this.onIdFail.bind(this)); - }; - - MeanUserKlass.prototype.register = function(user) { - $http.post('/api/register', { email: user.email, password: user.password, - confirmPassword: user.confirmPassword, - username: user.username, - name: user.name + redirect: destination }) .then(this.onIdentity.bind(this)) .catch(this.onIdFail.bind(this)); }; - MeanUserKlass.prototype.resetpassword = function(user) { - $http.post('/api/reset/' + $stateParams.tokenId, { - password: user.password, - confirmPassword: user.confirmPassword + MeanUserKlass.prototype.register = function (user) { + RestApi.getRequestServerIsAvailable() + .then(function (response) { + $http.post('/api/register', { + email: user.email, + password: user.password, + confirmPassword: user.confirmPassword, + username: user.username, + name: user.name, + legalIdentifier: user.legalIdentifier, + stateRegistration: user.stateRegistration, + birthday: user.birthday, + phone: user.phone + }) + .then(function (success) { + var encodedUser = decodeURI(b64_to_utf8(success.data.token.split('.')[1])); + var responseUser = JSON.parse(encodedUser); + var userData = { + "dueDay": 1, + "holder": { + "email": responseUser.email, + "legalIdentifier": responseUser.legalIdentifier, + "name": responseUser.name, + "stateRegistration": responseUser.stateRegistration + }, + "clientId": responseUser._id, + "status": "ACTIVE" + }; + RestApi.postRequest(EncoderDataUtil.encodeURIToBase64("api/bill-accounts"), EncoderDataUtil.encodeDataToBase64(userData)) + .then(function (response) { + MeanUser.onIdentity(success); + }) + .catch(function (response) { + MeanUser.delete(responseUser._id) + .then(function (response) { + MeanUser.onIdFail(response); + }) + .catch(function (err) { + MeanUser.onIdFail(err); + }); + }); + }) + .catch(function (err) { + MeanUser.onIdFail(err); + }); + }) + .catch(function (response) { + $location.path('/'); + }); + }; + + MeanUserKlass.prototype.update = function (user) { + RestApi.getRequestServerIsAvailable() + .then(function (response) { + var client; + RestApi.getRequest(EncoderDataUtil.encodeURIToBase64("api/bill-client-accounts/" + user._id)) + .then(function (response) { + client = response.data; + + $http.put('/api/update', { + email: user.email, + username: user.username, + name: user.name, + legalIdentifier: user.legalIdentifier, + stateRegistration: user.stateRegistration, + birthday: user.birthday, + phone: user.phone + }) + .then(function (success) { + var encodedUser = decodeURI(b64_to_utf8(success.data.token.split('.')[1])); + var responseUser = JSON.parse(encodedUser); + var userData = { + "dueDay": 1, + "holder": { + "email": responseUser.email, + "name": responseUser.name, + "stateRegistration": responseUser.stateRegistration + }, + "id": client.id, + "status": "ACTIVE" + }; + RestApi.putRequest(EncoderDataUtil.encodeURIToBase64("api/bill-accounts"), EncoderDataUtil.encodeDataToBase64(userData)) + .then(function (response) { + MeanUser.onIdentity(success); + }) + .catch(function (response) { + }); + }) + .catch(function (err) { + MeanUser.onIdFail(err); + }); + + }) + .catch(function (response) { + }); + }) + .catch(function (response) { + $location.path('/'); + }); + }; + + MeanUserKlass.prototype.changepassword = function (user) { + $http.post('/api/change', { + password: user.password, + confirmPassword: user.confirmPassword + }) .then(this.onIdentity.bind(this)) .catch(this.onIdFail.bind(this)); - }; + }; - MeanUserKlass.prototype.forgotpassword = function(user) { - $http.post('/api/forgot-password', { - text: user.email + MeanUserKlass.prototype.resetpassword = function (user) { + RestApi.getRequestServerIsAvailable() + .then(function (response) { + $http.post('/api/reset/' + $stateParams.tokenId, { + password: user.password, + confirmPassword: user.confirmPassword + }) + .then(this.onIdentity.bind(this)) + .catch(this.onIdFail.bind(this)); }) - .then(function(response) { - $rootScope.$emit('forgotmailsent', response.data); + .catch(function (response) { + $location.path('/'); + }); + + }; + + MeanUserKlass.prototype.forgotpassword = function (user) { + RestApi.getRequestServerIsAvailable() + .then(function (response) { + $http.post('/api/forgot-password', { + text: user.email }) - .catch(this.onIdFail.bind(this)); - }; + .then(function (response) { + $rootScope.$emit('forgotmailsent', response.data); + }) + .catch(this.onIdFail.bind(this)); + }) + .catch(function (response) { + $location.path('/'); + }); + + }; - MeanUserKlass.prototype.logout = function(){ + MeanUserKlass.prototype.logout = function () { this.user = {}; this.loggedin = false; this.isAdmin = false; - $http.get('/api/logout').then(function(response) { + $http.get('/api/logout').then(function (response) { localStorage.removeItem('JWT'); $rootScope.$emit('logout'); Global.authenticate(); }); }; - MeanUserKlass.prototype.checkLoggedin = function() { - var deferred = $q.defer(); + MeanUserKlass.prototype.checkLoggedin = function () { + var deferred = $q.defer(); // Make an AJAX call to check if the user is logged in - $http.get('/api/loggedin').then(function(response) { + $http.get('/api/loggedin').then(function (response) { var user = response.data; // Authenticated if (user !== '0') $timeout(deferred.resolve); // Not Authenticated else { - $cookies.put('redirect', $location.path()); + $cookies.put('redirect', $state.go('auth.login')); $timeout(deferred.reject); - $location.url($meanConfig.loginPage); + $state.go('auth.login'); } }); return deferred.promise; }; - MeanUserKlass.prototype.checkLoggedOut = function() { - // Check if the user is not connected + MeanUserKlass.prototype.checkLoggedOut = function () { + // Check if the user is not connected // Initialize a new promise var deferred = $q.defer(); // Make an AJAX call to check if the user is logged in - $http.get('/api/loggedin').then(function(response) { + $http.get('/api/loggedin').then(function (response) { var user = response.data; // Authenticated if (user !== '0') { @@ -207,11 +320,11 @@ angular.module('mean.users').factory('MeanUser', [ '$rootScope', '$http', '$loca return deferred.promise; }; - MeanUserKlass.prototype.checkAdmin = function() { - var deferred = $q.defer(); + MeanUserKlass.prototype.checkAdmin = function () { + var deferred = $q.defer(); // Make an AJAX call to check if the user is logged in - $http.get('/api/loggedin').then(function(response) { + $http.get('/api/loggedin').then(function (response) { var user = response.data; // Authenticated if (user !== '0' && user.roles.indexOf('admin') !== -1) $timeout(deferred.resolve); @@ -226,6 +339,14 @@ angular.module('mean.users').factory('MeanUser', [ '$rootScope', '$http', '$loca return deferred.promise; }; + MeanUserKlass.prototype.delete = function (userId) { + $http.post('/api/delete', { + id: userId + }) + .then(this.onIdentity.bind(this)) + .catch(this.onIdFail.bind(this)); + }; + return MeanUser; } ]); diff --git a/public/services/requestFactory.js b/public/services/requestFactory.js new file mode 100644 index 0000000..bf29bee --- /dev/null +++ b/public/services/requestFactory.js @@ -0,0 +1,45 @@ +'use strict'; + +angular.module('mean.users') + + .factory('RestApi', function ($http) { + + function RestApiKlass() { + + } + + var RestApi = new RestApiKlass(); + + RestApiKlass.prototype.getRequest = function (dataRoute) { + return $http.get('/api/get', { + params: { dataRoute: dataRoute } + }) + }; + + RestApiKlass.prototype.getRequestServerIsAvailable = function () { + return $http.get('/api/checkServerBillIsAvailable', { + headers: { + 'Cache-Control': 'no-cache' + } + }) + }; + + RestApiKlass.prototype.postRequest = function (dataRoute, data) { + return $http.post('/api/post', { + dataRoute: dataRoute, + data: data + }) + }; + + RestApiKlass.prototype.putRequest = function (dataRoute, data) { + return $http.put('/api/put', { + dataRoute: dataRoute, + data: data + }) + }; + + return RestApi; + }) + + + diff --git a/server/controllers/serviceRequest.js b/server/controllers/serviceRequest.js new file mode 100644 index 0000000..ddd804e --- /dev/null +++ b/server/controllers/serviceRequest.js @@ -0,0 +1,111 @@ +'use strict'; + +var request = require('request'), + xml = require('jstoxml'), + fs = require('fs'), + config = require('meanio').loadConfig(); + +module.exports = function (RestRequestApi) { + + var auth = Buffer.from("admin:admin").toString('base64'); + + function decodeURIFromBase64(pathRoute) { + return decodeURI(Buffer.from(pathRoute, 'base64').toString()); + } + + function decodeDataFromBase64(data) { + var dataStringifyed = Buffer.from(data, 'base64').toString(); + return JSON.parse(dataStringifyed); + } + + return { + + checkServerBillIsAvailable: function (req, res) { + return request.get({ + url: config.billHost + config.billHostAvailable, + withCredentials: true, + headers: { + 'Content-Type': 'application/json;charset=UTF-8', + 'Authorization': 'Basic ' + auth + }, + /*agentOptions: { + ca: fs.readFileSync('./config/env/calab2.pem') + }*/ + rejectUnauthorized: false, + requestCert: true + }, function (error, response, body) { + if (error) { + res.status(404).json("Requisição não obteve resposta do servidor"); + } else { + res.status(response.statusCode).json("Ok"); + } + + }) + }, + + getApi: function (req, res) { + return request.get({ + url: config.billHost + decodeURIFromBase64(req.query.dataRoute), + withCredentials: true, + headers: { + 'Content-Type': 'application/json;charset=UTF-8', + 'Authorization': 'Basic ' + auth + }, + /*agentOptions: { + ca: fs.readFileSync('./config/env/calab2.pem') + }*/ + rejectUnauthorized: false, + requestCert: true + }, function (error, response, body) { + if (error) { + res.status(404).json("Requisição não obteve resposta do servidor"); + } else { + res.status(response.statusCode).send(body); + } + }) + }, + + postApi: function (req, res) { + return request.post({ + url: config.billHost + decodeURIFromBase64(req.body.dataRoute), + withCredentials: true, + headers: { + 'Content-Type': 'application/json;charset=UTF-8', + 'Authorization': 'Basic ' + auth + }, + rejectUnauthorized: false, + requestCert: true, + body: decodeDataFromBase64(req.body.data), + json: true + }, function (error, response, body) { + if (error) { + res.status(404).json("Requisição não obteve resposta do servidor"); + } else { + res.status(response.statusCode).json(body); + } + }) + }, + + putApi: function (req, res) { + return request.put({ + url: config.billHost + decodeURIFromBase64(req.body.dataRoute), + withCredentials: true, + headers: { + 'Content-Type': 'application/json;charset=UTF-8', + 'Authorization': 'Basic ' + auth + }, + rejectUnauthorized: false, + requestCert: true, + body: decodeDataFromBase64(req.body.data), + json: true + }, function (error, response, body) { + if (error) { + res.status(404).json("Requisição não obteve resposta do servidor"); + } else { + res.status(response.statusCode).json(body); + } + }) + }, + + }; +} \ No newline at end of file diff --git a/server/controllers/users.js b/server/controllers/users.js index bfc8828..bb6b773 100644 --- a/server/controllers/users.js +++ b/server/controllers/users.js @@ -4,14 +4,14 @@ * Module dependencies. */ var mongoose = require('mongoose'), - User = mongoose.model('User'), - async = require('async'), - config = require('meanio').loadConfig(), - crypto = require('crypto'), - nodemailer = require('nodemailer'), - templates = require('../template'), - _ = require('lodash'), - jwt = require('jsonwebtoken'); //https://npmjs.org/package/node-jsonwebtoken + User = mongoose.model('User'), + async = require('async'), + config = require('meanio').loadConfig(), + crypto = require('crypto'), + nodemailer = require('nodemailer'), + templates = require('../template'), + _ = require('lodash'), + jwt = require('jsonwebtoken'); //https://npmjs.org/package/node-jsonwebtoken @@ -20,7 +20,7 @@ var mongoose = require('mongoose'), */ function sendMail(mailOptions) { var transport = nodemailer.createTransport(config.mailer); - transport.sendMail(mailOptions, function(err, response) { + transport.sendMail(mailOptions, function (err, response) { if (err) return err; return response; }); @@ -28,38 +28,38 @@ function sendMail(mailOptions) { -module.exports = function(MeanUser) { +module.exports = function (MeanUser) { return { /** * Auth callback */ - authCallback: function(req, res) { - var payload = req.user; - var escaped = JSON.stringify(payload); - escaped = encodeURI(escaped); - // We are sending the payload inside the token - var token = jwt.sign(escaped, config.secret); - res.cookie('token', token); - var destination = config.strategies.landingPage; - if(!req.cookies.redirect) - res.cookie('redirect', destination); - res.redirect(destination); + authCallback: function (req, res) { + var payload = req.user; + var escaped = JSON.stringify(payload); + escaped = encodeURI(escaped); + // We are sending the payload inside the token + var token = jwt.sign(escaped, config.secret); + res.cookie('token', token); + var destination = config.strategies.landingPage; + if (!req.cookies.redirect) + res.cookie('redirect', destination); + res.redirect(destination); }, /** * Show login form */ - signin: function(req, res) { - if (req.isAuthenticated()) { - return res.redirect('/'); - } - res.redirect('/login'); + signin: function (req, res) { + if (req.isAuthenticated()) { + return res.redirect('/'); + } + res.redirect('/login'); }, /** * Logout */ - signout: function(req, res) { + signout: function (req, res) { MeanUser.events.publish({ action: 'logged_out', @@ -75,24 +75,27 @@ module.exports = function(MeanUser) { /** * Session */ - session: function(req, res) { - res.redirect('/'); + session: function (req, res) { + res.redirect('/'); }, /** * Create user */ - create: function(req, res, next) { + create: function (req, res, next) { var user = new User(req.body); user.provider = 'local'; // because we set our user.provider to local our models/user.js validation will always be true - req.assert('name', 'You must enter a name').notEmpty(); - req.assert('email', 'You must enter a valid email address').isEmail(); - req.assert('password', 'Password must be between 8-20 characters long').len(8, 20); - req.assert('username', 'Username cannot be more than 20 characters').len(1, 20); - req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password); + req.assert('name', 'Você deve informar um nome').notEmpty(); + req.assert('email', 'Você deve informar um e-mail válido').isEmail(); + req.assert('password', 'Senha deve ter entre 8-20 caracteres').len(8, 20); + req.assert('username', 'Você deve informar um Apelido com 1 a 20 caracteres').len(1, 20); + req.assert('confirmPassword', 'Senhas não são iguais').equals(req.body.password); + req.assert('legalIdentifier', 'Você deve informar um CPF ou CNPJ válido').notEmpty(); + req.assert('birthday', 'Você deve informar uma data de nascimento').notEmpty(); + req.assert('phone', 'Você deve informar um telefone').notEmpty(); var errors = req.validationErrors(); if (errors) { @@ -101,31 +104,31 @@ module.exports = function(MeanUser) { // Hard coded for now. Will address this with the user permissions system in v0.3.5 user.roles = ['authenticated']; - user.save(function(err) { + user.save(function (err) { if (err) { switch (err.code) { case 11000: case 11001: - res.status(400).json([{ - msg: 'Username already taken', - param: 'username' - }]); - break; + res.status(400).json([{ + msg: 'Apelido já está sendo usado por outro usuário', + param: 'username' + }]); + break; default: - var modelErrors = []; + var modelErrors = []; - if (err.errors) { + if (err.errors) { - for (var x in err.errors) { - modelErrors.push({ - param: x, - msg: err.errors[x].message, - value: err.errors[x].value - }); - } + for (var x in err.errors) { + modelErrors.push({ + param: x, + msg: err.errors[x].message, + value: err.errors[x].value + }); + } - res.status(400).json(modelErrors); - } + res.status(400).json(modelErrors); + } } return res.status(400); } @@ -134,7 +137,7 @@ module.exports = function(MeanUser) { payload.redirect = req.body.redirect; var escaped = JSON.stringify(payload); escaped = encodeURI(escaped); - req.logIn(user, function(err) { + req.logIn(user, function (err) { if (err) { return next(err); } MeanUser.events.publish({ @@ -148,14 +151,84 @@ module.exports = function(MeanUser) { // We are sending the payload inside the token var token = jwt.sign(escaped, config.secret); - res.json({ - token: token, - redirect: config.strategies.landingPage + res.json({ + token: token, + redirect: config.strategies.landingPage }); }); res.status(200); }); }, + update: function (req, res, next) { + + // because we set our user.provider to local our models/user.js validation will always be true + req.assert('name', 'Você deve informar um nome').notEmpty(); + req.assert('email', 'Você deve informar um e-mail válido').isEmail(); + req.assert('username', 'Você deve informar um Apelido com 1 a 20 caracteres').len(1, 20); + req.assert('legalIdentifier', 'Você deve informar um CPF ou CNPJ válido').notEmpty(); + req.assert('birthday', 'Você deve informar uma data de nascimento').notEmpty(); + req.assert('phone', 'Você deve informar um telefone').notEmpty(); + + var errors = req.validationErrors(); + if (errors) { + return res.status(400).send(errors); + } + + var user = req.user; + _.extend(user, req.body); + user.save(function (err) { + if (err) { + switch (err.code) { + case 11000: + case 11001: + res.status(400).json([{ + msg: 'Apelido já está em uso por outro usuário', + param: 'username' + }]); + break; + default: + var modelErrors = []; + + if (err.errors) { + + for (var x in err.errors) { + modelErrors.push({ + param: x, + msg: err.errors[x].message, + value: err.errors[x].value + }); + } + + res.status(400).json(modelErrors); + } + } + return res.status(400); + } + + var payload = user; + payload.redirect = req.body.redirect; + var escaped = JSON.stringify(payload); + escaped = encodeURI(escaped); + var token = jwt.sign(escaped, config.secret); + res.status(200).json({ + token: token, + redirect: config.strategies.landingPage + }); + }); + + + }, + + delete: function (req, res, next) { + User.findByIdAndRemove(req.body.id, function (err, user) { + // We'll create a simple object to send back with a message and the id of the document that was removed + // You can really do this however you want, though. + res.status(400).json([{ + msg: "Desculpe, por alguma falha interna não foi possivel realizar o cadastro, tente novamente mais tarde." + }]); + }); + }, + loggedin: function (req, res) { if (!req.isAuthenticated()) return res.send('0'); User.findById(req.user._id) @@ -167,10 +240,10 @@ module.exports = function(MeanUser) { /** * Send User */ - me: function(req, res) { + me: function (req, res) { if (!req.user) return res.send(null); - if(!req.refreshJWT) { + if (!req.refreshJWT) { return res.json(req.user); } else { var payload = req.user; @@ -184,12 +257,12 @@ module.exports = function(MeanUser) { /** * Find user by id */ - user: function(req, res, next, id) { + user: function (req, res, next, id) { User.findOne({ _id: id - }).exec(function(err, user) { + }).exec(function (err, user) { if (err) return next(err); - if (!user) return next(new Error('Failed to load User ' + id)); + if (!user) return next(new Error('Falha ao carregar o Usuário ' + id)); req.profile = user; next(); }); @@ -197,7 +270,7 @@ module.exports = function(MeanUser) { /** * Loads a user into the request */ - loadUser: function(req, res, next) { + loadUser: function (req, res, next) { if (!req.isAuthenticated()) { return next(); } @@ -206,7 +279,7 @@ module.exports = function(MeanUser) { User.findOne({ _id: req.user._id - }, function(err, user) { + }, function (err, user) { if (err || !user) { delete req.user; } else { @@ -226,17 +299,49 @@ module.exports = function(MeanUser) { }); }, + changepassword: function (req, res, next) { + var user = req.user; + req.assert('password', 'Senha deve ter entre 8-20 caracteres').len(8, 20); + req.assert('confirmPassword', 'Senhas não são iguais').equals(req.body.password); + var errors = req.validationErrors(); + if (errors) { + return res.status(400).send(errors); + } + user.password = req.body.password; + user.resetPasswordToken = undefined; + user.resetPasswordExpires = undefined; + user.save(function (err) { + + MeanUser.events.publish({ + action: 'change_password', + user: { + name: user.name + } + }); + + var payload = user; + payload.redirect = req.body.redirect; + var escaped = JSON.stringify(payload); + escaped = encodeURI(escaped); + var token = jwt.sign(escaped, config.secret); + res.status(200).json({ + token: token, + redirect: config.strategies.landingPage + }); + }); + }, + /** * Resets the password */ - resetpassword: function(req, res, next) { + resetpassword: function (req, res, next) { User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } - }, function(err, user) { + }, function (err, user) { if (err) { return res.status(400).json({ msg: err @@ -244,11 +349,11 @@ module.exports = function(MeanUser) { } if (!user) { return res.status(400).json({ - msg: 'Token invalid or expired' + msg: 'Link inválido ou expirado' }); } - req.assert('password', 'Password must be between 8-20 characters long').len(8, 20); - req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password); + req.assert('password', 'Senha deve ter entre 8-20 caracteres').len(8, 20); + req.assert('confirmPassword', 'Senhas não são iguais').equals(req.body.password); var errors = req.validationErrors(); if (errors) { return res.status(400).send(errors); @@ -256,7 +361,7 @@ module.exports = function(MeanUser) { user.password = req.body.password; user.resetPasswordToken = undefined; user.resetPasswordExpires = undefined; - user.save(function(err) { + user.save(function (err) { MeanUser.events.publish({ action: 'reset_password', @@ -265,7 +370,7 @@ module.exports = function(MeanUser) { } }); - req.logIn(user, function(err) { + req.logIn(user, function (err) { if (err) return next(err); return res.send({ user: user @@ -278,35 +383,35 @@ module.exports = function(MeanUser) { /** * Callback for forgot password link */ - forgotpassword: function(req, res, next) { + forgotpassword: function (req, res, next) { async.waterfall([ - function(done) { - crypto.randomBytes(20, function(err, buf) { + function (done) { + crypto.randomBytes(20, function (err, buf) { var token = buf.toString('hex'); done(err, token); }); }, - function(token, done) { + function (token, done) { User.findOne({ $or: [{ email: req.body.text }, { username: req.body.text }] - }, function(err, user) { + }, function (err, user) { if (err || !user) return done(true); done(err, user, token); }); }, - function(user, token, done) { + function (user, token, done) { user.resetPasswordToken = token; user.resetPasswordExpires = Date.now() + 3600000; // 1 hour - user.save(function(err) { + user.save(function (err) { done(err, token, user); }); }, - function(token, user, done) { + function (token, user, done) { var mailOptions = { to: user.email, from: config.emailFrom @@ -316,25 +421,25 @@ module.exports = function(MeanUser) { done(null, user); } ], - function(err, user) { + function (err, user) { - var response = { - message: 'Mail successfully sent', - status: 'success' - }; - if (err) { - response.message = 'User does not exist'; - response.status = 'danger'; + var response = { + message: 'E-mail enviado com sucesso', + status: 'success' + }; + if (err) { + response.message = 'Usuário não existe'; + response.status = 'danger'; - } - MeanUser.events.publish({ - action: 'forgot_password', - user: { - name: req.body.text } + MeanUser.events.publish({ + action: 'forgot_password', + user: { + name: req.body.text + } + }); + res.json(response); }); - res.json(response); - }); } }; } diff --git a/server/models/user.js b/server/models/user.js index f41e7a0..3d432f8 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -31,6 +31,21 @@ var validateUniqueEmail = function(value, callback) { }); }; +var validateUniqueLegalIdentifier = function(value, callback) { + var User = mongoose.model('User'); + User.find({ + $and: [{ + legalIdentifier: value + }, { + _id: { + $ne: this._id + } + }] + }, function(err, user) { + callback(err || user.length === 0); + }); +}; + /** * Getter */ @@ -54,7 +69,7 @@ var UserSchema = new Schema({ unique: true, // Regexp to validate emails with more strict rules as added in tests/users.js which also conforms mostly with RFC2822 guide lines match: [/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, 'Please enter a valid email'], - validate: [validateUniqueEmail, 'E-mail address is already in-use'] + validate: [validateUniqueEmail, 'O endereço de E-mail já está em uso'] }, username: { type: String, @@ -66,9 +81,31 @@ var UserSchema = new Schema({ type: Array, default: ['authenticated', 'anonymous'] }, + legalIdentifier: { + type: String, + required: true, + unique: true, + get: escapeProperty, + validate: [validateUniqueLegalIdentifier, 'O CPF/CNPJ já está em uso'] + }, + stateRegistration: { + type: String, + unique: true, + get: escapeProperty + }, + birthday: { + type: Date, + required: true, + get: escapeProperty + }, + phone: { + type: String, + required: true, + get: escapeProperty + }, hashed_password: { type: String, - validate: [validatePresenceOf, 'Password cannot be blank'] + validate: [validatePresenceOf, 'Senha não pode ser vazia'] }, provider: { type: String, @@ -101,7 +138,7 @@ UserSchema.virtual('password').set(function(password) { */ UserSchema.pre('save', function(next) { if (this.isNew && this.provider === 'local' && this.password && !this.password.length) - return next(new Error('Invalid password')); + return next(new Error('Senha inválida')); next(); }); diff --git a/server/routes/users.js b/server/routes/users.js index d90a560..2aa0521 100644 --- a/server/routes/users.js +++ b/server/routes/users.js @@ -3,10 +3,12 @@ var config = require('meanio').loadConfig(); var jwt = require('jsonwebtoken'); //https://npmjs.org/package/node-jsonwebtoken -module.exports = function(MeanUser, app, auth, database, passport) { +module.exports = function (MeanUser, app, auth, database, passport, RestRequestApi) { // User routes use users controller var users = require('../controllers/users')(MeanUser); + var restRequest = require('../controllers/serviceRequest')(RestRequestApi); + app.use(users.loadUser); @@ -21,40 +23,54 @@ module.exports = function(MeanUser, app, auth, database, passport) { // AngularJS route to check for authentication app.route('/api/loggedin').get(users.loggedin); - if(config.strategies.local.enabled) - { - // Setting up the users api - app.route('/api/register') - .post(users.create); - - app.route('/api/forgot-password') - .post(users.forgotpassword); - - app.route('/api/reset/:token') - .post(users.resetpassword); - - // Setting the local strategy route - app.route('/api/login') - .post(passport.authenticate('local', { - failureFlash: false - }), function(req, res) { - var payload = req.user; - payload.redirect = req.body.redirect; - var escaped = JSON.stringify(payload); - escaped = encodeURI(escaped); - // We are sending the payload inside the token - var token = jwt.sign(escaped, config.secret); - MeanUser.events.publish({ - action: 'logged_in', - user: { - name: req.user.name - } - }); - res.json({ - token: token, - redirect: config.strategies.landingPage - }); + if (config.strategies.local.enabled) { + // Setting up the users api + app.route('/api/register') + .post(users.create); + + app.route('/api/update') + .put(users.update); + + app.route('/api/delete') + .post(users.delete); + + app.route('/api/forgot-password') + .post(users.forgotpassword); + + app.route('/api/reset/:token') + .post(users.resetpassword); + + app.route('/api/change') + .post(users.changepassword); + + app.get('/api/get', restRequest.getApi); + + app.post('/api/post', restRequest.postApi); + + app.put('/api/put', restRequest.putApi); + + // Setting the local strategy route + app.route('/api/login') + .post(passport.authenticate('local', { + failureFlash: false + }), function (req, res) { + var payload = req.user; + payload.redirect = req.body.redirect; + var escaped = JSON.stringify(payload); + escaped = encodeURI(escaped); + // We are sending the payload inside the token + var token = jwt.sign(escaped, config.secret); + MeanUser.events.publish({ + action: 'logged_in', + user: { + name: req.user.name + } + }); + res.json({ + token: token, + redirect: config.strategies.landingPage }); + }); } // AngularJS route to get config of social buttons @@ -63,93 +79,86 @@ module.exports = function(MeanUser, app, auth, database, passport) { // To avoid displaying unneccesary social logins var strategies = config.strategies; var configuredApps = {}; - for (var key in strategies) - { - if(strategies.hasOwnProperty(key)) - { + for (var key in strategies) { + if (strategies.hasOwnProperty(key)) { var strategy = strategies[key]; if (strategy.hasOwnProperty('enabled') && strategy.enabled === true) { - configuredApps[key] = true ; + configuredApps[key] = true; } } } res.send(configuredApps); }); - if(config.strategies.facebook.enabled) - { - // Setting the facebook oauth routes - app.route('/api/auth/facebook') - .get(passport.authenticate('facebook', { - scope: ['email', 'user_about_me'], - failureRedirect: '/auth/login', - }), users.signin); - - app.route('/api/auth/facebook/callback') - .get(passport.authenticate('facebook', { - failureRedirect: '/auth/login', - }), users.authCallback); + if (config.strategies.facebook.enabled) { + // Setting the facebook oauth routes + app.route('/api/auth/facebook') + .get(passport.authenticate('facebook', { + scope: ['email', 'user_about_me'], + failureRedirect: '/auth/login', + }), users.signin); + + app.route('/api/auth/facebook/callback') + .get(passport.authenticate('facebook', { + failureRedirect: '/auth/login', + }), users.authCallback); } - if(config.strategies.github.enabled) - { - // Setting the github oauth routes - app.route('/api/auth/github') - .get(passport.authenticate('github', { - failureRedirect: '/auth/login' - }), users.signin); - - app.route('/api/auth/github/callback') - .get(passport.authenticate('github', { - failureRedirect: '/auth/login' - }), users.authCallback); + if (config.strategies.github.enabled) { + // Setting the github oauth routes + app.route('/api/auth/github') + .get(passport.authenticate('github', { + failureRedirect: '/auth/login' + }), users.signin); + + app.route('/api/auth/github/callback') + .get(passport.authenticate('github', { + failureRedirect: '/auth/login' + }), users.authCallback); } - if(config.strategies.twitter.enabled) - { - // Setting the twitter oauth routes - app.route('/api/auth/twitter') - .get(passport.authenticate('twitter', { - failureRedirect: '/auth/login' - }), users.signin); - - app.route('/api/auth/twitter/callback') - .get(passport.authenticate('twitter', { - failureRedirect: '/auth/login' - }), users.authCallback); + if (config.strategies.twitter.enabled) { + // Setting the twitter oauth routes + app.route('/api/auth/twitter') + .get(passport.authenticate('twitter', { + failureRedirect: '/auth/login' + }), users.signin); + + app.route('/api/auth/twitter/callback') + .get(passport.authenticate('twitter', { + failureRedirect: '/auth/login' + }), users.authCallback); } - if(config.strategies.google.enabled) - { - // Setting the google oauth routes - app.route('/api/auth/google') - .get(passport.authenticate('google', { - failureRedirect: '/auth/login', - scope: [ - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/userinfo.email' - ] - }), users.signin); - - app.route('/api/auth/google/callback') - .get(passport.authenticate('google', { - failureRedirect: '/auth/login' - }), users.authCallback); + if (config.strategies.google.enabled) { + // Setting the google oauth routes + app.route('/api/auth/google') + .get(passport.authenticate('google', { + failureRedirect: '/auth/login', + scope: [ + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/userinfo.email' + ] + }), users.signin); + + app.route('/api/auth/google/callback') + .get(passport.authenticate('google', { + failureRedirect: '/auth/login' + }), users.authCallback); } - if(config.strategies.linkedin.enabled) - { - // Setting the linkedin oauth routes - app.route('/api/auth/linkedin') - .get(passport.authenticate('linkedin', { - failureRedirect: '/auth/login', - scope: ['r_emailaddress'] - }), users.signin); - - app.route('/api/auth/linkedin/callback') - .get(passport.authenticate('linkedin', { - failureRedirect: '/auth/login' - }), users.authCallback); + if (config.strategies.linkedin.enabled) { + // Setting the linkedin oauth routes + app.route('/api/auth/linkedin') + .get(passport.authenticate('linkedin', { + failureRedirect: '/auth/login', + scope: ['r_emailaddress'] + }), users.signin); + + app.route('/api/auth/linkedin/callback') + .get(passport.authenticate('linkedin', { + failureRedirect: '/auth/login' + }), users.authCallback); } }; diff --git a/server/template.js b/server/template.js index bb018d8..5e829d8 100644 --- a/server/template.js +++ b/server/template.js @@ -3,14 +3,16 @@ module.exports = { forgot_password_email: function(user, req, token, mailOptions) { mailOptions.html = [ - 'Hi ' + user.name + ',', - 'We have received a request to reset the password for your account.', - 'If you made this request, please click on the link below or paste this into your browser to complete the process:', + 'Olá ' + user.name + ',
', + 'Nós recebemos uma requisição para modificar a senha da sua conta.', + 'Por favor clique no link abaixo para modificar sua senha:

'+ + 'Alterar senha

'+ + 'Se não conseguir clicar no link, copie a URL abaixo e cole no seu navegador para completar o processo:

', 'http://' + req.headers.host + '/reset/' + token, - 'This link will work for 1 hour or until your password is reset.', - 'If you did not ask to change your password, please ignore this email and your account will remain unchanged.' + '

Este link funcionará por 1 hora ou até você alterar sua senha.', + '
Se você não solicitou a mudança da sua senha, por favor ignore este e-mail e sua conta não será modificada.' ].join('\n\n'); - mailOptions.subject = 'Resetting the password'; + mailOptions.subject = 'Alteração de senha'; return mailOptions; } };