diff --git a/server/camlistored/newui/debug.html b/server/camlistored/newui/debug.html new file mode 100644 index 000000000..4f8896c92 --- /dev/null +++ b/server/camlistored/newui/debug.html @@ -0,0 +1,52 @@ + + + + Camlistored UI + + + + + + + + + + + + +
+

Root Discovery

+

+
(discovery results)
+ +

Signing Discovery

+

+
(jsonsign discovery results)
+ +

Signing Debug

+ + + + + + + + + + + + + + + +
JSON blob to sign: Signed blob:Verification details:
+
+ + + + diff --git a/server/camlistored/newui/server_connection.js b/server/camlistored/newui/server_connection.js index 07e0f495f..c973239e8 100644 --- a/server/camlistored/newui/server_connection.js +++ b/server/camlistored/newui/server_connection.js @@ -125,6 +125,23 @@ function(success, fail, e) { success(result); }; +/** + * @param {Function} success callback with data. + * @param {?Function} opt_fail optional failure calback + */ +camlistore.ServerConnection.prototype.discoSignRoot = +function(success, opt_fail) { + var path = goog.uri.utils.appendPath( + this.config_.jsonSignRoot, '/camli/sig/discovery' + ); + + this.sendXhr_(path, + goog.bind(this.handleXhrResponseJson_, this, + success, this.safeFail_(opt_fail) + ) + ); +}; + /** * @param {Function} success Success callback. * @param {?Function} opt_fail Optional fail callback. @@ -287,7 +304,7 @@ function(clearObj, success, opt_fail) { } clearObj.camliSigner = sigConf.publicKeyBlobRef; - clearText = JSON.stringify(clearObj, null, 2); + clearText = JSON.stringify(clearObj); this.sendXhr_( sigConf.signHandler, @@ -299,6 +316,31 @@ function(clearObj, success, opt_fail) { ); }; +/** + * @param {Object} sObj Signed object. + * @param {Function} success Success callback. + * @param {?Function} opt_fail Optional fail callback. + * @private + */ +camlistore.ServerConnection.prototype.verify_ = +function(sObj, success, opt_fail) { + var sigConf = this.config_.signing; + if (!sigConf || !sigConf.publicKeyBlobRef) { + this.safeFail_(opt_fail)("Missing Camli.config.signing.publicKeyBlobRef"); + return; + } + + var clearText = JSON.stringify(sObj); + + this.sendXhr_( + sigConf.verifyHandler, + goog.bind(this.handlePost_, this, + success, this.safeFail_(opt_fail)), + "POST", + "sjson=" + encodeURIComponent(clearText), + {"Content-Type": "application/x-www-form-urlencoded"} + ); +}; /** * @param {Function} success Success callback. diff --git a/server/camlistored/newui/sigdebug.js b/server/camlistored/newui/sigdebug.js new file mode 100644 index 000000000..10750e2e8 --- /dev/null +++ b/server/camlistored/newui/sigdebug.js @@ -0,0 +1,185 @@ +/* +Copyright 2011 Google Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/** + * @fileoverview Discovery Debug page. + * + */ +goog.provide('camlistore.DebugPage'); + +goog.require('goog.dom'); +goog.require('goog.events.EventType'); +goog.require('goog.ui.Component'); +goog.require('camlistore.ServerConnection'); + + +// TODO(mpl): add button on index page (toolbar?) to come here. +/** + * @param {camlistore.ServerType.DiscoveryDocument} config Global config + * of the current server this page is being rendered for. + * @param {goog.dom.DomHelper=} opt_domHelper DOM helper to use. + * + * @extends {goog.ui.Component} + * @constructor + */ +camlistore.DebugPage = function(config, opt_domHelper) { + goog.base(this, opt_domHelper); + + /** + * @type {Object} + * @private + */ + this.config_ = config; + + /** + * @type {Object} + * @private + */ + this.sigdisco_ = null; + + /** + * @type {camlistore.ServerConnection} + * @private + */ + this.connection_ = new camlistore.ServerConnection(config); + +}; +goog.inherits(camlistore.DebugPage, goog.ui.Component); + +/** + * Called when component's element is known to be in the document. + */ +camlistore.DebugPage.prototype.enterDocument = function() { + camlistore.DebugPage.superClass_.enterDocument.call(this); + + // set up listeners + goog.events.listen(goog.dom.getElement('discobtn'), + goog.events.EventType.CLICK, + this.discoRoot_, + false, this); + goog.events.listen(goog.dom.getElement('sigdiscobtn'), + goog.events.EventType.CLICK, + this.discoJsonSignRoot_, + false, this); + goog.events.listen(goog.dom.getElement('addkeyref'), + goog.events.EventType.CLICK, + this.addKeyRef_, + false, this); + goog.events.listen(goog.dom.getElement('sign'), + goog.events.EventType.CLICK, + this.doSign_, + false, this); + goog.events.listen(goog.dom.getElement('verify'), + goog.events.EventType.CLICK, + this.doVerify_, + false, this); +}; + + +/** + * Called when component's element is known to have been removed from the + * document. + */ +camlistore.DebugPage.prototype.exitDocument = function() { + camlistore.DebugPage.superClass_.exitDocument.call(this); +}; + + +/** + * Fake. We just get the info from the initial config. + * @param {goog.events.Event} e The title form submit event. + * @private + */ +camlistore.DebugPage.prototype.discoRoot_ = function(e) { + var disco = "
" + JSON.stringify(this.config_, null, 2) + "
"; + goog.dom.getElement("discores").innerHTML = disco; +}; + + +/** + * @private + */ +camlistore.DebugPage.prototype.discoJsonSignRoot_ = function() { + this.connection_.discoSignRoot( + goog.bind(function(sigdisco) { + this.sigdisco_ = sigdisco; + var disco = "
" + JSON.stringify(sigdisco, null, 2) + "
"; + goog.dom.getElement("sigdiscores").innerHTML = disco; + }, this) + ) +}; + + +/** + * @private + */ +camlistore.DebugPage.prototype.addKeyRef_ = function() { + if (!this.sigdisco_) { + alert("must do jsonsign discovery first"); + return; + } + var clearta = goog.dom.getElement("clearjson"); + var j; + try { + j = JSON.parse(clearta.value); + } catch (x) { + alert(x); + return + } + j.camliSigner = this.sigdisco_.publicKeyBlobRef; + clearta.value = JSON.stringify(j, null, 2); +} + +/** + * @private + */ +camlistore.DebugPage.prototype.doSign_ = function() { + // we actually do not need sigdisco since sign_ will pull + // all the needed info from the config_ instead. But I'm + // leaving the check as the debug check is also a sort of demo. + if (!this.sigdisco_) { + alert("must do jsonsign discovery first"); + return; + } + var clearta = goog.dom.getElement("clearjson"); + var clearObj = JSON.parse(clearta.value); + this.connection_.sign_(clearObj, + function(response) { + goog.dom.getElement("signedjson").value = response; + } + ) +} + +/** + * @private + */ +camlistore.DebugPage.prototype.doVerify_ = function() { + // we actually do not need sigdisco since sign_ will pull + // all the needed info from the config_ instead. But I'm + // leaving the check as the debug check is also a sort of demo. + if (!this.sigdisco_) { + alert("must do jsonsign discovery first"); + return; + } + var signedta = goog.dom.getElement("signedjson"); + var sObj = JSON.parse(signedta.value); + this.connection_.verify_(sObj, + function(response) { + var text = "
" + response + "
"; + goog.dom.getElement("verifyinfo").innerHTML = text; + } + ) +}