mirror of
https://github.com/google/nomulus.git
synced 2025-05-16 01:17:14 +02:00
The dark lord Gosling designed the Java package naming system so that ownership flows from the DNS system. Since we own the domain name registry.google, it seems only appropriate that we should use google.registry as our package name.
309 lines
11 KiB
Java
309 lines
11 KiB
Java
// Copyright 2016 The Domain Registry 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 google.registry.model.ofy.ObjectifyService.ofy;
|
|
import static google.registry.security.XsrfTokenManager.X_CSRF_TOKEN;
|
|
import static google.registry.security.XsrfTokenManager.generateToken;
|
|
import static google.registry.testing.DatastoreHelper.createTlds;
|
|
import static google.registry.testing.DatastoreHelper.persistResource;
|
|
import static google.registry.testing.TestDataHelper.loadFileWithSubstitutions;
|
|
import static google.registry.xml.XmlTestUtils.assertXmlEqualsWithMessage;
|
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
import static org.joda.time.DateTimeZone.UTC;
|
|
import static org.mockito.Mockito.mock;
|
|
import static org.mockito.Mockito.reset;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.when;
|
|
|
|
import com.google.appengine.api.modules.ModulesService;
|
|
import com.google.common.base.Optional;
|
|
import com.google.common.collect.ImmutableMap;
|
|
import com.google.common.collect.ImmutableSet;
|
|
|
|
import google.registry.model.ofy.Ofy;
|
|
import google.registry.model.registrar.Registrar;
|
|
import google.registry.model.tmch.ClaimsListShard.ClaimsListSingleton;
|
|
import google.registry.monitoring.whitebox.Metrics;
|
|
import google.registry.security.XsrfProtectedServlet;
|
|
import google.registry.testing.FakeClock;
|
|
import google.registry.testing.FakeServletInputStream;
|
|
import google.registry.testing.InjectRule;
|
|
import google.registry.util.BasicHttpSession;
|
|
import google.registry.util.TypeUtils.TypeInstantiator;
|
|
|
|
import org.joda.time.DateTime;
|
|
import org.junit.Before;
|
|
import org.junit.Rule;
|
|
import org.mockito.Mock;
|
|
import org.mockito.invocation.InvocationOnMock;
|
|
import org.mockito.stubbing.Answer;
|
|
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.util.Map;
|
|
|
|
import javax.servlet.ServletConfig;
|
|
import javax.servlet.ServletOutputStream;
|
|
import javax.servlet.http.HttpServlet;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletResponse;
|
|
import javax.servlet.http.HttpSession;
|
|
|
|
/**
|
|
* Test setup for all EppServletTest subclasses.
|
|
*
|
|
* @param <S> The EppXXXServlet class to test.
|
|
*/
|
|
public abstract class EppServletTestCase<S extends HttpServlet> {
|
|
|
|
@Rule
|
|
public final InjectRule inject = new InjectRule();
|
|
|
|
@Mock
|
|
HttpServletRequest req;
|
|
|
|
@Mock
|
|
HttpServletResponse rsp;
|
|
|
|
@Mock
|
|
ModulesService modulesService;
|
|
|
|
HttpSession session;
|
|
|
|
FakeClock clock = new FakeClock();
|
|
|
|
private String currentTld = null;
|
|
private Optional<Boolean> isSuperuser = Optional.<Boolean> absent();
|
|
private Optional<String> clientIdentifier = Optional.<String> absent();
|
|
|
|
void setSuperuser(boolean isSuperuser) {
|
|
this.isSuperuser = Optional.of(isSuperuser);
|
|
}
|
|
|
|
void setClientIdentifier(String clientIdentifier) {
|
|
this.clientIdentifier = Optional.of(clientIdentifier);
|
|
}
|
|
|
|
static final DateTime START_OF_GA = DateTime.parse("2014-03-01T00:00:00Z");
|
|
|
|
@Before
|
|
public final void init() throws Exception {
|
|
inject.setStaticField(Ofy.class, "clock", clock); // For transactional flows.
|
|
inject.setStaticField(FlowRunner.class, "clock", clock); // For non-transactional flows.
|
|
inject.setStaticField(Metrics.class, "modulesService", modulesService);
|
|
when(modulesService.getVersionHostname("backend", null)).thenReturn("backend.hostname");
|
|
|
|
// Create RegistryData for all TLDs used in these tests.
|
|
// We want to create all of these even for tests that don't use them to make sure that
|
|
// tld-selection works correctly.
|
|
createTlds("net", "xn--q9jyb4c", "example");
|
|
ofy().saveWithoutBackup().entity(new ClaimsListSingleton()).now();
|
|
|
|
session = new BasicHttpSession();
|
|
persistResource(
|
|
Registrar.loadByClientId("NewRegistrar")
|
|
.asBuilder()
|
|
.setAllowedTlds(ImmutableSet.of("net", "example", "xn--q9jyb4c"))
|
|
.build());
|
|
|
|
persistResource(
|
|
Registrar.loadByClientId("TheRegistrar")
|
|
.asBuilder()
|
|
.setAllowedTlds(ImmutableSet.of("net", "example", "xn--q9jyb4c"))
|
|
.build());
|
|
}
|
|
|
|
void assertCommandAndResponse(
|
|
String inputFilename,
|
|
Map<String, String> inputSubstitutions,
|
|
String outputFilename,
|
|
Map<String, String> outputSubstitutions) throws Exception {
|
|
assertCommandAndResponse(
|
|
inputFilename,
|
|
inputSubstitutions,
|
|
outputFilename,
|
|
outputSubstitutions,
|
|
DateTime.now(UTC));
|
|
}
|
|
|
|
String assertCommandAndResponse(String inputFilename, String outputFilename) throws Exception {
|
|
return assertCommandAndResponse(inputFilename, outputFilename, DateTime.now(UTC));
|
|
}
|
|
|
|
String assertCommandAndResponse(
|
|
String inputFilename,
|
|
Map<String, String> inputSubstitutions,
|
|
String outputFilename,
|
|
Map<String, String> outputSubstitutions,
|
|
String nowString) throws Exception {
|
|
return assertCommandAndResponse(
|
|
inputFilename,
|
|
inputSubstitutions,
|
|
outputFilename,
|
|
outputSubstitutions,
|
|
DateTime.parse(nowString));
|
|
}
|
|
|
|
String assertCommandAndResponse(String inputFilename, String outputFilename, String nowString)
|
|
throws Exception {
|
|
return assertCommandAndResponse(inputFilename, outputFilename, DateTime.parse(nowString));
|
|
}
|
|
|
|
String assertCommandAndResponse(String inputFilename, String outputFilename, DateTime now)
|
|
throws Exception {
|
|
return assertCommandAndResponse(inputFilename, null, outputFilename, null, now);
|
|
}
|
|
|
|
String assertCommandAndResponse(
|
|
String inputFilename,
|
|
Map<String, String> inputSubstitutions,
|
|
String outputFilename,
|
|
Map<String, String> outputSubstitutions,
|
|
DateTime now) throws Exception {
|
|
String outputFile =
|
|
loadFileWithSubstitutions(EppServletTestCase.class, outputFilename, outputSubstitutions);
|
|
String actualOutput = expectXmlCommand(loadFileWithSubstitutions(
|
|
EppServletTestCase.class, inputFilename, inputSubstitutions), now);
|
|
assertXmlEqualsWithMessage(
|
|
outputFile,
|
|
actualOutput,
|
|
"Running " + inputFilename + " => " + outputFilename,
|
|
"epp.response.resData.infData.roid",
|
|
"epp.response.trID.svTRID");
|
|
ofy().clearSessionCache(); // Clear the cache like OfyFilter would.
|
|
return actualOutput;
|
|
}
|
|
|
|
HttpSession getOrRenewSession() {
|
|
// Try an idempotent op on the session to see if it's valid.
|
|
try {
|
|
session.getAttribute(null);
|
|
return session;
|
|
} catch (IllegalStateException e) {
|
|
// Session is invalid.
|
|
session = new BasicHttpSession();
|
|
return session;
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("resource")
|
|
String expectXmlCommand(String inputFile, DateTime now) throws Exception {
|
|
clock.setTo(now); // Makes Ofy use 'now' as its time
|
|
reset(req, rsp);
|
|
HttpServlet servlet = new TypeInstantiator<S>(getClass()){}.instantiate();
|
|
if (servlet instanceof XsrfProtectedServlet) {
|
|
when(req.getHeader(X_CSRF_TOKEN))
|
|
.thenReturn(generateToken(((XsrfProtectedServlet) servlet).getScope()));
|
|
}
|
|
when(req.getInputStream()).thenReturn(new FakeServletInputStream(inputFile.getBytes(UTF_8)));
|
|
when(req.getParameter("xml")).thenReturn(inputFile);
|
|
if (isSuperuser.isPresent()) {
|
|
when(req.getParameter("superuser")).thenReturn(isSuperuser.get().toString());
|
|
}
|
|
if (clientIdentifier.isPresent()) {
|
|
when(req.getParameter("clientIdentifier")).thenReturn(clientIdentifier.get());
|
|
}
|
|
when(req.getParameter("tld")).thenReturn(currentTld);
|
|
when(req.getServletPath()).thenReturn("");
|
|
when(req.getMethod()).thenReturn("POST");
|
|
when(req.getHeader("X-Requested-With")).thenReturn("XMLHttpRequest");
|
|
when(req.getSession(true)).thenAnswer(new Answer<HttpSession>() {
|
|
@Override
|
|
public HttpSession answer(InvocationOnMock invocation) {
|
|
return getOrRenewSession();
|
|
}});
|
|
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
when(rsp.getOutputStream()).thenReturn(new ServletOutputStream() {
|
|
@Override
|
|
public void write(int b) {
|
|
byteArrayOutputStream.write(b);
|
|
}});
|
|
extendedSessionConfig(inputFile);
|
|
|
|
servlet.init(mock(ServletConfig.class));
|
|
servlet.service(req, rsp);
|
|
verify(rsp).setStatus(HttpServletResponse.SC_OK);
|
|
String result = new String(byteArrayOutputStream.toByteArray(), UTF_8);
|
|
// Run the resulting xml through the unmarshaller to verify that it was valid.
|
|
EppXmlTransformer.validateOutput(result);
|
|
return result;
|
|
}
|
|
|
|
/** Create the two administrative contacts and two hosts that are used by a lot of our tests. */
|
|
protected void createContactsAndHosts() throws Exception {
|
|
DateTime startTime = DateTime.parse("2000-06-01T00:00:00Z");
|
|
assertCommandAndResponse(
|
|
"contact_create_sh8013.xml",
|
|
ImmutableMap.<String, String>of(),
|
|
"contact_create_response_sh8013.xml",
|
|
ImmutableMap.of("CRDATE", "2000-06-01T00:00:00Z"),
|
|
startTime);
|
|
assertCommandAndResponse(
|
|
"contact_create_jd1234.xml",
|
|
"contact_create_response_jd1234.xml",
|
|
startTime.plusMinutes(1));
|
|
assertCommandAndResponse(
|
|
"host_create.xml",
|
|
"host_create_response.xml",
|
|
startTime.plusMinutes(2));
|
|
assertCommandAndResponse(
|
|
"host_create2.xml",
|
|
"host_create2_response.xml",
|
|
startTime.plusMinutes(3));
|
|
}
|
|
|
|
/**
|
|
* Creates the domain fakesite.example with two nameservers on it.
|
|
*/
|
|
protected void createFakesite() throws Exception {
|
|
createContactsAndHosts();
|
|
assertCommandAndResponse(
|
|
"domain_create_fakesite.xml",
|
|
"domain_create_response_fakesite.xml",
|
|
"2000-06-01T00:04:00Z");
|
|
assertCommandAndResponse(
|
|
"domain_info_fakesite.xml",
|
|
"domain_info_response_fakesite_ok.xml",
|
|
"2000-06-06T00:00:00Z");
|
|
}
|
|
|
|
// Adds ns3.fakesite.example as a host, then adds it to fakesite.
|
|
protected void createSubordinateHost() throws Exception {
|
|
// Add the fakesite nameserver (requires that domain is already created).
|
|
assertCommandAndResponse(
|
|
"host_create_fakesite.xml",
|
|
"host_create_response_fakesite.xml",
|
|
"2000-06-06T00:01:00Z");
|
|
// Add new nameserver to domain.
|
|
assertCommandAndResponse(
|
|
"domain_update_add_nameserver_fakesite.xml",
|
|
"domain_update_add_nameserver_response_fakesite.xml",
|
|
"2000-06-08T00:00:00Z");
|
|
// Verify new nameserver was added.
|
|
assertCommandAndResponse(
|
|
"domain_info_fakesite.xml",
|
|
"domain_info_response_fakesite_3_nameservers.xml",
|
|
"2000-06-08T00:01:00Z");
|
|
// Verify that nameserver's data was set correctly.
|
|
assertCommandAndResponse(
|
|
"host_info_fakesite.xml",
|
|
"host_info_response_fakesite.xml",
|
|
"2000-06-08T00:02:00Z");
|
|
}
|
|
|
|
/** For subclasses to further setup the session. */
|
|
protected void extendedSessionConfig(
|
|
@SuppressWarnings("unused") String inputFile) throws Exception {}
|
|
}
|