From 9a143ffbbcb6b6b1212fd79d33369f75f75753f6 Mon Sep 17 00:00:00 2001
From: Alexander Olofsson <alexander.olofsson@liu.se>
Date: Mon, 11 Dec 2017 12:35:11 +0100
Subject: [PATCH] Working on some per-user limits

---
 client/App.vue          |  4 ++--
 client/ExternalUser.vue |  7 ++++---
 client/models/user.js   | 12 ++++++++++++
 server/auth.js          |  2 +-
 server/db.js            |  2 +-
 server/users.js         | 40 ++++++++++++++++++++++++++++++++++++++--
 6 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/client/App.vue b/client/App.vue
index 22b8317..69b03da 100644
--- a/client/App.vue
+++ b/client/App.vue
@@ -28,11 +28,11 @@
             <h4>Add external users</h4>
             <p>Employees and students are allowed to create external users for collaboration with actors outside of Linköpings University.<br/>
               This application will track and allow creation of such external users, up to a number configurable by the administrators.<br/></p>
-            <button class="btn btn-success" @click="showCreationForm = !showCreationForm">New External User</button>
+            <button class="btn btn-success" :disabled="external.length >= user.user_limit" @click="showCreationForm = !showCreationForm">New External User</button>
           </div>
         </transition>
 
-        <h2 class="mt-4 mb-3">Existing external users:</h2>
+        <h2 class="mt-4 mb-3">Existing external users: ({{ external.length }}/{{ user.user_limit }})</h2>
         <hr/>
         <transition name="fade" mode="out-in">
           <ul class="list-unstyled" is="transition-group" name="flip-list" v-if="external">
diff --git a/client/ExternalUser.vue b/client/ExternalUser.vue
index 52432c1..7660f40 100644
--- a/client/ExternalUser.vue
+++ b/client/ExternalUser.vue
@@ -3,12 +3,13 @@
     <img v-bind:src="user.avatar_url" class="d-flex align-self-center mr-3 rounded-circle user-avatar" alt="Avatar"/>
     <div class="media-body">
       <h4 class="mt-0">{{ user.name }}</h4>
-      <p class="text-muted"><a v-bind:href="user.web_url">@{{ user.username }}</a> - Created at {{ user.created_at | readable_date }}</p>
+      <p class="text-muted"><a v-bind:href="user.web_url">@{{ user.username }}</a> - {{ user.state }} - Created at {{ user.created_at | readable_date }}</p>
 
       <!-- Read if current user is an admin, show advanced features -->
       <template v-if="false">
-        <a href="#" @click.prevent="user.http.fetch()" class="btn btn-sm btn-primary" title="Reload User Data"><i class="fa fa-cog"></i></a>
-        <a href="#" @click.prevent="user.http.destroy()" class="btn btn-sm btn-danger" title="Remove User"><i class="fa fa-eraser"></i></a>
+        <a href="#" @click.prevent="user.http.fetch()" class="btn btn-sm btn-outline-primary" title="Reload User Data"><i class="fa fa-cog"></i></a>
+        <a href="#" @click.prevent="user.state == 'blocked' ? user.http.unblock() : user.http.block()" class="btn btn-sm btn-outline-warning" :title="user.state == 'blocked' ? 'Unblock' : 'Block'"><i class="fa" :class="{ 'fa-lock': user.state != 'blocked', 'fa-unlock': user.state == 'blocked' }"></i></a>
+        <a href="#" @click.prevent="user.http.destroy()" class="btn btn-sm btn-outline-danger" title="Remove User"><i class="fa fa-eraser"></i></a>
       </template>
     </div>
   </li>
diff --git a/client/models/user.js b/client/models/user.js
index b6c26e1..b1be20a 100644
--- a/client/models/user.js
+++ b/client/models/user.js
@@ -35,6 +35,18 @@ var User = module.exports = {
         }
       },
       update: false,
+      block: {
+        method: 'POST',
+        route: '{id}/block',
+        apply: true,
+        data: false,
+      },
+      unblock: {
+        method: 'POST',
+        route: '{id}/unblock',
+        apply: true,
+        data: false,
+      }
     }
   },
 };
