<?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>Blog &#8211; Digitaldocblog</title>
	<atom:link href="https://digitaldocblog.com/tag/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://digitaldocblog.com</link>
	<description>Various digital documentation</description>
	<lastBuildDate>Sat, 13 Aug 2022 07:19:26 +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>Blog &#8211; Digitaldocblog</title>
	<link>https://digitaldocblog.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<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>
		<item>
		<title>Node.js series Part 4. Express Website with authentication and authorization in a Mac Production Environment</title>
		<link>https://digitaldocblog.com/mac/nodejs-series-part-4-express-website-with-authentication-and-authorization-in-a-mac-production-environment/</link>
		
		<dc:creator><![CDATA[admin]]></dc:creator>
		<pubDate>Thu, 18 Jun 2020 07:00:00 +0000</pubDate>
				<category><![CDATA[Mac OS]]></category>
		<category><![CDATA[Web-Development]]></category>
		<category><![CDATA[Webdesign]]></category>
		<category><![CDATA[Webserver]]></category>
		<category><![CDATA[Blog]]></category>
		<category><![CDATA[Blog-Application]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[Express.js]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[Mongoose]]></category>
		<category><![CDATA[Node.js]]></category>
		<category><![CDATA[NPM Node package manager]]></category>
		<category><![CDATA[Role Based Access Control]]></category>
		<category><![CDATA[Web-Application]]></category>
		<guid isPermaLink="false">https://digitaldocblog.com/?p=117</guid>

					<description><![CDATA[In a real production environment the app runs as a service in the background and this service is managed by a process manager. And the app should run behind a&#8230;]]></description>
										<content:encoded><![CDATA[
<p>In a real production environment the app runs as a service in the background and this service is managed by a process manager. And the app should run behind a reverse proxy server. This reverse proxy server manage the TLS encryption, receives the requests from the client and route any request to the app running in the background. So the connection from the client to the reverse proxy server is TLS encrypted. Therefore the data transferred between client and the reverse proxy are secured. </p>



<p>How to setup such a <strong>production environment</strong> will be shown in the <strong>first chapter</strong> of this documentation. </p>



<p>The express app itself also contains some <strong>security features</strong>. The app contains a session based user authentication and HTTP headers which help to further secure the app. This is explained of the <strong>second chapter</strong> of this documentation.</p>



<p>Finally the express app should use the <strong>template engine PUG</strong> to render the HTML for us. This is describes of the <strong>third chapter</strong> of this documentation. </p>



<h3 class="wp-block-heading">1. Setup the production environment</h3>



<h4 class="wp-block-heading">Installation of mongodb</h4>



<p>The command <code>brew tap</code> without any arguments lists the GitHub repositories that are currently linked to your Homebrew installation. </p>



<pre class="wp-block-code"><code>Patricks-MBP:~ patrick$ brew tap
homebrew/cask
homebrew/core
homebrew/services
</code></pre>



<p>The formula mongodb has been removed from homebrew-core. But fortunately the MongoDB Team is maintaining a custom <a href="https://github.com/mongodb/homebrew-brew">Homebrew tap on GitHub</a>. Read the instructions in the README.md file. </p>



<p>Add the custom tap in the Mac OS terminal and install mongodb.</p>



<pre class="wp-block-code"><code>Patricks-MBP:~ patrick$ brew tap mongodb/brew

Patricks-MBP:~ patrick$ brew tap
homebrew/cask
homebrew/core
homebrew/services
mongodb/brew

Patricks-MBP:~ patrick$ brew install mongodb-community@4.2

</code></pre>



<p>After the installation the relevant paths are.</p>



<pre class="wp-block-code"><code>the configuration file (/usr/local/etc/mongod.conf)
the log directory path (/usr/local/var/log/mongodb)
the data directory path (/usr/local/var/mongodb)

</code></pre>



<p>Check the services with homebrew</p>



<pre class="wp-block-code"><code>brew services list

Name              Status  User    Plist
mongodb-community started patrick /Users/patrick/Library/LaunchAgents/homebrew.mxcl.mongodb-community.plist
  
</code></pre>



<p>Start and stop mongodb.</p>



<pre class="wp-block-code"><code>brew services start mongodb-community

brew services stop mongodb-community

</code></pre>



<h4 class="wp-block-heading">Setup mongodb for the project</h4>



<p>Setup an admin user</p>



<pre class="wp-block-code"><code>:# mongo

&gt; use admin
switched to db admin

&gt; db
admin

&gt; db.createUser({ user: "adminUser", pwd: "adminpassword", roles: &#91;{ role: "userAdminAnyDatabase", db: "admin" }, {"role" : "readWriteAnyDatabase", "db" : "admin"}] })

&gt; db.auth("adminUser", "adminpassword")
1

&gt; show users
{
    "_id" : "admin.adminUser",
    "userId" : UUID("5cbe2fc4-1e54-4c2d-89d1-317340429571"),
    "user" : "adminUser",
    "db" : "admin",
    "roles" : &#91;
        {
            "role" : "userAdminAnyDatabase",
            "db" : "admin"
        },
        {
            "role" : "readWriteAnyDatabase",
            "db" : "admin"
        }
    ],
    "mechanisms" : &#91;
        "SCRAM-SHA-1",
        "SCRAM-SHA-256"
    ]
}

&gt; exit

</code></pre>



<p>Enable authentication with <code>security: authorization: enabled</code></p>



<pre class="wp-block-code"><code>#&gt; nano /usr/local/etc/mongod.conf

systemLog:
  destination: file
  path: /usr/local/var/log/mongodb/mongo.log
  logAppend: true
storage:
  dbPath: /usr/local/var/mongodb
net:
  port: 27017
  bindIp: 127.0.0.1
security:
  authorization: enabled


</code></pre>



<p>Login and authenticate with admin</p>



<pre class="wp-block-code"><code>#&gt; mongo

MongoDB shell version v4.2.3
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&amp;gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("b3e7f48a-a05c-4894-87db-996cb34eb1fb") }
MongoDB server version: 4.2.3

&gt; show dbs
&gt; db
test
&gt; use admin
switched to db admin
&gt; db
admin
&gt; show dbs
&gt; db.auth("adminUser", "adminpassword")
1
&gt; show dbs
admin               0.000GB
config              0.000GB
local               0.000GB

&gt; 

</code></pre>



<p>If you login you dont see any databases when you call <code>show dbs</code>. The default database you are connected to is <code>test</code>. </p>



<p>Then you connect to admin database. For admin you setup the admin user with the roles <code>userAdminAnyDatabase</code> and <code>readWriteAnyDatabase</code>. With these permissions the admin user can manage users for any database and has read and write access to any database. </p>



<p>So wehen you logon to admin database with the admin user you are able to see all databases with <code>show dbs</code>. </p>



<p>Mongodb comes with 3 standard dbs pre installed:</p>



<ul class="wp-block-list"><li>admin </li><li>config</li><li>local<br></li></ul>



<p>Create a new database for our express-security app (authenticated as admin user &#8211; see above)</p>



<pre class="wp-block-code"><code>&gt; use express-security
switched to db express-security
&gt; db
express-security
&gt; show dbs
admin               0.000GB
config              0.000GB
local               0.000GB
&gt; 

</code></pre>



<p>The DB which you&#8217;ve created is not listed here. We need to insert at least one collection into it for displaying that database in the list. </p>



<pre class="wp-block-code"><code>&gt; db
express-security

&gt; db.createCollection("col_default")
{ "ok" : 1 }

&gt; show dbs
admin               0.000GB
config              0.000GB
express-security    0.000GB
local               0.000GB

&gt; exit

</code></pre>



<p>Create an owner user for express-security database using the admin user</p>



<pre class="wp-block-code"><code>#&gt; mongo

MongoDB shell version v4.2.3
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&amp;gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("79f79b63-9d08-489f-9e6c-bfc10d8cc09e") }
MongoDB server version: 4.2.3

&gt; db
test

&gt; show dbs

&gt; use admin
switched to db admin

&gt; db.auth("adminUser", "adminpassword")
1

&gt; db
admin

&gt; show dbs
admin               0.000GB
config              0.000GB
express-security    0.000GB
local               0.000GB

&gt; use express-security
switched to db express-security

&gt; db.createUser({ user: "owner_express-security", pwd: "passowrd", roles: &#91;{ role: "dbOwner", db: "express-security" }] })
Successfully added user: {
	"user" : "owner_express-security",
	"roles" : &#91;
		{
			"role" : "dbOwner",
			"db" : "express-security"
		}
	]
}

&gt; db
express-security

&gt; show users
{
	"_id" : "express-security.owner_express-security",
	"userId" : UUID("7a0bafb2-d2ed-4d18-9aba-e2f15a503ec5"),
	"user" : "owner_express-security",
	"db" : "express-security",
	"roles" : &#91;
		{
			"role" : "dbOwner",
			"db" : "express-security"
		}
	],
	"mechanisms" : &#91;
		"SCRAM-SHA-1",
		"SCRAM-SHA-256"
	]
}

&gt; exit 
</code></pre>



<p>Connection string to connect to express-security db using the owner_express-security user:</p>



<pre class="wp-block-code"><code>mongodb://owner_express-security:password@localhost/express-security
</code></pre>



<h4 class="wp-block-heading">Installation of PM2</h4>



<p>PM2 is a process manager for Node.js applications. It can daemonize applications to run them as a service in the background.</p>



<p>I install pm2 as a global npm package on my Mac.</p>



<pre class="wp-block-code"><code>Patricks-Macbook Pro:~ patrick$ npm install pm2 -g
</code></pre>



<p>Then navigate to your project directory.</p>



<pre class="wp-block-code"><code>Patricks-Macbook Pro:~ patrick$ cd Software/dev/node/articles/2020-05-15-express-security/express-security

Patricks-Macbook Pro:~ patrick$ ls -l 
total 112
drwxr-xr-x    5 patrick  staff    160 30 Mai 05:28 database
drwxr-xr-x  115 patrick  staff   3680 30 Mai 19:35 node_modules
-rw-r--r--    1 patrick  staff  34366 30 Mai 19:35 package-lock.json
-rw-r--r--    1 patrick  staff    339 30 Mai 19:35 package.json
-rw-r--r--@   1 patrick  staff  12343 30 Jun 05:00 secserver.js
drwxr-xr-x    3 patrick  staff     96 30 Mai 05:03 static

Patricks-Macbook Pro:express-security patrick$ 

</code></pre>



<p>Start your app using pm2</p>



<pre class="wp-block-code"><code>Patricks-Macbook Pro:express-security patrick$ pm2 start secserver.js

Patricks-Macbook Pro:~ patrick$ pm2 list
┌─────┬──────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐
│ id  │ name         │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │
├─────┼──────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤
│ 0   │ secserver    │ default     │ 1.0.0   │ fork    │ 640      │ 16h    │ 0    │ online    │ 0%       │ 48.6mb   │ patrick  │ disabled │
└─────┴──────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘

Patricks-Macbook Pro:~ patrick$ 

</code></pre>



<p>Other comands to control the process manager. </p>



<pre class="wp-block-code"><code>pm2 start secserver.js

pm2 start &lt;id&gt;

pm2 list

pm2 stop &lt;id&gt;

pm2 restart &lt;id&gt;

pm2 show &lt;id&gt;

</code></pre>



<h4 class="wp-block-heading">Installation of nginx</h4>



<p><a href="https://nginx.org/en/">nginx</a> is an open source HTTP and an HTTP Reverse Proxy Server (also mail proxy and load balancer etc.). I install nginx on my Mac with Homebrew. </p>



<pre class="wp-block-code"><code>brew install nginx

</code></pre>



<p>You can list the brew services with the following command.</p>



<pre class="wp-block-code"><code>Patricks-MBP:digitaldocblog-V3 patrick$ brew services list

Name              Status  User    Plist
mongodb-community started patrick /Users/patrick/Library/LaunchAgents/homebrew.mxcl.mongodb-community.plist
nginx             started patrick /Users/patrick/Library/LaunchAgents/homebrew.mxcl.nginx.plist
Patricks-MBP:digitaldocblog-V3 patrick$
</code></pre>



<p>You can start and stop the brew services as follows.</p>



<pre class="wp-block-code"><code>brew install nginx

brew services start nginx

brew services stop nginx

</code></pre>



<h4 class="wp-block-heading">Setup nginx with TLS/SSL</h4>



<p>SSL/TLS works by using the combination of a public certificate and a private key. </p>



<p>The <strong>SSL key (private key)</strong> is kept secret on the server. It is used to encrypt content sent to clients. </p>



<p>The <strong>SSL certificate</strong> is publicly shared with anyone requesting the content. It can be used to decrypt the content signed by the associated SSL key.</p>



<p><strong>create private key</strong></p>



<pre class="wp-block-code"><code>Patricks-MBP:express-security patrick$ cd /usr/local/etc/nginx

Patricks-MBP:nginx patrick$ ls -l
total 144
-rw-r--r--  1 patrick  admin  1077  5 Apr 13:18 fastcgi.conf
-rw-r--r--  1 patrick  admin  1077  5 Apr 13:18 fastcgi.conf.default
-rw-r--r--  1 patrick  admin  1007  5 Apr 13:18 fastcgi_params
-rw-r--r--  1 patrick  admin  1007  5 Apr 13:18 fastcgi_params.default
-rw-r--r--  1 patrick  admin  2837  5 Apr 13:18 koi-utf
-rw-r--r--  1 patrick  admin  2223  5 Apr 13:18 koi-win
-rw-r--r--  1 patrick  admin  5231  5 Apr 13:18 mime.types
-rw-r--r--  1 patrick  admin  5231  5 Apr 13:18 mime.types.default
-rw-r--r--  1 patrick  admin  3106 15 Mai 05:19 nginx.conf
-rw-r--r--  1 patrick  admin  2680  5 Apr 13:18 nginx.conf.default
-rw-r--r--  1 patrick  admin  3091 21 Jan 05:40 nginx.conf.working
-rw-r--r--  1 patrick  admin   636  5 Apr 13:18 scgi_params
-rw-r--r--  1 patrick  admin   636  5 Apr 13:18 scgi_params.default
drwxr-xr-x  3 patrick  admin    96 21 Jan 06:02 servers
-rw-r--r--  1 patrick  admin   664  5 Apr 13:18 uwsgi_params
-rw-r--r--  1 patrick  admin   664  5 Apr 13:18 uwsgi_params.default
-rw-r--r--  1 patrick  admin  3610  5 Apr 13:18 win-utf

Patricks-MBP:nginx patrick$ mkdir ssl

Patricks-MBP:nginx patrick$ ls -l
total 152
-rw-r--r--  1 patrick  admin  1077  5 Apr 13:18 fastcgi.conf
-rw-r--r--  1 patrick  admin  1077  5 Apr 13:18 fastcgi.conf.default
-rw-r--r--  1 patrick  admin  1007  5 Apr 13:18 fastcgi_params
-rw-r--r--  1 patrick  admin  1007  5 Apr 13:18 fastcgi_params.default
-rw-r--r--  1 patrick  admin  2837  5 Apr 13:18 koi-utf
-rw-r--r--  1 patrick  admin  2223  5 Apr 13:18 koi-win
-rw-r--r--  1 patrick  admin  5231  5 Apr 13:18 mime.types
-rw-r--r--  1 patrick  admin  5231  5 Apr 13:18 mime.types.default
-rw-r--r--@ 1 patrick  admin   373 18 Mai 05:38 nginx.conf
-rw-r--r--  1 patrick  admin  2680  5 Apr 13:18 nginx.conf.default
-rw-r--r--  1 patrick  admin  3091 21 Jan 05:40 nginx.conf.working
-rw-r--r--@ 1 patrick  admin  1390 17 Mai 05:19 nginx_old.conf
-rw-r--r--  1 patrick  admin   636  5 Apr 13:18 scgi_params
-rw-r--r--  1 patrick  admin   636  5 Apr 13:18 scgi_params.default
drwxr-xr-x  5 patrick  admin   160 18 Mai 05:20 servers
drwxr-xr-x  4 patrick  admin   128 16 Mai 05:41 ssl
-rw-r--r--  1 patrick  admin   664  5 Apr 13:18 uwsgi_params
-rw-r--r--  1 patrick  admin   664  5 Apr 13:18 uwsgi_params.default
-rw-r--r--  1 patrick  admin  3610  5 Apr 13:18 win-utf

Patricks-MBP:nginx patrick$ cd ssl

Patricks-MBP:ssl patrick$ pwd
/usr/local/etc/nginx/ssl

Patricks-MBP:ssl patrick$ openssl genrsa -out privateKey.pem 4096

Patricks-MBP:ssl patrick$ ls -l
total 16
-rw-r--r--  1 patrick  admin  3247 16 Mai 05:22 privateKey.pem

</code></pre>



<p><strong>create certificate signing request (CSR)</strong></p>



<pre class="wp-block-code"><code>Patricks-MBP:ssl patrick$ pwd
/usr/local/etc/nginx/ssl

Patricks-MBP:ssl patrick$ openssl req -new -key privateKey.pem -out csr.pem

Patricks-MBP:ssl patrick$ ls -l
total 16
-rw-r--r--  1 patrick  admin  1740 16 Mai 05:23 csr.pem
-rw-r--r--  1 patrick  admin  3247 16 Mai 05:22 privateKey.pem

</code></pre>



<p>In case I would like to request an official certificate I must send this csr to the certificate authority. This authority would then create an <strong>authority signed certificate</strong> from the CSR and send it back to me.</p>



<p>This step is done by ourselves and this is the reason why we create a <strong>self signed certificate</strong>. This self signed certificate is not a official certificae and not trusted by any browser. It is not useful to use a self signed certificate in production because it produces error messages in the browsers. But for local development a self signed certificate is ok. </p>



<p>So create the self signed certificale. The csr file can then be removed. </p>



<p><strong>create the self signed certificate</strong></p>



<pre class="wp-block-code"><code>Patricks-MBP:ssl patrick$ pwd
/usr/local/etc/nginx/ssl

Patricks-MBP:ssl patrick$ openssl x509 -in csr.pem -out selfsignedcertificate.pem -req -signkey privateKey.pem -days 365

Patricks-MBP:ssl patrick$ ls -l
total 24
-rw-r--r--  1 patrick  admin  1740 16 Mai 05:23 csr.pem
-rw-r--r--  1 patrick  admin  3247 16 Mai 05:22 privateKey.pem
-rw-r--r--  1 patrick  admin  1980 16 Mai 05:39 selfsignedcertificate.pem

Patricks-MBP:ssl patrick$ rm csr.pem

Patricks-MBP:ssl patrick$ ls -l
total 24
-rw-r--r--  1 patrick  admin  3247 16 Mai 05:22 privateKey.pem
-rw-r--r--  1 patrick  admin  1980 16 Mai 05:39 selfsignedcertificate.pem
 
</code></pre>



<p><strong>show certificate details</strong></p>



<pre class="wp-block-code"><code>Patricks-MBP:ssl patrick$ pwd
/usr/local/etc/nginx/ssl

Patricks-MBP:ssl patrick$ openssl x509 -in selfsignedcertificate.pem -text -noout

</code></pre>



<h4 class="wp-block-heading">Configure nginx Servers with SSL</h4>



<p>In our configuration we enforce ssl. Therefore we create a <strong>default Webserver</strong> listening on Port 80 with the server name  <code>servtest.rottlaender.lan</code>. </p>



<p>Any request to <code>servtest.rottlaender.lan:80</code> is redirected to my <strong>Reverse Proxy Server</strong> which is listening on <code>servtest.rottlaender.lan:443</code>.<br></p>



<p>The <strong>default Webserver</strong> is configured in <code>/usr/local/etc/nginx/nginx.conf</code>.</p>



<pre class="wp-block-code"><code># /usr/local/etc/nginx/nginx.conf
# default Webserver

worker_processes  1;
error_log  /usr/local/etc/nginx/logs/error.log;

events {
    worker_connections  1024;
}

http {
    include       		mime.types;
    default_type  		application/octet-stream;
    sendfile        	on;
    keepalive_timeout  	65;

    access_log  /usr/local/etc/nginx/logs/access.log;

    # default Webserver redirect from port 80 to port 443 ssl
    
    server {
      	listen 80;
    	listen &#91;::]:80;
    	server_name servtest.rottlaender.lan;
    	return 301 https://$host$request_uri;
    }
    
    include servers/*;
    
}
</code></pre>



<p>The <strong>Reverse Proxy Server</strong> is configured in <code>/usr/local/etc/nginx/servers/reverse</code>.</p>



<pre class="wp-block-code"><code>// /usr/local/etc/nginx/servers/reverse
// reverse Proxy Server

server {

    listen      443 ssl;
    server_name servtest.rottlaender.lan;

    ssl_certificate      ssl/selfsignedcertificate.pem;
    ssl_certificate_key  ssl/privateKey.pem;
    ssl_session_cache    shared:SSL:1m;
    ssl_session_timeout  5m;
    ssl_ciphers  HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers  on;

    location / {
       proxy_pass http://localhost:3300;
       proxy_set_header X-Forwarded-For $remote_addr;
    }
}

</code></pre>



<p>The server name servtest.rottlaender.lan is linked in /private/etc/hosts to the ip 192.168.178.20 which is the ip of my computer in my local network.</p>



<pre class="wp-block-code"><code>Patricks-MBP:digitaldocblog-V3 patrick$ cat /private/etc/hosts
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1			localhost
255.255.255.255		broadcasthost
::1             	localhost
192.168.178.20 		servtest.rottlaender.lan
</code></pre>



<h3 class="wp-block-heading">2. Express Secure App (Security Features HTML version)</h3>



<p>This is a very simple application but show the basic security features you should use when you run a node app in a production environment. </p>



<p>The app is a website with a simple layout and navigation. </p>



<p>The Home page contain static information and can be accessed by everyone. </p>



<p>On the register page, users can find a form to register. The user data entered here are saved in the database and the user is logged in at the same time. Known users can log in with their email and password after successful registration on the login page. The login and register page can only be accessed if the user is not logged in. If a user is logged in and tries to access the login or register, he will be redirected to the dashboard page.</p>



<p>The dashboard is a personalized area of the website. This area can only be accessed if the user is logged in. If a user is not logged in, he will be redirected to the login page.</p>



<p>Logout is not really a page but a link that contains a logout function. Users who are logged in can log out using this link. Users who are not already logged in will be redirected to the login page.</p>



<h4 class="wp-block-heading">Download the code from GitHub</h4>



<p>Pls. <a href="https://github.com/prottlaender/node-part-4-express-security-with-db-html">go to my GitHub site</a> and clone the code. Here you find a some inline documentation in the code. The details are explained in this chapter. </p>



<h4 class="wp-block-heading">Create your app home directory express-security</h4>



<p>My app home directory is different to the one that is available after you cloned the code from GitHub. </p>



<pre class="wp-block-code"><code>Patricks-MBP:2020-05-15-express-security patrick$ pwd
/Users/patrick/software/dev/node/articles/2020-05-15-express-security

Patricks-MBP:2020-05-15-express-security patrick$ mv node-part-5-express-security-with-db-pug express-security

Patricks-MBP:2020-05-15-express-security patrick$ cd express-security

Patricks-MBP:express-security patrick$ pwd
/Users/patrick/software/dev/node/articles/2020-05-15-express-security/express-security
</code></pre>



<h4 class="wp-block-heading">Manage environment variables</h4>



<p>To manage environment variables for my app I use <code>envy</code>. First you need the files <code>.env</code> and <code>.env.example</code> in the root of your project directory. In <code>.env.example</code> you create a list of all potential environment variables without any values and in <code>.env</code> you use the defined variables and assign the values to them. </p>



<pre class="wp-block-code"><code>Patricks-MBP:express-security patrick$ ls -al
total 152
drwxr-xr-x   14 patrick  staff    448 23 Jun 05:29 .
drwxr-xr-x    4 patrick  staff    128 26 Mai 05:40 ..
-rw-------    1 patrick  staff    181 23 Jun 05:59 .env
-rw-r--r--    1 patrick  staff     53 23 Jun 05:59 .env.example
....

Patricks-MBP:express-security patrick$ cat .env.example
port=
mongodbpath=
sessionsecret=
sessioncookiename=

Patricks-MBP:express-security patrick$ cat .env
port=&lt;YOUR_PORT&gt;
mongodbpath=&lt;YOUR_CONNECTION_STRING&gt;
sessionsecret=&lt;YOUR_SESSION_SECRET&gt;
sessioncookiename=&lt;YOUR_SESSION_COOKIE_NAME&gt;

Patricks-MBP:express-security patrick$ 

</code></pre>



<p>Envy must be installed as dependency and required in the main application file <em>secserver.js</em>. Then you can set the environment variables as follows. </p>



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

....

// envy module to manage environment variables
const envy = require('envy');

// set the environment variables
const env = envy()
const port = env.port
const mongodbpath = env.mongodbpath
const sessionsecret = env.sessionsecret
const sessioncookiename = env.sessioncookiename
....
</code></pre>



<h4 class="wp-block-heading">Start the MongoDB Server</h4>



<p>To run the db server we install <code>mongoose</code> as dependency and require it in the db.js configuration file. The database connection will be initiated with <code>mongoose.connect</code> and the StartMongoServer function will be exported to be called in the main application file <em>secserver.js</em>.</p>



<pre class="wp-block-code"><code>const envy = require('envy')
const env = envy()

const mongodbpath = env.mongodbpath

const mongoose = require('mongoose');
mongoose.set('useNewUrlParser', true);
mongoose.set('useUnifiedTopology', true);

const StartMongoServer = async function() {
  try {

    await mongoose.connect(mongodbpath)
    .then(function() {
      console.log(`Mongoose connection open on ${mongodbpath}`);
    })
    .catch(function(error) {
      console.log(`Connection error message: ${error.message}`);
    })

  } catch(error) {
    res.json( { status: "db connection error", message: error.message } );
  }

};

module.exports = StartMongoServer;
</code></pre>



<h4 class="wp-block-heading">Authentication and authorization</h4>



<p>For user authentication we use the module <code>express-session</code> and to store session data in the session store in our database we use <code>connect-mongodb-session</code>. Therefore we install these modules as dependencies in our project and require the modules in our <em>secserver.js</em> main application file.</p>



<p>Then we create with <code>new MongoDBStore</code> a session storage in our MongoDB to store session data in collection <code>col_sessions</code>. errors are catched with <code>store.on</code>. </p>



<p>We use the session in our app with <code>app.use( session({...}) )</code>. With every request to our site a new session object is created with a unique session ID which include a session cookie object. The session object has keys options and the values for each key define how to deal with the session object. The session ID is created and signed using the <code>secret</code> option. We use <code>name</code> to provide a session cookie name and <code>store</code> to define where the session object should be stored (in case we store the session). </p>



<p>We can access the session object with <code>req.session</code> and the session ID with <code>req.session.id</code>. With every request we have a new session and this new session will be created but not stored anywhere so far. We say the session is <em>uninitialized</em>. The <code>saveUninitialized</code> false option ensure that a session will only be written to the store in case it has been modified. What does this mean?<br></p>



<p>We can modify the session when we store additional data into it. We always do this when the user is logging in via the <code>login</code> or the <code>register</code> route. When we post the data from the <em>login-</em> or from the <em>registration-form</em> to the server we call <em>loginUser</em> or the <em>createUser</em> module which is defined in <code>database/controllers/userC.js</code>. Both modules do basically the same thing: They create a userData Object and store the userData object into the session object and redirect the user to the dashboard when login or registration was successful. </p>



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

var userData = { 
	userId: user._id, 
	name: user.name, 
	lastname: user.lastname, 
	email: user.email, 
	role: user.role 
	}

    req.session.userData = userData

    res.redirect('/dashboard')

....

</code></pre>



<p>If the user is successfully logged in the session is <em>initialized</em> (modified), the session object incl. the userData object are stored into the store and a cookie is stored into the requesting browser. The content of the cookie is only a hash of the session Id and with each request of a logged in user the session on the server is looked up. </p>



<p>The cookie in the browser will live max 1 week as we defined in the cookie object <code>maxAge</code> set to 1 week. Because of the cookie option <code>sameSite</code> true the cookie scope is limited to the same site. </p>



<p>Then the <code>resave</code> false option ensures that the session will not be updated with every request. This mean the session ID that has been created when the user has logged in will be kept until the user is logged out again.<br></p>



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

....

// server side session and cookie module
const session = require('express-session');
// mongodb session storage module
const connectMdbSession = require('connect-mongodb-session');

....

// Create MongoDB session storage
const MongoDBStore = connectMdbSession(session)
const store = new MongoDBStore({
  uri: mongodbpath,
  collection: 'col_sessions'
});

// catch errors in case store creation fails
store.on('error', function(error) {
  console.log(`error store session in session store: ${error.message}`);
});

// Create the express app
const app = express();

....

// 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
  },

}));

....
</code></pre>



<h4 class="wp-block-heading">Secure HTTP headers</h4>



<p>Response headers are HTTP header that come with the HTTP response from the server to the client. The http response header contain data that could possibly damage the integrity of the client. It is therefore important to secure the response header of your application.</p>



<p>To secure the http response headers I user the module <a href="https://helmetjs.github.io/">helmet</a>. This is a relatively easy-to-use module consisting of various middleware functionalities to secure various http response headers.</p>



<p>First we install <code>helmet</code>as a dependency of our project. Then we require helmet and use helmet right after we created the app. </p>



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

// hTTP module
const http = require('http');
// express module
const express = require('express');
// hTTP header security module
const helmet = require('helmet');

// Create the express app
const app = express();

....

// use secure HTTP headers using helmet
app.use(helmet())

</code></pre>



<p>Using simply <code>app.use(helmet())</code> set the http header security to default. Then the following 7 out 11 helmet features can be used.</p>



<ol class="wp-block-list"><li><a href="https://helmetjs.github.io/docs/dns-prefetch-control">dnsPrefetchControl</a> controls browser DNS prefetching</li><li><a href="https://helmetjs.github.io/docs/frameguard/">frameguard</a> to prevent clickjacking</li><li><a href="https://helmetjs.github.io/docs/hide-powered-by/">hidePoweredBy</a> to remove the X-Powered-By header</li><li><a href="https://helmetjs.github.io/docs/hsts/">hsts</a> for HTTP Strict Transport Security</li><li><a href="https://helmetjs.github.io/docs/ienoopen/">ieNoOpen</a> sets X-Download-Options for IE8+</li><li><a href="https://helmetjs.github.io/docs/dont-sniff-mimetype/">noSniff</a> to keep clients from sniffing the MIME type</li><li><a href="https://helmetjs.github.io/docs/xss-filter/">xssFilter</a> adds some small XSS protections<br></li></ol>



<p>When we then request our home page to retrieve the http headers using <code>curl -k --head</code> in the terminal we see the following output. </p>



<pre class="wp-block-code"><code>Patricks-MBP:express-security patrick$ curl -k --head https://servtest.rottlaender.lan
HTTP/1.1 200 OK
Server: nginx/1.19.0
Date: Fri, 26 Jun 2020 16:14:14 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 1734
Connection: keep-alive
X-DNS-Prefetch-Control: off
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=15552000; includeSubDomains
X-Download-Options: noopen
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
ETag: W/"6c6-U2uWyDNyzlyBAbSI/Quxqo9RRQE"

Patricks-MBP:express-security patrick$ 
</code></pre>



<h4 class="wp-block-heading">App routing</h4>



<p><strong>get routes:</strong> We have the following <code>get</code> routes and navigation.</p>



<ul class="wp-block-list"><li>Home (/)</li><li>Login (/login)</li><li>Register (/register)</li><li>Dashboard (/dashboard)</li><li>Logout (/logout)<br></li></ul>



<p><code>get</code> routes involve an optional middleware and respond HTML back to the client. </p>



<pre class="wp-block-code"><code>app.get('/&lt;route&gt;', &lt;optional: someMiddleware&gt;, (req, res) =&gt; {
	
	res.send(`&lt;some HTML&gt;`)
})
</code></pre>



<p>I will not explain the HTML and css in detail. But as everyone can see, the HTML is the same for every route except for the <code>&lt;body&gt;</code>. Of course, this is not very nice and becomes a bit more efficient with the use of a template engine, which I will explain below using the <a href="https://pugjs.org/api/getting-started.html">PUG template engine</a>. I will then rebuild the app using PUG.</p>



<p>Lets have a look at the <code>middleware</code>. If a request is made for a route and a middleware function is included, the middleware function is first executed before the next routing function <code>function(req, res)</code> is called. A condition is built into the middleware function which is checked. My middleware is built so that in case the condition is true the middleware code is executed directly and the next routing function is omitted. If the condition is false, the next routing function <code>function(req, res)</code> is called.</p>



<p>I have built 2 different middleware functions which each check</p>



<p><strong>middleware 1 (login- and register route):</strong> a user is logged in</p>



<pre class="wp-block-code"><code>// secserver.js
....
// middleware 1 to redirect authenticated users to their dashboard
const redirectDashboard = (req, res, next) =&gt; {
  if (req.session.userData) {
    res.redirect('/dashboard')

  } else {
    next()
  }
}
....
</code></pre>



<p>If a user is logged in the request should be redirected to the dashboard route, in any other case (user is not logged in) the next routing function <code>function(req, res)</code> is called and respond the HTML to the browser. This middleware 1 is included in the <strong>/login-</strong> and <strong>/register</strong> route. This mean logged in users will be redirected to their dashboard, not logged in users will see the login- and register form. </p>



<p><strong>middleware 2 (dashboard- and logout route):</strong> a user is not logged in.</p>



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

// middleware 2 to redirect not authenticated users to login
const redirectLogin = (req, res, next) =&gt; {
  if (!req.session.userData) {
    res.redirect('/login')
  } else {
    next()
  }
}
....
</code></pre>



<p>If a user is not logged in the request should be redirected to the login roure, in any other case (user is logged in) the next routing function <code>function(req, res)</code> is called and respond the HTML to the browser. This middleware 2 is included in the <strong>/dashboard-</strong> and <strong>/logout</strong> route. This mean not logged in users will be redirected to login route, logged in users will see the dashboard- or can log themselves out. </p>



<p><strong>post routes:</strong> We have the following <code>post</code> routes.</p>



<ul class="wp-block-list"><li>/login</li><li>/register<br></li></ul>



<p>The login and the register <code>get</code> routes contain a form in the HTML. With these forms the user provide the data to login and for user registration. When the user click the send button the <em>action</em> is to call the login- or register <code>post</code> route. This will happen for all not logged in users. The login and the register <code>get</code> routes have the middleware <code>redirectDashboard</code>to redirect the user to the dashbard if the user is already logged in.<br></p>



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

app.get('/login', redirectDashboard, (req, res) =&gt; {
....
res.send(`
....
&lt;div class="form"&gt;
	&lt;form id='register_form' method='post' action='/register'&gt;
	......
	&lt;label for='send'&gt;
   		&lt;input class='sendbutton' type='submit' name='send' value='Send'&gt;
	&lt;/label&gt;
	&lt;/form&gt;
&lt;/div&gt;

`)
)}
....

app.get('/register', redirectDashboard, (req, res) =&gt; {
....
res.send(`
....
&lt;div class="form"&gt;
	&lt;form id='login_form' method='post' action='/login'&gt;
	......
	&lt;label for='send'&gt;
   		&lt;input class='sendbutton' type='submit' name='send' value='Send'&gt;
	&lt;/label&gt;
	&lt;/form&gt;
&lt;/div&gt;
`)
)}
.....
</code></pre>



<p>The <code>post</code> routes contain functions to login- (loginUser) or register (createUser) the user.<br></p>



<pre class="wp-block-code"><code>// secserver.js
....
// Post routes to manage user login and user registration
app.post('/login', userController.loginUser);

app.post('/register', userController.createUser);
....
</code></pre>



<p>The <code>loginUser</code> function is defined in the user controller <code>database/controllers/userC.js</code>. This function lookup a user in the database based on the email address that has been provided by the request body. The data that are attached to the request body have been provided by the user vie the login form of the app. If no user could be found in the database login is not possible. If a user exist with the given email address then the provided password will be compared with the one stored in the database. If the password match fail login is not possible because the provided password is wrong. in any other case, the login takes place and a userData object is created and attached to the session object.</p>



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

User.findOne({ email: req.body.email }, function(error, user) {
  if (!user) {
    res.status(400).send({ code: 400, status: 'Bad Request', message: 'No User found with this email' })

    } else {
      if (bcrypt.compareSync(req.body.password, user.password)) {
    
        var userData = { userId: user._id, name: user.name, lastname: user.lastname, email: user.email, role: user.role }

          req.session.userData = userData

          res.redirect('/dashboard')

        } else {
          res.status(400).send({ code: 400, status: 'Bad Request', message: 'Wrong User password' })
        }

      }
    })

  }
</code></pre>



<p>The <code>createUser</code> function is also defined in the user controller <code>database/controllers/userC.js</code>. This function create a new User object based on the data from the request body provided by the user via the form. The provided password will be hashed and stored together with all other data into the database. Finally a userData object is created and attached to the session and the user will be redirected to the dashboard after the registration was successful.<br></p>



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

createUser: async function (req, res) {
    // assign input data from request body to input variables
    const name = req.body.name
    const lastname = req.body.lastname
    const email = req.body.email
    const password = req.body.password
    const role = req.body.role

    const newUser = new User({
      name: name,
      lastname: lastname,
      email: email,
      password: password,
      role: role
    })

    newUser.password = await bcrypt.hash(newUser.password, saltRounds)

    await newUser.save(function(err, user) {
          if (err) {
            // if a validation err occur end request and send response
            res.status(400).send({ code: 400, status: 'Bad Request', message: err.message })
          } else {
            // req.session.userId = user._id

            var userData = { userId: user._id, name: user.name, lastname: user.lastname, email: user.email, role: user.role }

            req.session.userData = userData

            res.redirect('/dashboard')
          }
        })
  },
</code></pre>



<p>And we have a default <code>get</code> route.</p>



<ul class="wp-block-list"><li>/favicon.ico<br></li></ul>



<p>Browsers will by default try to request /favicon.ico from the root of a hostname, in order to show an icon in the browser tab. As we dont use favicon so far we must avoid that these requests returning a 404 (Not Found). Here The /favicon.ico request will be catched and send a 204 No Content status.</p>



<pre class="wp-block-code"><code>// secserver.js
....
app.get('/favicon.ico', function(req, res) {
    console.log(req.url);
    res.status(204).json({status: 'no favicon'});
});
....
</code></pre>



<h3 class="wp-block-heading">3. Express App (Pug Template Version)</h3>



<p>From a functional point of view this app is pretty much the same app then the HTML version. The difference is that we use PUG templates instead of HTML in each res.send().</p>



<h4 class="wp-block-heading">Setup a seperate Database</h4>



<p>For the PUG version of my app I set up a new database to manage the users and the sessions.</p>



<pre class="wp-block-code"><code>#&gt; mongo

MongoDB shell version v4.2.3
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&amp;gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("b3e7f48a-a05c-4894-87db-996cb34eb1fb") }
MongoDB server version: 4.2.3

&gt; db
test

&gt; use admin
switched to db admin

&gt; db
admin

&gt; db.auth("adminUser", "adminpassword")
1

&gt; show dbs
admin               0.000GB
config              0.000GB
express-security    0.000GB
local               0.000GB

&gt; use express-security-pug
switched to db express-security-pug

&gt; db.createUser({ user: "owner_express-security-pug", pwd: "passowrd", roles: &#91;{ role: "dbOwner", db: "express-security-pug" }] })

Successfully added user: {
    "user" : "owner_express-security-pug",
    "roles" : &#91;
        {
            "role" : "dbOwner",
            "db" : "express-security-pug"
        }
    ]
}

&gt; db
express-security-pug

&gt; exit
</code></pre>



<p>Connection string to connect to express-security-pug db using the owner_express-security-pug user.</p>



<pre class="wp-block-code"><code>mongodb://owner_express-security-pug:password@localhost/express-security-pug
</code></pre>



<h4 class="wp-block-heading">Download the code from GitHub</h4>



<p>Pls. <a href="https://github.com/prottlaender/node-part-5-express-security-with-db-pug">go to my GitHub site</a> and clone the code. Here you find some inline documentation in the code.</p>



<h4 class="wp-block-heading">Create your app home directory express-security-pug</h4>



<p>My app home directory is different to the one that is available after you cloned the code from GitHub. </p>



<pre class="wp-block-code"><code>Patricks-MBP:2020-05-15-express-security patrick$ pwd
/Users/patrick/software/dev/node/articles/2020-05-15-express-security

Patricks-MBP:2020-05-15-express-security patrick$ mv node-part-5-express-security-with-db-pug express-security-pug

Patricks-MBP:2020-05-15-express-security patrick$ cd express-security-pug

Patricks-MBP:express-security-pug patrick$ pwd
/Users/patrick/software/dev/node/articles/2020-05-15-express-security/express-security-pug
</code></pre>



<h4 class="wp-block-heading">Install PUG and use it in your app</h4>



<p>First we install <a href="https://pugjs.org/api/getting-started.html">PUG</a> as a dependency. </p>



<pre class="wp-block-code"><code>Patricks-MBP:express-security-pug patrick$ pwd
/Users/patrick/software/dev/node/articles/2020-05-15-express-security/express-security-pug

Patricks-MBP:express-security-pug patrick$ npm install pug --save
</code></pre>



<p><a href="https://pugjs.org/api/getting-started.html">PUG</a> is already <a href="https://pugjs.org/api/express.html">fully integrated</a> into Express. Pls. read the documentation <a href="https://expressjs.com/en/guide/using-template-engines.html">how to use template engines in Express</a>. </p>



<p>After you installed PUG the view engine must be set in your main application file <em>secserverpug.js</em>.</p>



<pre class="wp-block-code"><code>// secserverpug.js
....
// use Pug Template Engine
app.set('view engine', 'pug')
app.set('views', './views')
....
</code></pre>



<p>These instructions tell your app that PUG template engine is used and that the templates can be found in <code>/views</code> directory.</p>



<h4 class="wp-block-heading">PUG Directory setup</h4>



<p>In <code>/views</code> I setup the templates for home, login, registration and an error template. </p>



<p>In <code>/views/includes</code> I setup the files containing HTML or JavaScript. These can be included in the templates. </p>



<pre class="wp-block-code"><code>Patricks-MBP:express-security-pug patrick$ ls -l
total 128
-rw-r--r--    1 patrick  staff    771  1 Jul 06:04 README.md
drwxr-xr-x    5 patrick  staff    160 29 Jun 05:26 database
drwxr-xr-x  150 patrick  staff   4800 29 Jun 06:11 node_modules
-rw-r--r--    1 patrick  staff  47547 29 Jun 06:11 package-lock.json
-rw-r--r--    1 patrick  staff    367 29 Jun 06:11 package.json
-rw-r--r--    1 patrick  staff   4393  2 Jul 05:34 secserverpug.js
drwxr-xr-x    3 patrick  staff     96 29 Jun 05:26 static
drwxr-xr-x    8 patrick  staff    256  2 Jul 05:45 views

Patricks-MBP:express-security-pug patrick$ ls -l views
total 40
-rw-r--r--  1 patrick  staff   549 30 Jun 05:35 dashboard.pug
-rw-r--r--  1 patrick  staff   522  2 Jul 05:50 err.pug
-rw-r--r--  1 patrick  staff   420 29 Jun 05:39 home.pug
drwxr-xr-x  6 patrick  staff   192 29 Jun 05:17 includes
-rw-r--r--  1 patrick  staff   735 30 Jun 05:02 login.pug
-rw-r--r--  1 patrick  staff  1067 30 Jun 05:08 register.pug

Patricks-MBP:express-security-pug patrick$ ls -l views/includes
total 32
-rw-r--r--  1 patrick  staff   76 29 Jun 05:39 foot.pug
-rw-r--r--  1 patrick  staff  167 29 Jun 05:24 head.pug
-rw-r--r--  1 patrick  staff  489  2 Jul 05:13 nav.pug
-rw-r--r--  1 patrick  staff  420 29 Jun 05:08 script.js

Patricks-MBP:express-security-pug patrick$ 
</code></pre>



<h4 class="wp-block-heading">The responsive Website Design</h4>



<p>Each site like <em>home</em>, <em>login</em>, <em>register</em> and <em>dashboard</em> has a <strong>specific site template</strong> in <code>/views</code> directory. The site content will be defined in the <em>main section</em> of each template. PUG enables files with HTML or JavaScript to be included. This makes the <em>site templates</em> clear and easy to maintain. The <strong>includes</strong> are located in <code>/views/includes</code> directory.</p>



<p>The website is build based on a grid design and each site template has the following structure.</p>



<pre class="wp-block-code"><code>doctype html

HTML

	Head
		include includes/head.pug	
	
	Body
		
		Grid-Container
			
				Header
					include includes/nav.pug
			
				Main
					... site template specific HTML ...
			
				Footer
					include includes/foot.pug
					
		&lt;script&gt;
			  include includes/script.js

</code></pre>



<p>The design of the website is defined in the css in <code>static/css/style.css</code>.</p>



<p>Here in the css we define the <strong>Site Structure</strong> as <em>grid areas</em> consisting of header, main and footer and link them to the <em>grid-container</em>. </p>



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

.header { grid-area: header; background-color: #ffffff; border-radius: 5px;}
.main { grid-area: main; background-color: #ffffff; border-radius: 5px;}
.footer { grid-area: footer; background-color: #ffffff; border-radius: 5px;}

.grid-container {
  display: grid;
  grid-template-areas:
      "header"
      "main"
      "footer";
  grid-gap: 5px;
  background-color: #d1d1e0;
  padding: 50px;
}

....
</code></pre>



<p>The <strong>Navigation</strong> is defined in the Header area of the Grid-Container and the HTML comes into the template via <code>include includes/nav.pug</code>.</p>



<pre class="wp-block-code"><code>// includes/nav.pug

//(this) refers to the DOM element to which the onclick attribute belongs to
// the a DOM element will be given as parameter to the function

a(class="burgericon" onclick="myFunction(this)")
  div(class='burgerline' id='bar1')
  div(class='burgerline' id='bar2')
  div(class='burgerline' id='bar3')

a(class='link' href='/') Home
a(class='link' href='/login') Login
a(class='link' href='/register') Register
a(class='link' href='/dashboard') Dashboard
a(class='link' href='/logout') Logout
</code></pre>



<p>So the navigation design is then defined in the css. Each navigation object is an <code>a</code> link. We have <code>a</code> links with class <code>link</code> and <code>burgericon</code>. The burgericon is used to open the navigation bar onclick when the screen is smaller than 600px (like iphone displays etc., explained below), is cosisting of 3 burgerlines and these lines are created using 3 div objects with class <code>burgerline</code>. The burgelines will be transformed with speed 0.4s when you click on the burgericon (explained below). The burgericon is not visible and aligned on the right edge. All other navigation links are visible and aligned on the left edge.<br></p>



<pre class="wp-block-code"><code>/* static/css/style.css */
....

/* style the navigation links with float on the left (side by side) */
.header a.link {
  float: left;
  display: block;
  padding: 14px 16px;
  text-decoration: none;
  font-size: 1.4vw;
  color: #28283e;
}

/* hover effect for each navigation link */
.header a.link:hover {
  background-color: #28283e;
  color: #ffffff;
}

/* style the burgericon link on the right */
.header a.burgericon {
  float: right;
  display: none;
  padding: 14px 16px;
}

/* style each burgerline that create the burgericon */
.burgerline {
  width: 35px;
  height: 5px;
  background-color: #28283e;
  margin: 6px 0;
  transition: 0.4s;
}
....
</code></pre>



<p>When the display screen is lower than 600px the navigation links will not be shown and the burgericon (on the right side) will be faded in instead. </p>



<pre class="wp-block-code"><code>/* static/css/style.css */
....
/* for screens up to 600px remove the navigation links and show the burgericon instead */
@media screen and (max-width: 600px) {
  .header a.link { display: none; }
  .header a.burgericon { display: block; }
}
....
</code></pre>



<p>When you click on the burgericon the burgerlines will be transformed so that you will see a cross instead of the hamburger like icon. The 2nd burgerline with <code>id='bar2'</code> will not be shown at all while the other 2 burgelines will be rotated 45 degrees counterclockwise (burgerline with <code>id='bar1'</code>) and clockwise (burgerline with <code>id='bar1'</code>). </p>



<pre class="wp-block-code"><code>/* static/css/style.css */
....
/* style burgerlines after on onclick event */
/* the .change class will be added onclick with classList.toggle in the JavaScript */
/* rotate first bar */
.change #bar1 {
  /* rotate -45 degrees (counterclockwise) move 15px down in Y-direction */
  transform: rotate(-45deg) translateY(15px);
}
/* fade out the second bar */
.change #bar2 {
  opacity: 0;
}
/* rotate third bar */
.change #bar3 {
  /* rotate +45 degrees (clockwise) move 15px up in Y-direction */
  transform: rotate(45deg) translateY(-15px);
}
....
</code></pre>



