Use AuthenticatedRegistrarAccessor in EppConsoleAction

EppConsoleAction still "manually" checks access by going over the
RegistrarContacts. We need it to use AuthenticatedRegistrarAccessor just like
every other part of the registrar console.

We still need to remove the (now unneeded) login EPP sent by the console, but that's left for a followup CL.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=222404208
This commit is contained in:
guyben 2018-11-21 07:19:15 -08:00 committed by jianglai
parent eca3461dec
commit 5f283ebd09
15 changed files with 102 additions and 176 deletions

View file

@ -1169,8 +1169,7 @@ An EPP flow for login.
* 2103
* Specified extension is not implemented.
* 2200
* GAE user id is not allowed to login as requested registrar.
* User is not logged in as a GAE user.
* GAE User can't access the requested registrar.
* Registrar certificate does not match stored certificate.
* Registrar IP address is not in stored whitelist.
* Registrar certificate not present.

View file

@ -15,10 +15,13 @@
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;
@ -34,13 +37,16 @@ public class EppConsoleAction implements Runnable {
@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 HttpSessionMetadata(session),
GaeUserCredentials.forCurrentUser(userService),
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.

View file

@ -15,98 +15,40 @@
package google.registry.flows;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Strings.nullToEmpty;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.common.annotations.VisibleForTesting;
import google.registry.flows.EppException.AuthenticationErrorException;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import javax.annotation.Nullable;
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 User gaeUser;
private final Boolean isAdmin;
private final AuthenticatedRegistrarAccessor registrarAccessor;
/**
* Create an instance for the current user, as determined by {@code UserService}.
*
* <p>Note that the current user may be null (i.e. there is no logged in user).
*/
public static GaeUserCredentials forCurrentUser(UserService userService) {
User user = userService.getCurrentUser();
return new GaeUserCredentials(user, user != null ? userService.isUserAdmin() : null);
}
/** Create an instance that represents an explicit user (for testing purposes). */
@VisibleForTesting
public static GaeUserCredentials forTestingUser(User gaeUser, Boolean isAdmin) {
checkArgumentNotNull(gaeUser);
checkArgumentNotNull(isAdmin);
return new GaeUserCredentials(gaeUser, isAdmin);
}
/** Create an instance that represents a non-logged in user (for testing purposes). */
@VisibleForTesting
public static GaeUserCredentials forLoggedOutUser() {
return new GaeUserCredentials(null, null);
}
private GaeUserCredentials(@Nullable User gaeUser, @Nullable Boolean isAdmin) {
this.gaeUser = gaeUser;
this.isAdmin = isAdmin;
}
@VisibleForTesting
User getUser() {
return gaeUser;
public GaeUserCredentials(AuthenticatedRegistrarAccessor registrarAccessor) {
this.registrarAccessor = registrarAccessor;
}
@Override
public void validate(Registrar registrar, String ignoredPassword)
throws AuthenticationErrorException {
if (gaeUser == null) {
throw new UserNotLoggedInException();
try {
registrarAccessor.verifyAccess(registrar.getClientId());
} catch (RegistrarAccessDeniedException e) {
throw new UserForbiddenException(e);
}
// Allow admins to act as any registrar.
if (Boolean.TRUE.equals(isAdmin)) {
return;
}
// Check Registrar's contacts to see if any are associated with this gaeUserId.
final String gaeUserId = gaeUser.getUserId();
for (RegistrarContact rc : registrar.getContacts()) {
if (gaeUserId.equals(rc.getGaeUserId())) {
return;
}
}
throw new BadGaeUserIdException(gaeUser);
}
@Override
public String toString() {
return toStringHelper(getClass())
.add("gaeUser", gaeUser)
.add("isAdmin", isAdmin)
.toString();
return toStringHelper(getClass()).add("user", registrarAccessor.userIdForLogging()).toString();
}
/** User is not logged in as a GAE user. */
public static class UserNotLoggedInException extends AuthenticationErrorException {
public UserNotLoggedInException() {
super("User is not logged in");
}
}
/** GAE user id is not allowed to login as requested registrar. */
public static class BadGaeUserIdException extends AuthenticationErrorException {
public BadGaeUserIdException(User user) {
super(
"User id is not allowed to login as requested registrar: "
+ (nullToEmpty(user.getEmail())));
/** GAE User can't access the requested registrar. */
public static class UserForbiddenException extends AuthenticationErrorException {
public UserForbiddenException(RegistrarAccessDeniedException e) {
super(e.getMessage());
}
}
}

View file

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

View file

@ -203,9 +203,13 @@ public class AuthenticatedRegistrarAccessor {
logger.atInfo().log("%s has %s access to registrar %s.", userIdForLogging, roles, clientId);
}
public String userIdForLogging() {
return userIdForLogging;
}
@Override
public String toString() {
return toStringHelper(getClass()).add("authResult", userIdForLogging).toString();
return toStringHelper(getClass()).add("user", userIdForLogging).toString();
}
private static boolean checkIsSupport(

View file

@ -33,7 +33,9 @@ goog.forwardDeclare('registry.registrar.Console');
*/
registry.registrar.EppSession = function(console) {
registry.registrar.EppSession.base(
this, 'constructor', new goog.Uri('/registrar-xhr'),
this, 'constructor',
new goog.Uri('/registrar-xhr')
.setParameterValue('clientId', console.params.clientId),
console.params.xsrfToken,
registry.Session.ContentType.EPP);

View file

@ -16,6 +16,7 @@ package google.registry.ui.server.registrar;
import static google.registry.request.RequestParameters.extractOptionalParameter;
import static google.registry.request.RequestParameters.extractRequiredParameter;
import dagger.Module;
import dagger.Provides;
@ -31,7 +32,13 @@ public final class RegistrarConsoleModule {
@Provides
@Parameter(PARAM_CLIENT_ID)
static Optional<String> provideClientId(HttpServletRequest req) {
static Optional<String> provideOptionalClientId(HttpServletRequest req) {
return extractOptionalParameter(req, PARAM_CLIENT_ID);
}
@Provides
@Parameter(PARAM_CLIENT_ID)
static String provideClientId(HttpServletRequest req) {
return extractRequiredParameter(req, PARAM_CLIENT_ID);
}
}

View file

@ -31,6 +31,7 @@ java_library(
"//java/google/registry/monitoring/whitebox",
"//java/google/registry/pricing",
"//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/request/lock",
"//java/google/registry/tmch",
"//java/google/registry/util",

View file

@ -22,6 +22,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;
@ -48,9 +50,11 @@ public class EppConsoleActionTest extends ShardableTestCase {
EppConsoleAction action = new EppConsoleAction();
action.inputXmlBytes = INPUT_XML_BYTES;
action.session = new FakeHttpSession();
action.session.setAttribute("CLIENT_ID", "ClientIdentifier");
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);
@ -62,8 +66,8 @@ public class EppConsoleActionTest extends ShardableTestCase {
eq(false),
eq(false),
eq(INPUT_XML_BYTES));
assertThat(((GaeUserCredentials) credentialsCaptor.getValue()).getUser().getEmail())
.isEqualTo("person@example.com");
assertThat(((GaeUserCredentials) credentialsCaptor.getValue()).toString())
.contains("user=TestUserId");
assertThat(metadataCaptor.getValue().getClientId()).isEqualTo("ClientIdentifier");
}
}

View file

@ -14,10 +14,9 @@
package google.registry.flows;
import static com.google.appengine.api.users.UserServiceFactory.getUserService;
import com.google.common.collect.ImmutableSetMultimap;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.testing.AppEngineRule;
import google.registry.testing.UserInfo;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@ -31,18 +30,16 @@ public class EppLoginAdminUserTest extends EppTestCase {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withUserService(UserInfo.createAdmin("someone@example.com", "12345"))
.build();
@Before
public void initTransportCredentials() {
setTransportCredentials(GaeUserCredentials.forCurrentUser(getUserService()));
}
@Test
public void testNonAuthedLogin_succeedsAsAdmin() throws Exception {
// Login succeeds even though this user isn't listed on the registrar.
assertThatLoginSucceeds("TheRegistrar", "password2");
setTransportCredentials(
new GaeUserCredentials(
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of(
"TheRegistrar", AuthenticatedRegistrarAccessor.Role.ADMIN,
"NewRegistrar", AuthenticatedRegistrarAccessor.Role.ADMIN))));
}
@Test

View file

@ -14,16 +14,10 @@
package google.registry.flows;
import static com.google.appengine.api.users.UserServiceFactory.getUserService;
import static google.registry.testing.DatastoreHelper.loadRegistrar;
import static google.registry.testing.DatastoreHelper.persistResource;
import com.google.appengine.api.users.User;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.model.registrar.RegistrarContact;
import com.google.common.collect.ImmutableSetMultimap;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.testing.AppEngineRule;
import google.registry.testing.UserInfo;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@ -37,20 +31,15 @@ public class EppLoginUserTest extends EppTestCase {
@Rule
public final AppEngineRule appEngine = AppEngineRule.builder()
.withDatastore()
.withUserService(UserInfo.create("user@example.com", "12345"))
.build();
@Before
public void initTest() {
User user = getUserService().getCurrentUser();
persistResource(
new RegistrarContact.Builder()
.setParent(loadRegistrar("NewRegistrar"))
.setEmailAddress(user.getEmail())
.setGaeUserId(user.getUserId())
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN))
.build());
setTransportCredentials(GaeUserCredentials.forCurrentUser(getUserService()));
setTransportCredentials(
new GaeUserCredentials(
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of(
"NewRegistrar", AuthenticatedRegistrarAccessor.Role.OWNER))));
}
@Test
@ -66,7 +55,7 @@ public class EppLoginUserTest extends EppTestCase {
"response_error.xml",
ImmutableMap.of(
"CODE", "2200",
"MSG", "User id is not allowed to login as requested registrar: user@example.com"));
"MSG", "TestUserId doesn't have access to registrar TheRegistrar"));
}
@Test
@ -80,7 +69,7 @@ public class EppLoginUserTest extends EppTestCase {
"response_error.xml",
ImmutableMap.of(
"CODE", "2200",
"MSG", "User id is not allowed to login as requested registrar: user@example.com"));
"MSG", "TestUserId doesn't have access to registrar TheRegistrar"));
}
@Test

