Remove the web console EPP endpoint

This removes the "create Domain/Host/Contact" forms that were supposed to be used instead of regular EPPs for CC-TLD that wanted to support it.

We're removing it because we don't use it and want to reduce unneeded code for the registry 3.0 migration.

Also, this is a security risk, as it allowed to do "billable actions" (creating a new domain for example) with the only authentication being access to the registrar's G Suite account.

This bypassed the certificate, IP whitelist, and EPP password, which is bad.

PUBLIC:
Remove the web console EPP endpoint

This removes the "create Domain/Host/Contact" forms that were supposed to be used instead of regular EPPs for CC-TLD that wanted to support it.

We're removing it because we don't use it and want to reduce unneeded code for the registry 3.0 migration.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=236244195
This commit is contained in:
guyben 2019-02-28 19:37:30 -08:00 committed by Weimin Yu
parent f12d368da3
commit dfad79759e
52 changed files with 58 additions and 3788 deletions

View file

@ -14,13 +14,7 @@
goog.provide('registry.Component');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.dom.classlist');
goog.require('goog.events');
goog.require('goog.events.EventHandler');
goog.require('goog.events.EventType');
goog.require('registry.soy.forms');
goog.require('registry.util');
goog.forwardDeclare('registry.Console');
@ -38,16 +32,9 @@ goog.forwardDeclare('registry.Console');
* ^
* \
* |-ui/js/resource_component.js - JSON resources
* | ^
* | \
* | |- ui/js/registrar/settings.js
* |
* |-ui/js/registrar/xml_resource_component.js - EPP resources
* ^
* \
* |- ui/js/registrar/contact.js
* |- ui/js/registrar/domain.js
* |- ui/js/registrar/host.js
* ^
* \
* |- ui/js/registrar/settings.js
* </pre>
*
* @param {!registry.Console} cons the console singleton.
@ -91,122 +78,3 @@ goog.inherits(registry.Component, goog.events.EventHandler);
registry.Component.prototype.bindToDom = function(id) {
registry.util.unbutter();
};
// XXX: Should clean up the many remove button handler setup funcs.
/**
* Shared functionality for contact and host add button click events
* to add form elements for inputing a new item name. Requires the
* template to have:
*
* <ul>
* <li>an add button with id="domain-[type]-add-button".
* <li>a table row with id="domain-[type]s-footer".
* </ul>
*
* @param {string} type e.g. 'contact', 'host'.
* @param {function(): string} newFieldNameFn generates new form field's name.
* @param {function(string): (?Element)=} opt_onAddFn currying further setup.
* @param {function()=} opt_tmpl input element template.
* @param {!Object=} opt_tmplArgs input element template parameter object.
* @param {boolean=} opt_disable optionally disable the add button.
* @protected
*/
registry.Component.prototype.addRemBtnHandlers = function(
type,
newFieldNameFn,
opt_onAddFn,
opt_tmpl,
opt_tmplArgs,
opt_disable) {
var addBtnId = 'domain-' + type + '-add-button';
var addBtn = goog.dom.getRequiredElement(addBtnId);
if (!opt_disable) {
addBtn.removeAttribute('disabled');
}
var addButtonClickCallback = function() {
var fieldElts = [];
var newFieldName = newFieldNameFn();
var tmpl =
opt_tmpl ? opt_tmpl : registry.soy.forms.inputFieldRow;
var tmplArgs = opt_tmplArgs ? opt_tmplArgs : {
label: 'New ' + type,
name: newFieldName + '.value'
};
var newFieldInputRow = registry.util.renderBeforeRow(
'domain-' + type + 's-footer', tmpl, tmplArgs);
fieldElts.push(newFieldInputRow);
// Save the add/rem op type as a hidden elt for use by
// determine EPP add/remove semantics in subclasses.
// e.g. domain.js#saveItem()
var opElt = goog.dom.createDom(goog.dom.TagName.INPUT, {
'type': 'hidden',
'name': newFieldName + '.op',
'value': 'add'
});
newFieldInputRow.lastChild.appendChild(opElt);
if (opt_onAddFn) {
var elt = opt_onAddFn(newFieldName);
if (elt) {
fieldElts.push(elt);
}
}
this.appendRemoveBtn(
goog.dom.getLastElementChild(newFieldInputRow),
fieldElts,
goog.bind(function() {
this.typeCounts[type]--;
}, this));
this.typeCounts[type]++;
};
goog.events.listen(goog.dom.getRequiredElement(addBtnId),
goog.events.EventType.CLICK,
goog.bind(addButtonClickCallback, this));
};
/**
* Helper for making an element removable.
* @param {Element} parent The element to append the remove button to.
* @param {(Array.<Element>|function())=} opt_eltsToRemove
* Elements to remove when the button is clicked or Function to do
* the removing for full customization.
* @param {function()=} opt_cb callback will be called if no
* customized function is given.
*/
registry.Component.prototype.appendRemoveBtn =
function(parent, opt_eltsToRemove, opt_cb) {
var rmBtn = goog.dom.createDom(goog.dom.TagName.BUTTON,
goog.getCssName('kd-button'),
'Remove');
goog.dom.appendChild(parent, rmBtn);
var clickCb;
if (opt_eltsToRemove instanceof Function) {
clickCb = opt_eltsToRemove;
} else {
var eltsToRemove = opt_eltsToRemove ? opt_eltsToRemove : [parent];
clickCb = function() {
for (var i = 0; i < eltsToRemove.length; i++) {
goog.dom.removeNode(eltsToRemove[i]);
}
if (opt_cb) {
opt_cb();
}
};
}
goog.events.listen(rmBtn, goog.events.EventType.CLICK, clickCb, true);
};
/**
* Bind the remove button action for the given container element.
* @param {!Element} containerElt
*/
registry.Component.prototype.enableRemoveButton = function(containerElt) {
var rmBtn = goog.dom.getElementByClass(
goog.getCssName('remove'), containerElt);
goog.dom.classlist.toggle(rmBtn, goog.getCssName('hidden'));
goog.events.listen(rmBtn, goog.events.EventType.CLICK, function() {
goog.dom.removeNode(goog.dom.getParentElement(rmBtn));
});
};

