From c05c0d3df66e587f35c5cd3eedcac432b1eed012 Mon Sep 17 00:00:00 2001 From: snlhnk Date: Wed, 18 Jul 2018 08:54:43 -0700 Subject: [PATCH] Module-ize server routes, add static file support and other enhancements(#2679) * Module-ize server routes and add static file support (#2510) * Refactor server routes to modules New module type: serverroute Caveats: Loading order is not deterministic but this would only matter if two route modules attempted to use the same path regexp (that would be silly). * Add static assets plugin This plugin allows the node server to fetch static assets in the /assets directory. I felt that this was a feature that goes above the core functionality. That is why I added it as a plugin. with the modular route extensions this was a breeze. * Add serverroute description to ModuleTypes * Coding standards tweaks * Fix filename typo * Move support for attachments from a plugin into the core * Missing "else" * Refactor server handling * Introduce a new named parameter scheme for commands * Move the SimpleServer class into it's own module * Deprecate the --server command because of the unwieldy syntax * Add a new --listen command using the new syntax For example: tiddlywiki mywiki --listen host:0.0.0.0 port:8090 * Add check for unknown parameters * Add support for multiple basic authentication credentials in a CSV file Beware: Passwords are stored in plain text. If that's a problem, use an authenticating proxy and the trusted header authentication approach. * Refactor module locations * Rename "serverroute" module type to "route" * Remove support for verifying optional named command parameters The idea was to be able to flag unknown parameter names, but requiring a command to pre-specify all the parameter names makes it harder for (say) the listen command to be extensible so that plugins can add new optional parameters that they handle. (This is particularly in the context of work in progress to encapsulate authenticators into their own modules). * Refactor the two authenticators into separate modules and add support for authorization * Correct mistaken path.join vs. path.resolve See https://stackoverflow.com/a/39836259 * Docs for the named command parameters I'd be grateful if anyone with sufficient Windows experience could confirm that the note about double quotes in "NamedCommandParameters" is correct. * Be consistent about lower case parameter names * Do the right thing when we have a username but no password With a username parameter but no password parameter we'll attribute edits to that username, but not require authentication. * Remove obsolete code * Add support for requiring authentication without restricting the username * Refactor authorization checks * Return read_only status in /status response * Fix two code typos * Add basic support for detecting readonly status and avoiding write errors We now have syncadaptors returning readonly status and avoid attempting to write to the server if it's going to fail * Add readonly-styles We hide editing-related buttons in read only mode I've made this part of the tiddlyweb plugin but I think a case could be made for putting it into the core. * Add custom request header as CSRF mitigation By default we require the header X-Requested-With to be set to TiddlyWiki. Can be overriden by setting csrfdisable to "yes" See https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Protecting_REST_Services:_Use_of_Custom_Request_Headers * Add support for HTTPS * First pass at a route for serving rendered tiddlers cc @Drakor * Tweaks to the single tiddler static view Adding a simple sidebar * Switch to "dash" separated parameter names * Typo * Docs: Update ServerCommand and ListenCommand * First pass at docs for the new web server stuff Writing the docs is turning out to be quite an undertaking, much harder than writing the code! * Get rid of extraneous paragraphs in static renderings * Rejig anonymous user handling Now we can support wikis that are read-only for anonymous access, but allow a user to login for read/write access. * More docs Slowly getting there... * Static tiddler rendering: Fix HTML content in page title * Docs updates * Fix server command parameter names Missed off 30ce7ea * Docs: Missing quotes * Avoid inadvertent dependency on Node.js > v9.6.0 The listenOptions parameter of the plain HTTP version of CreateServer was only introduced in v9.6.0 cc @Drakor @pmario * Typo --- core/language/en-GB/Docs/ModuleTypes.multids | 2 + core/language/en-GB/Help/listen.tid | 30 ++ core/language/en-GB/Help/server.tid | 21 +- core/modules/commander.js | 36 ++ core/modules/commands/listen.js | 48 +++ core/modules/commands/server.js | 308 ++---------------- core/modules/server/authenticators/basic.js | 94 ++++++ core/modules/server/authenticators/header.js | 47 +++ core/modules/server/routes/delete-tiddler.js | 28 ++ core/modules/server/routes/get-favicon.js | 25 ++ core/modules/server/routes/get-file.js | 50 +++ core/modules/server/routes/get-index.js | 25 ++ core/modules/server/routes/get-login-basic.js | 35 ++ core/modules/server/routes/get-status.js | 33 ++ .../modules/server/routes/get-tiddler-html.js | 42 +++ core/modules/server/routes/get-tiddler.js | 46 +++ .../server/routes/get-tiddlers-json.js | 37 +++ core/modules/server/routes/put-tiddler.js | 42 +++ core/modules/server/server.js | 252 ++++++++++++++ core/modules/syncer.js | 6 +- core/modules/utils/csv.js | 46 +++ core/modules/utils/dom/http.js | 3 + .../server/static.sidebar.wikitext.tid | 30 ++ core/templates/server/static.tiddler.html.tid | 29 ++ .../server/static.tiddler.wikitext.tid | 23 ++ .../tw5.com/tiddlers/commands/Commands.tid | 7 +- .../tiddlers/commands/ListenCommand.tid | 10 + .../commands/NamedCommandParameters.tid | 22 ++ .../tiddlers/commands/ServerCommand.tid | 8 +- ...ranslate TiddlyWiki into your language.tid | 4 +- .../tw5.com/tiddlers/features/LazyLoading.tid | 10 +- ...lling TiddlyWiki Prerelease on Node.js.tid | 4 +- .../Installing TiddlyWiki on Node.js.tid | 4 +- .../nodejs/Using TiddlyWiki on Node.js.tid | 12 +- .../saving/Example web.config for IIS.txt | 2 +- .../Example web.config for IIS.txt.meta | 2 +- ... Microsoft Internet Information Server.tid | 8 +- .../tiddlers/webserver/Using HTTPS.tid | 19 ++ ...sing the integrated static file server.tid | 16 + ...sing the read-only single tiddler view.tid | 7 + .../webserver/WebServer Anonymous Access.tid | 8 + .../webserver/WebServer Authentication.tid | 11 + .../webserver/WebServer Authorization.tid | 30 ++ .../WebServer Basic Authentication.tid | 11 + .../tiddlers/webserver/WebServer Guides.tid | 9 + .../WebServer Header Authentication.tid | 9 + .../WebServer Parameter_ anon-username.tid | 10 + ...r Parameter_ authenticated-user-header.tid | 8 + .../WebServer Parameter_ credentials.tid | 25 ++ .../WebServer Parameter_ csrf-disable.tid | 10 + .../WebServer Parameter_ debug-level.tid | 11 + .../webserver/WebServer Parameter_ host.tid | 12 + .../WebServer Parameter_ password.tid | 8 + .../WebServer Parameter_ path-prefix.tid | 14 + .../webserver/WebServer Parameter_ port.tid | 25 ++ .../WebServer Parameter_ readers.tid | 8 + .../WebServer Parameter_ root-render-type.tid | 12 + .../WebServer Parameter_ root-serve-type.tid | 8 + .../WebServer Parameter_ root-tiddler.tid | 8 + .../WebServer Parameter_ tls-cert.tid | 10 + .../WebServer Parameter_ tls-key.tid | 10 + .../WebServer Parameter_ username.tid | 26 ++ .../WebServer Parameter_ writers.tid | 8 + .../webserver/WebServer Parameters.tid | 9 + .../tiddlers/webserver/WebServer Routing.tid | 6 + .../tw5.com/tiddlers/webserver/WebServer.tid | 64 ++++ .../tiddlywiki/tiddlyweb/readonly-styles.tid | 27 ++ .../tiddlywiki/tiddlyweb/tiddlywebadaptor.js | 21 +- 68 files changed, 1555 insertions(+), 336 deletions(-) create mode 100644 core/language/en-GB/Help/listen.tid create mode 100644 core/modules/commands/listen.js create mode 100644 core/modules/server/authenticators/basic.js create mode 100644 core/modules/server/authenticators/header.js create mode 100644 core/modules/server/routes/delete-tiddler.js create mode 100644 core/modules/server/routes/get-favicon.js create mode 100644 core/modules/server/routes/get-file.js create mode 100644 core/modules/server/routes/get-index.js create mode 100644 core/modules/server/routes/get-login-basic.js create mode 100644 core/modules/server/routes/get-status.js create mode 100644 core/modules/server/routes/get-tiddler-html.js create mode 100644 core/modules/server/routes/get-tiddler.js create mode 100644 core/modules/server/routes/get-tiddlers-json.js create mode 100644 core/modules/server/routes/put-tiddler.js create mode 100644 core/modules/server/server.js create mode 100644 core/modules/utils/csv.js create mode 100644 core/templates/server/static.sidebar.wikitext.tid create mode 100644 core/templates/server/static.tiddler.html.tid create mode 100644 core/templates/server/static.tiddler.wikitext.tid create mode 100644 editions/tw5.com/tiddlers/commands/ListenCommand.tid create mode 100644 editions/tw5.com/tiddlers/commands/NamedCommandParameters.tid create mode 100644 editions/tw5.com/tiddlers/webserver/Using HTTPS.tid create mode 100644 editions/tw5.com/tiddlers/webserver/Using the integrated static file server.tid create mode 100644 editions/tw5.com/tiddlers/webserver/Using the read-only single tiddler view.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Anonymous Access.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Authentication.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Authorization.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Basic Authentication.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Guides.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Header Authentication.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ anon-username.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ authenticated-user-header.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ credentials.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ csrf-disable.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ debug-level.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ host.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ password.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ path-prefix.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ port.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ readers.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-render-type.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-serve-type.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-tiddler.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ tls-cert.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ tls-key.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ username.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameter_ writers.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Parameters.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer Routing.tid create mode 100644 editions/tw5.com/tiddlers/webserver/WebServer.tid create mode 100644 plugins/tiddlywiki/tiddlyweb/readonly-styles.tid diff --git a/core/language/en-GB/Docs/ModuleTypes.multids b/core/language/en-GB/Docs/ModuleTypes.multids index b58c85ad8..1e1abd424 100644 --- a/core/language/en-GB/Docs/ModuleTypes.multids +++ b/core/language/en-GB/Docs/ModuleTypes.multids @@ -2,6 +2,7 @@ title: $:/language/Docs/ModuleTypes/ allfilteroperator: A sub-operator for the ''all'' filter operator. animation: Animations that may be used with the RevealWidget. +authenticator: Defines how requests are authenticated by the built-in HTTP server. bitmapeditoroperation: A bitmap editor toolbar operation. command: Commands that can be executed under Node.js. config: Data to be inserted into `$tw.config`. @@ -12,6 +13,7 @@ isfilteroperator: Operands for the ''is'' filter operator. library: Generic module type for general purpose JavaScript modules. macro: JavaScript macro definitions. parser: Parsers for different content types. +route: Defines how individual URL patterns are handled by the built-in HTTP server. saver: Savers handle different methods for saving files from the browser. startup: Startup functions. storyview: Story views customise the animation and behaviour of list widgets. diff --git a/core/language/en-GB/Help/listen.tid b/core/language/en-GB/Help/listen.tid new file mode 100644 index 000000000..95c20af37 --- /dev/null +++ b/core/language/en-GB/Help/listen.tid @@ -0,0 +1,30 @@ +title: $:/language/Help/listen +description: Provides an HTTP server interface to TiddlyWiki + +Serves a wiki over HTTP. + +The listen command uses NamedCommandParameters: + +``` +--listen [=]... +``` + +All parameters are optional with safe defaults, and can be specified in any order. The recognised parameters are: + +* ''host'' - optional hostname to serve from (defaults to "127.0.0.1" aka "localhost") +* ''path-prefix'' - optional prefix for paths +* ''port'' - port number on which to listen; non-numeric values are interpreted as a system environment variable from which the port number is extracted (defaults to "8080") +* ''credentials'' - pathname of credentials CSV file (relative to wiki folder) +* ''anon-username'' - the username for signing edits for anonymous users +* ''username'' - optional username for basic authentication +* ''password'' - optional password for basic authentication +* ''authenticated-user-header'' - optional name of header to be used for trusted authentication +* ''readers'' - comma separated list of principals allowed to write to this wiki +* ''writers'' - comma separated list of principals allowed to read from this wiki +* ''csrf-disable'' - set to "yes" to disable CSRF checks (defaults to "no") +* ''root-tiddler'' - the tiddler to serve at the root (defaults to "$:/core/save/all") +* ''root-render-type'' - the content type to which the root tiddler should be rendered (defaults to "text/plain") +* ''root-serve-type'' - the content type with which the root tiddler should be served (defaults to "text/html") +* ''tls-cert'' - pathname of TLS certificate file (relative to wiki folder) +* ''tls-key'' - pathname of TLS key file (relative to wiki folder) +* ''debug-level'' - optional debug level; set to "debug" to view request details (defaults to "none") diff --git a/core/language/en-GB/Help/server.tid b/core/language/en-GB/Help/server.tid index 3f0adbe24..8c5f932b4 100644 --- a/core/language/en-GB/Help/server.tid +++ b/core/language/en-GB/Help/server.tid @@ -1,27 +1,25 @@ title: $:/language/Help/server -description: Provides an HTTP server interface to TiddlyWiki +description: Provides an HTTP server interface to TiddlyWiki (deprecated in favour of the new listen command) -The server built in to TiddlyWiki5 is very simple. Although compatible with TiddlyWeb it doesn't support many of the features needed for robust Internet-facing usage. - -At the root, it serves a rendering of a specified tiddler. Away from the root, it serves individual tiddlers encoded in JSON, and supports the basic HTTP operations for `GET`, `PUT` and `DELETE`. +Legacy command to serve a wiki over HTTP. ``` ---server +--server ``` The parameters are: * ''port'' - port number on which to listen; non-numeric values are interpreted as a system environment variable from which the port number is extracted (defaults to "8080") -* ''roottiddler'' - the tiddler to serve at the root (defaults to "$:/core/save/all") -* ''rendertype'' - the content type to which the root tiddler should be rendered (defaults to "text/plain") -* ''servetype'' - the content type with which the root tiddler should be served (defaults to "text/html") +* ''root-tiddler'' - the tiddler to serve at the root (defaults to "$:/core/save/all") +* ''root-render-type'' - the content type to which the root tiddler should be rendered (defaults to "text/plain") +* ''rooot-serve-type'' - the content type with which the root tiddler should be served (defaults to "text/html") * ''username'' - the default username for signing edits * ''password'' - optional password for basic authentication * ''host'' - optional hostname to serve from (defaults to "127.0.0.1" aka "localhost") -* ''pathprefix'' - optional prefix for paths -* ''debuglevel'' - optional debug level; set to "debug" to view request details (defaults to "none") +* ''path-prefix'' - optional prefix for paths +* ''debug-level'' - optional debug level; set to "debug" to view request details (defaults to "none") -If the password parameter is specified then the browser will prompt the user for the username and password. Note that the password is transmitted in plain text so this implementation isn't suitable for general use. +If the password parameter is specified then the browser will prompt the user for the username and password. Note that the password is transmitted in plain text so this implementation should only be used on a trusted network or over HTTPS. For example: @@ -37,7 +35,6 @@ The username and password can be specified as empty strings if you need to set t To run multiple TiddlyWiki servers at the same time you'll need to put each one on a different port. It can be useful to use an environment variable to pass the port number to the Node.js process. This example references an environment variable called "MY_PORT_NUMBER": - ``` --server MY_PORT_NUMBER $:/core/save/all text/plain text/html MyUserName passw0rd ``` diff --git a/core/modules/commander.js b/core/modules/commander.js index ad7f4b5b3..302af525b 100644 --- a/core/modules/commander.js +++ b/core/modules/commander.js @@ -94,6 +94,13 @@ Commander.prototype.executeNextCommand = function() { if(this.verbose) { this.streams.output.write("Executing command: " + commandName + " " + params.join(" ") + "\n"); } + // Parse named parameters if required + if(command.info.namedParameterMode) { + params = this.extractNamedParameters(params,command.info.mandatoryParameters); + if(typeof params === "string") { + return this.callback(params); + } + } if(command.info.synchronous) { // Synchronous command c = new command.Command(params,this); @@ -122,6 +129,35 @@ Commander.prototype.executeNextCommand = function() { } }; +/* +Given an array of parameter strings `params` in name:value format, and an array of mandatory parameter names in `mandatoryParameters`, returns a hashmap of values or a string if error +*/ +Commander.prototype.extractNamedParameters = function(params,mandatoryParameters) { + mandatoryParameters = mandatoryParameters || []; + var errors = [], + paramsByName = Object.create(null); + // Extract the parameters + $tw.utils.each(params,function(param) { + var index = param.indexOf("="); + if(index < 1) { + errors.push("malformed named parameter: '" + param + "'"); + } + paramsByName[param.slice(0,index)] = $tw.utils.trim(param.slice(index+1)); + }); + // Check the mandatory parameters are present + $tw.utils.each(mandatoryParameters,function(mandatoryParameter) { + if(!$tw.utils.hop(paramsByName,mandatoryParameter)) { + errors.push("missing mandatory parameter: '" + mandatoryParameter + "'"); + } + }); + // Return any errors + if(errors.length > 0) { + return errors.join(" and\n"); + } else { + return paramsByName; + } +}; + Commander.initCommands = function(moduleType) { moduleType = moduleType || "command"; $tw.commands = {}; diff --git a/core/modules/commands/listen.js b/core/modules/commands/listen.js new file mode 100644 index 000000000..dd22ea940 --- /dev/null +++ b/core/modules/commands/listen.js @@ -0,0 +1,48 @@ +/*\ +title: $:/core/modules/commands/listen.js +type: application/javascript +module-type: command + +Listen for HTTP requests and serve tiddlers + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var Server = require("$:/core/modules/server/server.js").Server; + +exports.info = { + name: "listen", + synchronous: true, + namedParameterMode: true, + mandatoryParameters: [], +}; + +var Command = function(params,commander,callback) { + var self = this; + this.params = params; + this.commander = commander; + this.callback = callback; +}; + +Command.prototype.execute = function() { + var self = this; + if(!$tw.boot.wikiTiddlersPath) { + $tw.utils.warning("Warning: Wiki folder '" + $tw.boot.wikiPath + "' does not exist or is missing a tiddlywiki.info file"); + } + // Set up server + this.server = new Server({ + wiki: this.commander.wiki, + variables: self.params + }); + var nodeServer = this.server.listen(); + $tw.hooks.invokeHook("th-server-command-post-start",this.server,nodeServer); + return null; +}; + +exports.Command = Command; + +})(); diff --git a/core/modules/commands/server.js b/core/modules/commands/server.js index 69cc9d2ab..fd1e4ea50 100644 --- a/core/modules/commands/server.js +++ b/core/modules/commands/server.js @@ -3,7 +3,7 @@ title: $:/core/modules/commands/server.js type: application/javascript module-type: command -Serve tiddlers over http +Deprecated legacy command for serving tiddlers \*/ (function(){ @@ -12,311 +12,41 @@ Serve tiddlers over http /*global $tw: false */ "use strict"; -if($tw.node) { - var util = require("util"), - fs = require("fs"), - url = require("url"), - path = require("path"), - http = require("http"); -} +var Server = require("$:/core/modules/server/server.js").Server; exports.info = { name: "server", synchronous: true }; -/* -A simple HTTP server with regexp-based routes -*/ -function SimpleServer(options) { - this.routes = options.routes || []; - this.wiki = options.wiki; - this.variables = options.variables || {}; -} - -SimpleServer.prototype.set = function(obj) { - var self = this; - $tw.utils.each(obj,function(value,name) { - self.variables[name] = value; - }); -}; - -SimpleServer.prototype.get = function(name) { - return this.variables[name]; -}; - -SimpleServer.prototype.addRoute = function(route) { - this.routes.push(route); -}; - -SimpleServer.prototype.findMatchingRoute = function(request,state) { - var pathprefix = this.get("pathprefix") || ""; - for(var t=0; t 0; +}; + +/* +Returns true if the request is authenticated and assigns the "authenticatedUsername" state variable. +Returns false if the request couldn't be authenticated having sent an appropriate response to the browser +*/ +BasicAuthenticator.prototype.authenticateRequest = function(request,response,state) { + // Extract the incoming username and password from the request + var header = request.headers.authorization || ""; + if(!header && state.allowAnon) { + // If there's no header and anonymous access is allowed then we don't set authenticatedUsername + return true; + } + var token = header.split(/\s+/).pop() || "", + auth = $tw.utils.base64Decode(token), + parts = auth.split(/:/), + incomingUsername = parts[0], + incomingPassword = parts[1]; + // Check that at least one of the credentials matches + var matchingCredentials = this.credentialsData.find(function(credential) { + return credential.username === incomingUsername && credential.password === incomingPassword; + }); + if(matchingCredentials) { + // If so, add the authenticated username to the request state + state.authenticatedUsername = incomingUsername; + return true; + } else { + // If not, return an authentication challenge + response.writeHead(401,"Authentication required",{ + "WWW-Authenticate": 'Basic realm="Please provide your username and password to login to ' + state.server.servername + '"' + }); + response.end(); + return false; + } +}; + +exports.AuthenticatorClass = BasicAuthenticator; + +})(); diff --git a/core/modules/server/authenticators/header.js b/core/modules/server/authenticators/header.js new file mode 100644 index 000000000..6d21cf023 --- /dev/null +++ b/core/modules/server/authenticators/header.js @@ -0,0 +1,47 @@ +/*\ +title: $:/core/modules/server/authenticators/header.js +type: application/javascript +module-type: authenticator + +Authenticator for trusted header authentication + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +function HeaderAuthenticator(server) { + this.server = server; + this.header = server.get("authenticated-user-header"); +} + +/* +Returns true if the authenticator is active, false if it is inactive, or a string if there is an error +*/ +HeaderAuthenticator.prototype.init = function() { + return !!this.header; +}; + +/* +Returns true if the request is authenticated and assigns the "authenticatedUsername" state variable. +Returns false if the request couldn't be authenticated having sent an appropriate response to the browser +*/ +HeaderAuthenticator.prototype.authenticateRequest = function(request,response,state) { + // Otherwise, authenticate as the username in the specified header + var username = request.headers[this.header]; + if(!username && !state.allowAnon) { + response.writeHead(401,"Authorization header required to login to '" + state.server.servername + "'"); + response.end(); + return false; + } else { + // authenticatedUsername will be undefined for anonymous users + state.authenticatedUsername = username; + return true; + } +}; + +exports.AuthenticatorClass = HeaderAuthenticator; + +})(); diff --git a/core/modules/server/routes/delete-tiddler.js b/core/modules/server/routes/delete-tiddler.js new file mode 100644 index 000000000..d04020c75 --- /dev/null +++ b/core/modules/server/routes/delete-tiddler.js @@ -0,0 +1,28 @@ +/*\ +title: $:/core/modules/server/routes/delete-tiddler.js +type: application/javascript +module-type: route + +DELETE /recipes/default/tiddlers/:title + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "DELETE"; + +exports.path = /^\/bags\/default\/tiddlers\/(.+)$/; + +exports.handler = function(request,response,state) { + var title = decodeURIComponent(state.params[0]); + state.wiki.deleteTiddler(title); + response.writeHead(204, "OK", { + "Content-Type": "text/plain" + }); + response.end(); +}; + +}()); diff --git a/core/modules/server/routes/get-favicon.js b/core/modules/server/routes/get-favicon.js new file mode 100644 index 000000000..79dd1a6be --- /dev/null +++ b/core/modules/server/routes/get-favicon.js @@ -0,0 +1,25 @@ +/*\ +title: $:/core/modules/server/routes/get-favicon.js +type: application/javascript +module-type: route + +GET /favicon.ico + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "GET"; + +exports.path = /^\/favicon.ico$/; + +exports.handler = function(request,response,state) { + response.writeHead(200, {"Content-Type": "image/x-icon"}); + var buffer = state.wiki.getTiddlerText("$:/favicon.ico",""); + response.end(buffer,"base64"); +}; + +}()); diff --git a/core/modules/server/routes/get-file.js b/core/modules/server/routes/get-file.js new file mode 100644 index 000000000..5bc4e09e1 --- /dev/null +++ b/core/modules/server/routes/get-file.js @@ -0,0 +1,50 @@ +/*\ +title: $:/core/modules/server/routes/get-file.js +type: application/javascript +module-type: route + +GET /files/:filepath + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "GET"; + +exports.path = /^\/files\/(.+)$/; + +exports.handler = function(request,response,state) { + var path = require("path"), + fs = require("fs"), + util = require("util"); + var filename = path.resolve($tw.boot.wikiPath,"files",decodeURIComponent(state.params[0])), + extension = path.extname(filename); + fs.readFile(filename,function(err,content) { + var status,content,type = "text/plain"; + if(err) { + if(err.code === "ENOENT") { + status = 404; + content = "File '" + filename + "' not found"; + } else if(err.code === "EACCES") { + status = 403; + content = "You do not have permission to access the file '" + filename + "'"; + } else { + status = 500; + content = err.toString(); + } + } else { + status = 200; + content = content; + type = $tw.config.fileExtensionInfo[extension] || "application/octet-stream"; + } + response.writeHead(status,{ + "Content-Type": type + }); + response.end(content); + }); +}; + +}()); diff --git a/core/modules/server/routes/get-index.js b/core/modules/server/routes/get-index.js new file mode 100644 index 000000000..603103c6b --- /dev/null +++ b/core/modules/server/routes/get-index.js @@ -0,0 +1,25 @@ +/*\ +title: $:/core/modules/server/routes/get-index.js +type: application/javascript +module-type: route + +GET / + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "GET"; + +exports.path = /^\/$/; + +exports.handler = function(request,response,state) { + response.writeHead(200, {"Content-Type": state.server.get("root-serve-type")}); + var text = state.wiki.renderTiddler(state.server.get("root-render-type"),state.server.get("root-tiddler")); + response.end(text,"utf8"); +}; + +}()); diff --git a/core/modules/server/routes/get-login-basic.js b/core/modules/server/routes/get-login-basic.js new file mode 100644 index 000000000..c3cb16eb6 --- /dev/null +++ b/core/modules/server/routes/get-login-basic.js @@ -0,0 +1,35 @@ +/*\ +title: $:/core/modules/server/routes/get-login-basic.js +type: application/javascript +module-type: route + +GET /login-basic -- force a Basic Authentication challenge + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "GET"; + +exports.path = /^\/login-basic$/; + +exports.handler = function(request,response,state) { + if(!state.authenticatedUsername) { + // Challenge if there's no username + response.writeHead(401,{ + "WWW-Authenticate": 'Basic realm="Please provide your username and password to login to ' + state.server.servername + '"' + }); + response.end(); + } else { + // Redirect to the root wiki if login worked + response.writeHead(302,{ + Location: "/" + }); + response.end(); + } +}; + +}()); diff --git a/core/modules/server/routes/get-status.js b/core/modules/server/routes/get-status.js new file mode 100644 index 000000000..0da5cb70f --- /dev/null +++ b/core/modules/server/routes/get-status.js @@ -0,0 +1,33 @@ +/*\ +title: $:/core/modules/server/routes/get-status.js +type: application/javascript +module-type: route + +GET /status + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "GET"; + +exports.path = /^\/status$/; + +exports.handler = function(request,response,state) { + response.writeHead(200, {"Content-Type": "application/json"}); + var text = JSON.stringify({ + username: state.authenticatedUsername || state.server.get("anon-username") || "", + anonymous: !state.authenticatedUsername, + read_only: !state.server.isAuthorized("writers",state.authenticatedUsername), + space: { + recipe: "default" + }, + tiddlywiki_version: $tw.version + }); + response.end(text,"utf8"); +}; + +}()); diff --git a/core/modules/server/routes/get-tiddler-html.js b/core/modules/server/routes/get-tiddler-html.js new file mode 100644 index 000000000..6f85a1f18 --- /dev/null +++ b/core/modules/server/routes/get-tiddler-html.js @@ -0,0 +1,42 @@ +/*\ +title: $:/core/modules/server/routes/get-tiddler-html.js +type: application/javascript +module-type: route + +GET /:title + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "GET"; + +exports.path = /^\/([^\/]+)$/; + +exports.handler = function(request,response,state) { + var title = decodeURIComponent(state.params[0]), + tiddler = state.wiki.getTiddler(title); + if(tiddler) { + var renderType,template; + // Render ordinary tiddlers as HTML, and system tiddlers in plain text + if(state.wiki.isSystemTiddler(title)) { + renderType = state.server.get("system-tiddler-render-type"); + template = state.server.get("system-tiddler-template"); + } else { + renderType = state.server.get("tiddler-render-type"); + template = state.server.get("tiddler-template"); + } + var text = state.wiki.renderTiddler(renderType,template,{parseAsInline: true, variables: {currentTiddler: title}}); + // Naughty not to set a content-type, but it's the easiest way to ensure the browser will see HTML pages as HTML, and accept plain text tiddlers as CSS or JS + response.writeHead(200); + response.end(text,"utf8"); + } else { + response.writeHead(404); + response.end(); + } +}; + +}()); diff --git a/core/modules/server/routes/get-tiddler.js b/core/modules/server/routes/get-tiddler.js new file mode 100644 index 000000000..9088aa49d --- /dev/null +++ b/core/modules/server/routes/get-tiddler.js @@ -0,0 +1,46 @@ +/*\ +title: $:/core/modules/server/routes/get-tiddler.js +type: application/javascript +module-type: route + +GET /recipes/default/tiddlers/:title + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "GET"; + +exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/; + +exports.handler = function(request,response,state) { + var title = decodeURIComponent(state.params[0]), + tiddler = state.wiki.getTiddler(title), + tiddlerFields = {}, + knownFields = [ + "bag", "created", "creator", "modified", "modifier", "permissions", "recipe", "revision", "tags", "text", "title", "type", "uri" + ]; + if(tiddler) { + $tw.utils.each(tiddler.fields,function(field,name) { + var value = tiddler.getFieldString(name); + if(knownFields.indexOf(name) !== -1) { + tiddlerFields[name] = value; + } else { + tiddlerFields.fields = tiddlerFields.fields || {}; + tiddlerFields.fields[name] = value; + } + }); + tiddlerFields.revision = state.wiki.getChangeCount(title); + tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki"; + response.writeHead(200, {"Content-Type": "application/json"}); + response.end(JSON.stringify(tiddlerFields),"utf8"); + } else { + response.writeHead(404); + response.end(); + } +}; + +}()); diff --git a/core/modules/server/routes/get-tiddlers-json.js b/core/modules/server/routes/get-tiddlers-json.js new file mode 100644 index 000000000..3ece35ce1 --- /dev/null +++ b/core/modules/server/routes/get-tiddlers-json.js @@ -0,0 +1,37 @@ +/*\ +title: $:/core/modules/server/routes/get-tiddlers-json.js +type: application/javascript +module-type: route + +GET /recipes/default/tiddlers/tiddlers.json + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "GET"; + +exports.path = /^\/recipes\/default\/tiddlers.json$/; + +exports.handler = function(request,response,state) { + response.writeHead(200, {"Content-Type": "application/json"}); + var tiddlers = []; + state.wiki.forEachTiddler({sortField: "title"},function(title,tiddler) { + var tiddlerFields = {}; + $tw.utils.each(tiddler.fields,function(field,name) { + if(name !== "text") { + tiddlerFields[name] = tiddler.getFieldString(name); + } + }); + tiddlerFields.revision = state.wiki.getChangeCount(title); + tiddlerFields.type = tiddlerFields.type || "text/vnd.tiddlywiki"; + tiddlers.push(tiddlerFields); + }); + var text = JSON.stringify(tiddlers); + response.end(text,"utf8"); +}; + +}()); diff --git a/core/modules/server/routes/put-tiddler.js b/core/modules/server/routes/put-tiddler.js new file mode 100644 index 000000000..f6e49e5b2 --- /dev/null +++ b/core/modules/server/routes/put-tiddler.js @@ -0,0 +1,42 @@ +/*\ +title: $:/core/modules/server/routes/put-tiddler.js +type: application/javascript +module-type: route + +PUT /recipes/default/tiddlers/:title + +\*/ +(function() { + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.method = "PUT"; + +exports.path = /^\/recipes\/default\/tiddlers\/(.+)$/; + +exports.handler = function(request,response,state) { + var title = decodeURIComponent(state.params[0]), + fields = JSON.parse(state.data); + // Pull up any subfields in the `fields` object + if(fields.fields) { + $tw.utils.each(fields.fields,function(field,name) { + fields[name] = field; + }); + delete fields.fields; + } + // Remove any revision field + if(fields.revision) { + delete fields.revision; + } + state.wiki.addTiddler(new $tw.Tiddler(state.wiki.getCreationFields(),fields,{title: title},state.wiki.getModificationFields())); + var changeCount = state.wiki.getChangeCount(title).toString(); + response.writeHead(204, "OK",{ + Etag: "\"default/" + encodeURIComponent(title) + "/" + changeCount + ":\"", + "Content-Type": "text/plain" + }); + response.end(); +}; + +}()); diff --git a/core/modules/server/server.js b/core/modules/server/server.js new file mode 100644 index 000000000..045363254 --- /dev/null +++ b/core/modules/server/server.js @@ -0,0 +1,252 @@ +/*\ +title: $:/core/modules/server/server.js +type: application/javascript +module-type: library + +Serve tiddlers over http + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +if($tw.node) { + var util = require("util"), + fs = require("fs"), + url = require("url"), + path = require("path"); +} + +/* +A simple HTTP server with regexp-based routes +options: variables - optional hashmap of variables to set (a misnomer - they are really constant parameters) + routes - optional array of routes to use + wiki - reference to wiki object +*/ +function Server(options) { + var self = this; + this.routes = options.routes || []; + this.authenticators = options.authenticators || []; + this.wiki = options.wiki; + this.servername = this.wiki.getTiddlerText("$:/SiteTitle") || "TiddlyWiki5"; + // Initialise the variables + this.variables = $tw.utils.extend({},this.defaultVariables); + if(options.variables) { + for(var variable in options.variables) { + if(options.variables[variable]) { + this.variables[variable] = options.variables[variable]; + } + } + } + $tw.utils.extend({},this.defaultVariables,options.variables); + // Initialise CSRF + this.csrfDisable = this.get("csrf-disable") === "yes"; + // Initialise authorization + var authorizedUserName = (this.get("username") && this.get("password")) ? this.get("username") : "(anon)"; + this.authorizationPrincipals = { + readers: (this.get("readers") || authorizedUserName).split(",").map($tw.utils.trim), + writers: (this.get("writers") || authorizedUserName).split(",").map($tw.utils.trim) + } + // Load and initialise authenticators + $tw.modules.forEachModuleOfType("authenticator", function(title,authenticatorDefinition) { + // console.log("Loading server route " + title); + self.addAuthenticator(authenticatorDefinition.AuthenticatorClass); + }); + // Load route handlers + $tw.modules.forEachModuleOfType("route", function(title,routeDefinition) { + // console.log("Loading server route " + title); + self.addRoute(routeDefinition); + }); + // Initialise the http vs https + this.listenOptions = null; + this.protocol = "http"; + var tlsKeyFilepath = this.get("tls-key"), + tlsCertFilepath = this.get("tls-cert"); + if(tlsCertFilepath && tlsKeyFilepath) { + this.listenOptions = { + key: fs.readFileSync(path.resolve($tw.boot.wikiPath,tlsKeyFilepath),"utf8"), + cert: fs.readFileSync(path.resolve($tw.boot.wikiPath,tlsCertFilepath),"utf8") + }; + this.protocol = "https"; + } + this.transport = require(this.protocol); +} + +Server.prototype.defaultVariables = { + port: "8080", + host: "127.0.0.1", + "root-tiddler": "$:/core/save/all", + "root-render-type": "text/plain", + "root-serve-type": "text/html", + "tiddler-render-type": "text/html", + "tiddler-template": "$:/core/templates/server/static.tiddler.html", + "system-tiddler-render-type": "text/plain", + "system-tiddler-template": "$:/core/templates/wikified-tiddler", + "debug-level": "none" +}; + +Server.prototype.get = function(name) { + return this.variables[name]; +}; + +Server.prototype.addRoute = function(route) { + this.routes.push(route); +}; + +Server.prototype.addAuthenticator = function(AuthenticatorClass) { + // Instantiate and initialise the authenticator + var authenticator = new AuthenticatorClass(this), + result = authenticator.init(); + if(typeof result === "string") { + $tw.utils.error("Error: " + result); + } else if(result) { + // Only use the authenticator if it initialised successfully + this.authenticators.push(authenticator); + } +}; + +Server.prototype.findMatchingRoute = function(request,state) { + var pathprefix = this.get("path-prefix") || ""; + for(var t=0; t 0) { + if(!this.authenticators[0].authenticateRequest(request,response,state)) { + // Bail if we failed (the authenticator will have sent the response) + return; + } + } + // Authorize with the authenticated username + if(!this.isAuthorized(authorizationType,state.authenticatedUsername)) { + response.writeHead(401,"'" + state.authenticatedUsername + "' is not authorized to access '" + this.servername + "'"); + response.end(); + return; + } + // Find the route that matches this path + var route = self.findMatchingRoute(request,state); + // Optionally output debug info + if(self.get("debug-level") !== "none") { + console.log("Request path:",JSON.stringify(state.urlInfo)); + console.log("Request headers:",JSON.stringify(request.headers)); + console.log("authenticatedUsername:",state.authenticatedUsername); + } + // Return a 404 if we didn't find a route + if(!route) { + response.writeHead(404); + response.end(); + return; + } + // Set the encoding for the incoming request + // TODO: Presumably this would need tweaking if we supported PUTting binary tiddlers + request.setEncoding("utf8"); + // Dispatch the appropriate method + switch(request.method) { + case "GET": // Intentional fall-through + case "DELETE": + route.handler(request,response,state); + break; + case "PUT": + var data = ""; + request.on("data",function(chunk) { + data += chunk.toString(); + }); + request.on("end",function() { + state.data = data; + route.handler(request,response,state); + }); + break; + } +}; + +/* +Listen for requests +port: optional port number (falls back to value of "port" variable) +host: optional host address (falls back to value of "hist" variable) +*/ +Server.prototype.listen = function(port,host) { + // Handle defaults for port and host + port = port || this.get("port"); + host = host || this.get("host"); + // Check for the port being a string and look it up as an environment variable + if(parseInt(port,10).toString() !== port) { + port = process.env[port] || 8080; + } + $tw.utils.log("Serving on " + this.protocol + "://" + host + ":" + port,"brown/orange"); + $tw.utils.log("(press ctrl-C to exit)","red"); + // Warn if required plugins are missing + if(!$tw.wiki.getTiddler("$:/plugins/tiddlywiki/tiddlyweb") || !$tw.wiki.getTiddler("$:/plugins/tiddlywiki/filesystem")) { + $tw.utils.warning("Warning: Plugins required for client-server operation (\"tiddlywiki/filesystem\" and \"tiddlywiki/tiddlyweb\") are missing from tiddlywiki.info file"); + } + // Listen + var server; + if(this.listenOptions) { + server = this.transport.createServer(this.listenOptions,this.requestHandler.bind(this)); + } else { + server = this.transport.createServer(this.requestHandler.bind(this)); + } + return server.listen(port,host); +}; + +exports.Server = Server; + +})(); diff --git a/core/modules/syncer.js b/core/modules/syncer.js index 94975ce80..e6d86ea61 100644 --- a/core/modules/syncer.js +++ b/core/modules/syncer.js @@ -16,6 +16,8 @@ The syncer tracks changes to the store. If a syncadaptor is used then individual Defaults */ Syncer.prototype.titleIsLoggedIn = "$:/status/IsLoggedIn"; +Syncer.prototype.titleIsAnonymous = "$:/status/IsAnonymous"; +Syncer.prototype.titleIsReadOnly = "$:/status/IsReadOnly"; Syncer.prototype.titleUserName = "$:/status/UserName"; Syncer.prototype.titleSyncFilter = "$:/config/SyncFilter"; Syncer.prototype.titleSavedNotification = "$:/language/Notifications/Save/Done"; @@ -169,12 +171,14 @@ Syncer.prototype.getStatus = function(callback) { // Mark us as not logged in this.wiki.addTiddler({title: this.titleIsLoggedIn,text: "no"}); // Get login status - this.syncadaptor.getStatus(function(err,isLoggedIn,username) { + this.syncadaptor.getStatus(function(err,isLoggedIn,username,isReadOnly,isAnonymous) { if(err) { self.logger.alert(err); return; } // Set the various status tiddlers + self.wiki.addTiddler({title: self.titleIsReadOnly,text: isReadOnly ? "yes" : "no"}); + self.wiki.addTiddler({title: self.titleIsAnonymous,text: isAnonymous ? "yes" : "no"}); self.wiki.addTiddler({title: self.titleIsLoggedIn,text: isLoggedIn ? "yes" : "no"}); if(isLoggedIn) { self.wiki.addTiddler({title: self.titleUserName,text: username || ""}); diff --git a/core/modules/utils/csv.js b/core/modules/utils/csv.js new file mode 100644 index 000000000..37312d847 --- /dev/null +++ b/core/modules/utils/csv.js @@ -0,0 +1,46 @@ +/*\ +title: $:/core/modules/utils/csv.js +type: application/javascript +module-type: utils + +A barebones CSV parser + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Parse a CSV string with a header row and return an array of hashmaps. +*/ +exports.parseCsvStringWithHeader = function(text,options) { + options = options || {}; + var separator = options.separator || ",", + rows = text.split(/\r?\n/mg).map(function(row) { + return $tw.utils.trim(row); + }).filter(function(row) { + return row !== ""; + }); + if(rows.length < 1) { + return "Missing header row"; + } + var headings = rows[0].split(separator), + results = []; + for(var row=1; row +
+

+<$transclude tiddler="$:/SiteTitle"/> +

+
+<$transclude tiddler="$:/SiteSubtitle"/> +
+

+

+
+<$list filter={{$:/DefaultTiddlers}}> +
+<$link><$text text=<>/> +
+ +
+ diff --git a/core/templates/server/static.tiddler.html.tid b/core/templates/server/static.tiddler.html.tid new file mode 100644 index 000000000..7966baeb1 --- /dev/null +++ b/core/templates/server/static.tiddler.html.tid @@ -0,0 +1,29 @@ +title: $:/core/templates/server/static.tiddler.html + +\whitespace trim +\define tv-wikilink-template() $uri_encoded$ +<$importvariables filter="[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]"> + + + + + + + + + + + + +<$view field="caption" format="plainwikified"><$view field="title"/></$view>: <$view tiddler="$:/core/wiki/title" format="plainwikified"/> + + +<$transclude tiddler="$:/core/templates/server/static.sidebar.wikitext" mode="inline"/> +
+
+<$transclude tiddler="$:/core/templates/server/static.tiddler.wikitext" mode="inline"/> +
+
+ + + \ No newline at end of file diff --git a/core/templates/server/static.tiddler.wikitext.tid b/core/templates/server/static.tiddler.wikitext.tid new file mode 100644 index 000000000..2d2bdaba6 --- /dev/null +++ b/core/templates/server/static.tiddler.wikitext.tid @@ -0,0 +1,23 @@ +title: $:/core/templates/server/static.tiddler.wikitext + +\whitespace trim +
+
+

<$text text=<>/>

+
+
+
+<$link to={{!!modifier}}> +<$view field="modifier"/> + <$view field="modified" format="date" template={{$:/language/Tiddler/DateFormat}}/> +
+
+<$list filter="[all[current]tags[]sort[title]]"> +encodeuricomponent[]] }}}> +<$macrocall $name="tag-pill" tag=<>/> + + +
+
+<$transclude mode="block"/> +
diff --git a/editions/tw5.com/tiddlers/commands/Commands.tid b/editions/tw5.com/tiddlers/commands/Commands.tid index fc95297db..c103258c3 100644 --- a/editions/tw5.com/tiddlers/commands/Commands.tid +++ b/editions/tw5.com/tiddlers/commands/Commands.tid @@ -1,8 +1,9 @@ created: 20150117174359000 -modified: 20150117205257000 -title: Commands +modified: 20180626122309578 tags: Concepts Reference +title: Commands +type: text/vnd.tiddlywiki -A <<.def command>> is one of the following words, written with a `--` prefix and used as a command-line option to [[TiddlyWiki on Node.js]], indicating which action is desired. +A <<.def command>> is one of the following words, written with a `--` prefix and used as a command-line option under Node.js, indicating which action is desired. See [[Using TiddlyWiki on Node.js]] for details of how to use them. <> diff --git a/editions/tw5.com/tiddlers/commands/ListenCommand.tid b/editions/tw5.com/tiddlers/commands/ListenCommand.tid new file mode 100644 index 000000000..b3f94368f --- /dev/null +++ b/editions/tw5.com/tiddlers/commands/ListenCommand.tid @@ -0,0 +1,10 @@ +caption: listen +created: 20180626135301279 +modified: 20180701171046122 +tags: Commands +title: ListenCommand +type: text/vnd.tiddlywiki + +<<.from-version "5.1.18">> See WebServer for details of TiddlyWiki's web server functionality. + +{{$:/language/Help/listen}} \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/commands/NamedCommandParameters.tid b/editions/tw5.com/tiddlers/commands/NamedCommandParameters.tid new file mode 100644 index 000000000..f282793c1 --- /dev/null +++ b/editions/tw5.com/tiddlers/commands/NamedCommandParameters.tid @@ -0,0 +1,22 @@ +created: 20180626122427188 +modified: 20180626134639673 +tags: [[Using TiddlyWiki on Node.js]] +title: NamedCommandParameters +type: text/vnd.tiddlywiki + +<<.from-version "5.1.18">> Most TiddlyWiki [[Commands]] use a position-based system for their parameters where each parameter must be listed in the precise order defined by the command. Some of the more complex commands offer an alternative scheme of named command parameters. For example, here we provide two parameters named "port" and "host": + +``` +--listen port=8090 host=0.0.0.0 +``` + +Note that the order of the parameters does not matter. + +Using special characters within a parameter requires quoting. Unix, Linux and the Mac use single quotes, and Windows uses double quotes: + +``` +--listen port=8090 username=joe 'password=s3cret(!' +--listen port=8090 username=joe "password=s3cret(!" +``` + +Note that the quotes are applied to the entire name=value pair, not just to the value part. diff --git a/editions/tw5.com/tiddlers/commands/ServerCommand.tid b/editions/tw5.com/tiddlers/commands/ServerCommand.tid index c21a8a430..73483ea15 100644 --- a/editions/tw5.com/tiddlers/commands/ServerCommand.tid +++ b/editions/tw5.com/tiddlers/commands/ServerCommand.tid @@ -1,8 +1,12 @@ +caption: server created: 20131219163923630 -modified: 20131229130513478 +modified: 20180626150505679 tags: Commands title: ServerCommand type: text/vnd.tiddlywiki -caption: server + +''Note that the `--server` command is now deprecated in favour of the new ListenCommand''. + +See WebServer for details of TiddlyWiki's web server functionality. {{$:/language/Help/server}} diff --git a/editions/tw5.com/tiddlers/community/Translate TiddlyWiki into your language.tid b/editions/tw5.com/tiddlers/community/Translate TiddlyWiki into your language.tid index 345d8c895..cf5de5106 100644 --- a/editions/tw5.com/tiddlers/community/Translate TiddlyWiki into your language.tid +++ b/editions/tw5.com/tiddlers/community/Translate TiddlyWiki into your language.tid @@ -1,5 +1,5 @@ created: 20141126153016142 -modified: 20160622141441383 +modified: 20180701185730340 tags: Community title: Translate TiddlyWiki into your language type: text/vnd.tiddlywiki @@ -11,6 +11,6 @@ There is a special edition of TiddlyWiki that simplifies creating and maintainin Note that no knowledge of Node.js or GitHub is required. -You can translate ~TiddlyWiki on Node.js, type `tiddlywiki editions/translators --server` and visit http://127.0.0.1:8080/ in your browser. +You can translate ~TiddlyWiki on Node.js, type `tiddlywiki editions/translators --listen` and visit http://127.0.0.1:8080/ in your browser. See https://tiddlywiki.com/dev for technical details of creating and maintaining translations. diff --git a/editions/tw5.com/tiddlers/features/LazyLoading.tid b/editions/tw5.com/tiddlers/features/LazyLoading.tid index 0d2adb623..893e22bd2 100644 --- a/editions/tw5.com/tiddlers/features/LazyLoading.tid +++ b/editions/tw5.com/tiddlers/features/LazyLoading.tid @@ -1,5 +1,5 @@ created: 20140206214608586 -modified: 20151105122712982 +modified: 20180701185417525 tags: Features title: LazyLoading type: text/vnd.tiddlywiki @@ -19,17 +19,13 @@ To start TiddlyWiki with lazy loading for image tiddlers use this command: ``` -tiddlywiki --server 8080 $:/core/save/lazy-images +tiddlywiki --listen root-tiddler=$:/core/save/lazy-images ``` To apply lazy loading to all non-system tiddlers use this command: ``` -tiddlywiki --server 8080 $:/core/save/lazy-all +tiddlywiki --listen root-tiddler=$:/core/save/lazy-all ``` -! Lazy loading under TiddlyWeb - -With the current configuration, lazy loading is enabled by default. - diff --git a/editions/tw5.com/tiddlers/nodejs/Installing TiddlyWiki Prerelease on Node.js.tid b/editions/tw5.com/tiddlers/nodejs/Installing TiddlyWiki Prerelease on Node.js.tid index ab4712a7e..827681e21 100644 --- a/editions/tw5.com/tiddlers/nodejs/Installing TiddlyWiki Prerelease on Node.js.tid +++ b/editions/tw5.com/tiddlers/nodejs/Installing TiddlyWiki Prerelease on Node.js.tid @@ -1,5 +1,5 @@ created: 20150926162849519 -modified: 20161215160531991 +modified: 20180701185329863 tags: [[Installing TiddlyWiki on Node.js]] title: Installing TiddlyWiki Prerelease on Node.js type: text/vnd.tiddlywiki @@ -7,7 +7,7 @@ type: text/vnd.tiddlywiki # Clone a local copy of the TiddlyWiki5 GitHub repository from https://github.com/Jermolene/TiddlyWiki5 # Open a command line terminal and change the current working directory to the root of the TiddlyWiki5 repo # Type `npm link` (Windows) or `sudo npm link` (Mac/Linux) to tell [[npm]] to use this copy of the repo as the globally installed one -# Inside the root, you can launch ~TiddlyWiki like this:
``tiddlywiki editions/tw5.com-server --server 8080 $:/core/save/all text/plain text/html`` +# Inside the root, you can launch ~TiddlyWiki like this:
``tiddlywiki editions/tw5.com-server --listen`` After this procedure you can work with TiddlyWiki5 via [[npm]] as though it had been installed in the usual way with `npm install -g tiddlywiki`. diff --git a/editions/tw5.com/tiddlers/nodejs/Installing TiddlyWiki on Node.js.tid b/editions/tw5.com/tiddlers/nodejs/Installing TiddlyWiki on Node.js.tid index 4f047c3ed..47eb9c603 100644 --- a/editions/tw5.com/tiddlers/nodejs/Installing TiddlyWiki on Node.js.tid +++ b/editions/tw5.com/tiddlers/nodejs/Installing TiddlyWiki on Node.js.tid @@ -3,7 +3,7 @@ created: 20131219100608529 delivery: DIY description: Flexible hosting on your own machine or in the cloud method: sync -modified: 20171115174105090 +modified: 20180701185303780 tags: Saving [[TiddlyWiki on Node.js]] Windows Mac Linux title: Installing TiddlyWiki on Node.js type: text/vnd.tiddlywiki @@ -20,7 +20,7 @@ type: text/vnd.tiddlywiki # In response, you should see TiddlyWiki report its current version (eg "<>"; you may also see other debugging information reported) # Try it out: ## `tiddlywiki mynewwiki --init server` to create a folder for a new wiki that includes server-related components -## `tiddlywiki mynewwiki --server` to start TiddlyWiki +## `tiddlywiki mynewwiki --listen` to start TiddlyWiki ## Visit http://127.0.0.1:8080/ in your browser ## Try editing and creating tiddlers # Optionally, make an offline copy: diff --git a/editions/tw5.com/tiddlers/nodejs/Using TiddlyWiki on Node.js.tid b/editions/tw5.com/tiddlers/nodejs/Using TiddlyWiki on Node.js.tid index 958f65986..17e7e0841 100644 --- a/editions/tw5.com/tiddlers/nodejs/Using TiddlyWiki on Node.js.tid +++ b/editions/tw5.com/tiddlers/nodejs/Using TiddlyWiki on Node.js.tid @@ -1,10 +1,10 @@ created: 20131219100520659 -modified: 20140920135025757 +modified: 20180626122347768 tags: [[TiddlyWiki on Node.js]] title: Using TiddlyWiki on Node.js type: text/vnd.tiddlywiki -TiddlyWiki5 can be used on the command line to perform an extensive set of operations based on TiddlyWikiFolders, TiddlerFiles and TiddlyWikiFiles. +TiddlyWiki5 includes a set of [[Commands]] for use on the command line to perform an extensive set of operations based on TiddlyWikiFolders, TiddlerFiles and TiddlyWikiFiles. For example, the following command loads the tiddlers from a TiddlyWiki HTML file and then saves one of them in static HTML: @@ -22,6 +22,10 @@ The commands and their individual arguments follow, each command being identifie tiddlywiki [] [-- [[,]]] ``` -The available commands are: +<<.from-version "5.1.18">> Commands such as the ListenCommand that support large numbers of parameters can use NamedCommandParameters to make things less unwieldy. For example: -<> +``` +tiddlywiki wikipath --listen username=jeremy port=8090 +``` + +See [[Commands]] for a full listing of the available commands. diff --git a/editions/tw5.com/tiddlers/saving/Example web.config for IIS.txt b/editions/tw5.com/tiddlers/saving/Example web.config for IIS.txt index 9a26b76a7..d8a75bb4f 100644 --- a/editions/tw5.com/tiddlers/saving/Example web.config for IIS.txt +++ b/editions/tw5.com/tiddlers/saving/Example web.config for IIS.txt @@ -15,7 +15,7 @@ stdoutLogFile=".\node.log" startupTimeLimit="20" processPath="C:\Program Files\nodejs\node.exe" - arguments=".\node_modules\tiddlywiki\tiddlywiki.js ./wiki --server PORT $:/core/save/all text/plain text/html "" "" 127.0.0.1 /MyApp"> + arguments=".\node_modules\tiddlywiki\tiddlywiki.js ./wiki --listen port=PORT path-prefix=/MyApp"> diff --git a/editions/tw5.com/tiddlers/saving/Example web.config for IIS.txt.meta b/editions/tw5.com/tiddlers/saving/Example web.config for IIS.txt.meta index 757d70318..c83eaebfb 100644 --- a/editions/tw5.com/tiddlers/saving/Example web.config for IIS.txt.meta +++ b/editions/tw5.com/tiddlers/saving/Example web.config for IIS.txt.meta @@ -1,5 +1,5 @@ created: 20180328145259455 -modified: 20180328151038658 +modified: 20180701185215523 tags: title: Example web.config for IIS type: text/plain diff --git a/editions/tw5.com/tiddlers/saving/Installing TiddlyWiki on Microsoft Internet Information Server.tid b/editions/tw5.com/tiddlers/saving/Installing TiddlyWiki on Microsoft Internet Information Server.tid index 7d8b91bba..e721e6518 100644 --- a/editions/tw5.com/tiddlers/saving/Installing TiddlyWiki on Microsoft Internet Information Server.tid +++ b/editions/tw5.com/tiddlers/saving/Installing TiddlyWiki on Microsoft Internet Information Server.tid @@ -3,7 +3,7 @@ created: 20180328120356008 delivery: DIY description: Windows' built-in web server method: sync -modified: 20180328151441294 +modified: 20180701185718671 tags: Saving [[TiddlyWiki on Node.js]] Windows title: Installing TiddlyWiki on Microsoft Internet Information Server type: text/vnd.tiddlywiki @@ -61,8 +61,8 @@ Test the app by visiting http://localhost/MyApp/ in a browser. ! Notes -* If you require authentication, specify a username and password in the `--server` command in `web.config`. For example: -** `arguments=".\node_modules\tiddlywiki\tiddlywiki.js ./wiki-server --server PORT $:/core/save/all text/plain text/html "username" "password" 127.0.0.1 /MyApp">` -** Take note of the need to HTML encode the double quotes around the username and password into `"` +* If you require authentication, specify a username and password in the `--listen` command in `web.config`. For example: +** `arguments=".\node_modules\tiddlywiki\tiddlywiki.js ./wiki-server --listen username=joe "password=bloggs" port=PORT path-prefix=/MyApp">` +** Take note of the need to use double quotes around non-alphanumeric passwords, and to HTML encode them into `"` * If you change the settings in the `web.config` file, or modify the app code, then you'll need to restart the server using the IIS manager application diff --git a/editions/tw5.com/tiddlers/webserver/Using HTTPS.tid b/editions/tw5.com/tiddlers/webserver/Using HTTPS.tid new file mode 100644 index 000000000..90aec56af --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/Using HTTPS.tid @@ -0,0 +1,19 @@ +created: 20180702160923664 +modified: 20180703100549667 +tags: [[WebServer Guides]] +title: Using HTTPS +type: text/vnd.tiddlywiki + +By default, TiddlyWiki's WebServer serves resources over the insecure HTTP protocol. The risk is minimal if it is only being used within a private, trusted network but in many situations it is desirable to use a secure HTTPS connection. + +HTTPS requires the server to be configured with a certificate via a "cert" file and a "key" file, configured via the [[tls-cert|WebServer Parameter: tls-cert]] and [[tls-key|WebServer Parameter: tls-key]] parameters + +Certificates can be obtained from a certification authority such as https://letsencrypt.org/, or you can create a self-signed certificate for internal testing. + +To create the required certificate files with the popular [[openssl|https://www.openssl.org/]] utility: + +``` +openssl req -newkey rsa:2048 -new -nodes -keyout mywikifolder/key.pem -out mywikifolder/csr.pem +openssl x509 -req -days 365 -in mywikifolder/csr.pem -signkey mywikifolder/key.pem -out mywikifolder/server.crt +tiddlywiki mywikifolder --listen username=joe password=bloggs tlskey=key.pem tlscert=server.crt +``` diff --git a/editions/tw5.com/tiddlers/webserver/Using the integrated static file server.tid b/editions/tw5.com/tiddlers/webserver/Using the integrated static file server.tid new file mode 100644 index 000000000..515730a3b --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/Using the integrated static file server.tid @@ -0,0 +1,16 @@ +created: 20180703095630828 +modified: 20180703100445719 +tags: [[WebServer Guides]] +title: Using the integrated static file server +type: text/vnd.tiddlywiki + +Any files in the subfolder `files` of the wiki folder will be available via the route `\files\`. For example: http://127.0.0.1:8080/files/Motovun%20Jack.jpg + +This can be useful for publishing large files that you don't want to incorporate into the main wiki (PDFs, videos, large images, etc.). + +Static files can be referenced directly: + +* `[ext[./files/a-big-document.pdf]]` - to make a link to a PDF +* `[img[./files/a-big-image.png]]` - to embed an image + +Alternatively, the ''_canonical_uri'' field can be used to reference the files as [[external tiddlers|ExternalImages]]. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/Using the read-only single tiddler view.tid b/editions/tw5.com/tiddlers/webserver/Using the read-only single tiddler view.tid new file mode 100644 index 000000000..7abeba36c --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/Using the read-only single tiddler view.tid @@ -0,0 +1,7 @@ +created: 20180703095435813 +modified: 20180703100525994 +tags: [[WebServer Guides]] +title: Using the read-only single tiddler view +type: text/vnd.tiddlywiki + +As well as serving the full interactive wiki at the path `/` (e.g. http://127.0.0.1:8080/), TiddlyWiki also serves an experimental single tiddler per page, read-only view of the wiki at the path `/` (e.g. http://127.0.0.1:8080/HelloThere). It uses a simplified page layout, and implements links between tiddlers, but there are no other interactive features. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Anonymous Access.tid b/editions/tw5.com/tiddlers/webserver/WebServer Anonymous Access.tid new file mode 100644 index 000000000..1453154f9 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Anonymous Access.tid @@ -0,0 +1,8 @@ +created: 20180701175139654 +modified: 20180701202339421 +tags: [[WebServer Authentication]] +title: WebServer Anonymous Access +type: text/vnd.tiddlywiki + +Anonymous access is only permitted if the special ''(anon)'' token is present in the [[readers|WebServer Parameter: readers]] (for reading) and optionally [[writers|WebServer Parameter: writers]] (for writing) authorization parameters. + diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Authentication.tid b/editions/tw5.com/tiddlers/webserver/WebServer Authentication.tid new file mode 100644 index 000000000..682891c4c --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Authentication.tid @@ -0,0 +1,11 @@ +created: 20180630193939007 +modified: 20180701184519624 +tags: WebServer +title: WebServer Authentication +type: text/vnd.tiddlywiki + +''Authentication'' is the process of identifying the current user. TiddlyWiki supports three types of authentication: + +* [[Anonymous Access|WebServer Anonymous Access]] allows any user to access resources without requiring authentication. Optionally, a username can still be specified for signing edits +* [[Basic Authentication|WebServer Basic Authentication]] requires the user to enter a username and password combination which TiddlyWiki validates against an internal database of credentials +* [[Header Authentication|WebServer Header Authentication]] requires an external proxy to place the username of the current user in a trusted header of the request. It is often used as the basis of "single sign-on" features diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Authorization.tid b/editions/tw5.com/tiddlers/webserver/WebServer Authorization.tid new file mode 100644 index 000000000..8bd2e0566 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Authorization.tid @@ -0,0 +1,30 @@ +created: 20180630194006239 +modified: 20180701174944267 +tags: WebServer +title: WebServer Authorization +type: text/vnd.tiddlywiki + +''Authorization'' is the process of determining which resources may be accessed by a particular user. It occurs after [[authentication|WebServer Authentication]] has determined the identity of the user. TiddlyWiki's WebServer implements a simple authorization scheme which permits independent control of who has read and write access to a wiki. + +The WebServer parameters [[readers|WebServer Parameter: readers]] and [[writers|WebServer Parameter: writers]] each contain a comma separated list of //principals// (which is to say, either usernames or certain special tokens) which should have read or write access respectively. + +The available special tokens are: + +* ''(anon)'' - indicates all anonymous users +* ''(authenticated)'' - indicates all authenticated users + +!! Examples + +These example use the [[credentials|WebServer Parameter: credentials]] parameter to specify the location of a file containing usernames and passwords. + +In the first example, read access is permitted for the users "joe" and "mary", with write access restricted to "mary": + +``` +tiddlywiki mywikifolder --listen credentials=myusers.csv readers=joe,mary writers=mary +``` + +In the following example, read access is granted to all authenticated users, but only "mary" is granted write access: + +``` +tiddlywiki mywikifolder --listen credentials=myusers.csv "readers=(authenticated)" writers=mary +``` diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Basic Authentication.tid b/editions/tw5.com/tiddlers/webserver/WebServer Basic Authentication.tid new file mode 100644 index 000000000..e65f391fe --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Basic Authentication.tid @@ -0,0 +1,11 @@ +created: 20180701175133376 +modified: 20180702132942777 +tags: [[WebServer Authentication]] +title: WebServer Basic Authentication +type: text/vnd.tiddlywiki + +[[Basic authentication|https://en.wikipedia.org/wiki/Basic_access_authentication]] is a standard [[mechanism|WebServer Authentication]] for servers to instruct browsers to prompt the user for credentials. It is recommended to use it in association with HTTPS due to the way that it passes unencrypted passwords over the network. + +Basic authentication is activated if credentials are specified via the [[username|WebServer Parameter: username]]/[[password|WebServer Parameter: password]] or [[credentials|WebServer Parameter: credentials]] parameters. + +If [[WebServer Authorization]] is configured to allow access by both anonymous and authenticated users then by default users will not be prompted for credentials, and will be given anonymous access. To force a password prompt visit the route `/login-basic` (for example, http://127.0.0.1:8080/login-basic). diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Guides.tid b/editions/tw5.com/tiddlers/webserver/WebServer Guides.tid new file mode 100644 index 000000000..b30d49de6 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Guides.tid @@ -0,0 +1,9 @@ +created: 20180703094704164 +modified: 20180703094729900 +tags: WebServer +title: WebServer Guides +type: text/vnd.tiddlywiki + +Further information on usage of the integrated [[WebServer]]: + +<> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Header Authentication.tid b/editions/tw5.com/tiddlers/webserver/WebServer Header Authentication.tid new file mode 100644 index 000000000..d4832d17f --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Header Authentication.tid @@ -0,0 +1,9 @@ +created: 20180701175127987 +modified: 20180702140238032 +tags: [[WebServer Authentication]] +title: WebServer Header Authentication +type: text/vnd.tiddlywiki + +Header authentication is a web integration technique enabling external entities to securely pass details of the authenticated user to an application. It is commonly used for "single sign on" in corporate environments. + +Header authentication is activated if is configured via the [[authenticated-user-header|WebServer Parameter: authenticated-user-header]] diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ anon-username.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ anon-username.tid new file mode 100644 index 000000000..ba8564da8 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ anon-username.tid @@ -0,0 +1,10 @@ +caption: anon-username +created: 20180702124636124 +modified: 20180702124836655 +tags: [[WebServer Parameters]] +title: WebServer Parameter: anon-username +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''anon-username'' provides an optional username for signing edits from anonymous users. + +Without this parameter, anonymous users will be given a blank username. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ authenticated-user-header.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ authenticated-user-header.tid new file mode 100644 index 000000000..acd409220 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ authenticated-user-header.tid @@ -0,0 +1,8 @@ +caption: authenticated-user-header +created: 20180630180213047 +modified: 20180702140416583 +tags: [[WebServer Parameters]] +title: WebServer Parameter: authenticated-user-header +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''authenticated-user-header'' activates [[header authentication|WebServer Header Authentication]] by specifying the name of the HTTP header that will be used to pass the username to TiddlyWiki. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ credentials.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ credentials.tid new file mode 100644 index 000000000..f7bdb3e99 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ credentials.tid @@ -0,0 +1,25 @@ +caption: credentials +created: 20180630180156036 +modified: 20180702154456353 +tags: [[WebServer Parameters]] +title: WebServer Parameter: credentials +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''credentials'' contains the pathname of a [[CSV file|https://en.wikipedia.org/wiki/Comma-separated_values]] containing a list of username/password combinations. Using the ''credentials'' parameter activates [[WebServer Basic Authentication]]. + +The CSV file must contain a header row and columns labelled ''username'' and ''password''. For example: + +``` +username,password +jane,do3 +andy,sm1th +roger,m00re +``` + +Notes: + +* The optional [[username|WebServer Parameter: username]]/[[password|WebServer Parameter: password]] parameters may be used to provide an additional single set of credentials +* The pathname is taken relative to the wiki folder +* Passwords cannot contain the comma character `,` +* The header row must be present + diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ csrf-disable.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ csrf-disable.tid new file mode 100644 index 000000000..125f1466f --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ csrf-disable.tid @@ -0,0 +1,10 @@ +caption: csrf-disable +created: 20180630180340448 +modified: 20180702142051779 +tags: [[WebServer Parameters]] +title: WebServer Parameter: csrf-disable +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''csrf-disable'' causes the usual [[cross-site request forgery|https://en.wikipedia.org/wiki/Cross-site_request_forgery]] checks to be disabled. This might be necessary in unusual or experimental configurations. + +The only currently implemented check is the use of a [[custom header|https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet#Protecting_REST_Services:_Use_of_Custom_Request_Headers]] called `x-requested-with` that must contain the string `TiddlyWiki` in order for write requests to succeed. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ debug-level.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ debug-level.tid new file mode 100644 index 000000000..5900ba39b --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ debug-level.tid @@ -0,0 +1,11 @@ +caption: debug-level +created: 20180630180535728 +modified: 20180702142154308 +tags: [[WebServer Parameters]] +title: WebServer Parameter: debug-level +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''debug-level'' determines the level of debugging information printed to the console: + +* ''full'' - maximum logging +* ''none'' - no logging diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ host.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ host.tid new file mode 100644 index 000000000..5fcd0ba9a --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ host.tid @@ -0,0 +1,12 @@ +caption: host +created: 20180630180123321 +modified: 20180702153356797 +tags: [[WebServer Parameters]] +title: WebServer Parameter: host +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''host'' is the IP address on which the server listens. The most common settings are: + +* ''127.0.0.1'' (default) - only listens for connections from browsers on the same computer +* ''0.0.0.0'' - listens for connections on all network interfaces, and thus from any browser on a reachable network +* ''n.n.n.n'' - listens for connections on the network interface with the specified IP address diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ password.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ password.tid new file mode 100644 index 000000000..2731b93cb --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ password.tid @@ -0,0 +1,8 @@ +caption: password +created: 20180630170932602 +modified: 20180630171122879 +tags: [[WebServer Parameters]] +title: WebServer Parameter: password +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''password'', is used with its companion [[password|WebServer Parameter: username]] as a shortcut for setting user credentials for [[WebServer Basic Authentication]]. diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ path-prefix.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ path-prefix.tid new file mode 100644 index 000000000..ed929c406 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ path-prefix.tid @@ -0,0 +1,14 @@ +caption: path-prefix +created: 20180630180514893 +modified: 20180702154716090 +tags: [[WebServer Parameters]] +title: WebServer Parameter: path-prefix +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''path-prefix'' can be used to set an optional prefix for all paths served. + +This example causes the server to serve from http://127.0.0.1/MyApp instead of the default http://127.0.0.1/MyApp. + +``` +tiddlywiki mywikifolder --listen "path-prefix=/MyApp" +``` diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ port.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ port.tid new file mode 100644 index 000000000..78c0713b1 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ port.tid @@ -0,0 +1,25 @@ +caption: port +created: 20180630180552254 +modified: 20180702155017130 +tags: [[WebServer Parameters]] +title: WebServer Parameter: port +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''port'' specifies the TCP port on which the server will listen for connections. The default value is `8080`. + +The ''port'' parameter accepts two types of value: + +* Numerical values are interpreted as a decimal port number +* Non-numeric values are interpreted as an environment variable from which the port should be read + +This example configures the server to listen on port 8090: + +``` +tiddlywiki mywikifolder --listen port:8090 +``` + +This example configures the server to listen on the port specified in the environment variable `THE_PORT`: + +``` +tiddlywiki mywikifolder --listen port:THE_PORT +``` diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ readers.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ readers.tid new file mode 100644 index 000000000..a6e0abc0d --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ readers.tid @@ -0,0 +1,8 @@ +caption: readers +created: 20180630180405978 +modified: 20180702155139006 +tags: [[WebServer Parameters]] +title: WebServer Parameter: readers +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''readers'' is used to specify the security principals with read access to the wiki. See [[WebServer Authorization]] for more details. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-render-type.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-render-type.tid new file mode 100644 index 000000000..080354b76 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-render-type.tid @@ -0,0 +1,12 @@ +caption: root-render-type +created: 20180630180301861 +modified: 20180702160458499 +tags: [[WebServer Parameters]] +title: WebServer Parameter: root-render-type +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''root-render-type'' determines the way that the [[root wiki tiddler|WebServer Parameter: root-tiddler]] is rendered: + +* `text/plain` (default) -- the plain text content of the output is rendered (i.e. HTML elements are ignored) +* `text/html` -- the full HTML content of the output is rendered (i.e. including HTML elements) + diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-serve-type.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-serve-type.tid new file mode 100644 index 000000000..095d63be4 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-serve-type.tid @@ -0,0 +1,8 @@ +caption: root-serve-type +created: 20180630180233285 +modified: 20180702160557700 +tags: [[WebServer Parameters]] +title: WebServer Parameter: root-serve-type +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''root-serve-type'' determines the content type with which the root wiki tiddler is rendered. The default is `text/html`. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-tiddler.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-tiddler.tid new file mode 100644 index 000000000..eba2d0274 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ root-tiddler.tid @@ -0,0 +1,8 @@ +caption: root-tiddler +created: 20180630180320376 +modified: 20180702160659310 +tags: [[WebServer Parameters]] +title: WebServer Parameter: root-tiddler +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''root-tiddler'' determines the title of the tiddler that is rendered as the root wiki. The default setting is `$:/core/save/all`. diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ tls-cert.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ tls-cert.tid new file mode 100644 index 000000000..dbf47be2e --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ tls-cert.tid @@ -0,0 +1,10 @@ +caption: tls-cert +created: 20180630180449581 +modified: 20180703100612649 +tags: [[WebServer Parameters]] +title: WebServer Parameter: tls-cert +type: text/vnd.tiddlywiki + +The optional [[web server configuration parameter|WebServer Parameters]] ''tls-cert'' contains the pathname to the certificate file required when running the web server under HTTPS. The pathname is taken relative to the wiki folder. + +See [[Using HTTPS]] for details. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ tls-key.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ tls-key.tid new file mode 100644 index 000000000..b73a94f83 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ tls-key.tid @@ -0,0 +1,10 @@ +caption: tls-key +created: 20180630180435405 +modified: 20180703100619863 +tags: [[WebServer Parameters]] +title: WebServer Parameter: tls-key +type: text/vnd.tiddlywiki + +The optional [[web server configuration parameter|WebServer Parameters]] ''tls-key'' contains the pathname to the key file required when running the web server under HTTPS. The pathname is taken relative to the wiki folder. + +See [[Using HTTPS]] for details. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ username.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ username.tid new file mode 100644 index 000000000..e49f15c4b --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ username.tid @@ -0,0 +1,26 @@ +caption: username +created: 20180628130114210 +modified: 20180702124624290 +tags: [[WebServer Parameters]] +title: WebServer Parameter: username +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''username'', in conjunction with its companion [[password|WebServer Parameter: password]]: + +* Enables [[Basic Authentication|WebServer Basic Authentication]] with the specified username/password combination being added to any credentials specified with the [[credentials|WebServer Parameter: credentials]] parameter +* The specified username is used as a default value for the [[readers|WebServer Parameter: readers]] and [[writers|WebServer Parameter: writers]] authorization parameters if they not specified + +!! Examples + +Serve anonymous users, setting the username to "joe": + +``` +tiddlywik mywikifolder --listen anon-username=joe +``` + +Restrict access to the user "joe" with a password of "secret": + +``` +tiddlywik mywikifolder --listen username=joe password=secret +``` + diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ writers.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ writers.tid new file mode 100644 index 000000000..acc30812c --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ writers.tid @@ -0,0 +1,8 @@ +caption: writers +created: 20180630180359439 +modified: 20180702155133411 +tags: [[WebServer Parameters]] +title: WebServer Parameter: writers +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''writers'' is used to specify the security principals with write access to the wiki. See [[WebServer Authorization]] for more details. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameters.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameters.tid new file mode 100644 index 000000000..939f98208 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameters.tid @@ -0,0 +1,9 @@ +created: 20180628125910136 +modified: 20180630170919859 +tags: WebServer +title: WebServer Parameters +type: text/vnd.tiddlywiki + +The following configuration parameters are supported by the integrated [[WebServer]]: + +<> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Routing.tid b/editions/tw5.com/tiddlers/webserver/WebServer Routing.tid new file mode 100644 index 000000000..8364ec379 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Routing.tid @@ -0,0 +1,6 @@ +created: 20180630194032981 +modified: 20180630194042074 +tags: WebServer +title: WebServer Routing +type: text/vnd.tiddlywiki + diff --git a/editions/tw5.com/tiddlers/webserver/WebServer.tid b/editions/tw5.com/tiddlers/webserver/WebServer.tid new file mode 100644 index 000000000..f32113630 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer.tid @@ -0,0 +1,64 @@ +created: 20180626150526207 +modified: 20180703095555387 +tags: ListenCommand ServerCommand Features +title: WebServer +type: text/vnd.tiddlywiki + +When [[running under Node.js|TiddlyWiki on Node.js]], TiddlyWiki includes a simple HTTP/HTTPS web server that allows you to use it from any browser running on the same machine or over a network. + +<<.tip """The web server includes a very simple mechanism allowing multiple users to log in with different credentials. The implementation is designed to be simple and easy to use, and would not generally be considered robust enough for use on the open internet. It is intended for use by individuals or small groups on a trusted network. It is recommended to use an external proxy before exposing it on the Internet.""">> + +! How It Works + +The web server listens for requests coming over the network, and performs the following actions in turn: + +* [[Authentication|WebServer Authentication]] is the process of identifying the current user. TiddlyWiki supports three types of authentication: [[Anonymous|WebServer Anonymous Access]], [[Basic|WebServer Basic Authentication]] and [[Header|WebServer Header Authentication]] +* [[Authorization|WebServer Authorization]] is the process of determining which resources may be accessed by a particular user. TiddlyWiki implements a simple scheme whereby read and write access to the wiki can be independently controlled. +* [[Routing|WebServer Routing]] is the process of acting on the request, and returning any required data. + +! Usage + +!! Anonymous Access + +The web server is started with the ListenCommand (which supersedes the older ServerCommand). All +the NamedCommandParameters are optional, so the simplest form is: + +``` +tiddlywiki mywikifolder --listen +``` + +Visit http://127.0.0.1:8080/ to access the wiki. Access is anonymous, so anyone can read or write to the wiki. + +!! Authenticated Access + +Adding [[username|WebServer Parameter: username]] and [[password|WebServer Parameter: password]] parameters enforces basic authentication for both reading and writing: + +``` +tiddlywiki mywikifolder --listen username=test password=tset +``` + +Visiting the wiki will prompt for a username and password, and access is denied if they do not match the provided credentials. + +!! Anonymous Read, Authenticated Write + +This example adds the [[authorization|WebServer Authorization]] parameters [[readers|WebServer Parameter: readers]] and [[writers|WebServer Parameter: writers]] to grant read access to anonymous users, but require authentication as "joe" in order to gain write access. + +> Note that anonymous users can trigger a username/password prompt by visiting the route `\login-basic` (eg http://127.0.0.1:8080/login-basic). + +``` +tiddlywiki mywikifolder --listen "readers=(anon)" writers=joe username=joe password=bloggs +``` + +Note the double quotes that are required for parameters containing special characters. + +! Arguments + +The full list of available optional parameters is: + +<> + +! Guides + +Further information on usage of the integrated [[WebServer]]: + +<> diff --git a/plugins/tiddlywiki/tiddlyweb/readonly-styles.tid b/plugins/tiddlywiki/tiddlyweb/readonly-styles.tid new file mode 100644 index 000000000..ab5da0e12 --- /dev/null +++ b/plugins/tiddlywiki/tiddlyweb/readonly-styles.tid @@ -0,0 +1,27 @@ +title: $:/plugins/tiddlywiki/tiddlyweb/readonly +tags: [[$:/tags/Stylesheet]] + +\define button-selector(title) +button.$title$, .tc-drop-down button.$title$, div.$title$ +\end + +\define hide-edit-controls() +<$reveal state="$:/status/IsReadOnly" type="match" text="yes" default="yes"> +<>`,` +<>`,` +<>`,` +<>`,` +<>`,` +<>`,` +<>`,` +<>`,` +<>`,` +<> `{ + display: none; +}` + +\end + +\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock + +<> diff --git a/plugins/tiddlywiki/tiddlyweb/tiddlywebadaptor.js b/plugins/tiddlywiki/tiddlyweb/tiddlywebadaptor.js index f3cdde939..492909324 100644 --- a/plugins/tiddlywiki/tiddlyweb/tiddlywebadaptor.js +++ b/plugins/tiddlywiki/tiddlyweb/tiddlywebadaptor.js @@ -21,6 +21,8 @@ function TiddlyWebAdaptor(options) { this.recipe = undefined; this.hasStatus = false; this.logger = new $tw.utils.Logger("TiddlyWebAdaptor"); + this.isLoggedIn = false; + this.isReadOnly = false; } TiddlyWebAdaptor.prototype.name = "tiddlyweb"; @@ -63,8 +65,7 @@ TiddlyWebAdaptor.prototype.getStatus = function(callback) { return callback(err); } // Decode the status JSON - var json = null, - isLoggedIn = false; + var json = null; try { json = JSON.parse(data); } catch (e) { @@ -76,11 +77,13 @@ TiddlyWebAdaptor.prototype.getStatus = function(callback) { self.recipe = json.space.recipe; } // Check if we're logged in - isLoggedIn = json.username !== "GUEST"; + self.isLoggedIn = json.username !== "GUEST"; + self.isReadOnly = !!json["read_only"]; + self.isAnonymous = !!json.anonymous; } // Invoke the callback if present if(callback) { - callback(null,isLoggedIn,json.username); + callback(null,self.isLoggedIn,json.username,self.isReadOnly,self.isAnonymous); } } }); @@ -165,6 +168,9 @@ Save a tiddler and invoke the callback with (err,adaptorInfo,revision) */ TiddlyWebAdaptor.prototype.saveTiddler = function(tiddler,callback) { var self = this; + if(this.isReadOnly) { + return callback(null); + } $tw.utils.httpRequest({ url: this.host + "recipes/" + encodeURIComponent(this.recipe) + "/tiddlers/" + encodeURIComponent(tiddler.fields.title), type: "PUT", @@ -209,9 +215,12 @@ options include: tiddlerInfo: the syncer's tiddlerInfo for this tiddler */ TiddlyWebAdaptor.prototype.deleteTiddler = function(title,callback,options) { - var self = this, - bag = options.tiddlerInfo.adaptorInfo.bag; + var self = this; + if(this.isReadOnly) { + return callback(null); + } // If we don't have a bag it means that the tiddler hasn't been seen by the server, so we don't need to delete it + var bag = options.tiddlerInfo.adaptorInfo.bag; if(!bag) { return callback(null); }