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 193bd49406
commit 847795d58d
52 changed files with 58 additions and 3788 deletions

View file

@ -945,7 +945,6 @@ An EPP flow for login.
* 2103 * 2103
* Specified extension is not implemented. * Specified extension is not implemented.
* 2200 * 2200
* GAE User can't access the requested registrar.
* Registrar certificate does not match stored certificate. * Registrar certificate does not match stored certificate.
* Registrar IP address is not in stored whitelist. * Registrar IP address is not in stored whitelist.
* Registrar certificate not present. * Registrar certificate not present.

View file

@ -1,55 +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.
package google.registry.flows;
import com.google.appengine.api.users.UserService;
import google.registry.model.eppcommon.ProtocolDefinition;
import google.registry.request.Action;
import google.registry.request.Action.Method;
import google.registry.request.Parameter;
import google.registry.request.Payload;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import javax.inject.Inject;
import javax.servlet.http.HttpSession;
/** Runs EPP from the console and requires GAE user authentication. */
@Action(
service = Action.Service.DEFAULT,
path = "/registrar-xhr",
method = Method.POST,
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
public class EppConsoleAction implements Runnable {
@Inject @Payload byte[] inputXmlBytes;
@Inject HttpSession session;
@Inject EppRequestHandler eppRequestHandler;
@Inject UserService userService;
@Inject AuthenticatedRegistrarAccessor registrarAccessor;
@Inject @Parameter("clientId") String clientId;
@Inject EppConsoleAction() {}
@Override
public void run() {
eppRequestHandler.executeEpp(
new StatelessRequestSessionMetadata(clientId,
ProtocolDefinition.getVisibleServiceExtensionUris()),
new GaeUserCredentials(registrarAccessor),
EppRequestSource.CONSOLE,
false, // This endpoint is never a dry run.
false, // This endpoint is never a superuser.
inputXmlBytes);
}
}

View file

@ -1,54 +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.
package google.registry.flows;
import static com.google.common.base.MoreObjects.toStringHelper;
import google.registry.flows.EppException.AuthenticationErrorException;
import google.registry.model.registrar.Registrar;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
/** Credentials provided by {@link com.google.appengine.api.users.UserService}. */
public class GaeUserCredentials implements TransportCredentials {
private final AuthenticatedRegistrarAccessor registrarAccessor;
public GaeUserCredentials(AuthenticatedRegistrarAccessor registrarAccessor) {
this.registrarAccessor = registrarAccessor;
}
@Override
public void validate(Registrar registrar, String ignoredPassword)
throws AuthenticationErrorException {
try {
registrarAccessor.verifyAccess(registrar.getClientId());
} catch (RegistrarAccessDeniedException e) {
throw new UserForbiddenException(e);
}
}
@Override
public String toString() {
return toStringHelper(getClass()).add("user", registrarAccessor.userIdForLogging()).toString();
}
/** GAE User can't access the requested registrar. */
public static class UserForbiddenException extends AuthenticationErrorException {
public UserForbiddenException(RegistrarAccessDeniedException e) {
super(e.getMessage());
}
}
}

View file

@ -51,7 +51,6 @@ import javax.inject.Inject;
* @error {@link google.registry.flows.EppException.UnimplementedExtensionException} * @error {@link google.registry.flows.EppException.UnimplementedExtensionException}
* @error {@link google.registry.flows.EppException.UnimplementedObjectServiceException} * @error {@link google.registry.flows.EppException.UnimplementedObjectServiceException}
* @error {@link google.registry.flows.EppException.UnimplementedProtocolVersionException} * @error {@link google.registry.flows.EppException.UnimplementedProtocolVersionException}
* @error {@link google.registry.flows.GaeUserCredentials.UserForbiddenException}
* @error {@link google.registry.flows.TlsCredentials.BadRegistrarCertificateException} * @error {@link google.registry.flows.TlsCredentials.BadRegistrarCertificateException}
* @error {@link google.registry.flows.TlsCredentials.BadRegistrarIpAddressException} * @error {@link google.registry.flows.TlsCredentials.BadRegistrarIpAddressException}
* @error {@link google.registry.flows.TlsCredentials.MissingRegistrarCertificateException} * @error {@link google.registry.flows.TlsCredentials.MissingRegistrarCertificateException}

View file

@ -17,7 +17,6 @@ package google.registry.module.frontend;
import dagger.Module; import dagger.Module;
import dagger.Subcomponent; import dagger.Subcomponent;
import google.registry.dns.DnsModule; import google.registry.dns.DnsModule;
import google.registry.flows.EppConsoleAction;
import google.registry.flows.EppTlsAction; import google.registry.flows.EppTlsAction;
import google.registry.flows.FlowComponent; import google.registry.flows.FlowComponent;
import google.registry.flows.TlsCredentials.EppTlsModule; import google.registry.flows.TlsCredentials.EppTlsModule;
@ -46,7 +45,6 @@ interface FrontendRequestComponent {
ConsoleOteSetupAction consoleOteSetupAction(); ConsoleOteSetupAction consoleOteSetupAction();
ConsoleRegistrarCreatorAction consoleRegistrarCreatorAction(); ConsoleRegistrarCreatorAction consoleRegistrarCreatorAction();
ConsoleUiAction consoleUiAction(); ConsoleUiAction consoleUiAction();
EppConsoleAction eppConsoleAction();
EppTlsAction eppTlsAction(); EppTlsAction eppTlsAction();
FlowComponent.Builder flowComponentBuilder(); FlowComponent.Builder flowComponentBuilder();
OteStatusAction oteStatusAction(); OteStatusAction oteStatusAction();

View file

@ -14,13 +14,7 @@
goog.provide('registry.Component'); 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.EventHandler');
goog.require('goog.events.EventType');
goog.require('registry.soy.forms');
goog.require('registry.util'); goog.require('registry.util');
goog.forwardDeclare('registry.Console'); goog.forwardDeclare('registry.Console');
@ -38,16 +32,9 @@ goog.forwardDeclare('registry.Console');
* ^ * ^
* \ * \
* |-ui/js/resource_component.js - JSON resources * |-ui/js/resource_component.js - JSON resources
* | ^ * ^
* | \ * \
* | |- ui/js/registrar/settings.js * |- 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
* </pre> * </pre>
* *
* @param {!registry.Console} cons the console singleton. * @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.Component.prototype.bindToDom = function(id) {
registry.util.unbutter(); 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.require('registry.util');
goog.forwardDeclare('goog.events.KeyEvent'); goog.forwardDeclare('goog.events.KeyEvent');
goog.forwardDeclare('registry.Session');
/** /**
* Abstract console for both admin and registrar console UIs. * Abstract console for both admin and registrar console UIs.
* @param {?registry.Session} session server request session.
* @constructor * @constructor
* @extends {goog.Disposable} * @extends {goog.Disposable}
*/ */
registry.Console = function(session) { registry.Console = function() {
registry.Console.base(this, 'constructor'); registry.Console.base(this, 'constructor');
/** /**
@ -42,19 +40,37 @@ registry.Console = function(session) {
* @protected * @protected
*/ */
this.history = new goog.History(); 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( goog.events.listen(
this.history, this.history,
goog.history.EventType.NAVIGATE, goog.history.EventType.NAVIGATE,
goog.bind(this.handleHashChange, this)); goog.bind(this.handleHashChange, this));
/**
* @type {?registry.Session} The server session.
*/
this.session = session;
this.bindToDom(); 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. * @param {string} resourcePath Resource description path.
*/ */
registry.Console.prototype.view = function(resourcePath) { 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); 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 * Subclasses should provide a function to parse JSON response from server and
* from server and return a result object as described below. * return a result object as described below.
* @param {!Object} rsp Decoded XML/JSON response from the server. * @param {!Object} rsp Decoded JSON response from the server.
* @return {!Object} a result object describing next steps. On * @return {!Object} a result object describing next steps. On
* success, if next is defined, visit(ret.next) is called, otherwise * success, if next is defined, visit(ret.next) is called, otherwise
* if err is set, the butterbar message is set to it. * 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.Console');
goog.require('registry.Resource'); goog.require('registry.Resource');
goog.require('registry.registrar.AdminSettings'); goog.require('registry.registrar.AdminSettings');
goog.require('registry.registrar.Contact');
goog.require('registry.registrar.ContactSettings'); goog.require('registry.registrar.ContactSettings');
goog.require('registry.registrar.ContactUs'); goog.require('registry.registrar.ContactUs');
goog.require('registry.registrar.Dashboard'); 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.Resources');
goog.require('registry.registrar.SecuritySettings'); goog.require('registry.registrar.SecuritySettings');
goog.require('registry.registrar.WhoisSettings'); goog.require('registry.registrar.WhoisSettings');
@ -49,16 +45,13 @@ goog.forwardDeclare('registry.Component');
* @final * @final
*/ */
registry.registrar.Console = function(params) { registry.registrar.Console = function(params) {
registry.registrar.Console.base(this, 'constructor');
/** /**
* @type {!Object} * @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; this.params = params;
registry.registrar.Console.base(
this, 'constructor', new registry.registrar.EppSession(this));
/** /**
* Component that's currently embedded in the page. * Component that's currently embedded in the page.
* @type {?registry.Component} * @type {?registry.Component}
@ -66,13 +59,9 @@ registry.registrar.Console = function(params) {
*/ */
this.component_ = null; 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. * Last active nav element.
* @type {Element} * @type {?Element}
*/ */
this.lastActiveNavElt; this.lastActiveNavElt;
@ -107,13 +96,6 @@ registry.registrar.Console = function(params) {
if (this.params.isAdmin) { if (this.params.isAdmin) {
this.pageMap['admin-settings'] = registry.registrar.AdminSettings; 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); goog.inherits(registry.registrar.Console, registry.Console);
@ -134,17 +116,7 @@ goog.inherits(registry.registrar.Console, registry.Console);
*/ */
registry.registrar.Console.prototype.handleHashChange = function() { registry.registrar.Console.prototype.handleHashChange = function() {
var hashToken = this.history.getToken(); 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 parts = hashToken.split('/');
var type = ''; var type = '';
var id = ''; 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. * Handler for contact save that navigates to that item on success.
* Does nothing on failure as UI will be left with error messages for * Does nothing on failure as UI will be left with error messages for
* the user to resolve. * the user to resolve.
* @param {!Object} rsp Decoded XML/JSON response from the server. * @param {!Object} rsp Decoded JSON response from the server.
* @override * @override
*/ */
registry.registrar.ContactSettings.prototype.handleCreateResponse = 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. * 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 * Does nothing on failure as UI will be left with error messages for
* the user to resolve. * the user to resolve.
* @param {!Object} rsp Decoded XML/JSON response from the server. * @param {!Object} rsp Decoded JSON response from the server.
* @override * @override
*/ */
registry.registrar.ContactSettings.prototype.handleDeleteResponse = registry.registrar.ContactSettings.prototype.handleDeleteResponse =

View file

@ -40,10 +40,10 @@ registry.registrar.Dashboard = function(console) {
/** @private {number} */ /** @private {number} */
this.x_ = 0; this.x_ = 0;
/** @private {Element} */ /** @private {?Element} */
this.gear_ = null; this.gear_ = null;
/** @private {goog.Timer} */ /** @private {?goog.Timer} */
this.timer_ = null; this.timer_ = null;
}; };
goog.inherits(registry.registrar.Dashboard, registry.Component); 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. * Let's do the twist.
* @private * @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 * @param {string} technicalDocsUrl
* @export * @export
*/ */
registry.registrar.main = function(xsrfToken, clientId, isAdmin, isOwner, registry.registrar.main = function(
productName, integrationEmail, supportEmail, xsrfToken, clientId, isAdmin, isOwner, productName, integrationEmail,
announcementsEmail, supportPhoneNumber, supportEmail, announcementsEmail, supportPhoneNumber, technicalDocsUrl) {
technicalDocsUrl) { const console = new registry.registrar.Console({
new registry.registrar.Console({
xsrfToken: xsrfToken, xsrfToken: xsrfToken,
clientId: clientId, clientId: clientId,
isAdmin: isAdmin, isAdmin: isAdmin,
@ -54,4 +53,6 @@ registry.registrar.main = function(xsrfToken, clientId, isAdmin, isOwner,
supportPhoneNumber: supportPhoneNumber, supportPhoneNumber: supportPhoneNumber,
technicalDocsUrl: technicalDocsUrl 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 * @constructor
*/ */
registry.Resource = function(baseUri, id, xsrfToken) { registry.Resource = function(baseUri, id, xsrfToken) {
registry.Resource.base(this, 'constructor', baseUri, xsrfToken, registry.Resource.base(this, 'constructor', baseUri, xsrfToken);
registry.Session.ContentType.JSON);
/** @const @private {string} the ID of the target resource. */ /** @const @private {string} the ID of the target resource. */
this.id_ = id; 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 {!goog.Uri} defaultUri URI to which requests are POSTed.
* @param {string} xsrfToken Cross-site request forgery protection token. * @param {string} xsrfToken Cross-site request forgery protection token.
* @param {!registry.Session.ContentType} contentType Payload mode.
* @constructor * @constructor
* @template REQUEST, RESPONSE * @template REQUEST, RESPONSE
*/ */
registry.Session = function(defaultUri, xsrfToken, contentType) { registry.Session = function(defaultUri, xsrfToken) {
/** /**
* URI to which requests are posted. * URI to which requests are posted.
@ -39,36 +38,19 @@ registry.Session = function(defaultUri, xsrfToken, contentType) {
*/ */
this.uri = defaultUri; this.uri = defaultUri;
/**
* Content type set in request body.
* @private {!registry.Session.ContentType}
* @const
*/
this.contentType_ = contentType;
/** /**
* XHR request headers. * XHR request headers.
* @private {!Object<string, string>} * @private {!Object<string, string>}
* @const * @const
*/ */
this.headers_ = { this.headers_ = {
'Content-Type': contentType, 'Content-Type': 'application/json; charset=utf-8',
'X-CSRF-Token': xsrfToken, 'X-CSRF-Token': xsrfToken,
'X-Requested-With': 'XMLHttpRequest' '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. * Abstract method to send a request to the server.
* @param {REQUEST} body HTTP request body as a string or JSON object. * @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) { registry.Session.prototype.onXhrComplete_ = function(onSuccess, onError, e) {
if (e.target.isSuccess()) { if (e.target.isSuccess()) {
onSuccess(/** @type {!RESPONSE} */ ( onSuccess(/** @type {!RESPONSE} */ (
this.contentType_ == registry.Session.ContentType.JSON ? e.target.getResponseJson(registry.Session.PARSER_BREAKER_)));
e.target.getResponseJson(registry.Session.PARSER_BREAKER_) :
e.target.getResponseXml()));
} else { } else {
onError(e.target.getLastError()); 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';

View file

@ -1,177 +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.
{namespace registry.soy.registrar.contact}
/**
* Set view for contacts.
*/
{template .set}
<div class="{css('set')} {css('contact')}">
<p>Please enter a query for a single contact in the form "contact/[contact id]".
</div>
{/template}
/**
* Item view for contact.
*/
{template .item}
{@param? item: ?}
{@param? readonly: ?} /** passed through to field rendering. */
<form name="item" class="{css('item')} {css('contact')}">
<h1>
{if isNonnull($item['contact:id'])}
{$item['contact:id']['keyValue']}
{else}
New Contact
{/if}
</h1>
<table>
<tr class="{css('section-lead')}">
<th colspan="2"><h2>Authentication</h2></th>
</tr>
{if not isNonnull($item['contact:id'])}
{call registry.soy.forms.inputFieldRow data="all"}
{param label: 'Contact ID *' /}
{param name: 'contact:id' /}
{/call}
{/if}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Password *' /}
{param name: 'contact:authInfo.contact:pw' /}
{param value: isNonnull($item['contact:authInfo']) ?
$item['contact:authInfo']['contact:pw'] : '' /}
{/call}
<tr class="{css('section-lead')}">
<th colspan="2"><h2>Contacts</h2></th>
</tr>
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Email *' /}
{param name: 'contact:email' /}
{param value: $item['contact:email'] /}
{/call}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Phone' /}
{param name: 'contact:voice' /}
{param value: $item['contact:voice'] /}
{param placeholder: 'e.g. +1.6508675309' /}
{/call}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Fax' /}
{param name: 'contact:fax' /}
{param value: $item['contact:fax'] /}
{param placeholder: 'e.g. +1.2125552638' /}
{/call}
<tr class="{css('section-lead')} {css('subsection')}">
<th id="contact-postalInfoHeader" colspan="2">
<h3>Postal address(es)</h3>
<button id="domain-contact-postalInfo-add-button" type="button"
class="{css('kd-button')} {css('reg-add')}"
{if $readonly}disabled{/if}>
Add Localized Address
</button>
</th>
</tr>
<tr>
<td colspan="2">
<div id="contact-postalInfo">
{if isNonnull($item['contact:postalInfo'])}
{for $pi in $item['contact:postalInfo']}
{call .postalInfo data="all"}
{param localized: index($pi) == 1 /}
{param item: $pi/}
{param namePrefix: 'contact:postalInfo[' + index($pi) + '].contact:' /}
{/call}
{/for}
{else}
{call .postalInfo data="all"}
{param namePrefix: 'contact:postalInfo[0].contact:' /}
{/call}
{/if}
<table id="domain-contact-postalInfos-footer"></table>
</div>
</td>
</tr>
</table>
{if isNonnull($item['contact:id'])}
<input type="hidden" name="contact:id" value="{$item['contact:id']['keyValue']}"/>
{/if}
</form>
{/template}
/**
* Postal info.
*/
{template .postalInfo}
{@param item: ?}
{@param namePrefix: ?}
{@param? localized: ?} /** if true, this is the second, localized postalInfo. */
<table>
<tr><th colspan=2>
{if $localized}
<strong>Localized address</strong><br>
<span class="{css('info')}">Full UTF-8 charsets allowed</span>
{else}
<strong>Internationalized address</strong><br>
<span class="{css('info')}">Only 7-bit ASCII allowed</span>
{/if}
</th></tr>
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Name *' /}
{param name: 'name' /}
{param value: $item['contact:name']/}
{/call}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Organization' /}
{param name: 'org' /}
{param value: $item['contact:org'] /}
{/call}
{call registry.soy.forms.textareaFieldRowWithValue data="all"}
{param label: 'Street' /}
{param name: 'street' /}
{param namePrefix: $namePrefix + 'addr.contact:' /}
{param value: isNonnull($item['contact:addr']) ? $item['contact:addr']['contact:street'] : '' /}
{/call}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'City *' /}
{param name: 'city' /}
{param namePrefix: $namePrefix + 'addr.contact:' /}
{param value: isNonnull($item['contact:addr']) ? $item['contact:addr']['contact:city'] : '' /}
{/call}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'State / Region' /}
{param name: 'sp' /}
{param namePrefix: $namePrefix + 'addr.contact:' /}
{param value: isNonnull($item['contact:addr']) ? $item['contact:addr']['contact:sp'] : '' /}
{param placeholder: 'e.g. CA' /}
{/call}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Zip / Postal code' /}
{param name: 'pc' /}
{param namePrefix: $namePrefix + 'addr.contact:' /}
{param value: isNonnull($item['contact:addr']) ? $item['contact:addr']['contact:pc'] : '' /}
{param placeholder: 'e.g. 10282' /}
{/call}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Country code *' /}
{param name: 'cc' /}
{param namePrefix: $namePrefix + 'addr.contact:' /}
{param value: isNonnull($item['contact:addr']) ? $item['contact:addr']['contact:cc'] : '' /}
{param placeholder: 'e.g. US' /}
{/call}
</table>
{/template}

View file

@ -1,118 +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.
{namespace registry.soy.registrar.contactepp}
/* XXX: Forces first postalInfo type to be "int" and second "loc" if it is
* present, for compatibility with the server. */
/**
* Contact create request.
*/
{template .create stricthtml="false"}
{@param item: ?}
{@param clTrid: ?}
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<contact:create xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>{$item['contact:id']}</contact:id>
{for $pi in $item['contact:postalInfo']}
<contact:postalInfo type="{if index($pi) == 0}int{else}loc{/if}">
<contact:name>{$pi['contact:name']}</contact:name>
<contact:org>{$pi['contact:org']}</contact:org>
<contact:addr>
{let $addr: $pi['contact:addr'] /}
<contact:street>{$addr['contact:street']}</contact:street>
<contact:city>{$addr['contact:city']}</contact:city>
<contact:sp>{$addr['contact:sp']}</contact:sp>
<contact:pc>{$addr['contact:pc']}</contact:pc>
<contact:cc>{$addr['contact:cc']}</contact:cc>
</contact:addr>
</contact:postalInfo>
{/for}
<contact:voice>{$item['contact:voice']}</contact:voice>
<contact:fax>{$item['contact:fax']}</contact:fax>
<contact:email>{$item['contact:email']}</contact:email>
<contact:authInfo>
<contact:pw>{$item['contact:authInfo']['contact:pw']}</contact:pw>
</contact:authInfo>
</contact:create>
</create>
<clTRID>{$clTrid}</clTRID>
</command>
</epp>
{/template}
/* XXX: Forces first postalInfo type to be "int" and second "loc" if it is
* present, for compatibility with the server. */
/**
* Contact update request.
*/
{template .update stricthtml="false"}
{@param item: ?}
{@param clTrid: ?}
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<contact:update xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>{$item['contact:id']}</contact:id>
<contact:chg>
{for $pi in $item['contact:postalInfo']}
<contact:postalInfo type="{if index($pi) == 0}int{else}loc{/if}">
<contact:name>{$pi['contact:name']}</contact:name>
<contact:org>{$pi['contact:org']}</contact:org>
{let $addr: $pi['contact:addr'] /}
<contact:addr>
<contact:street>{$addr['contact:street']}</contact:street>
<contact:city>{$addr['contact:city']}</contact:city>
<contact:sp>{$addr['contact:sp']}</contact:sp>
<contact:pc>{$addr['contact:pc']}</contact:pc>
<contact:cc>{$addr['contact:cc']}</contact:cc>
</contact:addr>
</contact:postalInfo>
{/for}
<contact:voice>{$item['contact:voice']}</contact:voice>
<contact:fax>{$item['contact:fax']}</contact:fax>
<contact:email>{$item['contact:email']}</contact:email>
<contact:authInfo>
<contact:pw>{$item['contact:authInfo']['contact:pw']}</contact:pw>
</contact:authInfo>
</contact:chg>
</contact:update>
</update>
<clTRID>{$clTrid}</clTRID>
</command>
</epp>
{/template}
/**
* Contact info request.
*/
{template .info stricthtml="false"}
{@param id: ?}
{@param clTrid: ?}
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<info>
<contact:info xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>{$id}</contact:id>
</contact:info>
</info>
<clTRID>{$clTrid}</clTRID>
</command>
</epp>
{/template}

View file

@ -1,200 +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.
{namespace registry.soy.registrar.domain}
/**
* Set view for domains.
*/
{template .set}
<div class="{css('set')} {css('domain')}">
<p>Please enter a query for a single contact in the form "domain/[domain id]".
</div>
{/template}
/**
* Item view for domain.
*/
{template .item}
{@param item: ?}
{@param? readonly: ?} /** passed through to field rendering. */
{let $isEdit: isNonnull($item['domain:name']) /}
<form name="item" class="{css('item')} {css('domain')}">
<h1>
{if $isEdit}
{$item['domain:name']['keyValue']}
{else}
New Domain
{/if}
</h1>
<table>
{if not $isEdit}
<tr class="{css('section-lead')}">
<th colspan="2"><h2>Domain</h2></th>
</tr>
{call registry.soy.forms.inputFieldRow data="all"}
{param label: 'Domain name *' /}
{param name: 'domain:name' /}
{/call}
{call registry.soy.forms.inputFieldRow data="all"}
{param label: 'Period (in years) *' /}
{param name: 'domain:period' /}
{/call}
{/if}
{if isNonnull($item['domain:exDate'])}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Expiration date' /}
{param name: 'domain:exDate' /}
{param value: $item['domain:exDate'] /}
{/call}
{/if}
<tr class="{css('section-lead')}">
<th colspan="2"><h2>Authentication</h2></th>
</tr>
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Password *' /}
{param name: 'domain:authInfo.domain:pw' /}
{param value: isNonnull($item['domain:authInfo']) ?
$item['domain:authInfo']['domain:pw'] : '' /}
{/call}
<tr class="{css('section-lead')}">
<th colspan="2">
<h3>Contact information</h3>
<button id="domain-contact-add-button" type="button"
class="{css('kd-button')} {css('reg-add')}"
{if $readonly}disabled{/if}>
Add Contact
</button>
</th>
</tr>
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Registrant *' /}
{param name: 'domain:registrant' /}
{param value: $item['domain:registrant'] /}
{/call}
{if isNonnull($item['domain:contact'])}
// Render contact list with stable ordering for the screenshot tests.
{call .showContact_ data="all"}
{param contacts: $item['domain:contact'] /}
{param type: 'admin' /}
{/call}
{call .showContact_ data="all"}
{param contacts: $item['domain:contact'] /}
{param type: 'billing' /}
{/call}
{call .showContact_ data="all"}
{param contacts: $item['domain:contact'] /}
{param type: 'tech' /}
{/call}
{/if}
<tr id="domain-contacts-footer"></tr>
<tr class="{css('section-lead')}">
<th colspan="2">
<h3>Nameservers</h3>
<button id="domain-host-add-button" type="button"
class="{css('kd-button')} {css('reg-add')}"
{if $readonly}disabled{/if}>
Add Host
</button>
</th>
</tr>
{if isNonnull($item['domain:ns']) and isNonnull($item['domain:ns']['domain:hostObj'])}
{for $hostObj in $item['domain:ns']['domain:hostObj']}
{let $hostIdx: index($hostObj) /}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Host ' + $hostIdx /}
{param name: 'domain:ns.domain:hostObj[' + $hostIdx + '].value' /}
{param value: $hostObj /}
{param clazz kind="text"}{css('domain-hostObj')}{/param}
{/call}
{/for}
{/if}
<tr id="domain-hosts-footer"></tr>
{if isNonnull($item['mark:mark'])}
<tr>
<td>Mark Data
<td>
<textarea
class="{css('reg-domain-mark')}"
spellcheck="false"
{if $readonly}readonly{/if}>
{$item['mark:mark']['keyValue']}</textarea>
</td>
</tr>
{/if}
</table>
{if $isEdit}
<input type="hidden"
name="domain:name"
value="{$item['domain:name']['keyValue']}">
{/if}
</form>
{/template}
/** Renders an input form row for a specific type of contact. */
{template .showContact_ visibility="private"}
{@param contacts: list<legacy_object_map<string, ?>>} /** List of EPP domain:contacts. */
{@param type: string} /** Type of contact (e.g. admin, tech) */
{for $contact in $contacts}
{if $type == $contact['@type']}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: $contact['@type'] + ' contact' /}
{param name: 'domain:contact[' + index($contact) + '].value' /}
{param value: $contact /}
{param clazz: 'domain-contact' /}
{/call}
<input name="domain:contact[{index($contact)}].@type"
type="hidden"
value="{$contact['@type']}">
{/if}
{/for}
{/template}
/* XXX: Should change support for admin/tech. */
/**
* Update domain.
*/
{template .update}
{@param? item: ?}
<form name="item" class="{css('item')} {css('domain')}">
<h1>{$item['domain:name']['keyValue']}</h1>
<table>
<tr class="{css('section-lead')}">
<th colspan="2"><h2>Contact</h2></th>
</tr>
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Registrant' /}
{param name: 'domain:registrant' /}
{param value: $item['domain:registrant'] /}
{/call}
<tr class="{css('section-lead')}">
<th colspan="2"><h2>Authentication</h2></th>
</tr>
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Password' /}
{param name: 'domain:authInfo.domain:pw' /}
{param value: isNonnull($item['domain:authInfo']) ?
$item['domain:authInfo']['domain:pw'] : '' /}
{/call}
<input type="hidden"
name="domain:name"
value="{$item['domain:name']['keyValue']}">
</table>
</form>
{/template}

View file

@ -1,140 +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.
{namespace registry.soy.registrar.domainepp}
/* General Availability. */
/**
* Domain create request.
*/
{template .create stricthtml="false"}
{@param item: ?}
{@param clTrid: ?}
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>{$item['domain:name']}</domain:name>
{if isNonnull($item['domain:period'])}
<domain:period unit="y">{$item['domain:period']}</domain:period>
{/if}
{if isNonnull($item['domain:ns'])}
<domain:ns>
{for $hostObj in $item['domain:ns']['domain:hostObj']}
<domain:hostObj>{$hostObj.value}</domain:hostObj>
{/for}
</domain:ns>
{/if}
{if isNonnull($item['domain:registrant'])}
<domain:registrant>{$item['domain:registrant']}</domain:registrant>
{/if}
{if isNonnull($item['domain:contact'])}
{for $contact in $item['domain:contact']}
<domain:contact type="{$contact['@type']}">{$contact.value}</domain:contact>
{/for}
{/if}
<domain:authInfo>
<domain:pw>{$item['domain:authInfo']['domain:pw']}</domain:pw>
</domain:authInfo>
</domain:create>
</create>
<clTRID>{$clTrid}</clTRID>
</command>
</epp>
{/template}
/**
* Domain info request.
*/
{template .info stricthtml="false"}
{@param id: ?}
{@param clTrid: ?}
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<info>
<domain:info xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name hosts="all">{$id}</domain:name>
</domain:info>
</info>
<clTRID>{$clTrid}</clTRID>
</command>
</epp>
{/template}
/**
* Domain update request.
*/
{template .update stricthtml="false"}
{@param item: ?}
{@param clTrid: ?}
{@param? addHosts: ?} /** list of hostObj to add. */
{@param? remHosts: ?} /** list of hostObj to remove. */
{@param? addContacts: ?} /** list of contact to add. */
{@param? remContacts: ?} /** list of contact to remove. */
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>{$item['domain:name']}</domain:name>
{if isNonnull($addHosts) or isNonnull($addContacts)}
{call .addRem}
{param isAdd: true /}
{param hosts: $addHosts /}
{param contacts: $addContacts /}
{/call}
{/if}
{if isNonnull($remHosts) or isNonnull($remContacts)}
{call .addRem}
{param isAdd: false /}
{param hosts: $remHosts /}
{param contacts: $remContacts /}
{/call}
{/if}
<domain:chg>
<domain:registrant>{$item['domain:registrant']}</domain:registrant>
<domain:authInfo>
<domain:pw>{$item['domain:authInfo']['domain:pw']}</domain:pw>
</domain:authInfo>
</domain:chg>
</domain:update>
</update>
<clTRID>{$clTrid}</clTRID>
</command>
</epp>
{/template}
{template .addRem}
{@param isAdd: ?}
{@param? hosts: ?}
{@param? contacts: ?}
{let $tagName: $isAdd ? 'domain:add' : 'domain:rem' /}
<{$tagName}>
{if isNonnull($hosts)}
<domain:ns>
{for $host in $hosts}
<domain:hostObj>{$host.value}</domain:hostObj>
{/for}
</domain:ns>
{/if}
{if isNonnull($contacts)}
{for $contact in $contacts}
<domain:contact type="{$contact['@type']}">{$contact.value}</domain:contact>
{/for}
{/if}
</{$tagName}>
{/template}

View file

@ -1,57 +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.
{namespace registry.soy.registrar.epp}
/**
* Login request.
* @param clId
* @param pw
* @param clTrid
*/
{template .login stricthtml="false"}
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<login>
<clID>{$clId}</clID>
<pw>{$pw}</pw>
<options>
<version>1.0</version>
<lang>en</lang>
</options>
<svcs>
<objURI>urn:ietf:params:xml:ns:host-1.0</objURI>
<objURI>urn:ietf:params:xml:ns:domain-1.0</objURI>
<objURI>urn:ietf:params:xml:ns:contact-1.0</objURI>
</svcs>
</login>
<clTRID>{$clTrid}</clTRID>
</command>
</epp>
{/template}
/**
* Logout request.
* @param clTrid
*/
{template .logout stricthtml="false"}
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<logout/>
<clTRID>{$clTrid}</clTRID>
</command>
</epp>
{/template}

View file

@ -1,108 +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.
{namespace registry.soy.registrar.host}
/**
* Set view for hosts.
*/
{template .set}
<div class="{css('set')} {css('host')}">
<p>Please enter a query for a single host in the form "host/[hostname]".
</div>
{/template}
/**
* Item view for host.
* @param? item
* @param? readonly passed through to field rendering.
*/
{template .item}
<form name="item" class="{css('item')} {css('host')}">
<h1>
{if isNonnull($item['host:name'])}
{$item['host:name']['keyValue']}
{else}
New Host
{/if}
</h1>
<table>
<tr class="{css('section-lead')}">
<th colspan="2"><h2>Host</h2></th>
</tr>
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Name *' /}
{param name: isNonnull($item['host:name']) ? 'host:chgName' : 'host:name' /}
{param value: $item['host:name'] /}
{/call}
<tr class="{css('section-lead')}">
<th colspan="2">
<h3>Addresses</h3>
<button id="domain-host-addr-add-button" type="button"
class="{css('kd-button')} {css('reg-add')}"
{if $readonly}disabled{/if}>
Add Address
</button>
</th>
</tr>
{if isNonnull($item['host:addr'])}
{for $addr in $item['host:addr']}
{if not $readonly}
<input type="hidden"
name="host:oldAddr[{index($addr)}].value"
value="{$item['host:addr'][index($addr)]['keyValue']}">
{/if}
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Address No. ' + (index($addr) + 1) /}
{param name: 'host:addr[' + index($addr) + '].value' /}
{param value: $item['host:addr'][index($addr)] /}
{/call}
{/for}
{/if}
<tr id="domain-host-addrs-footer"></tr>
</table>
{if isNonnull($item['host:name'])}
<input type="hidden"
name="host:name"
value="{$item['host:name']['keyValue']}">
{/if}
</form>
{/template}
/**
* Item view for host.
* @param? item
* @param? readonly passed through to field rendering.
*/
{template .update}
<form name="item" class="{css('item')} {css('host')}">
<h1>{$item['host:name']['keyValue']}</h1>
<table>
<tr class="{css('section-lead')}">
<th colspan="2"><h2>Host</h2></th>
</tr>
{call registry.soy.forms.inputFieldRowWithValue data="all"}
{param label: 'Name' /}
{param name: 'host:chgName' /}
{param value: $item['host:name'] /}
{/call}
</table>
{if isNonnull($item['host:name'])}
<input type="hidden" name="host:name" value="{$item['host:name']['keyValue']}">
{/if}
</form>
{/template}

View file

@ -1,108 +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.
{namespace registry.soy.registrar.hostepp}
/**
* Host create request.
*/
{template .create stricthtml="false"}
{@param item: ?}
{@param clTrid: ?}
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<host:create xmlns:host="urn:ietf:params:xml:ns:host-1.0">
<host:name>{$item['host:name']}</host:name>
{if isNonnull($item['host:addr'])}
{for $addr in $item['host:addr']}
{let $type: strContains($addr['value'], ':') ? 'v6' : 'v4' /}
<host:addr ip="{$type}">{$addr['value']}</host:addr>
{/for}
{/if}
</host:create>
</create>
<clTRID>{$clTrid}</clTRID>
</command>
</epp>
{/template}
/**
* Host update request.
*/
{template .update stricthtml="false"}
{@param item: ?}
{@param clTrid: ?}
{@param? addAddrs: ?} /** list of addrs to add. */
{@param? remAddrs: ?} /** list of addrs to remove. */
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<host:update xmlns:host="urn:ietf:params:xml:ns:host-1.0">
<host:name>{$item['host:name']}</host:name>
{call .addRem}
{param isAdd: true /}
{param addrs: $addAddrs /}
{/call}
{call .addRem}
{param isAdd: false /}
{param addrs: $remAddrs /}
{/call}
{if $item['host:name'] != $item['host:chgName']}
<host:chg>
<host:name>{$item['host:chgName']}</host:name>
</host:chg>
{/if}
</host:update>
</update>
<clTRID>{$clTrid}</clTRID>
</command>
</epp>
{/template}
/**
* Host info request.
*/
{template .info stricthtml="false"}
{@param clTrid: ?}
{@param id: ?} /** The hostname (named "id" to preserve component API). */
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<info>
<host:info xmlns:host="urn:ietf:params:xml:ns:host-1.0">
<host:name>{$id}</host:name>
</host:info>
</info>
<clTRID>{$clTrid}</clTRID>
</command>
</epp>
{/template}
{template .addRem}
{@param isAdd: ?}
{@param? addrs: ?}
{let $tagName: $isAdd ? 'host:add' : 'host:rem' /}
{if length($addrs) > 0}
<{$tagName}>
{for $addr in $addrs}
{let $type: strContains($addr, ':') ? 'v6' : 'v4' /}
<host:addr ip="{$type}">{$addr}</host:addr>
{/for}
</{$tagName}>
{/if}
{/template}

View file

@ -1,73 +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.
package google.registry.flows;
import static com.google.appengine.api.users.UserServiceFactory.getUserService;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import com.google.common.collect.ImmutableSetMultimap;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeHttpSession;
import google.registry.testing.ShardableTestCase;
import google.registry.testing.UserInfo;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
/** Tests for {@link EppConsoleAction}. */
@RunWith(JUnit4.class)
public class EppConsoleActionTest extends ShardableTestCase {
private static final byte[] INPUT_XML_BYTES = "<xml>".getBytes(UTF_8);
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withUserService(UserInfo.create("person@example.com", "12345"))
.build();
@Test
public void testAction() {
EppConsoleAction action = new EppConsoleAction();
action.inputXmlBytes = INPUT_XML_BYTES;
action.session = new FakeHttpSession();
action.clientId = "ClientIdentifier";
action.eppRequestHandler = mock(EppRequestHandler.class);
action.userService = getUserService();
action.registrarAccessor =
AuthenticatedRegistrarAccessor.createForTesting(ImmutableSetMultimap.of());
action.run();
ArgumentCaptor<TransportCredentials> credentialsCaptor =
ArgumentCaptor.forClass(TransportCredentials.class);
ArgumentCaptor<SessionMetadata> metadataCaptor = ArgumentCaptor.forClass(SessionMetadata.class);
verify(action.eppRequestHandler)
.executeEpp(
metadataCaptor.capture(),
credentialsCaptor.capture(),
eq(EppRequestSource.CONSOLE),
eq(false),
eq(false),
eq(INPUT_XML_BYTES));
assertThat(credentialsCaptor.getValue().toString()).contains("user=TestUserId");
assertThat(metadataCaptor.getValue().getClientId()).isEqualTo("ClientIdentifier");
}
}

View file

@ -1,61 +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.
package google.registry.flows;
import com.google.common.collect.ImmutableSetMultimap;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.testing.AppEngineRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Test logging in with appengine admin user credentials. */
@RunWith(JUnit4.class)
public class EppLoginAdminUserTest extends EppTestCase {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Before
public void initTransportCredentials() {
setTransportCredentials(
new GaeUserCredentials(
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of(
"TheRegistrar", AuthenticatedRegistrarAccessor.Role.ADMIN,
"NewRegistrar", AuthenticatedRegistrarAccessor.Role.ADMIN))));
}
@Test
public void testLoginLogout_wrongPasswordStillWorks() throws Exception {
// For user-based logins the password in the epp xml is ignored.
assertThatLoginSucceeds("NewRegistrar", "incorrect");
assertThatLogoutSucceeds();
}
@Test
public void testNonAuthedMultiLogin_succeedsAsAdmin() throws Exception {
// The admin can log in as different registrars.
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
assertThatLogoutSucceeds();
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
assertThatLogoutSucceeds();
assertThatLoginSucceeds("TheRegistrar", "password2");
}
}

View file

@ -1,81 +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.
package google.registry.flows;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSetMultimap;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.testing.AppEngineRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Test logging in with appengine user credentials, such as via the console. */
@RunWith(JUnit4.class)
public class EppLoginUserTest extends EppTestCase {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.build();
@Before
public void initTest() {
setTransportCredentials(
new GaeUserCredentials(
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of(
"NewRegistrar", AuthenticatedRegistrarAccessor.Role.OWNER))));
}
@Test
public void testLoginLogout() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
assertThatLogoutSucceeds();
}
@Test
public void testNonAuthedLogin_fails() throws Exception {
assertThatLogin("TheRegistrar", "password2")
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2200",
"MSG", "TestUserId doesn't have access to registrar TheRegistrar"));
}
@Test
public void testMultiLogin() throws Exception {
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
assertThatLogoutSucceeds();
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
assertThatLogoutSucceeds();
assertThatLogin("TheRegistrar", "password2")
.hasResponse(
"response_error.xml",
ImmutableMap.of(
"CODE", "2200",
"MSG", "TestUserId doesn't have access to registrar TheRegistrar"));
}
@Test
public void testLoginLogout_wrongPasswordStillWorks() throws Exception {
// For user-based logins the password in the epp xml is ignored.
assertThatLoginSucceeds("NewRegistrar", "incorrect");
assertThatLogoutSucceeds();
}
}

View file

@ -26,14 +26,12 @@ import static org.mockito.Mockito.verify;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.flogger.LoggerConfig; import com.google.common.flogger.LoggerConfig;
import com.google.common.testing.TestLogHandler; import com.google.common.testing.TestLogHandler;
import google.registry.model.eppcommon.Trid; import google.registry.model.eppcommon.Trid;
import google.registry.model.eppoutput.EppOutput.ResponseOrGreeting; import google.registry.model.eppoutput.EppOutput.ResponseOrGreeting;
import google.registry.model.eppoutput.EppResponse; import google.registry.model.eppoutput.EppResponse;
import google.registry.monitoring.whitebox.EppMetric; import google.registry.monitoring.whitebox.EppMetric;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.testing.AppEngineRule; import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeClock; import google.registry.testing.FakeClock;
import google.registry.testing.FakeHttpSession; import google.registry.testing.FakeHttpSession;
@ -140,16 +138,6 @@ public class FlowRunnerTest extends ShardableTestCase {
+ "{clientId=TheRegistrar, failedLoginAttempts=0, serviceExtensionUris=}"); + "{clientId=TheRegistrar, failedLoginAttempts=0, serviceExtensionUris=}");
} }
@Test
public void testRun_loggingStatement_gaeUserCredentials() throws Exception {
flowRunner.credentials =
new GaeUserCredentials(AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of()));
flowRunner.run(eppMetricBuilder);
assertThat(findFirstLogMessageByPrefix(handler, "EPP Command\n\t"))
.contains("user=TestUserId");
}
@Test @Test
public void testRun_loggingStatement_tlsCredentials() throws Exception { public void testRun_loggingStatement_tlsCredentials() throws Exception {
flowRunner.credentials = new TlsCredentials(true, "abc123def", Optional.of("127.0.0.1")); flowRunner.credentials = new TlsCredentials(true, "abc123def", Optional.of("127.0.0.1"));

View file

@ -1,58 +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.
package google.registry.flows.session;
import com.google.common.collect.ImmutableSetMultimap;
import google.registry.flows.GaeUserCredentials;
import google.registry.flows.GaeUserCredentials.UserForbiddenException;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import org.junit.Test;
/**
* Unit tests for {@link LoginFlow} when accessed via a web frontend
* transport, i.e. with GAIA ids.
*/
public class LoginFlowViaConsoleTest extends LoginFlowTestCase {
@Test
public void testSuccess_withAccess() throws Exception {
credentials =
new GaeUserCredentials(
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of(
"NewRegistrar", AuthenticatedRegistrarAccessor.Role.OWNER)));
doSuccessfulTest("login_valid.xml");
}
@Test
public void testFailure_withoutAccess() {
credentials =
new GaeUserCredentials(
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of()));
doFailingTest("login_valid.xml", UserForbiddenException.class);
}
@Test
public void testFailure_withAccessToDifferentRegistrar() {
credentials =
new GaeUserCredentials(
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of(
"TheRegistrar", AuthenticatedRegistrarAccessor.Role.OWNER)));
doFailingTest("login_valid.xml", UserForbiddenException.class);
}
}

View file

@ -5,4 +5,3 @@ PATH CLASS METHODS OK AUTH_METHODS
/registrar-ote-setup ConsoleOteSetupAction POST,GET n INTERNAL,API,LEGACY NONE PUBLIC /registrar-ote-setup ConsoleOteSetupAction POST,GET n INTERNAL,API,LEGACY NONE PUBLIC
/registrar-ote-status OteStatusAction POST n API,LEGACY USER PUBLIC /registrar-ote-status OteStatusAction POST n API,LEGACY USER PUBLIC
/registrar-settings RegistrarSettingsAction POST n API,LEGACY USER PUBLIC /registrar-settings RegistrarSettingsAction POST n API,LEGACY USER PUBLIC
/registrar-xhr EppConsoleAction POST n API,LEGACY USER PUBLIC

View file

@ -26,7 +26,6 @@ import google.registry.module.frontend.FrontendServlet;
import java.net.URL; import java.net.URL;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Optional; import java.util.Optional;
import javax.servlet.Filter; import javax.servlet.Filter;
@ -54,7 +53,6 @@ public final class RegistryTestServer {
// Frontend Services // Frontend Services
route("/whois/*", FrontendServlet.class), route("/whois/*", FrontendServlet.class),
route("/rdap/*", FrontendServlet.class), route("/rdap/*", FrontendServlet.class),
route("/registrar-xhr", FrontendServlet.class),
route("/check", FrontendServlet.class), route("/check", FrontendServlet.class),
// Proxy Services // Proxy Services

View file

@ -21,7 +21,6 @@ goog.require('goog.testing.MockControl');
goog.require('goog.testing.PropertyReplacer'); goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.asserts'); goog.require('goog.testing.asserts');
goog.require('goog.testing.jsunit'); goog.require('goog.testing.jsunit');
goog.require('goog.testing.mockmatchers');
goog.require('goog.testing.net.XhrIo'); goog.require('goog.testing.net.XhrIo');
goog.require('registry.registrar.ConsoleTestUtil'); goog.require('registry.registrar.ConsoleTestUtil');
goog.require('registry.testing'); goog.require('registry.testing');
@ -71,36 +70,6 @@ function testButter() {
} }
/**
* The EPP login should be triggered if the user `isGaeLoggedIn`
* but not yet `isEppLoggedIn`.
*/
function testEppLogin() {
// This is a little complex, as handleHashChange triggers an async
// event to do the EPP login with a callback to come back to
// handleHashChange after completion.
registry.registrar.ConsoleTestUtil.visit(
test, {
isEppLoggedIn: true,
clientId: test.testClientId,
xsrfToken: test.testXsrfToken,
productName: 'Foo Registry'
}, function() {
test.sessionMock.login(
goog.testing.mockmatchers.isFunction).$does(function() {
test.sessionMock.$reset();
test.sessionMock.isEppLoggedIn().$returns(true).$anyTimes();
test.sessionMock.getClientId().$returns(
test.testClientId).$anyTimes();
test.sessionMock.$replay();
test.console.handleHashChange(test.testClientId);
}).$anyTimes();
});
assertTrue(test.console.session.isEppLoggedIn());
assertNotNull(goog.dom.getElement('domain-registrar-dashboard'));
}
/** Authed user with no path op specified should nav to welcome page. */ /** Authed user with no path op specified should nav to welcome page. */
function testShowLoginOrDash() { function testShowLoginOrDash() {
registry.registrar.ConsoleTestUtil.visit(test, { registry.registrar.ConsoleTestUtil.visit(test, {

View file

@ -16,14 +16,9 @@ goog.provide('registry.registrar.ConsoleTestUtil');
goog.setTestOnly('registry.registrar.ConsoleTestUtil'); goog.setTestOnly('registry.registrar.ConsoleTestUtil');
goog.require('goog.History'); goog.require('goog.History');
goog.require('goog.asserts');
goog.require('goog.dom.xml');
goog.require('goog.soy'); goog.require('goog.soy');
goog.require('goog.testing.mockmatchers');
goog.require('registry.registrar.Console'); goog.require('registry.registrar.Console');
goog.require('registry.registrar.EppSession');
goog.require('registry.soy.registrar.console'); goog.require('registry.soy.registrar.console');
goog.require('registry.xml');
/** /**
@ -34,14 +29,8 @@ goog.require('registry.xml');
*/ */
registry.registrar.ConsoleTestUtil.setup = function(test) { registry.registrar.ConsoleTestUtil.setup = function(test) {
test.historyMock = test.mockControl.createLooseMock(goog.History, true); test.historyMock = test.mockControl.createLooseMock(goog.History, true);
test.sessionMock = test.mockControl.createLooseMock(
registry.registrar.EppSession, true);
test.mockControl.createConstructorMock(goog, 'History')() test.mockControl.createConstructorMock(goog, 'History')()
.$returns(test.historyMock); .$returns(test.historyMock);
test.mockControl
.createConstructorMock(registry.registrar, 'EppSession')(
goog.testing.mockmatchers.isObject)
.$returns(test.sessionMock);
}; };
/** /**
@ -76,10 +65,10 @@ registry.registrar.ConsoleTestUtil.renderConsoleMain = function(
/** /**
* Simulates visiting a page on the console. Sets path, then mocks * Simulates visiting a page on the console. Sets path, then calls
* session and calls `handleHashChange_`. * `handleHashChange_`.
* @param {!Object} test the test case to configure. * @param {!Object} test the test case to configure.
* @param {?Object=} opt_args may include path, isEppLoggedIn. * @param {?Object=} opt_args may include path.
* @param {?Function=} opt_moar extra setup after called just before * @param {?Function=} opt_moar extra setup after called just before
* `$replayAll`. See memegen/3437690. * `$replayAll`. See memegen/3437690.
*/ */
@ -99,33 +88,17 @@ registry.registrar.ConsoleTestUtil.visit = function(
if (opt_args.isOwner === undefined) { if (opt_args.isOwner === undefined) {
opt_args.isOwner = !opt_args.isAdmin; opt_args.isOwner = !opt_args.isAdmin;
} }
if (opt_args.isEppLoggedIn === undefined) {
opt_args.isEppLoggedIn = true;
}
test.historyMock.$reset(); test.historyMock.$reset();
test.sessionMock.$reset();
test.historyMock.getToken().$returns(opt_args.path).$anyTimes(); test.historyMock.getToken().$returns(opt_args.path).$anyTimes();
test.sessionMock.isEppLoggedIn().$returns(opt_args.isEppLoggedIn).$anyTimes();
test.sessionMock.getClientId().$returns(opt_args.isEppLoggedIn ?
opt_args.clientId : null).$anyTimes();
if (opt_args.rspXml) {
test.sessionMock
.send(goog.testing.mockmatchers.isString,
goog.testing.mockmatchers.isFunction)
.$does(function(args, cb) {
// XXX: Args should be checked.
const xml = goog.dom.xml.loadXml(opt_args.rspXml);
goog.asserts.assert(xml != null);
cb(registry.xml.convertToJson(xml));
}).$anyTimes();
}
if (opt_moar) { if (opt_moar) {
opt_moar(); opt_moar();
} }
test.mockControl.$replayAll(); test.mockControl.$replayAll();
/** @type {!registry.registrar.Console} */ /** @type {!registry.registrar.Console} */
test.console = new registry.registrar.Console(opt_args); test.console = new registry.registrar.Console(opt_args);
// XXX: Should be triggered via event passing. test.console.setUp();
// Should be triggered via the History object in test.console.setUp(), but
// since we're using a mock that isn't happening. So we call it manually.
test.console.handleHashChange(); test.console.handleHashChange();
test.mockControl.$verifyAll(); test.mockControl.$verifyAll();
}; };

View file

@ -1,119 +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.setTestOnly();
goog.require('goog.dispose');
goog.require('goog.dom');
goog.require('goog.testing.MockControl');
goog.require('goog.testing.asserts');
goog.require('goog.testing.jsunit');
goog.require('registry.registrar.ConsoleTestUtil');
goog.require('registry.testing');
const $ = goog.dom.getRequiredElement;
const test = {
mockControl: new goog.testing.MockControl()
};
function setUp() {
registry.testing.addToDocument('<div id="test"/>');
registry.testing.addToDocument('<div class="kd-butterbar"/>');
registry.registrar.ConsoleTestUtil.renderConsoleMain($('test'), {});
registry.registrar.ConsoleTestUtil.setup(test);
}
function tearDown() {
goog.dispose(test.console);
test.mockControl.$tearDown();
}
/** Contact hash path should nav to contact page. */
function testVisitContact() {
registry.registrar.ConsoleTestUtil.visit(test, {
path: 'contact/pabloistrad',
rspXml: '<?xml version="1.0"?>' +
'<epp xmlns:domain="urn:ietf:params:xml:ns:domain-1.0"' +
' xmlns:contact="urn:ietf:params:xml:ns:contact-1.0"' +
' xmlns:host="urn:ietf:params:xml:ns:host-1.0"' +
' xmlns:launch="urn:ietf:params:xml:ns:launch-1.0"' +
' xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0"' +
' xmlns="urn:ietf:params:xml:ns:epp-1.0"' +
' xmlns:secDNS="urn:ietf:params:xml:ns:secDNS-1.1"' +
' xmlns:mark="urn:ietf:params:xml:ns:mark-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>Command completed successfully</msg>' +
' </result>' +
' <resData>' +
' <contact:infData>' +
' <contact:id>pabloistrad</contact:id>' +
' <contact:roid>1-roid</contact:roid>' +
' <contact:status s="ok"/>' +
' <contact:postalInfo type="int">' +
' <contact:name>name2</contact:name>' +
' <contact:addr>' +
' <contact:street></contact:street>' +
' <contact:city>city2</contact:city>' +
' <contact:cc>US</contact:cc>' +
' </contact:addr>' +
' </contact:postalInfo>' +
' <contact:voice/>' +
' <contact:fax/>' +
' <contact:email>test2.ui@example.com</contact:email>' +
' <contact:clID>daddy</contact:clID>' +
' <contact:crID>daddy</contact:crID>' +
' <contact:crDate>2014-05-06T22:16:36Z</contact:crDate>' +
' <contact:upID>daddy</contact:upID>' +
' <contact:upDate>2014-05-07T16:20:07Z</contact:upDate>' +
' <contact:authInfo>' +
' <contact:pw>asdfasdf</contact:pw>' +
' </contact:authInfo>' +
' </contact:infData>' +
' </resData>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>c4O3B0pRRKKSrrXsJvxP5w==-2</svTRID>' +
' </trID>' +
' </response>' +
'</epp>'
});
assertEquals(2, $('contact-postalInfo').childNodes.length);
}
/** Contact hash path should nav to contact page. */
function testEdit() {
testVisitContact();
registry.testing.assertVisible($('reg-app-btns-edit'));
registry.testing.click($('reg-app-btn-edit'));
registry.testing.assertHidden($('reg-app-btns-edit'));
registry.testing.assertVisible($('reg-app-btns-save'));
}
/** Contact hash path should nav to contact page. */
function testAddPostalInfo() {
testEdit();
const addPiBtn = $('domain-contact-postalInfo-add-button');
assertNull(addPiBtn.getAttribute('disabled'));
registry.testing.click(addPiBtn);
assertTrue(addPiBtn.hasAttribute('disabled'));
assertEquals(3, $('contact-postalInfo').childNodes.length);
}

View file

@ -1,475 +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.setTestOnly();
goog.require('goog.History');
goog.require('goog.dispose');
goog.require('goog.dom');
goog.require('goog.testing.MockControl');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.asserts');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.mockmatchers');
goog.require('goog.testing.net.XhrIo');
goog.require('registry.registrar.Console');
goog.require('registry.registrar.ConsoleTestUtil');
goog.require('registry.testing');
const $ = goog.dom.getRequiredElement;
const _ = goog.testing.mockmatchers.ignoreArgument;
const stubs = new goog.testing.PropertyReplacer();
const mocks = new goog.testing.MockControl();
let historyMock;
let registrarConsole;
function setUp() {
registry.testing.addToDocument('<div id="test"/>');
registry.testing.addToDocument('<div class="kd-butterbar"/>');
registry.registrar.ConsoleTestUtil.renderConsoleMain($('test'), {});
stubs.setPath('goog.net.XhrIo', goog.testing.net.XhrIo);
historyMock = mocks.createStrictMock(goog.History);
mocks.createConstructorMock(goog, 'History')().$returns(historyMock);
historyMock.addEventListener(_, _, _);
historyMock.setEnabled(true);
mocks.$replayAll();
registrarConsole = new registry.registrar.Console({
xsrfToken: '☢',
clientId: 'jartine'
});
mocks.$verifyAll();
}
function tearDown() {
goog.dispose(registrarConsole);
stubs.reset();
mocks.$tearDown();
goog.testing.net.XhrIo.cleanup();
}
/** Handles EPP login. */
function handleLogin() {
const request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <login>' +
' <clID>jartine</clID>' +
' <pw>undefined</pw>' +
' <options>' +
' <version>1.0</version>' +
' <lang>en</lang>' +
' </options>' +
' <svcs>' +
' <objURI>urn:ietf:params:xml:ns:host-1.0</objURI>' +
' <objURI>urn:ietf:params:xml:ns:domain-1.0</objURI>' +
' <objURI>urn:ietf:params:xml:ns:contact-1.0</objURI>' +
' </svcs>' +
' </login>' +
' <clTRID>asdf-1235</clTRID>' +
' </command>' +
'</epp>');
const response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <response>' +
' <result code="2002">' +
' <msg>Registrar is already logged in</msg>' +
' </result>' +
' <trID>' +
' <clTRID>asdf-1235</clTRID>' +
' <svTRID>ytk1RO+8SmaDQxrTIdulnw==-3</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
const xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue(xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
}
function testView() {
historyMock.$reset();
historyMock.getToken().$returns('domain/justine.lol').$anyTimes();
mocks.$replayAll();
registrarConsole.handleHashChange();
handleLogin();
const request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <info>' +
' <domain:info xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">' +
' <domain:name hosts="all">justine.lol</domain:name>' +
' </domain:info>' +
' </info>' +
' <clTRID>abc-1234</clTRID>' +
' </command>' +
'</epp>');
const response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>Command completed successfully</msg>' +
' </result>' +
' <resData>' +
' <domain:infData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">' +
' <domain:name>justine.lol</domain:name>' +
' <domain:roid>6-roid</domain:roid>' +
' <domain:status s="inactive"/>' +
' <domain:registrant>GK Chesterton</domain:registrant>' +
' <domain:contact type="admin">&lt;justine&gt;</domain:contact>' +
' <domain:contact type="billing">candycrush</domain:contact>' +
' <domain:contact type="tech">krieger</domain:contact>' +
' <domain:ns>' +
' <domain:hostObj>ns1.justine.lol</domain:hostObj>' +
' <domain:hostObj>ns2.justine.lol</domain:hostObj>' +
' </domain:ns>' +
' <domain:host>ns1.justine.lol</domain:host>' +
' <domain:clID>justine</domain:clID>' +
' <domain:crID>justine</domain:crID>' +
' <domain:crDate>2014-07-10T02:17:02Z</domain:crDate>' +
' <domain:exDate>2015-07-10T02:17:02Z</domain:exDate>' +
' <domain:authInfo>' +
' <domain:pw>lolcat</domain:pw>' +
' </domain:authInfo>' +
' </domain:infData>' +
' </resData>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>ytk1RO+8SmaDQxrTIdulnw==-4</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
const xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
assertEquals('We require more vespene gas.',
0, goog.testing.net.XhrIo.getSendInstances().length);
mocks.$verifyAll();
assertTrue('Form should be read-only.', $('domain:exDate').readOnly);
assertContains('justine.lol', $('reg-content').innerHTML);
assertEquals('2015-07-10T02:17:02Z', $('domain:exDate').value);
assertEquals('GK Chesterton', $('domain:registrant').value);
assertEquals('<justine>', $('domain:contact[0].value').value);
assertEquals('candycrush', $('domain:contact[1].value').value);
assertEquals('krieger', $('domain:contact[2].value').value);
assertEquals('lolcat', $('domain:authInfo.domain:pw').value);
assertEquals('ns1.justine.lol', $('domain:ns.domain:hostObj[0].value').value);
assertEquals('ns2.justine.lol', $('domain:ns.domain:hostObj[1].value').value);
}
function testEdit() {
testView();
historyMock.$reset();
mocks.$replayAll();
registry.testing.click($('reg-app-btn-edit'));
assertFalse('Form should be edible.', $('domain:exDate').readOnly);
$('domain:registrant').value = 'Jonathan Swift';
$('domain:authInfo.domain:pw').value = '(✿◕‿◕)';
registry.testing.click($('reg-app-btn-save'));
let request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <update>' +
' <domain:update xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">' +
' <domain:name>justine.lol</domain:name>' +
' <domain:chg>' +
' <domain:registrant>Jonathan Swift</domain:registrant>' +
' <domain:authInfo>' +
' <domain:pw>(✿◕‿◕)</domain:pw>' +
' </domain:authInfo>' +
' </domain:chg>' +
' </domain:update>' +
' </update>' +
' <clTRID>abc-1234</clTRID>' +
' </command>' +
'</epp>');
let response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>This world is built from a million lies.</msg>' +
' </result>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>214CjbYuTsijoP8sgyFUNg==-e</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
let xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <info>' +
' <domain:info xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">' +
' <domain:name hosts="all">justine.lol</domain:name>' +
' </domain:info>' +
' </info>' +
' <clTRID>abc-1234</clTRID>' +
' </command>' +
'</epp>');
response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>How can we live in the land of the dead?</msg>' +
' </result>' +
' <resData>' +
' <domain:infData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">' +
' <domain:name>justine.lol</domain:name>' +
' <domain:roid>6-roid</domain:roid>' +
' <domain:status s="inactive"/>' +
' <domain:registrant>Jonathan Swift</domain:registrant>' +
' <domain:contact type="admin">&lt;justine&gt;</domain:contact>' +
' <domain:contact type="billing">candycrush</domain:contact>' +
' <domain:contact type="tech">krieger</domain:contact>' +
' <domain:ns>' +
' <domain:hostObj>ns1.justine.lol</domain:hostObj>' +
' <domain:hostObj>ns2.justine.lol</domain:hostObj>' +
' </domain:ns>' +
' <domain:host>ns1.justine.lol</domain:host>' +
' <domain:clID>justine</domain:clID>' +
' <domain:crID>justine</domain:crID>' +
' <domain:crDate>2014-07-10T02:17:02Z</domain:crDate>' +
' <domain:exDate>2015-07-10T02:17:02Z</domain:exDate>' +
' <domain:authInfo>' +
' <domain:pw>(✿◕‿◕)</domain:pw>' +
' </domain:authInfo>' +
' </domain:infData>' +
' </resData>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>ytk1RO+8SmaDQxrTIdulnw==-4</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
assertEquals('We require more vespene gas.',
0, goog.testing.net.XhrIo.getSendInstances().length);
mocks.$verifyAll();
assertTrue($('domain:exDate').readOnly);
assertContains('justine.lol', $('reg-content').innerHTML);
assertEquals('2015-07-10T02:17:02Z', $('domain:exDate').value);
assertEquals('Jonathan Swift', $('domain:registrant').value);
assertEquals('<justine>', $('domain:contact[0].value').value);
assertEquals('candycrush', $('domain:contact[1].value').value);
assertEquals('krieger', $('domain:contact[2].value').value);
assertEquals('(✿◕‿◕)', $('domain:authInfo.domain:pw').value);
assertEquals('ns1.justine.lol', $('domain:ns.domain:hostObj[0].value').value);
assertEquals('ns2.justine.lol', $('domain:ns.domain:hostObj[1].value').value);
}
function testEdit_cancel_restoresOriginalValues() {
testView();
registry.testing.click($('reg-app-btn-edit'));
assertFalse('Form should be edible.', $('domain:exDate').readOnly);
$('domain:registrant').value = 'Jonathan Swift';
$('domain:authInfo.domain:pw').value = '(✿◕‿◕)';
registry.testing.click($('reg-app-btn-cancel'));
assertTrue('Form should be read-only.', $('domain:exDate').readOnly);
assertEquals('GK Chesterton', $('domain:registrant').value);
assertEquals('lolcat', $('domain:authInfo.domain:pw').value);
}
function testCreate() {
historyMock.$reset();
historyMock.getToken().$returns('domain').$anyTimes();
mocks.$replayAll();
registrarConsole.handleHashChange();
handleLogin();
mocks.$verifyAll();
assertFalse('Form should be edible.', $('domain:name').readOnly);
$('domain:name').value = 'bog.lol';
$('domain:period').value = '1';
$('domain:authInfo.domain:pw').value = 'attorney at lawl';
$('domain:registrant').value = 'Chris Pohl';
registry.testing.click($('domain-contact-add-button'));
$('domain:contact[0].value').value = 'BlutEngel';
$('domain:contact[0].@type').value = 'admin';
registry.testing.click($('domain-contact-add-button'));
$('domain:contact[1].value').value = 'Ravenous';
$('domain:contact[1].@type').value = 'tech';
registry.testing.click($('domain-contact-add-button'));
$('domain:contact[2].value').value = 'Dark Angels';
$('domain:contact[2].@type').value = 'billing';
historyMock.$reset();
mocks.$replayAll();
registry.testing.click($('reg-app-btn-save'));
let request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <create>' +
' <domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">' +
' <domain:name>bog.lol</domain:name>' +
' <domain:period unit="y">1</domain:period>' +
' <domain:registrant>Chris Pohl</domain:registrant>' +
' <domain:contact type="admin">BlutEngel</domain:contact>' +
' <domain:contact type="tech">Ravenous</domain:contact>' +
' <domain:contact type="billing">Dark Angels</domain:contact>' +
' <domain:authInfo>' +
' <domain:pw>attorney at lawl</domain:pw>' +
' </domain:authInfo>' +
' </domain:create>' +
' </create>' +
' <clTRID>abc-1234</clTRID>' +
' </command>' +
'</epp>');
let response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>Command completed successfully</msg>' +
' </result>' +
' <resData>' +
' <domain:creData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">' +
' <domain:name>bog.lol</domain:name>' +
' <domain:crDate>2014-07-17T08:19:24Z</domain:crDate>' +
' <domain:exDate>2015-07-17T08:19:24Z</domain:exDate>' +
' </domain:creData>' +
' </resData>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>OBPI6JvEQfOUaO8qGf+IKA==-7</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
let xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <info>' +
' <domain:info xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">' +
' <domain:name hosts="all">bog.lol</domain:name>' +
' </domain:info>' +
' </info>' +
' <clTRID>abc-1234</clTRID>' +
' </command>' +
'</epp>');
response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>Command completed successfully</msg>' +
' </result>' +
' <resData>' +
' <domain:infData xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">' +
' <domain:name>bog.lol</domain:name>' +
' <domain:roid>1f-roid</domain:roid>' +
' <domain:status s="inactive"/>' +
' <domain:registrant>Chris Pohl</domain:registrant>' +
' <domain:contact type="admin">BlutEngel</domain:contact>' +
' <domain:contact type="tech">Ravenous</domain:contact>' +
' <domain:contact type="billing">Dark Angels</domain:contact>' +
' <domain:clID>justine</domain:clID>' +
' <domain:crID>justine</domain:crID>' +
' <domain:crDate>2014-07-17T08:19:24Z</domain:crDate>' +
' <domain:exDate>2015-07-17T08:19:24Z</domain:exDate>' +
' <domain:authInfo>' +
' <domain:pw>attorney at lawl</domain:pw>' +
' </domain:authInfo>' +
' </domain:infData>' +
' </resData>' +
' <extension>' +
' <rgp:infData xmlns:rgp="urn:ietf:params:xml:ns:rgp-1.0">' +
' <rgp:rgpStatus s="addPeriod"/>' +
' </rgp:infData>' +
' </extension>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>OBPI6JvEQfOUaO8qGf+IKA==-8</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
assertEquals('We require more vespene gas.',
0, goog.testing.net.XhrIo.getSendInstances().length);
mocks.$verifyAll();
assertTrue('Form should be read-only.', $('domain:exDate').readOnly);
assertContains('bog.lol', $('reg-content').innerHTML);
assertEquals('2015-07-17T08:19:24Z', $('domain:exDate').value);
assertEquals('Chris Pohl', $('domain:registrant').value);
assertEquals('BlutEngel', $('domain:contact[0].value').value);
assertEquals('Ravenous', $('domain:contact[1].value').value);
assertEquals('Dark Angels', $('domain:contact[2].value').value);
assertEquals('attorney at lawl', $('domain:authInfo.domain:pw').value);
assertNull(goog.dom.getElement('domain:ns.domain:hostObj[0].value'));
}

View file

@ -1,415 +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.setTestOnly();
goog.require('goog.History');
goog.require('goog.dispose');
goog.require('goog.dom');
goog.require('goog.testing.MockControl');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.asserts');
goog.require('goog.testing.jsunit');
goog.require('goog.testing.mockmatchers');
goog.require('goog.testing.net.XhrIo');
goog.require('registry.registrar.Console');
goog.require('registry.registrar.ConsoleTestUtil');
goog.require('registry.testing');
const $ = goog.dom.getRequiredElement;
const _ = goog.testing.mockmatchers.ignoreArgument;
const stubs = new goog.testing.PropertyReplacer();
const mocks = new goog.testing.MockControl();
let historyMock;
let registrarConsole;
function setUp() {
registry.testing.addToDocument('<div id="test"/>');
registry.testing.addToDocument('<div class="kd-butterbar"/>');
registry.registrar.ConsoleTestUtil.renderConsoleMain($('test'), {});
stubs.setPath('goog.net.XhrIo', goog.testing.net.XhrIo);
historyMock = mocks.createStrictMock(goog.History);
mocks.createConstructorMock(goog, 'History')().$returns(historyMock);
historyMock.addEventListener(_, _, _);
historyMock.setEnabled(true);
mocks.$replayAll();
registrarConsole = new registry.registrar.Console({
xsrfToken: '☢',
clientId: 'jartine'
});
mocks.$verifyAll();
}
function tearDown() {
goog.dispose(registrarConsole);
stubs.reset();
mocks.$tearDown();
goog.testing.net.XhrIo.cleanup();
}
/** Handles EPP login. */
function handleLogin() {
const request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <login>' +
' <clID>jartine</clID>' +
' <pw>undefined</pw>' +
' <options>' +
' <version>1.0</version>' +
' <lang>en</lang>' +
' </options>' +
' <svcs>' +
' <objURI>urn:ietf:params:xml:ns:host-1.0</objURI>' +
' <objURI>urn:ietf:params:xml:ns:domain-1.0</objURI>' +
' <objURI>urn:ietf:params:xml:ns:contact-1.0</objURI>' +
' </svcs>' +
' </login>' +
' <clTRID>asdf-1235</clTRID>' +
' </command>' +
'</epp>');
const response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <response>' +
' <result code="2002">' +
' <msg>Registrar is already logged in</msg>' +
' </result>' +
' <trID>' +
' <clTRID>asdf-1235</clTRID>' +
' <svTRID>ytk1RO+8SmaDQxrTIdulnw==-3</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
const xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue(xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
}
function testView() {
historyMock.$reset();
historyMock.getToken().$returns('host/ns1.justine.lol').$anyTimes();
mocks.$replayAll();
registrarConsole.handleHashChange();
handleLogin();
const request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <info>' +
' <host:info xmlns:host="urn:ietf:params:xml:ns:host-1.0">' +
' <host:name>ns1.justine.lol</host:name>' +
' </host:info>' +
' </info>' +
' <clTRID>abc-1234</clTRID>' +
' </command>' +
'</epp>');
const response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"' +
' xmlns:host="urn:ietf:params:xml:ns:host-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>Command completed successfully</msg>' +
' </result>' +
' <resData>' +
' <host:infData>' +
' <host:name>ns1.justine.lol</host:name>' +
' <host:roid>8-roid</host:roid>' +
' <host:status s="ok"/>' +
' <host:addr ip="v4">8.8.8.8</host:addr>' +
' <host:addr ip="v6">feed:a:bee::1</host:addr>' +
' <host:clID>justine</host:clID>' +
' <host:crID>justine</host:crID>' +
' <host:crDate>2014-07-10T02:18:34Z</host:crDate>' +
' </host:infData>' +
' </resData>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>EweBEzCZTJirOqRmrtYrAA==-b</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
const xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('application/epp+xml',
xhr.getLastRequestHeaders()['Content-Type']);
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
assertEquals('We require more vespene gas.',
0, goog.testing.net.XhrIo.getSendInstances().length);
mocks.$verifyAll();
assertTrue('Form should be read-only.', $('host:chgName').readOnly);
assertContains('ns1.justine.lol', $('reg-content').innerHTML);
assertEquals('ns1.justine.lol', $('host:chgName').value);
assertEquals('8.8.8.8', $('host:addr[0].value').value);
assertEquals('feed:a:bee::1', $('host:addr[1].value').value);
}
function testEditFirstAddr_ignoreSecond_addThird() {
testView();
historyMock.$reset();
mocks.$replayAll();
registry.testing.click($('reg-app-btn-edit'));
assertFalse('Form should be edible.', $('host:addr[0].value').readOnly);
$('host:addr[0].value').value = '1.2.3.4';
registry.testing.click($('domain-host-addr-add-button'));
$('host:addr[2].value').value = 'feed:a:fed::1';
registry.testing.click($('reg-app-btn-save'));
let request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <update>' +
' <host:update xmlns:host="urn:ietf:params:xml:ns:host-1.0">' +
' <host:name>ns1.justine.lol</host:name>' +
' <host:add>' +
' <host:addr ip="v4">1.2.3.4</host:addr>' +
' <host:addr ip="v6">feed:a:fed::1</host:addr>' +
' </host:add>' +
' <host:rem>' +
' <host:addr ip="v4">8.8.8.8</host:addr>' +
' </host:rem>' +
' </host:update>' +
' </update>' +
' <clTRID>abc-1234</clTRID>' +
' </command>' +
'</epp>');
let response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>This world is built from a million lies.</msg>' +
' </result>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>214CjbYuTsijoP8sgyFUNg==-e</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
let xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <info>' +
' <host:info xmlns:host="urn:ietf:params:xml:ns:host-1.0">' +
' <host:name>ns1.justine.lol</host:name>' +
' </host:info>' +
' </info>' +
' <clTRID>abc-1234</clTRID>' +
' </command>' +
'</epp>');
response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0"' +
' xmlns:host="urn:ietf:params:xml:ns:host-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>Command completed successfully</msg>' +
' </result>' +
' <resData>' +
' <host:infData>' +
' <host:name>ns1.justine.lol</host:name>' +
' <host:roid>8-roid</host:roid>' +
' <host:status s="ok"/>' +
' <host:addr ip="v6">feed:a:bee::1</host:addr>' +
' <host:addr ip="v4">1.2.3.4</host:addr>' +
' <host:addr ip="v6">feed:a:fed::1</host:addr>' +
' <host:clID>justine</host:clID>' +
' <host:crID>justine</host:crID>' +
' <host:crDate>2014-07-10T02:18:34Z</host:crDate>' +
' </host:infData>' +
' </resData>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>EweBEzCZTJirOqRmrtYrAA==-b</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
assertEquals('We require more vespene gas.',
0, goog.testing.net.XhrIo.getSendInstances().length);
mocks.$verifyAll();
assertTrue('Form should be read-only.', $('host:chgName').readOnly);
assertContains('ns1.justine.lol', $('reg-content').innerHTML);
assertEquals('ns1.justine.lol', $('host:chgName').value);
assertEquals('feed:a:bee::1', $('host:addr[0].value').value);
assertEquals('1.2.3.4', $('host:addr[1].value').value);
assertEquals('feed:a:fed::1', $('host:addr[2].value').value);
}
function testCreate() {
historyMock.$reset();
historyMock.getToken().$returns('host').$anyTimes();
mocks.$replayAll();
registrarConsole.handleHashChange();
handleLogin();
mocks.$verifyAll();
assertFalse('Form should be edible.', $('host:name').readOnly);
$('host:name').value = 'ns1.example.tld';
registry.testing.click($('domain-host-addr-add-button'));
$('host:addr[0].value').value = '192.0.2.2';
registry.testing.click($('domain-host-addr-add-button'));
$('host:addr[1].value').value = '192.0.2.29';
registry.testing.click($('domain-host-addr-add-button'));
$('host:addr[2].value').value = '1080:0:0:0:8:800:200C:417A';
historyMock.$reset();
mocks.$replayAll();
registry.testing.click($('reg-app-btn-save'));
let request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <create>' +
' <host:create xmlns:host="urn:ietf:params:xml:ns:host-1.0">' +
' <host:name>ns1.example.tld</host:name>' +
' <host:addr ip="v4">192.0.2.2</host:addr>' +
' <host:addr ip="v4">192.0.2.29</host:addr>' +
' <host:addr ip="v6">1080:0:0:0:8:800:200C:417A</host:addr>' +
' </host:create>' +
' </create>' +
' <clTRID>abc-1234</clTRID>' +
' </command>' +
'</epp>');
let response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>Command completed successfully</msg>' +
' </result>' +
' <resData>' +
' <host:creData xmlns:host="urn:ietf:params:xml:ns:host-1.0">' +
' <host:name>ns1.example.tld</host:name>' +
' <host:crDate>1999-04-03T22:00:00.0Z</host:crDate>' +
' </host:creData>' +
' </resData>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>EweBEzCZTJirOqRmrtYrAA==-b</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
let xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
request = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <command>' +
' <info>' +
' <host:info xmlns:host="urn:ietf:params:xml:ns:host-1.0">' +
' <host:name>ns1.example.tld</host:name>' +
' </host:info>' +
' </info>' +
' <clTRID>abc-1234</clTRID>' +
' </command>' +
'</epp>');
response = registry.testing.loadXml(
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>Command completed successfully</msg>' +
' </result>' +
' <resData>' +
' <host:infData xmlns:host="urn:ietf:params:xml:ns:host-1.0">' +
' <host:name>ns1.example.tld</host:name>' +
' <host:roid>NS1_EXAMPLE1-REP</host:roid>' +
' <host:status s="linked"/>' +
' <host:status s="clientUpdateProhibited"/>' +
' <host:addr ip="v4">192.0.2.2</host:addr>' +
' <host:addr ip="v4">192.0.2.29</host:addr>' +
' <host:addr ip="v6">1080:0:0:0:8:800:200C:417A</host:addr>' +
' <host:clID>TheRegistrar</host:clID>' +
' <host:crID>NewRegistrar</host:crID>' +
' <host:crDate>1999-04-03T22:00:00.0Z</host:crDate>' +
' <host:upID>NewRegistrar</host:upID>' +
' <host:upDate>1999-12-03T09:00:00.0Z</host:upDate>' +
' <host:trDate>2000-04-08T09:00:00.0Z</host:trDate>' +
' </host:infData>' +
' </resData>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>EweBEzCZTJirOqRmrtYrAA==-b</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
assertEquals('We require more vespene gas.',
0, goog.testing.net.XhrIo.getSendInstances().length);
mocks.$verifyAll();
assertTrue('Form should be read-only.', $('host:chgName').readOnly);
assertEquals('ns1.example.tld', $('host:chgName').value);
assertEquals('192.0.2.2', $('host:addr[0].value').value);
assertEquals('192.0.2.29', $('host:addr[1].value').value);
assertEquals('1080:0:0:0:8:800:200C:417A', $('host:addr[2].value').value);
}

View file

@ -15,15 +15,12 @@
goog.provide('registry.testing'); goog.provide('registry.testing');
goog.setTestOnly('registry.testing'); goog.setTestOnly('registry.testing');
goog.require('goog.asserts');
goog.require('goog.dom'); goog.require('goog.dom');
goog.require('goog.dom.classlist'); goog.require('goog.dom.classlist');
goog.require('goog.dom.xml');
goog.require('goog.events.EventType'); goog.require('goog.events.EventType');
goog.require('goog.format.JsonPrettyPrinter'); goog.require('goog.format.JsonPrettyPrinter');
goog.require('goog.html.testing'); goog.require('goog.html.testing');
goog.require('goog.json'); goog.require('goog.json');
goog.require('goog.testing.asserts');
goog.require('goog.testing.events'); goog.require('goog.testing.events');
goog.require('goog.testing.events.Event'); goog.require('goog.testing.events.Event');
goog.require('goog.testing.net.XhrIo'); goog.require('goog.testing.net.XhrIo');
@ -40,32 +37,6 @@ registry.testing.addToDocument = function(html) {
}; };
/**
* Extracts XML document from inside an `<iframe>`.
* @param {string} xmlText
* @return {!Document}
*/
registry.testing.loadXml = function(xmlText) {
var xml = goog.dom.xml.loadXml(xmlText);
goog.asserts.assert(xml != null);
if ('parsererror' in xml) {
fail(xml['parsererror']['keyValue']);
}
return xml;
};
/**
* Extracts plain text string from inside an `<iframe>`.
* @param {string|!Document|!Element} want
* @param {string|!Document|!Element} got
*/
registry.testing.assertXmlEquals = function(want, got) {
assertHTMLEquals(registry.testing.sanitizeXml_(want),
registry.testing.sanitizeXml_(got));
};
/** /**
* Simulates a mouse click on a browser element. * Simulates a mouse click on a browser element.
* @param {!Element} element * @param {!Element} element
@ -135,22 +106,6 @@ registry.testing.assertReqMockRsp =
}; };
/**
* Removes stuff from XML text that we don't want to compare.
* @param {string|!Document|!Element} xml
* @return {string}
* @private
*/
registry.testing.sanitizeXml_ = function(xml) {
var xmlString = goog.isString(xml) ? xml : goog.dom.xml.serialize(xml);
return xmlString
.replace(/^\s*<\?.*?\?>\s*/, '') // Remove declaration thing.
.replace(/xmlns(:\w+)?="[^"]+"/g, '') // Remove namespace things.
.replace(/>\s+</g, '><') // Remove spaces between XML tags.
.replace(/<!--.*?-->/, ''); // Remove comments.
};
/** /**
* JSON pretty printer. * JSON pretty printer.
* @type {!goog.format.JsonPrettyPrinter} * @type {!goog.format.JsonPrettyPrinter}

View file

@ -1,183 +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.setTestOnly();
goog.require('goog.dom.xml');
goog.require('goog.testing.asserts');
goog.require('goog.testing.jsunit');
goog.require('registry.testing');
goog.require('registry.xml');
function testEmptyElement_hasNoKeyValue() {
assertXmlTurnsIntoJson(
{'epp': {}},
'<epp></epp>');
}
function testSelfClosingRootElement_hasNoKeyValue() {
assertXmlTurnsIntoJson(
{'epp': {}},
'<epp/>');
}
function testElementWithWhitespaceTextContent_getsIgnored() {
assertXmlTurnsIntoJson(
{'epp': {}},
'<epp> \r\n </epp>');
}
function testElementWithTextContent_getsSetToKeyValueField() {
assertXmlTurnsIntoJson(
{'epp': {'keyValue': 'hello'}},
'<epp>hello</epp>');
}
function testTextWithSpacesOnSides_getsTrimmed() {
assertXmlTurnsIntoJson(
{'epp': {'keyValue': 'hello'}},
'<epp> hello </epp>');
}
function testAttribute_getsSetToFieldPrefixedByAtSymbol() {
assertXmlTurnsIntoJson(
{'epp': {'@ohmy': 'goth'}},
'<epp ohmy="goth"/>');
}
function testSingleNestedElement_keyIsNameAndValueIsNode() {
assertXmlTurnsIntoJson(
{'epp': {'ohmy': {'keyValue': 'goth'}}},
'<epp><ohmy>goth</ohmy></epp>');
}
function testMultipleNestedElements_valueBecomesArray() {
assertXmlTurnsIntoJson(
{'epp': {'ohmy': [{'keyValue': 'goth1'}, {'keyValue': 'goth2'}]}},
'<epp><ohmy>goth1</ohmy><ohmy>goth2</ohmy></epp>');
}
function testInterspersedText_throwsError() {
assertEquals(
'XML text "hello" interspersed with "there"',
assertThrows(function() {
registry.xml.convertToJson(
goog.dom.xml.loadXml(
'<epp> hello <omg/> there </epp>'));
}).message);
}
function testEppMessage() {
assertXmlTurnsIntoJson(
{
'epp': {
'@xmlns': 'urn:ietf:params:xml:ns:epp-1.0',
'response': {
'result': {
'@code': '1000',
'msg': {'keyValue': 'Command completed successfully'}
},
'resData': {
'domain:infData': {
'@xmlns:domain': 'urn:ietf:params:xml:ns:domain-1.0',
'domain:name': {'keyValue': 'justine.lol'},
'domain:roid': {'keyValue': '6-roid'},
'domain:status': {'@s': 'inactive'},
'domain:registrant': {'keyValue': 'GK Chesterton'},
'domain:contact': [
{'@type': 'admin', 'keyValue': '<justine>'},
{'@type': 'billing', 'keyValue': 'candycrush'},
{'@type': 'tech', 'keyValue': 'krieger'}
],
'domain:ns': {
'domain:hostObj': [
{'keyValue': 'ns1.justine.lol'},
{'keyValue': 'ns2.justine.lol'}
]
},
'domain:host': {'keyValue': 'ns1.justine.lol'},
'domain:clID': {'keyValue': 'justine'},
'domain:crID': {'keyValue': 'justine'},
'domain:crDate': {'keyValue': '2014-07-10T02:17:02Z'},
'domain:exDate': {'keyValue': '2015-07-10T02:17:02Z'},
'domain:authInfo': {
'domain:pw': {'keyValue': 'lolcat'}
}
}
},
'trID': {
'clTRID': {'keyValue': 'abc-1234'},
'svTRID': {'keyValue': 'ytk1RO+8SmaDQxrTIdulnw==-4'}
}
}
}
},
'<?xml version="1.0"?>' +
'<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">' +
' <response>' +
' <result code="1000">' +
' <msg>Command completed successfully</msg>' +
' </result>' +
' <resData>' +
' <domain:infData' +
' xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">' +
' <domain:name>justine.lol</domain:name>' +
' <domain:roid>6-roid</domain:roid>' +
' <domain:status s="inactive"/>' +
' <domain:registrant>GK Chesterton</domain:registrant>' +
' <domain:contact type="admin">&lt;justine&gt;</domain:contact>' +
' <domain:contact type="billing">candycrush</domain:contact>' +
' <domain:contact type="tech">krieger</domain:contact>' +
' <domain:ns>' +
' <domain:hostObj>ns1.justine.lol</domain:hostObj>' +
' <domain:hostObj>ns2.justine.lol</domain:hostObj>' +
' </domain:ns>' +
' <domain:host>ns1.justine.lol</domain:host>' +
' <domain:clID>justine</domain:clID>' +
' <domain:crID>justine</domain:crID>' +
' <domain:crDate>2014-07-10T02:17:02Z</domain:crDate>' +
' <domain:exDate>2015-07-10T02:17:02Z</domain:exDate>' +
' <domain:authInfo>' +
' <domain:pw>lolcat</domain:pw>' +
' </domain:authInfo>' +
' </domain:infData>' +
' </resData>' +
' <trID>' +
' <clTRID>abc-1234</clTRID>' +
' <svTRID>ytk1RO+8SmaDQxrTIdulnw==-4</svTRID>' +
' </trID>' +
' </response>' +
'</epp>');
}
/**
* Asserts `xml` turns into `json`.
* @param {!Object} json
* @param {string} xml
*/
function assertXmlTurnsIntoJson(json, xml) {
registry.testing.assertObjectEqualsPretty(
json, registry.xml.convertToJson(goog.dom.xml.loadXml(xml)));
}

View file

@ -26,7 +26,6 @@ import google.registry.model.ofy.OfyFilter;
import google.registry.module.frontend.FrontendServlet; import google.registry.module.frontend.FrontendServlet;
import google.registry.server.RegistryTestServer; import google.registry.server.RegistryTestServer;
import google.registry.testing.CertificateSamples; import google.registry.testing.CertificateSamples;
import org.junit.Ignore;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -44,8 +43,7 @@ public class RegistrarConsoleScreenshotTest {
.setRoutes( .setRoutes(
route("/registrar", FrontendServlet.class), route("/registrar", FrontendServlet.class),
route("/registrar-ote-status", FrontendServlet.class), route("/registrar-ote-status", FrontendServlet.class),
route("/registrar-settings", FrontendServlet.class), route("/registrar-settings", FrontendServlet.class))
route("/registrar-xhr", FrontendServlet.class))
.setFilters(ObjectifyFilter.class, OfyFilter.class) .setFilters(ObjectifyFilter.class, OfyFilter.class)
.setFixtures(BASIC) .setFixtures(BASIC)
.setEmail("Marla.Singer@google.com") .setEmail("Marla.Singer@google.com")
@ -286,77 +284,6 @@ public class RegistrarConsoleScreenshotTest {
driver.diffPage("page"); driver.diffPage("page");
} }
// EPP pages aren't being included in launch, so temporarily disable the following tests.
@Test
public void domainCreate() throws Throwable {
// TODO(b/17675279): Change labels to unicode.
driver.get(server.getUrl("/registrar#domain"));
driver.waitForElement(By.tagName("h1"));
driver.diffPage("page");
}
@Test
@Ignore("TODO(b/26984251): Unflake nameserver ordering.")
public void domainView() throws Throwable {
driver.get(server.getUrl("/registrar#domain/love.xn--q9jyb4c"));
driver.waitForElement(By.tagName("h1"));
driver.diffPage("page");
}
@Test
@Ignore("TODO(b/26984251): Unflake nameserver ordering.")
public void domainEdit() throws Throwable {
driver.get(server.getUrl("/registrar#domain/love.xn--q9jyb4c"));
driver.waitForElement(By.id("reg-app-btn-edit")).click();
Thread.sleep(1000);
driver.diffPage("page");
}
@Test
public void contactCreate() throws Throwable {
driver.get(server.getUrl("/registrar#contact"));
driver.waitForElement(By.tagName("h1"));
driver.diffPage("page");
}
@Test
public void contactView() throws Throwable {
driver.get(server.getUrl("/registrar#contact/google"));
driver.waitForElement(By.tagName("h1"));
driver.diffPage("page");
}
@Test
public void contactEdit() throws Throwable {
driver.get(server.getUrl("/registrar#contact/google"));
driver.waitForElement(By.id("reg-app-btn-edit")).click();
Thread.sleep(1000);
driver.diffPage("page");
}
@Test
public void hostCreate() throws Throwable {
driver.get(server.getUrl("/registrar#host"));
driver.waitForElement(By.tagName("h1"));
driver.diffPage("page");
}
@Test
public void hostView() throws Throwable {
driver.get(server.getUrl("/registrar#host/ns1.love.xn--q9jyb4c"));
driver.waitForElement(By.tagName("h1"));
driver.diffPage("page");
}
@Test
public void hostEdit() throws Throwable {
driver.get(server.getUrl("/registrar#host/ns1.love.xn--q9jyb4c"));
driver.waitForElement(By.id("reg-app-btn-edit")).click();
Thread.sleep(1000);
driver.diffPage("page");
}
@Test @Test
public void indexPage_smallScrolledDown() throws Throwable { public void indexPage_smallScrolledDown() throws Throwable {
driver.manage().window().setSize(new Dimension(400, 300)); driver.manage().window().setSize(new Dimension(400, 300));

View file

@ -17,9 +17,7 @@ package google.registry.webdriver;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static google.registry.server.Fixture.BASIC; import static google.registry.server.Fixture.BASIC;
import static google.registry.server.Route.route; import static google.registry.server.Route.route;
import static google.registry.testing.DatastoreHelper.createTld;
import static google.registry.testing.DatastoreHelper.loadRegistrar; import static google.registry.testing.DatastoreHelper.loadRegistrar;
import static google.registry.testing.DatastoreHelper.persistActiveDomain;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@ -31,7 +29,6 @@ import google.registry.model.registrar.RegistrarContact;
import google.registry.module.frontend.FrontendServlet; import google.registry.module.frontend.FrontendServlet;
import google.registry.server.RegistryTestServer; import google.registry.server.RegistryTestServer;
import google.registry.testing.AppEngineRule; import google.registry.testing.AppEngineRule;
import google.registry.ui.server.registrar.ConsoleUiAction;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.Timeout; import org.junit.rules.Timeout;
@ -55,7 +52,6 @@ public class RegistrarConsoleWebTest {
new TestServerRule.Builder() new TestServerRule.Builder()
.setRunfiles(RegistryTestServer.RUNFILES) .setRunfiles(RegistryTestServer.RUNFILES)
.setRoutes( .setRoutes(
route("/registrar-xhr", FrontendServlet.class),
route("/registrar", FrontendServlet.class), route("/registrar", FrontendServlet.class),
route("/registrar-settings", FrontendServlet.class)) route("/registrar-settings", FrontendServlet.class))
.setFilters(ObjectifyFilter.class, OfyFilter.class) .setFilters(ObjectifyFilter.class, OfyFilter.class)
@ -209,44 +205,4 @@ public class RegistrarConsoleWebTest {
.getAttribute("value")) .getAttribute("value"))
.isEqualTo(registrar.getPhonePasscode()); .isEqualTo(registrar.getPhonePasscode());
} }
@Test
public void testHostCreate_hostIsAnSld_eppErrorShowsInButterBar() throws Throwable {
driver.get(server.getUrl("/registrar#host"));
driver.waitForElement(By.id("domain-host-addr-add-button")).click();
driver.setFormFieldsById(ImmutableMap.of(
"host:name", "empire.vampyre",
"host:addr[0].value", "1.2.3.4"));
driver.findElement(By.id("reg-app-btn-save")).click();
Thread.sleep(1000); // TODO(b/26129174): Change butterbar code to add/remove dynamically.
assertThat(getButterBarText())
.isEqualTo("Host names must be at least two levels below the registry suffix");
}
@Test
public void testHostCreate() throws Throwable {
server.runInAppEngineEnvironment(
() -> {
createTld("vampyre");
persistActiveDomain("empire.vampyre");
return null;
});
driver.get(server.getUrl("/registrar#host"));
driver.waitForElement(By.id("domain-host-addr-add-button")).click();
driver.setFormFieldsById(ImmutableMap.of(
"host:name", "the.empire.vampyre",
"host:addr[0].value", "1.2.3.4"));
driver.findElement(By.id("reg-app-btn-save")).click();
Thread.sleep(1000); // TODO(b/26129174): Change butterbar code to add/remove dynamically.
assertThat(getButterBarText()).isEqualTo("Saved.");
}
private String getButterBarText() {
return (String) driver.executeScript(
String.format("return document.querySelector('.%s').innerText", css("kd-butterbar-text")));
}
private static String css(String name) {
return ConsoleUiAction.CSS_RENAMING_MAP_SUPPLIER.get().get(name);
}
} }