View file

@ -23,16 +23,17 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import com.google.appengine.api.users.User;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.flogger.LoggerConfig;
import com.google.common.testing.TestLogHandler;
import google.registry.model.eppcommon.Trid;
import google.registry.model.eppoutput.EppOutput.ResponseOrGreeting;
import google.registry.model.eppoutput.EppResponse;
import google.registry.monitoring.whitebox.EppMetric;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeHttpSession;
@ -142,10 +143,11 @@ public class FlowRunnerTest extends ShardableTestCase {
@Test
public void testRun_loggingStatement_gaeUserCredentials() throws Exception {
flowRunner.credentials =
GaeUserCredentials.forTestingUser(new User("user@example.com", "authDomain"), false);
new GaeUserCredentials(AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of()));
flowRunner.run(eppMetricBuilder);
assertThat(Splitter.on("\n\t").split(findFirstLogMessageByPrefix(handler, "EPP Command\n\t")))
.contains("GaeUserCredentials{gaeUser=user@example.com, isAdmin=false}");
assertThat(findFirstLogMessageByPrefix(handler, "EPP Command\n\t"))
.contains("user=TestUserId");
}
@Test

View file

@ -14,16 +14,11 @@
package google.registry.flows.session;
import static google.registry.testing.DatastoreHelper.loadRegistrar;
import static google.registry.testing.DatastoreHelper.persistResource;
import com.google.appengine.api.users.User;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import google.registry.flows.GaeUserCredentials;
import google.registry.flows.GaeUserCredentials.BadGaeUserIdException;
import google.registry.flows.GaeUserCredentials.UserNotLoggedInException;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.flows.GaeUserCredentials.UserForbiddenException;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import org.junit.Test;
/**
@ -32,53 +27,32 @@ import org.junit.Test;
*/
public class LoginFlowViaConsoleTest extends LoginFlowTestCase {
private static final String GAE_USER_ID1 = "12345";
private static final String GAE_USER_ID2 = "54321";
@Test
public void testSuccess_withLoginAndLinkedAccount() throws Exception {
persistLinkedAccount("person@example.com", GAE_USER_ID1);
public void testSuccess_withAccess() throws Exception {
credentials =
GaeUserCredentials.forTestingUser(new User("person", "example.com", GAE_USER_ID1), false);
new GaeUserCredentials(
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of(
"NewRegistrar", AuthenticatedRegistrarAccessor.Role.OWNER)));
doSuccessfulTest("login_valid.xml");
}
@Test
public void testFailure_withoutLoginAndLinkedAccount() {
persistLinkedAccount("person@example.com", GAE_USER_ID1);
credentials = GaeUserCredentials.forLoggedOutUser();
doFailingTest("login_valid.xml", UserNotLoggedInException.class);
}
@Test
public void testFailure_withoutLoginAndWithoutLinkedAccount() {
credentials = GaeUserCredentials.forLoggedOutUser();
doFailingTest("login_valid.xml", UserNotLoggedInException.class);
}
@Test
public void testFailure_withLoginAndWithoutLinkedAccount() {
public void testFailure_withoutAccess() {
credentials =
GaeUserCredentials.forTestingUser(new User("person", "example.com", GAE_USER_ID1), false);
doFailingTest("login_valid.xml", BadGaeUserIdException.class);
new GaeUserCredentials(
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of()));
doFailingTest("login_valid.xml", UserForbiddenException.class);
}
@Test
public void testFailure_withLoginAndNoMatchingLinkedAccount() {
persistLinkedAccount("joe@example.com", GAE_USER_ID2);
public void testFailure_withAccessToDifferentRegistrar() {
credentials =
GaeUserCredentials.forTestingUser(new User("person", "example.com", GAE_USER_ID1), false);
doFailingTest("login_valid.xml", BadGaeUserIdException.class);
}
private void persistLinkedAccount(String email, String gaeUserId) {
Registrar registrar = loadRegistrar("NewRegistrar");
RegistrarContact c = new RegistrarContact.Builder()
.setParent(registrar)
.setEmailAddress(email)
.setTypes(ImmutableSet.of(RegistrarContact.Type.ADMIN))
.setGaeUserId(gaeUserId)
.build();
persistResource(c);
new GaeUserCredentials(
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of(
"TheRegistrar", AuthenticatedRegistrarAccessor.Role.OWNER)));
doFailingTest("login_valid.xml", UserForbiddenException.class);
}
}