<p>After clicking on the burgericon, the burgerlines are transformed as described. The links of the navigation menu are displayed one below the other (float none) and aligned left.</p>



<pre class="wp-block-code"><code>/* static/css/style.css */
....
/* for screens up to 600px and after onclick event the responsive class will be added to the header */
@media screen and (max-width: 600px) {
  /* show navigation links left with no float (links shown among themselves) */
  .header.responsive a.link {
    float: none;
    display: block;
    text-align: left;
  }
}
....
</code></pre>



<p>All onclick functionalities are controlled by the javascript which is embedded in the HTML of each site template (pls see above includes/nav.pug). In the HTML, the onclick event is initiated in the burgericon link and the function myFunction is called with <code>onclick =" myFunction(this) "</code>. With the parameter <em>this</em> the entire burgericon object is transferred to the javascript function.</p>



<p>With each click on the burger icon, the class <code>change</code> is added to each burgerline or, if available, removed. This is done by the toggle() function. If <code>change</code> is set, the hamburg icon is transformed into a cross according to the specification in the css (see above). If <code>change</code> is withdrawn with a new click, the hamburger icon is displayed again.</p>



<p>But it happens even more in the javascript when you click on the hamburger icon. The element that has the id <code>responsivenav</code> is searched for and the variable<code>reponsiveNavElement</code> is assigned to this element. Is the class of the reponsiveNavElement <code>header</code> the class<code>responsive</code> is added after clicking on the hamburger icon. If the class <code>responsive</code> is set, as described above, the links of the navigation menu are displayed one below the other (float none) and aligned left. So it applies in the css <code>.header.responsive a.link {....}</code></p>



