1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-23 10:07:19 +00:00

Restore encrypted TiddlyWiki support

This commit is contained in:
Jeremy Ruston 2013-01-31 10:20:13 +00:00
parent d4da6d4ced
commit a9de17bd55
10 changed files with 145 additions and 37 deletions

23
bld.sh
View File

@ -21,28 +21,35 @@ echo "Using TW5_BUILD_OUTPUT as [$TW5_BUILD_OUTPUT]"
echo "five.tiddlywiki.com" > $TW5_BUILD_OUTPUT/CNAME echo "five.tiddlywiki.com" > $TW5_BUILD_OUTPUT/CNAME
# First, index.html: the main file, including content # First,
# readme.md: the readme file for GitHub
# index.html: the main file, including content
# static.html: the static version of the default tiddlers
node ../../tiddlywiki.js \
--verbose \
--savetiddler ReadMe ../../readme.md text/html \
--savetiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/index.html text/plain \
--savetiddler $:/core/templates/static.template.html $TW5_BUILD_OUTPUT/static.html text/plain \
|| exit 1
# Second, encrypted.html: a version of the main file encrypted with the password "password"
node ../../tiddlywiki.js \ node ../../tiddlywiki.js \
--verbose \ --verbose \
--password password \ --password password \
--savetiddler ReadMe ../../readme.md text/html \ --savetiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/encrypted.html text/plain \
--savetiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/index.html text/plain \
--savetiddler $:/core/templates/tiddlywiki5.encrypted.template.html $TW5_BUILD_OUTPUT/encrypted.html text/plain \
--savetiddler $:/core/templates/static.template.html $TW5_BUILD_OUTPUT/static.html text/plain \
|| exit 1 || exit 1
popd > /dev/null popd > /dev/null
# Second, empty.html: empty wiki for reuse # Third, empty.html: empty wiki for reuse
pushd editions/empty > /dev/null pushd editions/empty > /dev/null
node ../../tiddlywiki.js \ node ../../tiddlywiki.js \
--verbose \ --verbose \
--password password \
--savetiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/empty.html text/plain \ --savetiddler $:/core/templates/tiddlywiki5.template.html $TW5_BUILD_OUTPUT/empty.html text/plain \
--savetiddler $:/core/templates/tiddlywiki5.encrypted.template.html $TW5_BUILD_OUTPUT/empty_encrypted.html text/plain \
|| exit 1 || exit 1
popd > /dev/null popd > /dev/null

View File

