From 0e8bba84a301cae20ccc34c94d04b8bf7be86f8b Mon Sep 17 00:00:00 2001 From: Lukasz Sielski Date: Tue, 12 May 2015 20:10:03 +0100 Subject: [PATCH] Refactor user --- bower.json | 3 +- config/assets/default.js | 6 +- config/env/development.js | 48 ++-- gruntfile.js | 14 +- .../controllers/errors.server.controller.js | 11 +- .../client/config/users.client.routes.js | 9 + .../admin/users.client.controller.js | 13 + .../controllers/settings.client.controller.js | 79 ++++-- .../edit-profile.client.controller.js | 38 ++- ...anage-social-accounts.client.controller.js | 29 ++- .../settings/settings.client.controller.js | 2 +- modules/users/client/users.client.module.js | 2 +- .../views/admin/users.list.client.view.html | 34 +++ .../settings/edit-profile.client.view.html | 33 ++- .../manage-social-accounts.client.view.html | 6 +- .../server/config/strategies/facebook.js | 17 +- .../users/server/config/strategies/github.js | 22 +- .../users/server/config/strategies/google.js | 13 +- .../server/config/strategies/linkedin.js | 13 +- .../users/server/config/strategies/local.js | 8 +- .../users/server/config/strategies/twitter.js | 22 +- .../controllers/users.server.controller.js | 3 +- .../users.authentication.server.controller.js | 126 +++------- .../users.authorization.server.controller.js | 6 +- .../users/users.crud.server.controller.js | 13 + .../users/users.password.server.controller.js | 64 ++--- .../users/server/models/user.server.model.js | 238 +++++++++++++++--- .../server/policies/users.server.policy.js | 46 ++++ .../server/routes/users.server.routes.js | 7 +- .../tests/server/user.server.model.tests.js | 73 ++++-- package.json | 47 ++-- 31 files changed, 717 insertions(+), 328 deletions(-) create mode 100644 modules/users/client/controllers/admin/users.client.controller.js create mode 100644 modules/users/client/views/admin/users.list.client.view.html create mode 100644 modules/users/server/controllers/users/users.crud.server.controller.js create mode 100644 modules/users/server/policies/users.server.policy.js diff --git a/bower.json b/bower.json index 951e48aaac..38be8f6194 100644 --- a/bower.json +++ b/bower.json @@ -11,6 +11,7 @@ "angular-bootstrap": "~0.11.0", "angular-ui-utils": "~0.1.1", "angular-ui-router": "~0.2.10", - "angular-file-upload": "~1.1.5" + "angular-file-upload": "~1.1.5", + "angular-json-tree": "*" } } diff --git a/config/assets/default.js b/config/assets/default.js index 42f1b1062a..4377b7fd1c 100644 --- a/config/assets/default.js +++ b/config/assets/default.js @@ -5,7 +5,8 @@ module.exports = { lib: { css: [ 'public/lib/bootstrap/dist/css/bootstrap.css', - 'public/lib/bootstrap/dist/css/bootstrap-theme.css' + 'public/lib/bootstrap/dist/css/bootstrap-theme.css', + 'public/lib/angular-json-tree/build/angular-json-tree.css' ], js: [ 'public/lib/angular/angular.js', @@ -14,7 +15,8 @@ module.exports = { 'public/lib/angular-ui-router/release/angular-ui-router.js', 'public/lib/angular-ui-utils/ui-utils.js', 'public/lib/angular-bootstrap/ui-bootstrap-tpls.js', - 'public/lib/angular-file-upload/angular-file-upload.js' + 'public/lib/angular-file-upload/angular-file-upload.js', + 'public/lib/angular-json-tree/build/angular-json-tree.min.js' ], tests: ['public/lib/angular-mocks/angular-mocks.js'] }, diff --git a/config/env/development.js b/config/env/development.js index 310189454b..abf0ff9afe 100644 --- a/config/env/development.js +++ b/config/env/development.js @@ -6,30 +6,30 @@ module.exports = { title: 'MEAN.JS - Development Environment' }, facebook: { - clientID: process.env.FACEBOOK_ID || 'APP_ID', - clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET', - callbackURL: '/api/auth/facebook/callback' - }, - twitter: { - clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY', - clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET', - callbackURL: '/api/auth/twitter/callback' - }, - google: { - clientID: process.env.GOOGLE_ID || 'APP_ID', - clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET', - callbackURL: '/api/auth/google/callback' - }, - linkedin: { - clientID: process.env.LINKEDIN_ID || 'APP_ID', - clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET', - callbackURL: '/api/auth/linkedin/callback' - }, - github: { - clientID: process.env.GITHUB_ID || 'APP_ID', - clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET', - callbackURL: '/api/auth/github/callback' - }, + clientID: process.env.FACEBOOK_ID || 'APP_ID', + clientSecret: process.env.FACEBOOK_SECRET || 'APP_SECRET', + callbackURL: '/api/auth/facebook/callback' + }, + twitter: { + clientID: process.env.TWITTER_KEY || 'CONSUMER_KEY', + clientSecret: process.env.TWITTER_SECRET || 'CONSUMER_SECRET', + callbackURL: '/api/auth/twitter/callback' + }, + google: { + clientID: process.env.GOOGLE_ID || 'APP_ID', + clientSecret: process.env.GOOGLE_SECRET || 'APP_SECRET', + callbackURL: '/api/auth/google/callback' + }, + linkedin: { + clientID: process.env.LINKEDIN_ID || 'APP_ID', + clientSecret: process.env.LINKEDIN_SECRET || 'APP_SECRET', + callbackURL: '/api/auth/linkedin/callback' + }, + github: { + clientID: process.env.GITHUB_ID || 'APP_ID', + clientSecret: process.env.GITHUB_SECRET || 'APP_SECRET', + callbackURL: '/api/auth/github/callback' + }, mailer: { from: process.env.MAILER_FROM || 'MAILER_FROM', options: { diff --git a/gruntfile.js b/gruntfile.js index e8abfe71ab..584cf1331e 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -138,8 +138,8 @@ module.exports = function (grunt) { expand: true, src: defaultAssets.client.sass, ext: '.css', - rename: function(base, src) { - return src.replace('/scss/', '/css/'); + rename: function (base, src) { + return src.replace('/scss/', '/css/'); } }] } @@ -150,8 +150,8 @@ module.exports = function (grunt) { expand: true, src: defaultAssets.client.less, ext: '.css', - rename: function(base, src) { - return src.replace('/less/', '/css/'); + rename: function (base, src) { + return src.replace('/less/', '/css/'); } }] } @@ -203,14 +203,14 @@ module.exports = function (grunt) { } }); - // Load NPM tasks + // Load NPM tasks require('load-grunt-tasks')(grunt); // Making grunt default to force in order not to break the project. grunt.option('force', true); // Connect to the MongoDB instance and load the models - grunt.task.registerTask('mongoose', 'Task that connects to the MongoDB instance and loads the application models.', function() { + grunt.task.registerTask('mongoose', 'Task that connects to the MongoDB instance and loads the application models.', function () { // Get the callback var done = this.async(); @@ -218,7 +218,7 @@ module.exports = function (grunt) { var mongoose = require('./config/lib/mongoose.js'); // Connect to database - mongoose.connect(function(db) { + mongoose.connect(function (db) { done(); }); }); diff --git a/modules/core/server/controllers/errors.server.controller.js b/modules/core/server/controllers/errors.server.controller.js index db81a8ec86..ae1497c274 100644 --- a/modules/core/server/controllers/errors.server.controller.js +++ b/modules/core/server/controllers/errors.server.controller.js @@ -3,14 +3,14 @@ /** * Get unique error field name */ -var getUniqueErrorMessage = function(err) { +var getUniqueErrorMessage = function (err) { var output; try { var fieldName = err.err.substring(err.err.lastIndexOf('.$') + 2, err.err.lastIndexOf('_1')); output = fieldName.charAt(0).toUpperCase() + fieldName.slice(1) + ' already exist'; - } catch(ex) { + } catch (ex) { output = 'Unique field already exist'; } @@ -20,9 +20,9 @@ var getUniqueErrorMessage = function(err) { /** * Get the error message from error object */ -exports.getErrorMessage = function(err) { +exports.getErrorMessage = function (err) { var message = ''; - + if (err.code) { switch (err.code) { case 11000: @@ -33,6 +33,9 @@ exports.getErrorMessage = function(err) { message = 'Something went wrong'; } } else { + if (!err.errors && err.message) { + return err.message; + } for (var errName in err.errors) { if (err.errors[errName].message) message = err.errors[errName].message; } diff --git a/modules/users/client/config/users.client.routes.js b/modules/users/client/config/users.client.routes.js index 9b0546360e..71e2300f6f 100644 --- a/modules/users/client/config/users.client.routes.js +++ b/modules/users/client/config/users.client.routes.js @@ -64,6 +64,15 @@ angular.module('users').config(['$stateProvider', state('password.reset.form', { url: '/:token', templateUrl: 'modules/users/views/password/reset-password.client.view.html' + }). + state('admin', { + abstract: true, + url: '/admin', + template: '' + }). + state('admin.users', { + url: '/users', + templateUrl: 'modules/users/views/admin/users.list.client.view.html' }); } ]); diff --git a/modules/users/client/controllers/admin/users.client.controller.js b/modules/users/client/controllers/admin/users.client.controller.js new file mode 100644 index 0000000000..d1266e4c3c --- /dev/null +++ b/modules/users/client/controllers/admin/users.client.controller.js @@ -0,0 +1,13 @@ +'use strict'; + +angular.module('users').controller('AdminUsersController', ['$scope', 'Users', 'Authentication', + function ($scope, Users, Authentication) { + + $scope.rows = Users.query(); + + $scope.identityToLink = function (identity) { + return 'noneYet'; + }; + + } +]); diff --git a/modules/users/client/controllers/settings.client.controller.js b/modules/users/client/controllers/settings.client.controller.js index 70b16c5151..2cbb830f05 100644 --- a/modules/users/client/controllers/settings.client.controller.js +++ b/modules/users/client/controllers/settings.client.controller.js @@ -1,14 +1,49 @@ 'use strict'; angular.module('users').controller('SettingsController', ['$scope', '$http', '$location', 'Users', 'Authentication', - function($scope, $http, $location, Users, Authentication) { + function ($scope, $http, $location, Users, Authentication) { $scope.user = Authentication.user; + $scope.linkedProviders = []; // If user is not signed in then redirect back home if (!$scope.user) $location.path('/'); + $scope.user.identities.forEach(function (identity) { + if (identity.provider === 'username') { + $scope.user.username = identity.id; + } + }); + + $scope.identitiesToEmails = function () { + $scope.user.emails = []; + if ($scope.user.identities) { + $scope.user.identities.forEach(function (identity, idx) { + if (identity.provider === 'email') { + $scope.user.identities.splice(idx, 1); + $scope.user.emails.push(identity); + } + }); + } + }; + $scope.identitiesToEmails(); + + $scope.removeEmail = function (emailIndex) { + $scope.user.emails.splice(emailIndex, 1); + return false; + }; + + $scope.addEmail = function () { + if (!$scope.user.identities) { + $scope.user.identities = []; + } + $scope.user.emails.push({ + provider: 'email', + id: '' + }); + }; + // Check if there are additional accounts - $scope.hasConnectedAdditionalSocialAccounts = function(provider) { + $scope.hasConnectedAdditionalSocialAccounts = function (provider) { for (var i in $scope.user.additionalProvidersData) { return true; } @@ -17,37 +52,51 @@ angular.module('users').controller('SettingsController', ['$scope', '$http', '$l }; // Check if provider is already in use with current user - $scope.isConnectedSocialAccount = function(provider) { - return $scope.user.provider === provider || ($scope.user.additionalProvidersData && $scope.user.additionalProvidersData[provider]); + $scope.isConnectedSocialAccount = function (provider) { + var connected = false; + angular.forEach($scope.user.identities, function (identity) { + if (identity.provider === provider) { + connected = true; + $scope.linkedProviders.push(provider); + } + }); + return connected; }; // Remove a user social account - $scope.removeUserSocialAccount = function(provider) { + $scope.removeUserSocialAccount = function (provider) { $scope.success = $scope.error = null; $http.delete('/api/users/accounts', { params: { provider: provider } - }).success(function(response) { + }).success(function (response) { // If successful show success message and clear form $scope.success = true; $scope.user = Authentication.user = response; - }).error(function(response) { + }).error(function (response) { $scope.error = response.message; }); }; // Update a user profile - $scope.updateUserProfile = function(isValid) { - if (isValid){ + $scope.updateUserProfile = function (isValid) { + if (isValid) { $scope.success = $scope.error = null; + + $scope.user.emails.forEach(function (email) { + $scope.user.identities.push(email); + }); + delete $scope.user.emails; + var user = new Users($scope.user); - - user.$update(function(response) { + + user.$update(function (response) { $scope.success = true; Authentication.user = response; - }, function(response) { + $scope.identitiesToEmails(); + }, function (response) { $scope.error = response.data.message; }); } else { @@ -56,14 +105,14 @@ angular.module('users').controller('SettingsController', ['$scope', '$http', '$l }; // Change user password - $scope.changeUserPassword = function() { + $scope.changeUserPassword = function () { $scope.success = $scope.error = null; - $http.post('/api/users/password', $scope.passwordDetails).success(function(response) { + $http.post('/api/users/password', $scope.passwordDetails).success(function (response) { // If successful show success message and clear form $scope.success = true; $scope.passwordDetails = null; - }).error(function(response) { + }).error(function (response) { $scope.error = response.message; }); }; diff --git a/modules/users/client/controllers/settings/edit-profile.client.controller.js b/modules/users/client/controllers/settings/edit-profile.client.controller.js index 8e1d423819..5f6da1e258 100644 --- a/modules/users/client/controllers/settings/edit-profile.client.controller.js +++ b/modules/users/client/controllers/settings/edit-profile.client.controller.js @@ -1,24 +1,52 @@ 'use strict'; angular.module('users').controller('EditProfileController', ['$scope', '$http', '$location', 'Users', 'Authentication', - function($scope, $http, $location, Users, Authentication) { + function ($scope, $http, $location, Users, Authentication) { $scope.user = Authentication.user; // Update a user profile - $scope.updateUserProfile = function(isValid) { - if (isValid){ + $scope.updateUserProfile = function (isValid) { + if (isValid) { $scope.success = $scope.error = null; var user = new Users($scope.user); - user.$update(function(response) { + user.$update(function (response) { $scope.success = true; Authentication.user = response; - }, function(response) { + }, function (response) { $scope.error = response.data.message; }); } else { $scope.submitted = true; } }; + + $scope.identitiesToEmails = function () { + $scope.user.emails = []; + if ($scope.user.identities) { + $scope.user.identities.forEach(function (identity, idx) { + if (identity.provider === 'email') { + $scope.user.identities.splice(idx, 1); + $scope.user.emails.push(identity); + } + }); + } + }; + $scope.identitiesToEmails(); + + $scope.removeEmail = function (emailIndex) { + $scope.user.emails.splice(emailIndex, 1); + return false; + }; + + $scope.addEmail = function () { + if (!$scope.user.identities) { + $scope.user.identities = []; + } + $scope.user.emails.push({ + provider: 'email', + id: '' + }); + }; } ]); diff --git a/modules/users/client/controllers/settings/manage-social-accounts.client.controller.js b/modules/users/client/controllers/settings/manage-social-accounts.client.controller.js index 74774f06d1..35ba8f93e8 100644 --- a/modules/users/client/controllers/settings/manage-social-accounts.client.controller.js +++ b/modules/users/client/controllers/settings/manage-social-accounts.client.controller.js @@ -1,36 +1,39 @@ 'use strict'; angular.module('users').controller('SocialAccountsController', ['$scope', '$http', '$location', 'Users', 'Authentication', - function($scope, $http, $location, Users, Authentication) { + function ($scope, $http, $location, Users, Authentication) { $scope.user = Authentication.user; // Check if there are additional accounts - $scope.hasConnectedAdditionalSocialAccounts = function(provider) { - for (var i in $scope.user.additionalProvidersData) { - return true; - } - - return false; + $scope.hasConnectedAdditionalSocialAccounts = function (provider) { + return true; }; // Check if provider is already in use with current user - $scope.isConnectedSocialAccount = function(provider) { - return $scope.user.provider === provider || ($scope.user.additionalProvidersData && $scope.user.additionalProvidersData[provider]); + $scope.isConnectedSocialAccount = function (provider) { + var has = false; + angular.forEach($scope.user.identities, function (identity) { + if (identity.provider === provider) { + has = true; + } + }); + return has; }; // Remove a user social account - $scope.removeUserSocialAccount = function(provider) { + $scope.removeUserSocialAccount = function (provider, id) { $scope.success = $scope.error = null; $http.delete('/api/users/accounts', { params: { - provider: provider + provider: provider, + id: id } - }).success(function(response) { + }).success(function (response) { // If successful show success message and clear form $scope.success = true; $scope.user = Authentication.user = response; - }).error(function(response) { + }).error(function (response) { $scope.error = response.message; }); }; diff --git a/modules/users/client/controllers/settings/settings.client.controller.js b/modules/users/client/controllers/settings/settings.client.controller.js index 25af58435f..524cdcbc96 100644 --- a/modules/users/client/controllers/settings/settings.client.controller.js +++ b/modules/users/client/controllers/settings/settings.client.controller.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('users').controller('SettingsController', ['$scope', '$http', '$location', 'Users', 'Authentication', - function($scope, $http, $location, Users, Authentication) { + function ($scope, $http, $location, Users, Authentication) { $scope.user = Authentication.user; // If user is not signed in then redirect back home diff --git a/modules/users/client/users.client.module.js b/modules/users/client/users.client.module.js index 569aba8c16..5d3164a205 100644 --- a/modules/users/client/users.client.module.js +++ b/modules/users/client/users.client.module.js @@ -1,4 +1,4 @@ 'use strict'; // Use Applicaion configuration module to register a new module -ApplicationConfiguration.registerModule('users'); +ApplicationConfiguration.registerModule('users', ['angular-json-tree']); diff --git a/modules/users/client/views/admin/users.list.client.view.html b/modules/users/client/views/admin/users.list.client.view.html new file mode 100644 index 0000000000..1c0966c995 --- /dev/null +++ b/modules/users/client/views/admin/users.list.client.view.html @@ -0,0 +1,34 @@ +
+

Browse Users

+ +
+ + + + + + + + + + + + + + + + + +
IDUser NameDataAccounts
{{ row._id | limitTo : 6 }}{{ row.displayName }} + + + +
+
+
diff --git a/modules/users/client/views/settings/edit-profile.client.view.html b/modules/users/client/views/settings/edit-profile.client.view.html index ae8cd30327..3e6f877918 100644 --- a/modules/users/client/views/settings/edit-profile.client.view.html +++ b/modules/users/client/views/settings/edit-profile.client.view.html @@ -1,22 +1,43 @@
-