<p>In all other cases only the class <code>header</code> is set. So it applies in the css <code>.header a.link {....}</code> and the navigation links are not shown. </p>



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

// the (burgerlines) parameter represent the DOM element that has been given to the function
function myFunction(burgerlines) {
  burgerlines.classList.toggle('change');

  var reponsiveNavElement = document.getElementById('responsivenav');
    if (reponsiveNavElement.className === 'header') {
      reponsiveNavElement.classList.add('responsive')
    } else {
      reponsiveNavElement.className = 'header';
    }
  }

</code></pre>



<p>Finally at the end of the css we define the defaults for <em>h1</em>, for our text content, the forms, the input fields and the send buttons.<br></p>



<h3 class="wp-block-heading">Summary and Outlook</h3>



<p>In this part 4 of my little node.js series we have seen how to setup a production ready environment for our express app. I showed this using a Mac OS, but in principle this setup also applies to Linux, for example.</p>



<p>The basic setup is, to put it simply, the app runs as a service on the server in the background using a Process Manager, but has no interface to the client. This client interface regulates a reverse proxy which is upstream of the app and accepts all requests and forwards them to the app, as well as the responses from the app back to the client. The communication is SSL/TLS secured.</p>



<p>At the center of the setup is a separate local MongoDB that manages all application data. In our example, these are the users, but also the sessions. I prefer to set up my own MongoDB on my server but of course it is conceivable to use a cloud-based solution or to install another database locally.</p>



<p>The express app itself uses secure HTTP response headers so that HTTP attacks like <em>clickjacking</em>, <em>MIME type sniffing</em> or some <em>smaller XSS attacks</em> on the client are made as difficult as possible. Access to personal areas of the application is secured by session-based authentication and authorization. The session-relevant data is stored in the database and not in the browser cookie, which means additional security with regard to attacks on the client. The browser cookie only contains a hash of the session ID to query the relevant user data from the database.</p>



<p>I would like to end my node.js series with Part 4. I discussed and demonstrated the basic concepts and procedures in parts 1 to 4. Of course there will be other interesting articles on the topic node.js and web programming on <a href="https://digitaldocblog.com">Digitaldocblog</a>. Just take a look.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