View file

@ -24,17 +24,15 @@ goog.require('goog.history.EventType');
goog.require('registry.util');
goog.forwardDeclare('goog.events.KeyEvent');
goog.forwardDeclare('registry.Session');
/**
* Abstract console for both admin and registrar console UIs.
* @param {?registry.Session} session server request session.
* @constructor
* @extends {goog.Disposable}
*/
registry.Console = function(session) {
registry.Console = function() {
registry.Console.base(this, 'constructor');
/**
@ -42,19 +40,37 @@ registry.Console = function(session) {
* @protected
*/
this.history = new goog.History();
};
goog.inherits(registry.Console, goog.Disposable);
/**
* Registers the console's events and sets everything up.
*
* Should be called after the constructor.
*
* The reason this isn't done in the constructor is that this is a base class
* designed to be extended. We have to wait for the actual implementation to
* finish constructing before using it.
*/
registry.Console.prototype.setUp = function() {
goog.events.listen(
this.history,
goog.history.EventType.NAVIGATE,
goog.bind(this.handleHashChange, this));
/**
* @type {?registry.Session} The server session.
*/
this.session = session;
this.bindToDom();
// goog.History always starts off as "not enabled", meaning it doesn't trigger
// the listeners on change.
//
// When it's set to be enabled, it will start triggering the listeners on
// every change, but it also triggers the listeners immediately with the
// current history entry.
//
// This means the handleHashChange listener registered above will be called
// now.
this.history.setEnabled(true);
};
goog.inherits(registry.Console, goog.Disposable);
/**
@ -86,6 +102,8 @@ registry.Console.prototype.handleHashChange = goog.abstractMethod;
* @param {string} resourcePath Resource description path.
*/
registry.Console.prototype.view = function(resourcePath) {
// Setting the new history token will also trigger the handleHashChange
// listener registered in the setUp() function.
this.history.setToken(resourcePath);
};

View file

@ -305,9 +305,9 @@ registry.EditItem.prototype.prepareUpdate = goog.abstractMethod;
/**
* Subclasses should provide a function to parse either XML or JSON response
* from server and return a result object as described below.
* @param {!Object} rsp Decoded XML/JSON response from the server.
* Subclasses should provide a function to parse JSON response from server and
* return a result object as described below.
* @param {!Object} rsp Decoded JSON response from the server.
* @return {!Object} a result object describing next steps. On
* success, if next is defined, visit(ret.next) is called, otherwise
* if err is set, the butterbar message is set to it.

View file

@ -22,13 +22,9 @@ goog.require('goog.net.XhrIo');
goog.require('registry.Console');
goog.require('registry.Resource');
goog.require('registry.registrar.AdminSettings');
goog.require('registry.registrar.Contact');
goog.require('registry.registrar.ContactSettings');
goog.require('registry.registrar.ContactUs');
goog.require('registry.registrar.Dashboard');
goog.require('registry.registrar.Domain');
goog.require('registry.registrar.EppSession');
goog.require('registry.registrar.Host');
goog.require('registry.registrar.Resources');
goog.require('registry.registrar.SecuritySettings');
goog.require('registry.registrar.WhoisSettings');
@ -49,16 +45,13 @@ goog.forwardDeclare('registry.Component');
* @final
*/
registry.registrar.Console = function(params) {
registry.registrar.Console.base(this, 'constructor');
/**
* @type {!Object}
*/
// We have to define this before creating an EppSession because EppSession's
// constructor expects us to have it as an attribute.
this.params = params;
registry.registrar.Console.base(
this, 'constructor', new registry.registrar.EppSession(this));
/**
* Component that's currently embedded in the page.
* @type {?registry.Component}
@ -66,13 +59,9 @@ registry.registrar.Console = function(params) {
*/
this.component_ = null;
// XXX: This was in parent ctor but was triggering event dispatching before
// ready here.
this.history.setEnabled(true);
/**
* Last active nav element.
* @type {Element}
* @type {?Element}
*/
this.lastActiveNavElt;
@ -107,13 +96,6 @@ registry.registrar.Console = function(params) {
if (this.params.isAdmin) {
this.pageMap['admin-settings'] = registry.registrar.AdminSettings;
}
// sending EPPs through the console. Currently hidden (doesn't have a "tab")
// but still accessible if the user manually puts #domain (or other) in the
// fragment
this.pageMap['contact'] = registry.registrar.Contact;
this.pageMap['domain'] = registry.registrar.Domain;
this.pageMap['host'] = registry.registrar.Host;
};
goog.inherits(registry.registrar.Console, registry.Console);
@ -134,17 +116,7 @@ goog.inherits(registry.registrar.Console, registry.Console);
*/
registry.registrar.Console.prototype.handleHashChange = function() {
var hashToken = this.history.getToken();
// On page reloads, opening a new tab, etc. it's possible that the
// session cookie for a logged-in session exists, but the
// this.session is not yet aware, so come back here after syncing.
//
// XXX: Method should be refactored to avoid this 2-stage behavior.
if (!this.session.isEppLoggedIn()) {
this.session.login(goog.bind(this.handleHashChange, this));
return;
}
// Otherwise, a resource operation.
var parts = hashToken.split('/');
var type = '';
var id = '';

View file

@ -1,129 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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.
goog.provide('registry.registrar.Contact');
goog.require('goog.dom');
goog.require('registry.registrar.XmlResourceComponent');
goog.require('registry.soy.registrar.contact');
goog.require('registry.soy.registrar.contactepp');
goog.forwardDeclare('registry.registrar.Console');
/**
* The `Contact` class respresents a registry contact object and
* binds UI CRUD operations to it.
* @param {!registry.registrar.Console} console the
* console singleton.
* @constructor
* @extends {registry.registrar.XmlResourceComponent}
* @final
*/
registry.registrar.Contact = function(console) {
registry.registrar.Contact.base(
this, 'constructor',
/** @type {function()} */(registry.soy.registrar.contact.item),
registry.soy.registrar.contactepp,
console);
};
goog.inherits(registry.registrar.Contact,
registry.registrar.XmlResourceComponent);
/** @override */
registry.registrar.Contact.prototype.processItem = function() {
this.model.item = this.model['epp']['response']['resData']['contact:infData'];
if (!goog.isArray(this.model.item['contact:postalInfo'])) {
this.model.item['contact:postalInfo'] =
[this.model.item['contact:postalInfo']];
}
// XXX: Is this code necessary?
var fixPlus = function(val) {
var str = (val || '') + '';
if (str == '' || str.match(/\+.*/)) {
return str;
} else {
return '+' + str;
}
};
// Both of these are optional.
if (this.model.item['contact:voice']) {
this.model.item['contact:voice']['keyValue'] =
fixPlus(this.model.item['contact:voice']['keyValue']);
}
if (this.model.item['contact:voice']) {
this.model.item['contact:fax']['keyValue'] =
fixPlus(this.model.item['contact:fax']['keyValue']);
}
};
/** @override */
registry.registrar.Contact.prototype.setupEditor = function(objArgs) {
// For now always keep the first contact and make it i18n. Toggle button
// disables to enforce state.
//
// XXX: Should be simplified to make more modular.
var postalElt = goog.dom.getRequiredElement('contact-postalInfo');
var addPostalInfoBtn = goog.dom.getRequiredElement(
'domain-contact-postalInfo-add-button');
this.typeCounts['contact-postalInfo'] = postalElt.childNodes.length;
// 4 child nodes means both addresses are present:
// 2 data tables, the footer id elt and a hidden input.
var setupRemoveBtns = this.typeCounts['contact-postalInfo'] == 4;
if (setupRemoveBtns) {
this.appendRemoveBtn(/** @type {!Element} */ (postalElt.childNodes[0]));
this.appendRemoveBtn(/** @type {!Element} */ (postalElt.childNodes[1]));
} else {
addPostalInfoBtn.removeAttribute('disabled');
}
this.addRemBtnHandlers(
'contact-postalInfo',
function() {
return 'contact:postalInfo[1].contact:';
},
function() {
addPostalInfoBtn.setAttribute('disabled', true);
return null;
},
/** @type {function()} */ (registry.soy.registrar.contact.postalInfo),
{
item: {},
localized: true,
itemPrefix: 'contact:',
namePrefix: 'contact:postalInfo[1].contact:'
},
setupRemoveBtns);
};
/** @override */
registry.registrar.Contact.prototype.prepareCreate = function(params) {
params.nextId = params.item['contact:id'];
return registry.soy.registrar.contactepp.create(
/** @type {{clTrid: ?, item: ?}} */ (params)).toString();
};
/** @override */
registry.registrar.Contact.prototype.prepareUpdate = function(params) {
params.nextId = params.item['contact:id'];
return registry.soy.registrar.contactepp.update(
/** @type {{clTrid: ?, item: ?}} */(params)).toString();
};

View file

@ -221,7 +221,7 @@ registry.registrar.ContactSettings.prototype.prepareUpdate =
* Handler for contact save that navigates to that item on success.
* Does nothing on failure as UI will be left with error messages for
* the user to resolve.
* @param {!Object} rsp Decoded XML/JSON response from the server.
* @param {!Object} rsp Decoded JSON response from the server.
* @override
*/
registry.registrar.ContactSettings.prototype.handleCreateResponse =
@ -238,7 +238,7 @@ registry.registrar.ContactSettings.prototype.handleCreateResponse =
* Handler for contact delete that navigates back to the collection on success.
* Does nothing on failure as UI will be left with error messages for
* the user to resolve.
* @param {!Object} rsp Decoded XML/JSON response from the server.
* @param {!Object} rsp Decoded JSON response from the server.
* @override
*/
registry.registrar.ContactSettings.prototype.handleDeleteResponse =

View file

@ -40,10 +40,10 @@ registry.registrar.Dashboard = function(console) {
/** @private {number} */
this.x_ = 0;
/** @private {Element} */
/** @private {?Element} */
this.gear_ = null;
/** @private {goog.Timer} */
/** @private {?goog.Timer} */
this.timer_ = null;
};
goog.inherits(registry.registrar.Dashboard, registry.Component);
@ -63,14 +63,6 @@ registry.registrar.Dashboard.prototype.bindToDom = function(id) {
};
/**
* Do EPP logout.
*/
registry.registrar.Dashboard.prototype.doEppLogout = function() {
this.console.session.logout();
};
/**
* Let's do the twist.
* @private

View file

@ -1,167 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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.
goog.provide('registry.registrar.Domain');
goog.require('goog.json');
goog.require('registry.registrar.XmlResourceComponent');
goog.require('registry.soy.forms');
goog.require('registry.soy.registrar.domain');
goog.require('registry.soy.registrar.domainepp');
goog.require('registry.util');
goog.forwardDeclare('registry.registrar.Console');
/**
* CRUD for EPP domain objects.
* @param {!registry.registrar.Console} console
* @constructor
* @extends {registry.registrar.XmlResourceComponent}
* @final
*/
registry.registrar.Domain = function(console) {
registry.registrar.Domain.base(
this, 'constructor',
/** @type {function()} */ (registry.soy.registrar.domain.item),
registry.soy.registrar.domainepp,
console);
};
goog.inherits(registry.registrar.Domain,
registry.registrar.XmlResourceComponent);
/** @override */
registry.registrar.Domain.prototype.newModel = function() {
var newModel = {item: {'domain:period': ''}};
return newModel;
};
/**
* Prepare a fetch query for the domain by its id.
* @param {!Object} params should have a name field with a
* possibly extended domain name id of the form "example.tld:1234"
* where the 1234 is the domain application id assigned by the
* backend flows.
* @override
*/
registry.registrar.Domain.prototype.prepareFetch = function(params) {
return registry.soy.registrar.domainepp.info(
/** @type {{clTrid: ?, id: ?}} */ (params)).toString();
};
/** @override */
registry.registrar.Domain.prototype.processItem = function() {
this.model.item = this.model['epp']['response']['resData']['domain:infData'];
// Hoist extensions for easy soy access.
var extension = this.model['epp']['response']['extension'];
if (extension && extension['launch:infData']) {
var extendedInfData = extension['launch:infData'];
var mark = extendedInfData['mark:mark'];
if (mark) {
this.model.item['mark:mark'] = {'keyValue': goog.json.serialize(mark)};
}
}
// Wrap single item into an array.
if (this.model.item['domain:ns'] &&
this.model.item['domain:ns']['domain:hostObj']) {
var hostObj = this.model.item['domain:ns']['domain:hostObj'];
if (!goog.isArray(hostObj)) {
this.model.item['domain:ns']['domain:hostObj'] = [hostObj];
}
}
};
/** @override */
registry.registrar.Domain.prototype.setupEditor = function(objArgs) {
this.typeCounts['contact'] = objArgs.item['domain:contact'] ?
objArgs.item['domain:contact'].length : 0;
var ji = objArgs.item['domain:ns'];
this.typeCounts['host'] =
ji && ji['domain:hostObj'] ? ji['domain:hostObj'].length : 0;
this.formInputRowRemovable(
document.querySelectorAll('input.domain-hostObj[readonly]'));
this.formInputRowRemovable(
document.querySelectorAll('input.domain-contact[readonly]'));
this.addRemBtnHandlers(
'contact',
goog.bind(function() {
return 'domain:contact[' + this.typeCounts['contact'] + ']';
}, this),
goog.bind(function(newFieldName) {
return registry.util.renderBeforeRow(
'domain-contacts-footer',
/** @type {function()} */(registry.soy.forms.selectRow), {
label: 'Type',
name: newFieldName + '.@type',
options: ['admin', 'tech', 'billing']
});
}, this));
this.addRemBtnHandlers('host', goog.bind(function() {
return 'domain:ns.domain:hostObj[' + this.typeCounts['host'] + ']';
}, this));
};
/** @override */
registry.registrar.Domain.prototype.prepareCreate = function(params) {
var form = params.item;
params.nextId = form['domain:name'];
// The presence of this field is used to signal extended template, so remove
// if not used.
if (form['smd:encodedSignedMark'] == '') {
delete form['smd:encodedSignedMark'];
}
var xml = registry.soy.registrar.domainepp.create(
/** @type {{clTrid: ?, item: ?}} */(params));
return xml.toString();
};
/** @override */
registry.registrar.Domain.prototype.prepareUpdate =
function(params) {
var form = params.item;
var nextId = form['domain:name'];
params.nextId = nextId;
if (form['domain:contact']) {
this.addRem(form['domain:contact'], 'Contacts', params);
}
if (form['domain:ns'] && form['domain:ns']['domain:hostObj']) {
this.addRem(form['domain:ns']['domain:hostObj'], 'Hosts', params);
}
if (form['domain:ns'] && form['domain:ns']['domain:hostObj']) {
this.addRem(form['domain:ns']['domain:hostObj'], 'Hosts', params);
}
this.addRem(form['domain:contact'], 'Contacts', params);
var xml = registry.soy.registrar.domainepp.update(
/** @type {{clTrid: ?, item: ?}} */(params));
return xml.toString();
};

View file

@ -1,128 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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.
goog.provide('registry.registrar.EppSession');
goog.require('goog.Uri');
goog.require('registry.Session');
goog.require('registry.soy.registrar.epp');
goog.require('registry.util');
goog.require('registry.xml');
goog.forwardDeclare('registry.registrar.Console');
/**
* Session state for console.
* @param {!registry.registrar.Console} console
* @constructor
* @extends {registry.Session}
* @final
*/
registry.registrar.EppSession = function(console) {
registry.registrar.EppSession.base(
this, 'constructor',
new goog.Uri('/registrar-xhr')
.setParameterValue('clientId', console.params.clientId),
console.params.xsrfToken,
registry.Session.ContentType.EPP);
/**
* @type {!registry.registrar.Console}
*/
this.console = console;
/**
* @type {!boolean}
* @private
*/
this.isEppLoggedIn_ = false;
};
goog.inherits(registry.registrar.EppSession, registry.Session);
/**
* Whether the session has received an EPP success response to an EPP
* login attempt.
* @return {boolean} Whether the user is logged into an EPP session.
*/
registry.registrar.EppSession.prototype.isEppLoggedIn = function() {
return this.isEppLoggedIn_;
};
/**
* Get the clientId if the user is logged in, or throw Error.
* @throws Error if the user is not logged in.
* @return {string} the clientId.
*/
registry.registrar.EppSession.prototype.getClientId = function() {
return this.console.params.clientId;
};
/**
* Login or display butterbar info about error.
* @param {function()} successCb to be called on success.
*/
registry.registrar.EppSession.prototype.login = function(successCb) {
var eppArgs = {clId: this.console.params.clientId, clTrid: 'asdf-1235'};
this.send(
registry.soy.registrar.epp.login(eppArgs).getContent(),
goog.bind(function(xml) {
var result = xml['epp']['response']['result'];
var eppCode = result['@code'];
if (eppCode == '1000' || eppCode == '2002') {
// Success || Already logged in.
this.isEppLoggedIn_ = true;
successCb();
} else {
// Failure.
this.isEppLoggedIn_ = false;
registry.util.butter('login error: ' + eppCode);
}
}, this));
};
/**
* Logout or display butterbar info about error.
*/
registry.registrar.EppSession.prototype.logout = function() {
this.send(
registry.soy.registrar.epp.logout(
{clTrid: 'asdf-1235'}).getContent(),
goog.bind(function(xml) {
var result = xml['epp']['response']['result'];
var eppCode = result['@code'];
registry.util.butter(
'logout ' + eppCode + ': ' + result['msg']['keyValue']);
// Going to be safe here and force a login either way.
this.isEppLoggedIn_ = false;
}, this));
};
/**
* Send xml to the server.
* @param {string} xml Request document.
* @param {function(!Object)} callback For XhrIo result throws.
*/
registry.registrar.EppSession.prototype.send = function(xml, callback) {
var toXmlJsonCb = function(rspXml) {
callback(registry.xml.convertToJson(rspXml));
};
this.sendXhrIo(xml, toXmlJsonCb);
};

View file

@ -1,107 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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.
goog.provide('registry.registrar.Host');
goog.require('registry.registrar.XmlResourceComponent');
goog.require('registry.soy.registrar.host');
goog.require('registry.soy.registrar.hostepp');
goog.forwardDeclare('registry.registrar.Console');
/**
* CRUD for EPP host objects.
* @param {!registry.registrar.Console} console
* @constructor
* @extends {registry.registrar.XmlResourceComponent}
* @final
*/
registry.registrar.Host = function(console) {
registry.registrar.Host.base(
this, 'constructor',
registry.soy.registrar.host.item,
registry.soy.registrar.hostepp,
console);
};
goog.inherits(registry.registrar.Host,
registry.registrar.XmlResourceComponent);
/** @override */
registry.registrar.Host.prototype.processItem = function() {
this.model.item = this.model['epp']['response']['resData']['host:infData'];
if (this.model.item['host:addr']) {
if (!goog.isArray(this.model.item['host:addr'])) {
this.model.item['host:addr'] = [this.model.item['host:addr']];
}
} else {
this.model.item['host:addr'] = [];
}
};
/** @override */
registry.registrar.Host.prototype.setupEditor = function(objArgs) {
this.typeCounts['host-addr'] =
objArgs.item['host:addr'] ? objArgs.item['host:addr'].length : 0;
this.addRemBtnHandlers('host-addr', goog.bind(function() {
return 'host:addr[' + this.typeCounts['host-addr'] + ']';
}, this));
this.formInputRowRemovable(document.querySelectorAll('input[readonly]'));
};
/** @override */
registry.registrar.Host.prototype.prepareCreate = function(params) {
params.nextId = params.item['host:name'];
return registry.soy.registrar.hostepp.create(
/** @type {{clTrid: ?, item: ?}} */(params)).toString();
};
/** @override */
registry.registrar.Host.prototype.prepareUpdate = function(params) {
var form = params.item;
var addAddrs = [];
var remAddrs = [];
if (form['host:addr']) {
var oldAddrs = form['host:oldAddr'] || [];
var newAddrs = form['host:addr'];
var length = Math.max(oldAddrs.length, newAddrs.length);
for (var i = 0; i < length; i++) {
if (i >= oldAddrs.length) {
addAddrs.push(newAddrs[i]['value']);
} else if (i >= newAddrs.length) {
remAddrs.push(oldAddrs[i]['value']);
} else {
if (newAddrs[i]['value'] == oldAddrs[i]['value']) {
// Do nothing.
} else if (newAddrs[i]['value'] == '') {
remAddrs.push(oldAddrs[i]['value']);
} else {
remAddrs.push(oldAddrs[i]['value']);
addAddrs.push(newAddrs[i]['value']);
}
}
}
}
params.addAddrs = addAddrs;
params.remAddrs = remAddrs;
params.nextId = form['host:chgName'];
return registry.soy.registrar.hostepp.update(
/** @type {{clTrid: ?, item: ?}} */(params)).toString();
};

View file

@ -38,11 +38,10 @@ goog.require('registry.registrar.Console');
* @param {string} technicalDocsUrl
* @export
*/
registry.registrar.main = function(xsrfToken, clientId, isAdmin, isOwner,
productName, integrationEmail, supportEmail,
announcementsEmail, supportPhoneNumber,
technicalDocsUrl) {
new registry.registrar.Console({
registry.registrar.main = function(
xsrfToken, clientId, isAdmin, isOwner, productName, integrationEmail,
supportEmail, announcementsEmail, supportPhoneNumber, technicalDocsUrl) {
const console = new registry.registrar.Console({
xsrfToken: xsrfToken,
clientId: clientId,
isAdmin: isAdmin,
@ -54,4 +53,6 @@ registry.registrar.main = function(xsrfToken, clientId, isAdmin, isOwner,
supportPhoneNumber: supportPhoneNumber,
technicalDocsUrl: technicalDocsUrl
});
console.setUp();
};

View file

@ -1,219 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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.
goog.provide('registry.registrar.XmlResourceComponent');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.dom.classlist');
goog.require('registry.EditItem');
goog.require('registry.util');
goog.forwardDeclare('registry.registrar.Console');
/**
* The ResourceComponent class respresents server state for a named
* resource and binds UI CRUD operations on it, or its constituent
* collection.
* @param {function()} itemTmpl
* @param {!Object} eppTmpls Epp xml templates for info requests.
* @param {!registry.registrar.Console} console
* @constructor
* @extends {registry.EditItem}
*/
registry.registrar.XmlResourceComponent = function(
itemTmpl, eppTmpls, console) {
registry.registrar.XmlResourceComponent.base(
this, 'constructor', console, itemTmpl, console.params.isOwner);
/** @type {!Object} */
this.eppTmpls = eppTmpls;
};
goog.inherits(registry.registrar.XmlResourceComponent, registry.EditItem);
/** @override */
registry.registrar.XmlResourceComponent.prototype.bindToDom =
function(id) {
// XXX: EPP resources still use null state.
registry.registrar.XmlResourceComponent.base(
this, 'bindToDom', (id || ''));
if (id) {
this.fetchItem(id);
} else {
// Start edit of empty object.
this.edit();
}
};
/** @override */
registry.registrar.XmlResourceComponent.prototype.fetchItem = function(id) {
var queryParams = {id: id, clTrid: 'abc-1234'};
var xml = this.prepareFetch(queryParams);
this.console.session.send(xml, goog.bind(this.handleFetchItem, this, id));
};
/** @override */
registry.registrar.XmlResourceComponent.prototype.handleFetchItem =
function(id, rsp) {
this.model = rsp;
var result = rsp['epp']['response']['result'];
var resCode = result['@code'];
// XXX: Should use enum.
if (resCode == 1000) { // OK
this.model.readonly = true;
this.processItem();
this.renderItem(this.model);
} else if (resCode == 2303) { // Missing
registry.util.butter(
'Could not find: "' + id + '". Please enter as "type/id"');
} else {
// includes general failure.
registry.util.butter(resCode + ':' + result['msg']['keyValue']);
}
};
/**
* Sublcasses should override to populate create queryParams with form
* fields as needed. `queryParams.nextId` MUST be set to the
* new object's ID.
* @param {!Object} queryParams
*/
registry.registrar.XmlResourceComponent.prototype.prepareCreate =
goog.abstractMethod;
/**
* Calls prepareCreate with template params and then send the returned
* XML to the server
* @override
*/
registry.registrar.XmlResourceComponent.prototype.sendCreate = function() {
var form = registry.util.parseForm('item');
var queryParams = {item: form, clTrid: 'abc-1234'};
var xml = this.prepareCreate(queryParams);
this.nextId = queryParams.nextId;
this.console.session.send(xml, goog.bind(this.handleUpdateResponse, this));
};
/**
* Calls prepareUpdate with template params and then send the returned
* XML to the server.
* @override
*/
registry.registrar.XmlResourceComponent.prototype.sendUpdate = function() {
var form = registry.util.parseForm('item');
var queryParams = {item: form, clTrid: 'abc-1234'};
var xml = this.prepareUpdate(queryParams);
this.nextId = queryParams.nextId;
this.console.session.send(xml, goog.bind(this.handleUpdateResponse, this));
};
/**
* Sublcasses should override to populate fetch queryParams with form
* fields as needed.
* @param {!Object} queryParams
* @return {string} EPP xml string
*/
registry.registrar.XmlResourceComponent.prototype.prepareFetch =
function(queryParams) {
return this.eppTmpls.info(queryParams).toString();
};
/** @override */
registry.registrar.XmlResourceComponent.prototype.handleUpdateResponse =
function(rsp) {
var result = rsp['epp']['response']['result'];
if (result['@code'] == 1000) {
// XXX: Consider timer, probably just as a seconds arg with impl in butter.
registry.util.butter('Saved.');
this.fetchItem(this.nextId || '');
this.nextId = null;
this.toggleEdit();
} else {
registry.util.butter(result['msg']['keyValue']);
}
return rsp;
};
/**
* Helper to add add/remove hosts/contacts on queryParams:
*
* queryParams[op + fieldType] = formFields.
*
* @param {Array.<Object>} formFields named form item representations
* that have an associated {op: (add|rem)} attribute.
* @param {string} fieldType capitalized pluralized type name, e.g. "Contacts".
* @param {!Object} queryParams
*/
registry.registrar.XmlResourceComponent.prototype.addRem =
function(formFields, fieldType, queryParams) {
var add = [];
var rem = [];
for (var i = 0; i < formFields.length; i++) {
var formField = formFields[i];
var op = formField['op'];
switch (op) {
case 'add':
add.push(formField);
break;
case 'rem':
rem.push(formField);
break;
default:
registry.util.log(
'Unknown op attribute ' + op + ' on form field:',
formField);
}
}
if (add.length > 0) {
queryParams['add' + fieldType] = add;
}
if (rem.length > 0) {
queryParams['rem' + fieldType] = rem;
}
};
/**
* Make the given form input rows removable.
* @param {NodeList} elts array of input elements.
* @param {function()=} opt_onclickCb called when remove button is clicked.
*/
registry.registrar.XmlResourceComponent.prototype.formInputRowRemovable =
function(elts, opt_onclickCb) {
for (var i = 0; i < elts.length; i++) {
var parent = goog.dom.getParentElement(elts[i]);
this.appendRemoveBtn(parent, goog.partial(function(elt) {
var eppRemInput = goog.dom.createDom(goog.dom.TagName.INPUT, {
'name': elt.name.replace(/\.value/, '.op'),
'value': 'rem',
'type': 'hidden'
});
goog.dom.appendChild(goog.dom.getParentElement(elt), eppRemInput);
goog.dom.classlist.add(
goog.dom.getParentElement(
goog.dom.getParentElement(elt)), goog.getCssName('hidden'));
}, elts[i]));
}
};

View file

@ -31,8 +31,7 @@ goog.forwardDeclare('goog.Uri');
* @constructor
*/
registry.Resource = function(baseUri, id, xsrfToken) {
registry.Resource.base(this, 'constructor', baseUri, xsrfToken,
registry.Session.ContentType.JSON);
registry.Resource.base(this, 'constructor', baseUri, xsrfToken);
/** @const @private {string} the ID of the target resource. */
this.id_ = id;
};

View file

@ -23,14 +23,13 @@ goog.forwardDeclare('goog.Uri');
/**
* XHR launcher for both JSON and XML requests.
* XHR launcher for JSON requests.
* @param {!goog.Uri} defaultUri URI to which requests are POSTed.
* @param {string} xsrfToken Cross-site request forgery protection token.
* @param {!registry.Session.ContentType} contentType Payload mode.
* @constructor
* @template REQUEST, RESPONSE
*/
registry.Session = function(defaultUri, xsrfToken, contentType) {
registry.Session = function(defaultUri, xsrfToken) {
/**
* URI to which requests are posted.
@ -39,36 +38,19 @@ registry.Session = function(defaultUri, xsrfToken, contentType) {
*/
this.uri = defaultUri;
/**
* Content type set in request body.
* @private {!registry.Session.ContentType}
* @const
*/
this.contentType_ = contentType;
/**
* XHR request headers.
* @private {!Object<string, string>}
* @const
*/
this.headers_ = {
'Content-Type': contentType,
'Content-Type': 'application/json; charset=utf-8',
'X-CSRF-Token': xsrfToken,
'X-Requested-With': 'XMLHttpRequest'
};
};
/**
* Payload modes supported by this class.
* @enum {string}
*/
registry.Session.ContentType = {
JSON: 'application/json; charset=utf-8',
EPP: 'application/epp+xml'
};
/**
* Abstract method to send a request to the server.
* @param {REQUEST} body HTTP request body as a string or JSON object.
@ -99,9 +81,7 @@ registry.Session.prototype.sendXhrIo =
registry.Session.prototype.onXhrComplete_ = function(onSuccess, onError, e) {
if (e.target.isSuccess()) {
onSuccess(/** @type {!RESPONSE} */ (
this.contentType_ == registry.Session.ContentType.JSON ?
e.target.getResponseJson(registry.Session.PARSER_BREAKER_) :
e.target.getResponseXml()));
e.target.getResponseJson(registry.Session.PARSER_BREAKER_)));
} else {
onError(e.target.getLastError());
}

View file

@ -1,89 +0,0 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// 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.
goog.provide('registry.xml');
goog.provide('registry.xml.XmlJson');
goog.require('goog.dom.NodeType');
goog.require('goog.object');
/**
* Turns XML document into a JSON data structure. This function is similar to
* <a href="https://developer.mozilla.org/en-US/docs/JXON">Mozilla JXON</a>
* except it handles text differently. This routine will not coalesce text
* interspersed with elements. This routine does not coerce string types to
* number, boolean, or null.
* @param {!Node} node
* @return {!Object<string, registry.xml.XmlJson>}
* @throws {Error} upon encountering interspersed text.
*/
registry.xml.convertToJson = function(node) {
var result = goog.object.create();
if (goog.isDefAndNotNull(node.attributes)) {
for (var i = 0; i < node.attributes.length; i++) {
var attr = node.attributes.item(i);
goog.object.set(result, '@' + attr.name, attr.value || '');
}
}
for (var j = 0; j < node.childNodes.length; j++) {
var child = node.childNodes.item(j);
switch (child.nodeType) {
case goog.dom.NodeType.TEXT:
case goog.dom.NodeType.CDATA_SECTION:
var text = String(child.nodeValue).trim();
if (text != '') {
var curr = goog.object.get(result, registry.xml.jsonValueFieldName_);
if (goog.isDef(curr)) {
throw new Error(
'XML text "' + curr + '" interspersed with "' + text + '"');
}
goog.object.set(result, registry.xml.jsonValueFieldName_, text);
}
break;
case goog.dom.NodeType.ELEMENT:
var json = registry.xml.convertToJson(child);
var name = child.nodeName;
if (goog.object.containsKey(result, name)) {
var field = goog.object.get(result, name);
if (goog.isArray(field)) {
field.push(json);
} else {
goog.object.set(result, name, [field, json]);
}
} else {
goog.object.set(result, name, json);
}
break;
}
}
return result;
};
/**
* XML JSON object recursive type definition.
* @typedef {(string|
* !Array<registry.xml.XmlJson>|
* !Object<string, registry.xml.XmlJson>)}
*/
registry.xml.XmlJson;
/**
* XML JSON value field name, inherited from JXON.
* @private {string}
* @const
*/
registry.xml.jsonValueFieldName_ = 'keyValue';