<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Java Script &#8211; Digitaldocblog</title>
	<atom:link href="https://digitaldocblog.com/tag/java-script/feed/" rel="self" type="application/rss+xml" />
	<link>https://digitaldocblog.com</link>
	<description>Various digital documentation</description>
	<lastBuildDate>Sat, 13 Aug 2022 16:46:50 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>

<image>
	<url>https://digitaldocblog.com/wp-content/uploads/2022/08/cropped-website-icon-star-500-x-452-transparent-32x32.png</url>
	<title>Java Script &#8211; Digitaldocblog</title>
	<link>https://digitaldocblog.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Role Based Access Control using express-session in a node-js app</title>
		<link>https://digitaldocblog.com/webdesign/role-based-access-control-using-express-session-in-a-node-js-app/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Tue, 04 May 2021 12:00:00 +0000</pubDate>
				<category><![CDATA[Web-Development]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[Webserver]]></category>
		<category><![CDATA[Express.js]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[Java Script]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Mongoose]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[NPM Node package manager]]></category>
		<guid isPermaLink="false">https://digitaldocblog.com/?p=141</guid>

					<description><![CDATA[In this article I refer to an application I created a couple of months ago. It&#8217;s about a booking system with which players can book ice-hockey trainings in different locations,&#8230;]]></description>
										<content:encoded><![CDATA[
<p>In this article I refer to an application I created a couple of months ago. It&#8217;s about a booking system with which players can book ice-hockey trainings in different locations, the coach can confirm participation in a training session  and a club manager can organize training sessions and bill the players for booked trainings. You can see the code on my <a href="https://github.com/prottlaender/bookingsystem" title="Node-Js Booking-System Code on GitHub Account of Patrick Rottlaender">GitHub Account</a> and read a detailed application description in the style of a user manual on my blog <a href="https://digitaldocblog.com/singleblog?article=9" title="Booking-System Application Description on Digitaldocblog Website owned by Patrick Rottlaender">Digitaldocblog</a>.</p>



<p>In my booking system I give users different roles in my app and depending on their role, the users have different authorizations. An <em>admin</em> for example is able to access more sensitive data and functionalities than a normal <em>player</em> or a <em>coach</em>. So my app must know the role of a user to assign different authorizations to the particular user.</p>



<p>Clients, usually browsers send requests the app. The app responds to requests and is solely responsible for ensuring that the client only has access to the data that are intended for it. This request and response game is based on the HTTP protocol. HTTP is a stateless network protocol and requests cannot be related to each other. Each request is isolated and unrelated to previous requests and the server has no chance to recognize clients and does therefore not know their role. </p>



<p>This problem can be solved with sessions and cookies and means that session management must be implemented in the application. The application creates a session and stores session data such as the role of a requestor in this session. The session has a unique ID and the app saves only this ID in a cookie. The cookie is transferred to the browser and stored locally there. From now on, the browser always sends this cookie with the HTTP request and thus identifies itself to the application. The application can check the role of the requestor in the stored session data and control the appropriate access.</p>



<h3 class="wp-block-heading">Basic setup of the server</h3>



<p>First we need a working Server OS. I run Linux Ubuntu in production and have written an article about the <a href="https://digitaldocblog.com/singleblog?article=10" title="Basic Setup for Node-Js Apps running on Ubuntu Linux on Digitaldocblog Website owned by Patrick Rottländer ">basic setup of a production Linux server</a> on my blog site <a href="https://digitaldocblog.com/home?currpage=1" title="Digitaldocblog Website owned by Patrick Rottlaender">Digitaldocblog</a>. Since I am going to store the sessions in a MongoDB, MongoDB must be installed on the Linux server. I use <em>MongoDB Community Edition</em> but you can also install or upgrade to the <em>MongoDB Enterprise</em> Server version. In the lower part of the article you find the instructions how to install and setup your <em>MongoDB Community Edition</em> on your Linux System. In case you want to read the original documentation go on the MongoDB site and read how to install the <a href="https://docs.mongodb.com/manual/administration/install-community/" title="MongoDB Community Edition Documentation">MongoDB Community Edition</a> for your OS.  </p>



<p>In my express application I use a number of external modules or dependencies that have to be installed for the application in order for the application to run. In the repository of the <a href="https://github.com/prottlaender/bookingsystem" title="Node-Js Booking-System Code on GitHub Account of Patrick Rottlaender">bookingsystem</a>on my <a href="https://github.com/prottlaender" title="GitHub Account of Patrick Rottlaender">GitHub account</a> you find the <a href="https://github.com/prottlaender/bookingsystem/blob/master/package.json" title="package.json file of Booking System on GitHub Account of Patrick Rottlaender">package.json</a> file which contains all the necessary dependencies. In principle, it is sufficient if you put this <em>package.json</em> file in your application main directory and install all dependencies with <code>npm install</code>. </p>



<p>Alternatively, of course, all modules can also be installed individually with </p>



<p><code>npm install &lt;module&gt; --save</code></p>



<h3 class="wp-block-heading">Session Management</h3>



<p>I discuss in the first part different code snippets in my application main file <a href="https://github.com/prottlaender/bookingsystem/blob/master/booking.js" title="booking.js file of Booking System on GitHub Account of Patrick Rottlaender">booking.js</a>. The goal here is that you understand how session management is basically implemented.</p>



<pre class="wp-block-code"><code>// booking.js

// Load express module and create app
const express = require('express');
const app = express();
// Trust the first Proxy
app.set('trust proxy', 1);
// Load HTTP response header security module
const helmet = require('helmet');
// use secure HTTP headers using helmet with every request
app.use(
  helmet({
      frameguard: {
        action: "deny",
      },
      referrerPolicy: {
        policy: "no-referrer",
    },
    })
  );
// Load envy module to manage environment variables
const envy = require('envy');
const env = envy();

// Set environment variables
const port = env.port
const host = env.host
const mongodbpath = env.mongodbpath
const sessionsecret = env.sessionsecret
const sessioncookiename = env.sessioncookiename
const sessioncookiecollection = env.sessioncookiecollection

// Load server side session and cookie module
const session = require('express-session');
// Load mongodb session storage module
const connectMdbSession = require('connect-mongodb-session');
// Create MongoDB session store object
const MongoDBStore = connectMdbSession(session)
// Create new session store in mongodb
const store = new MongoDBStore({
  uri: mongodbpath,
  collection: sessioncookiecollection
});
// Catch errors in case session store creation fails
store.on('error', function(error) {
  console.log(`error store session in session store: ${error.message}`);
});
// Use session to create session and session cookie
app.use(session({
  secret: sessionsecret,
  name: sessioncookiename,
  store: store,
  resave: false,
  saveUninitialized: false,
  // set cookie to 1 week maxAge
  cookie: {
    maxAge: 1000 * 60 * 60 * 24 * 7,
    sameSite: true
  },
}));

... //further code not taken into account at this point
</code></pre>



<p>I create a server application using the <a href="https://www.npmjs.com/package/express" title="Express-Js Web-Application Framework for node-js">Express-js&nbsp;</a> Web Application Framework. Therefore I load the Express-js  module with the <code>require()</code> function and store the <code>express()</code> function in the constant <em>app</em>. Because my app is running behind a reverse proxy server I set the app to trust the first proxy server. Then I load the <a href="https://www.npmjs.com/package/helmet" title="Helmet Package for node-js">helmet module</a> to use secure response headers in my app. I configure that all browsers should deny iFrames and that my app will set no referrer in the response header. </p>



<p>I use the <a href="https://www.npmjs.com/package/envy" title="Envy module for node-js">envy module</a> in my application to manage environment variables. Therefore I load the module with <code>require()</code> and store the <code>envy()</code> function in the constant <em>env</em>. With envy you can define your environment variables in your <em>.env</em> and <em>.env.example</em> files. These files must be stored in the application main directory as explained in the envy documentation. </p>



<p>Since my booking app is a real web application running on a web server in production I can not discuss the real environment variables because of security reasons. Therefore let us see how this work and make an example <em>.env</em> file. </p>



<pre class="wp-block-code"><code>// .env

port=myport
host=myhost
mongodbpath=myexamplemongodbpath
sessionsecret=myexamplesecret
sessioncookiename=booking
sessioncookiecollection=col_sessions

</code></pre>



<p>These variables have different values in my <em>.env</em> file. In the <a href="https://github.com/prottlaender/bookingsystem/blob/master/booking.js" title="booking.js file">booking.js</a>file above I define the constant <em>env</em> for the envy function with <code>env = envy()</code> . Then I have access to the environment variables defined in my <em>.env</em> file with <em>env.&lt;variable&gt;</em>. I define constants for each variable and assign the variable from the .env file with <code>env.&lt;variable&gt;</code>. These constants can now be used as values in the code. </p>



<p>I load the <a href="https://www.npmjs.com/package/express-session" title="Node-Js Express-Session Module">express-session</a> module and the <a href="https://www.npmjs.com/package/connect-mongodb-session" title="Node-Js Connect-MongoDB-Session Module">connect-mongodb-session</a> module with the <code>require()</code> function. The session module stored in the constant <em>session</em> takes over the entire control of the session and cookie management. </p>



<p>The <em>connect-mongodb-session</em> stored in the constant <em>connectMdbSession</em> module is basically responsible for storing the session in the database. That is why we pass <em>session</em> as a parameter in the code and assign the constant <em>MongoDBstore</em>.</p>



<p><code>const MongoDBstore = connectMdbSession(session)</code> </p>



<p>With <code>new MongoDBStore</code> I create a new store object. Here I pass the <em>uri</em> of the mongodb path and the <em>collection</em> where sessions should be stored. </p>



<pre class="wp-block-code"><code>// booking.js
...

const store = new MongoDBStore({
  uri: mongodbpath,
  collection: sessioncookiecollection
});

...

</code></pre>



<p>The store object initialized in this way contains all necessary parameters to successfully store a session object in my MongoDB database.</p>



<p>After we have defined the storage of the session object, we take care of the session object itself. </p>



<p>With <code>app.use(session( {... cookie: {...} }))</code> I create a session object with various options. The session object will be created with each request and also contains a cookie object. I pass the values for <code>cookie: {...}</code> and then other options like<code>secret: sessionsecret</code>, the session object name with <code>name: sessioncookiename</code> as well as the location where the session object should be stored with <code>store: store</code>. Furthermore the session object has the option <code>saveUninitialized: false</code> and <code>resave: false</code> . </p>



<p>When the <em>saveUninitialized</em> option is set to <em>false</em> the session object is <em>not stored</em> into the store as long as the session is <strong>un-initialized</strong>. The option <code>resave: false</code> enforce that a session will <em>not be saved back</em> to the store even if the session is <strong>initialized</strong>.  So we must understand what <em>initialized</em> and <em>un-initialized</em> mean. This must be explained.</p>



<p>A browser send a request to the app. More precisely, the browser sends the request to a defined endpoint in the app. An endpoint defines a path within the app that reacts to HTTP requests and executes code. Depending on the HTTP method GET or POST, the endpoint expects that the requestor requires a document back (GET) or that the requestor wants to send data to the app (POST).</p>



<p>In the example below the browser should send a GET request to GET <em>home</em> endpoint. The endpoint render the HTML template <em>index</em> and send the HTML back to the browser. Then the request is finished. So this process, which starts with the <em>request</em> and ends with the <em>response</em> is the <em>runtime of the request</em>. </p>



<p>In the code snippet below you see 2 GET endpoints in the <a href="https://github.com/prottlaender/bookingsystem/blob/master/booking.js" title="booking.js file">booking.js</a> file one for the <em>home</em> route and another one for the <em>register</em> route.  </p>



<pre class="wp-block-code"><code>// booking.js

... //further code not taken into account at this point

// GET home route only for anonym users. Authenticated users redirected to dashboard
app.get('/', redirectDashboard, (req, res) =&gt; {
	
  console.log(req.url);
  console.log(req.session.id);
  console.log(req.session);

  res.render('index', {
      title: 'User Login Page',
    });

});

// GET register route only for anonym users. Authenticated users redirected to dashboard
app.get('/register', redirectDashboard, (req, res) =&gt; {
  
  console.log(req.url);
  console.log(req.session.id);
  console.log(req.session);

  res.render('register', {
      title: 'User Registration Page',
    });
});

... //further code not taken into account at this point


</code></pre>



<p>The code with <code>app.use (session({ ... }))</code> in my <a href="https://github.com/prottlaender/bookingsystem/blob/master/booking.js" title="booking.js file">booking.js</a> file ensures that a session object is always generated with each request. As long as a session object is not changed during the runtime of a request a separate session is created for each request and has its own session ID. The option <code>saveUninitialized: false</code> ensure that the session object will not be stored into the database. Each session object created in this way is <strong>un-initialized</strong>. </p>



<p>You can see the following output on the console for the <em>home</em> route and for the <em>register</em> route when we log the <em>path</em>, the <em>session-ID</em> and the <em>session object</em> on the console for each route.</p>



<pre class="wp-block-code"><code>/
BmbE8RVoTRcPP9nUnBm5JLE1w1mQiNyt
Session {
  cookie: {
    path: '/',
    _expires: 2021-04-24T04:27:04.265Z,
    originalMaxAge: 604800000,
    httpOnly: true,
    sameSite: true
  }
}

/register
awlPO-KpyVM51Gp6UAoeXGGmRWo-QFtP
Session {
  cookie: {
    path: '/',
    _expires: 2021-04-24T05:54:57.439Z,
    originalMaxAge: 604800000,
    httpOnly: true,
    sameSite: true
  }
}
  
</code></pre>



<p>The code of my app changes a session object during the runtime of a request by adding a data object when a user has successfully logged in. I will explain the code in detail in the next chapter but at the moment it is enough to know that. Therefore we play through the login of a user as follows.</p>



<p>The browser send a GET request to the <em>home</em> route as explained above, then the <em>index</em> template is rendered and the HTML page with the login form is sent back to the browser. During the runtime of this GET request a session object is created but the session object has not changed. We have already seen this above.</p>



<p>Then the user enters <em>email</em> and <em>password</em> in the login form and click submit. With this submit the browser send a POST request to the POST endpoint <em>/loginusers</em> and again a session object is generated for this POST request. During the runtime of the POST request, the code checks whether the transferred credentials are correct. If the credentials are correct, a data object with user data is generated and attached to the session object. Here the session object is changed during the runtime of the POST request. The existing session created with the POST request is now <strong>initialized</strong> at that moment. Because of the option <code>saveUninitialized: false</code> this session object is stored into the database store. When we look into the database store using the tool <a href="https://www.mongodb.com/products/compass" title="MongoDB Compass Management Console ">MongoDB Compass</a> we see that the entire session object has been saved into the <em>col<em>sessions</em></em> collection including the data object containing the required data of the user.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img fetchpriority="high" decoding="async" width="2460" height="1126" src="https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object.png" alt="Session Object saved in the *col_sessions* " class="wp-image-136" srcset="https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object.png 2460w, https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-300x137.png 300w, https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-1024x469.png 1024w, https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-768x352.png 768w, https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-1536x703.png 1536w, https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-2048x937.png 2048w" sizes="(max-width: 2460px) 100vw, 2460px" /><figcaption>Session Object saved in the *col_sessions* </figcaption></figure>
</div>


<p>After the session initialization the code called by the POST endpoint redirect the request and send a new GET request to the <em>/dashboard</em> route. The code with <code>app.use(session({ ... }))</code>  is called again but now there is an initialized session existing in the store. Because of the option <code>resave: false</code> the existing session object will not be updated and dragged along unchanged with every further request.</p>



<p>You see this in the output on the console when we log the <em>path</em>, the <em>session-ID</em> and the <em>session object</em> on the console for each route. The first output on the console is created when the GET request is sent to the <em>home</em> route. Then, the second output, after the user clicked submit the POST route <em>/loginusers</em> is called and a new session object is created. You see this from the different session IDs. During the runtime of this POST request the data object is added to the session object which initializes the session. Then, the third output, the GET route <em>/dashboard</em> is called and we see the same session object ID but the session object now contain the data object with the user data.</p>



<pre class="wp-block-code"><code>/
TEAZITdX7nLWBDc8uOk2HhXIiMZO7W-4
Session {
  cookie: {
    path: '/',
    _expires: 2021-05-02T07:21:13.236Z,
    originalMaxAge: 604800000,
    httpOnly: true,
    sameSite: true
  }
}

/loginusers
gVlKut3bdEMiDHnK455FGjCi6YbPTBuZ
Session {
  cookie: {
    path: '/',
    _expires: 2021-05-02T07:21:35.202Z,
    originalMaxAge: 604800000,
    httpOnly: true,
    sameSite: true
  }
}

/dashboard
gVlKut3bdEMiDHnK455FGjCi6YbPTBuZ
Session {
  cookie: {
    path: '/',
    _expires: 2021-05-02T07:21:35.468Z,
    originalMaxAge: 604800000,
    httpOnly: true,
    secure: null,
    domain: null,
    sameSite: true
  },
  data: {
    userId: 5f716b7439777365c18639f1,
    status: 'active',
    name: 'Oskar David',
    lastname: 'Rottländer',
    email: 'oskar@test.com',
    role: 'player',
    age: 17,
    cat: 'youth'
  }
}

</code></pre>



<p>In summary, session management works as follows: A session object will be created with each request and the session object is only saved in the database when the user is logged in (<em>saveUninitialized: false</em>). As long as the user is logged in, the session object is not changed and the data of the session object in the database are not updated (<em>resave: false</em>).</p>



<p><strong>But what happens to the cookie ?</strong> This will be explained in the next chapter.</p>



<h3 class="wp-block-heading">User login</h3>



<p>When the session has been initialized the cookie containing the session ID is stored in the browser of the requestor. With every request the browser provide the cookie to authenticate the requestor. To authenticate the requestor the code <code>app.use(session({...}))</code>  is called and compare the session ID sent by the browser with the session IDs stored in the session store. If a session ID matches, the session object including the data object is attached to the request object to give the app access to the data object. Within the app we now have access to any attribute of the data object with <em>req.session.data.&lt;attribute&gt;</em>. Therefore we can now implement role based authorization by accessing the role of the requestor with <em>req.session.data.role</em> and use this information in conditions in the code to control access depending on the role of the requestor. </p>



<p>But lets start from the beginning with the login of the requestor or user as I call the requestor from now on. In order for a user to be able to login, he or she must first call the <strong>login page</strong> which can be displayed by calling up the home endpoint. </p>



<pre class="wp-block-code"><code>// booking.js

... // Code not discussed here

// Redirect GET requests from authenticated users to dashboard
const redirectDashboard = (req, res, next) =&gt; {
  if (req.session.data) {
    res.redirect('/dashboard')

  } else {
    next()

  }
}

... // Code not discussed here

// GET home route only for anonym users. Authenticated users redirected to dashboard

app.get('/', redirectDashboard, (req, res) =&gt; {

  res.render('index', {
      title: 'User Login Page',
    });

});

... // Code not discussed here

</code></pre>



<p>As you see above in the code I have first defined the middleware function <em>redirectDashboard</em>. This middleware ensure that only users who are not logged in see the login page. If we look at the code of the middleware function we can see that <em>req.session.data</em> is used in an if-condition to check whether a data object is attached to the current session object. In case the <strong>if-condition is true</strong>, the user is logged in and the request is redirected to the dashboard, but in case the <strong>if-condition is false</strong>, the user is not logged in and the <em>next()</em> function is called. </p>



<p>The GET endpoint has the <em>routingPath</em> to the home route. When a user visits the homepage of my booking application, the GET HTTP request ask for the home <em>routingPath</em>. The middleware function <em>redirectDashboard</em> is put in front of the <em>routingHandler</em> function. If the user is not logged in the <em>routingHandler</em> function render the HTML template <a href="https://github.com/prottlaender/bookingsystem/blob/master/views/index.pug" title="index.pug file">index.pug</a>and send the HTML back to the user or more precisely to the user&#8217;s browser. </p>



<p>So far so good. We now imagine a not logged in user who sees the index page in front of him or her now wants to login using his or her <em>email</em> and <em>password</em>.</p>



<p>As described above, the index page is nothing more than a login form for entering an email address and a password. When we look at the <a href="https://github.com/prottlaender/bookingsystem/blob/master/views/index.pug" title="index.pug file">index.pug</a> file we see that the form action attribute define that the form data <code>email</code> and <code>password</code> will be sent to the form handler <code>/loginusers</code> using the POST method when the Submit button is clicked.</p>



<pre class="wp-block-code"><code>...

form#loginForm.col.s12(
		method='post', 
		action='/loginusers'
		)

		input.validate(
			type='email', 
			name='email', 
			autocomplete='username' 
			required
			)
		...

		input.validate(
			type='password', 
			name='password', 
			autocomplete='current-password' 
			required
			)
		...

button.btn.waves-effect.waves-light(
		type='submit', 
		form='loginForm'
		)
...

</code></pre>



<p><strong>Note</strong>: To understand the <em>autocomplete</em> attributes of the input tags I recommend reading the documentation of the <a href="https://www.chromium.org/developers/design-documents/form-styles-that-chromium-understands" title="The Chromium Project">Chromium Project</a>. Most browsers have password management functionalities and automatically fill in the credentials after you provide a master password to unlock your local password store. By using these autocomplete attributes in login forms but also in user registration forms or change password forms you help browsers by using these <em>autocomplete</em> functions to better identify these forms.</p>



<p>When the user has entered his or her <em>email</em> and <em>password</em> in the HTML form and clicked the Submit button, the <strong>request body</strong> contain the <em>Form Data</em> attributes <em>email</em> and <em>password</em>. Then a POST HTTP request is sent via HTTPS to the POST endpoint <code>/loginusers</code> defined in my <a href="https://github.com/prottlaender/bookingsystem/blob/master/booking.js" title="booking.js file">booking.js</a> file (see above). </p>



<p>In the picture below you can see the output of the network analysis in the developer tool of the chrome browser. Here you can see that the <em>Form Data</em> are not encrypted on <strong>browser side</strong> but you also see that the POST request URL <code>/loginusers</code> is HTTPS. This mean that when the browser sent the POST request to the server these data are encrypted with SSL/TLS in transit from the browser to the server.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" width="2002" height="612" src="https://digitaldocblog.com/wp-content/uploads/2022/08/050-POST-route-request-with-form-data.png" alt="*Form Data* not encrypted on browser side" class="wp-image-137" srcset="https://digitaldocblog.com/wp-content/uploads/2022/08/050-POST-route-request-with-form-data.png 2002w, https://digitaldocblog.com/wp-content/uploads/2022/08/050-POST-route-request-with-form-data-300x92.png 300w, https://digitaldocblog.com/wp-content/uploads/2022/08/050-POST-route-request-with-form-data-1024x313.png 1024w, https://digitaldocblog.com/wp-content/uploads/2022/08/050-POST-route-request-with-form-data-768x235.png 768w, https://digitaldocblog.com/wp-content/uploads/2022/08/050-POST-route-request-with-form-data-1536x470.png 1536w" sizes="(max-width: 2002px) 100vw, 2002px" /><figcaption>*Form Data* not encrypted on browser side</figcaption></figure>
</div>


<p>On the <strong>server side</strong> we have the web application behind a proxy server listening to HTTP requests addressed to the POST endpoint <code>/loginusers</code>.  This POST endpoint is an anonym POST Route which means that the <em>routingHandler</em> controller function is restricted to not logged-in users only. This makes sense because a login function must not be used by already logged in users. So already logged in users can not send data to this POST endpoint. This check is controlled by the middleware function <em>verifyAnonym</em> which is put in front of the <em>routingHandler</em>.  </p>



<p>So lets look at the relevant code snippets in <a href="https://github.com/prottlaender/bookingsystem/blob/master/booking.js" title="booking.js file">booking.js</a>.</p>



<pre class="wp-block-code"><code>// booking.js

...

// Load db controllers and db models
const userController = require('./database/controllers/userC');

...

// Verify POST requests only for anonym users
const verifyAnonym = (req, res, next) =&gt; {

  if (req.session.data) {
    var message = 'You are not authorized to perform this request because you are already logged-in !';
    res.status(400).redirect('/400badRequest?message='+message);

  } else {
    next()

  }
}

...

// Anonym POST Route
// Login user available for anonym only
app.post('/loginusers', verifyAnonym, userController.loginUser)

...

// GET bad request route render 400badRequest
app.get('/400badRequest', (req, res) =&gt; {
 
  res.status(400).render('400badRequest', {
    title: 'Bad Request',
    code: 400,
    status: 'Bad Request',
    message: req.query.message,
  })
})

...

</code></pre>



<p>At the beginning of the code I refer the constant <em>userController</em> to the user controller file <a href="https://github.com/prottlaender/bookingsystem/blob/master/database/controllers/userC.js" title="userC.js file">userC.js</a> using the <code>require</code> method. In <a href="https://github.com/prottlaender/bookingsystem/blob/master/database/controllers/userC.js" title="userC.js file">userC.js</a> all user functions are defined to control user related operations.</p>



<p><strong>Note</strong>: When you look into the <a href="https://github.com/prottlaender/bookingsystem/blob/master/database/controllers/userC.js" title="userC.js file">userC.js</a> file you see that we export modules using <code>module.exports = {...}</code>. Using this directive we export in fact an object with various attributes and the values of these attributes are functions. So with  <code>module.exports = { loginUser: function(...) ...}</code> we export the object including the attribute <em>loginUser</em> which contains a function as value. So when we refer the constant  <em>userController</em> using the <code>require()</code> function in the <a href="https://github.com/prottlaender/bookingsystem/blob/master/booking.js" title="booking.js file">booking.js</a> file we store the complete exported object with all its attributes to the <em>userController</em> constant. Now we have access to any attribute of the exported object from <a href="https://github.com/prottlaender/bookingsystem/blob/master/database/controllers/userC.js" title="userC.js file">userC.js</a> file with <em>userController.&lt;attribute&gt;</em>. Because the attributes are in fact functions we call these functions with this statement.  </p>



<p>In the <em>verifyAnonym</em> function <em>req.session.data</em> is used in the if-condition to check whether a data object is attached to the current session object. In case the <strong>if-condition is true</strong>, the user is already logged-in and is redirected to the Bad Request GET endpoint <code>/400badRequest</code> which is the standard route in my application to show the user that something went wrong. The user can see what went wrong from a message that has been attached to the request using the request parameter <code>?message=+message</code>. In case <strong>the if-condition is false</strong>, the user is not logged-in and the <em>next()</em> function forwards the request to the <em>routingHandler</em> controller function that call the <code>loginUser</code> function using <em>userController.loginUser</em>.  This function has access to the attributes <em>email</em> and <em>password</em> of the <strong>request body</strong> with <em>req.body.email</em> and <em>req.body.password</em>. </p>



<p>So lets look at the relevant code snippets in <a href="https://github.com/prottlaender/bookingsystem/blob/master/database/controllers/userC.js" title="userC.js file">userC.js</a> file.</p>



<pre class="wp-block-code"><code>// database/controllers/userC.js

// load the bcryptjs module
const bcrypt = require('bcryptjs');
// define hash saltrounds for password hashing
const saltRounds = 10;
// load the relevant Prototype Objects (exported from the models)
...

const User = require('../models/userM');

...

loginUser: function (req, res) {

    const inputemail = req.body.email
    const email = inputemail.toLowerCase()

    console.log(req.url);
    console.log(req.session.id);
    console.log(req.session);

    try {

      User.findOne({ email: email }, async function(error, user) {
        if (!user) {
          var message = 'User not found. Login not possible';
          res.status(400).redirect('/400badRequest?message='+message);

        } else {
          if (user._status !== 'active') {
            var message = 'Login not possible. Await User to be activated';
            res.status(400).redirect('/400badRequest?message='+message);

          } else {
              if (bcrypt.compareSync(req.body.password, user.password)) {

                var yearInMs = 3.15576e+10;
                var currentDate = new Date ()
                var currentDateMs = currentDate.getTime()
                var birthDateMs = user.birthdate.getTime()
                var age = Math.floor((currentDateMs - birthDateMs) / yearInMs)

                if (age &lt; 18) {
                  var cat = 'youth'
                } else {
                  var cat = 'adult'
                };

                var userData = {
                  userId: user._id,
                  status: user._status,
                  name: user.name,
                  lastname: user.lastname,
                  email: user.email,
                  role: user.role,
                  age: age,
                  cat: cat,
                }

                req.session.data = userData

                res.status(200).redirect('/dashboard')

              } else {
                var message = 'Login not possible. Wrong User password';
                res.status(400).redirect('/400badRequest?message='+message);
              }
          }
        }
      })

    } catch (error) {
      // if user query fail call default error function
      next(error)

    }
  // End Module
  },

...

</code></pre>



<p>In order to authenticate a user, the <em>loginUser</em> function must find a user in the user database with the same email address as the one that was sent by the browser and attached to the request body by the app. If a user was found with the email, the function must check whether the transmitted password matches the password that is stored in the database for this user. If the email and password match the user is authenticated and the login is successful, if not, the login fails. </p>



<p>Passwords are never saved in plain text. Therefore I use the <a href="https://www.npmjs.com/package/bcryptjs" title="Bcrypt-Js module for node-js">bcryptjs module</a> to hash passwords. The bcryptjs module is loaded into the code with the <code>require()</code> function and assigned to the constant <em>bcrypt</em>. We set the constant <em>saltRounds</em> to the value of 10. This is the so called cost factor in the bcrypt hashing function and controls how much time bcrypt need to calculate a single bcrypt hash. Increasing the cost factor by 1 doubles the time and the more time bycrypt need to hash the more difficult it is to brute force stored passwords.</p>



<p>Then I load the user model from <a href="https://github.com/prottlaender/bookingsystem/blob/master/database/models/userM.js" title="userM.js file">userM.js</a> using the <code>require()</code> function and assign the constant <em>User</em>. Here at this point I have to explain the background. To do this, we also need a look at the <a href="https://github.com/prottlaender/bookingsystem/blob/master/database/models/userM.js" title="userM.js file">userM.js</a> file. </p>



<p><strong>Note</strong>: I use MongoDB as the database and Mongoose to model the data. If you look in the file <a href="https://github.com/prottlaender/bookingsystem/blob/master/database/models/userM.js" title="userM.js file">userM.js</a> you see that a user object is created with the function <em>new Schema()</em> and saved in the variable <em>userSchema</em>. This <em>userSchema</em> object describes a user with all its attributes. At the end of the file, the <em>mongoose.model()</em> function is used to reference the <em>userSchema</em> to the collection <em>col<em>users</em></em> in my MongoDB. This reference is assigned to the variable <em>User</em> and exported using the function <em>module.exports()</em>. With <em>User</em> I have access to the user model meaning to all user objects and attributes in my database that are stored in the <em>col<em>users</em></em> collection. So that I can use this access in the code of my <a href="https://github.com/prottlaender/bookingsystem/blob/master/database/controllers/userC.js" title="userC.js file">userC.js</a> file I load <a href="https://github.com/prottlaender/bookingsystem/blob/master/database/models/userM.js" title="userM.js file">userM.js</a> with the <code>require()</code> function and assign the constant <em>User</em>. I can now use Mongoose functions for example to query user data from my <em>col<em>users</em></em> collection. This is exactly what we do with <em>User.findOne()</em> when we try to find a user with a certain email.</p>



<p>The actual <strong>user authentication</strong> now takes place in the <em>userFindOne()</em> function. </p>



<p>When we run <em>User.findOne()</em> we check the criteria that do not lead to a successful authentication. </p>



<ol class="wp-block-list"><li><strong>No user found</strong>: We are looking for a user object that matches the email that has been submitted. If no user object is found with that email or the user found is not active, the request is redirected to the 400badRequest route. If we have found an active user, the submitted password string is hashed with bcrypt and compared with the saved password. </li><li><strong>Wrong password</strong>: If the password comparison is not successful, the submitted password was wrong and the request is also redirected to the 400badRequest route. <br></li></ol>



<p><strong>Note</strong>: <em>User.findOne()</em> has a query object <code>{email: email}</code> and a callback function <code>async function(error, user {...})</code> as parameters. When the async function find a user with the email in the database, this async function returns a user object with all the user attributes and store this object into the <em>user</em> parameter. Within the scope of the async function I have now access to the user attributes using <em>user.&lt;attribute&gt;</em>.</p>



<p>Only in case the user with the email is found and the submitted password is correct the authentication is successful.</p>



<p>If the user is successfully authenticated, the category of the user is calculated based on the current date and the user&#8217;s birth date. Then a <em>userData</em> object is created in which various user attributes are stored. The data of the <em>userData</em> object are then attached to the session. More precisely, the object <em>data</em> is attached to the session with <em>req.session.data</em> and the value <em>userData</em> is assigned. Now the session is initialized and the session object is stored in the <em>col<em>sessions</em></em> collection of the MongoDB.</p>


<div class="wp-block-image">
<figure class="aligncenter"><img decoding="async" width="2460" height="1126" src="https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-1.png" alt="Initialized session and session object stored in the col_sessions" class="wp-image-135" srcset="https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-1.png 2460w, https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-1-300x137.png 300w, https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-1-1024x469.png 1024w, https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-1-768x352.png 768w, https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-1-1536x703.png 1536w, https://digitaldocblog.com/wp-content/uploads/2022/08/070-DB-session-object-1-2048x937.png 2048w" sizes="(max-width: 2460px) 100vw, 2460px" /><figcaption>Initialized session and session object stored in the col_sessions</figcaption></figure>
</div>


<p>Then the <strong>response</strong> is sent back to the browser. </p>



<p>In this response, the browser is instructed to call up a GET request to the GET endpoint <code>/dashboard</code>. The response is sent using <code>res.status(200).redirect('/dashboard')</code>. In the Response Header you see that the cookie with the name <em>booking</em> is set in the users browser using the <code>set-cookie</code> directive. The cookie only contain the session ID which has been signed and encrypted with the <em>secret</em> we provided in  <code>app.use(session( {... cookie: {...} }))</code>. </p>


<div class="wp-block-image">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1759" height="557" src="https://digitaldocblog.com/wp-content/uploads/2022/08/060-POST-route-request-with-form-data-set-cookie.png" alt="Response Header with cookie named *booking*" class="wp-image-138" srcset="https://digitaldocblog.com/wp-content/uploads/2022/08/060-POST-route-request-with-form-data-set-cookie.png 1759w, https://digitaldocblog.com/wp-content/uploads/2022/08/060-POST-route-request-with-form-data-set-cookie-300x95.png 300w, https://digitaldocblog.com/wp-content/uploads/2022/08/060-POST-route-request-with-form-data-set-cookie-1024x324.png 1024w, https://digitaldocblog.com/wp-content/uploads/2022/08/060-POST-route-request-with-form-data-set-cookie-768x243.png 768w, https://digitaldocblog.com/wp-content/uploads/2022/08/060-POST-route-request-with-form-data-set-cookie-1536x486.png 1536w" sizes="auto, (max-width: 1759px) 100vw, 1759px" /><figcaption>Response Header with cookie named *booking*</figcaption></figure>
</div>


<p>Then the browser send the GET request to the endpoint <code>/dashboard</code>. Lets have a look into the <a href="https://github.com/prottlaender/bookingsystem/blob/master/booking.js" title="booking.js file">booking.js</a> file again.</p>



<pre class="wp-block-code"><code>// booking.js 

...

// Redirect GET requests from not authenticated users to login
const redirectLogin = (req, res, next) =&gt; {
  if (!req.session.data) {
    res.redirect('/')

  } else {
    next()

  }
}

...

// GET dashboard route only for authenticated users. Anonym users redirected to home
app.get('/dashboard', redirectLogin, async (req, res) =&gt; {

  // Check admin authorization and render admin_dashboard
  if (req.session.data.role == 'admin') {

    const user_query = User.find( {} ).sort({lastname: 1, name: 1});
    var users = await user_query.exec();

    const training_query = Training.find( {} ).sort({date: 'desc'});
    var trainings = await training_query.exec();

    const location_query = Location.find( {} ).sort({location: 'desc'});
    var locations = await location_query.exec();

    const booking_query = Booking.find( {} ).sort({_booktrainingdate: 'desc'});
    var bookings = await booking_query.exec();

    const invoice_query = Invoice.find( {} ).sort({invoicedate: 'desc'});
    var invoices = await invoice_query.exec();

    res.status(200).render('admin_dashboard', {
      title: 'Admin Dashboard Page',
      name: req.session.data.name,
      lastname: req.session.data.lastname,
      role: req.session.data.role,
      data_users: users,
      data_trainings: trainings,
      data_locations: locations,
      data_bookings: bookings,
      data_invoices: invoices,

      });

  // Check player authorization and render player_dashboard
  } else if (req.session.data.role == 'player') {

    var currentDate = new Date();
    console.log('current date: ' +currentDate);

    const availabletraining_query = Training.find( { _status: 'active', date: { $gte: currentDate } } ).sort({ date: 'desc' });
    var availabletrainings = await availabletraining_query.exec();

    const booking_query = Booking.find( { _bookuseremail: req.session.data.email, _bookparticipation: { $ne: 'invoice' } } ).sort({ _booktrainingdate: 'desc' });
    var bookings = await booking_query.exec();

    const myuser_query = User.findOne( { email: req.session.data.email } );
    var myuser = await myuser_query.exec();

    const invoice_query = Invoice.find( {invoiceemail: req.session.data.email} ).sort({invoicedate: 'desc'});
    var invoices = await invoice_query .exec();

    res.status(200).render('player_dashboard', {
      title: 'Player Dashboard Page',
      name: req.session.data.name,
      lastname: req.session.data.lastname,
      role: req.session.data.role,
      email: req.session.data.email,
      data_availabletrainings: availabletrainings,
      data_bookings: bookings,
      data_myuser: myuser,
      data_myinvoices: invoices,
      });

  // Check coach authorization and render coach_dashboard
  } else if (req.session.data.role == 'coach') {
   
    var currentDate = new Date().setHours(00, 00, 00);
    console.log('currentDate: ' +currentDate);

    const training_query = Training.find( { _status: 'active', date: { $gte: currentDate } } ).sort({ date: 'asc' });
    var trainings = await training_query.exec();

    res.status(200).render('coach_dashboard', {
      title: 'Coach Dashboard Page',
      name: req.session.data.name,
      lastname: req.session.data.lastname,
      role: req.session.data.role,
      data_trainings: trainings,
      });

  } else {
    // if user not authorized as admin, player or coach end request and send response
    var message = 'You are not authorized. Access prohibited';
    res.status(400).redirect('/400badRequest?message='+message);
  }

});

...

</code></pre>



<p>As you see above in the code we have first defined the middleware function <em>redirectLogin</em>. This middleware ensure that only users who are logged in see the dashboard page. In case the <strong>if-condition is true</strong>, the user is not logged in and the request is redirected to the home route, but in case the <strong>if-condition is false</strong>, the user is logged in and the <em>next()</em> function is called. </p>



<p>The GET HTTP request ask for the dashboard <em>routingPath</em>. The middleware function <em>&nbsp;redirectLogin</em> is put in front of the <em>routingHandler</em> function. If the user is not logged in the <em>&nbsp;redirectLogin</em> middleware redirect the request to the home route. In case the user is logged-in the <em>routingHandler</em> function is called using the request object and the response object as parameters.</p>



<pre class="wp-block-code"><code>app.get('/dashboard', redirectLogin, async (req, res) =&gt; {...})
</code></pre>



<p>If we look at the Request Header of this new GET request in the browser we can see that the cookie is dragged along unchanged with the GET request to the endpoint <code>/dashboard</code>. This happens from now on with every request until the session expires or until the user logout. </p>


<div class="wp-block-image">
<figure class="aligncenter"><img loading="lazy" decoding="async" width="1751" height="590" src="https://digitaldocblog.com/wp-content/uploads/2022/08/080-GET-route-request-dashboard-with-cookie.png" alt="Cookie dragged along with GET request " class="wp-image-139" srcset="https://digitaldocblog.com/wp-content/uploads/2022/08/080-GET-route-request-dashboard-with-cookie.png 1751w, https://digitaldocblog.com/wp-content/uploads/2022/08/080-GET-route-request-dashboard-with-cookie-300x101.png 300w, https://digitaldocblog.com/wp-content/uploads/2022/08/080-GET-route-request-dashboard-with-cookie-1024x345.png 1024w, https://digitaldocblog.com/wp-content/uploads/2022/08/080-GET-route-request-dashboard-with-cookie-768x259.png 768w, https://digitaldocblog.com/wp-content/uploads/2022/08/080-GET-route-request-dashboard-with-cookie-1536x518.png 1536w" sizes="auto, (max-width: 1751px) 100vw, 1751px" /><figcaption>Cookie dragged along with GET request </figcaption></figure>
</div>


<p>And now within the <em>routingHandler</em> function we do the <strong>user authorization</strong> check. The if condition check the users role using <em>req.session.data.role</em>. Depending on the role of the user different <em>&lt;role&gt;<em>dashboard</em></em> HTML templates are rendered and for each role different HTML is sent back to the user&#8217;s browser. Various queries are executed beforehand because we need role specific data within each <em>&lt;role&gt;<em>dashboard</em></em> HTML template. The return values ​​of the different queries <code>find()</code> and <code>findOne()</code> are only executed in case one of the if conditions become true. Then in each case the return values of the queries are stored in variables. In case all if conditions are false, meaning we cannot find a user with a role like <em>admin</em>, <em>player</em> or <em>coach</em> in the database for some reason the request is redirected to the Bad Request GET endpoint <code>/400badRequest</code> using the message as request parameter that this user is not authorized.</p>



<p>Within each if condition and for each role <em>admin</em>, <em>player</em> or <em>coach</em>, we create the response object by first setting the HTTP status to the value of 200 and then using the render method to render the respective HTML template.</p>



<pre class="wp-block-code"><code>res.status(200).render('&lt;role&gt;_dashboard', {...})
</code></pre>



<p>Within the render method, we now have the option of transferring a data object with different attributes to the HTML template. Later we can access these data in the respective HTML template and use it within the HTML template. How this works is not part of this article. But of course you can take a closer look at the templates <a href="https://github.com/prottlaender/bookingsystem/blob/master/views/admin_dashboard.pug" title="admin_dashboard.pug file">admin dashboard</a>, <a href="https://github.com/prottlaender/bookingsystem/blob/master/views/player_dashboard.pug" title="player_dashboard.pug file">player dashboard</a>and <a href="https://github.com/prottlaender/bookingsystem/blob/master/views/coach_dashboard.pug" title="coach_dashboard.pug file">coach dashboard</a>on my <a href="https://github.com/prottlaender" title="GitHub Account of Patrick Rottlaender">GitHub repository</a> and you will immediately see how this works.</p>



<h3 class="wp-block-heading">Create Authorizations</h3>



<p>As I have already shown in the upper part, I work with middleware functions to control access to GET and POST endpoints in my app. Therefore these middleware functions are the Authorizations and you can find them in the code of my <a href="https://github.com/prottlaender/bookingsystem/blob/master/booking.js" title="booking.js file">booking.js</a> file.</p>



<pre class="wp-block-code"><code>// booking.js

...

// Authorizations
// Redirect GET requests from not authenticated users to login
const redirectLogin = (req, res, next) =&gt; {
  if (!req.session.data) {
    res.redirect('/')

  } else {
    next()

  }
}

// Redirect GET requests from authenticated users to dashboard
const redirectDashboard = (req, res, next) =&gt; {
  if (req.session.data) {
    res.redirect('/dashboard')

  } else {
    next()

  }
}

// Authorize POST requests only for not authenticated users
const verifyAnonym = (req, res, next) =&gt; {
  if (!req.session.data) {
    next()

  } else {
    var message = 'You are already logged-in. You are not authorized to perform this request !';
    res.status(400).redirect('/400badRequest?message='+message);

  }
}

// Authorize POST requests only for anonym and admin users
const verifyAnonymAndAdmin = (req, res, next) =&gt; {
  if (!req.session.data) {
    next()

  } else {

    if (req.session.data.role == 'admin') {
      next()

    } else {
      var message = 'You are no Admin. You are not authorized to perform this request !';
      res.status(400).redirect('/400badRequest?message='+message);

    }

  }
}

// Authorize POST requests only for admin and player users
const verifyAdminAndPlayer = (req, res, next) =&gt; {
  if (req.session.data) {
    if (req.session.data.role == 'admin') {
      next()

    } else if (req.session.data.role == 'player') {
      next()

    } else {
      var message = 'You are no Admin, no Player. You are not authorized to perform this request !';
      res.status(400).redirect('/400badRequest?message='+message);
    }

  } else {
    var message = 'You are not logged-in. You are not authorized to perform this request !';
    res.status(400).redirect('/400badRequest?message='+message);
  }

}

// Authorize POST requests only for admin users
const verifyAdmin = (req, res, next) =&gt; {
  if (req.session.data) {
    if (req.session.data.role == 'admin') {
      next()

    } else {
      var message = 'You are no Admin. You are not authorized to perform this request !';
      res.status(400).redirect('/400badRequest?message='+message);
    }

  } else {
    var message = 'You are not logged-in. You are not authorized to perform this request !';
    res.status(400).redirect('/400badRequest?message='+message);

  }
}

// Authorize POST requests only for player users
const verifyPlayer = (req, res, next) =&gt; {
  if (req.session.data) {
    if (req.session.data.role == 'player') {
      next()

    } else {
      var message = 'You are no Player. You are not authorized to perform this request !';
      res.status(400).redirect('/400badRequest?message='+message);
    }

  } else {
    var message = 'You are not logged-in. You are not authorized to perform this request !';
    res.status(400).redirect('/400badRequest?message='+message);

  }
}

// Authorize POST requests only for coach users
const verifyCoach = (req, res, next) =&gt; {
  if (req.session.data) {
    if (req.session.data.role == 'coach') {
      next()

    } else {
      var message = 'You are no Coach. You are not authorized to perform this request !';
      res.status(400).redirect('/400badRequest?message='+message);

    }

  } else {
    var message = 'You are not logged-in. You are not authorized to perform this request !';
    res.status(400).redirect('/400badRequest?message='+message);

  }
}

...

</code></pre>



<p>As I have already explained above I use <strong>redirect functions</strong> as a middleware to control access to the <strong>GET endpoints</strong> <em>home</em>, <em>register</em> and <em>dashboard</em>. These middleware functions basically control access based on whether a user is logged-in or not. The redirect function <em>redirectDashboard</em> allow only not logged-in users access to the <em>home</em> endpoint and to the <em>register</em> endpoint, while users who are already logged-in have no access and would be redirected directly to the <em>dashboard</em> route if they try to access here. The <em>redirectLogin</em> middleware function allow only logged-in users access to the <em>dashboard</em> route while not logged-in users are redirected to the login or better to the <em>home</em> endpoint.</p>



<p>In addition to  <em>redirect functions</em> I use <strong>verify functions</strong> as a middleware to control access to the <strong>POST endpoints</strong>. With the help of POST requests, data are sent via POST endpoints to the app. That is why it is particularly important to control who is allowed to send data and who is not. I use basically 5 types of POST endpoints.</p>



<p><strong>Anonym POST endpoint</strong>. I only have one endpoint here. The <em>loginusers</em> endpoint can only be called by not logged-in users. Therefore the <em>verifyAnonym&nbsp;</em> middleware is set before the <em>routingHandler</em> function to verify if the user is not logged-in. </p>



<pre class="wp-block-code"><code>// booking.js
...

// Anonym POST endpoint
// Login user available for anonym only
app.post('/loginusers', verifyAnonym, userController.loginUser)

...

</code></pre>



<p><strong>Shared POST endpoints</strong>. The <em>createusers</em> endpoint can be called by not logged-in users and Admin users. The <em>&nbsp;verifyAnonymAndAdmin&nbsp;</em> middleware is set before the <em>routingHandler</em> function to verify if the user is not logged-in or if the user that is logged-in has the role <em>admin</em>.  The <em>&nbsp;updateuseremail</em> and <em>setnewuserpassword</em> endpoints can be called only by Admin and Player users. Therefore the <em>&nbsp;verifyAdminAndPlayer&nbsp;</em> middleware is set before the <em>routingHandler</em> function to verify if the user is logged-in and if the users role is <em>admin</em> or <em>player</em>. </p>



<pre class="wp-block-code"><code>// booking.js
...

// Shared POST endpoints
// Create Users available for anonym and admin
app.post('/createusers', verifyAnonymAndAdmin, birthdateFormatValidation, userController.createUser)

// Update User-Email available for admin and player
app.post('/updateuseremail', verifyAdminAndPlayer, userController.updateUserEmail)

// Update User-Password available for admin and player
app.post('/setnewuserpassword', verifyAdminAndPlayer, userController.setNewUserPassword)

...

</code></pre>



<p><strong>Admin POST endpoints</strong>.  I have 19 endpoints here and each of these endpoint can only be called by Admin users. The <em>&nbsp;verifyAdmin</em> middleware is set before the <em>routingHandler</em> function to verify if the user is logged-in and if the users role is <em>admin</em>. </p>



<pre class="wp-block-code"><code>// booking.js
...

// Admin POST endpoints
// Admin User Management
app.post('/callupdateusers', verifyAdmin, userController.callUpdateUsers)

app.post('/updateuser', verifyAdmin, birthdateFormatValidation, userController.updateUser)

app.post('/terminateusers', verifyAdmin, userController.terminateUser)

app.post('/activateusers', verifyAdmin, userController.activateUser)

app.post('/removeusers', verifyAdmin, userController.removeUser)

// Admin Update Training
app.post('/callupdatetrainings', verifyAdmin, trainingController.callUpdateTrainings)

app.post('/updatetraining', verifyAdmin, trainingController.updateTraining)

// Admin Location Management
app.post('/createlocations', verifyAdmin, locationController.createLocation)

app.post('/callupdatelocations', verifyAdmin, locationController.callUpdateLocations)

app.post('/updatelocation', verifyAdmin, locationController.updateLocation)

app.post('/callcreatetrainings', verifyAdmin, trainingController.callCreateTrainings)

app.post('/createtraining', verifyAdmin, trainingController.createTraining)

// Admin Invoice Management
app.post('/createinvoice', verifyAdmin, invoiceController.createInvoiceUser)

app.post('/callcancelinvoice', verifyAdmin, invoiceController.callCancelInvoice)

app.post('/cancelinvoice', verifyAdmin, invoiceController.cancelInvoice)

app.post('/callpayinvoice', verifyAdmin, invoiceController.callPayInvoice)

app.post('/payinvoice', verifyAdmin, invoiceController.payInvoice)

app.post('/callrepayinvoice', verifyAdmin, invoiceController.callRePayInvoice)

app.post('/repayinvoice', verifyAdmin, invoiceController.rePayInvoice)

...

</code></pre>



<p><strong>Player POST endpoints</strong>.  I have 7 endpoints here and each of these endpoint can only be called by Player users. The <em>&nbsp;verifyPlayer</em> middleware is set before the <em>routingHandler</em> function to verify if the user is logged-in and if the users role is <em>player</em>. </p>



<pre class="wp-block-code"><code>// booking.js
...

// Player POST endpoints
// Player Booking Management
app.post('/callbooktrainings', verifyPlayer, bookingController.callBookTrainings)

app.post('/booktrainings', verifyPlayer, bookingController.bookTraining)

app.post('/bookingreactivate', verifyPlayer, bookingController.bookingReactivate)

app.post('/callcancelbookings', verifyPlayer, bookingController.callCancelBooking)

app.post('/cancelbookings', verifyPlayer, bookingController.cancelBooking)

// Player User Management
app.post('/callupdatemyuserdata', verifyPlayer, userController.callUpdateMyUserData)

app.post('/updatemyuserdata', verifyPlayer, birthdateFormatValidation, userController.updateMyUserData)

...

</code></pre>



<p><strong>Coach POST endpoints</strong>.  I have 2 endpoints here and each of these endpoint can only be called by Coach users. The <em>&nbsp;verifyCoach</em> middleware is set before the <em>routingHandler</em> function to verify if the user is logged-in and if the users role is <em>coach</em>. </p>



<pre class="wp-block-code"><code>// booking.js
...

// Coach POST endpoints
// Coach Confirmation Management
app.post('/callparticipants', verifyCoach, bookingController.callParticipants)

app.post('/callconfirmpatricipants', verifyCoach, bookingController.callConfirmPatricipants)

...

</code></pre>



<h3 class="wp-block-heading">User logout</h3>



<p>The user initiates a <strong>logout</strong> himself by clicking on the logout link in the navigation of the application. This sends a GET request to the <code>/logout</code> endpoint of the application. In this routing definition, the session is first deleted from the database using <code>req.session.destroy()</code> and then the cookie is removed from the browser and the user is redirected to the <em>200success site</em> using <code>res.status(200).clearCookie('booking').redirect()</code>.</p>



<pre class="wp-block-code"><code>// booking.js

...

// GET logout route only for authenticated users. Anonym users redirected to home
app.get('/logout', redirectLogin, (req, res) =&gt; {
  req.session.destroy(function(err) {
    if (err) {
      res.send('An err occured: ' +err.message);
    } else {
      var message = 'You have been successfully logged out';
      res.status(200).clearCookie('booking').redirect('/200success?message='+message)
    }
  });
})

...

</code></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>A not so simple booking system for sports clubs (Version 1.0)</title>
		<link>https://digitaldocblog.com/webdesign/a-not-so-simple-booking-system-for-sports-clubs-version-10/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Sat, 30 Jan 2021 09:00:00 +0000</pubDate>
				<category><![CDATA[Web-Development]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Blog-Application]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Java Script]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[NPM Node package manager]]></category>
		<guid isPermaLink="false">https://digitaldocblog.com/?p=120</guid>

					<description><![CDATA[Some sport clubs offer trainings to their members and must ask for money to participate. At least that&#8217;s how it is with my son&#8217;s ice hockey club. This causes an&#8230;]]></description>
										<content:encoded><![CDATA[
<p>Some sport clubs offer trainings to their members and must ask for money to participate. At least that&#8217;s how it is with my son&#8217;s ice hockey club. This causes an administrative effort, since both the training on the one hand and the bookings and invoicing on the other hand have to be managed. </p>



<p>The ice hockey club where my son plays has so far administered this with high manual effort. The players can register themselves on a website and after the training has taken place the amounts are added together for each player and published in another list as a request for payment. In advance, of course, the lists for each training must be created, one list for each training that takes place at a certain point in time.</p>



<p>My goal was to write a software that regulates everything that is related to the administration of the training courses and the bookings automatically as far as possible.</p>



<h1 class="wp-block-heading">Preliminary technical considerations</h1>



<p>From my point of view, it makes sense to develop a web-application that can be accessed with a standard web-browser. I use the possibilities to optimize such a web-application for mobile devices. The application has been developed according to the mobile first principle. This means that in addition to standard use on a PC or laptop, it is technically possible to use the web application on smartphones and tablets with the standard browser installed there. As a technical platform, I use server-side JavaScript for the application backend. Specifically, I use Node-JS and Express-JS to program the application logic. MongoDB is used as the database. The front end design is developed with Materialize CSS Framework.</p>



<p>You are welcome to view and download my code on my <a href="https://github.com/prottlaender/bookingsystem">GitHub repository.</a> Of course, I am happy to receive your comments.</p>



<h1 class="wp-block-heading">Authorization Concept</h1>



<p>Of course, an application like this should also have a corresponding authorization or role concept. This means that the application is used by users with different roles. Depending on the respective role, the user is presented with a range of functions that is specialized for him. I implement 3 different roles.</p>



<ul class="wp-block-list"><li>There is an <strong>Administrator</strong>. The administrator takes care of the user and training management and the billing of trainings for the users.</li><li>There is a <strong>Player</strong>. The player can select trainings and book trainings accordingly. He sees his bills as well as outstanding payments and can use this information to instruct payments or transfers.</li><li>Then there is also a <strong>Coach</strong>. The coach operates on site at the time the training takes place. He confirms the participants in a training session using a list that is displayed to him. Alternatively, the participants can be recorded in the system at a later point in time. For example the coach write down a list of participants by hand and record the participants in the system based on that list at home.<br></li></ul>



<p>The application can only be used if the user is authenticated and authorized. So a user has to register. Users who are already registered can log in to the application on the login site.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611476166686_000-000-Login.png" alt="Login Site"/><figcaption>Login Site</figcaption></figure>



<p>Unregistered users go to the registration site and carry out a self-registration there. After registering, the user is not logged in immediately. </p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611476271090_000-001-Registration.png" alt="Registration Site"/><figcaption>Registration Site</figcaption></figure>



<p>Any self-registration must first be approved by activating the new user account in the system. Only then the user can log in to the system. This is an administrator&#8217;s job and will be described below.</p>



<h1 class="wp-block-heading">Limitations in this version</h1>



<p>This version has the following limitations. I will use this list of limitations as a store of ideas for the further development of the system. Maybe there are more limitations and the list gets longer. Any comment is welcome. Just send me an email. </p>



<p><strong>Multi-Role Concept</strong></p>



<p>In this version of the system, only one role per user is possible. This means that i.e. an administrator cannot be a player or a coach at the same time. This multi-role concept is an extension that I will implement in a later release.</p>



<p><strong>Confirm participants without booking</strong></p>



<p>As described below, the coach can confirm the participation of players in a training session. Here the coach can select from a list of players who have booked the training. Because it is possible for the player to book a training first, then cancel this booking but still appear for the training, canceled bookings are also displayed to the coach here. In this version, the coach cannot confirm the participation of players who have no booking at all. He would not find them on the list. Such a recording of participants without booking is a process that will be taken into account in a later version of the system.</p>



<h1 class="wp-block-heading">General function of the application</h1>



<p>In general, with the application, data is recorded by the actors and once recorded data can be changed again by the actors. For example, an administrator can create users in the system. The user data recorded in this way receive various attributes like name and address or birthdate. These data can be changed later by the administrator or by the user itself, for example when the address changed because the user has moved to another city.</p>



<p>The first entry of data is always done by the administrator. An exception is the self registration process as described briefly above using the registration form. The administrator initially records data using a form. This form is directly displayed in a modal after the function has been activated in the top navigation. A modal is an overlay pop up which is presented to the user after a click and is used for the interaction between the user and the application. </p>



<p><strong>Create modals</strong></p>



<p>Create modals are activated via the top navigation without having previously marked a data record with the checkbox. Create modals are used to capture new data and store them into the database. They contain a form for entering data and a close, reset and submit button at the bottom right. The modal can be closed with the close button and all data entered so far remain available until the browser is refreshed. The reset button deletes all data entered in the form. If the Submit button is clicked, the data entered will be saved.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611985593825_000-010-Create-Modal.png" alt="Create Modal"/><figcaption>Create Modal</figcaption></figure>



<p><strong>Confirm modals</strong></p>



<p>Confirm modals are activated via the top navigation after having previously marked a data record with the checkbox. Confirm modals are used to change existing data and store these changes into the database. They contain data in a from from the data record that was previously marked with the checkbox and a close and a submit button at the bottom right. The modal can be closed with the close button and all data entered so far remain available until the browser is refreshed. If the Submit button is clicked, the data of the marked dataset is loaded from the database and displayed on a call site.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611985619675_000-020-Confirm-Modal.png" alt="Confirm Modal"/><figcaption>Confirm Modal</figcaption></figure>



<p><strong>Call Sites</strong></p>



<p>Call sites contain a form with unchangeable and changeable data fields from the data record which was previously marked with the checkbox. In the top navigation there is a single link that leads the user back to the dashboard. Changes are not saved. </p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611986396085_000-030-Call-Site-1.png" alt="Call Site - 1"/><figcaption>Call Site &#8211; 1</figcaption></figure>



<p>At the bottom right of the call site, the user can click the Back Button to return to the dashboard without saving any changes. With Confirm, the changes entered by the user are saved in the database. The data fields that cannot be changed are only displayed to the user for information purposes, the data fields that can be changed can be overwritten. After the changes have been recorded, the user clicks Confirm to confirm his changes and to save the changes in the database.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611986415718_000-030-Call-Site-2.png" alt="Call Site - 2"/><figcaption>Call Site &#8211; 2</figcaption></figure>



<p><strong>Bad Request Sites</strong></p>



<p>The application checks all user data entries and monitors its functionality itself. Whenever the user has entered incorrect data or the application produces an error for some other reason (e.g. a data record is not found), the user is informed of this with a Bad Request Site. The Bad Request Site only contains the error message and a Back Button with which the user comes directly back to the dashboard.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611986987173_000-040-Bad-request.png" alt="Bad Request Site"/><figcaption>Bad Request Site</figcaption></figure>



<p><strong>Success Sites</strong></p>



<p>Each successful operation is confirmed with a success site. The Success Site also contains the success message and a Back Button with which the user comes directly back to the dashboard.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611987149138_000-050-Success.png" alt="Success Site"/><figcaption>Success Site</figcaption></figure>



<h1 class="wp-block-heading">Administrator</h1>



<p>The administrator is the user with the greatest range of functions. If an administrator has logged in to the application with his user ID (email) and password, he is given access to the admin dashboard. </p>



<p>The <strong>admin-dashboard</strong> shows 5 tables:</p>



<ul class="wp-block-list"><li>Users</li><li>Bookings</li><li>Trainings</li><li>Locations</li><li>Invoices<br></li></ul>



<p>Each table can be viewed by clicking on the tabs in the admin dashboard. You can see which table is currently selected by the fact that the corresponding tab has a light gray background and a blue line is displayed at the bottom of the tab. In this example, the Locations table has been clicked.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611474588299_001-000-Admin-Dashboard.png" alt="Admin Dashboard"/><figcaption>Admin Dashboard</figcaption></figure>



<p>Each line of the tables show a data record. A data record can be selected if there is a checkbox on the left. </p>



<p>The user table shows all users in the system with their most important attributes. The booking table shows the administrator all training bookings in the system and their status. The bookings cannot be edited by the administrator. The trainings, locations and the invoices tables show all trainings, locations and all invoices that the administrator has entered in the system so far.</p>



<p>At the top there is a navigation bar with links and dropdown menus with all the important functions for the administrator.</p>



<h2 class="wp-block-heading">User Management</h2>



<p>User Management is a dropdown menu in the top navigation bar. Here the administrator has the possibility to use the following user management functions. </p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611474452484_001-001-Usermanagement.png" alt="Admin Usermanagement Fuctionalities"/><figcaption>Admin Usermanagement Fuctionalities</figcaption></figure>



<p><strong>Create User</strong></p>



<p>To create a new user, the administrator clicks on the create user function via the dropdown menu. A modal opens that shows the administrator a form to capture all relevant user data. The administrator records all relevant data and assigns a role (admin, player or coach) and an initial password, which can later be changed by the user himself. The email address of the user serves as the UserID and may only appear once in the system. By clicking on Submit on the lower right edge of the modal, the data are written to the database. The new user is created and the user status is awaitapproval (user must be activated by the administrator).</p>



<p><strong>Update User</strong></p>



<p>To update a user, the administrator clicks on the checkbox of a user in the user table and then activates the update user function via the dropdown menu. A modal opens that shows the administrator the email address of the selected user. By clicking on Submit on the lower right edge of the modal, the user&#8217;s data is loaded from the database and displayed on a Confirm Update User call site. The administrator can edit some fields such as name, street or date of birth. By clicking on Confirm at the bottom right of the Confirm Update User call site, the changes are written to the database.</p>



<p><strong>Update Email</strong></p>



<p>To update a users email, the administrator clicks on the checkbox of a user in the user table and then activates the update email function via the dropdown menu. A modal opens that shows the administrator the current email address of the selected user. The administrator can enter the user&#8217;s new email address in another field. By clicking on Submit on the lower right edge of the modal, the user&#8217;s new email address is written to the database. Changing the email address means that the user can only log into the system with the new email address. If the user has already booked a training, the email address stored on the booking is updated with the new email address.</p>



<p><strong>Update Status</strong></p>



<p>To update a users status, the administrator clicks on the checkbox of a user in the user table and then activates the update users status function via the dropdown menu. A modal opens that shows the administrator the current email address of the selected user. On the lower right edge of the modal, the Administrator can click on the Activate, Terminate or on the Remove Button. By clicking on Activate the user can be activated. When the administrator click on the Terminate button users can be terminated where the user balance is not greater 0. By clicking on Remove only terminated users can be completely removed from the database.</p>



<p><strong>Update Password</strong></p>



<p>To set a new user password, the administrator clicks on the checkbox of a user in the user table and then activates the update password function via the dropdown menu. A modal opens that shows the administrator the current email address of the selected user. The administrator must enter the new user password in one field and then repeat this new password in a further field. The new user password must be at least 7 characters long. There are no further requirements for the complexity of the password. If both entered passwords are identical, the new user password is written to the database. The user must log in to the system with the new user password.</p>



<h2 class="wp-block-heading">Update Trainings</h2>



<p>To update the data for an existing training, the administrator clicks on the Training tab to display the table with all the available trainings in the admin dashboard. Then the administrator selects the checkbox of the training that is to be updated and clicks the update training button in the navigation bar above. A modal opens that shows the selected training ID, the location and the date of the selected training. At the lower right edge of the modal, the administrator clicks on the Submit button to call the Create Training call site.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611475407416_001-002-Update-Trainings.png" alt="Update Training Modal"/><figcaption>Update Training Modal</figcaption></figure>



<p>This Create Training call site opens and show the data of the selected training course. If the administrator clicks on Dashboard in the top navigation, the administrator go directly back to the dashboard and no changes are saved to the database. </p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611476882326_001-003-Create-Training-Call-Site.png" alt="Create Training Call Site - No Changes possible"/><figcaption>Create Training Call Site &#8211; No Changes possible</figcaption></figure>



<p>The call site shows a form with data that cannot be changed but are displayed to the administrator for information. The call site shows also data that can be changed by the administrator. These data can be overwritten with the respective change.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611477505500_001-004-Create-Training-Call-Site.png" alt="Create Training Call Site - Changes possible"/><figcaption>Create Training Call Site &#8211; Changes possible</figcaption></figure>



<p>At the bottom of the Create Training call site there is a Back and a Confirm button. If the administrator clicks on Back, he goes back to the admin dashboard without saving any changes. If the administrator clicks on Confirm, the changes are saved in the database.</p>



<h2 class="wp-block-heading">Location Management</h2>



<p>Location Management is again a dropdown menu in the top navigation of the admin dashboard. The create and update locations functions are available here and the create training functionality.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611478385524_001-010-Locationmanagement.png" alt="Location Management and Location Table in the Admin Dashboard"/><figcaption>Location Management and Location Table in the Admin Dashboard</figcaption></figure>



<p>A location is a place where different trainings can take place at different times. In addition to the location name and the address, the price that has to be paid for a training session in this location is also linked to the location. The prices differ in adult and youth prices. </p>



<p><strong>Create Location</strong></p>



<p>To create a new Location, the administrator clicks on the create Location function via the dropdown menu. A modal opens that shows the administrator a form to capture all relevant Location data. The administrator records all relevant data. By clicking on Submit on the lower right edge of the modal, the data are written to the database. The new Location is created.</p>



<p><strong>Update Location</strong></p>



<p>To update a Location, the administrator clicks on the checkbox of a Location in the Location table and then activates the update Location function via the dropdown menu. A modal opens that shows the administrator the Location ID, Location Name and the City of the selected Location. At the lower right edge of the modal, the administrator clicks on the Submit button to call the Update Location call site. The call site shows data fields that can be changed by the administrator. The currently saved data is displayed and can be overwritten with the respective change. The Location ID is also displayed but cannot be overwritten. At the bottom of the call site there is a Back and a Confirm button. If the administrator clicks on Back, he goes back to the admin dashboard without saving any changes. If the administrator clicks on Confirm, the changes are saved in the database.</p>



<p><strong>Create Training</strong></p>



<p>To crate a Training, the administrator clicks on the checkbox of a Location in the Location table and then activates the create Training function via the dropdown menu. A modal opens that shows the administrator the Location ID, Location Name and the City of the selected Location. At the lower right edge of the modal, the administrator clicks on the Submit button to call the create Trainings call site. The Location data of the selected Location is displayed on the call Site but can not be overwritten. The call site also contains fields that can be written with training data i.e time start, time end and the date of the training session. At the bottom of the call site form there is a Back and a Confirm button. If the administrator clicks on Back, he goes back to the admin dashboard without saving any changes. If the administrator clicks on Confirm, the Training data are saved in the database. The Training is then created for the selected Location and the status is in-active. The administrator must then activate the training via the update trainings function. </p>



<h2 class="wp-block-heading">Invoice Management</h2>



<p>Invoice Management is a dropdown menu in the top navigation of the admin dashboard. The create and cancel Invoice are available here, but also the function to create a payment for an Invoice and the creation of a Re- Payment in the event that a user has paid too much and thus his balance becomes negative.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611555517464_001-020-Invoicemanagement.png" alt="Invoice Management"/><figcaption>Invoice Management</figcaption></figure>



<p><strong>Create Invoice for User</strong></p>



<p>To create a new invoice for a user, the administrator clicks on the checkbox of a user in the user table and then activates the create invoice for user function via the dropdown menu. A modal opens and shows the administrator the email address of the user who was selected and who will be the invoice recipient. At the lower right edge of the modal, the administrator clicks on the Submit button to create the new invoice for that user. All training sessions (bookings) in the past that have not yet been billed will be billed in this newly created invoice. The invoice status is open, the invoice date is the current date. Every booking that is billed in an invoice receives the status invoice. The administrator can view the newly created invoice in the invoice table in the admin dashboard.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611638396475_001-021-Invoice-table.png" alt="Invoice Table in the Admin Dasboard"/><figcaption>Invoice Table in the Admin Dasboard</figcaption></figure>



<p><strong>Cancel Invoice</strong></p>



<p>To cancel an invoice, the administrator clicks on the checkbox of the invoice in the invoice table and then activates the cancel invoice function via the dropdown menu. A modal opens that shows the administrator the invoice ID, invoice Date and the user email address. The invoice status must be open and no payment may have been recorded so far (amount paid must be 0). At the lower right edge of the modal, the administrator clicks on the Submit button to call the cancel invoice call site. The invoice data of the selected invoice is displayed on the call site but these data can not be overwritten. At the bottom of the call site there is a Back and a Confirm button. If the administrator clicks on Back, he goes back to the admin dashboard without canceling the invoice. If the administrator clicks on Confirm, the invoice will be canceled. This also means that every booking that has been billed in the canceled invoice receives the status booked again. </p>



<p><strong>Create Payment</strong></p>



<p>To create a payment, the administrator clicks on the checkbox of the invoice in the invoice table and then activates the create payment, function via the dropdown menu. If the invoice status is open a modal opens that shows the administrator the invoice ID, invoice Date and the user email address. At the lower right edge of the modal, the administrator clicks on the Submit button to call the pay invoice call site. The invoice data of the selected invoice is displayed on the call site but these data can not be overwritten. There is also a field in which the administrator can enter the amount paid. At the bottom of the call site there is a Back and a Confirm button. If the administrator clicks on Back, he goes back to the admin dashboard without creating the payment. If the administrator clicks on Confirm, the payment will be created. The amount paid for the invoice is a value that is written on every invoice. When the invoice has been created, the amount paid is equal to 0. After a payment has been recorded, the amount paid is added. The invoice balance is also a value that is on every invoice. If the invoice has been created, the invoice balance is equal to the invoice amount. After a payment has been recorded, the amount paid is subtracted from the invoice balance. When the invoice balance is less than or equal to 0 the amount for the invoice has been paid in full and the invoice status changes from open to paid. In case the invoice balance is greater 0 the invoice has been paid partly and the status remains open. </p>



<p><strong>Create Re- Payment</strong></p>



<p>To create a re-payment, the administrator clicks on the checkbox of the invoice in the invoice table and then activates the create re-payment, function via the dropdown menu. A modal opens that shows the administrator the invoice ID, invoice Date and the user email address. The invoice status must be paid and the invoice balance must be less than 0 because only then there is an overpayment and a re-payment can be initiated. At the lower right edge of the modal, the administrator clicks on the Submit button to call the re-pay invoice call site. The invoice data of the selected invoice is displayed on the call site but these data can not be overwritten. There is also a field in which the administrator can enter the amount re-paid. At the bottom of the call site there is a Back and a Confirm button. If the administrator clicks on Back, he goes back to the admin dashboard without creating the re-payment. If the administrator clicks on Confirm, the re-payment will be created.  After the re-payment has been created, the amount paid on the invoice is reduced by the re-payment amount and the re-payment amount is added to the invoice balance which is less than 0 before the re-payment. When the invoice balance after the re-payment is still less than or equal to 0 the invoice status remains paid. In case the invoice balance become greater 0 the invoice status changes from paid to open (very unrealistic use case).</p>



<h1 class="wp-block-heading">Player</h1>



<p>If a player has logged in to the application with his user ID (email) and password, he is given access to the player dashboard. </p>



<p>The <strong>player-dashboard</strong> shows 4 tables:</p>



<ul class="wp-block-list"><li>Available Trainings</li><li>My Bookings</li><li>My Invoices</li><li>My Data<br></li></ul>



<p>Each table can be viewed by clicking on the tabs in the player dashboard. You can see which table is currently selected by the fact that the corresponding tab has a light gray background and a blue line is displayed at the bottom of the tab.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611649669440_002-000-Player-Dahboard.png" alt="Player Dashboard"/><figcaption>Player Dashboard</figcaption></figure>



<p>Each line of the tables show a data record. A data record can be selected if there is a checkbox on the left. The Available Trainings table shows all bookable trainings with their most important attributes. The My Bookings and My Invoices tables shows the user all the current training bookings and invoices including their status. The My Data table show all personal data of the user stored in the system.</p>



<p>At the top there is a navigation bar with dropdown menus with all the important functions.</p>



<h2 class="wp-block-heading">My Bookings Management</h2>



<p>My Booking Management is a dropdown menu in the top navigation of the user dashboard. The create and cancel bookings functions are available here.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611722508091_002-020-My-Bookings-Mgmt.png" alt="My Bookings Management"/><figcaption>My Bookings Management</figcaption></figure>



<p><strong>Book Training</strong></p>



<p>To create a new booking in a certain training location a user the user clicks on the checkbox of a training in the available trainings table and then activates the book training function via the dropdown menu. A modal opens and shows the user the training ID, the location name and the training date of the training that was selected. At the lower right edge of the modal, the user clicks on the Submit button to call the book trainings call site. The booking data of the selected training is displayed on the call site but these data can not be overwritten. Depending on the category of the logged in user (adult or youth), the youth price or the adult price is displayed here on the call site. At the bottom of the call site there is a Back and a Confirm button. If the user clicks on Back, he goes back to the user dashboard without booking the training. If the user clicks on Confirm, the training will be booked and the data stored in the database. In addition, the user balance is increased by the price of the booked training.</p>



<p><strong>Cancel Booking</strong></p>



<p>To cancel an existing booking a user clicks on the checkbox of a booking in the my bookings table and then activates the cancel booking function via the dropdown menu. A modal opens and shows the user the training ID, the location name and the training date of the booking that was selected. At the lower right edge of the modal, the user clicks on the Submit button to call the cancel bookings call site. The booking data of the selected booking is displayed on the call site but these data can not be overwritten. At the bottom of the call site there is a Back and a Confirm button. If the user clicks on Back, he goes back to the user dashboard without canceling the booking. If the user clicks on Confirm, the booking will be canceled and the data are stored to the database. The status of the booking changes from booked to canceled and the user balance is reduced by the booking price.</p>



<h2 class="wp-block-heading">My Data Management</h2>



<p>My Data Management is a dropdown menu in the top navigation of the user dashboard. The update my data, change Email and change password functions are available here.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611722534154_002-030-My-Data-Mgmt.png" alt="My Data Management"/><figcaption>My Data Management</figcaption></figure>



<p><strong>Update my Data</strong></p>



<p>To update my Data, the user activates the update my data function via the dropdown menu. A modal opens that shows the users email address. At the lower right edge of the modal, the user clicks on the Submit button to call the Update My Data call site. The current user data of the user is displayed on the call site. Some data fields can be changed by the user. At the bottom of the call site there is a Back and a Confirm button. If the user clicks on Back, he goes back to the user dashboard without any changes. If the user clicks on Confirm, the data are stored to the database. In case the user did some changes the user data are updated. </p>



<p><strong>Change Email</strong></p>



<p>To change the email, the user activates the update change email function via the dropdown menu. A modal opens that shows the users current email address and a field to enter the new email address. By clicking on Submit on the lower right edge of the modal, the user&#8217;s new email address is written to the database. The user will then be logged off automatically and must log in using the new email address.  If the user has already booked trainings the email address stored on the bookings is updated with the new email address.</p>



<p><strong>Change Password</strong></p>



<p>To change the password, the user activates the change password function via the dropdown menu. A modal opens that shows the 3 fields: Current Password, New Password and Repeat New Password. The user must enter the current password in the current password field and then enter the new password twice. The new user password must be at least 7 characters long. There are no further requirements for the complexity of the password. If the current password is correct and the new password match the repeated password the new password is written to the database. The user remains logged in but has to log in with the new password the next time.</p>



<h1 class="wp-block-heading">Coach</h1>



<p>If a coach has logged in to the application with his user ID (email) and password, he is given access to the coach dashboard. </p>



<p>The <strong>coach-dashboard</strong> shows only one table with active trainings from today or in the future.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611727409524_003-000-Coach-Dahboard.png" alt=""/></figure>



<p>We are assuming that the coach will conduct a training session on January 30th and would like to confirm the presence of the players. The coach selects the today&#8217;s training (30.01.2021) on his coach dashboard and clicks on select training in the top navigation. The modal select training opens and shows a few details on the selected training such as training ID, location name and the training date.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611895665987_003-010-Select-Training.png" alt="Select Training Modal"/><figcaption>Select Training Modal</figcaption></figure>



<p>At the lower right edge of the modal, the coach can confirm with Submit that he will lead this training and now wants to confirm the participants of this training. After clicking on submit, the coach comes to the confirm participants call site and is shown all the players who have booked this training (here also player will be listed who have canceled their booking). This mean all bookings for this training session will be listed where the booking status is booked or canceled.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611895979277_003-020-Call-Participants.png" alt="Confirm Participants"/><figcaption>Confirm Participants</figcaption></figure>



<p>With the checkbox next to each player, the coach can now select the training participants. Here the coach can explicitly click on several (not just one). After all participants have been selected, the coach clicks on confirm participants in the top navigation and a modal opens that shows how many participants were selected by the coach for this training.</p>



<figure class="wp-block-image"><img decoding="async" src="https://paper-attachments.dropbox.com/s_0F8819BAE5224982CCCDCFC6F09C768579694B457B2BA217E6878D242017908C_1611896360934_003-030-Confirm-Participants.png" alt="Confirm Participants"/><figcaption>Confirm Participants</figcaption></figure>



<p>At the lower right edge of the modal, the coach can confirm with Submit that he now wants to confirm the participants of this training. After clicking on submit, the confirm participants process ends. The corresponding booking of the player receives the status participated.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
