diff --git a/lib/routes/user/user.js b/lib/routes/user/user.js new file mode 100644 index 00000000..7b276d71 --- /dev/null +++ b/lib/routes/user/user.js @@ -0,0 +1,58 @@ +var models = require('../../models') +var mongoose = require('mongoose') +var express = require('express') + +exports.unauthorized = function (app, template) { + template([ + 'view' + ], { + prefix: 'user' + }); +} + +exports.httpRouter = function (app) { +} + +exports.router = function (app) { + var user = express.Router(); + + user.get('/', view) + .put('/', save) + .delete('/', remove) + + app.param('user_id', get) + app.use('/user/:user_id', user); +} + +function get (req, res, next, id) { + models.User.findOne({ + _id: id + }).select('-password').exec(function (err, identity) { + if (err || !user) { + return res.send(404); + } + + req.user = user; + next(); + }); +} + +function view (req, res) { + res.send(req.user); +} + +function save (req, res) { + req.user.name = req.body.name; + models.User.hashPassword(req.body.password, function (hash) { + req.user.password = hash; + }); + + req.user.save(); + res.send(201); +} + +function remove (req, res) { + req.user.remove(function (err) { + res.send(201); + }) +} diff --git a/lib/routes/user/users.js b/lib/routes/user/users.js new file mode 100644 index 00000000..ea2b4cbf --- /dev/null +++ b/lib/routes/user/users.js @@ -0,0 +1,52 @@ +var models = require('../../models') +var mongoose = require('mongoose') + +var identity = require('./user') + +var validator = require('validator') + +exports.unauthorized = function (app, template) { + template([ + 'add', + 'list' + ], { + prefix: 'user' + }); + + user.unauthorized(app, template); +} + +exports.httpRouter = function (app) { + user.httpRouter(app); +} + +exports.router = function (app) { + app.get('/users', get) + .post('/users', add) + + user.router(app); +} + +function get (req, res) { + models.User.find({ + }).sort('-created').select('-password').exec(function (err, identities) { + res.send(users) + }) +} + +function add (req, res) { + if (!validator.isLength(req.body.name, 1)) { + return res.send(400); + } + + var user = new models.User({ + name: req.body.name, + password: req.body.password, + email: req.body.email, + username: req.body.username + }); + + user.save(function () { + res.send(user); + }); +} diff --git a/lib/views/layout.jade b/lib/views/layout.jade index 70694e22..1ca118b1 100644 --- a/lib/views/layout.jade +++ b/lib/views/layout.jade @@ -22,6 +22,7 @@ html nav.nav.navbar-nav li: a(ui-sref="playbooks") Playbooks li: a(ui-sref="identities.list") Identities + li: a(ui.sref="users.list") Users nav.nav.navbar-nav.pull-right li: a(ui-sref="addPlaybook") Add Playbook @@ -38,4 +39,4 @@ html script(src="/vendor/requirejs/require.js" data-main="/js/semaphore.js") //- page-specific js - block addonjs \ No newline at end of file + block addonjs diff --git a/lib/views/user/add.jade b/lib/views/user/add.jade new file mode 100644 index 00000000..515905c1 --- /dev/null +++ b/lib/views/user/add.jade @@ -0,0 +1,32 @@ +h1 New User + button.btn.btn-default.pull-right(ui-sref="identities.list") + i.fa.fa-fw.fa-chevron-left + | Back +.row + .col-md-6 + .panel.panel-default + .panel-body + form.form-horizontal(name="userForm") + .form-group + label.control-label.col-sm-4(for="name") Name + .col-sm-7 + input#name.form-control(type="text" placeholder="Display Name" ng-model="user.data.name" autofocus required) + + .form-group + label.control-label.col-sm-4(for="password") Password + .col-sm-7 + input#password.form-control(type="password" placeholder="Vault Password" ng-model="user.data.password" required) + + .form-group + label.control-label.col-sm-4(for="username") Username + .col-sm-7 + input#username.form-control(type="text" placeholder="Private Key" ng-model="user.data.username" required) + + .form-group + label.control-label.col-sm-4(for="email") Email + .col-sm-7 + textarea#email.form-control(type="text" placeholder="Public Key Contents" ng-model="user.data.email" required) + + .form-group + .col-sm-7.col-sm-offset-4 + button.btn.btn-success(ng-click="add()" ng-disabled="userForm.$invalid") Add diff --git a/lib/views/user/list.jade b/lib/views/user/list.jade new file mode 100644 index 00000000..0c9fc2d5 --- /dev/null +++ b/lib/views/user/list.jade @@ -0,0 +1,12 @@ +h1 Users + button.btn.btn-default.pull-right(ui-sref="users.add") + i.fa.fa-fw.fa-plus + | Add + +table.table + tr(ng-repeat="user in users") + td: a(ui-sref="user.view({ user_id: user._id })") {{ user.name }} + +p(ng-show="identities.length == 0") No Users + +blockquote Users are the user accounts that have access to Semaphore. diff --git a/lib/views/user/view.jade b/lib/views/user/view.jade new file mode 100644 index 00000000..237de1d9 --- /dev/null +++ b/lib/views/user/view.jade @@ -0,0 +1,14 @@ +h1 {{ user.data.name }} + button.btn.pull-right.btn-danger(ng-click="delete()") + i.fa.fa-trash.fa-fw + | Delete + +p Created on {{ user.data.created }} + +hr + +h3 Username + code {{ user.data.username }} + +h3 Email + code {{ user.data.email }} diff --git a/public/js/controllers/user/add.js b/public/js/controllers/user/add.js new file mode 100644 index 00000000..7b0d0037 --- /dev/null +++ b/public/js/controllers/user/add.js @@ -0,0 +1,21 @@ +define([ + 'app', + 'factories/identity', + 'services/identities' +], function(app) { + app.registerController('AddIdentityCtrl', ['$scope', '$state', 'Identity', function($scope, $state, Identity) { + $scope.identity = new Identity(); + + $scope.add = function () { + $scope.identity.add() + .success(function (data) { + $state.transitionTo('identity.view', { + identity_id: data._id + }); + }) + .error(function (data) { + + }) + } + }]); +}); \ No newline at end of file diff --git a/public/js/controllers/user/user.js b/public/js/controllers/user/user.js new file mode 100644 index 00000000..a155f10d --- /dev/null +++ b/public/js/controllers/user/user.js @@ -0,0 +1,11 @@ +define([ + 'app' +], function(app) { + app.registerController('UserCtrl', ['$scope', '$state', function($scope, $state) { + $scope.delete = function () { + $scope.user.delete(); + + $state.transitionTo('users.list'); + } + }]); +}); diff --git a/public/js/controllers/user/users.js b/public/js/controllers/user/users.js new file mode 100644 index 00000000..051ab9c3 --- /dev/null +++ b/public/js/controllers/user/users.js @@ -0,0 +1,10 @@ +define([ + 'app', + 'services/users' +], function(app) { + app.registerController('UsersCtrl', ['$scope', '$state', 'users', function($scope, $state, users) { + users.getUsers(function () { + $scope.users = users.users; + }); + }]); +}); diff --git a/public/js/factories/user.js b/public/js/factories/user.js new file mode 100644 index 00000000..e9ea484f --- /dev/null +++ b/public/js/factories/user.js @@ -0,0 +1,29 @@ +define(['app'], function (app) { + app.registerFactory('User', ['$http', function ($http) { + var Model = function (id) { + if (!id) { + return; + } + + this.id = id; + } + + Model.prototype.save = function () { + return $http.put('/user/'+this.data._id, this.data); + } + + Model.prototype.add = function () { + return $http.post('/users', this.data); + } + + Model.prototype.delete = function () { + return $http.delete('/user/'+this.data._id); + } + + Model.prototype.get = function () { + return $http.get('/user/'+this.id); + } + + return Model; + }]) +}) diff --git a/public/js/routes/routes.js b/public/js/routes/routes.js index d3a0f906..eca87eb0 100644 --- a/public/js/routes/routes.js +++ b/public/js/routes/routes.js @@ -4,6 +4,7 @@ define([ 'services/user', 'routes/playbooks', 'routes/identities', + 'routes/users', 'services/playbooks' ], function(app, io) { app.config(function($stateProvider, $urlRouterProvider, $locationProvider, $couchPotatoProvider) { @@ -11,16 +12,16 @@ define([ enabled: true, requireBase: false }) - + $urlRouterProvider.otherwise(''); - + $stateProvider .state('homepage', { url: '/', pageTitle: 'Homepage', templateUrl: "/view/homepage" }) - + .state('logout', { url: '/logout', pageTitle: 'Log Out', diff --git a/public/js/routes/users.js b/public/js/routes/users.js new file mode 100644 index 00000000..95462260 --- /dev/null +++ b/public/js/routes/users.js @@ -0,0 +1,70 @@ +define([ + 'app', + 'factories/user' +], function(app) { + app.config(function($stateProvider, $couchPotatoProvider) { + $stateProvider + + .state('users', { + url: '/users', + templateUrl: '/view/abstract', + abstract: true + }) + .state('users.add', { + url: '/add', + pageTitle: 'Add User', + templateUrl: "/view/user/add", + controller: 'AddUserCtrl', + resolve: { + dummy: $couchPotatoProvider.resolve(['controllers/user/add']) + } + }) + .state('users.list', { + url: '/all', + pageTitle: 'Users', + templateUrl: "/view/user/list", + controller: 'UsersCtrl', + resolve: { + dummy: $couchPotatoProvider.resolve(['controllers/user/users']) + } + }) + + .state('user', { + abstract: true, + url: '/user/:user_id', + templateUrl: '/view/abstract', + controller: ['$scope', 'user', function ($scope, user) { + $scope.user = user; + }], + resolve: { + user: ['User', '$stateParams', '$q', '$state', function (User, $stateParams, $q, $state) { + var deferred = $q.defer(); + + var user = new User($stateParams.user_id) + user.get() + .success(function (data, status) { + user.data = data; + deferred.resolve(user); + }) + .error(function (data, status) { + if (status == 404) { + $state.transitionTo('homepage'); + return deferred.reject(); + } + }); + + return deferred.promise; + }] + } + }) + + .state('user.view', { + url: '/', + controller: 'UserCtrl', + templateUrl: '/view/user/view', + resolve: { + dummy: $couchPotatoProvider.resolve(['controllers/user/user']) + } + }) + }) +})