@ -358,7 +358,10 @@ $tw.utils.PasswordPrompt.prototype.createPrompt = function(options) {
this.setWrapperDisplay(); this.setWrapperDisplay();
}; };
// Crypto helper object for encrypted content /*
Crypto helper object for encrypted content. It maintains the password text in a closure, and provides methods to change
the password, and to encrypt/decrypt a block of text
*/
$tw.utils.Crypto = function() { $tw.utils.Crypto = function() {
var sjcl = $tw.browser ? window.sjcl : require("./sjcl.js"), var sjcl = $tw.browser ? window.sjcl : require("./sjcl.js"),
password = null, password = null,
@ -374,7 +377,14 @@ $tw.utils.Crypto = function() {
}; };
this.setPassword = function(newPassword) { this.setPassword = function(newPassword) {
password = newPassword; password = newPassword;
this.updateCryptoStateTiddler();
}; };
this.updateCryptoStateTiddler = function() {
$tw.wiki.addTiddler(new $tw.Tiddler({title: "$:/isEncrypted", text: password ? "yes" : "no"}));
};
this.hasPassword = function() {
return !!password;
}
this.encrypt = function(text) { this.encrypt = function(text) {
return callSjcl("encrypt",text); return callSjcl("encrypt",text);
}; };
@ -723,22 +733,13 @@ $tw.wiki = new $tw.Wiki();
if($tw.browser) { if($tw.browser) {
/* /*
Get any encrypted tiddlers Decrypt any tiddlers stored within the element with the ID "encryptedArea". The function is asynchronous to allow the user to be prompted for a password
callback: function to be called the decryption is complete
*/ */
$tw.boot.decryptEncryptedTiddlers = function(callback) { $tw.boot.decryptEncryptedTiddlers = function(callback) {
var encryptedArea = document.getElementById("encryptedArea"), var encryptedArea = document.getElementById("encryptedStoreArea");
encryptedTiddlers = [];
if(encryptedArea) { if(encryptedArea) {
for(var t = 0; t <encryptedArea.childNodes.length; t++) { var encryptedText = encryptedArea.innerHTML;
var childNode = encryptedArea.childNodes[t];
if(childNode.hasAttribute && childNode.hasAttribute("data-tw-encrypted-tiddlers")) {
var e = childNode.firstChild;
while(e && e.nodeName.toLowerCase() !== "pre") {
e = e.nextSibling;
}
encryptedTiddlers.push($tw.utils.htmlDecode(e.innerHTML));
}
}
// Prompt for the password // Prompt for the password
$tw.passwordPrompt.createPrompt({ $tw.passwordPrompt.createPrompt({
serviceName: "Enter a password to decrypt this TiddlyWiki", serviceName: "Enter a password to decrypt this TiddlyWiki",
@ -747,18 +748,12 @@ $tw.boot.decryptEncryptedTiddlers = function(callback) {
callback: function(data) { callback: function(data) {
// Attempt to decrypt the tiddlers // Attempt to decrypt the tiddlers
$tw.crypto.setPassword(data.password); $tw.crypto.setPassword(data.password);
for(var t=encryptedTiddlers.length-1; t>=0; t--) { var decryptedText = $tw.crypto.decrypt(encryptedText);
var decrypted = $tw.crypto.decrypt(encryptedTiddlers[t]); if(decryptedText) {
if(decrypted) { var json = JSON.parse(decryptedText);
var json = JSON.parse(decrypted); for(var title in json) {
for(var title in json) { $tw.preloadTiddler(json[title]);
$tw.preloadTiddler(json[title]);
}
encryptedTiddlers.splice(t,1);
} }
}
// Check if we're all done
if(encryptedTiddlers.length === 0) {
// Call the callback // Call the callback
callback(); callback();
// Exit and remove the password prompt // Exit and remove the password prompt
@ -770,7 +765,7 @@ $tw.boot.decryptEncryptedTiddlers = function(callback) {
} }
}); });
} else { } else {
// Just invoke the callback straight away if there wasn't any encrypted tiddlers // Just invoke the callback straight away if there weren't any encrypted tiddlers
callback(); callback();
} }
}; };
@ -1094,6 +1089,8 @@ $tw.boot.startup = function() {
$tw.wiki.defineTiddlerModules(); $tw.wiki.defineTiddlerModules();
// And any modules within bundles // And any modules within bundles
$tw.wiki.defineBundledModules(); $tw.wiki.defineBundledModules();
// Make sure the crypto state tiddler is up to date
$tw.crypto.updateCryptoStateTiddler();
// Run any startup modules // Run any startup modules
$tw.modules.forEachModuleOfType("startup",function(title,module) { $tw.modules.forEachModuleOfType("startup",function(title,module) {
if(module.startup) { if(module.startup) {

View File

@ -82,6 +82,21 @@ exports.startup = function() {
downloadType: "text/plain" downloadType: "text/plain"
}); });
},false); },false);
// Install the crypto event handler
document.addEventListener("tw-set-password",function(event) {
$tw.passwordPrompt.createPrompt({
serviceName: "Set new password for this TiddlyWiki",
noUserName: true,
submitText: "Set password",
callback: function(data) {
$tw.crypto.setPassword(data.password);
return true; // Get rid of the password prompt
}
});
});
document.addEventListener("tw-clear-password",function(event) {
$tw.crypto.setPassword(null);
});
// Apply stylesheets // Apply stylesheets
var styleTiddlers = $tw.wiki.getTiddlersWithTag("$:/core/styles"); var styleTiddlers = $tw.wiki.getTiddlersWithTag("$:/core/styles");
$tw.utils.each(styleTiddlers,function(title) { $tw.utils.each(styleTiddlers,function(title) {

View File

@ -0,0 +1,51 @@
/*\
title: $:/core/modules/widget/encrypt.js
type: application/javascript
module-type: widget
Implements the encrypt widget.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var EncryptWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
EncryptWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.filter = this.renderer.getAttribute("filter");
// Check whether we've got an encryption password
var isEncrypted = $tw.crypto.hasPassword();
// Encrypt the filtered tiddlers
var tiddlers = this.renderer.renderTree.wiki.filterTiddlers(this.filter),
json = {},
self = this;
$tw.utils.each(tiddlers,function(title) {
var tiddler = self.renderer.renderTree.wiki.getTiddler(title),
jsonTiddler = {};
for(var f in tiddler.fields) {
jsonTiddler[f] = tiddler.getFieldString(f);
}
json[title] = jsonTiddler;
});
var encryptedText = $tw.utils.htmlEncode($tw.crypto.encrypt(JSON.stringify(json)));
// Set the return element
this.tag = "pre";
this.attributes ={"class": "tw-encrypt"};
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,[{
type: "text",
text: encryptedText
}]);
};
exports.encrypt = EncryptWidget;
})();

View File

@ -5,6 +5,7 @@ title: $:/templates/PageTemplate
<!-- The page header --> <!-- The page header -->
<div class="pull-right"> <div class="pull-right">
{{$:/snippets/encryptionstatus}}
<$button message="tw-NewTiddler" class="btn btn-mini btn-success">New</$button> <$button message="tw-NewTiddler" class="btn btn-mini btn-success">New</$button>
<$button message="tw-save-wiki" class="btn btn-mini btn-primary">Save</$button> <$button message="tw-save-wiki" class="btn btn-mini btn-primary">Save</$button>
</div> </div>

View File

@ -0,0 +1,13 @@
title: $:/core/templates/store.area.template.html
<$reveal type="nomatch" state="$:/isEncrypted" text="yes">
`<div id="storeArea" style="display:none;">`
{{{ [!is[shadow]] ||$:/core/templates/html-div-tiddler}}}
`</div>`
</$reveal>
<$reveal type="match" state="$:/isEncrypted" text="yes">
`<!------------- Encrypted tiddlers --------->`
`<pre id="encryptedStoreArea" type="text/plain" style="display:none;">`
<$encrypt filter="[!is[shadow]]"/>
`</pre>`
</$reveal>

View File

@ -34,9 +34,7 @@ title: $:/core/templates/tiddlywiki5.template.html
{{{ [is[shadow]] -[type[text/css]] -[type[application/javascript]has[module-type]] -[type[application/javascript]library[yes]] -[[$:/core/boot.js]] -[[$:/core/bootprefix.js]] ||$:/core/templates/html-div-tiddler}}} {{{ [is[shadow]] -[type[text/css]] -[type[application/javascript]has[module-type]] -[type[application/javascript]library[yes]] -[[$:/core/boot.js]] -[[$:/core/bootprefix.js]] ||$:/core/templates/html-div-tiddler}}}
</div> </div>
<!----------- Ordinary tiddlers -----------> <!----------- Ordinary tiddlers ----------->
<div id="storeArea" style="display:none;"> {{$:/core/templates/store.area.template.html}}
{{{ [!is[shadow]] ||$:/core/templates/html-div-tiddler}}}
</div>
<!----------- Library modules -----------> <!----------- Library modules ----------->
<div id="libraryModules" style="display:none;"> <div id="libraryModules" style="display:none;">
{{$:/core/lib/jquery.min.js||$:/core/templates/javascript-tiddler}} {{$:/core/lib/jquery.min.js||$:/core/templates/javascript-tiddler}}

View File

@ -0,0 +1,11 @@
title: $:/snippets/encryptionstatus
<$reveal type="match" state="$:/isEncrypted" text="yes">
This wiki is encrypted.
<$button message="tw-clear-password">Clear password</$button>
<$button message="tw-set-password">Change password</$button>
</$reveal>
<$reveal type="nomatch" state="$:/isEncrypted" text="yes">
This wiki is not encrypted.
<$button message="tw-set-password">Set password</$button>
</$reveal>

View File

@ -28,6 +28,7 @@ Internally, TiddlyWiki is built on a number of key objects and mechanisms:
* SyncMechanism * SyncMechanism
* CommandMechanism * CommandMechanism
* ConfigMechanism * ConfigMechanism
* EncryptionMechanism
! Plugin Module Types ! Plugin Module Types

View File

@ -0,0 +1,14 @@
title: EncryptionMechanism
tags: docs mechanism
TiddlyWiki5 allows the entire content of a TiddlyWiki HTML file to be encrypted with the Stanford JavaScript Crypto Library. Opening an encrypted TiddlyWiki in the browser prompts for a password before decrypting and displaying the content.
The EncryptionMechanism is implemented with the following elements:
* A crypto "password vault" within the BootMechanism that holds the current encryption password
* The ability of the BootMechanism to read a block of encrypted tiddlers from the TiddlyWiki file, to prompt the user for a password, and to decrypt the tiddlers
* Handlers for the messages SetPasswordMessage and ClearPasswordMessage that handle the user interface for password changes
* The `<$encrypt>` widget within the main file template that encrypts a filtered list of tiddlers with the currently held password
* The [[$:/isEncrypted]] tiddler that contains "yes" or "no" according to whether there is a password in the password vault
** The availability of this tiddler allows the RevealWidget to be used to selectively display user interface elements according to whether encryption is in force
* The [[$:/snippets/encryptionstatus]] snippet that displays the current encryption status