diff --git a/server/auth.js b/server/auth.js
index 68fecbd..9eba839 100644
--- a/server/auth.js
+++ b/server/auth.js
@@ -40,7 +40,7 @@ router.get('/', (req, res) => {
   console.log('GET: /auth');
 
   if (req.user) {
-    res.send(req.user._json);
+    res.send(Object.assign({}, req.user._json, { user_limit: config.external_limit }));
   } else {
     res.status(401).send({ message: 'Not authenticated' });
   }
diff --git a/server/db.js b/server/db.js
index 62bc830..47620d8 100644
--- a/server/db.js
+++ b/server/db.js
@@ -16,7 +16,7 @@ CREATE TABLE IF NOT EXISTS external_users (
   user_id INTEGER NOT NULL,
   owner_id INTEGER NOT NULL,
   username VARCHAR(256) NOT NULL,
-  active BOOLEAN DEFAULT TRUE,
+  state VARCHAR(16) DEFAULT 'active',
   created_at TIMESTAMP DEFAULT NOW(),
   updated_at TIMESTAMP DEFAULT NOW()
 );
diff --git a/server/users.js b/server/users.js
index 533d985..f869370 100644
--- a/server/users.js
+++ b/server/users.js
@@ -13,6 +13,26 @@ const axios  = require('axios').create({
   }
 });
 
+router.get('/audit', async (req, res) => {
+  console.log('GET: /users/audit');
+
+  try {
+    const queryText = 'SELECT * FROM audit_events WHERE user_id = $1';
+    console.log('> DB Query:');
+    console.log(queryText);
+    const dbResponse = await db.query(queryText, [req.user.id]);
+    console.log('> DB Response:');
+    console.log(dbResponse);
+    var rows = dbResponse.rows;
+
+    res.send(rows);
+  } catch(err) {
+    console.log("> DB Error:");
+    console.log(err);
+    return res.status(500).send({ 'message': "Database error occured" });
+  }
+});
+
 router.get('/', async (req, res) => {
   console.log('GET: /users');
 
@@ -25,7 +45,7 @@ router.get('/', async (req, res) => {
     console.log(dbResponse);
     var rows = dbResponse.rows;
 
-    res.send(rows.map((row) => row.id));
+    res.send(rows.map((row) => row.user_id));
   } catch(err) {
     console.log("> DB Error:");
     console.log(err);
@@ -40,6 +60,22 @@ router.post('/', async (req, res) => {
     .filter( key => !['admin', 'skip_confirmation'].includes(key) )
     .reduce( (rs, key) => (rs[key] = req.body[key], rs), {} );
 
+  try {
+    const queryText = 'SELECT * FROM external_users WHERE owner_id = $1';
+    console.log('> DB Query:');
+    console.log(queryText);
+    const dbResponse = await db.query(queryText, [req.user.id]);
+    console.log('> DB Response:');
+    console.log(dbResponse);
+
+    if (dbResponse.rowCount >= config.external_limit) {
+      return res.status(400).send({ 'message': 'External user limit reached' });
+    }
+  } catch(err) {
+    console.log("> DB Error:");
+    console.log(err);
+    return res.status(500).send({ 'message': "Database error occured" });
+  }
   // TODO: Apply further validation on input
   // XXX   Make sure user is allowed to create
 
@@ -71,7 +107,7 @@ router.post('/', async (req, res) => {
       const queryText = 'INSERT INTO audit_events(event, user_id, message) VALUES($1, $2, $3)';
       console.log('> DB Query:');
       console.log(queryText);
-      const auditresp = await db.query(queryText, ['user.create', req.user.id, `Created account: ${dbResponse.name} <${dbResponse.email}>, username: ${dbResponse.username}`]);
+      const auditresp = await db.query(queryText, ['user.create', req.user.id, `Created account: ${data.name} <${data.email}>, username: ${data.username}`]);
       console.log('> DB Response:');
       console.log(auditresp);
     } catch(err) {
-- 
GitLab