diff --git a/editions/multiwikiserver/tiddlywiki.info b/editions/multiwikiserver/tiddlywiki.info index 1c9a935b9..94a6717e6 100644 --- a/editions/multiwikiserver/tiddlywiki.info +++ b/editions/multiwikiserver/tiddlywiki.info @@ -4,8 +4,7 @@ "tiddlywiki/tiddlyweb", "tiddlywiki/filesystem", "tiddlywiki/multiwikiclient", - "tiddlywiki/multiwikiserver", - "tiddlywiki/authentication" + "tiddlywiki/multiwikiserver" ], "themes": [ "tiddlywiki/vanilla", diff --git a/plugins/tiddlywiki/authentication/plugin.info b/plugins/tiddlywiki/authentication/plugin.info deleted file mode 100644 index 241370a36..000000000 --- a/plugins/tiddlywiki/authentication/plugin.info +++ /dev/null @@ -1,9 +0,0 @@ -{ - "title": "$:/plugins/tiddlywiki/authentication", - "description": "Authentication plugin for TiddlyWiki", - "author": "Anon", - "version": "0.1.0", - "core-version": ">=5.0.0", - "plugin-type": "plugin", - "list": ["login"] -} \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/auth/form/login.tid b/plugins/tiddlywiki/multiwikiserver/auth/form/login.tid new file mode 100644 index 000000000..a1923b8ca --- /dev/null +++ b/plugins/tiddlywiki/multiwikiserver/auth/form/login.tid @@ -0,0 +1,19 @@ +title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login +tags: $:/tags/ServerRoute +route-method: GET +route-path: /login + +<$transclude tiddler="$:/plugins/tiddlywiki/multiwikiserver/auth/form/login/styles"/> + + + + <$transclude tiddler="$:/plugins/tiddlywiki/multiwikiserver/auth/form/login/head"/> + + +
+ <$transclude tiddler="$:/plugins/tiddlywiki/multiwikiserver/auth/form/login/header" mode="block"/> + <$transclude tiddler="$:/plugins/tiddlywiki/multiwikiserver/auth/form/login/form" mode="block"/> + <$transclude tiddler="$:/plugins/tiddlywiki/multiwikiserver/auth/form/login/error-message" mode="block"/> +
+ + \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/auth/form/login/error-message.tid b/plugins/tiddlywiki/multiwikiserver/auth/form/login/error-message.tid new file mode 100644 index 000000000..372e7c8f1 --- /dev/null +++ b/plugins/tiddlywiki/multiwikiserver/auth/form/login/error-message.tid @@ -0,0 +1,7 @@ +title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login/error-message + +<$list filter="[[$:/temp/mws/login/error]!is[missing]]" variable="errorTiddler"> +
+ {{$:/temp/mws/login/error}} +
+ \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/auth/form/login/form.tid b/plugins/tiddlywiki/multiwikiserver/auth/form/login/form.tid new file mode 100644 index 000000000..93d6ca773 --- /dev/null +++ b/plugins/tiddlywiki/multiwikiserver/auth/form/login/form.tid @@ -0,0 +1,10 @@ +title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login/form + + +<$macrocall $name="loginForm"/> +
+ >/> + + + +
diff --git a/plugins/tiddlywiki/multiwikiserver/auth/form/login/head.tid b/plugins/tiddlywiki/multiwikiserver/auth/form/login/head.tid new file mode 100644 index 000000000..b5df2aa5a --- /dev/null +++ b/plugins/tiddlywiki/multiwikiserver/auth/form/login/head.tid @@ -0,0 +1,3 @@ +title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login/head + +TiddlyWiki Login \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/auth/form/login/header.tid b/plugins/tiddlywiki/multiwikiserver/auth/form/login/header.tid new file mode 100644 index 000000000..d6e75df8e --- /dev/null +++ b/plugins/tiddlywiki/multiwikiserver/auth/form/login/header.tid @@ -0,0 +1,3 @@ +title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login/header + +

TiddlyWiki Login

\ No newline at end of file diff --git a/plugins/tiddlywiki/authentication/tiddlers/login.tid b/plugins/tiddlywiki/multiwikiserver/auth/form/login/styles.tid similarity index 50% rename from plugins/tiddlywiki/authentication/tiddlers/login.tid rename to plugins/tiddlywiki/multiwikiserver/auth/form/login/styles.tid index 5294a7e0f..1d633c459 100644 --- a/plugins/tiddlywiki/authentication/tiddlers/login.tid +++ b/plugins/tiddlywiki/multiwikiserver/auth/form/login/styles.tid @@ -1,19 +1,5 @@ -title: $:/plugins/tiddlywiki/authentication/login -tags: $:/tags/ServerRoute -route-method: GET -route-path: /login +title: $:/plugins/tiddlywiki/multiwikiserver/auth/form/login/styles -\define loginForm() -
- >/> - - - -
-\end - - - - - -
-

TiddlyWiki Login

- <$set name="returnUrl" value={{{ [{$:/temp/mws/login/returnUrl}!is[blank]else{$:/info/url/query}split[returnUrl=]last[]else[/]] }}}> - <> - - <$list filter="[[$:/temp/mws/login/error]!is[missing]]" variable="errorTiddler"> -
- {{$:/temp/mws/login/error}} -
- -
- - \ No newline at end of file + \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js b/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js index e4ffd05b7..922da7c2c 100644 --- a/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js +++ b/plugins/tiddlywiki/multiwikiserver/modules/mws-server.js @@ -77,8 +77,6 @@ function Server(options) { $tw.modules.forEachModuleOfType("mws-route", function(title,routeDefinition) { self.addRoute(routeDefinition); }); - // Load tiddler-based routes - self.loadAuthRoutes(); // Initialise the http vs https this.listenOptions = null; this.protocol = "http"; @@ -304,88 +302,6 @@ Server.prototype.addRoute = function(route) { this.routes.push(route); }; -Server.prototype.loadAuthRoutes = function () { - var self = this; - // add the login page route - self.addRoute({ - method: "GET", - path: /^\/login$/, - handler: function (request, response, state) { - // Check if the user already has a valid session - const authenticatedUser = self.authenticateUser(request, response); - if (authenticatedUser) { - // User is already logged in, redirect to home page - response.writeHead(302, { 'Location': '/' }); - response.end(); - return; - } - var loginTiddler = self.wiki.getTiddler("$:/plugins/tiddlywiki/authentication/login"); - if (loginTiddler) { - var text = self.wiki.renderTiddler("text/html", loginTiddler.fields.title); - response.writeHead(200, { "Content-Type": "text/html" }); - response.end(text); - } else { - response.writeHead(404); - response.end("Login page not found"); - } - } - }); - // add the login submission handler route - self.addRoute({ - method: "POST", - path: /^\/login$/, - csrfDisable: true, - handler: function(request, response, state) { - self.handleLogin(request, response, state); - }.bind(self) - }); - self.addRoute({ - method: "POST", - path: /^\/logout$/, - csrfDisable: true, - handler: function(request, response, state) { - self.handleLogout(request, response, state); - }.bind(self) - }); -}; - -Server.prototype.handleLogout = function (request, response, state) { - var self = this; - if (state.authenticatedUser) { - self.sqlTiddlerDatabase.deleteSession(state.authenticatedUser.sessionId); - } - response.setHeader('Set-Cookie', 'session=; HttpOnly; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT'); - response.writeHead(302, { 'Location': '/login' }); - response.end(); -} - -Server.prototype.handleLogin = function (request, response, state) { - var self = this; - const querystring = require('querystring'); - const formData = querystring.parse(state.data); - const { username, password } = formData; - const user = self.sqlTiddlerDatabase.getUserByUsername(username); - const isPasswordValid = self.verifyPassword(password, user?.password) - - if (user && isPasswordValid) { - const sessionId = self.createSession(user.user_id); - const {returnUrl} = this.parseCookieString(request.headers.cookie) - response.setHeader('Set-Cookie', `session=${sessionId}; HttpOnly; Path=/`); - response.writeHead(302, { - 'Location': returnUrl || '/' - }); - } else { - this.wiki.addTiddler(new $tw.Tiddler({ - title: "$:/temp/mws/login/error", - text: "Invalid username or password" - })); - response.writeHead(302, { - 'Location': '/login' - }); - } - response.end(); -}; - Server.prototype.verifyPassword = function(inputPassword, storedHash) { const hashedInput = this.hashPassword(inputPassword); return hashedInput === storedHash; diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-login.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-login.js new file mode 100644 index 000000000..dd0421a66 --- /dev/null +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-login.js @@ -0,0 +1,39 @@ +/*\ +title: $:/plugins/tiddlywiki/multiwikiserver/routes/handlers/get-login.js +type: application/javascript +module-type: mws-route + +GET /login + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "GET"; + +exports.path = /^\/login$/; + +exports.handler = function(request,response,state) { + // Check if the user already has a valid session + var authenticatedUser = state.server.authenticateUser(request, response); + if(authenticatedUser) { + // User is already logged in, redirect to home page + response.writeHead(302, { "Location": "/" }); + response.end(); + return; + } + var loginTiddler = $tw.mws.store.adminWiki.getTiddler("$:/plugins/tiddlywiki/multiwikiserver/auth/form/login"); + if(loginTiddler) { + var text = $tw.mws.store.adminWiki.renderTiddler("text/html", loginTiddler.fields.title); + response.writeHead(200, { "Content-Type": "text/html" }); + response.end(text); + } else { + response.writeHead(404); + response.end("Login page not found"); + } +}; + +}()); diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-users.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-users.js new file mode 100644 index 000000000..fea50ba19 --- /dev/null +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/get-users.js @@ -0,0 +1,54 @@ +/*\ +title: $:/plugins/tiddlywiki/multiwikiserver/routes/handlers/get-users.js +type: application/javascript +module-type: mws-route + +GET /admin/users + +\*/ +(function() { + + /*jslint node: true, browser: true */ + /*global $tw: false */ + "use strict"; + + exports.method = "GET"; + + exports.path = /^\/admin\/users$/; + + exports.handler = function(request,response,state) { + var userList = state.server.sqlTiddlerDatabase.listUsers(); + + // Ensure userList is an array + if (!Array.isArray(userList)) { + userList = []; + console.error("userList is not an array"); + } + + // Convert dates to strings and ensure all necessary fields are present + userList = userList.map(user => ({ + user_id: user.user_id || '', + username: user.username || '', + email: user.email || '', + created_at: user.created_at ? new Date(user.created_at).toISOString() : '', + last_login: user.last_login ? new Date(user.last_login).toISOString() : '' + })); + + console.log("Processed userList =>", userList); + + response.writeHead(200, "OK", { + "Content-Type": "text/html" + }); + + // Render the html + var html = $tw.mws.store.adminWiki.renderTiddler("text/plain","$:/plugins/tiddlywiki/multiwikiserver/templates/page",{ + variables: { + "page-content": "$:/plugins/tiddlywiki/multiwikiserver/templates/get-users", + "user-list": JSON.stringify(userList), + } + }); + response.write(html); + response.end(); + }; + + }()); \ No newline at end of file diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-login.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-login.js new file mode 100644 index 000000000..0df0dd003 --- /dev/null +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-login.js @@ -0,0 +1,53 @@ +/*\ +title: $:/plugins/tiddlywiki/multiwikiserver/routes/handlers/post-login.js +type: application/javascript +module-type: mws-route + +POST /login + +Parameters: + +username +password + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "POST"; + +exports.path = /^\/login$/; + +exports.bodyFormat = "www-form-urlencoded"; + +exports.csrfDisable = true; + +exports.handler = function(request,response,state) { + var username = state.data.username; + var password = state.data.password; + var user = state.server.sqlTiddlerDatabase.getUserByUsername(username); + var isPasswordValid = state.server.verifyPassword(password, user ? user.password : null) + + if(user && isPasswordValid) { + var sessionId = state.server.createSession(user.user_id); + var returnUrl = state.server.parseCookieString(request.headers.cookie).returnUrl + response.setHeader('Set-Cookie', `session=${sessionId}; HttpOnly; Path=/`); + response.writeHead(302, { + 'Location': returnUrl || '/' + }); + } else { + $tw.mws.store.adminWiki.addTiddler(new $tw.Tiddler({ + title: "$:/temp/mws/login/error", + text: "Invalid username or password" + })); + response.writeHead(302, { + 'Location': '/login' + }); + } + response.end(); +}; + +}()); diff --git a/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-logout.js b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-logout.js new file mode 100644 index 000000000..423ddf3bc --- /dev/null +++ b/plugins/tiddlywiki/multiwikiserver/modules/routes/handlers/post-logout.js @@ -0,0 +1,30 @@ +/*\ +title: $:/plugins/tiddlywiki/multiwikiserver/routes/handlers/post-logout.js +type: application/javascript +module-type: mws-route + +POST /logout + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "POST"; + +exports.path = /^\/logout$/; + +exports.csrfDisable = true; + +exports.handler = function(request,response,state) { + if(state.authenticatedUser) { + state.server.sqlTiddlerDatabase.deleteSession(state.authenticatedUser.sessionId); + } + response.setHeader("Set-Cookie", "session=; HttpOnly; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT"); + response.writeHead(302, { "Location": "/login" }); + response.end(); +}; + +}()); diff --git a/plugins/tiddlywiki/multiwikiserver/templates/get-users.tid b/plugins/tiddlywiki/multiwikiserver/templates/get-users.tid new file mode 100644 index 000000000..310312d61 --- /dev/null +++ b/plugins/tiddlywiki/multiwikiserver/templates/get-users.tid @@ -0,0 +1,89 @@ + + +\define lingo-base() $:/language/ControlPanel/Tools/ + +

User Management

+ + +

Debug Info

+

Raw user list JSON: <$text text=<>/>

+ + +<$set name="userList" value=<>> + + <$list filter="[!is[blank]]" emptyMessage="The user list is empty or not provided."> +

User list found: <$text text=<>/>

+ + + <$set name="parsedUserList" value={{{ [jsonparse[]] }}}/> + + + <$list filter="[count[]compare:number:gt[0]]" emptyMessage="No users found or failed to parse user data"> + +

Parsed User List (as JSON): <$text text={{{ [jsonstringify[]] }}}/>

+ + +

Total users: <$text text={{{ [count[]] }}}/>

+ + + + + + + + + + + + + + <$list filter="[jsonindexes[]]" variable="userIndex"> + <$let currentUser={{{ [jsonget] }}}> + + + + + + + + + + +
UsernameEmailCreated AtLast LoginActions
<$text text={{{ [jsonget[username]] }}}/><$text text={{{ [jsonget[email]] }}}/><$text text={{{ [jsonget[created_at]] }}}/><$text text={{{ [jsonget[last_login]] }}}/> + <$button message="tm-server-command" param="edit-user" user_id={{{ [jsonget[user_id]] }}} class="tc-btn-invisible"> + {{$:/core/images/edit-button}} Edit + + <$button message="tm-server-command" param="delete-user" user_id={{{ [jsonget[user_id]] }}} class="tc-btn-invisible"> + {{$:/core/images/delete-button}} Delete + +
+ + + + +<$button message="tm-modal" param="$:/plugins/tiddlywiki/multiwikiserver/templates/add-user-modal" class="tc-btn-big-green"> +Add New User + + +