2022-04-08 18:09:52 +02:00
var http = require ( 'http' ) ;
var fs = require ( 'fs' ) ;
var path = require ( 'path' ) ;
2022-04-08 20:46:36 +02:00
var querystring = require ( 'querystring' ) ;
var crypto = require ( 'crypto' ) ;
2022-04-08 21:00:17 +02:00
var toml = require ( 'toml' ) ;
2022-04-08 20:46:36 +02:00
2022-04-08 21:00:17 +02:00
// Parse config file
const config = toml . parse ( fs . readFileSync ( 'config.toml' , 'utf-8' ) ) ;
2022-04-08 18:09:52 +02:00
2022-04-08 20:46:36 +02:00
// Just a few mime types
2022-04-08 18:09:52 +02:00
const contentTypes = {
'.html' : 'text/html' ,
'.css' : 'text/css' ,
'.js' : 'application/javascript' ,
'.jpg' : 'image/jpeg' ,
'.jpeg' : 'image/jpeg' ,
'.png' : 'image/png' ,
'.webp' : 'image/webp' ,
'.bmp' : 'image/bmp' ,
'.svg' : 'image/svg+xml' ,
'.ico' : 'image/x-icon' ,
'.mp4' : 'video/mp4' ,
'.webm' : 'video/webm' ,
} ;
2022-04-08 20:46:36 +02:00
sessions = [ ] ;
//! Will create a session and return it's id.
function createSession ( ) {
const timestamp = Date . now ( ) ;
const sessionId = SHA512Digest ( timestamp . toString ( ) + Math . floor ( Math . random ( ) * 100000 ) . toString ( ) ) ;
sessions . push ( {
'sessionId' : sessionId ,
'timestamp' : timestamp
} ) ;
return sessionId ;
}
//! Will check if a session of a given id exsists, and if it has been expired.
//! Will removed if expired.
//! Will also renew a sessions timestamp
function isSessisionValid ( id ) {
// Get an array of all sessions matching this id (should be 1 or 0)
var filteredSessions = sessions . filter ( ( value , index , array ) => {
return value . sessionId === id ;
} ) ;
// Quick-reject: No session of this id
if ( filteredSessions . length === 0 ) {
console . log ( 'No session of this id...' ) ;
return false ;
}
// Else: fetch the session
var sessionById = filteredSessions [ 0 ] ;
// Is the session still valid?
2022-04-08 21:00:17 +02:00
if ( Date . now ( ) - sessionById . timestamp > config . SESSION _DURATION * 1000 ) {
2022-04-08 20:46:36 +02:00
console . log ( 'Session is no longer valid, because it expired... Removing it...' ) ;
// Remove the session from the list of sessions
const indexOfSession = sessions . indexOf ( sessionById ) ;
sessions . splice ( indexOfSession , 1 ) ;
return false ;
}
2022-04-08 18:09:52 +02:00
2022-04-08 20:46:36 +02:00
// Else: It must be valid. We should update its timestamp.
console . log ( 'Session is active. Bumping timestamp...' ) ;
sessionById . timestamp = Date . now ( ) ;
return true ;
}
//! Will decode cookies to an array
//! Source: https://stackoverflow.com/a/3409200
//! I know this fails if a cookie contains '='. Mine don't!
function parseCookies ( request ) {
const list = { } ;
const cookieHeader = request . headers ? . cookie ;
if ( ! cookieHeader ) return list ;
cookieHeader . split ( ` ; ` ) . forEach ( function ( cookie ) {
let [ name , ... rest ] = cookie . split ( ` = ` ) ;
name = name ? . trim ( ) ;
if ( ! name ) return ;
const value = rest . join ( ` = ` ) . trim ( ) ;
if ( ! value ) return ;
list [ name ] = decodeURIComponent ( value ) ;
} ) ;
return list ;
}
2022-04-08 21:01:43 +02:00
//! Duh?
2022-04-08 20:46:36 +02:00
function SHA512Digest ( string ) {
return crypto . createHash ( 'sha512' ) . update ( string , 'utf-8' ) . digest ( 'hex' ) ;
}
2022-04-08 21:01:43 +02:00
//! This function simply serves the authentication page
2022-04-08 20:46:36 +02:00
function serveAuthenticatePage ( request , response ) {
fs . readFile ( _ _dirname + '/authenticate.html' , function ( error , data ) {
if ( ! error ) {
response . writeHead ( 200 , {
'Content-Type' : 'text/html'
} ) ;
response . end ( data ) ;
return ;
} else {
response . writeHead ( 500 , {
'Content-Type' : 'text/html'
} ) ;
console . error ( 'Unable to read authentication html file: ' + JSON . stringify ( error ) ) ;
response . end ( 'Internal server error.' ) ;
return ;
}
} ) ;
}
2022-04-08 21:01:43 +02:00
//! This function will handle the api--authenticate call, checks if the users password
//! is valid, and if yes, creates a new session and sets the session cookie.
2022-04-08 20:46:36 +02:00
function testAuthentication ( request , response ) {
// Wait for the request to have been received completely (including request body)
console . log ( 'Request is trying to authenticate... Waiting for request body...' ) ;
response . writeHead ( 200 , {
'Content-Type' : 'text/html'
} ) ;
// Collect post data (request body)
var requestBody = '' ;
request . on ( 'data' , function ( data ) {
requestBody += data . toString ( ) ;
} ) ;
// Process post data
request . on ( 'end' , function ( ) {
console . log ( 'Received complete request body.' ) ;
// Extract password from the request and hash it
const postData = querystring . parse ( requestBody ) ;
const password = postData [ 'password' ] ;
const passwordHash = SHA512Digest ( password ) ;
// Is the password good?
2022-04-08 21:00:17 +02:00
if ( passwordHash === config . PASSWD _HASH ) {
2022-04-08 20:46:36 +02:00
// Yes, it is:
// Create session
const sessionId = createSession ( ) ;
2022-04-08 21:31:18 +02:00
response . writeHead ( 303 , {
2022-04-08 20:46:36 +02:00
'Content-Type' : 'text/html' ,
2022-04-08 21:31:18 +02:00
'Set-Cookie' : 'sesid=' + sessionId ,
'Location' : request . headers . referer
2022-04-08 20:46:36 +02:00
} ) ;
response . end ( 'Access granted! You\'re in!' ) ;
return ;
} else {
response . writeHead ( 401 , {
'Content-Type' : 'text/html'
} ) ;
2022-04-08 21:00:17 +02:00
response . end ( 'WOOP! WOOP! Invalid password!<br><br>Need to reset your password? Replace the password hash in config.yaml with a new one.<br>This password hashes to: <em>' + passwordHash + '</em>.' ) ;
2022-04-08 20:46:36 +02:00
return ;
}
return ;
} ) ;
}
2022-04-08 21:01:43 +02:00
//! This function just serves files as they are...
2022-04-08 20:46:36 +02:00
function serverStaticFiles ( request , response ) {
2022-04-08 18:09:52 +02:00
// Fetch requested file
fs . readFile ( _ _dirname + request . url , function ( error , data ) {
if ( ! error ) {
const mimetype = path . extname ( request . url ) ;
if ( ! ( typeof mimetype === 'undefined' ) ) {
response . writeHead ( 200 , {
'Content-Type' : mimetype
} ) ;
response . end ( data ) ;
return ;
} else {
response . writeHead ( 500 , {
'Content-Type' : 'text/html'
} ) ;
console . error ( 'Unknown file mime type for file: ' + _ _dirname + request . url ) ;
response . end ( 'Unknown file mime type.' ) ;
return ;
}
} else {
response . writeHead ( 404 , {
'Content-Type' : 'text/html'
} ) ;
console . error ( 'File not found: ' + JSON . stringify ( error ) ) ;
response . end ( 'File not found.' ) ;
return ;
}
} ) ;
2022-04-08 20:46:36 +02:00
}
var server = http . createServer ( function ( request , response ) {
// Handle requests here...
2022-04-08 21:31:18 +02:00
console . log ( request . headers . referer ) ;
2022-04-08 20:46:36 +02:00
// If request is trying to authenticate
if ( request . url == '/api--authenticate' ) {
testAuthentication ( request , response ) ;
return ;
}
else /* Request is not trying to authenticate */ {
// Parse request cookies
const cookies = parseCookies ( request ) ;
// Check if the user is authenticated
if ( ( cookies . hasOwnProperty ( 'sesid' ) ) && ( isSessisionValid ( cookies [ 'sesid' ] ) ) ) {
// Session is authenticated. File access is granted.
serverStaticFiles ( request , response ) ;
return ;
} else /* Session is not authenticated */ {
serveAuthenticatePage ( request , response ) ;
return ;
}
}
2022-04-08 18:09:52 +02:00
} ) ;
const port = 80 ;
server . listen ( port ) ;
console . log ( 'Node.js sellery server running and listening to port ' + port ) ;