View file

@ -102,7 +102,7 @@ function handleLogin() {
'</epp>');
const xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue(xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
@ -168,7 +168,7 @@ function testView() {
'</epp>');
const xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
@ -237,7 +237,7 @@ function testEdit() {
'</epp>');
let xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
@ -292,7 +292,7 @@ function testEdit() {
'</epp>');
xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
@ -399,7 +399,7 @@ function testCreate() {
'</epp>');
let xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
@ -454,7 +454,7 @@ function testCreate() {
'</epp>');
xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);

View file

@ -102,7 +102,7 @@ function handleLogin() {
'</epp>');
const xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue(xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
@ -158,7 +158,7 @@ function testView() {
'</epp>');
const xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('application/epp+xml',
xhr.getLastRequestHeaders()['Content-Type']);
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
@ -227,7 +227,7 @@ function testEditFirstAddr_ignoreSecond_addThird() {
'</epp>');
let xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
@ -273,7 +273,7 @@ function testEditFirstAddr_ignoreSecond_addThird() {
'</epp>');
xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
@ -349,7 +349,7 @@ function testCreate() {
'</epp>');
let xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);
@ -398,7 +398,7 @@ function testCreate() {
'</epp>');
xhr = goog.testing.net.XhrIo.getSendInstances().pop();
assertTrue('XHR is inactive.', xhr.isActive());
assertEquals('/registrar-xhr', xhr.getLastUri());
assertEquals('/registrar-xhr?clientId=jartine', xhr.getLastUri());
assertEquals('☢', xhr.getLastRequestHeaders()['X-CSRF-Token']);
registry.testing.assertXmlEquals(request, xhr.getLastContent());
xhr.simulateResponse(200, response);