mirror of
https://github.com/google/nomulus
synced 2026-05-22 15:51:49 +00:00
Compare commits
23 Commits
nomulus-20
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6868b771b | ||
|
|
f34aec8b56 | ||
|
|
b27b077638 | ||
|
|
0e8cd75a58 | ||
|
|
2a1748ba9c | ||
|
|
f4889191a4 | ||
|
|
9eddecf70f | ||
|
|
d4bcff0c31 | ||
|
|
62065f88fb | ||
|
|
c9ac9437fd | ||
|
|
1f6a09182d | ||
|
|
a0eff00031 | ||
|
|
89698c6ed6 | ||
|
|
a7696c3fac | ||
|
|
7ec599f849 | ||
|
|
70291af9ad | ||
|
|
5fb95f38ed | ||
|
|
dfe8e24761 | ||
|
|
bd30fcc81c | ||
|
|
8cecc8d3a8 | ||
|
|
c5a39bccc5 | ||
|
|
a90a117341 | ||
|
|
b40ad54daf |
@@ -14,30 +14,71 @@
|
||||
|
||||
import { provideHttpClient } from '@angular/common/http';
|
||||
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { AppComponent } from './app.component';
|
||||
import { MaterialModule } from './material.module';
|
||||
import { BackendService } from './shared/services/backend.service';
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { routes } from './app-routing.module';
|
||||
import { AppModule } from './app.module';
|
||||
import { PocReminderComponent } from './shared/components/pocReminder/pocReminder.component';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { UserData, UserDataService } from './shared/services/userData.service';
|
||||
import { Registrar, RegistrarService } from './registrar/registrar.service';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { signal, WritableSignal } from '@angular/core';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
let component: AppComponent;
|
||||
let fixture: ComponentFixture<AppComponent>;
|
||||
let mockRegistrarService: {
|
||||
registrar: WritableSignal<Partial<Registrar> | null | undefined>;
|
||||
registrarId: WritableSignal<string>;
|
||||
registrars: WritableSignal<Array<Partial<Registrar>>>;
|
||||
};
|
||||
let mockUserDataService: { userData: WritableSignal<Partial<UserData>> };
|
||||
let mockSnackBar: jasmine.SpyObj<MatSnackBar>;
|
||||
|
||||
const dummyPocReminderComponent = class {}; // Dummy class for type checking
|
||||
|
||||
beforeEach(async () => {
|
||||
mockRegistrarService = {
|
||||
registrar: signal<Registrar | null | undefined>(undefined),
|
||||
registrarId: signal('123'),
|
||||
registrars: signal([]),
|
||||
};
|
||||
|
||||
mockUserDataService = {
|
||||
userData: signal({
|
||||
globalRole: 'NONE',
|
||||
}),
|
||||
};
|
||||
|
||||
mockSnackBar = jasmine.createSpyObj('MatSnackBar', ['openFromComponent']);
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [AppComponent],
|
||||
imports: [
|
||||
MaterialModule,
|
||||
BrowserAnimationsModule,
|
||||
AppRoutingModule,
|
||||
MatSidenavModule,
|
||||
NoopAnimationsModule,
|
||||
MatSnackBarModule,
|
||||
AppModule,
|
||||
RouterModule.forRoot(routes),
|
||||
],
|
||||
providers: [
|
||||
BackendService,
|
||||
{ provide: RegistrarService, useValue: mockRegistrarService },
|
||||
{ provide: UserDataService, useValue: mockUserDataService },
|
||||
{ provide: MatSnackBar, useValue: mockSnackBar },
|
||||
{ provide: PocReminderComponent, useClass: dummyPocReminderComponent },
|
||||
provideHttpClient(),
|
||||
provideHttpClientTesting(),
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(AppComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
|
||||
it('should create the app', () => {
|
||||
@@ -46,4 +87,51 @@ describe('AppComponent', () => {
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('PoC Verification Reminder', () => {
|
||||
beforeEach(() => {
|
||||
jasmine.clock().install();
|
||||
});
|
||||
|
||||
it('should open snackbar if lastPocVerificationDate is older than one year', fakeAsync(() => {
|
||||
const MOCK_TODAY = new Date('2024-07-15T10:00:00.000Z');
|
||||
jasmine.clock().mockDate(MOCK_TODAY);
|
||||
|
||||
const twoYearsAgo = new Date(MOCK_TODAY);
|
||||
twoYearsAgo.setFullYear(MOCK_TODAY.getFullYear() - 2);
|
||||
|
||||
mockRegistrarService.registrar.set({
|
||||
lastPocVerificationDate: twoYearsAgo.toISOString(),
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
TestBed.flushEffects();
|
||||
|
||||
expect(mockSnackBar.openFromComponent).toHaveBeenCalledWith(
|
||||
PocReminderComponent,
|
||||
{
|
||||
horizontalPosition: 'center',
|
||||
verticalPosition: 'top',
|
||||
duration: 1000000000,
|
||||
}
|
||||
);
|
||||
}));
|
||||
|
||||
it('should NOT open snackbar if lastPocVerificationDate is within last year', fakeAsync(() => {
|
||||
const MOCK_TODAY = new Date('2024-07-15T10:00:00.000Z');
|
||||
jasmine.clock().mockDate(MOCK_TODAY);
|
||||
|
||||
const sixMonthsAgo = new Date(MOCK_TODAY);
|
||||
sixMonthsAgo.setMonth(MOCK_TODAY.getMonth() - 6);
|
||||
|
||||
mockRegistrarService.registrar.set({
|
||||
lastPocVerificationDate: sixMonthsAgo.toISOString(),
|
||||
});
|
||||
|
||||
fixture.detectChanges();
|
||||
TestBed.flushEffects();
|
||||
|
||||
expect(mockSnackBar.openFromComponent).not.toHaveBeenCalled();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,13 +12,15 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { AfterViewInit, Component, ViewChild } from '@angular/core';
|
||||
import { AfterViewInit, Component, effect, ViewChild } from '@angular/core';
|
||||
import { MatSidenav } from '@angular/material/sidenav';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { RegistrarService } from './registrar/registrar.service';
|
||||
import { BreakPointObserverService } from './shared/services/breakPoint.service';
|
||||
import { GlobalLoaderService } from './shared/services/globalLoader.service';
|
||||
import { UserDataService } from './shared/services/userData.service';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { PocReminderComponent } from './shared/components/pocReminder/pocReminder.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -35,8 +37,28 @@ export class AppComponent implements AfterViewInit {
|
||||
protected userDataService: UserDataService,
|
||||
protected globalLoader: GlobalLoaderService,
|
||||
protected breakpointObserver: BreakPointObserverService,
|
||||
private router: Router
|
||||
) {}
|
||||
private router: Router,
|
||||
private _snackBar: MatSnackBar
|
||||
) {
|
||||
effect(() => {
|
||||
const registrar = this.registrarService.registrar();
|
||||
const oneYearAgo = new Date();
|
||||
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
|
||||
oneYearAgo.setHours(0, 0, 0, 0);
|
||||
if (
|
||||
registrar &&
|
||||
registrar.lastPocVerificationDate &&
|
||||
new Date(registrar.lastPocVerificationDate) < oneYearAgo &&
|
||||
this.userDataService?.userData()?.globalRole === 'NONE'
|
||||
) {
|
||||
this._snackBar.openFromComponent(PocReminderComponent, {
|
||||
horizontalPosition: 'center',
|
||||
verticalPosition: 'top',
|
||||
duration: 1000000000,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.router.events.subscribe((event) => {
|
||||
|
||||
@@ -60,6 +60,7 @@ import { TldsComponent } from './tlds/tlds.component';
|
||||
import { ForceFocusDirective } from './shared/directives/forceFocus.directive';
|
||||
import RdapComponent from './settings/rdap/rdap.component';
|
||||
import RdapEditComponent from './settings/rdap/rdapEdit.component';
|
||||
import { PocReminderComponent } from './shared/components/pocReminder/pocReminder.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [SelectedRegistrarWrapper],
|
||||
@@ -86,6 +87,7 @@ export class SelectedRegistrarModule {}
|
||||
RdapComponent,
|
||||
RdapEditComponent,
|
||||
ReasonDialogComponent,
|
||||
PocReminderComponent,
|
||||
RegistrarComponent,
|
||||
RegistrarDetailsComponent,
|
||||
RegistrarSelectorComponent,
|
||||
|
||||
@@ -57,7 +57,7 @@ export class NavigationComponent {
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.subscription.unsubscribe();
|
||||
this.subscription && this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
getElementId(node: RouteWithIcon) {
|
||||
|
||||
@@ -71,6 +71,7 @@ export interface Registrar
|
||||
registrarName: string;
|
||||
registryLockAllowed?: boolean;
|
||||
type?: string;
|
||||
lastPocVerificationDate?: string;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
|
||||
@@ -24,7 +24,7 @@ export type contactType =
|
||||
| 'LEGAL'
|
||||
| 'MARKETING'
|
||||
| 'TECH'
|
||||
| 'RDAP';
|
||||
| 'WHOIS';
|
||||
|
||||
type contactTypesToUserFriendlyTypes = { [type in contactType]: string };
|
||||
|
||||
@@ -35,7 +35,7 @@ export const contactTypeToTextMap: contactTypesToUserFriendlyTypes = {
|
||||
LEGAL: 'Legal contact',
|
||||
MARKETING: 'Marketing contact',
|
||||
TECH: 'Technical contact',
|
||||
RDAP: 'RDAP-Inquiry contact',
|
||||
WHOIS: 'RDAP-Inquiry contact',
|
||||
};
|
||||
|
||||
type UserFriendlyType = (typeof contactTypeToTextMap)[contactType];
|
||||
@@ -59,7 +59,10 @@ export interface ViewReadyContact extends Contact {
|
||||
export function contactTypeToViewReadyContact(c: Contact): ViewReadyContact {
|
||||
return {
|
||||
...c,
|
||||
userFriendlyTypes: c.types?.map((cType) => contactTypeToTextMap[cType]),
|
||||
userFriendlyTypes: (c.types || []).map(
|
||||
(cType) => contactTypeToTextMap[cType]
|
||||
),
|
||||
types: c.types || [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -98,19 +101,21 @@ export class ContactService {
|
||||
);
|
||||
}
|
||||
|
||||
saveContacts(contacts: ViewReadyContact[]): Observable<Contact[]> {
|
||||
updateContact(contact: ViewReadyContact) {
|
||||
return this.backend
|
||||
.postContacts(this.registrarService.registrarId(), contacts)
|
||||
.updateContact(this.registrarService.registrarId(), contact)
|
||||
.pipe(switchMap((_) => this.fetchContacts()));
|
||||
}
|
||||
|
||||
addContact(contact: ViewReadyContact) {
|
||||
const newContacts = this.contacts().concat([contact]);
|
||||
return this.saveContacts(newContacts);
|
||||
return this.backend
|
||||
.createContact(this.registrarService.registrarId(), contact)
|
||||
.pipe(switchMap((_) => this.fetchContacts()));
|
||||
}
|
||||
|
||||
deleteContact(contact: ViewReadyContact) {
|
||||
const newContacts = this.contacts().filter((c) => c !== contact);
|
||||
return this.saveContacts(newContacts);
|
||||
return this.backend
|
||||
.deleteContact(this.registrarService.registrarId(), contact)
|
||||
.pipe(switchMap((_) => this.fetchContacts()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,9 +69,13 @@ export class ContactDetailsComponent {
|
||||
|
||||
save(e: SubmitEvent) {
|
||||
e.preventDefault();
|
||||
if ((this.contactService.contactInEdit.types || []).length === 0) {
|
||||
this._snackBar.open('Required to select contact type');
|
||||
return;
|
||||
}
|
||||
const request = this.contactService.isContactNewView
|
||||
? this.contactService.addContact(this.contactService.contactInEdit)
|
||||
: this.contactService.saveContacts(this.contactService.contacts());
|
||||
: this.contactService.updateContact(this.contactService.contactInEdit);
|
||||
request.subscribe({
|
||||
complete: () => {
|
||||
this.goBack();
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="console-app__pocReminder">
|
||||
<p class="">
|
||||
Please take a moment to complete annual review of
|
||||
<a routerLink="/settings">contacts</a>.
|
||||
</p>
|
||||
<span matSnackBarActions>
|
||||
<button mat-button matSnackBarAction (click)="confirmReviewed()">
|
||||
Confirm reviewed
|
||||
</button>
|
||||
<button mat-button matSnackBarAction (click)="snackBarRef.dismiss()">
|
||||
Close
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
@@ -0,0 +1,5 @@
|
||||
.console-app__pocReminder {
|
||||
a {
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2025 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.
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { MatSnackBar, MatSnackBarRef } from '@angular/material/snack-bar';
|
||||
import { RegistrarService } from '../../../registrar/registrar.service';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-poc-reminder',
|
||||
templateUrl: './pocReminder.component.html',
|
||||
styleUrls: ['./pocReminder.component.scss'],
|
||||
standalone: false,
|
||||
})
|
||||
export class PocReminderComponent {
|
||||
constructor(
|
||||
public snackBarRef: MatSnackBarRef<PocReminderComponent>,
|
||||
private registrarService: RegistrarService,
|
||||
private _snackBar: MatSnackBar
|
||||
) {}
|
||||
|
||||
confirmReviewed() {
|
||||
if (this.registrarService.registrar()) {
|
||||
const todayMidnight = new Date();
|
||||
todayMidnight.setHours(0, 0, 0, 0);
|
||||
this.registrarService
|
||||
// @ts-ignore - if check above won't allow empty object to be submitted
|
||||
.updateRegistrar({
|
||||
...this.registrarService.registrar(),
|
||||
lastPocVerificationDate: todayMidnight.toISOString(),
|
||||
})
|
||||
.subscribe({
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.error || err.message);
|
||||
},
|
||||
next: () => {
|
||||
this.snackBarRef.dismiss();
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,13 +70,26 @@ export class BackendService {
|
||||
.pipe(catchError((err) => this.errorCatcher<Contact[]>(err)));
|
||||
}
|
||||
|
||||
postContacts(
|
||||
registrarId: string,
|
||||
contacts: Contact[]
|
||||
): Observable<Contact[]> {
|
||||
return this.http.post<Contact[]>(
|
||||
updateContact(registrarId: string, contact: Contact): Observable<Contact> {
|
||||
return this.http.put<Contact>(
|
||||
`/console-api/settings/contacts?registrarId=${registrarId}`,
|
||||
contacts
|
||||
contact
|
||||
);
|
||||
}
|
||||
|
||||
createContact(registrarId: string, contact: Contact): Observable<Contact> {
|
||||
return this.http.post<Contact>(
|
||||
`/console-api/settings/contacts?registrarId=${registrarId}`,
|
||||
contact
|
||||
);
|
||||
}
|
||||
|
||||
deleteContact(registrarId: string, contact: Contact): Observable<Contact> {
|
||||
return this.http.delete<Contact>(
|
||||
`/console-api/settings/contacts?registrarId=${registrarId}`,
|
||||
{
|
||||
body: JSON.stringify(contact),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ def fragileTestPatterns = [
|
||||
// Currently changes a global configuration parameter that for some reason
|
||||
// results in timestamp inversions for other tests. TODO(mmuller): fix.
|
||||
"google/registry/flows/host/HostInfoFlowTest.*",
|
||||
"google/registry/beam/common/RegistryPipelineWorkerInitializerTest.*",
|
||||
] + dockerIncompatibleTestPatterns
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -172,7 +172,7 @@ public record BillingEvent(
|
||||
.minusDays(1)
|
||||
.toString(),
|
||||
billingId(),
|
||||
registrarId(),
|
||||
"",
|
||||
String.format("%s | TLD: %s | TERM: %d-year", action(), tld(), years()),
|
||||
amount(),
|
||||
currency(),
|
||||
|
||||
@@ -40,6 +40,8 @@ public class RegistryPipelineWorkerInitializer implements JvmInitializer {
|
||||
|
||||
@Override
|
||||
public void beforeProcessing(PipelineOptions options) {
|
||||
// TODO(b/416299900): remove next line after GAE is removed.
|
||||
System.setProperty("google.registry.jetty", "true");
|
||||
RegistryPipelineOptions registryOptions = options.as(RegistryPipelineOptions.class);
|
||||
RegistryEnvironment environment = registryOptions.getRegistryEnvironment();
|
||||
if (environment == null || environment.equals(RegistryEnvironment.UNITTEST)) {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2025 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.dns.writer;
|
||||
|
||||
import dagger.Module;
|
||||
import google.registry.dns.writer.clouddns.CloudDnsWriterModule;
|
||||
import google.registry.dns.writer.dnsupdate.DnsUpdateWriterModule;
|
||||
|
||||
/**
|
||||
* Groups all {@link DnsWriter} implementations to be installed.
|
||||
*
|
||||
* <p>To cherry-pick the DNS writers to install, overwrite this file with your private version in
|
||||
* the release process.
|
||||
*/
|
||||
@Module(
|
||||
includes = {CloudDnsWriterModule.class, DnsUpdateWriterModule.class, VoidDnsWriterModule.class})
|
||||
public class DnsWritersModule {}
|
||||
@@ -185,7 +185,8 @@ public class CheckApiAction implements Runnable {
|
||||
}
|
||||
|
||||
private boolean checkExists(String domainString, DateTime now) {
|
||||
return !ForeignKeyUtils.loadCached(Domain.class, ImmutableList.of(domainString), now).isEmpty();
|
||||
return !ForeignKeyUtils.loadByCache(Domain.class, ImmutableList.of(domainString), now)
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
private Optional<String> checkReserved(InternetDomainName domainName) {
|
||||
|
||||
@@ -432,7 +432,7 @@ public final class DomainCheckFlow implements TransactionalFlow {
|
||||
.filter(existingDomains::containsKey)
|
||||
.collect(toImmutableMap(d -> d, existingDomains::get));
|
||||
ImmutableMap<VKey<? extends EppResource>, EppResource> loadedDomains =
|
||||
EppResource.loadCached(ImmutableList.copyOf(existingDomainsToLoad.values()));
|
||||
EppResource.loadByCacheIfEnabled(ImmutableList.copyOf(existingDomainsToLoad.values()));
|
||||
return ImmutableMap.copyOf(
|
||||
Maps.transformEntries(existingDomainsToLoad, (k, v) -> (Domain) loadedDomains.get(v)));
|
||||
}
|
||||
|
||||
@@ -417,7 +417,7 @@ public class DomainFlowUtils {
|
||||
contacts.stream().map(DesignatedContact::getContactKey).forEach(keysToLoad::add);
|
||||
registrant.ifPresent(keysToLoad::add);
|
||||
keysToLoad.addAll(nameservers);
|
||||
verifyNotInPendingDelete(EppResource.loadCached(keysToLoad.build()).values());
|
||||
verifyNotInPendingDelete(EppResource.loadByCacheIfEnabled(keysToLoad.build()).values());
|
||||
}
|
||||
|
||||
private static void verifyNotInPendingDelete(Iterable<EppResource> resources)
|
||||
|
||||
@@ -31,6 +31,7 @@ import google.registry.flows.ExtensionManager;
|
||||
import google.registry.flows.FlowModule.RegistrarId;
|
||||
import google.registry.flows.FlowModule.Superuser;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.MutatingFlow;
|
||||
import google.registry.flows.TransactionalFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.flows.custom.DomainInfoFlowCustomLogic;
|
||||
@@ -53,6 +54,8 @@ import google.registry.model.eppinput.ResourceCommand;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.persistence.IsolationLevel;
|
||||
import google.registry.persistence.PersistenceModule;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
@@ -62,8 +65,12 @@ import org.joda.time.DateTime;
|
||||
* An EPP flow that returns information about a domain.
|
||||
*
|
||||
* <p>The registrar that owns the domain, and any registrar presenting a valid authInfo for the
|
||||
* domain, will get a rich result with all of the domain's fields. All other requests will be
|
||||
* answered with a minimal result containing only basic information about the domain.
|
||||
* domain, will get a rich result with all the domain's fields. All other requests will be answered
|
||||
* with a minimal result containing only basic information about the domain.
|
||||
*
|
||||
* <p>This implements {@link MutatingFlow} instead of {@link TransactionalFlow} as a workaround so
|
||||
* that the common workflow of "create domain, then immediately get domain info" does not run into
|
||||
* replication lag issues where the info command claims the domain does not exist.
|
||||
*
|
||||
* @error {@link google.registry.flows.FlowUtils.NotLoggedInException}
|
||||
* @error {@link google.registry.flows.FlowUtils.UnknownCurrencyEppException}
|
||||
@@ -76,7 +83,8 @@ import org.joda.time.DateTime;
|
||||
* @error {@link DomainFlowUtils.TransfersAreAlwaysForOneYearException}
|
||||
*/
|
||||
@ReportingSpec(ActivityReportField.DOMAIN_INFO)
|
||||
public final class DomainInfoFlow implements TransactionalFlow {
|
||||
@IsolationLevel(PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ)
|
||||
public final class DomainInfoFlow implements MutatingFlow {
|
||||
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject ResourceCommand resourceCommand;
|
||||
|
||||
@@ -404,7 +404,7 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
* <p>Don't use this unless you really need it for performance reasons, and be sure that you are
|
||||
* OK with the trade-offs in loss of transactional consistency.
|
||||
*/
|
||||
public static ImmutableMap<VKey<? extends EppResource>, EppResource> loadCached(
|
||||
public static ImmutableMap<VKey<? extends EppResource>, EppResource> loadByCacheIfEnabled(
|
||||
Iterable<VKey<? extends EppResource>> keys) {
|
||||
if (!RegistryConfig.isEppResourceCachingEnabled()) {
|
||||
return tm().reTransact(() -> tm().loadByKeys(keys));
|
||||
@@ -413,15 +413,12 @@ public abstract class EppResource extends UpdateAutoTimestampEntity implements B
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a given EppResource by its key using the cache (if enabled).
|
||||
* Loads a given EppResource by its key using the cache.
|
||||
*
|
||||
* <p>Don't use this unless you really need it for performance reasons, and be sure that you are
|
||||
* OK with the trade-offs in loss of transactional consistency.
|
||||
* <p>This method ignores the `isEppResourceCachingEnabled` config setting. It is reserved for use
|
||||
* cases that can tolerate slightly stale data, e.g., RDAP queries.
|
||||
*/
|
||||
public static <T extends EppResource> T loadCached(VKey<T> key) {
|
||||
if (!RegistryConfig.isEppResourceCachingEnabled()) {
|
||||
return tm().reTransact(() -> tm().loadByKey(key));
|
||||
}
|
||||
public static <T extends EppResource> T loadByCache(VKey<T> key) {
|
||||
// Safe to cast because loading a Key<T> returns an entity of type T.
|
||||
@SuppressWarnings("unchecked")
|
||||
T resource = (T) cacheEppResources.get(key);
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.model;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.isAtOrAfter;
|
||||
@@ -40,6 +41,7 @@ import google.registry.model.transfer.DomainTransferData;
|
||||
import google.registry.model.transfer.TransferData;
|
||||
import google.registry.model.transfer.TransferStatus;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.TransactionManager;
|
||||
import jakarta.persistence.Query;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
@@ -109,12 +111,12 @@ public final class EppResourceUtils {
|
||||
*/
|
||||
public static <T extends EppResource> Optional<T> loadByForeignKey(
|
||||
Class<T> clazz, String foreignKey, DateTime now) {
|
||||
return loadByForeignKeyHelper(clazz, foreignKey, now, false);
|
||||
return loadByForeignKeyHelper(tm(), clazz, foreignKey, now, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the last created version of an {@link EppResource} from the database by foreign key,
|
||||
* using a cache.
|
||||
* using a cache, if caching is enabled in config settings.
|
||||
*
|
||||
* <p>Returns null if no resource with this foreign key was ever created, or if the most recently
|
||||
* created resource was deleted before time "now".
|
||||
@@ -134,20 +136,36 @@ public final class EppResourceUtils {
|
||||
* @param foreignKey id to match
|
||||
* @param now the current logical time to project resources at
|
||||
*/
|
||||
public static <T extends EppResource> Optional<T> loadByForeignKeyCached(
|
||||
public static <T extends EppResource> Optional<T> loadByForeignKeyByCacheIfEnabled(
|
||||
Class<T> clazz, String foreignKey, DateTime now) {
|
||||
return loadByForeignKeyHelper(
|
||||
clazz, foreignKey, now, RegistryConfig.isEppResourceCachingEnabled());
|
||||
tm(), clazz, foreignKey, now, RegistryConfig.isEppResourceCachingEnabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the last created version of an {@link EppResource} from the replica database by foreign
|
||||
* key, using a cache.
|
||||
*
|
||||
* <p>This method ignores the config setting for caching, and is reserved for use cases that can
|
||||
* tolerate slightly stale data.
|
||||
*/
|
||||
public static <T extends EppResource> Optional<T> loadByForeignKeyByCache(
|
||||
Class<T> clazz, String foreignKey, DateTime now) {
|
||||
return loadByForeignKeyHelper(replicaTm(), clazz, foreignKey, now, true);
|
||||
}
|
||||
|
||||
private static <T extends EppResource> Optional<T> loadByForeignKeyHelper(
|
||||
Class<T> clazz, String foreignKey, DateTime now, boolean useCache) {
|
||||
TransactionManager txnManager,
|
||||
Class<T> clazz,
|
||||
String foreignKey,
|
||||
DateTime now,
|
||||
boolean useCache) {
|
||||
checkArgument(
|
||||
ForeignKeyedEppResource.class.isAssignableFrom(clazz),
|
||||
"loadByForeignKey may only be called for foreign keyed EPP resources");
|
||||
VKey<T> key =
|
||||
useCache
|
||||
? ForeignKeyUtils.loadCached(clazz, ImmutableList.of(foreignKey), now).get(foreignKey)
|
||||
? ForeignKeyUtils.loadByCache(clazz, ImmutableList.of(foreignKey), now).get(foreignKey)
|
||||
: ForeignKeyUtils.load(clazz, foreignKey, now);
|
||||
// The returned key is null if the resource is hard deleted or soft deleted by the given time.
|
||||
if (key == null) {
|
||||
@@ -155,10 +173,10 @@ public final class EppResourceUtils {
|
||||
}
|
||||
T resource =
|
||||
useCache
|
||||
? EppResource.loadCached(key)
|
||||
? EppResource.loadByCache(key)
|
||||
// This transaction is buried very deeply inside many outer nested calls, hence merits
|
||||
// the use of reTransact() for now pending a substantial refactoring.
|
||||
: tm().reTransact(() -> tm().loadByKeyIfPresent(key).orElse(null));
|
||||
: txnManager.reTransact(() -> txnManager.loadByKeyIfPresent(key).orElse(null));
|
||||
if (resource == null || isAtOrAfter(now, resource.getDeletionTime())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@@ -204,11 +204,25 @@ public final class ForeignKeyUtils {
|
||||
* <p>Don't use the cached version of this method unless you really need it for performance
|
||||
* reasons, and are OK with the trade-offs in loss of transactional consistency.
|
||||
*/
|
||||
public static <E extends EppResource> ImmutableMap<String, VKey<E>> loadCached(
|
||||
public static <E extends EppResource> ImmutableMap<String, VKey<E>> loadByCacheIfEnabled(
|
||||
Class<E> clazz, Collection<String> foreignKeys, final DateTime now) {
|
||||
if (!RegistryConfig.isEppResourceCachingEnabled()) {
|
||||
return load(clazz, foreignKeys, now);
|
||||
}
|
||||
return loadByCache(clazz, foreignKeys, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a list of {@link VKey} to {@link EppResource} instances by class and foreign key strings
|
||||
* that are active at or after the specified moment in time, using the cache.
|
||||
*
|
||||
* <p>The returned map will omit any keys for which the {@link EppResource} doesn't exist or has
|
||||
* been soft-deleted.
|
||||
*
|
||||
* <p>This method is reserved for use cases that can tolerate slightly stale data.
|
||||
*/
|
||||
public static <E extends EppResource> ImmutableMap<String, VKey<E>> loadByCache(
|
||||
Class<E> clazz, Collection<String> foreignKeys, final DateTime now) {
|
||||
return foreignKeyCache
|
||||
.getAll(foreignKeys.stream().map(fk -> VKey.create(clazz, fk)).collect(toImmutableList()))
|
||||
.entrySet()
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright 2025 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.model.console;
|
||||
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.CreateAutoTimestamp;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.WithVKey;
|
||||
import jakarta.persistence.AttributeOverride;
|
||||
import jakarta.persistence.AttributeOverrides;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.Id;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Represents a password reset request of some type.
|
||||
*
|
||||
* <p>Password reset requests must be performed within an hour of the time that they were requested,
|
||||
* as well as requiring that the requester and the fulfiller have the proper respective permissions.
|
||||
*/
|
||||
@Entity
|
||||
@WithVKey(String.class)
|
||||
public class PasswordResetRequest extends ImmutableObject implements Buildable {
|
||||
|
||||
public enum Type {
|
||||
EPP,
|
||||
REGISTRY_LOCK
|
||||
}
|
||||
|
||||
@Id private String verificationCode;
|
||||
|
||||
@Column(nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
Type type;
|
||||
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(
|
||||
name = "creationTime",
|
||||
column = @Column(name = "requestTime", nullable = false))
|
||||
})
|
||||
CreateAutoTimestamp requestTime = CreateAutoTimestamp.create(null);
|
||||
|
||||
@Column(nullable = false)
|
||||
String requester;
|
||||
|
||||
@Column DateTime fulfillmentTime;
|
||||
|
||||
@Column(nullable = false)
|
||||
String destinationEmail;
|
||||
|
||||
@Column(nullable = false)
|
||||
String registrarId;
|
||||
|
||||
public String getVerificationCode() {
|
||||
return verificationCode;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public DateTime getRequestTime() {
|
||||
return requestTime.getTimestamp();
|
||||
}
|
||||
|
||||
public String getRequester() {
|
||||
return requester;
|
||||
}
|
||||
|
||||
public Optional<DateTime> getFulfillmentTime() {
|
||||
return Optional.ofNullable(fulfillmentTime);
|
||||
}
|
||||
|
||||
public String getDestinationEmail() {
|
||||
return destinationEmail;
|
||||
}
|
||||
|
||||
public String getRegistrarId() {
|
||||
return registrarId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
/** Builder for constructing immutable {@link PasswordResetRequest} objects. */
|
||||
public static class Builder extends Buildable.Builder<PasswordResetRequest> {
|
||||
|
||||
public Builder() {}
|
||||
|
||||
private Builder(PasswordResetRequest instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PasswordResetRequest build() {
|
||||
checkArgumentNotNull(getInstance().type, "Type must be specified");
|
||||
checkArgumentNotNull(getInstance().requester, "Requester must be specified");
|
||||
checkArgumentNotNull(getInstance().destinationEmail, "Destination email must be specified");
|
||||
checkArgumentNotNull(getInstance().registrarId, "Registrar ID must be specified");
|
||||
getInstance().verificationCode = UUID.randomUUID().toString();
|
||||
return super.build();
|
||||
}
|
||||
|
||||
public Builder setType(Type type) {
|
||||
getInstance().type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRequester(String requester) {
|
||||
getInstance().requester = requester;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setDestinationEmail(String destinationEmail) {
|
||||
getInstance().destinationEmail = destinationEmail;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegistrarId(String registrarId) {
|
||||
getInstance().registrarId = registrarId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFulfillmentTime(DateTime fulfillmentTime) {
|
||||
getInstance().fulfillmentTime = fulfillmentTime;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -441,7 +441,8 @@ public class DomainCommand {
|
||||
private static <T extends EppResource> ImmutableMap<String, VKey<T>> loadByForeignKeysCached(
|
||||
final Set<String> foreignKeys, final Class<T> clazz, final DateTime now)
|
||||
throws InvalidReferencesException {
|
||||
ImmutableMap<String, VKey<T>> fks = ForeignKeyUtils.loadCached(clazz, foreignKeys, now);
|
||||
ImmutableMap<String, VKey<T>> fks =
|
||||
ForeignKeyUtils.loadByCacheIfEnabled(clazz, foreignKeys, now);
|
||||
if (!fks.keySet().equals(foreignKeys)) {
|
||||
throw new InvalidReferencesException(
|
||||
clazz, ImmutableSet.copyOf(difference(foreignKeys, fks.keySet())));
|
||||
|
||||
@@ -27,6 +27,7 @@ import static com.google.common.io.BaseEncoding.base64;
|
||||
import static google.registry.config.RegistryConfig.getDefaultRegistrarWhoisServer;
|
||||
import static google.registry.model.CacheUtils.memoizeWithShortExpiration;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
||||
import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy;
|
||||
@@ -62,6 +63,7 @@ import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.converter.CidrBlockListUserType;
|
||||
import google.registry.persistence.converter.CurrencyToStringMapUserType;
|
||||
import google.registry.persistence.transaction.TransactionManager;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.PasswordUtils;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
@@ -576,7 +578,20 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* address.
|
||||
*/
|
||||
public ImmutableSortedSet<RegistrarPoc> getContacts() {
|
||||
return getContactPocs().stream()
|
||||
return getContactPocs(tm()).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all {@link RegistrarPoc} objects for this registrar sorted by their email
|
||||
* address.
|
||||
*
|
||||
* <p>This method queries the replica database. It is reserved for use cases that can tolerate
|
||||
* slightly stale data.
|
||||
*/
|
||||
public ImmutableSortedSet<RegistrarPoc> getContactsFromReplica() {
|
||||
return getContactPocs(replicaTm()).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR));
|
||||
}
|
||||
@@ -586,7 +601,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* their email address.
|
||||
*/
|
||||
public ImmutableSortedSet<RegistrarPoc> getContactsOfType(final RegistrarPoc.Type type) {
|
||||
return getContactPocs().stream()
|
||||
return getContactPocs(tm()).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter((@Nullable RegistrarPoc contact) -> contact.getTypes().contains(type))
|
||||
.collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR));
|
||||
@@ -600,13 +615,8 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
return getContacts().stream().filter(RegistrarPoc::getVisibleInDomainWhoisAsAbuse).findFirst();
|
||||
}
|
||||
|
||||
private ImmutableSet<RegistrarPoc> getContactPocs() {
|
||||
return tm().transact(
|
||||
() ->
|
||||
tm().query("FROM RegistrarPoc WHERE registrarId = :registrarId", RegistrarPoc.class)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.getResultStream()
|
||||
.collect(toImmutableSet()));
|
||||
private ImmutableList<RegistrarPoc> getContactPocs(TransactionManager txnManager) {
|
||||
return txnManager.transact(() -> RegistrarPoc.loadForRegistrar(registrarId));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -27,6 +27,7 @@ import static google.registry.util.PasswordUtils.hashPassword;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.gson.annotations.Expose;
|
||||
@@ -36,6 +37,7 @@ import google.registry.model.JsonMapBuilder;
|
||||
import google.registry.model.Jsonifiable;
|
||||
import google.registry.model.UnsafeSerializable;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.QueryComposer;
|
||||
import google.registry.util.PasswordUtils;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
@@ -93,6 +95,10 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
}
|
||||
}
|
||||
|
||||
@Expose
|
||||
@Column(insertable = false, updatable = false)
|
||||
protected Long id;
|
||||
|
||||
/** The name of the contact. */
|
||||
@Expose String name;
|
||||
|
||||
@@ -179,6 +185,10 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
tm().putAll(contacts);
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -295,6 +305,7 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
.put("visibleInDomainWhoisAsAbuse", visibleInDomainWhoisAsAbuse)
|
||||
.put("allowedToSetRegistryLockPassword", allowedToSetRegistryLockPassword)
|
||||
.put("registryLockAllowed", isRegistryLockAllowed())
|
||||
.put("id", getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -423,6 +434,12 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
}
|
||||
}
|
||||
|
||||
public static ImmutableList<RegistrarPoc> loadForRegistrar(String registrarId) {
|
||||
return tm().createQueryComposer(RegistrarPoc.class)
|
||||
.where("registrarId", QueryComposer.Comparator.EQ, registrarId)
|
||||
.list();
|
||||
}
|
||||
|
||||
/** Class to represent the composite primary key for {@link RegistrarPoc} entity. */
|
||||
@VisibleForTesting
|
||||
public static class RegistrarPocId extends ImmutableObject implements Serializable {
|
||||
|
||||
@@ -38,10 +38,8 @@ import google.registry.dns.PublishDnsUpdatesAction;
|
||||
import google.registry.dns.ReadDnsRefreshRequestsAction;
|
||||
import google.registry.dns.RefreshDnsAction;
|
||||
import google.registry.dns.RefreshDnsOnHostRenameAction;
|
||||
import google.registry.dns.writer.VoidDnsWriterModule;
|
||||
import google.registry.dns.writer.clouddns.CloudDnsWriterModule;
|
||||
import google.registry.dns.writer.DnsWritersModule;
|
||||
import google.registry.dns.writer.dnsupdate.DnsUpdateConfigModule;
|
||||
import google.registry.dns.writer.dnsupdate.DnsUpdateWriterModule;
|
||||
import google.registry.export.ExportDomainListsAction;
|
||||
import google.registry.export.ExportPremiumTermsAction;
|
||||
import google.registry.export.ExportReservedTermsAction;
|
||||
@@ -140,14 +138,13 @@ import google.registry.whois.WhoisModule;
|
||||
BatchModule.class,
|
||||
BillingModule.class,
|
||||
CheckApiModule.class,
|
||||
CloudDnsWriterModule.class,
|
||||
ConsoleModule.class,
|
||||
CronModule.class,
|
||||
CustomLogicModule.class,
|
||||
DnsCountQueryCoordinatorModule.class,
|
||||
DnsModule.class,
|
||||
DnsUpdateConfigModule.class,
|
||||
DnsUpdateWriterModule.class,
|
||||
DnsWritersModule.class,
|
||||
EppTlsModule.class,
|
||||
EppToolModule.class,
|
||||
IcannReportingModule.class,
|
||||
@@ -160,7 +157,6 @@ import google.registry.whois.WhoisModule;
|
||||
Spec11Module.class,
|
||||
TmchModule.class,
|
||||
ToolsServerModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
WhiteboxModule.class,
|
||||
WhoisModule.class,
|
||||
})
|
||||
|
||||
@@ -22,7 +22,6 @@ import google.registry.bigquery.BigqueryModule;
|
||||
import google.registry.config.CloudTasksUtilsModule;
|
||||
import google.registry.config.CredentialModule;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.dns.writer.VoidDnsWriterModule;
|
||||
import google.registry.export.DriveModule;
|
||||
import google.registry.export.sheet.SheetsServiceModule;
|
||||
import google.registry.flows.ServerTridProviderModule;
|
||||
@@ -73,7 +72,6 @@ import jakarta.inject.Singleton;
|
||||
SheetsServiceModule.class,
|
||||
StackdriverModule.class,
|
||||
UrlConnectionServiceModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
UtilsModule.class
|
||||
})
|
||||
interface BackendComponent {
|
||||
|
||||
@@ -34,10 +34,8 @@ import google.registry.dns.PublishDnsUpdatesAction;
|
||||
import google.registry.dns.ReadDnsRefreshRequestsAction;
|
||||
import google.registry.dns.RefreshDnsAction;
|
||||
import google.registry.dns.RefreshDnsOnHostRenameAction;
|
||||
import google.registry.dns.writer.VoidDnsWriterModule;
|
||||
import google.registry.dns.writer.clouddns.CloudDnsWriterModule;
|
||||
import google.registry.dns.writer.DnsWritersModule;
|
||||
import google.registry.dns.writer.dnsupdate.DnsUpdateConfigModule;
|
||||
import google.registry.dns.writer.dnsupdate.DnsUpdateWriterModule;
|
||||
import google.registry.export.ExportDomainListsAction;
|
||||
import google.registry.export.ExportPremiumTermsAction;
|
||||
import google.registry.export.ExportReservedTermsAction;
|
||||
@@ -82,13 +80,12 @@ import google.registry.tmch.TmchSmdrlAction;
|
||||
modules = {
|
||||
BatchModule.class,
|
||||
BillingModule.class,
|
||||
CloudDnsWriterModule.class,
|
||||
CronModule.class,
|
||||
CustomLogicModule.class,
|
||||
DnsCountQueryCoordinatorModule.class,
|
||||
DnsModule.class,
|
||||
DnsUpdateConfigModule.class,
|
||||
DnsUpdateWriterModule.class,
|
||||
DnsWritersModule.class,
|
||||
IcannReportingModule.class,
|
||||
RdeModule.class,
|
||||
ReportingModule.class,
|
||||
@@ -96,7 +93,6 @@ import google.registry.tmch.TmchSmdrlAction;
|
||||
SheetModule.class,
|
||||
Spec11Module.class,
|
||||
TmchModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
WhiteboxModule.class,
|
||||
})
|
||||
public interface BackendRequestComponent {
|
||||
|
||||
@@ -336,7 +336,7 @@ abstract class AbstractJsonableObject implements Jsonable {
|
||||
// According to RFC 9083 section 3, the syntax of dates and times is defined in RFC3339.
|
||||
//
|
||||
// According to RFC3339, we should use ISO8601, which is what DateTime.toString does!
|
||||
return new JsonPrimitive(((DateTime) object).toString());
|
||||
return new JsonPrimitive(object.toString());
|
||||
}
|
||||
if (object == null) {
|
||||
return JsonNull.INSTANCE;
|
||||
|
||||
@@ -24,10 +24,14 @@ import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
@@ -41,6 +45,7 @@ import google.registry.request.HttpException;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.RequestMethod;
|
||||
import google.registry.request.RequestPath;
|
||||
import google.registry.request.RequestUrl;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
@@ -50,7 +55,7 @@ import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Base RDAP (new WHOIS) action for all requests.
|
||||
* Base RDAP action for all requests.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
@@ -75,6 +80,7 @@ public abstract class RdapActionBase implements Runnable {
|
||||
@Inject Response response;
|
||||
@Inject @RequestMethod Action.Method requestMethod;
|
||||
@Inject @RequestPath String requestPath;
|
||||
@Inject @RequestUrl String requestUrl;
|
||||
@Inject RdapAuthorization rdapAuthorization;
|
||||
@Inject RdapJsonFormatter rdapJsonFormatter;
|
||||
@Inject @Parameter("includeDeleted") Optional<Boolean> includeDeletedParam;
|
||||
@@ -132,7 +138,7 @@ public abstract class RdapActionBase implements Runnable {
|
||||
// RFC7480 4.2 - servers receiving an RDAP request return an entity with a Content-Type header
|
||||
// containing the RDAP-specific JSON media type.
|
||||
response.setContentType(RESPONSE_MEDIA_TYPE);
|
||||
// RDAP Technical Implementation Guide 1.13 - when responding to RDAP valid requests, we MUST
|
||||
// RDAP Technical Implementation Guide 1.14 - when responding to RDAP valid requests, we MUST
|
||||
// include the Access-Control-Allow-Origin, which MUST be "*" unless otherwise specified.
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
try {
|
||||
@@ -198,7 +204,9 @@ public abstract class RdapActionBase implements Runnable {
|
||||
TopLevelReplyObject topLevelObject =
|
||||
TopLevelReplyObject.create(replyObject, rdapJsonFormatter.createTosNotice());
|
||||
Gson gson = formatOutputParam.orElse(false) ? FORMATTED_OUTPUT_GSON : GSON;
|
||||
response.setPayload(gson.toJson(topLevelObject.toJson()));
|
||||
JsonObject jsonResult = topLevelObject.toJson();
|
||||
addLinkValuesRecursively(jsonResult);
|
||||
response.setPayload(gson.toJson(jsonResult));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,4 +272,34 @@ public abstract class RdapActionBase implements Runnable {
|
||||
return rdapJsonFormatter.getRequestTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a request-referencing "value" to each link object.
|
||||
*
|
||||
* <p>This is the "context URI" as described in RFC 8288. Basically, this contains a reference to
|
||||
* the request URL that generated this RDAP response.
|
||||
*
|
||||
* <p>This is required per the RDAP February 2024 response profile sections 2.6.3 and 2.10, and
|
||||
* the technical implementation guide sections 3.2 and 3.3.2.
|
||||
*
|
||||
* <p>We must do this here (instead of where the links are generated) because many of the links
|
||||
* (e.g. terms of service) are static constants, and thus cannot by default know what the request
|
||||
* URL was.
|
||||
*/
|
||||
private void addLinkValuesRecursively(JsonElement jsonElement) {
|
||||
if (jsonElement instanceof JsonArray jsonArray) {
|
||||
jsonArray.forEach(this::addLinkValuesRecursively);
|
||||
} else if (jsonElement instanceof JsonObject jsonObject) {
|
||||
if (jsonObject.get("links") instanceof JsonArray linksArray) {
|
||||
addLinkValues(linksArray);
|
||||
}
|
||||
jsonObject.entrySet().forEach(entry -> addLinkValuesRecursively(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
private void addLinkValues(JsonArray linksArray) {
|
||||
Streams.stream(linksArray)
|
||||
.map(JsonElement::getAsJsonObject)
|
||||
.filter(o -> !o.has("value"))
|
||||
.forEach(o -> o.addProperty("value", requestUrl));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for RDAP autonomous system number requests.
|
||||
* RDAP action for RDAP autonomous system number requests.
|
||||
*
|
||||
* <p>This feature is not implemented because it's only necessary for <i>address</i> registries like
|
||||
* ARIN, not domain registries.
|
||||
|
||||
@@ -41,14 +41,13 @@ final class RdapDataStructures {
|
||||
// Conformance to RFC 9083
|
||||
jsonArray.add("rdap_level_0");
|
||||
|
||||
// Conformance to the RDAP Response Profile V2.1
|
||||
// Conformance to the RDAP Response Profile V2.2 (February 2024)
|
||||
// (see section 1.2)
|
||||
jsonArray.add("icann_rdap_response_profile_1");
|
||||
|
||||
// Conformance to the RDAP Technical Implementation Guide V2.2 (February 2024)
|
||||
// (see section 1.3)
|
||||
jsonArray.add("icann_rdap_response_profile_0");
|
||||
|
||||
// Conformance to the RDAP Technical Implementation Guide V2.1
|
||||
// (see section 1.14)
|
||||
jsonArray.add("icann_rdap_technical_implementation_guide_0");
|
||||
|
||||
jsonArray.add("icann_rdap_technical_implementation_guide_1");
|
||||
return jsonArray;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.rdap;
|
||||
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDomainName;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyByCache;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.HEAD;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
@@ -36,7 +36,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/** RDAP (new WHOIS) action for domain requests. */
|
||||
/** RDAP action for domain requests. */
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/domain/",
|
||||
@@ -65,7 +65,7 @@ public class RdapDomainAction extends RdapActionBase {
|
||||
}
|
||||
// The query string is not used; the RDAP syntax is /rdap/domain/mydomain.com.
|
||||
Optional<Domain> domain =
|
||||
loadByForeignKeyCached(
|
||||
loadByForeignKeyByCache(
|
||||
Domain.class,
|
||||
pathSearchString,
|
||||
shouldIncludeDeleted() ? START_OF_TIME : rdapJsonFormatter.getRequestTime());
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.rdap;
|
||||
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyByCache;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.HEAD;
|
||||
@@ -51,6 +51,7 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.Query;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Comparator;
|
||||
@@ -60,17 +61,15 @@ import java.util.stream.Stream;
|
||||
import org.hibernate.Hibernate;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for domain search requests.
|
||||
* RDAP action for domain search requests.
|
||||
*
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in STD 95 and its RFCs.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9083">RFC 9083: JSON Responses for the Registration
|
||||
* Data Access Protocol (RDAP)</a>
|
||||
*/
|
||||
// TODO: This isn't required by the RDAP Technical Implementation Guide, and hence should be
|
||||
// deleted, at least until it's actually required.
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/domains",
|
||||
@@ -184,7 +183,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
private DomainSearchResponse searchByDomainNameWithoutWildcard(
|
||||
final RdapSearchPattern partialStringQuery) {
|
||||
Optional<Domain> domain =
|
||||
loadByForeignKeyCached(
|
||||
loadByForeignKeyByCache(
|
||||
Domain.class, partialStringQuery.getInitialString(), getRequestTime());
|
||||
return makeSearchResults(
|
||||
shouldBeVisible(domain) ? ImmutableList.of(domain.get()) : ImmutableList.of());
|
||||
@@ -339,7 +338,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
Optional<String> desiredRegistrar = getDesiredRegistrar();
|
||||
if (desiredRegistrar.isPresent()) {
|
||||
Optional<Host> host =
|
||||
loadByForeignKeyCached(
|
||||
loadByForeignKeyByCache(
|
||||
Host.class,
|
||||
partialStringQuery.getInitialString(),
|
||||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
@@ -364,7 +363,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
// through the subordinate hosts. This is more efficient, and lets us permit wildcard searches
|
||||
// with no initial string.
|
||||
Domain domain =
|
||||
loadByForeignKeyCached(
|
||||
loadByForeignKeyByCache(
|
||||
Domain.class,
|
||||
partialStringQuery.getSuffix(),
|
||||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime())
|
||||
@@ -381,7 +380,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
if (partialStringQuery.matches(fqhn)) {
|
||||
if (desiredRegistrar.isPresent()) {
|
||||
Optional<Host> host =
|
||||
loadByForeignKeyCached(
|
||||
loadByForeignKeyByCache(
|
||||
Host.class, fqhn, shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
if (host.isPresent()
|
||||
&& desiredRegistrar
|
||||
@@ -442,7 +441,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
replicaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
jakarta.persistence.Query query =
|
||||
Query query =
|
||||
replicaTm()
|
||||
.getEntityManager()
|
||||
.createNativeQuery(queryBuilder.toString())
|
||||
|
||||
@@ -37,14 +37,12 @@ import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for entity (contact and registrar) requests. the ICANN operational
|
||||
* profile dictates that the "handle" for registrars is to be the IANA registrar ID:
|
||||
* RDAP action for entity (contact and registrar) requests. the ICANN operational profile dictates
|
||||
* that the "handle" for registrars is to be the IANA registrar ID:
|
||||
*
|
||||
* <p>2.8.3. Registries MUST support lookup for entities with the registrar role within other
|
||||
* objects using the handle (as described in 3.1.5 of RFC 9082). The handle of the entity with the
|
||||
* registrar role MUST be equal to IANA Registrar ID. The entity with the registrar role in the RDAP
|
||||
* response MUST contain a publicIDs member to identify the IANA Registrar ID from the IANA’s
|
||||
* Registrar ID registry. The type value of the publicID object MUST be equal to IANA Registrar ID.
|
||||
* <p>2.4.1.Registry RDAP servers MUST support Registrar object lookup using an entity path request
|
||||
* for entities with the registrar role using the handle (as described in 3.1.5 of RFC9082) where
|
||||
* the handle of the entity with the registrar role is be [sic] equal to the IANA Registrar ID.
|
||||
*/
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
@@ -104,7 +102,7 @@ public class RdapEntityAction extends RdapActionBase {
|
||||
// query, it MUST reply with 404 response code.
|
||||
//
|
||||
// Note we don't do RFC7480 5.3 - returning a different code if we wish to say "this info
|
||||
// exists but we don't want to show it to you", because we DON'T wish to say that.
|
||||
// exists, but we don't want to show it to you", because we DON'T wish to say that.
|
||||
throw new NotFoundException(pathSearchString + " not found");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for entity (contact and registrar) search requests.
|
||||
* RDAP action for entity (contact and registrar) search requests.
|
||||
*
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in STD 95 and its RFCs.
|
||||
*
|
||||
* <p>The RDAP specification lumps contacts and registrars together and calls them "entities", which
|
||||
* is confusing for us, because "entity" means something else in SQL. But here, when we use the
|
||||
@@ -76,8 +76,6 @@ import java.util.Optional;
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9083">RFC 9083: JSON Responses for the Registration
|
||||
* Data Access Protocol (RDAP)</a>
|
||||
*/
|
||||
// TODO: This isn't required by the RDAP Technical Implementation Guide, and hence should be
|
||||
// deleted, at least until it's actually required.
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/entities",
|
||||
|
||||
@@ -28,7 +28,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/** RDAP (new WHOIS) action for help requests. */
|
||||
/** RDAP action for help requests. */
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = RdapHelpAction.PATH,
|
||||
|
||||
@@ -22,42 +22,34 @@ import google.registry.rdap.RdapDataStructures.Remark;
|
||||
/**
|
||||
* This file contains boilerplate required by the ICANN RDAP Profile.
|
||||
*
|
||||
* @see <a href="https://www.icann.org/resources/pages/rdap-operational-profile-2016-07-26-en">RDAP
|
||||
* Operational Profile for gTLD Registries and Registrars</a>
|
||||
* @see <a
|
||||
* href="https://itp.cdn.icann.org/en/files/registry-operators/rdap-response-profile-21feb24-en.pdf">
|
||||
* RDAP Response Profile</a>
|
||||
*/
|
||||
public class RdapIcannStandardInformation {
|
||||
|
||||
/** Required by ICANN RDAP Profile section 1.4.10. */
|
||||
private static final Notice CONFORMANCE_NOTICE =
|
||||
Notice.builder()
|
||||
.setDescription(
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and"
|
||||
+ " Registrars version 1.0")
|
||||
.build();
|
||||
|
||||
/** Required by ICANN RDAP Profile section 1.5.18. */
|
||||
/** Required by RDAP Response Profile section 2.6.3. */
|
||||
private static final Notice DOMAIN_STATUS_CODES_NOTICE =
|
||||
Notice.builder()
|
||||
.setTitle("Status Codes")
|
||||
.setDescription(
|
||||
"For more information on domain status codes, please visit"
|
||||
+ " https://icann.org/epp")
|
||||
"For more information on domain status codes, please visit https://icann.org/epp")
|
||||
.addLink(
|
||||
Link.builder()
|
||||
.setRel("alternate")
|
||||
.setRel("glossary")
|
||||
.setHref("https://icann.org/epp")
|
||||
.setType("text/html")
|
||||
.build())
|
||||
.build();
|
||||
|
||||
/** Required by ICANN RDAP Response Profile section 2.11. */
|
||||
/** Required by RDAP Response Profile section 2.10. */
|
||||
private static final Notice INACCURACY_COMPLAINT_FORM_NOTICE =
|
||||
Notice.builder()
|
||||
.setTitle("RDDS Inaccuracy Complaint Form")
|
||||
.setDescription("URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf")
|
||||
.addLink(
|
||||
Link.builder()
|
||||
.setRel("alternate")
|
||||
.setRel("help")
|
||||
.setHref("https://icann.org/wicf")
|
||||
.setType("text/html")
|
||||
.build())
|
||||
@@ -79,28 +71,16 @@ public class RdapIcannStandardInformation {
|
||||
/** Boilerplate notices required by domain responses. */
|
||||
static final ImmutableList<Notice> DOMAIN_BOILERPLATE_NOTICES =
|
||||
ImmutableList.of(
|
||||
CONFORMANCE_NOTICE,
|
||||
// RDAP Response Profile 2.6.3
|
||||
DOMAIN_STATUS_CODES_NOTICE,
|
||||
// RDAP Response Profile 2.11
|
||||
// RDAP Response Profile 2.10
|
||||
INACCURACY_COMPLAINT_FORM_NOTICE);
|
||||
|
||||
/** Boilerplate notice for when a domain is blocked by BSA. */
|
||||
static final ImmutableList<Notice> DOMAIN_BLOCKED_BY_BSA_BOILERPLATE_NOTICES =
|
||||
ImmutableList.of(DOMAIN_BLOCKED_BY_BSA_NOTICE);
|
||||
|
||||
/** Boilerplate remarks required by nameserver and entity responses. */
|
||||
static final ImmutableList<Notice> NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES =
|
||||
ImmutableList.of(CONFORMANCE_NOTICE);
|
||||
|
||||
/**
|
||||
* Required by ICANN RDAP Profile section 1.4.9, as corrected by Gustavo Lozano of ICANN.
|
||||
*
|
||||
* <p>Also mentioned in the RDAP Technical Implementation Guide 3.6.
|
||||
*
|
||||
* @see <a href="http://mm.icann.org/pipermail/gtld-tech/2016-October/000822.html">Questions about
|
||||
* the ICANN RDAP Profile</a>
|
||||
*/
|
||||
/** Required by the RDAP Technical Implementation Guide 3.6. */
|
||||
static final Remark SUMMARY_DATA_REMARK =
|
||||
Remark.builder()
|
||||
.setTitle("Incomplete Data")
|
||||
@@ -109,14 +89,7 @@ public class RdapIcannStandardInformation {
|
||||
.setType(Remark.Type.OBJECT_TRUNCATED_UNEXPLAINABLE)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Required by ICANN RDAP Profile section 1.4.8, as corrected by Gustavo Lozano of ICANN.
|
||||
*
|
||||
* <p>Also mentioned in the RDAP Technical Implementation Guide 3.5.
|
||||
*
|
||||
* @see <a href="http://mm.icann.org/pipermail/gtld-tech/2016-October/000822.html">Questions about
|
||||
* the ICANN RDAP Profile</a>
|
||||
*/
|
||||
/** Required by the RDAP Technical Implementation Guide 3.5. */
|
||||
static final Notice TRUNCATED_RESULT_SET_NOTICE =
|
||||
Notice.builder()
|
||||
.setTitle("Search Policy")
|
||||
@@ -148,7 +121,9 @@ public class RdapIcannStandardInformation {
|
||||
/**
|
||||
* Included when requester is not logged in as the owner of the contact being returned.
|
||||
*
|
||||
* <p>Format required by ICANN RDAP Response Profile 15feb19 section 2.7.4.3.
|
||||
* <p>>Note: if we were keeping this around, we'd want/need to implement the <a
|
||||
* href="https://datatracker.ietf.org/doc/rfc9537/">official RDAP redaction spec</a> for contacts.
|
||||
* We are getting rid of contacts in 2025 though so this should be unnecessary.
|
||||
*/
|
||||
static final Remark CONTACT_PERSONAL_DATA_HIDDEN_DATA_REMARK =
|
||||
Remark.builder()
|
||||
@@ -169,10 +144,9 @@ public class RdapIcannStandardInformation {
|
||||
/**
|
||||
* Included in ALL contact responses, even if the user is authorized.
|
||||
*
|
||||
* <p>Format required by ICANN RDAP Response Profile 15feb19 section 2.7.5.3.
|
||||
*
|
||||
* <p>NOTE that unlike other redacted fields, there's no allowance to give the email to authorized
|
||||
* users or allow for registrar consent.
|
||||
* <p>>Note: if we were keeping this around, we'd want/need to implement the <a
|
||||
* href="https://datatracker.ietf.org/doc/rfc9537/">official RDAP redaction spec</a> for contacts.
|
||||
* We are getting rid of contacts in 2025 though so this should be unnecessary.
|
||||
*/
|
||||
static final Remark CONTACT_EMAIL_REDACTED_FOR_DOMAIN =
|
||||
Remark.builder()
|
||||
|
||||
@@ -26,7 +26,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for RDAP IP address requests.
|
||||
* RDAP action for RDAP IP address requests.
|
||||
*
|
||||
* <p>This feature is not implemented because it's only necessary for <i>address</i> registries like
|
||||
* ARIN, not domain registries.
|
||||
|
||||
@@ -23,20 +23,21 @@ import static google.registry.model.EppResourceUtils.isLinked;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.util.CollectionUtils.union;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import com.google.gson.JsonArray;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.adapters.EnumToAttributeAdapter.EppEnum;
|
||||
import google.registry.model.contact.Contact;
|
||||
@@ -73,13 +74,11 @@ import google.registry.rdap.RdapObjectClasses.VcardArray;
|
||||
import google.registry.request.RequestServerName;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.Entity;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -103,6 +102,16 @@ public class RdapJsonFormatter {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@VisibleForTesting
|
||||
record HistoryTimeAndRegistrar(DateTime modificationTime, String registrarId) {}
|
||||
|
||||
private static final LoadingCache<String, ImmutableMap<EventAction, HistoryTimeAndRegistrar>>
|
||||
DOMAIN_HISTORIES_BY_REPO_ID =
|
||||
CacheUtils.newCacheBuilder(RegistryConfig.getEppResourceCachingDuration())
|
||||
// Cache more than the EPP resource cache because we're only caching small objects
|
||||
.maximumSize(RegistryConfig.getEppResourceMaxCachedEntries() * 4L)
|
||||
.build(repoId -> getLastHistoryByType(repoId, Domain.class));
|
||||
|
||||
private DateTime requestTime = null;
|
||||
|
||||
@Inject
|
||||
@@ -212,7 +221,7 @@ public class RdapJsonFormatter {
|
||||
* Map of EPP event values to the RDAP equivalents.
|
||||
*
|
||||
* <p>Only has entries for optional events, either stated as optional in the RDAP Response Profile
|
||||
* 15feb19, or not mentioned at all but thought to be useful anyway.
|
||||
* section 2.3.2, or not mentioned at all but thought to be useful anyway.
|
||||
*
|
||||
* <p>Any required event should be added elsewhere, preferably without using HistoryEntries (so
|
||||
* that we don't need to load HistoryEntries for "summary" responses).
|
||||
@@ -271,7 +280,7 @@ public class RdapJsonFormatter {
|
||||
URI htmlUri = htmlBaseURI.resolve(rdapTosStaticUrl);
|
||||
noticeBuilder.addLink(
|
||||
Link.builder()
|
||||
.setRel("alternate")
|
||||
.setRel("terms-of-service")
|
||||
.setHref(htmlUri.toString())
|
||||
.setType("text/html")
|
||||
.build());
|
||||
@@ -283,8 +292,8 @@ public class RdapJsonFormatter {
|
||||
* Creates a JSON object for a {@link Domain}.
|
||||
*
|
||||
* <p>NOTE that domain searches aren't in the spec yet - they're in the RFC 9082 that describes
|
||||
* the query format, but they aren't in the RDAP Technical Implementation Guide 15feb19, meaning
|
||||
* we don't have to implement them yet and the RDAP Response Profile doesn't apply to them.
|
||||
* the query format, but they aren't in the RDAP Technical Implementation Guide, meaning we don't
|
||||
* have to implement them yet and the RDAP Response Profile doesn't apply to them.
|
||||
*
|
||||
* <p>We're implementing domain searches anyway, BUT we won't have the response for searches
|
||||
* conform to the RDAP Response Profile.
|
||||
@@ -298,9 +307,9 @@ public class RdapJsonFormatter {
|
||||
if (outputDataType != OutputDataType.FULL) {
|
||||
builder.remarksBuilder().add(RdapIcannStandardInformation.SUMMARY_DATA_REMARK);
|
||||
}
|
||||
// RDAP Response Profile 15feb19 section 2.1 discusses the domain name.
|
||||
// RDAP Response Profile section 2.1 discusses the domain name.
|
||||
builder.setLdhName(domain.getDomainName());
|
||||
// RDAP Response Profile 15feb19 section 2.2:
|
||||
// RDAP Response Profile section 2.2:
|
||||
// The domain handle MUST be the ROID
|
||||
builder.setHandle(domain.getRepoId());
|
||||
// If this is a summary (search result) - we'll return now. Since there's no requirement for
|
||||
@@ -308,9 +317,9 @@ public class RdapJsonFormatter {
|
||||
if (outputDataType == OutputDataType.SUMMARY) {
|
||||
return builder.build();
|
||||
}
|
||||
// RDAP Response Profile 15feb19 section 2.3.1:
|
||||
// RDAP Response Profile section 2.3.1:
|
||||
// The domain object in the RDAP response MUST contain the following events:
|
||||
// [registration, expiration, last update of RDAP database]
|
||||
// [registration, expiration]
|
||||
builder
|
||||
.eventsBuilder()
|
||||
.add(
|
||||
@@ -324,14 +333,18 @@ public class RdapJsonFormatter {
|
||||
.setEventAction(EventAction.EXPIRATION)
|
||||
.setEventDate(domain.getRegistrationExpirationTime())
|
||||
.build(),
|
||||
// RDAP response profile section 1.5:
|
||||
// The topmost object in the RDAP response MUST contain an event of "eventAction" type
|
||||
// "last update of RDAP database" with a value equal to the timestamp when the RDAP
|
||||
// database was last updated
|
||||
Event.builder()
|
||||
.setEventAction(EventAction.LAST_UPDATE_OF_RDAP_DATABASE)
|
||||
.setEventDate(getRequestTime())
|
||||
.build());
|
||||
// RDAP Response Profile 15feb19 section 2.3.2 discusses optional events. We add some of those
|
||||
// RDAP Response Profile section 2.3.2 discusses optional events. We add some of those
|
||||
// here. We also add a few others we find interesting.
|
||||
builder.eventsBuilder().addAll(makeOptionalEvents(domain));
|
||||
// RDAP Response Profile 15feb19 section 2.4.1:
|
||||
// RDAP Response Profile section 2.4.1:
|
||||
// The domain object in the RDAP response MUST contain an entity with the Registrar role.
|
||||
//
|
||||
// See {@link createRdapRegistrarEntity} for details of section 2.4 conformance
|
||||
@@ -369,8 +382,6 @@ public class RdapJsonFormatter {
|
||||
// RDAP Response Profile 2.6.3, must have a notice about statuses. That is in {@link
|
||||
// RdapIcannStandardInformation#domainBoilerplateNotices}
|
||||
|
||||
// Kick off the database loads of the nameservers that we will need, so it can load
|
||||
// asynchronously while we load and process the contacts.
|
||||
ImmutableSet<Host> loadedHosts =
|
||||
replicaTm()
|
||||
.transact(
|
||||
@@ -415,12 +426,12 @@ public class RdapJsonFormatter {
|
||||
}
|
||||
|
||||
// Add the nameservers to the data; the load was kicked off above for efficiency.
|
||||
// RDAP Response Profile 2.9: we MUST have the nameservers
|
||||
// RDAP Response Profile 2.8: we MUST have the nameservers
|
||||
for (Host host : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
|
||||
builder.nameserversBuilder().add(createRdapNameserver(host, OutputDataType.INTERNAL));
|
||||
}
|
||||
|
||||
// RDAP Response Profile 2.10 - MUST contain a secureDns member including at least a
|
||||
// RDAP Response Profile 2.9 - MUST contain a secureDns member including at least a
|
||||
// delegationSigned element. Other elements (e.g. dsData) MUST be included if the domain name is
|
||||
// signed and the elements are stored in the Registry
|
||||
//
|
||||
@@ -445,13 +456,13 @@ public class RdapJsonFormatter {
|
||||
builder.remarksBuilder().add(RdapIcannStandardInformation.SUMMARY_DATA_REMARK);
|
||||
}
|
||||
|
||||
// We need the ldhName: RDAP Response Profile 2.9.1, 4.1
|
||||
// We need the ldhName: RDAP Response Profile 2.8.1, 4.1
|
||||
builder.setLdhName(host.getHostName());
|
||||
// Handle is optional, but if given it MUST be the ROID.
|
||||
// We will set it always as it's important as a "self link"
|
||||
builder.setHandle(host.getRepoId());
|
||||
|
||||
// Status is optional for internal Nameservers - RDAP Response Profile 2.9.2
|
||||
// Status is optional for internal Nameservers - RDAP Response Profile 2.8.2
|
||||
// It isn't mentioned at all anywhere else. So we can just not put it at all?
|
||||
//
|
||||
// To be safe, we'll put it on the "FULL" version anyway
|
||||
@@ -483,7 +494,7 @@ public class RdapJsonFormatter {
|
||||
|
||||
// For query responses - we MUST have all the ip addresses: RDAP Response Profile 4.2.
|
||||
//
|
||||
// However, it is optional for internal responses: RDAP Response Profile 2.9.2
|
||||
// However, it is optional for internal responses: RDAP Response Profile 2.8.2
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
for (InetAddress inetAddress : host.getInetAddresses()) {
|
||||
if (inetAddress instanceof Inet4Address) {
|
||||
@@ -501,7 +512,7 @@ public class RdapJsonFormatter {
|
||||
builder.entitiesBuilder().add(createRdapRegistrarEntity(registrar, OutputDataType.INTERNAL));
|
||||
}
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
// Rdap Response Profile 4.4, must have "last update of RDAP database" response. But this is
|
||||
// Rdap Response Profile 1.5, must have "last update of RDAP database" response. But this is
|
||||
// only for direct query responses and not for internal objects.
|
||||
builder.setLastUpdateOfRdapDatabaseEvent(
|
||||
Event.builder()
|
||||
@@ -526,10 +537,7 @@ public class RdapJsonFormatter {
|
||||
Contact contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
|
||||
RdapContactEntity.Builder contactBuilder = RdapContactEntity.builder();
|
||||
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts. 2.7.4 discusses censoring of
|
||||
// fields we don't want to show (as opposed to not having contacts at all) because of GDPR etc.
|
||||
//
|
||||
// 2.8 allows for unredacted output for authorized people.
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts
|
||||
boolean isAuthorized =
|
||||
rdapAuthorization.isAuthorizedForRegistrar(contact.getCurrentSponsorRegistrarId());
|
||||
|
||||
@@ -569,7 +577,7 @@ public class RdapJsonFormatter {
|
||||
.add(RdapIcannStandardInformation.CONTACT_EMAIL_REDACTED_FOR_DOMAIN);
|
||||
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
// Rdap Response Profile 2.7.6 must have "last update of RDAP database" response. But this is
|
||||
// Rdap Response Profile 1.5 must have "last update of RDAP database" response. But this is
|
||||
// only for direct query responses and not for internal objects. I'm not sure why it's in that
|
||||
// section at all...
|
||||
contactBuilder.setLastUpdateOfRdapDatabaseEvent(
|
||||
@@ -647,8 +655,8 @@ public class RdapJsonFormatter {
|
||||
* Creates a JSON object for a {@link Registrar}.
|
||||
*
|
||||
* <p>This object can be INTERNAL to the Domain and Nameserver responses, with requirements
|
||||
* discussed in the RDAP Response Profile 15feb19 sections 2.4 (internal to Domain) and 4.3
|
||||
* (internal to Namesever)
|
||||
* discussed in the RDAP Response Profile sections 2.4 (internal to Domain) and 4.3 (internal to
|
||||
* Namesever)
|
||||
*
|
||||
* @param registrar the registrar object from which the RDAP response
|
||||
* @param outputDataType whether to generate FULL, SUMMARY, or INTERNAL data.
|
||||
@@ -712,6 +720,15 @@ public class RdapJsonFormatter {
|
||||
builder.linksBuilder().add(makeSelfLink("entity", ianaIdentifier.toString()));
|
||||
}
|
||||
|
||||
// RDAP Response Profile 2.4.6: must have a links entry pointing to the registrar URL, with a
|
||||
// rel:about and a value containing the registrar RDAP base URL (if present)
|
||||
if (registrar.getUrl() != null) {
|
||||
Link.Builder registrarLinkBuilder =
|
||||
Link.builder().setHref(registrar.getUrl()).setRel("about").setType("text/html");
|
||||
registrar.getRdapBaseUrls().stream().findFirst().ifPresent(registrarLinkBuilder::setValue);
|
||||
builder.linksBuilder().add(registrarLinkBuilder.build());
|
||||
}
|
||||
|
||||
// There's no mention of the registrar STATUS in the RDAP Response Profile, so we'll only add it
|
||||
// for FULL response
|
||||
// We could probably not add it at all, but it could be useful for us internally
|
||||
@@ -737,10 +754,9 @@ public class RdapJsonFormatter {
|
||||
//
|
||||
// Write the minimum, meaning only ABUSE for INTERNAL registrars, nothing for SUMMARY and
|
||||
// everything for FULL.
|
||||
//
|
||||
if (outputDataType != OutputDataType.SUMMARY) {
|
||||
ImmutableList<RdapContactEntity> registrarContacts =
|
||||
registrar.getContacts().stream()
|
||||
registrar.getContactsFromReplica().stream()
|
||||
.map(RdapJsonFormatter::makeRdapJsonForRegistrarContact)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
@@ -758,7 +774,7 @@ public class RdapJsonFormatter {
|
||||
builder.entitiesBuilder().addAll(registrarContacts);
|
||||
}
|
||||
|
||||
// Rdap Response Profile 3.3, must have "last update of RDAP database" response. But this is
|
||||
// Rdap Response Profile 1.5, must have "last update of RDAP database" response. But this is
|
||||
// only for direct query responses and not for internal objects.
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
builder.setLastUpdateOfRdapDatabaseEvent(
|
||||
@@ -860,8 +876,18 @@ public class RdapJsonFormatter {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ImmutableMap<EventAction, HistoryEntry> getLastHistoryEntryByType(EppResource resource) {
|
||||
HashMap<EventAction, HistoryEntry> lastEntryOfType = Maps.newHashMap();
|
||||
static ImmutableMap<EventAction, HistoryTimeAndRegistrar> getLastHistoryByType(
|
||||
EppResource eppResource) {
|
||||
if (eppResource instanceof Domain) {
|
||||
return DOMAIN_HISTORIES_BY_REPO_ID.get(eppResource.getRepoId());
|
||||
}
|
||||
return getLastHistoryByType(eppResource.getRepoId(), eppResource.getClass());
|
||||
}
|
||||
|
||||
private static ImmutableMap<EventAction, HistoryTimeAndRegistrar> getLastHistoryByType(
|
||||
String repoId, Class<? extends EppResource> resourceType) {
|
||||
ImmutableMap.Builder<EventAction, HistoryTimeAndRegistrar> lastEntryOfType =
|
||||
new ImmutableMap.Builder<>();
|
||||
// Events (such as transfer, but also create) can appear multiple times. We only want the last
|
||||
// time they appeared.
|
||||
//
|
||||
@@ -873,49 +899,48 @@ public class RdapJsonFormatter {
|
||||
// 2.3.2.3 An event of *eventAction* type *transfer*, with the last date and time that the
|
||||
// domain was transferred. The event of *eventAction* type *transfer* MUST be omitted if the
|
||||
// domain name has not been transferred since it was created.
|
||||
VKey<? extends EppResource> resourceVkey = resource.createVKey();
|
||||
Class<? extends HistoryEntry> historyClass =
|
||||
HistoryEntryDao.getHistoryClassFromParent(resourceVkey.getKind());
|
||||
String entityName = historyClass.getAnnotation(Entity.class).name();
|
||||
if (Strings.isNullOrEmpty(entityName)) {
|
||||
entityName = historyClass.getSimpleName();
|
||||
}
|
||||
String entityName = HistoryEntryDao.getHistoryClassFromParent(resourceType).getSimpleName();
|
||||
String jpql =
|
||||
GET_LAST_HISTORY_BY_TYPE_JPQL_TEMPLATE
|
||||
.replace("%entityName%", entityName)
|
||||
.replace("%repoIdValue%", resourceVkey.getKey().toString());
|
||||
Iterable<HistoryEntry> historyEntries =
|
||||
replicaTm()
|
||||
.transact(
|
||||
() ->
|
||||
replicaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(jpql, HistoryEntry.class)
|
||||
.getResultList());
|
||||
for (HistoryEntry historyEntry : historyEntries) {
|
||||
EventAction rdapEventAction =
|
||||
HISTORY_ENTRY_TYPE_TO_RDAP_EVENT_ACTION_MAP.get(historyEntry.getType());
|
||||
// Only save the historyEntries if this is a type we care about.
|
||||
if (rdapEventAction == null) {
|
||||
continue;
|
||||
}
|
||||
lastEntryOfType.put(rdapEventAction, historyEntry);
|
||||
}
|
||||
return ImmutableMap.copyOf(lastEntryOfType);
|
||||
.replace("%repoIdValue%", repoId);
|
||||
replicaTm()
|
||||
.transact(
|
||||
() ->
|
||||
replicaTm()
|
||||
.getEntityManager()
|
||||
.createQuery(jpql, HistoryEntry.class)
|
||||
.getResultStream()
|
||||
.forEach(
|
||||
historyEntry -> {
|
||||
EventAction rdapEventAction =
|
||||
HISTORY_ENTRY_TYPE_TO_RDAP_EVENT_ACTION_MAP.get(
|
||||
historyEntry.getType());
|
||||
// Only save the entries if this is a type we care about.
|
||||
if (rdapEventAction != null) {
|
||||
lastEntryOfType.put(
|
||||
rdapEventAction,
|
||||
new HistoryTimeAndRegistrar(
|
||||
historyEntry.getModificationTime(),
|
||||
historyEntry.getRegistrarId()));
|
||||
}
|
||||
}));
|
||||
return lastEntryOfType.buildKeepingLast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the list of optional events to list in domain, nameserver, or contact replies.
|
||||
*
|
||||
* <p>Only has entries for optional events that won't be shown in "SUMMARY" versions of these
|
||||
* objects. These are either stated as optional in the RDAP Response Profile 15feb19, or not
|
||||
* mentioned at all but thought to be useful anyway.
|
||||
* objects. These are either stated as optional in the RDAP Response Profile, or not mentioned at
|
||||
* all but thought to be useful anyway.
|
||||
*
|
||||
* <p>Any required event should be added elsewhere, preferably without using HistoryEntries (so
|
||||
* that we don't need to load HistoryEntries for "summary" responses).
|
||||
*/
|
||||
private ImmutableList<Event> makeOptionalEvents(EppResource resource) {
|
||||
ImmutableMap<EventAction, HistoryEntry> lastEntryOfType = getLastHistoryEntryByType(resource);
|
||||
ImmutableMap<EventAction, HistoryTimeAndRegistrar> lastHistoryOfType =
|
||||
getLastHistoryByType(resource);
|
||||
ImmutableList.Builder<Event> eventsBuilder = new ImmutableList.Builder<>();
|
||||
DateTime creationTime = resource.getCreationTime();
|
||||
DateTime lastChangeTime =
|
||||
@@ -923,12 +948,12 @@ public class RdapJsonFormatter {
|
||||
// The order of the elements is stable - it's the order in which the enum elements are defined
|
||||
// in EventAction
|
||||
for (EventAction rdapEventAction : EventAction.values()) {
|
||||
HistoryEntry historyEntry = lastEntryOfType.get(rdapEventAction);
|
||||
HistoryTimeAndRegistrar historyTimeAndRegistrar = lastHistoryOfType.get(rdapEventAction);
|
||||
// Check if there was any entry of this type
|
||||
if (historyEntry == null) {
|
||||
if (historyTimeAndRegistrar == null) {
|
||||
continue;
|
||||
}
|
||||
DateTime modificationTime = historyEntry.getModificationTime();
|
||||
DateTime modificationTime = historyTimeAndRegistrar.modificationTime();
|
||||
// We will ignore all events that happened before the "creation time", since these events are
|
||||
// from a "previous incarnation of the domain" (for a domain that was owned by someone,
|
||||
// deleted, and then bought by someone else)
|
||||
@@ -938,7 +963,7 @@ public class RdapJsonFormatter {
|
||||
eventsBuilder.add(
|
||||
Event.builder()
|
||||
.setEventAction(rdapEventAction)
|
||||
.setEventActor(historyEntry.getRegistrarId())
|
||||
.setEventActor(historyTimeAndRegistrar.registrarId())
|
||||
.setEventDate(modificationTime)
|
||||
.build());
|
||||
// The last change time might not be the lastEppUpdateTime, since some changes happen without
|
||||
@@ -947,29 +972,24 @@ public class RdapJsonFormatter {
|
||||
lastChangeTime = modificationTime;
|
||||
}
|
||||
}
|
||||
// RDAP Response Profile 15feb19 section 2.3.2.2:
|
||||
// RDAP Response Profile section 2.3.2.2:
|
||||
// The event of eventAction type last changed MUST be omitted if the domain name has not been
|
||||
// updated since it was created
|
||||
if (lastChangeTime.isAfter(creationTime)) {
|
||||
eventsBuilder.add(makeEvent(EventAction.LAST_CHANGED, null, lastChangeTime));
|
||||
// Creates an RDAP event object as defined by RFC 9083
|
||||
eventsBuilder.add(
|
||||
Event.builder()
|
||||
.setEventAction(EventAction.LAST_CHANGED)
|
||||
.setEventDate(lastChangeTime)
|
||||
.build());
|
||||
}
|
||||
return eventsBuilder.build();
|
||||
}
|
||||
|
||||
/** Creates an RDAP event object as defined by RFC 9083. */
|
||||
private static Event makeEvent(
|
||||
EventAction eventAction, @Nullable String eventActor, DateTime eventDate) {
|
||||
Event.Builder builder = Event.builder().setEventAction(eventAction).setEventDate(eventDate);
|
||||
if (eventActor != null) {
|
||||
builder.setEventActor(eventActor);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a vCard address entry: array of strings specifying the components of the address.
|
||||
*
|
||||
* <p>Rdap Response Profile 3.1.1: MUST contain the following fields: Street, City, Country Rdap
|
||||
* <p>RDAP Response Profile 3.1.1: MUST contain the following fields: Street, City, Country Rdap
|
||||
* Response Profile 3.1.2: optional fields: State/Province, Postal Code, Fax Number
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7095">RFC 7095: jCard: The JSON Format for
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package google.registry.rdap;
|
||||
|
||||
import static google.registry.flows.host.HostFlowUtils.validateHostName;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyByCache;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.HEAD;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
@@ -33,7 +33,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/** RDAP (new WHOIS) action for nameserver requests. */
|
||||
/** RDAP action for nameserver requests. */
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/nameserver/",
|
||||
@@ -48,7 +48,7 @@ public class RdapNameserverAction extends RdapActionBase {
|
||||
|
||||
@Override
|
||||
public RdapNameserver getJsonObjectForResource(String pathSearchString, boolean isHeadRequest) {
|
||||
// RDAP Technical Implementation Guide 2.2.1 - we must support A-label (Punycode) and U-label
|
||||
// RDAP Technical Implementation Guide 2.1.1 - we must support A-label (Punycode) and U-label
|
||||
// (Unicode) formats. canonicalizeName will transform Unicode to Punycode so we support both.
|
||||
pathSearchString = canonicalizeName(pathSearchString);
|
||||
// The RDAP syntax is /rdap/nameserver/ns1.mydomain.com.
|
||||
@@ -63,7 +63,7 @@ public class RdapNameserverAction extends RdapActionBase {
|
||||
// If there are no undeleted nameservers with the given name, the foreign key should point to
|
||||
// the most recently deleted one.
|
||||
Optional<Host> host =
|
||||
loadByForeignKeyCached(
|
||||
loadByForeignKeyByCache(
|
||||
Host.class,
|
||||
pathSearchString,
|
||||
shouldIncludeDeleted() ? START_OF_TIME : getRequestTime());
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.rdap;
|
||||
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyByCache;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.HEAD;
|
||||
@@ -47,9 +47,9 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for nameserver search requests.
|
||||
* RDAP action for nameserver search requests.
|
||||
*
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in STD 95 and its RFCs.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
@@ -159,7 +159,8 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
.setIncompletenessWarningType(IncompletenessWarningType.COMPLETE);
|
||||
|
||||
Optional<Host> host =
|
||||
loadByForeignKeyCached(Host.class, partialStringQuery.getInitialString(), getRequestTime());
|
||||
loadByForeignKeyByCache(
|
||||
Host.class, partialStringQuery.getInitialString(), getRequestTime());
|
||||
|
||||
metricInformationBuilder.setNumHostsRetrieved(host.isPresent() ? 1 : 0);
|
||||
|
||||
@@ -175,7 +176,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
private NameserverSearchResponse searchByNameUsingSuperordinateDomain(
|
||||
RdapSearchPattern partialStringQuery) {
|
||||
Optional<Domain> domain =
|
||||
loadByForeignKeyCached(Domain.class, partialStringQuery.getSuffix(), getRequestTime());
|
||||
loadByForeignKeyByCache(Domain.class, partialStringQuery.getSuffix(), getRequestTime());
|
||||
if (domain.isEmpty()) {
|
||||
// Don't allow wildcards with suffixes which are not domains we manage. That would risk a
|
||||
// table scan in many easily foreseeable cases. The user might ask for ns*.zombo.com,
|
||||
@@ -193,7 +194,7 @@ public class RdapNameserverSearchAction extends RdapSearchActionBase {
|
||||
// We can't just check that the host name starts with the initial query string, because
|
||||
// then the query ns.exam*.example.com would match against nameserver ns.example.com.
|
||||
if (partialStringQuery.matches(fqhn)) {
|
||||
Optional<Host> host = loadByForeignKeyCached(Host.class, fqhn, getRequestTime());
|
||||
Optional<Host> host = loadByForeignKeyByCache(Host.class, fqhn, getRequestTime());
|
||||
if (shouldBeVisible(host)) {
|
||||
hostList.add(host.get());
|
||||
if (hostList.size() > rdapResultSetMaxSize) {
|
||||
|
||||
@@ -45,11 +45,7 @@ import java.util.Optional;
|
||||
/** Object Classes defined in RFC 9083 section 5. */
|
||||
final class RdapObjectClasses {
|
||||
|
||||
/**
|
||||
* Temporary implementation of VCards.
|
||||
*
|
||||
* <p>Will create a better implementation soon.
|
||||
*/
|
||||
/** Rough implementation of VCards. */
|
||||
@RestrictJsonNames({})
|
||||
@AutoValue
|
||||
public abstract static class Vcard implements Jsonable {
|
||||
@@ -140,8 +136,8 @@ final class RdapObjectClasses {
|
||||
public enum BoilerplateType {
|
||||
DOMAIN(RdapIcannStandardInformation.DOMAIN_BOILERPLATE_NOTICES),
|
||||
DOMAIN_BLOCKED_BY_BSA(RdapIcannStandardInformation.DOMAIN_BLOCKED_BY_BSA_BOILERPLATE_NOTICES),
|
||||
NAMESERVER(RdapIcannStandardInformation.NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES),
|
||||
ENTITY(RdapIcannStandardInformation.NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES),
|
||||
NAMESERVER(ImmutableList.of()),
|
||||
ENTITY(ImmutableList.of()),
|
||||
OTHER(ImmutableList.of());
|
||||
|
||||
@SuppressWarnings("ImmutableEnumChecker") // immutable lists are, in fact, immutable
|
||||
@@ -173,8 +169,8 @@ final class RdapObjectClasses {
|
||||
* The Top Level JSON reply, Adds the required top-level boilerplate to a ReplyPayloadBase.
|
||||
*
|
||||
* <p>RFC 9083 specifies that the top-level object should include an entry indicating the
|
||||
* conformance level. ICANN RDAP spec for 15feb19 mandates several additional entries, in sections
|
||||
* 2.6.3, 2.11 of the Response Profile and 3.3, 3.5, of the Technical Implementation Guide.
|
||||
* conformance level. The RDAP spec mandates several additional entries, in sections 2.6.3, 2.10
|
||||
* of the Response Profile and 3.3, 3.5, of the Technical Implementation Guide.
|
||||
*/
|
||||
@AutoValue
|
||||
@RestrictJsonNames({})
|
||||
@@ -353,7 +349,7 @@ final class RdapObjectClasses {
|
||||
*
|
||||
* <p>Takes care of the name and unicode field.
|
||||
*
|
||||
* <p>See RDAP Response Profile 15feb19 sections 2.1 and 4.1.
|
||||
* <p>See RDAP Response Profile sections 2.1 and 4.1.
|
||||
*
|
||||
* <p>Note the ldhName field is only required for non-IDN names or IDN names when the query was an
|
||||
* A-label. It is optional for IDN names when the query was a U-label. Because we don't want to
|
||||
@@ -471,7 +467,7 @@ final class RdapObjectClasses {
|
||||
}
|
||||
|
||||
/**
|
||||
* an integer representing the signature lifetime in seconds to be used when creating the RRSIG
|
||||
* An integer representing the signature lifetime in seconds to be used when creating the RRSIG
|
||||
* DS record in the parent zone [RFC5910].
|
||||
*
|
||||
* <p>Note that although it isn't given as optional in RFC 9083, in RFC5910 it's mentioned as
|
||||
|
||||
@@ -31,7 +31,6 @@ import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.HttpException.UnprocessableEntityException;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.ParameterMap;
|
||||
import google.registry.request.RequestUrl;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@@ -45,7 +44,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Base RDAP (new WHOIS) action for domain, nameserver and entity search requests.
|
||||
* Base RDAP action for domain, nameserver and entity search requests.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
@@ -54,7 +53,6 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
|
||||
private static final int RESULT_SET_SIZE_SCALING_FACTOR = 30;
|
||||
|
||||
@Inject @RequestUrl String requestUrl;
|
||||
@Inject @ParameterMap ImmutableListMultimap<String, String> parameterMap;
|
||||
@Inject @Parameter("cursor") Optional<String> cursorTokenParam;
|
||||
@Inject @Parameter("registrar") Optional<String> registrarParam;
|
||||
@@ -157,7 +155,6 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
*/
|
||||
<T extends EppResource> RdapResultSet<T> getMatchingResources(
|
||||
CriteriaQueryBuilder<T> builder, boolean checkForVisibility, int querySizeLimit) {
|
||||
replicaTm().assertInTransaction();
|
||||
Optional<String> desiredRegistrar = getDesiredRegistrar();
|
||||
if (desiredRegistrar.isPresent()) {
|
||||
builder =
|
||||
|
||||
@@ -39,8 +39,8 @@ public final class RdapSearchPattern {
|
||||
/**
|
||||
* Pattern for allowed LDH searches.
|
||||
*
|
||||
* <p>Based on RFC 9082 4.1. Must contains only alphanumeric plus dots and hyphens. A single
|
||||
* whildcard asterix is allowed - but if exists must be the last character of a domain name label
|
||||
* <p>Based on RFC 9082 4.1. Must contain only alphanumeric plus dots and hyphens. A single
|
||||
* wildcard asterix is allowed - but if exists must be the last character of a domain name label
|
||||
* (so exam* and exam*.com are allowed, but exam*le.com isn't allowd)
|
||||
*
|
||||
* <p>The prefix is in group(1), and the suffix without the dot (if it exists) is in group(4). If
|
||||
@@ -123,7 +123,7 @@ public final class RdapSearchPattern {
|
||||
* Creates a SearchPattern using the provided domain search pattern in LDH format.
|
||||
*
|
||||
* <p>The domain search pattern can have a single wildcard asterix that can match 0 or more
|
||||
* charecters. If such an asterix exists - it must be at the end of a domain label.
|
||||
* characters. If such an asterix exists - it must be at the end of a domain label.
|
||||
*
|
||||
* @param searchQuery the string containing the partial match pattern
|
||||
* @throws UnprocessableEntityException if {@code pattern} does not meet the requirements of RFC
|
||||
@@ -150,7 +150,7 @@ public final class RdapSearchPattern {
|
||||
* Creates a SearchPattern using the provided domain search pattern in LDH or Unicode format.
|
||||
*
|
||||
* <p>The domain search pattern can have a single wildcard asterix that can match 0 or more
|
||||
* charecters. If such an asterix exists - it must be at the end of a domain label.
|
||||
* characters. If such an asterix exists - it must be at the end of a domain label.
|
||||
*
|
||||
* <p>In theory, according to RFC 9082 4.1 - we should make some checks about partial matching in
|
||||
* unicode queries. We don't, but we might want to just disable partial matches for unicode inputs
|
||||
|
||||
@@ -37,7 +37,7 @@ public final class RdapUtils {
|
||||
*
|
||||
* <p>Used for RDAP Technical Implementation Guide 2.4.2 - search of registrar by the fn element.
|
||||
*
|
||||
* <p>For convenience, we use case insensitive search.
|
||||
* <p>For convenience, we use case-insensitive search.
|
||||
*/
|
||||
static Optional<Registrar> getRegistrarByName(String registrarName) {
|
||||
return Streams.stream(Registrar.loadAllCached())
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.PersistenceModule;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.HttpException.InternalServerErrorException;
|
||||
@@ -36,7 +37,7 @@ import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
import java.security.GeneralSecurityException;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
@@ -72,7 +73,7 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
public void run() {
|
||||
try {
|
||||
ImmutableMap<String, String> ianaIdsToUrls = getIanaIdsToUrls();
|
||||
tm().transact(() -> processAllRegistrars(ianaIdsToUrls));
|
||||
processAllRegistrars(ianaIdsToUrls);
|
||||
} catch (Exception e) {
|
||||
throw new InternalServerErrorException("Error when retrieving RDAP base URL CSV file", e);
|
||||
}
|
||||
@@ -80,7 +81,14 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
|
||||
private static void processAllRegistrars(ImmutableMap<String, String> ianaIdsToUrls) {
|
||||
int nonUpdatedRegistrars = 0;
|
||||
for (Registrar registrar : Registrar.loadAll()) {
|
||||
// Split into multiple transactions to avoid load-save-reload conflicts. Re-building a registrar
|
||||
// requires a full (cached) load of all registrars to avoid IANA ID conflicts, so if multiple
|
||||
// registrars are modified in the same transaction, the second build call will fail.
|
||||
Iterable<Registrar> registrars =
|
||||
tm().transact(
|
||||
PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ,
|
||||
Registrar::loadAll);
|
||||
for (Registrar registrar : registrars) {
|
||||
// Only update REAL registrars
|
||||
if (registrar.getType() != Registrar.Type.REAL) {
|
||||
continue;
|
||||
@@ -100,7 +108,12 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
"Updating RDAP base URLs for registrar %s from %s to %s",
|
||||
registrar.getRegistrarId(), registrar.getRdapBaseUrls(), baseUrls);
|
||||
}
|
||||
tm().put(registrar.asBuilder().setRdapBaseUrls(baseUrls).build());
|
||||
tm().transact(
|
||||
() -> {
|
||||
// Reload inside a transaction to avoid race conditions
|
||||
Registrar reloadedRegistrar = tm().loadByEntity(registrar);
|
||||
tm().put(reloadedRegistrar.asBuilder().setRdapBaseUrls(baseUrls).build());
|
||||
});
|
||||
}
|
||||
}
|
||||
logger.atInfo().log("No change in RDAP base URLs for %d registrars", nonUpdatedRegistrars);
|
||||
@@ -108,9 +121,9 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
|
||||
private ImmutableMap<String, String> getIanaIdsToUrls()
|
||||
throws IOException, GeneralSecurityException {
|
||||
CSVParser csv;
|
||||
HttpURLConnection connection = urlConnectionService.createConnection(new URL(RDAP_IDS_URL));
|
||||
// Explictly set the accepted encoding, as we know Brotli causes us problems when talking to
|
||||
HttpURLConnection connection =
|
||||
urlConnectionService.createConnection(URI.create(RDAP_IDS_URL).toURL());
|
||||
// Explicitly set the accepted encoding, as we know Brotli causes us problems when talking to
|
||||
// ICANN.
|
||||
connection.setRequestProperty(ACCEPT_ENCODING, "gzip");
|
||||
String csvString;
|
||||
@@ -128,11 +141,11 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
csv =
|
||||
CSVParser csv =
|
||||
CSVFormat.Builder.create(CSVFormat.DEFAULT)
|
||||
.setHeader()
|
||||
.setSkipHeaderRecord(true)
|
||||
.build()
|
||||
.get()
|
||||
.parse(new StringReader(csvString));
|
||||
ImmutableMap.Builder<String, String> result = new ImmutableMap.Builder<>();
|
||||
for (CSVRecord record : csv) {
|
||||
|
||||
@@ -72,11 +72,11 @@ final class CreateCdnsTld extends ConfirmingCommand {
|
||||
.setDescription(description)
|
||||
.setNameServerSet(
|
||||
RegistryToolEnvironment.get() == RegistryToolEnvironment.PRODUCTION
|
||||
? "cloud-dns-registry"
|
||||
: "cloud-dns-registry-test")
|
||||
? "cloud-dns-registry"
|
||||
: "cloud-dns-registry-test")
|
||||
.setDnsName(dnsName)
|
||||
.setName((name != null) ? name : dnsName)
|
||||
.setDnssecConfig(new ManagedZoneDnsSecConfig().setNonExistence("NSEC").setState("ON"));
|
||||
.setDnssecConfig(new ManagedZoneDnsSecConfig().setNonExistence("nsec").setState("on"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
@@ -23,6 +24,7 @@ import static org.joda.time.DateTimeZone.UTC;
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.pricing.PremiumPricingEngine.DomainPrices;
|
||||
import google.registry.tools.soy.DomainCreateSoyInfo;
|
||||
import google.registry.util.StringGenerator;
|
||||
@@ -58,9 +60,15 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
|
||||
@Override
|
||||
protected void initMutatingEppToolCommand() {
|
||||
checkArgumentNotNull(registrant, "Registrant must be specified");
|
||||
checkArgument(!admins.isEmpty(), "At least one admin must be specified");
|
||||
checkArgument(!techs.isEmpty(), "At least one tech must be specified");
|
||||
tm().transact(
|
||||
() -> {
|
||||
if (!FeatureFlag.isActiveNowOrElse(
|
||||
FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL, false)) {
|
||||
checkArgumentNotNull(registrant, "Registrant must be specified");
|
||||
checkArgument(!admins.isEmpty(), "At least one admin must be specified");
|
||||
checkArgument(!techs.isEmpty(), "At least one tech must be specified");
|
||||
}
|
||||
});
|
||||
if (isNullOrEmpty(password)) {
|
||||
password = passwordGenerator.createString(PASSWORD_LENGTH);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.batch.AsyncTaskEnqueuer.QUEUE_ASYNC_ACTIONS;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyByCacheIfEnabled;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||
|
||||
@@ -326,7 +326,7 @@ public final class DomainLockUtils {
|
||||
|
||||
private Domain getDomain(String domainName, String registrarId, DateTime now) {
|
||||
Domain domain =
|
||||
loadByForeignKeyCached(Domain.class, domainName, now)
|
||||
loadByForeignKeyByCacheIfEnabled(Domain.class, domainName, now)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Domain doesn't exist"));
|
||||
// The user must have specified either the correct registrar ID or the admin registrar ID
|
||||
checkArgument(
|
||||
|
||||
@@ -23,9 +23,7 @@ import google.registry.config.CloudTasksUtilsModule;
|
||||
import google.registry.config.CredentialModule.LocalCredentialJson;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryConfig.ConfigModule;
|
||||
import google.registry.dns.writer.VoidDnsWriterModule;
|
||||
import google.registry.dns.writer.clouddns.CloudDnsWriterModule;
|
||||
import google.registry.dns.writer.dnsupdate.DnsUpdateWriterModule;
|
||||
import google.registry.dns.writer.DnsWritersModule;
|
||||
import google.registry.keyring.KeyringModule;
|
||||
import google.registry.keyring.api.KeyModule;
|
||||
import google.registry.model.ModelModule;
|
||||
@@ -56,9 +54,8 @@ import javax.annotation.Nullable;
|
||||
BatchModule.class,
|
||||
BigqueryModule.class,
|
||||
ConfigModule.class,
|
||||
CloudDnsWriterModule.class,
|
||||
CloudTasksUtilsModule.class,
|
||||
DnsUpdateWriterModule.class,
|
||||
DnsWritersModule.class,
|
||||
GsonModule.class,
|
||||
KeyModule.class,
|
||||
KeyringModule.class,
|
||||
@@ -71,7 +68,6 @@ import javax.annotation.Nullable;
|
||||
SecretManagerModule.class,
|
||||
UrlConnectionServiceModule.class,
|
||||
UtilsModule.class,
|
||||
VoidDnsWriterModule.class,
|
||||
NonCachingWhoisModule.class
|
||||
})
|
||||
interface RegistryToolComponent {
|
||||
|
||||
@@ -251,11 +251,12 @@ final class UniformRapidSuspensionCommand extends MutatingEppToolCommand {
|
||||
if (undo) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder undoBuilder = new StringBuilder("UNDO COMMAND:\n\n)")
|
||||
.append("nomulus -e ")
|
||||
.append(RegistryToolEnvironment.get())
|
||||
.append(" uniform_rapid_suspension --undo --domain_name ")
|
||||
.append(domainName);
|
||||
StringBuilder undoBuilder =
|
||||
new StringBuilder("UNDO COMMAND:\n\n")
|
||||
.append("nomulus -e ")
|
||||
.append(RegistryToolEnvironment.get())
|
||||
.append(" uniform_rapid_suspension --undo --domain_name ")
|
||||
.append(domainName);
|
||||
if (!existingNameservers.isEmpty()) {
|
||||
undoBuilder.append(" --hosts ").append(Joiner.on(',').join(existingNameservers));
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class ConsoleDomainGetAction extends ConsoleApiAction {
|
||||
Optional<Domain> possibleDomain =
|
||||
tm().transact(
|
||||
() ->
|
||||
EppResourceUtils.loadByForeignKeyCached(
|
||||
EppResourceUtils.loadByForeignKeyByCacheIfEnabled(
|
||||
Domain.class, paramDomain, tm().getTransactionTime()));
|
||||
if (possibleDomain.isEmpty()) {
|
||||
consoleApiParams.response().setStatus(SC_NOT_FOUND);
|
||||
|
||||
@@ -19,7 +19,6 @@ import static google.registry.request.RequestParameters.extractOptionalIntParame
|
||||
import static google.registry.request.RequestParameters.extractOptionalParameter;
|
||||
import static google.registry.request.RequestParameters.extractRequiredParameter;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import dagger.Module;
|
||||
@@ -192,10 +191,10 @@ public final class ConsoleModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("contacts")
|
||||
public static Optional<ImmutableSet<RegistrarPoc>> provideContacts(
|
||||
@Parameter("contact")
|
||||
public static Optional<RegistrarPoc> provideContacts(
|
||||
Gson gson, @OptionalJsonPayload Optional<JsonElement> payload) {
|
||||
return payload.map(s -> ImmutableSet.copyOf(gson.fromJson(s, RegistrarPoc[].class)));
|
||||
return payload.map(s -> gson.fromJson(s, RegistrarPoc.class));
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.ui.server.console;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
|
||||
@@ -37,6 +38,7 @@ import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
@@ -88,18 +90,35 @@ public class ConsoleUpdateRegistrarAction extends ConsoleApiAction {
|
||||
}
|
||||
}
|
||||
|
||||
Registrar updatedRegistrar =
|
||||
DateTime now = tm().getTransactionTime();
|
||||
DateTime newLastPocVerificationDate =
|
||||
registrarParam.getLastPocVerificationDate() == null
|
||||
? START_OF_TIME
|
||||
: registrarParam.getLastPocVerificationDate();
|
||||
|
||||
checkArgument(
|
||||
newLastPocVerificationDate.isBefore(now),
|
||||
"Invalid value of LastPocVerificationDate - value is in the future");
|
||||
|
||||
var updatedRegistrarBuilder =
|
||||
existingRegistrar
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setAllowedTlds(
|
||||
registrarParam.getAllowedTlds().stream()
|
||||
.map(DomainNameUtils::canonicalizeHostname)
|
||||
.collect(Collectors.toSet()))
|
||||
.setRegistryLockAllowed(registrarParam.isRegistryLockAllowed())
|
||||
.setLastPocVerificationDate(registrarParam.getLastPocVerificationDate())
|
||||
.build();
|
||||
.setLastPocVerificationDate(newLastPocVerificationDate);
|
||||
|
||||
if (user.getUserRoles()
|
||||
.hasGlobalPermission(ConsolePermission.EDIT_REGISTRAR_DETAILS)) {
|
||||
updatedRegistrarBuilder =
|
||||
updatedRegistrarBuilder
|
||||
.setAllowedTlds(
|
||||
registrarParam.getAllowedTlds().stream()
|
||||
.map(DomainNameUtils::canonicalizeHostname)
|
||||
.collect(Collectors.toSet()))
|
||||
.setRegistryLockAllowed(registrarParam.isRegistryLockAllowed())
|
||||
.setLastPocVerificationDate(newLastPocVerificationDate);
|
||||
}
|
||||
|
||||
var updatedRegistrar = updatedRegistrarBuilder.build();
|
||||
tm().put(updatedRegistrar);
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
|
||||
@@ -15,12 +15,13 @@
|
||||
package google.registry.ui.server.console.settings;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.DELETE;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.request.Action.Method.PUT;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
@@ -33,66 +34,122 @@ import google.registry.model.console.User;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.registrar.RegistrarPoc.Type;
|
||||
import google.registry.persistence.transaction.QueryComposer.Comparator;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.Action.GkeService;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.ui.forms.FormException;
|
||||
import google.registry.ui.server.RegistrarFormFields;
|
||||
import google.registry.ui.server.console.ConsoleApiAction;
|
||||
import google.registry.ui.server.console.ConsoleApiParams;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
gkeService = GkeService.CONSOLE,
|
||||
path = ContactAction.PATH,
|
||||
method = {GET, POST},
|
||||
method = {GET, POST, DELETE, PUT},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class ContactAction extends ConsoleApiAction {
|
||||
static final String PATH = "/console-api/settings/contacts";
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private final Optional<ImmutableSet<RegistrarPoc>> contacts;
|
||||
private final Optional<RegistrarPoc> contact;
|
||||
private final String registrarId;
|
||||
|
||||
@Inject
|
||||
public ContactAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Parameter("registrarId") String registrarId,
|
||||
@Parameter("contacts") Optional<ImmutableSet<RegistrarPoc>> contacts) {
|
||||
@Parameter("contact") Optional<RegistrarPoc> contact) {
|
||||
super(consoleApiParams);
|
||||
this.registrarId = registrarId;
|
||||
this.contacts = contacts;
|
||||
this.contact = contact;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getHandler(User user) {
|
||||
checkPermission(user, registrarId, ConsolePermission.VIEW_REGISTRAR_DETAILS);
|
||||
ImmutableList<RegistrarPoc> am =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm()
|
||||
.createQueryComposer(RegistrarPoc.class)
|
||||
.where("registrarId", Comparator.EQ, registrarId)
|
||||
.stream()
|
||||
.filter(r -> !r.getTypes().isEmpty())
|
||||
.collect(toImmutableList()));
|
||||
|
||||
ImmutableList<RegistrarPoc> contacts =
|
||||
tm().transact(() -> RegistrarPoc.loadForRegistrar(registrarId));
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(am));
|
||||
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(contacts));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteHandler(User user) {
|
||||
updateContacts(
|
||||
user,
|
||||
(registrar, oldContacts) ->
|
||||
oldContacts.stream()
|
||||
.filter(
|
||||
oldContact ->
|
||||
!oldContact.getEmailAddress().equals(contact.get().getEmailAddress()))
|
||||
.collect(toImmutableSet()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postHandler(User user) {
|
||||
updateContacts(
|
||||
user,
|
||||
(registrar, oldContacts) -> {
|
||||
RegistrarPoc newContact = contact.get();
|
||||
return ImmutableSet.<RegistrarPoc>builder()
|
||||
.addAll(oldContacts)
|
||||
.add(
|
||||
new RegistrarPoc()
|
||||
.asBuilder()
|
||||
.setTypes(newContact.getTypes())
|
||||
.setVisibleInWhoisAsTech(newContact.getVisibleInWhoisAsTech())
|
||||
.setVisibleInWhoisAsAdmin(newContact.getVisibleInWhoisAsAdmin())
|
||||
.setVisibleInDomainWhoisAsAbuse(newContact.getVisibleInDomainWhoisAsAbuse())
|
||||
.setFaxNumber(newContact.getFaxNumber())
|
||||
.setName(newContact.getName())
|
||||
.setEmailAddress(newContact.getEmailAddress())
|
||||
.setPhoneNumber(newContact.getPhoneNumber())
|
||||
.setRegistrar(registrar)
|
||||
.build())
|
||||
.build();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void putHandler(User user) {
|
||||
updateContacts(
|
||||
user,
|
||||
(registrar, oldContacts) -> {
|
||||
RegistrarPoc updatedContact = contact.get();
|
||||
return oldContacts.stream()
|
||||
.map(
|
||||
oldContact ->
|
||||
oldContact.getId().equals(updatedContact.getId())
|
||||
? oldContact
|
||||
.asBuilder()
|
||||
.setTypes(updatedContact.getTypes())
|
||||
.setVisibleInWhoisAsTech(updatedContact.getVisibleInWhoisAsTech())
|
||||
.setVisibleInWhoisAsAdmin(updatedContact.getVisibleInWhoisAsAdmin())
|
||||
.setVisibleInDomainWhoisAsAbuse(
|
||||
updatedContact.getVisibleInDomainWhoisAsAbuse())
|
||||
.setFaxNumber(updatedContact.getFaxNumber())
|
||||
.setName(updatedContact.getName())
|
||||
.setEmailAddress(updatedContact.getEmailAddress())
|
||||
.setPhoneNumber(updatedContact.getPhoneNumber())
|
||||
.build()
|
||||
: oldContact)
|
||||
.collect(toImmutableSet());
|
||||
});
|
||||
}
|
||||
|
||||
private void updateContacts(
|
||||
User user,
|
||||
BiFunction<Registrar, ImmutableSet<RegistrarPoc>, ImmutableSet<RegistrarPoc>>
|
||||
contactsUpdater) {
|
||||
checkPermission(user, registrarId, ConsolePermission.EDIT_REGISTRAR_DETAILS);
|
||||
checkArgument(contacts.isPresent(), "Contacts parameter is not present");
|
||||
checkArgument(contact.isPresent(), "Contact parameter is not present");
|
||||
Registrar registrar =
|
||||
Registrar.loadByRegistrarId(registrarId)
|
||||
.orElseThrow(
|
||||
@@ -101,20 +158,10 @@ public class ContactAction extends ConsoleApiAction {
|
||||
String.format("Unknown registrar %s", registrarId)));
|
||||
|
||||
ImmutableSet<RegistrarPoc> oldContacts = registrar.getContacts();
|
||||
ImmutableSet<RegistrarPoc> updatedContacts =
|
||||
RegistrarFormFields.getRegistrarContactBuilders(
|
||||
oldContacts,
|
||||
Collections.singletonMap(
|
||||
"contacts",
|
||||
contacts.get().stream()
|
||||
.map(RegistrarPoc::toJsonMap)
|
||||
.collect(toImmutableList())))
|
||||
.stream()
|
||||
.map(builder -> builder.setRegistrar(registrar).build())
|
||||
.collect(toImmutableSet());
|
||||
ImmutableSet<RegistrarPoc> newContacts = contactsUpdater.apply(registrar, oldContacts);
|
||||
|
||||
try {
|
||||
checkContactRequirements(oldContacts, updatedContacts);
|
||||
checkContactRequirements(oldContacts, newContacts);
|
||||
} catch (FormException e) {
|
||||
logger.atWarning().withCause(e).log(
|
||||
"Error processing contacts post request for registrar: %s", registrarId);
|
||||
@@ -123,14 +170,13 @@ public class ContactAction extends ConsoleApiAction {
|
||||
|
||||
tm().transact(
|
||||
() -> {
|
||||
RegistrarPoc.updateContacts(registrar, updatedContacts);
|
||||
RegistrarPoc.updateContacts(registrar, newContacts);
|
||||
Registrar updatedRegistrar =
|
||||
registrar.asBuilder().setContactsRequireSyncing(true).build();
|
||||
tm().put(updatedRegistrar);
|
||||
sendExternalUpdatesIfNecessary(
|
||||
EmailInfo.create(registrar, updatedRegistrar, oldContacts, updatedContacts));
|
||||
EmailInfo.create(registrar, updatedRegistrar, oldContacts, newContacts));
|
||||
});
|
||||
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
|
||||
@@ -169,6 +215,7 @@ public class ContactAction extends ConsoleApiAction {
|
||||
throw new ContactRequirementException(t);
|
||||
}
|
||||
}
|
||||
|
||||
enforcePrimaryContactRestrictions(oldContactsByType, newContactsByType);
|
||||
ensurePhoneNumberNotRemovedForContactTypes(oldContactsByType, newContactsByType, Type.TECH);
|
||||
Optional<RegistrarPoc> domainWhoisAbuseContact =
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.whois;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.isBlockedByBsa;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyByCache;
|
||||
import static google.registry.model.tld.Tlds.findTldForName;
|
||||
import static google.registry.model.tld.Tlds.getTlds;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
@@ -94,7 +94,7 @@ public class DomainLookupCommand implements WhoisCommand {
|
||||
private Optional<WhoisResponse> getResponse(InternetDomainName domainName, DateTime now) {
|
||||
Optional<Domain> domainResource =
|
||||
cached
|
||||
? loadByForeignKeyCached(Domain.class, domainName.toString(), now)
|
||||
? loadByForeignKeyByCache(Domain.class, domainName.toString(), now)
|
||||
: loadByForeignKey(Domain.class, domainName.toString(), now);
|
||||
return domainResource.map(
|
||||
domain -> new DomainWhoisResponse(domain, fullOutput, whoisRedactedEmailText, now));
|
||||
|
||||
@@ -81,7 +81,7 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
|
||||
domain.getCurrentSponsorRegistrarId());
|
||||
Registrar registrar = registrarOptional.get();
|
||||
Optional<RegistrarPoc> abuseContact =
|
||||
registrar.getContacts().stream()
|
||||
registrar.getContactsFromReplica().stream()
|
||||
.filter(RegistrarPoc::getVisibleInDomainWhoisAsAbuse)
|
||||
.findFirst();
|
||||
return WhoisResponseResults.create(
|
||||
@@ -154,7 +154,7 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
|
||||
// If we refer to a contact that doesn't exist, that's a bug. It means referential integrity
|
||||
// has somehow been broken. We skip the rest of this contact, but log it to hopefully bring it
|
||||
// someone's attention.
|
||||
Contact contact1 = EppResource.loadCached(contact.get());
|
||||
Contact contact1 = EppResource.loadByCache(contact.get());
|
||||
if (contact1 == null) {
|
||||
logger.atSevere().log(
|
||||
"(BUG) Broken reference found from domain %s to contact %s.",
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.whois;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKeyByCache;
|
||||
import static google.registry.model.tld.Tlds.findTldForName;
|
||||
import static google.registry.model.tld.Tlds.getTlds;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND;
|
||||
@@ -57,7 +57,7 @@ public class NameserverLookupByHostCommand implements WhoisCommand {
|
||||
private Optional<WhoisResponse> getResponse(InternetDomainName hostName, DateTime now) {
|
||||
Optional<Host> host =
|
||||
cached
|
||||
? loadByForeignKeyCached(Host.class, hostName.toString(), now)
|
||||
? loadByForeignKeyByCache(Host.class, hostName.toString(), now)
|
||||
: loadByForeignKey(Host.class, hostName.toString(), now);
|
||||
return host.map(h -> new NameserverWhoisResponse(h, now));
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class RegistrarWhoisResponse extends WhoisResponseImpl {
|
||||
|
||||
@Override
|
||||
public WhoisResponseResults getResponse(boolean preferUnicode, String disclaimer) {
|
||||
Set<RegistrarPoc> contacts = registrar.getContacts();
|
||||
Set<RegistrarPoc> contacts = registrar.getContactsFromReplica();
|
||||
String plaintext =
|
||||
new RegistrarEmitter()
|
||||
.emitField("Registrar", registrar.getRegistrarName())
|
||||
|
||||
@@ -47,13 +47,14 @@
|
||||
<class>google.registry.model.billing.BillingRecurrence</class>
|
||||
<class>google.registry.model.common.Cursor</class>
|
||||
<class>google.registry.model.common.DnsRefreshRequest</class>
|
||||
<class>google.registry.model.common.FeatureFlag</class>
|
||||
<class>google.registry.model.console.ConsoleUpdateHistory</class>
|
||||
<class>google.registry.model.console.PasswordResetRequest</class>
|
||||
<class>google.registry.model.console.User</class>
|
||||
<class>google.registry.model.contact.ContactHistory</class>
|
||||
<class>google.registry.model.contact.Contact</class>
|
||||
<class>google.registry.model.domain.Domain</class>
|
||||
<class>google.registry.model.domain.DomainHistory</class>
|
||||
<class>google.registry.model.common.FeatureFlag</class>
|
||||
<class>google.registry.model.domain.GracePeriod</class>
|
||||
<class>google.registry.model.domain.GracePeriod$GracePeriodHistory</class>
|
||||
<class>google.registry.model.domain.secdns.DomainDsData</class>
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
{@param domain: string}
|
||||
{@param period: int}
|
||||
{@param nameservers: list<string>}
|
||||
{@param registrant: string}
|
||||
{@param admins: list<string>}
|
||||
{@param techs: list<string>}
|
||||
{@param? registrant: string|null}
|
||||
{@param? admins: list<string>|null}
|
||||
{@param? techs: list<string>|null}
|
||||
{@param password: string}
|
||||
{@param? currency: string|null}
|
||||
{@param? price: string|null}
|
||||
@@ -45,13 +45,19 @@
|
||||
{/for}
|
||||
</domain:ns>
|
||||
{/if}
|
||||
<domain:registrant>{$registrant}</domain:registrant>
|
||||
{for $admin in $admins}
|
||||
<domain:contact type="admin">{$admin}</domain:contact>
|
||||
{/for}
|
||||
{for $tech in $techs}
|
||||
<domain:contact type="tech">{$tech}</domain:contact>
|
||||
{/for}
|
||||
{if $registrant != null}
|
||||
<domain:registrant>{$registrant}</domain:registrant>
|
||||
{/if}
|
||||
{if $admins != null}
|
||||
{for $admin in $admins}
|
||||
<domain:contact type="admin">{$admin}</domain:contact>
|
||||
{/for}
|
||||
{/if}
|
||||
{if $techs != null}
|
||||
{for $tech in $techs}
|
||||
<domain:contact type="tech">{$tech}</domain:contact>
|
||||
{/for}
|
||||
{/if}
|
||||
<domain:authInfo>
|
||||
<domain:pw>{$password}</domain:pw>
|
||||
</domain:authInfo>
|
||||
|
||||
@@ -85,7 +85,7 @@ class BillingEventTest {
|
||||
assertThat(invoiceKey.startDate()).isEqualTo("2017-10-01");
|
||||
assertThat(invoiceKey.endDate()).isEqualTo("2022-09-30");
|
||||
assertThat(invoiceKey.productAccountKey()).isEqualTo("12345-CRRHELLO");
|
||||
assertThat(invoiceKey.usageGroupingKey()).isEqualTo("myRegistrar");
|
||||
assertThat(invoiceKey.usageGroupingKey()).isEqualTo("");
|
||||
assertThat(invoiceKey.description()).isEqualTo("RENEW | TLD: test | TERM: 5-year");
|
||||
assertThat(invoiceKey.unitPrice()).isEqualTo(20.5);
|
||||
assertThat(invoiceKey.unitPriceCurrency()).isEqualTo("USD");
|
||||
@@ -106,7 +106,7 @@ class BillingEventTest {
|
||||
assertThat(invoiceKey.toCsv(3L))
|
||||
.isEqualTo(
|
||||
"2017-10-01,2022-09-30,12345-CRRHELLO,61.50,USD,10125,1,PURCHASE,"
|
||||
+ "myRegistrar,3,RENEW | TLD: test | TERM: 5-year,20.50,USD,");
|
||||
+ ",3,RENEW | TLD: test | TERM: 5-year,20.50,USD,");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,7 +116,7 @@ class BillingEventTest {
|
||||
assertThat(invoiceKey.toCsv(3L))
|
||||
.isEqualTo(
|
||||
"2017-10-01,,12345-CRRHELLO,61.50,USD,10125,1,PURCHASE,"
|
||||
+ "myRegistrar,3,RENEW | TLD: test | TERM: 0-year,20.50,USD,");
|
||||
+ ",3,RENEW | TLD: test | TERM: 0-year,20.50,USD,");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -199,6 +199,36 @@ class InvoicingPipelineTest {
|
||||
0,
|
||||
"USD",
|
||||
20.0,
|
||||
""),
|
||||
google.registry.beam.billing.BillingEvent.create(
|
||||
15,
|
||||
DateTime.parse("2017-10-02T00:00:00.0Z"),
|
||||
DateTime.parse("2017-10-04T00:00:00.0Z"),
|
||||
"theRegistrarCopy",
|
||||
"234",
|
||||
"",
|
||||
"test",
|
||||
"CREATE",
|
||||
"mydomainfromanotherclient.test",
|
||||
"REPO-ID",
|
||||
5,
|
||||
"JPY",
|
||||
70.0,
|
||||
""),
|
||||
google.registry.beam.billing.BillingEvent.create(
|
||||
16,
|
||||
DateTime.parse("2017-10-04T00:00:00Z"),
|
||||
DateTime.parse("2017-10-04T00:00:00Z"),
|
||||
"theRegistrarCopy",
|
||||
"234",
|
||||
"",
|
||||
"test",
|
||||
"RENEW",
|
||||
"mydomain2fromanotherclient.test",
|
||||
"REPO-ID",
|
||||
3,
|
||||
"USD",
|
||||
20.5,
|
||||
""));
|
||||
|
||||
private static final ImmutableMap<String, ImmutableList<String>> EXPECTED_DETAILED_REPORT_MAP =
|
||||
@@ -224,18 +254,26 @@ class InvoicingPipelineTest {
|
||||
"invoice_details_2017-10_anotherRegistrar_test.csv",
|
||||
ImmutableList.of(
|
||||
"5,2017-10-04 00:00:00 UTC,2017-10-04 00:00:00 UTC,anotherRegistrar,789,,"
|
||||
+ "test,CREATE,mydomain5.test,REPO-ID,1,USD,0.00,SUNRISE ANCHOR_TENANT"));
|
||||
+ "test,CREATE,mydomain5.test,REPO-ID,1,USD,0.00,SUNRISE ANCHOR_TENANT"),
|
||||
"invoice_details_2017-10_theRegistrarCopy_test.csv",
|
||||
ImmutableList.of(
|
||||
"15,2017-10-02 00:00:00 UTC,2017-10-04 00:00:00"
|
||||
+ " UTC,theRegistrarCopy,234,,test,CREATE,mydomainfromanotherclient.test,REPO-ID,5,JPY,70.00,",
|
||||
"16,2017-10-04 00:00:00 UTC,2017-10-04 00:00:00"
|
||||
+ " UTC,theRegistrarCopy,234,,test,RENEW,mydomain2fromanotherclient.test,REPO-ID,3,USD,20.50,"));
|
||||
|
||||
private static final ImmutableList<String> EXPECTED_INVOICE_OUTPUT =
|
||||
ImmutableList.of(
|
||||
"2017-10-01,2020-09-30,234,41.00,USD,10125,1,PURCHASE,theRegistrar,2,"
|
||||
"2017-10-01,2020-09-30,234,61.50,USD,10125,1,PURCHASE,,3,"
|
||||
+ "RENEW | TLD: test | TERM: 3-year,20.50,USD,",
|
||||
"2017-10-01,2022-09-30,234,70.00,JPY,10125,1,PURCHASE,theRegistrar,1,"
|
||||
"2017-10-01,2022-09-30,234,70.00,JPY,10125,1,PURCHASE,,1,"
|
||||
+ "CREATE | TLD: hello | TERM: 5-year,70.00,JPY,",
|
||||
"2017-10-01,,234,20.00,USD,10125,1,PURCHASE,theRegistrar,1,"
|
||||
"2017-10-01,,234,20.00,USD,10125,1,PURCHASE,,1,"
|
||||
+ "SERVER_STATUS | TLD: test | TERM: 0-year,20.00,USD,",
|
||||
"2017-10-01,2018-09-30,456,20.50,USD,10125,1,PURCHASE,bestdomains,1,"
|
||||
+ "RENEW | TLD: test | TERM: 1-year,20.50,USD,116688");
|
||||
"2017-10-01,2018-09-30,456,20.50,USD,10125,1,PURCHASE,,1,"
|
||||
+ "RENEW | TLD: test | TERM: 1-year,20.50,USD,116688",
|
||||
"2017-10-01,2022-09-30,234,70.00,JPY,10125,1,PURCHASE,,1,CREATE | TLD: test | TERM:"
|
||||
+ " 5-year,70.00,JPY,");
|
||||
|
||||
private final InvoicingPipelineOptions options =
|
||||
PipelineOptionsFactory.create().as(InvoicingPipelineOptions.class);
|
||||
@@ -355,21 +393,21 @@ class InvoicingPipelineTest {
|
||||
.isEqualTo(
|
||||
"""
|
||||
|
||||
SELECT b, r FROM BillingEvent b
|
||||
JOIN Registrar r ON b.clientId = r.registrarId
|
||||
JOIN Domain d ON b.domainRepoId = d.repoId
|
||||
JOIN Tld t ON t.tldStr = d.tld
|
||||
LEFT JOIN BillingCancellation c ON b.id = c.billingEvent
|
||||
LEFT JOIN BillingCancellation cr ON b.cancellationMatchingBillingEvent = cr.billingRecurrence
|
||||
WHERE r.billingAccountMap IS NOT NULL
|
||||
AND r.type = 'REAL'
|
||||
AND t.invoicingEnabled IS TRUE
|
||||
AND CAST(b.billingTime AS timestamp)
|
||||
BETWEEN CAST('2017-10-01T00:00:00Z' AS timestamp)
|
||||
AND CAST('2017-11-01T00:00:00Z' AS timestamp)
|
||||
AND c.id IS NULL
|
||||
AND cr.id IS NULL
|
||||
""");
|
||||
SELECT b, r FROM BillingEvent b
|
||||
JOIN Registrar r ON b.clientId = r.registrarId
|
||||
JOIN Domain d ON b.domainRepoId = d.repoId
|
||||
JOIN Tld t ON t.tldStr = d.tld
|
||||
LEFT JOIN BillingCancellation c ON b.id = c.billingEvent
|
||||
LEFT JOIN BillingCancellation cr ON b.cancellationMatchingBillingEvent = cr.billingRecurrence
|
||||
WHERE r.billingAccountMap IS NOT NULL
|
||||
AND r.type = 'REAL'
|
||||
AND t.invoicingEnabled IS TRUE
|
||||
AND CAST(b.billingTime AS timestamp)
|
||||
BETWEEN CAST('2017-10-01T00:00:00Z' AS timestamp)
|
||||
AND CAST('2017-11-01T00:00:00Z' AS timestamp)
|
||||
AND c.id IS NULL
|
||||
AND cr.id IS NULL
|
||||
""");
|
||||
}
|
||||
|
||||
/** Returns the text contents of a file under the beamBucket/results directory. */
|
||||
@@ -391,6 +429,13 @@ class InvoicingPipelineTest {
|
||||
.setBillingAccountMap(ImmutableMap.of(JPY, "234", USD, "234"))
|
||||
.build();
|
||||
persistResource(registrar1);
|
||||
Registrar registrar11 = persistNewRegistrar("theRegistrarCopy");
|
||||
registrar11 =
|
||||
registrar11
|
||||
.asBuilder()
|
||||
.setBillingAccountMap(ImmutableMap.of(JPY, "234", USD, "234"))
|
||||
.build();
|
||||
persistResource(registrar11);
|
||||
Registrar registrar2 = persistNewRegistrar("bestdomains");
|
||||
registrar2 =
|
||||
registrar2
|
||||
@@ -547,6 +592,21 @@ class InvoicingPipelineTest {
|
||||
.setDomainHistory(domainHistoryRecurrence)
|
||||
.build();
|
||||
persistResource(cancellationRecurrence);
|
||||
|
||||
// Domains created for registrar with = key but != client id.
|
||||
Domain domain14 = persistActiveDomain("mydomainfromanotherclient.test");
|
||||
Domain domain15 = persistActiveDomain("mydomain2fromanotherclient.test");
|
||||
|
||||
persistBillingEvent(
|
||||
15,
|
||||
domain14,
|
||||
registrar11,
|
||||
Reason.CREATE,
|
||||
5,
|
||||
Money.ofMajor(JPY, 70),
|
||||
DateTime.parse("2017-10-04T00:00:00.0Z"),
|
||||
DateTime.parse("2017-10-02T00:00:00.0Z"));
|
||||
persistBillingEvent(16, domain15, registrar11, Reason.RENEW, 3, Money.of(USD, 20.5));
|
||||
}
|
||||
|
||||
private static DomainHistory persistDomainHistory(Domain domain, Registrar registrar) {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2025 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.beam.common;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import org.apache.beam.sdk.options.PipelineOptionsFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class RegistryPipelineWorkerInitializerTest {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
RegistryPipelineOptions options =
|
||||
PipelineOptionsFactory.fromArgs(
|
||||
"--registryEnvironment=ALPHA", "--isolationOverride=TRANSACTION_SERIALIZABLE")
|
||||
.withValidation()
|
||||
.as(RegistryPipelineOptions.class);
|
||||
new RegistryPipelineWorkerInitializer().beforeProcessing(options);
|
||||
assertThat(RegistryEnvironment.isOnJetty()).isTrue();
|
||||
System.clearProperty("google.registry.jetty");
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public class FlowModuleTest {
|
||||
|
||||
@Test
|
||||
void givenNonMutatingFlow_thenReplicaTmIsUsed() throws EppException {
|
||||
String eppInputXmlFilename = "domain_info.xml";
|
||||
String eppInputXmlFilename = "domain_check.xml";
|
||||
FlowModule flowModule =
|
||||
new FlowModule.Builder().setEppInput(getEppInput(eppInputXmlFilename)).build();
|
||||
JpaTransactionManager tm =
|
||||
|
||||
@@ -179,7 +179,7 @@ class DomainInfoFlowTest extends ResourceFlowTestCase<DomainInfoFlow, Domain> {
|
||||
ImmutableMap<String, String> substitutions,
|
||||
boolean expectHistoryAndBilling)
|
||||
throws Exception {
|
||||
assertMutatingFlow(false);
|
||||
assertMutatingFlow(true);
|
||||
String expected =
|
||||
loadFile(expectedXmlFilename, updateSubstitutions(substitutions, "ROID", "2FF-TLD"));
|
||||
if (inactive) {
|
||||
|
||||
@@ -36,27 +36,27 @@ public class EppResourceTest extends EntityTestCase {
|
||||
new TestCacheExtension.Builder().withEppResourceCache(Duration.ofDays(1)).build();
|
||||
|
||||
@Test
|
||||
void test_loadCached_ignoresContactChange() {
|
||||
void test_loadByCacheIfEnabled_ignoresContactChange() {
|
||||
Contact originalContact = persistActiveContact("contact123");
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(originalContact.createVKey())))
|
||||
assertThat(EppResource.loadByCacheIfEnabled(ImmutableList.of(originalContact.createVKey())))
|
||||
.containsExactly(originalContact.createVKey(), originalContact);
|
||||
Contact modifiedContact =
|
||||
persistResource(originalContact.asBuilder().setEmailAddress("different@fake.lol").build());
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(originalContact.createVKey())))
|
||||
assertThat(EppResource.loadByCacheIfEnabled(ImmutableList.of(originalContact.createVKey())))
|
||||
.containsExactly(originalContact.createVKey(), originalContact);
|
||||
assertThat(loadByForeignKey(Contact.class, "contact123", fakeClock.nowUtc()))
|
||||
.hasValue(modifiedContact);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_loadCached_ignoresHostChange() {
|
||||
void test_loadByCacheIfEnabled_ignoresHostChange() {
|
||||
Host originalHost = persistActiveHost("ns1.example.com");
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(originalHost.createVKey())))
|
||||
assertThat(EppResource.loadByCacheIfEnabled(ImmutableList.of(originalHost.createVKey())))
|
||||
.containsExactly(originalHost.createVKey(), originalHost);
|
||||
Host modifiedHost =
|
||||
persistResource(
|
||||
originalHost.asBuilder().setLastTransferTime(fakeClock.nowUtc().minusDays(60)).build());
|
||||
assertThat(EppResource.loadCached(ImmutableList.of(originalHost.createVKey())))
|
||||
assertThat(EppResource.loadByCacheIfEnabled(ImmutableList.of(originalHost.createVKey())))
|
||||
.containsExactly(originalHost.createVKey(), originalHost);
|
||||
assertThat(loadByForeignKey(Host.class, "ns1.example.com", fakeClock.nowUtc()))
|
||||
.hasValue(modifiedHost);
|
||||
|
||||
@@ -121,7 +121,7 @@ class ForeignKeyUtilsTest {
|
||||
fakeClock.advanceOneMilli();
|
||||
Host newHost1 = persistActiveHost("ns1.example.com");
|
||||
assertThat(
|
||||
ForeignKeyUtils.loadCached(
|
||||
ForeignKeyUtils.loadByCacheIfEnabled(
|
||||
Host.class,
|
||||
ImmutableList.of("ns1.example.com", "ns2.example.com", "ns3.example.com"),
|
||||
fakeClock.nowUtc()))
|
||||
@@ -134,7 +134,7 @@ class ForeignKeyUtilsTest {
|
||||
Host host2 = persistActiveHost("ns2.example.com");
|
||||
persistResource(host2.asBuilder().setDeletionTime(fakeClock.nowUtc().minusDays(1)).build());
|
||||
assertThat(
|
||||
ForeignKeyUtils.loadCached(
|
||||
ForeignKeyUtils.loadByCacheIfEnabled(
|
||||
Host.class,
|
||||
ImmutableList.of("ns1.example.com", "ns2.example.com", "ns3.example.com"),
|
||||
fakeClock.nowUtc()))
|
||||
@@ -144,7 +144,7 @@ class ForeignKeyUtilsTest {
|
||||
persistActiveHost("ns1.example.com");
|
||||
// Even though a new host1 is now live, the cache still returns the VKey to the old one.
|
||||
assertThat(
|
||||
ForeignKeyUtils.loadCached(
|
||||
ForeignKeyUtils.loadByCacheIfEnabled(
|
||||
Host.class,
|
||||
ImmutableList.of("ns1.example.com", "ns2.example.com", "ns3.example.com"),
|
||||
fakeClock.nowUtc()))
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright 2025 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.model.console;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link PasswordResetRequest}. */
|
||||
public class PasswordResetRequestTest extends EntityTestCase {
|
||||
|
||||
PasswordResetRequestTest() {
|
||||
super(JpaEntityCoverageCheck.ENABLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_persistence() {
|
||||
PasswordResetRequest request =
|
||||
new PasswordResetRequest.Builder()
|
||||
.setRequester("requestor@email.tld")
|
||||
.setDestinationEmail("destination@email.tld")
|
||||
.setType(PasswordResetRequest.Type.EPP)
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.build();
|
||||
String verificationCode = request.getVerificationCode();
|
||||
assertThat(verificationCode).isNotEmpty();
|
||||
persistResource(request);
|
||||
PasswordResetRequest fromDatabase =
|
||||
DatabaseHelper.loadByKey(VKey.create(PasswordResetRequest.class, verificationCode));
|
||||
assertAboutImmutableObjects().that(fromDatabase).isEqualExceptFields(request, "requestTime");
|
||||
assertThat(fromDatabase.getRequestTime()).isEqualTo(fakeClock.nowUtc());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nullFields() {
|
||||
PasswordResetRequest.Builder builder = new PasswordResetRequest.Builder();
|
||||
assertThrows(IllegalArgumentException.class, builder::build);
|
||||
builder.setType(PasswordResetRequest.Type.EPP);
|
||||
assertThrows(IllegalArgumentException.class, builder::build);
|
||||
builder.setRequester("foobar@email.tld");
|
||||
assertThrows(IllegalArgumentException.class, builder::build);
|
||||
builder.setDestinationEmail("email@email.tld");
|
||||
assertThrows(IllegalArgumentException.class, builder::build);
|
||||
builder.setRegistrarId("TheRegistrar");
|
||||
builder.build();
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,7 @@ import google.registry.model.registrar.Registrar.Type;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldType;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.SerializeUtils;
|
||||
import java.math.BigDecimal;
|
||||
@@ -340,6 +341,7 @@ class RegistrarTest extends EntityTestCase {
|
||||
.setFaxNumber("+1.2125551213")
|
||||
.setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH, RegistrarPoc.Type.ABUSE))
|
||||
.build());
|
||||
abuseAdminContact = DatabaseHelper.loadByKey(abuseAdminContact.createVKey());
|
||||
ImmutableSortedSet<RegistrarPoc> techContacts =
|
||||
registrar.getContactsOfType(RegistrarPoc.Type.TECH);
|
||||
assertThat(techContacts).containsExactly(newTechContact, newTechAbuseContact).inOrder();
|
||||
|
||||
@@ -379,6 +379,7 @@ public abstract class JpaTransactionManagerExtension
|
||||
.setPassword("foo-BAR2")
|
||||
.setPhoneNumber("+1.3334445555")
|
||||
.setPhonePasscode("12345")
|
||||
.setRdapBaseUrls(ImmutableSet.of("https://rdap.newregistrar.com/"))
|
||||
.setRegistryLockAllowed(false)
|
||||
.build();
|
||||
}
|
||||
@@ -393,6 +394,7 @@ public abstract class JpaTransactionManagerExtension
|
||||
.setPassword("password2")
|
||||
.setPhoneNumber("+1.2223334444")
|
||||
.setPhonePasscode("22222")
|
||||
.setRdapBaseUrls(ImmutableSet.of("https://rdap.theregistrar.com/"))
|
||||
.setRegistryLockAllowed(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -90,12 +90,6 @@ class RdapActionBaseTest extends RdapActionBaseTestCase<RdapActionBaseTest.RdapT
|
||||
assertThat(response.getStatus()).isEqualTo(500);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidName_works() {
|
||||
assertThat(generateActualJson("no.thing")).isEqualTo(loadJsonFile("rdapjson_toplevel.json"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContentType_rdapjson_utf8() {
|
||||
generateActualJson("no.thing");
|
||||
|
||||
@@ -22,7 +22,12 @@ import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.HEAD;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
@@ -35,6 +40,7 @@ import google.registry.util.Idn;
|
||||
import google.registry.util.TypeUtils;
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
@@ -43,6 +49,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
abstract class RdapActionBaseTestCase<A extends RdapActionBase> {
|
||||
|
||||
protected final FakeClock clock = new FakeClock(DateTime.parse("2000-01-01TZ"));
|
||||
static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
@@ -107,18 +114,13 @@ abstract class RdapActionBaseTestCase<A extends RdapActionBase> {
|
||||
metricRole = ADMINISTRATOR;
|
||||
}
|
||||
|
||||
JsonObject generateActualJson(String domainName) {
|
||||
action.requestPath = actionPath + domainName;
|
||||
action.requestMethod = GET;
|
||||
action.run();
|
||||
return RdapTestHelper.parseJsonObject(response.getPayload());
|
||||
JsonObject generateActualJson(String name) {
|
||||
return RdapTestHelper.parseJsonObject(runAction(name));
|
||||
}
|
||||
|
||||
String generateHeadPayload(String domainName) {
|
||||
action.requestPath = actionPath + domainName;
|
||||
String generateHeadPayload(String name) {
|
||||
action.requestMethod = HEAD;
|
||||
action.run();
|
||||
return response.getPayload();
|
||||
return runAction(name);
|
||||
}
|
||||
|
||||
JsonObject generateExpectedJsonError(String description, int code) {
|
||||
@@ -138,16 +140,125 @@ abstract class RdapActionBaseTestCase<A extends RdapActionBase> {
|
||||
"TITLE",
|
||||
title,
|
||||
"CODE",
|
||||
String.valueOf(code));
|
||||
String.valueOf(code),
|
||||
"REQUEST_URL",
|
||||
action.requestUrl);
|
||||
}
|
||||
|
||||
static JsonFileBuilder jsonFileBuilder() {
|
||||
return new JsonFileBuilder();
|
||||
JsonFileBuilder jsonFileBuilder() {
|
||||
return new JsonFileBuilder(action.requestUrl);
|
||||
}
|
||||
|
||||
private String runAction(String name) {
|
||||
action.requestPath = actionPath + name;
|
||||
action.requestUrl = "https://example.tld" + actionPath + name;
|
||||
action.run();
|
||||
return response.getPayload();
|
||||
}
|
||||
|
||||
JsonElement createTosNotice() {
|
||||
return JsonParser.parseString(
|
||||
"""
|
||||
{
|
||||
"title": "RDAP Terms of Service",
|
||||
"description": [
|
||||
"By querying our Domain Database, you are agreeing to comply with these terms so please read \
|
||||
them carefully.",
|
||||
"Any information provided is 'as is' without any guarantee of accuracy.",
|
||||
"Please do not misuse the Domain Database. It is intended solely for query-based access.",
|
||||
"Don't use the Domain Database to allow, enable, or otherwise support the transmission of mass \
|
||||
unsolicited, commercial advertising or solicitations.",
|
||||
"Don't access our Domain Database through the use of high volume, automated electronic \
|
||||
processes that send queries or data to the systems of any ICANN-accredited registrar.",
|
||||
"You may only use the information contained in the Domain Database for lawful purposes.",
|
||||
"Do not compile, repackage, disseminate, or otherwise use the information contained in the \
|
||||
Domain Database in its entirety, or in any substantial portion, without our prior written \
|
||||
permission.",
|
||||
"We may retain certain details about queries to our Domain Database for the purposes of \
|
||||
detecting and preventing misuse.",
|
||||
"We reserve the right to restrict or deny your access to the database if we suspect that you \
|
||||
have failed to comply with these terms.",
|
||||
"We reserve the right to modify this agreement at any time."
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "https://example.tld/rdap/help/tos",
|
||||
"type": "application/rdap+json",
|
||||
"value": "%REQUEST_URL%"
|
||||
},
|
||||
{
|
||||
"rel": "terms-of-service",
|
||||
"href": "https://www.example.tld/about/rdap/tos.html",
|
||||
"type": "text/html",
|
||||
"value": "%REQUEST_URL%"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
.replaceAll("%REQUEST_URL%", action.requestUrl));
|
||||
}
|
||||
|
||||
JsonObject addPermanentBoilerplateNotices(JsonObject jsonObject) {
|
||||
if (!jsonObject.has("notices")) {
|
||||
jsonObject.add("notices", new JsonArray());
|
||||
}
|
||||
JsonArray notices = jsonObject.getAsJsonArray("notices");
|
||||
notices.add(createTosNotice());
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
JsonObject addDomainBoilerplateNotices(JsonObject jsonObject) {
|
||||
addPermanentBoilerplateNotices(jsonObject);
|
||||
JsonArray notices = jsonObject.getAsJsonArray("notices");
|
||||
notices.add(
|
||||
JsonParser.parseString(
|
||||
"""
|
||||
{
|
||||
"title": "Status Codes",
|
||||
"description": [
|
||||
"For more information on domain status codes, please visit https://icann.org/epp"
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"rel": "glossary",
|
||||
"href": "https://icann.org/epp",
|
||||
"type": "text/html",
|
||||
"value": "%REQUEST_URL%"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
.replaceAll("%REQUEST_URL%", action.requestUrl)));
|
||||
notices.add(
|
||||
JsonParser.parseString(
|
||||
"""
|
||||
{
|
||||
"title": "RDDS Inaccuracy Complaint Form",
|
||||
"description": [
|
||||
"URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"rel": "help",
|
||||
"href": "https://icann.org/wicf",
|
||||
"type": "text/html",
|
||||
"value": "%REQUEST_URL%"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
.replaceAll("%REQUEST_URL%", action.requestUrl)));
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
protected static final class JsonFileBuilder {
|
||||
private final HashMap<String, String> substitutions = new HashMap<>();
|
||||
|
||||
private JsonFileBuilder(String requestUrl) {
|
||||
substitutions.put("REQUEST_URL", requestUrl);
|
||||
}
|
||||
|
||||
public JsonObject load(String filename) {
|
||||
return RdapTestHelper.loadJsonFile(filename, substitutions);
|
||||
}
|
||||
@@ -158,6 +269,14 @@ abstract class RdapActionBaseTestCase<A extends RdapActionBase> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public JsonFileBuilder putAll(String... keysAndValues) {
|
||||
checkArgument(keysAndValues.length % 2 == 0);
|
||||
for (int i = 0; i < keysAndValues.length; i += 2) {
|
||||
put(keysAndValues[i], keysAndValues[i + 1]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public JsonFileBuilder put(String key, int index, String value) {
|
||||
return put(String.format("%s%d", key, index), value);
|
||||
}
|
||||
@@ -189,10 +308,38 @@ abstract class RdapActionBaseTestCase<A extends RdapActionBase> {
|
||||
return putNext("REGISTRAR_FULL_NAME_", fullName);
|
||||
}
|
||||
|
||||
JsonFileBuilder addFullRegistrar(
|
||||
String handle, @Nullable String fullName, String status, @Nullable String address) {
|
||||
if (fullName != null) {
|
||||
putNext("REGISTRAR_FULLNAME_", fullName);
|
||||
}
|
||||
if (address != null) {
|
||||
putNext("REGISTRAR_ADDRESS_", address);
|
||||
}
|
||||
return putNext("REGISTRAR_HANDLE_", handle, "STATUS_", status);
|
||||
}
|
||||
|
||||
JsonFileBuilder addContact(String handle) {
|
||||
return putNext("CONTACT_HANDLE_", handle);
|
||||
}
|
||||
|
||||
JsonFileBuilder addFullContact(
|
||||
String handle,
|
||||
@Nullable String status,
|
||||
@Nullable String fullName,
|
||||
@Nullable String address) {
|
||||
if (fullName != null) {
|
||||
putNext("CONTACT_FULLNAME_", fullName);
|
||||
}
|
||||
if (address != null) {
|
||||
putNext("CONTACT_ADDRESS_", address);
|
||||
}
|
||||
if (status != null) {
|
||||
putNext("STATUS_", status);
|
||||
}
|
||||
return putNext("CONTACT_HANDLE_", handle);
|
||||
}
|
||||
|
||||
JsonFileBuilder setNextQuery(String nextQuery) {
|
||||
return put("NEXT_QUERY", nextQuery);
|
||||
}
|
||||
|
||||
@@ -43,12 +43,13 @@ final class RdapDataStructuresTest {
|
||||
@Test
|
||||
void testRdapConformance() {
|
||||
assertThat(RdapConformance.INSTANCE.toJson())
|
||||
.isEqualTo(createJson(
|
||||
"[",
|
||||
" 'rdap_level_0',",
|
||||
" 'icann_rdap_response_profile_0',",
|
||||
" 'icann_rdap_technical_implementation_guide_0'",
|
||||
"]"));
|
||||
.isEqualTo(
|
||||
createJson(
|
||||
"[",
|
||||
" 'rdap_level_0',",
|
||||
" 'icann_rdap_response_profile_1',",
|
||||
" 'icann_rdap_technical_implementation_guide_1'",
|
||||
"]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -59,9 +60,12 @@ final class RdapDataStructuresTest {
|
||||
.setRel("myRel")
|
||||
.setTitle("myTitle")
|
||||
.setType("myType")
|
||||
.setValue("myValue")
|
||||
.build();
|
||||
assertThat(link.toJson())
|
||||
.isEqualTo(createJson("{'href':'myHref','rel':'myRel','title':'myTitle','type':'myType'}"));
|
||||
.isEqualTo(
|
||||
createJson(
|
||||
"{'href':'myHref','rel':'myRel','title':'myTitle','type':'myType','value':'myValue'}"));
|
||||
assertRestrictedNames(link, "links[]");
|
||||
}
|
||||
|
||||
|
||||
@@ -230,16 +230,11 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
clock.nowUtc().minusMonths(6)));
|
||||
}
|
||||
|
||||
private JsonObject addBoilerplate(JsonObject obj) {
|
||||
RdapTestHelper.addDomainBoilerplateNotices(obj, "https://example.tld/rdap/");
|
||||
return obj;
|
||||
}
|
||||
|
||||
private void assertProperResponseForCatLol(String queryString, String expectedOutputFile) {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(queryString))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addDomain("cat.lol", "C-LOL")
|
||||
.addContact("4-ROID")
|
||||
@@ -357,7 +352,7 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("cat.みんな"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addDomain("cat.みんな", "1D-Q9JYB4C")
|
||||
.addContact("19-ROID")
|
||||
@@ -376,7 +371,7 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("cat.%E3%81%BF%E3%82%93%E3%81%AA"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addDomain("cat.みんな", "1D-Q9JYB4C")
|
||||
.addContact("19-ROID")
|
||||
@@ -395,7 +390,7 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("cat.xn--q9jyb4c"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addDomain("cat.みんな", "1D-Q9JYB4C")
|
||||
.addContact("19-ROID")
|
||||
@@ -414,7 +409,7 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("cat.1.tld"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addDomain("cat.1.tld", "25-1_TLD")
|
||||
.addContact("21-ROID")
|
||||
@@ -473,7 +468,7 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("dodo.lol"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addDomain("dodo.lol", "15-LOL")
|
||||
.addContact("11-ROID")
|
||||
@@ -493,7 +488,7 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("dodo.lol"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addDomain("dodo.lol", "15-LOL")
|
||||
.addContact("11-ROID")
|
||||
@@ -512,7 +507,9 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
"addgraceperiod", "lol", clock.nowUtc(), clock.nowUtc().plusYears(1));
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("addgraceperiod.lol"))
|
||||
.isEqualTo(addBoilerplate(jsonFileBuilder().load("rdap_domain_add_grace_period.json")));
|
||||
.isEqualTo(
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder().load("rdap_domain_add_grace_period.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -522,7 +519,8 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("autorenew.lol"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(jsonFileBuilder().load("rdap_domain_auto_renew_grace_period.json")));
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder().load("rdap_domain_auto_renew_grace_period.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -545,7 +543,7 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("redemption.lol"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder().load("rdap_domain_pending_delete_redemption_grace_period.json")));
|
||||
}
|
||||
|
||||
@@ -568,7 +566,8 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("renew.lol"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(jsonFileBuilder().load("rdap_domain_explicit_renew_grace_period.json")));
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder().load("rdap_domain_explicit_renew_grace_period.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -590,7 +589,8 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("transfer.lol"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(jsonFileBuilder().load("rdap_domain_transfer_grace_period.json")));
|
||||
addDomainBoilerplateNotices(
|
||||
jsonFileBuilder().load("rdap_domain_transfer_grace_period.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -631,12 +631,15 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
"rel",
|
||||
"alternate",
|
||||
"type",
|
||||
"text/html")));
|
||||
"text/html",
|
||||
"value",
|
||||
"https://example.tld/rdap/domain/example.lol")));
|
||||
JsonObject actuaResponse = generateActualJson("example.lol");
|
||||
JsonObject expectedErrorResponse = generateExpectedJsonError("example.lol blocked by BSA", 404);
|
||||
expectedErrorResponse
|
||||
.getAsJsonArray("notices")
|
||||
.add(RdapTestHelper.GSON.toJsonTree(expectedBsaNotice));
|
||||
assertAboutJson().that(generateActualJson("example.lol")).isEqualTo(expectedErrorResponse);
|
||||
assertAboutJson().that(actuaResponse).isEqualTo(expectedErrorResponse);
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
|
||||
@@ -473,8 +473,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
|
||||
|
||||
private JsonObject wrapInSearchReply(JsonObject obj) {
|
||||
obj = RdapTestHelper.wrapInSearchReply("domainSearchResults", obj);
|
||||
RdapTestHelper.addDomainBoilerplateNotices(obj, "https://example.tld/rdap/");
|
||||
return obj;
|
||||
return addDomainBoilerplateNotices(obj);
|
||||
}
|
||||
|
||||
private void runSuccessfulTest(RequestType requestType, String queryString, JsonObject expected) {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.rdap;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.rdap.RdapTestHelper.loadJsonFile;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
|
||||
@@ -28,7 +27,6 @@ import static google.registry.testing.GsonSubject.assertAboutJson;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.JsonObject;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
@@ -39,13 +37,15 @@ import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.testing.FullFieldsTestEntityHelper;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link RdapEntityAction}. */
|
||||
class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
|
||||
|
||||
private static final String CONTACT_NAME = "(◕‿◕)";
|
||||
private static final String CONTACT_ADDRESS = "\"1 Smiley Row\", \"Suite みんな\"";
|
||||
|
||||
RdapEntityActionTest() {
|
||||
super(RdapEntityAction.class);
|
||||
}
|
||||
@@ -67,7 +67,7 @@ class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
|
||||
registrant =
|
||||
FullFieldsTestEntityHelper.makeAndPersistContact(
|
||||
"8372808-REG",
|
||||
"(◕‿◕)",
|
||||
CONTACT_NAME,
|
||||
"lol@cat.みんな",
|
||||
ImmutableList.of("1 Smiley Row", "Suite みんな"),
|
||||
clock.nowUtc(),
|
||||
@@ -75,7 +75,7 @@ class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
|
||||
adminContact =
|
||||
FullFieldsTestEntityHelper.makeAndPersistContact(
|
||||
"8372808-ADM",
|
||||
"(◕‿◕)",
|
||||
CONTACT_NAME,
|
||||
"lol@cat.みんな",
|
||||
ImmutableList.of("1 Smiley Row", "Suite みんな"),
|
||||
clock.nowUtc(),
|
||||
@@ -83,7 +83,7 @@ class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
|
||||
techContact =
|
||||
FullFieldsTestEntityHelper.makeAndPersistContact(
|
||||
"8372808-TEC",
|
||||
"(◕‿◕)",
|
||||
CONTACT_NAME,
|
||||
"lol@cat.みんな",
|
||||
ImmutableList.of("1 Smiley Row", "Suite みんな"),
|
||||
clock.nowUtc(),
|
||||
@@ -110,7 +110,7 @@ class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
|
||||
disconnectedContact =
|
||||
FullFieldsTestEntityHelper.makeAndPersistContact(
|
||||
"8372808-DIS",
|
||||
"(◕‿◕)",
|
||||
CONTACT_NAME,
|
||||
"lol@cat.みんな",
|
||||
ImmutableList.of("1 Smiley Row", "Suite みんな"),
|
||||
clock.nowUtc(),
|
||||
@@ -123,186 +123,191 @@ class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
|
||||
clock.nowUtc().minusMonths(6));
|
||||
}
|
||||
|
||||
private JsonObject generateExpectedJson(
|
||||
String handle,
|
||||
String fullName,
|
||||
String status,
|
||||
@Nullable String address,
|
||||
String expectedOutputFile) {
|
||||
return loadJsonFile(
|
||||
expectedOutputFile,
|
||||
"NAME", handle,
|
||||
"FULLNAME", fullName,
|
||||
"ADDRESS", (address == null) ? "\"1 Smiley Row\", \"Suite みんな\"" : address,
|
||||
"TYPE", "entity",
|
||||
"STATUS", status);
|
||||
}
|
||||
|
||||
private JsonObject generateExpectedJsonWithTopLevelEntries(
|
||||
String handle,
|
||||
String expectedOutputFile) {
|
||||
return generateExpectedJsonWithTopLevelEntries(
|
||||
handle, "(◕‿◕)", "active", null, expectedOutputFile);
|
||||
}
|
||||
|
||||
private JsonObject generateExpectedJsonWithTopLevelEntries(
|
||||
String handle,
|
||||
String fullName,
|
||||
String status,
|
||||
String address,
|
||||
String expectedOutputFile) {
|
||||
JsonObject obj = generateExpectedJson(handle, fullName, status, address, expectedOutputFile);
|
||||
RdapTestHelper.addNonDomainBoilerplateNotices(obj, "https://example.tld/rdap/");
|
||||
return obj;
|
||||
}
|
||||
|
||||
private void runSuccessfulHandleTest(String handleQuery, String fileName) {
|
||||
runSuccessfulHandleTest(handleQuery, "(◕‿◕)", "active", null, fileName);
|
||||
}
|
||||
|
||||
private void runSuccessfulHandleTest(String handleQuery, String fullName, String fileName) {
|
||||
runSuccessfulHandleTest(handleQuery, fullName, "active", null, fileName);
|
||||
}
|
||||
|
||||
private void runSuccessfulHandleTest(
|
||||
String handleQuery,
|
||||
String fullName,
|
||||
String rdapStatus,
|
||||
String address,
|
||||
String fileName) {
|
||||
@Test
|
||||
void testUnknownEntity_RoidPattern_notFound() {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(handleQuery))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
handleQuery, fullName, rdapStatus, address, fileName));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
private void runNotFoundTest(String handleQuery) {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(handleQuery))
|
||||
.isEqualTo(generateExpectedJsonError(handleQuery + " not found", 404));
|
||||
.that(generateActualJson("_MISSING-ENTITY_"))
|
||||
.isEqualTo(generateExpectedJsonError("_MISSING-ENTITY_ not found", 404));
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnknownEntity_RoidPattern_notFound() {
|
||||
runNotFoundTest("_MISSING-ENTITY_");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnknownEntity_IanaPattern_notFound() {
|
||||
runNotFoundTest("123");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("123"))
|
||||
.isEqualTo(generateExpectedJsonError("123 not found", 404));
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnknownEntity_notRoidNotIana_notFound() {
|
||||
// Since we allow search by registrar name, every string is a possible name
|
||||
runNotFoundTest("some,random,string");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("some,random,string"))
|
||||
.isEqualTo(generateExpectedJsonError("some,random,string not found", 404));
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidRegistrantContact_works() {
|
||||
login("evilregistrar");
|
||||
runSuccessfulHandleTest(registrant.getRepoId(), "rdap_associated_contact.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(registrant.getRepoId()))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullContact(registrant.getRepoId(), null, CONTACT_NAME, CONTACT_ADDRESS)
|
||||
.load("rdap_associated_contact.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidRegistrantContact_found_asAdministrator() {
|
||||
loginAsAdmin();
|
||||
runSuccessfulHandleTest(registrant.getRepoId(), "rdap_associated_contact.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(registrant.getRepoId()))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullContact(registrant.getRepoId(), null, CONTACT_NAME, CONTACT_ADDRESS)
|
||||
.load("rdap_associated_contact.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidRegistrantContact_found_notLoggedIn() {
|
||||
runSuccessfulHandleTest(
|
||||
registrant.getRepoId(),
|
||||
"(◕‿◕)",
|
||||
"active",
|
||||
null,
|
||||
"rdap_associated_contact_no_personal_data.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(registrant.getRepoId()))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullContact(registrant.getRepoId(), "active", CONTACT_NAME, CONTACT_ADDRESS)
|
||||
.load("rdap_associated_contact_no_personal_data.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidRegistrantContact_found_loggedInAsOtherRegistrar() {
|
||||
login("otherregistrar");
|
||||
runSuccessfulHandleTest(
|
||||
registrant.getRepoId(),
|
||||
"(◕‿◕)",
|
||||
"active",
|
||||
null,
|
||||
"rdap_associated_contact_no_personal_data.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(registrant.getRepoId()))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullContact(registrant.getRepoId(), "active", CONTACT_NAME, CONTACT_ADDRESS)
|
||||
.load("rdap_associated_contact_no_personal_data.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidAdminContact_works() {
|
||||
login("evilregistrar");
|
||||
runSuccessfulHandleTest(adminContact.getRepoId(), "rdap_associated_contact.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(adminContact.getRepoId()))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullContact(adminContact.getRepoId(), null, CONTACT_NAME, CONTACT_ADDRESS)
|
||||
.load("rdap_associated_contact.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidTechContact_works() {
|
||||
login("evilregistrar");
|
||||
runSuccessfulHandleTest(techContact.getRepoId(), "rdap_associated_contact.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(techContact.getRepoId()))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullContact(techContact.getRepoId(), null, CONTACT_NAME, CONTACT_ADDRESS)
|
||||
.load("rdap_associated_contact.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDisconnectedContact_works() {
|
||||
login("evilregistrar");
|
||||
runSuccessfulHandleTest(disconnectedContact.getRepoId(), "rdap_contact.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(disconnectedContact.getRepoId()))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullContact(
|
||||
disconnectedContact.getRepoId(), "active", CONTACT_NAME, CONTACT_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeletedContact_notFound() {
|
||||
runNotFoundTest(deletedContact.getRepoId());
|
||||
String repoId = deletedContact.getRepoId();
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(repoId))
|
||||
.isEqualTo(generateExpectedJsonError(repoId + " not found", 404));
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeletedContact_notFound_includeDeletedSetFalse() {
|
||||
action.includeDeletedParam = Optional.of(false);
|
||||
runNotFoundTest(deletedContact.getRepoId());
|
||||
String repoId = deletedContact.getRepoId();
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(repoId))
|
||||
.isEqualTo(generateExpectedJsonError(repoId + " not found", 404));
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeletedContact_notFound_notLoggedIn() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runNotFoundTest(deletedContact.getRepoId());
|
||||
String repoId = deletedContact.getRepoId();
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(repoId))
|
||||
.isEqualTo(generateExpectedJsonError(repoId + " not found", 404));
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeletedContact_notFound_loggedInAsDifferentRegistrar() {
|
||||
login("idnregistrar");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runNotFoundTest(deletedContact.getRepoId());
|
||||
String repoId = deletedContact.getRepoId();
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(repoId))
|
||||
.isEqualTo(generateExpectedJsonError(repoId + " not found", 404));
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeletedContact_found_loggedInAsCorrectRegistrar() {
|
||||
login("evilregistrar");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runSuccessfulHandleTest(
|
||||
deletedContact.getRepoId(),
|
||||
"",
|
||||
"inactive",
|
||||
"",
|
||||
"rdap_contact_deleted.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(deletedContact.getRepoId()))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addContact(deletedContact.getRepoId())
|
||||
.load("rdap_contact_deleted.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDeletedContact_found_loggedInAsAdmin() {
|
||||
loginAsAdmin();
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runSuccessfulHandleTest(
|
||||
deletedContact.getRepoId(),
|
||||
"",
|
||||
"inactive",
|
||||
"",
|
||||
"rdap_contact_deleted.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(deletedContact.getRepoId()))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addContact(deletedContact.getRepoId())
|
||||
.load("rdap_contact_deleted.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrar_found() {
|
||||
runSuccessfulHandleTest("101", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("101"))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("101", "Yes Virginia <script>", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -310,58 +315,97 @@ class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("IDN%20Registrar"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
"102", "IDN Registrar", "active", null, "rdap_registrar.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("102", "IDN Registrar", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrar102_works() {
|
||||
runSuccessfulHandleTest("102", "IDN Registrar", "rdap_registrar.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("102"))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("102", "IDN Registrar", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrar103_works() {
|
||||
runSuccessfulHandleTest("103", "Multilevel Registrar", "rdap_registrar.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("103"))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("103", "Multilevel Registrar", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrar104_notFound() {
|
||||
runNotFoundTest("104");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("104"))
|
||||
.isEqualTo(generateExpectedJsonError("104 not found", 404));
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrar104_notFound_deletedFlagWhenNotLoggedIn() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runNotFoundTest("104");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("104"))
|
||||
.isEqualTo(generateExpectedJsonError("104 not found", 404));
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrar104_found_deletedFlagWhenLoggedIn() {
|
||||
login("deletedregistrar");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runSuccessfulHandleTest(
|
||||
"104", "Yes Virginia <script>", "inactive", null, "rdap_registrar.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("104"))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("104", "Yes Virginia <script>", "inactive", null)
|
||||
.load("rdap_registrar.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrar104_notFound_deletedFlagWhenLoggedInAsOther() {
|
||||
login("1tldregistrar");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runNotFoundTest("104");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("104"))
|
||||
.isEqualTo(generateExpectedJsonError("104 not found", 404));
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrar104_found_deletedFlagWhenLoggedInAsAdmin() {
|
||||
loginAsAdmin();
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runSuccessfulHandleTest(
|
||||
"104", "Yes Virginia <script>", "inactive", null, "rdap_registrar.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("104"))
|
||||
.isEqualTo(
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("104", "Yes Virginia <script>", "inactive", null)
|
||||
.load("rdap_registrar.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRegistrar105_doesNotExist() {
|
||||
runNotFoundTest("105");
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("105"))
|
||||
.isEqualTo(generateExpectedJsonError("105 not found", 404));
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -370,8 +414,10 @@ class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
|
||||
assertAboutJson()
|
||||
.that(generateActualJson(techContact.getRepoId() + "?key=value"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
techContact.getRepoId(), "rdap_associated_contact.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addFullContact(techContact.getRepoId(), null, CONTACT_NAME, CONTACT_ADDRESS)
|
||||
.load("rdap_associated_contact.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.rdap;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.rdap.RdapTestHelper.loadJsonFile;
|
||||
import static google.registry.rdap.RdapTestHelper.parseJsonObject;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
@@ -30,7 +29,6 @@ import static google.registry.testing.GsonSubject.assertAboutJson;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
@@ -45,13 +43,15 @@ import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.FullFieldsTestEntityHelper;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link RdapEntitySearchAction}. */
|
||||
class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySearchAction> {
|
||||
|
||||
private static final String BINKY_ADDRESS = "\"123 Blinky St\", \"Blinkyland\"";
|
||||
private static final String BINKY_FULL_NAME = "Blinky (赤ベイ)";
|
||||
|
||||
RdapEntitySearchActionTest() {
|
||||
super(RdapEntitySearchAction.class);
|
||||
}
|
||||
@@ -128,7 +128,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
|
||||
FullFieldsTestEntityHelper.makeAndPersistContact(
|
||||
"blinky",
|
||||
"Blinky (赤ベイ)",
|
||||
BINKY_FULL_NAME,
|
||||
"blinky@b.tld",
|
||||
ImmutableList.of("123 Blinky St", "Blinkyland"),
|
||||
clock.nowUtc(),
|
||||
@@ -150,45 +150,9 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
action.subtypeParam = Optional.empty();
|
||||
}
|
||||
|
||||
private JsonObject generateExpectedJson(String expectedOutputFile) {
|
||||
return loadJsonFile(expectedOutputFile, "TYPE", "entity");
|
||||
}
|
||||
|
||||
private JsonObject generateExpectedJson(
|
||||
String handle,
|
||||
String expectedOutputFile) {
|
||||
return generateExpectedJson(handle, null, "active", null, expectedOutputFile);
|
||||
}
|
||||
|
||||
private JsonObject generateExpectedJson(
|
||||
String handle,
|
||||
@Nullable String fullName,
|
||||
String status,
|
||||
@Nullable String address,
|
||||
String expectedOutputFile) {
|
||||
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
|
||||
builder.put("NAME", handle);
|
||||
if (fullName != null) {
|
||||
builder.put("FULLNAME", fullName);
|
||||
}
|
||||
if (address != null) {
|
||||
builder.put("ADDRESS", address);
|
||||
}
|
||||
builder.put("TYPE", "entity");
|
||||
builder.put("STATUS", status);
|
||||
return loadJsonFile(expectedOutputFile, builder.build());
|
||||
}
|
||||
|
||||
private JsonObject generateExpectedJsonForEntity(
|
||||
String handle,
|
||||
String fullName,
|
||||
String status,
|
||||
@Nullable String address,
|
||||
String expectedOutputFile) {
|
||||
JsonObject obj = generateExpectedJson(handle, fullName, status, address, expectedOutputFile);
|
||||
obj = RdapTestHelper.wrapInSearchReply("entitySearchResults", obj);
|
||||
RdapTestHelper.addNonDomainBoilerplateNotices(obj, "https://example.tld/rdap/");
|
||||
return obj;
|
||||
private JsonObject addBoilerplate(JsonObject jsonObject) {
|
||||
jsonObject = RdapTestHelper.wrapInSearchReply("entitySearchResults", jsonObject);
|
||||
return addPermanentBoilerplateNotices(jsonObject);
|
||||
}
|
||||
|
||||
private void createManyContactsAndRegistrars(
|
||||
@@ -259,38 +223,6 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
verifyMetrics(numContactsRetrieved);
|
||||
}
|
||||
|
||||
private void runSuccessfulNameTestWithBlinky(String queryString, String fileName) {
|
||||
runSuccessfulNameTest(
|
||||
queryString,
|
||||
"2-ROID",
|
||||
"Blinky (赤ベイ)",
|
||||
"active",
|
||||
"\"123 Blinky St\", \"Blinkyland\"",
|
||||
fileName);
|
||||
}
|
||||
|
||||
private void runSuccessfulNameTest(
|
||||
String queryString,
|
||||
String handle,
|
||||
@Nullable String fullName,
|
||||
String fileName) {
|
||||
runSuccessfulNameTest(queryString, handle, fullName, "active", null, fileName);
|
||||
}
|
||||
|
||||
private void runSuccessfulNameTest(
|
||||
String queryString,
|
||||
String handle,
|
||||
@Nullable String fullName,
|
||||
String status,
|
||||
@Nullable String address,
|
||||
String fileName) {
|
||||
rememberWildcardType(queryString);
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName(queryString))
|
||||
.isEqualTo(generateExpectedJsonForEntity(handle, fullName, status, address, fileName));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
private void runNotFoundNameTest(String queryString) {
|
||||
rememberWildcardType(queryString);
|
||||
assertAboutJson()
|
||||
@@ -299,38 +231,6 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
private void runSuccessfulHandleTestWithBlinky(String queryString, String fileName) {
|
||||
runSuccessfulHandleTest(
|
||||
queryString,
|
||||
"2-ROID",
|
||||
"Blinky (赤ベイ)",
|
||||
"active",
|
||||
"\"123 Blinky St\", \"Blinkyland\"",
|
||||
fileName);
|
||||
}
|
||||
|
||||
private void runSuccessfulHandleTest(
|
||||
String queryString,
|
||||
String handle,
|
||||
@Nullable String fullName,
|
||||
String fileName) {
|
||||
runSuccessfulHandleTest(queryString, handle, fullName, "active", null, fileName);
|
||||
}
|
||||
|
||||
private void runSuccessfulHandleTest(
|
||||
String queryString,
|
||||
String handle,
|
||||
@Nullable String fullName,
|
||||
String status,
|
||||
@Nullable String address,
|
||||
String fileName) {
|
||||
rememberWildcardType(queryString);
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle(queryString))
|
||||
.isEqualTo(generateExpectedJsonForEntity(handle, fullName, status, address, fileName));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
private void runNotFoundHandleTest(String queryString) {
|
||||
rememberWildcardType(queryString);
|
||||
assertAboutJson()
|
||||
@@ -470,7 +370,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testInvalidSubtype_rejected() {
|
||||
action.subtypeParam = Optional.of("Space Aliens");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Blinky (赤ベイ)"))
|
||||
.that(generateActualJsonWithFullName(BINKY_FULL_NAME))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonError(
|
||||
"Subtype parameter must specify contacts, registrars or all", 400));
|
||||
@@ -482,23 +382,44 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
@Test
|
||||
void testNameMatchContact_found() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulNameTestWithBlinky("Blinky (赤ベイ)", "rdap_contact.json");
|
||||
rememberWildcardType(BINKY_FULL_NAME);
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName(BINKY_FULL_NAME))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNameMatchContact_found_subtypeAll() {
|
||||
login("2-RegistrarTest");
|
||||
rememberWildcardType(BINKY_FULL_NAME);
|
||||
action.subtypeParam = Optional.of("aLl");
|
||||
runSuccessfulNameTestWithBlinky("Blinky (赤ベイ)", "rdap_contact.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName(BINKY_FULL_NAME))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNameMatchContact_found_subtypeContacts() {
|
||||
login("2-RegistrarTest");
|
||||
rememberWildcardType(BINKY_FULL_NAME);
|
||||
action.subtypeParam = Optional.of("cONTACTS");
|
||||
runSuccessfulNameTestWithBlinky("Blinky (赤ベイ)", "rdap_contact.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName(BINKY_FULL_NAME))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -506,15 +427,22 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testNameMatchContact_notFound_subtypeRegistrars() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("Registrars");
|
||||
runNotFoundNameTest("Blinky (赤ベイ)");
|
||||
runNotFoundNameTest(BINKY_FULL_NAME);
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNameMatchContact_found_specifyingSameRegistrar() {
|
||||
login("2-RegistrarTest");
|
||||
rememberWildcardType(BINKY_FULL_NAME);
|
||||
action.registrarParam = Optional.of("2-RegistrarTest");
|
||||
runSuccessfulNameTestWithBlinky("Blinky (赤ベイ)", "rdap_contact.json");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName(BINKY_FULL_NAME))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -522,35 +450,48 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testNameMatchContact_notFound_specifyingOtherRegistrar() {
|
||||
login("2-RegistrarTest");
|
||||
action.registrarParam = Optional.of("2-RegistrarInact");
|
||||
runNotFoundNameTest("Blinky (赤ベイ)");
|
||||
runNotFoundNameTest(BINKY_FULL_NAME);
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNameMatchContact_found_asAdministrator() {
|
||||
loginAsAdmin();
|
||||
rememberWildcardType("Blinky (赤ベイ)");
|
||||
runSuccessfulNameTestWithBlinky("Blinky (赤ベイ)", "rdap_contact.json");
|
||||
rememberWildcardType(BINKY_FULL_NAME);
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName(BINKY_FULL_NAME))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNameMatchContact_notFound_notLoggedIn() {
|
||||
runNotFoundNameTest("Blinky (赤ベイ)");
|
||||
runNotFoundNameTest(BINKY_FULL_NAME);
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNameMatchContact_notFound_loggedInAsOtherRegistrar() {
|
||||
login("2-Registrar");
|
||||
runNotFoundNameTest("Blinky (赤ベイ)");
|
||||
runNotFoundNameTest(BINKY_FULL_NAME);
|
||||
verifyErrorMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNameMatchContact_found_wildcard() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulNameTestWithBlinky("Blinky*", "rdap_contact.json");
|
||||
rememberWildcardType("Blinky*");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Blinky*"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -558,7 +499,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testNameMatchContact_found_wildcardSpecifyingSameRegistrar() {
|
||||
login("2-RegistrarTest");
|
||||
action.registrarParam = Optional.of("2-RegistrarTest");
|
||||
runSuccessfulNameTestWithBlinky("Blinky*", "rdap_contact.json");
|
||||
rememberWildcardType("Blinky*");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Blinky*"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -576,7 +524,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
rememberWildcardType("Blin*");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Blin*"))
|
||||
.isEqualTo(generateExpectedJson("rdap_multiple_contacts2.json"));
|
||||
.isEqualTo(jsonFileBuilder().load("rdap_multiple_contacts2.json"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(2);
|
||||
}
|
||||
@@ -615,8 +563,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
@Test
|
||||
void testNameMatchRegistrar_found() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulNameTest(
|
||||
"Yes Virginia <script>", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
rememberWildcardType("Yes Virginia <script>");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Yes Virginia <script>"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("20", "Yes Virginia <script>", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@@ -624,8 +578,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testNameMatchRegistrar_found_subtypeAll() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("all");
|
||||
runSuccessfulNameTest(
|
||||
"Yes Virginia <script>", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
rememberWildcardType("Yes Virginia <script>");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Yes Virginia <script>"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("20", "Yes Virginia <script>", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@@ -633,8 +593,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testNameMatchRegistrar_found_subtypeRegistrars() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("REGISTRARS");
|
||||
runSuccessfulNameTest(
|
||||
"Yes Virginia <script>", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
rememberWildcardType("Yes Virginia <script>");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Yes Virginia <script>"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("20", "Yes Virginia <script>", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@@ -649,8 +615,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
@Test
|
||||
void testNameMatchRegistrar_found_specifyingSameRegistrar() {
|
||||
action.registrarParam = Optional.of("2-Registrar");
|
||||
runSuccessfulNameTest(
|
||||
"Yes Virginia <script>", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
rememberWildcardType("Yes Virginia <script>");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Yes Virginia <script>"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("20", "Yes Virginia <script>", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@@ -666,10 +638,9 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
login("2-RegistrarTest");
|
||||
createManyContactsAndRegistrars(4, 0, registrarTest);
|
||||
rememberWildcardType("Entity *");
|
||||
// JsonObject foo = generateActualJsonWithFullName("Entity *");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Entity *"))
|
||||
.isEqualTo(generateExpectedJson("rdap_nontruncated_contacts.json"));
|
||||
.isEqualTo(jsonFileBuilder().load("rdap_nontruncated_contacts.json"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(4);
|
||||
}
|
||||
@@ -682,8 +653,9 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Entity *"))
|
||||
.isEqualTo(
|
||||
generateExpectedJson(
|
||||
"fn=Entity+*&cursor=YzpFbnRpdHkgNA%3D%3D", "rdap_truncated_contacts.json"));
|
||||
jsonFileBuilder()
|
||||
.put("NAME", "fn=Entity+*&cursor=YzpFbnRpdHkgNA%3D%3D")
|
||||
.load("rdap_truncated_contacts.json"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(5, IncompletenessWarningType.TRUNCATED);
|
||||
}
|
||||
@@ -696,8 +668,9 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Entity *"))
|
||||
.isEqualTo(
|
||||
generateExpectedJson(
|
||||
"fn=Entity+*&cursor=YzpFbnRpdHkgNA%3D%3D", "rdap_truncated_contacts.json"));
|
||||
jsonFileBuilder()
|
||||
.put("NAME", "fn=Entity+*&cursor=YzpFbnRpdHkgNA%3D%3D")
|
||||
.load("rdap_truncated_contacts.json"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
// For contacts, we only need to fetch one result set's worth (plus one).
|
||||
verifyMetrics(5, IncompletenessWarningType.TRUNCATED);
|
||||
@@ -728,7 +701,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
rememberWildcardType("Entity *");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Entity *"))
|
||||
.isEqualTo(generateExpectedJson("rdap_nontruncated_registrars.json"));
|
||||
.isEqualTo(jsonFileBuilder().load("rdap_nontruncated_registrars.json"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(0);
|
||||
}
|
||||
@@ -740,8 +713,9 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Entity *"))
|
||||
.isEqualTo(
|
||||
generateExpectedJson(
|
||||
"fn=Entity+*&cursor=cjpFbnRpdHkgNA%3D%3D", "rdap_truncated_registrars.json"));
|
||||
jsonFileBuilder()
|
||||
.put("NAME", "fn=Entity+*&cursor=cjpFbnRpdHkgNA%3D%3D")
|
||||
.load("rdap_truncated_registrars.json"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(0, IncompletenessWarningType.TRUNCATED);
|
||||
}
|
||||
@@ -753,8 +727,9 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Entity *"))
|
||||
.isEqualTo(
|
||||
generateExpectedJson(
|
||||
"fn=Entity+*&cursor=cjpFbnRpdHkgNA%3D%3D", "rdap_truncated_registrars.json"));
|
||||
jsonFileBuilder()
|
||||
.put("NAME", "fn=Entity+*&cursor=cjpFbnRpdHkgNA%3D%3D")
|
||||
.load("rdap_truncated_registrars.json"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(0, IncompletenessWarningType.TRUNCATED);
|
||||
}
|
||||
@@ -815,8 +790,9 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Entity *"))
|
||||
.isEqualTo(
|
||||
generateExpectedJson(
|
||||
"fn=Entity+*&cursor=cjpFbnRpdHkgNA%3D%3D", "rdap_truncated_mixed_entities.json"));
|
||||
jsonFileBuilder()
|
||||
.put("NAME", "fn=Entity+*&cursor=cjpFbnRpdHkgNA%3D%3D")
|
||||
.load("rdap_truncated_mixed_entities.json"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(3, IncompletenessWarningType.TRUNCATED);
|
||||
}
|
||||
@@ -845,7 +821,7 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
rememberWildcardType("Entity *");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Entity *"))
|
||||
.isEqualTo(generateExpectedJson("rdap_nontruncated_contacts.json"));
|
||||
.isEqualTo(jsonFileBuilder().load("rdap_nontruncated_contacts.json"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(4);
|
||||
}
|
||||
@@ -855,8 +831,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("registrars");
|
||||
createManyContactsAndRegistrars(1, 1, registrarTest);
|
||||
runSuccessfulNameTest(
|
||||
"Entity *", "301", "Entity 2", "rdap_registrar.json");
|
||||
rememberWildcardType("Entity *");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Entity *"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("301", "Entity 2", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@@ -878,7 +860,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testNameMatchRegistrar_found_inactiveAsSameRegistrar() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
login("2-RegistrarInact");
|
||||
runSuccessfulNameTest("No Way", "21", "No Way", "inactive", null, "rdap_registrar.json");
|
||||
rememberWildcardType("No Way");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("No Way"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("21", "No Way", "inactive", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@@ -886,7 +875,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testNameMatchRegistrar_found_inactiveAsAdmin() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
loginAsAdmin();
|
||||
runSuccessfulNameTest("No Way", "21", "No Way", "inactive", null, "rdap_registrar.json");
|
||||
rememberWildcardType("No Way");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("No Way"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("21", "No Way", "inactive", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@@ -908,8 +904,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testNameMatchRegistrar_found_testAsSameRegistrar() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulNameTest(
|
||||
"Da Test Registrar", "not applicable", "Da Test Registrar", "rdap_registrar_test.json");
|
||||
rememberWildcardType("Da Test Registrar");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Da Test Registrar"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("not applicable", "Da Test Registrar", "active", null)
|
||||
.load("rdap_registrar_test.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@@ -917,15 +919,28 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testNameMatchRegistrar_found_testAsAdmin() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
loginAsAdmin();
|
||||
runSuccessfulNameTest(
|
||||
"Da Test Registrar", "not applicable", "Da Test Registrar", "rdap_registrar_test.json");
|
||||
rememberWildcardType("Da Test Registrar");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithFullName("Da Test Registrar"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("not applicable", "Da Test Registrar", "active", null)
|
||||
.load("rdap_registrar_test.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleMatchContact_found() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulHandleTestWithBlinky("2-ROID", "rdap_contact.json");
|
||||
rememberWildcardType("2-ROID");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("2-ROID"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -933,7 +948,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testHandleMatchContact_found_subtypeAll() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("all");
|
||||
runSuccessfulHandleTestWithBlinky("2-ROID", "rdap_contact.json");
|
||||
rememberWildcardType("2-ROID");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("2-ROID"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -941,7 +963,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testHandleMatchContact_found_subtypeContacts() {
|
||||
login("2-RegistrarTest");
|
||||
action.subtypeParam = Optional.of("contacts");
|
||||
runSuccessfulHandleTestWithBlinky("2-ROID", "rdap_contact.json");
|
||||
rememberWildcardType("2-ROID");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("2-ROID"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -956,7 +985,12 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
@Test
|
||||
void testHandleMatchContact_found_specifyingSameRegistrar() {
|
||||
action.registrarParam = Optional.of("2-RegistrarTest");
|
||||
runSuccessfulHandleTestWithBlinky("2-ROID", "rdap_contact_no_personal_data_with_remark.json");
|
||||
rememberWildcardType("2-ROID");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("2-ROID"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder().load("rdap_contact_no_personal_data_with_remark.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -986,13 +1020,12 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testHandleMatchContact_found_deletedWhenLoggedInAsSameRegistrar() {
|
||||
login("2-Registrar");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runSuccessfulHandleTest(
|
||||
"6-ROID",
|
||||
"6-ROID",
|
||||
"",
|
||||
"inactive",
|
||||
"",
|
||||
"rdap_contact_deleted.json");
|
||||
rememberWildcardType("6-ROID");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("6-ROID"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder().addContact("6-ROID").load("rdap_contact_deleted.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -1000,13 +1033,12 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testHandleMatchContact_found_deletedWhenLoggedInAsAdmin() {
|
||||
loginAsAdmin();
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runSuccessfulHandleTest(
|
||||
"6-ROID",
|
||||
"6-ROID",
|
||||
"",
|
||||
"inactive",
|
||||
"",
|
||||
"rdap_contact_deleted.json");
|
||||
rememberWildcardType("6-ROID");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("6-ROID"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder().addContact("6-ROID").load("rdap_contact_deleted.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -1029,13 +1061,12 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testHandleMatchContact_found_deletedWildcardWhenLoggedInAsSameRegistrar() {
|
||||
login("2-Registrar");
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runSuccessfulHandleTest(
|
||||
"6-ROI*",
|
||||
"6-ROID",
|
||||
"",
|
||||
"inactive",
|
||||
"",
|
||||
"rdap_contact_deleted.json");
|
||||
rememberWildcardType("6-ROI*");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("6-ROI*"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder().addContact("6-ROID").load("rdap_contact_deleted.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -1043,33 +1074,53 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testHandleMatchContact_found_deletedWildcardWhenLoggedInAsAdmin() {
|
||||
loginAsAdmin();
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
runSuccessfulHandleTest(
|
||||
"6-ROI*",
|
||||
"6-ROID",
|
||||
"",
|
||||
"inactive",
|
||||
"",
|
||||
"rdap_contact_deleted.json");
|
||||
rememberWildcardType("6-ROI*");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("6-ROI*"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder().addContact("6-ROID").load("rdap_contact_deleted.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleMatchRegistrar_found() {
|
||||
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
rememberWildcardType("20");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("20"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("20", "Yes Virginia <script>", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleMatchRegistrar_found_subtypeAll() {
|
||||
action.subtypeParam = Optional.of("all");
|
||||
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
rememberWildcardType("20");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("20"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("20", "Yes Virginia <script>", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleMatchRegistrar_found_subtypeRegistrars() {
|
||||
action.subtypeParam = Optional.of("registrars");
|
||||
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
rememberWildcardType("20");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("20"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("20", "Yes Virginia <script>", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@@ -1083,7 +1134,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
@Test
|
||||
void testHandleMatchRegistrar_found_specifyingSameRegistrar() {
|
||||
action.registrarParam = Optional.of("2-Registrar");
|
||||
runSuccessfulHandleTest("20", "20", "Yes Virginia <script>", "rdap_registrar.json");
|
||||
rememberWildcardType("20");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("20"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("20", "Yes Virginia <script>", "active", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@@ -1098,14 +1156,28 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testHandleMatchContact_found_wildcardWithResultSetSizeOne() {
|
||||
login("2-RegistrarTest");
|
||||
action.rdapResultSetMaxSize = 1;
|
||||
runSuccessfulHandleTestWithBlinky("2-R*", "rdap_contact.json");
|
||||
rememberWildcardType("2-R*");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("2-R*"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHandleMatchContact_found_wildcard() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulHandleTestWithBlinky("2-RO*", "rdap_contact.json");
|
||||
rememberWildcardType("2-R*");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("2-R*"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -1113,7 +1185,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testHandleMatchContact_found_wildcardSpecifyingSameRegistrar() {
|
||||
action.registrarParam = Optional.of("2-RegistrarTest");
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulHandleTestWithBlinky("2-RO*", "rdap_contact.json");
|
||||
rememberWildcardType("2-RO*");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("2-RO*"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -1128,7 +1207,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
@Test
|
||||
void testHandleMatchContact_found_deleted() {
|
||||
login("2-RegistrarTest");
|
||||
runSuccessfulHandleTestWithBlinky("2-RO*", "rdap_contact.json");
|
||||
rememberWildcardType("2-R*");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("2-R*"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullContact("2-ROID", "active", BINKY_FULL_NAME, BINKY_ADDRESS)
|
||||
.load("rdap_contact.json")));
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -1245,7 +1331,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testHandleMatchRegistrar_found_inactiveAsSameRegistrar() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
login("2-RegistrarInact");
|
||||
runSuccessfulHandleTest("21", "21", "No Way", "inactive", null, "rdap_registrar.json");
|
||||
rememberWildcardType("21");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("21"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("21", "No Way", "inactive", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
|
||||
@@ -1253,7 +1346,14 @@ class RdapEntitySearchActionTest extends RdapSearchActionTestCase<RdapEntitySear
|
||||
void testHandleMatchRegistrar_found_inactiveAsAdmin() {
|
||||
action.includeDeletedParam = Optional.of(true);
|
||||
loginAsAdmin();
|
||||
runSuccessfulHandleTest("21", "21", "No Way", "inactive", null, "rdap_registrar.json");
|
||||
rememberWildcardType("21");
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithHandle("21"))
|
||||
.isEqualTo(
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addFullRegistrar("21", "No Way", "inactive", null)
|
||||
.load("rdap_registrar.json")));
|
||||
verifyMetrics(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,13 +48,15 @@ class RdapHelpActionTest extends RdapActionBaseTestCase<RdapHelpAction> {
|
||||
|
||||
@Test
|
||||
void testHelpActionDefault_getsIndex() {
|
||||
assertThat(generateActualJson("")).isEqualTo(loadJsonFile("rdap_help_index.json"));
|
||||
assertThat(generateActualJson(""))
|
||||
.isEqualTo(loadJsonFile("rdap_help_index.json", "POSSIBLE_SLASH", ""));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHelpActionSlash_getsIndex() {
|
||||
assertThat(generateActualJson("/")).isEqualTo(loadJsonFile("rdap_help_index.json"));
|
||||
assertThat(generateActualJson("/"))
|
||||
.isEqualTo(loadJsonFile("rdap_help_index.json", "POSSIBLE_SLASH", "/"));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
|
||||
@@ -462,12 +462,12 @@ class RdapJsonFormatterTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetLastHistoryEntryByType() {
|
||||
void testGetLastHistoryByType() {
|
||||
// Expected data are from "rdapjson_domain_summary.json"
|
||||
assertThat(
|
||||
Maps.transformValues(
|
||||
rdapJsonFormatter.getLastHistoryEntryByType(domainFull),
|
||||
HistoryEntry::getModificationTime))
|
||||
RdapJsonFormatter.getLastHistoryByType(domainFull),
|
||||
RdapJsonFormatter.HistoryTimeAndRegistrar::modificationTime))
|
||||
.containsExactlyEntriesIn(
|
||||
ImmutableMap.of(TRANSFER, DateTime.parse("1999-12-01T00:00:00.000Z")));
|
||||
}
|
||||
|
||||
@@ -15,15 +15,12 @@
|
||||
package google.registry.rdap;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.rdap.RdapTestHelper.loadJsonFile;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrar;
|
||||
import static google.registry.testing.GsonSubject.assertAboutJson;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.JsonObject;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||
import google.registry.rdap.RdapMetrics.SearchType;
|
||||
@@ -32,7 +29,6 @@ import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.testing.FullFieldsTestEntityHelper;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -72,37 +68,6 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
"ns1.domain.external", "9.10.11.12", clock.nowUtc().minusYears(1));
|
||||
}
|
||||
|
||||
private JsonObject generateExpectedJson(
|
||||
String name,
|
||||
@Nullable ImmutableMap<String, String> otherSubstitutions,
|
||||
String expectedOutputFile) {
|
||||
ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
|
||||
builder.put("TYPE", "nameserver");
|
||||
builder.put("NAME", name);
|
||||
boolean punycodeSet = false;
|
||||
if (otherSubstitutions != null) {
|
||||
builder.putAll(otherSubstitutions);
|
||||
if (otherSubstitutions.containsKey("PUNYCODENAME")) {
|
||||
punycodeSet = true;
|
||||
}
|
||||
}
|
||||
if (!punycodeSet) {
|
||||
builder.put("PUNYCODENAME", name);
|
||||
}
|
||||
return loadJsonFile(
|
||||
expectedOutputFile,
|
||||
builder.build());
|
||||
}
|
||||
|
||||
private JsonObject generateExpectedJsonWithTopLevelEntries(
|
||||
String name,
|
||||
@Nullable ImmutableMap<String, String> otherSubstitutions,
|
||||
String expectedOutputFile) {
|
||||
JsonObject obj = generateExpectedJson(name, otherSubstitutions, expectedOutputFile);
|
||||
RdapTestHelper.addNonDomainBoilerplateNotices(obj, "https://example.tld/rdap/");
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInvalidNameserver_returns400() {
|
||||
assertAboutJson()
|
||||
@@ -126,14 +91,11 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("ns1.cat.lol"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
"ns1.cat.lol",
|
||||
ImmutableMap.of(
|
||||
"HANDLE", "2-ROID",
|
||||
"ADDRESSTYPE", "v4",
|
||||
"ADDRESS", "1.2.3.4",
|
||||
"STATUS", "active"),
|
||||
"rdap_host.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.lol", "2-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.4", "STATUS", "active")
|
||||
.load("rdap_host.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@@ -142,14 +104,11 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("ns1.cat.lol."))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
"ns1.cat.lol",
|
||||
ImmutableMap.of(
|
||||
"HANDLE", "2-ROID",
|
||||
"ADDRESSTYPE", "v4",
|
||||
"ADDRESS", "1.2.3.4",
|
||||
"STATUS", "active"),
|
||||
"rdap_host.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.lol", "2-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.4", "STATUS", "active")
|
||||
.load("rdap_host.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@@ -158,14 +117,11 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("Ns1.CaT.lOl."))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
"ns1.cat.lol",
|
||||
ImmutableMap.of(
|
||||
"HANDLE", "2-ROID",
|
||||
"ADDRESSTYPE", "v4",
|
||||
"ADDRESS", "1.2.3.4",
|
||||
"STATUS", "active"),
|
||||
"rdap_host.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.lol", "2-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.4", "STATUS", "active")
|
||||
.load("rdap_host.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@@ -174,14 +130,11 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("ns1.cat.lol?key=value"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
"ns1.cat.lol",
|
||||
ImmutableMap.of(
|
||||
"HANDLE", "2-ROID",
|
||||
"ADDRESSTYPE", "v4",
|
||||
"ADDRESS", "1.2.3.4",
|
||||
"STATUS", "active"),
|
||||
"rdap_host.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.lol", "2-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.4", "STATUS", "active")
|
||||
.load("rdap_host.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@@ -190,15 +143,11 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("ns1.cat.みんな"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
"ns1.cat.みんな",
|
||||
ImmutableMap.of(
|
||||
"PUNYCODENAME", "ns1.cat.xn--q9jyb4c",
|
||||
"HANDLE", "5-ROID",
|
||||
"ADDRESSTYPE", "v6",
|
||||
"ADDRESS", "bad:f00d:cafe::15:beef",
|
||||
"STATUS", "active"),
|
||||
"rdap_host_unicode.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.みんな", "5-ROID")
|
||||
.putAll("ADDRESSTYPE", "v6", "ADDRESS", "bad:f00d:cafe::15:beef")
|
||||
.load("rdap_host_unicode.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@@ -207,15 +156,11 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("ns1.cat.xn--q9jyb4c"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
"ns1.cat.みんな",
|
||||
ImmutableMap.of(
|
||||
"PUNYCODENAME", "ns1.cat.xn--q9jyb4c",
|
||||
"HANDLE", "5-ROID",
|
||||
"ADDRESSTYPE", "v6",
|
||||
"ADDRESS", "bad:f00d:cafe::15:beef",
|
||||
"STATUS", "active"),
|
||||
"rdap_host_unicode.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.みんな", "5-ROID")
|
||||
.putAll("ADDRESSTYPE", "v6", "ADDRESS", "bad:f00d:cafe::15:beef")
|
||||
.load("rdap_host_unicode.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@@ -224,14 +169,11 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("ns1.domain.1.tld"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
"ns1.domain.1.tld",
|
||||
ImmutableMap.of(
|
||||
"HANDLE", "8-ROID",
|
||||
"ADDRESSTYPE", "v4",
|
||||
"ADDRESS", "5.6.7.8",
|
||||
"STATUS", "active"),
|
||||
"rdap_host.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.domain.1.tld", "8-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "5.6.7.8", "STATUS", "active")
|
||||
.load("rdap_host.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@@ -240,14 +182,11 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("ns1.domain.external"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
"ns1.domain.external",
|
||||
ImmutableMap.of(
|
||||
"HANDLE", "C-ROID",
|
||||
"ADDRESSTYPE", "v4",
|
||||
"ADDRESS", "9.10.11.12",
|
||||
"STATUS", "active"),
|
||||
"rdap_host.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.domain.external", "C-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "9.10.11.12", "STATUS", "active")
|
||||
.load("rdap_host.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@@ -287,14 +226,11 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("nsdeleted.cat.lol"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
"nsdeleted.cat.lol",
|
||||
ImmutableMap.of(
|
||||
"HANDLE", "A-ROID",
|
||||
"ADDRESSTYPE", "v4",
|
||||
"ADDRESS", "1.2.3.4",
|
||||
"STATUS", "inactive"),
|
||||
"rdap_host.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("nsdeleted.cat.lol", "A-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.4", "STATUS", "inactive")
|
||||
.load("rdap_host.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@@ -305,14 +241,11 @@ class RdapNameserverActionTest extends RdapActionBaseTestCase<RdapNameserverActi
|
||||
assertAboutJson()
|
||||
.that(generateActualJson("nsdeleted.cat.lol"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonWithTopLevelEntries(
|
||||
"nsdeleted.cat.lol",
|
||||
ImmutableMap.of(
|
||||
"HANDLE", "A-ROID",
|
||||
"ADDRESSTYPE", "v4",
|
||||
"ADDRESS", "1.2.3.4",
|
||||
"STATUS", "inactive"),
|
||||
"rdap_host.json"));
|
||||
addPermanentBoilerplateNotices(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("nsdeleted.cat.lol", "A-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.4", "STATUS", "inactive")
|
||||
.load("rdap_host.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
}
|
||||
|
||||
|
||||
@@ -158,26 +158,9 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
action.nameParam = Optional.empty();
|
||||
}
|
||||
|
||||
private JsonObject generateExpectedJsonForNameserver(
|
||||
String name,
|
||||
String punycodeName,
|
||||
String handle,
|
||||
String ipAddressType,
|
||||
String ipAddress,
|
||||
String expectedOutputFile) {
|
||||
JsonObject obj =
|
||||
loadJsonFile(
|
||||
expectedOutputFile,
|
||||
"NAME", name,
|
||||
"PUNYCODENAME", punycodeName,
|
||||
"HANDLE", handle,
|
||||
"ADDRESSTYPE", ipAddressType,
|
||||
"ADDRESS", ipAddress,
|
||||
"STATUS", "active",
|
||||
"TYPE", "nameserver");
|
||||
obj = RdapTestHelper.wrapInSearchReply("nameserverSearchResults", obj);
|
||||
RdapTestHelper.addNonDomainBoilerplateNotices(obj, "https://example.tld/rdap/");
|
||||
return obj;
|
||||
private JsonObject addBoilerplate(JsonObject jsonObject) {
|
||||
jsonObject = RdapTestHelper.wrapInSearchReply("nameserverSearchResults", jsonObject);
|
||||
return addPermanentBoilerplateNotices(jsonObject);
|
||||
}
|
||||
|
||||
private void createManyHosts(int numHosts) {
|
||||
@@ -318,8 +301,11 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithName("ns1.cat.lol"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonForNameserver(
|
||||
"ns1.cat.lol", null, "2-ROID", "v4", "1.2.3.4", "rdap_host_linked.json"));
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.lol", "2-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.4")
|
||||
.load("rdap_host_linked.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(1);
|
||||
}
|
||||
@@ -329,8 +315,11 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithName("Ns1.CaT.lOl"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonForNameserver(
|
||||
"ns1.cat.lol", null, "2-ROID", "v4", "1.2.3.4", "rdap_host_linked.json"));
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.lol", "2-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.4")
|
||||
.load("rdap_host_linked.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(1);
|
||||
}
|
||||
@@ -356,13 +345,11 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithName("ns2.cat.lol"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonForNameserver(
|
||||
"ns2.cat.lol",
|
||||
null,
|
||||
"4-ROID",
|
||||
"v6",
|
||||
"bad:f00d:cafe::15:beef",
|
||||
"rdap_host_linked.json"));
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns2.cat.lol", "4-ROID")
|
||||
.putAll("ADDRESSTYPE", "v6", "ADDRESS", "bad:f00d:cafe::15:beef")
|
||||
.load("rdap_host_linked.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(1);
|
||||
}
|
||||
@@ -380,8 +367,10 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithName("ns1.cat.external"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonForNameserver(
|
||||
"ns1.cat.external", null, "8-ROID", null, null, "rdap_host_external.json"));
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.external", "8-ROID")
|
||||
.load("rdap_host_external.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(1);
|
||||
}
|
||||
@@ -391,13 +380,11 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithName("ns1.cat.みんな"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonForNameserver(
|
||||
"ns1.cat.みんな",
|
||||
"ns1.cat.xn--q9jyb4c",
|
||||
"B-ROID",
|
||||
"v4",
|
||||
"1.2.3.5",
|
||||
"rdap_host_unicode.json"));
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.みんな", "B-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.5")
|
||||
.load("rdap_host_unicode.json")));
|
||||
metricWildcardType = WildcardType.NO_WILDCARD;
|
||||
metricPrefixLength = 19;
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
@@ -409,13 +396,11 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithName("ns1.cat.xn--q9jyb4c"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonForNameserver(
|
||||
"ns1.cat.みんな",
|
||||
"ns1.cat.xn--q9jyb4c",
|
||||
"B-ROID",
|
||||
"v4",
|
||||
"1.2.3.5",
|
||||
"rdap_host_unicode.json"));
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.みんな", "B-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.5")
|
||||
.load("rdap_host_unicode.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(1);
|
||||
}
|
||||
@@ -425,8 +410,11 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithName("ns1.cat.1.test"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonForNameserver(
|
||||
"ns1.cat.1.test", null, "E-ROID", "v4", "1.2.3.6", "rdap_host.json"));
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.1.test", "E-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.6", "STATUS", "active")
|
||||
.load("rdap_host.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(1);
|
||||
}
|
||||
@@ -553,13 +541,11 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithName("ns*.cat.lol"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonForNameserver(
|
||||
"ns2.cat.lol",
|
||||
null,
|
||||
"4-ROID",
|
||||
"v6",
|
||||
"bad:f00d:cafe::15:beef",
|
||||
"rdap_host_linked.json"));
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns2.cat.lol", "4-ROID")
|
||||
.putAll("ADDRESSTYPE", "v6", "ADDRESS", "bad:f00d:cafe::15:beef")
|
||||
.load("rdap_host_linked.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(2);
|
||||
}
|
||||
@@ -749,8 +735,11 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
|
||||
assertAboutJson()
|
||||
.that(generateActualJsonWithIp("1.2.3.4"))
|
||||
.isEqualTo(
|
||||
generateExpectedJsonForNameserver(
|
||||
"ns1.cat.lol", null, "2-ROID", "v4", "1.2.3.4", "rdap_host_linked.json"));
|
||||
addBoilerplate(
|
||||
jsonFileBuilder()
|
||||
.addNameserver("ns1.cat.lol", "2-ROID")
|
||||
.putAll("ADDRESSTYPE", "v4", "ADDRESS", "1.2.3.4")
|
||||
.load("rdap_host_linked.json")));
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
verifyMetrics(1);
|
||||
}
|
||||
|
||||
@@ -45,107 +45,6 @@ class RdapTestHelper {
|
||||
CONTACT
|
||||
}
|
||||
|
||||
private static JsonObject createTosNotice(String linkBase) {
|
||||
return GSON.toJsonTree(
|
||||
ImmutableMap.of(
|
||||
"title", "RDAP Terms of Service",
|
||||
"description",
|
||||
ImmutableList.of(
|
||||
"By querying our Domain Database, you are agreeing to comply with these"
|
||||
+ " terms so please read them carefully.",
|
||||
"Any information provided is 'as is' without any guarantee of accuracy.",
|
||||
"Please do not misuse the Domain Database. It is intended solely for"
|
||||
+ " query-based access.",
|
||||
"Don't use the Domain Database to allow, enable, or otherwise support the"
|
||||
+ " transmission of mass unsolicited, commercial advertising or"
|
||||
+ " solicitations.",
|
||||
"Don't access our Domain Database through the use of high volume, automated"
|
||||
+ " electronic processes that send queries or data to the systems of"
|
||||
+ " any ICANN-accredited registrar.",
|
||||
"You may only use the information contained in the Domain Database for"
|
||||
+ " lawful purposes.",
|
||||
"Do not compile, repackage, disseminate, or otherwise use the information"
|
||||
+ " contained in the Domain Database in its entirety, or in any"
|
||||
+ " substantial portion, without our prior written permission.",
|
||||
"We may retain certain details about queries to our Domain Database for the"
|
||||
+ " purposes of detecting and preventing misuse.",
|
||||
"We reserve the right to restrict or deny your access to the database if we"
|
||||
+ " suspect that you have failed to comply with these terms.",
|
||||
"We reserve the right to modify this agreement at any time."),
|
||||
"links",
|
||||
ImmutableList.of(
|
||||
ImmutableMap.of(
|
||||
"rel", "self",
|
||||
"href", linkBase + "help/tos",
|
||||
"type", "application/rdap+json"),
|
||||
ImmutableMap.of(
|
||||
"rel", "alternate",
|
||||
"href", "https://www.registry.tld/about/rdap/tos.html",
|
||||
"type", "text/html"))))
|
||||
.getAsJsonObject();
|
||||
}
|
||||
|
||||
static void addNonDomainBoilerplateNotices(JsonObject jsonObject, String linkBase) {
|
||||
if (!jsonObject.has("notices")) {
|
||||
jsonObject.add("notices", new JsonArray());
|
||||
}
|
||||
JsonArray notices = jsonObject.getAsJsonArray("notices");
|
||||
|
||||
notices.add(createTosNotice(linkBase));
|
||||
notices.add(
|
||||
GSON.toJsonTree(
|
||||
ImmutableMap.of(
|
||||
"description",
|
||||
ImmutableList.of(
|
||||
"This response conforms to the RDAP Operational Profile for gTLD"
|
||||
+ " Registries and Registrars version 1.0"))));
|
||||
}
|
||||
|
||||
static void addDomainBoilerplateNotices(JsonObject jsonObject, String linkBase) {
|
||||
if (!jsonObject.has("notices")) {
|
||||
jsonObject.add("notices", new JsonArray());
|
||||
}
|
||||
JsonArray notices = jsonObject.getAsJsonArray("notices");
|
||||
|
||||
notices.add(createTosNotice(linkBase));
|
||||
notices.add(
|
||||
GSON.toJsonTree(
|
||||
ImmutableMap.of(
|
||||
"description",
|
||||
ImmutableList.of(
|
||||
"This response conforms to the RDAP Operational Profile for gTLD"
|
||||
+ " Registries and Registrars version 1.0"))));
|
||||
notices.add(
|
||||
GSON.toJsonTree(
|
||||
ImmutableMap.of(
|
||||
"title",
|
||||
"Status Codes",
|
||||
"description",
|
||||
ImmutableList.of(
|
||||
"For more information on domain status codes, please visit"
|
||||
+ " https://icann.org/epp"),
|
||||
"links",
|
||||
ImmutableList.of(
|
||||
ImmutableMap.of(
|
||||
"rel", "alternate",
|
||||
"href", "https://icann.org/epp",
|
||||
"type", "text/html")))));
|
||||
notices.add(
|
||||
GSON.toJsonTree(
|
||||
ImmutableMap.of(
|
||||
"title",
|
||||
"RDDS Inaccuracy Complaint Form",
|
||||
"description",
|
||||
ImmutableList.of(
|
||||
"URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"),
|
||||
"links",
|
||||
ImmutableList.of(
|
||||
ImmutableMap.of(
|
||||
"rel", "alternate",
|
||||
"href", "https://icann.org/wicf",
|
||||
"type", "text/html")))));
|
||||
}
|
||||
|
||||
static RdapJsonFormatter getTestRdapJsonFormatter(Clock clock) {
|
||||
RdapJsonFormatter rdapJsonFormatter = new RdapJsonFormatter();
|
||||
rdapJsonFormatter.rdapAuthorization = RdapAuthorization.PUBLIC_AUTHORIZATION;
|
||||
@@ -174,7 +73,7 @@ class RdapTestHelper {
|
||||
"We reserve the right to restrict or deny your access to the database if we"
|
||||
+ " suspect that you have failed to comply with these terms.",
|
||||
"We reserve the right to modify this agreement at any time.");
|
||||
rdapJsonFormatter.rdapTosStaticUrl = "https://www.registry.tld/about/rdap/tos.html";
|
||||
rdapJsonFormatter.rdapTosStaticUrl = "https://www.example.tld/about/rdap/tos.html";
|
||||
return rdapJsonFormatter;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import google.registry.model.common.CursorTest;
|
||||
import google.registry.model.common.DnsRefreshRequestTest;
|
||||
import google.registry.model.common.FeatureFlagTest;
|
||||
import google.registry.model.console.ConsoleUpdateHistoryTest;
|
||||
import google.registry.model.console.PasswordResetRequestTest;
|
||||
import google.registry.model.console.UserTest;
|
||||
import google.registry.model.contact.ContactTest;
|
||||
import google.registry.model.domain.DomainSqlTest;
|
||||
@@ -104,6 +105,7 @@ import org.junit.runner.RunWith;
|
||||
FeatureFlagTest.class,
|
||||
HostHistoryTest.class,
|
||||
LockTest.class,
|
||||
PasswordResetRequestTest.class,
|
||||
PollMessageTest.class,
|
||||
PremiumListDaoTest.class,
|
||||
RdeRevisionTest.class,
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
package google.registry.schema.registrar;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.model.registrar.RegistrarPoc.Type.WHOIS;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -63,7 +63,9 @@ class RegistrarPocTest {
|
||||
@Test
|
||||
void testPersistence_succeeds() {
|
||||
insertInDb(testRegistrarPoc);
|
||||
assertThat(loadByEntity(testRegistrarPoc)).isEqualTo(testRegistrarPoc);
|
||||
assertAboutImmutableObjects()
|
||||
.that(testRegistrarPoc)
|
||||
.isEqualExceptFields(testRegistrarPoc, "id");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -84,9 +84,7 @@ public final class FullFieldsTestEntityHelper {
|
||||
.setFaxNumber("+1.2125551213")
|
||||
.setEmailAddress("contact-us@example.com")
|
||||
.setWhoisServer("whois.example.com")
|
||||
.setRdapBaseUrls(
|
||||
ImmutableSet.of(
|
||||
"https://rdap.example.com/withSlash/", "https://rdap.example.com/withoutSlash"))
|
||||
.setRdapBaseUrls(ImmutableSet.of("https://rdap.example.com/withSlash/"))
|
||||
.setUrl("http://my.fake.url")
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ class CreateCdnsTldTest extends CommandTestCase<CreateCdnsTld> {
|
||||
.setDnsName(dnsName)
|
||||
.setDescription(description)
|
||||
.setName(name)
|
||||
.setDnssecConfig(new ManagedZoneDnsSecConfig().setState("ON").setNonExistence("NSEC"));
|
||||
.setDnssecConfig(new ManagedZoneDnsSecConfig().setState("on").setNonExistence("nsec"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -15,16 +15,21 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.JPY;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.beust.jcommander.ParameterException;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.dns.writer.VoidDnsWriter;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
@@ -111,12 +116,15 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
|
||||
@Test
|
||||
void testSuccess_minimal() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, ACTIVE))
|
||||
.build());
|
||||
// Test that each optional field can be omitted. Also tests the auto-gen password.
|
||||
runCommandForced(
|
||||
"--client=NewRegistrar",
|
||||
"--registrant=crr-admin",
|
||||
"--admins=crr-admin",
|
||||
"--techs=crr-tech",
|
||||
"example.tld");
|
||||
eppVerifier.verifySent("domain_create_minimal.xml");
|
||||
}
|
||||
@@ -131,7 +139,9 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
"--techs=crr-tech",
|
||||
"example.tld",
|
||||
"example.abc");
|
||||
eppVerifier.verifySent("domain_create_minimal.xml").verifySent("domain_create_minimal_abc.xml");
|
||||
eppVerifier
|
||||
.verifySent("domain_create_contacts.xml")
|
||||
.verifySent("domain_create_contacts_abc.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -152,8 +162,8 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
"example.tld",
|
||||
"example.abc");
|
||||
eppVerifier
|
||||
.verifySent("domain_create_minimal.xml")
|
||||
.verifySent("domain_create_minimal_abc.xml");
|
||||
.verifySent("domain_create_contacts.xml")
|
||||
.verifySent("domain_create_contacts_abc.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -192,9 +202,9 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
"palladium.tld",
|
||||
"example.abc");
|
||||
eppVerifier
|
||||
.verifySent("domain_create_minimal.xml")
|
||||
.verifySent("domain_create_contacts.xml")
|
||||
.verifySent("domain_create_palladium.xml")
|
||||
.verifySent("domain_create_minimal_abc.xml");
|
||||
.verifySent("domain_create_contacts_abc.xml");
|
||||
assertInStdout(
|
||||
"palladium.tld is premium at USD 877.00 per year; "
|
||||
+ "sending total cost for 1 year(s) of USD 877.00.");
|
||||
@@ -227,6 +237,19 @@ class CreateDomainCommandTest extends EppToolCommandTestCase<CreateDomainCommand
|
||||
eppVerifier.verifySent("domain_create_token.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_contactsStillRequired() throws Exception {
|
||||
// Verify that if contacts are still required, the minimum+contacts request is sent
|
||||
createTld("tld");
|
||||
runCommandForced(
|
||||
"--client=NewRegistrar",
|
||||
"--registrant=crr-admin",
|
||||
"--admins=crr-admin",
|
||||
"--techs=crr-tech",
|
||||
"example.tld");
|
||||
eppVerifier.verifySent("domain_create_contacts.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_duplicateDomains() {
|
||||
IllegalArgumentException thrown =
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.model.registrar.RegistrarPoc.Type.ABUSE;
|
||||
import static google.registry.model.registrar.RegistrarPoc.Type.ADMIN;
|
||||
import static google.registry.model.registrar.RegistrarPoc.Type.TECH;
|
||||
@@ -102,8 +103,9 @@ class RegistrarPocCommandTest extends CommandTestCase<RegistrarPocCommand> {
|
||||
"--visible_in_domain_whois_as_abuse=false",
|
||||
"NewRegistrar");
|
||||
RegistrarPoc registrarPoc = loadRegistrar("NewRegistrar").getContacts().asList().get(1);
|
||||
assertThat(registrarPoc)
|
||||
.isEqualTo(
|
||||
assertAboutImmutableObjects()
|
||||
.that(registrarPoc)
|
||||
.isEqualExceptFields(
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName("Judith Registrar")
|
||||
@@ -115,7 +117,8 @@ class RegistrarPocCommandTest extends CommandTestCase<RegistrarPocCommand> {
|
||||
.setVisibleInWhoisAsAdmin(true)
|
||||
.setVisibleInWhoisAsTech(false)
|
||||
.setVisibleInDomainWhoisAsAbuse(false)
|
||||
.build());
|
||||
.build(),
|
||||
"id");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -261,8 +264,9 @@ class RegistrarPocCommandTest extends CommandTestCase<RegistrarPocCommand> {
|
||||
"--visible_in_domain_whois_as_abuse=true",
|
||||
"NewRegistrar");
|
||||
RegistrarPoc registrarPoc = loadRegistrar("NewRegistrar").getContacts().asList().get(1);
|
||||
assertThat(registrarPoc)
|
||||
.isEqualTo(
|
||||
assertAboutImmutableObjects()
|
||||
.that(registrarPoc)
|
||||
.isEqualExceptFields(
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName("Jim Doe")
|
||||
@@ -272,7 +276,8 @@ class RegistrarPocCommandTest extends CommandTestCase<RegistrarPocCommand> {
|
||||
.setVisibleInWhoisAsAdmin(true)
|
||||
.setVisibleInWhoisAsTech(false)
|
||||
.setVisibleInDomainWhoisAsAbuse(true)
|
||||
.build());
|
||||
.build(),
|
||||
"id");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2025 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.ui.server.console;
|
||||
|
||||
import static google.registry.testing.DatabaseHelper.createAdminUser;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
public abstract class ConsoleActionBaseTestCase {
|
||||
|
||||
protected static final Gson GSON = GsonUtils.provideGson();
|
||||
|
||||
protected final FakeClock clock = new FakeClock(DateTime.parse("2024-04-15T00:00:00.000Z"));
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
|
||||
protected ConsoleApiParams consoleApiParams;
|
||||
protected FakeResponse response;
|
||||
protected User fteUser;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEachBaseTestCase() {
|
||||
createTld("tld");
|
||||
fteUser = createAdminUser("fte@email.tld");
|
||||
AuthResult authResult = AuthResult.createUser(fteUser);
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
response = (FakeResponse) consoleApiParams.response();
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
||||
@@ -25,7 +24,6 @@ import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
@@ -33,20 +31,12 @@ import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link google.registry.ui.server.console.ConsoleDomainGetAction}. */
|
||||
public class ConsoleDomainGetActionTest {
|
||||
|
||||
private ConsoleApiParams consoleApiParams;
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
public class ConsoleDomainGetActionTest extends ConsoleActionBaseTestCase {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
DatabaseHelper.persistActiveDomain("exists.tld");
|
||||
}
|
||||
|
||||
@@ -62,8 +52,8 @@ public class ConsoleDomainGetActionTest {
|
||||
.build())),
|
||||
"exists.tld");
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo(
|
||||
"{\"domainName\":\"exists.tld\",\"adminContact\":{\"key\":\"3-ROID\",\"kind\":"
|
||||
+ "\"google.registry.model.contact.Contact\"},\"techContact\":{\"key\":\"3-ROID\","
|
||||
@@ -81,7 +71,7 @@ public class ConsoleDomainGetActionTest {
|
||||
void testFailure_emptyAuth() {
|
||||
ConsoleDomainGetAction action = createAction(AuthResult.NOT_AUTHENTICATED, "exists.tld");
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_UNAUTHORIZED);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -89,7 +79,7 @@ public class ConsoleDomainGetActionTest {
|
||||
ConsoleDomainGetAction action =
|
||||
createAction(AuthResult.createApp("service@registry.example"), "exists.tld");
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_UNAUTHORIZED);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -98,17 +88,14 @@ public class ConsoleDomainGetActionTest {
|
||||
createAction(
|
||||
AuthResult.createUser(createUser(new UserRoles.Builder().build())), "exists.tld");
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_NOT_FOUND);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_nonexistentDomain() {
|
||||
ConsoleDomainGetAction action =
|
||||
createAction(
|
||||
AuthResult.createUser(createUser(new UserRoles.Builder().setIsAdmin(true).build())),
|
||||
"nonexistent.tld");
|
||||
ConsoleDomainGetAction action = createAction(AuthResult.createUser(fteUser), "nonexistent.tld");
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_NOT_FOUND);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_NOT_FOUND);
|
||||
}
|
||||
|
||||
private User createUser(UserRoles userRoles) {
|
||||
@@ -120,6 +107,7 @@ public class ConsoleDomainGetActionTest {
|
||||
|
||||
private ConsoleDomainGetAction createAction(AuthResult authResult, String domain) {
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
response = (FakeResponse) consoleApiParams.response();
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(Action.Method.GET.toString());
|
||||
return new ConsoleDomainGetAction(consoleApiParams, domain);
|
||||
}
|
||||
|
||||
@@ -16,49 +16,31 @@ package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createAdminUser;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainAsDeleted;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.EppResourceUtils;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import google.registry.ui.server.console.ConsoleDomainListAction.DomainListResult;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link ConsoleDomainListAction}. */
|
||||
public class ConsoleDomainListActionTest {
|
||||
|
||||
private static final Gson GSON = GsonUtils.provideGson();
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2023-10-20T00:00:00.000Z"));
|
||||
|
||||
private ConsoleApiParams consoleApiParams;
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
public class ConsoleDomainListActionTest extends ConsoleActionBaseTestCase {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
for (int i = 0; i < 10; i++) {
|
||||
DatabaseHelper.persistActiveDomain(i + "exists.tld", clock.nowUtc());
|
||||
clock.advanceOneMilli();
|
||||
@@ -70,9 +52,7 @@ public class ConsoleDomainListActionTest {
|
||||
void testSuccess_allDomains() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar");
|
||||
action.run();
|
||||
DomainListResult result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).hasSize(10);
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
assertThat(result.checkpointTime).isEqualTo(clock.nowUtc());
|
||||
@@ -84,9 +64,7 @@ public class ConsoleDomainListActionTest {
|
||||
void testSuccess_noDomains() {
|
||||
ConsoleDomainListAction action = createAction("NewRegistrar");
|
||||
action.run();
|
||||
DomainListResult result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).hasSize(0);
|
||||
assertThat(result.totalResults).isEqualTo(0);
|
||||
assertThat(result.checkpointTime).isEqualTo(clock.nowUtc());
|
||||
@@ -97,9 +75,7 @@ public class ConsoleDomainListActionTest {
|
||||
// Two pages of results should go in reverse chronological order
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, null);
|
||||
action.run();
|
||||
DomainListResult result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
.containsExactly("9exists.tld", "8exists.tld", "7exists.tld", "6exists.tld", "5exists.tld");
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
@@ -107,9 +83,7 @@ public class ConsoleDomainListActionTest {
|
||||
// Now do the second page
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 5, 10L, null);
|
||||
action.run();
|
||||
result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
.containsExactly("4exists.tld", "3exists.tld", "2exists.tld", "1exists.tld", "0exists.tld");
|
||||
}
|
||||
@@ -118,9 +92,7 @@ public class ConsoleDomainListActionTest {
|
||||
void testSuccess_partialPage() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 1, 8, null, null);
|
||||
action.run();
|
||||
DomainListResult result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
.containsExactly("1exists.tld", "0exists.tld");
|
||||
}
|
||||
@@ -130,9 +102,7 @@ public class ConsoleDomainListActionTest {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 10, null, null);
|
||||
action.run();
|
||||
|
||||
DomainListResult result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).hasSize(10);
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
|
||||
@@ -142,9 +112,7 @@ public class ConsoleDomainListActionTest {
|
||||
// Even though we persisted a new domain, the old checkpoint should return no more results
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 10, null, null);
|
||||
action.run();
|
||||
result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).isEmpty();
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
}
|
||||
@@ -153,9 +121,7 @@ public class ConsoleDomainListActionTest {
|
||||
void testSuccess_checkpointTime_deletion() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, null);
|
||||
action.run();
|
||||
DomainListResult result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
|
||||
clock.advanceOneMilli();
|
||||
Domain toDelete =
|
||||
@@ -165,9 +131,7 @@ public class ConsoleDomainListActionTest {
|
||||
// Second page should include the domain that is now deleted due to the checkpoint time
|
||||
action = createAction("TheRegistrar", result.checkpointTime, 1, 5, null, null);
|
||||
action.run();
|
||||
result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains.stream().map(Domain::getDomainName).collect(toImmutableList()))
|
||||
.containsExactly("4exists.tld", "3exists.tld", "2exists.tld", "1exists.tld", "0exists.tld");
|
||||
}
|
||||
@@ -176,9 +140,7 @@ public class ConsoleDomainListActionTest {
|
||||
void testSuccess_searchTerm_oneMatch() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, "0");
|
||||
action.run();
|
||||
DomainListResult result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(Iterables.getOnlyElement(result.domains).getDomainName()).isEqualTo("0exists.tld");
|
||||
}
|
||||
|
||||
@@ -186,9 +148,7 @@ public class ConsoleDomainListActionTest {
|
||||
void testSuccess_searchTerm_returnsNone() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, "deleted");
|
||||
action.run();
|
||||
DomainListResult result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).isEmpty();
|
||||
}
|
||||
|
||||
@@ -196,9 +156,7 @@ public class ConsoleDomainListActionTest {
|
||||
void testSuccess_searchTerm_caseInsensitive() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, "eXiStS");
|
||||
action.run();
|
||||
DomainListResult result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).hasSize(5);
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
}
|
||||
@@ -207,9 +165,7 @@ public class ConsoleDomainListActionTest {
|
||||
void testSuccess_searchTerm_tld() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 5, null, "tld");
|
||||
action.run();
|
||||
DomainListResult result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).hasSize(5);
|
||||
assertThat(result.totalResults).isEqualTo(10);
|
||||
}
|
||||
@@ -218,9 +174,7 @@ public class ConsoleDomainListActionTest {
|
||||
void testPartialSuccess_pastEnd() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 5, 5, null, null);
|
||||
action.run();
|
||||
DomainListResult result =
|
||||
GSON.fromJson(
|
||||
((FakeResponse) consoleApiParams.response()).getPayload(), DomainListResult.class);
|
||||
DomainListResult result = GSON.fromJson(response.getPayload(), DomainListResult.class);
|
||||
assertThat(result.domains).isEmpty();
|
||||
}
|
||||
|
||||
@@ -228,14 +182,14 @@ public class ConsoleDomainListActionTest {
|
||||
void testFailure_invalidResultsPerPage() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, 0, 0, null, null);
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Results per page must be between 1 and 500 inclusive");
|
||||
|
||||
action = createAction("TheRegistrar", null, 0, 501, null, null);
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("Results per page must be between 1 and 500 inclusive");
|
||||
}
|
||||
|
||||
@@ -243,9 +197,8 @@ public class ConsoleDomainListActionTest {
|
||||
void testFailure_invalidPageNumber() {
|
||||
ConsoleDomainListAction action = createAction("TheRegistrar", null, -1, 10, null, null);
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
.isEqualTo("Page number must be non-negative");
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("Page number must be non-negative");
|
||||
}
|
||||
|
||||
private ConsoleDomainListAction createAction(String registrarId) {
|
||||
@@ -259,9 +212,10 @@ public class ConsoleDomainListActionTest {
|
||||
@Nullable Integer resultsPerPage,
|
||||
@Nullable Long totalResults,
|
||||
@Nullable String searchTerm) {
|
||||
AuthResult authResult = AuthResult.createUser(createAdminUser("email@email.example"));
|
||||
AuthResult authResult = AuthResult.createUser(fteUser);
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(Action.Method.GET.toString());
|
||||
response = (FakeResponse) consoleApiParams.response();
|
||||
return new ConsoleDomainListAction(
|
||||
consoleApiParams,
|
||||
registrarId,
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -24,32 +23,19 @@ import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import java.io.IOException;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
class ConsoleDumDownloadActionTest {
|
||||
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2024-04-15T00:00:00.000Z"));
|
||||
|
||||
private ConsoleApiParams consoleApiParams;
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(clock).buildIntegrationTestExtension();
|
||||
class ConsoleDumDownloadActionTest extends ConsoleActionBaseTestCase {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
createTld("tld");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
DatabaseHelper.persistActiveDomain(
|
||||
i + "exists.tld", clock.nowUtc(), clock.nowUtc().plusDays(300));
|
||||
@@ -60,13 +46,7 @@ class ConsoleDumDownloadActionTest {
|
||||
|
||||
@Test
|
||||
void testSuccess_returnsCorrectDomains() throws IOException {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
|
||||
AuthResult authResult = AuthResult.createUser(user);
|
||||
AuthResult authResult = AuthResult.createUser(fteUser);
|
||||
ConsoleDumDownloadAction action = createAction(authResult);
|
||||
action.run();
|
||||
ImmutableList<String> expected =
|
||||
@@ -75,7 +55,6 @@ class ConsoleDumDownloadActionTest {
|
||||
"2exists.tld,2024-04-15 00:00:00.002+00,2025-02-09 00:00:00.002+00,{INACTIVE}",
|
||||
"1exists.tld,2024-04-15 00:00:00.001+00,2025-02-09 00:00:00.001+00,{INACTIVE}",
|
||||
"0exists.tld,2024-04-15 00:00:00+00,2025-02-09 00:00:00+00,{INACTIVE}");
|
||||
FakeResponse response = (FakeResponse) consoleApiParams.response();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
ImmutableList<String> actual =
|
||||
ImmutableList.copyOf(response.getStringWriter().toString().split("\r\n"));
|
||||
@@ -93,11 +72,12 @@ class ConsoleDumDownloadActionTest {
|
||||
AuthResult authResult = AuthResult.createUser(user);
|
||||
ConsoleDumDownloadAction action = createAction(authResult);
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_FORBIDDEN);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
private ConsoleDumDownloadAction createAction(AuthResult authResult) {
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
response = (FakeResponse) consoleApiParams.response();
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(Action.Method.GET.toString());
|
||||
return new ConsoleDumDownloadAction(clock, consoleApiParams, "TheRegistrar", "test_name");
|
||||
}
|
||||
|
||||
@@ -30,22 +30,12 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.flows.PasswordOnlyTransportCredentials;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import google.registry.ui.server.console.ConsoleEppPasswordAction.EppPasswordData;
|
||||
import google.registry.util.EmailMessage;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
@@ -56,20 +46,12 @@ import java.io.StringReader;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
class ConsoleEppPasswordActionTest {
|
||||
private static final Gson GSON = GsonUtils.provideGson();
|
||||
class ConsoleEppPasswordActionTest extends ConsoleActionBaseTestCase {
|
||||
private static String eppPostData =
|
||||
"{\"registrarId\":\"%s\",\"oldPassword\":\"%s\",\"newPassword\":\"%s\",\"newPasswordRepeat\":\"%s\"}";
|
||||
|
||||
private ConsoleApiParams consoleApiParams;
|
||||
protected PasswordOnlyTransportCredentials credentials = new PasswordOnlyTransportCredentials();
|
||||
private FakeResponse response;
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
@@ -86,9 +68,8 @@ class ConsoleEppPasswordActionTest {
|
||||
void testFailure_emptyParams() throws IOException {
|
||||
ConsoleEppPasswordAction action = createAction("", "", "", "");
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
.isEqualTo("Missing param(s): registrarId");
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("Missing param(s): registrarId");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -96,9 +77,8 @@ class ConsoleEppPasswordActionTest {
|
||||
ConsoleEppPasswordAction action =
|
||||
createAction("TheRegistrar", "oldPassword", "newPassword", "newPasswordRepeat");
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
.contains("New password fields don't match");
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).contains("New password fields don't match");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -106,9 +86,8 @@ class ConsoleEppPasswordActionTest {
|
||||
ConsoleEppPasswordAction action =
|
||||
createAction("TheRegistrar", "oldPassword", "randomPasword", "randomPasword");
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_FORBIDDEN);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
.contains("Registrar password is incorrect");
|
||||
assertThat(response.getStatus()).isEqualTo(SC_FORBIDDEN);
|
||||
assertThat(response.getPayload()).contains("Registrar password is incorrect");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -124,12 +103,12 @@ class ConsoleEppPasswordActionTest {
|
||||
+ " environment")
|
||||
.setBody(
|
||||
"The following changes were made in registry unittest environment to the"
|
||||
+ " registrar TheRegistrar by user email@email.com:\n"
|
||||
+ " registrar TheRegistrar by admin fte@email.tld:\n"
|
||||
+ "\n"
|
||||
+ "password: ******** -> ••••••••\n")
|
||||
.setRecipients(ImmutableList.of(new InternetAddress("notification@test.example")))
|
||||
.build());
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -137,7 +116,7 @@ class ConsoleEppPasswordActionTest {
|
||||
ConsoleEppPasswordAction action =
|
||||
createAction("TheRegistrar", "foobar", "randomPassword", "randomPassword");
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertDoesNotThrow(() -> credentials.validate(loadRegistrar("TheRegistrar"), "randomPassword"));
|
||||
ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get();
|
||||
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.EPP_PASSWORD_UPDATE);
|
||||
@@ -147,16 +126,6 @@ class ConsoleEppPasswordActionTest {
|
||||
private ConsoleEppPasswordAction createAction(
|
||||
String registrarId, String oldPassword, String newPassword, String newPasswordRepeat)
|
||||
throws IOException {
|
||||
response = new FakeResponse();
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
DatabaseHelper.putInDb(user);
|
||||
|
||||
AuthResult authResult = AuthResult.createUser(user);
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
AuthenticatedRegistrarAccessor authenticatedRegistrarAccessor =
|
||||
AuthenticatedRegistrarAccessor.createForTesting(
|
||||
ImmutableSetMultimap.of("TheRegistrar", OWNER));
|
||||
|
||||
@@ -27,19 +27,16 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.OteStatsTestHelper;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.ui.server.console.ConsoleOteAction.OteCreateData;
|
||||
import google.registry.util.StringGenerator;
|
||||
@@ -50,19 +47,11 @@ import java.util.stream.Collectors;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
class ConsoleOteActionTest {
|
||||
class ConsoleOteActionTest extends ConsoleActionBaseTestCase {
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
private static final Gson GSON = GsonUtils.provideGson();
|
||||
private final IamClient iamClient = mock(IamClient.class);
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
private FakeResponse response;
|
||||
private ConsoleApiParams consoleApiParams;
|
||||
|
||||
private StringGenerator passwordGenerator =
|
||||
new DeterministicStringGenerator("abcdefghijklmnopqrstuvwxyz");
|
||||
@@ -90,16 +79,12 @@ class ConsoleOteActionTest {
|
||||
Optional.of("someRandomString"),
|
||||
Optional.of(new OteCreateData("testRegistrarId", "tescontact@registry.example")));
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_FORBIDDEN);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_invalidParamsNoRegistrarId() {
|
||||
user =
|
||||
user.asBuilder()
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
AuthResult authResult = AuthResult.createUser(user);
|
||||
AuthResult authResult = AuthResult.createUser(fteUser);
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
ConsoleOteAction action =
|
||||
createAction(
|
||||
@@ -109,9 +94,8 @@ class ConsoleOteActionTest {
|
||||
Optional.of("someRandomString"),
|
||||
Optional.of(new OteCreateData("", "test@email.com")));
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
.isEqualTo("OT&E create body is invalid");
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("OT&E create body is invalid");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,18 +114,13 @@ class ConsoleOteActionTest {
|
||||
Optional.of("someRandomString"),
|
||||
Optional.of(new OteCreateData("testRegistrarId", "")));
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
.isEqualTo("OT&E create body is invalid");
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("OT&E create body is invalid");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_oteCreated() {
|
||||
user =
|
||||
user.asBuilder()
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
AuthResult authResult = AuthResult.createUser(user);
|
||||
AuthResult authResult = AuthResult.createUser(fteUser);
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
ConsoleOteAction action =
|
||||
createAction(
|
||||
@@ -152,8 +131,7 @@ class ConsoleOteActionTest {
|
||||
Optional.of(new OteCreateData("theregistrar", "contact@registry.example")));
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
action.run();
|
||||
String response = ((FakeResponse) consoleApiParams.response()).getPayload();
|
||||
var obsResponse = GSON.fromJson(response, Map.class);
|
||||
var obsResponse = GSON.fromJson(response.getPayload(), Map.class);
|
||||
assertThat(
|
||||
ImmutableMap.of(
|
||||
"theregistrar-1", "theregistrar-sunrise",
|
||||
@@ -162,7 +140,7 @@ class ConsoleOteActionTest {
|
||||
"theregistrar-5", "theregistrar-eap",
|
||||
"password", "abcdefghijklmnop"))
|
||||
.containsExactlyEntriesIn(obsResponse);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
verifyIapPermission(
|
||||
"contact@registry.example",
|
||||
Optional.of("someRandomString@email.test"),
|
||||
@@ -183,50 +161,40 @@ class ConsoleOteActionTest {
|
||||
Optional.of(new OteCreateData("theregistrar", "contact@registry.example")));
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
.isEqualTo("Missing registrarId parameter");
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("Missing registrarId parameter");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_finishedOte() throws Exception {
|
||||
OteStatsTestHelper.setupCompleteOte("theregistrar");
|
||||
user =
|
||||
user.asBuilder()
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
AuthResult authResult = AuthResult.createUser(user);
|
||||
AuthResult authResult = AuthResult.createUser(fteUser);
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
ConsoleOteAction action =
|
||||
createAction(
|
||||
Action.Method.GET, authResult, "theregistrar-1", Optional.empty(), Optional.empty());
|
||||
action.run();
|
||||
|
||||
List<Map<String, ?>> response =
|
||||
GSON.fromJson(((FakeResponse) consoleApiParams.response()).getPayload(), JSONArray.class);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
assertTrue(response.stream().allMatch(status -> Boolean.TRUE.equals(status.get("completed"))));
|
||||
List<Map<String, ?>> responseMaps = GSON.fromJson(response.getPayload(), JSONArray.class);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertTrue(
|
||||
responseMaps.stream().allMatch(status -> Boolean.TRUE.equals(status.get("completed"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_unfinishedOte() throws Exception {
|
||||
OteStatsTestHelper.setupIncompleteOte("theregistrar");
|
||||
user =
|
||||
user.asBuilder()
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
AuthResult authResult = AuthResult.createUser(user);
|
||||
AuthResult authResult = AuthResult.createUser(fteUser);
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
ConsoleOteAction action =
|
||||
createAction(
|
||||
Action.Method.GET, authResult, "theregistrar-1", Optional.empty(), Optional.empty());
|
||||
action.run();
|
||||
|
||||
List<Map<String, ?>> response =
|
||||
GSON.fromJson(((FakeResponse) consoleApiParams.response()).getPayload(), JSONArray.class);
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
List<Map<String, ?>> responseMaps = GSON.fromJson(response.getPayload(), JSONArray.class);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(
|
||||
response.stream()
|
||||
responseMaps.stream()
|
||||
.filter(status -> Boolean.FALSE.equals(status.get("completed")))
|
||||
.map(status -> status.get("description"))
|
||||
.collect(Collectors.toList()))
|
||||
@@ -240,9 +208,9 @@ class ConsoleOteActionTest {
|
||||
String registrarId,
|
||||
Optional<String> maybeGroupEmailAddress,
|
||||
Optional<OteCreateData> oteCreateData) {
|
||||
response = new FakeResponse();
|
||||
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
|
||||
when(consoleApiParams.request().getMethod()).thenReturn(method.toString());
|
||||
response = (FakeResponse) consoleApiParams.response();
|
||||
return new ConsoleOteAction(
|
||||
consoleApiParams,
|
||||
iamClient,
|
||||
|
||||
@@ -42,12 +42,10 @@ import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
@@ -55,13 +53,11 @@ import google.registry.util.StringGenerator;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
@@ -71,7 +67,7 @@ import org.mockito.quality.Strictness;
|
||||
/** Tests for {@link ConsoleRegistryLockAction}. */
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
public class ConsoleRegistryLockActionTest {
|
||||
public class ConsoleRegistryLockActionTest extends ConsoleActionBaseTestCase {
|
||||
|
||||
private static final String EXPECTED_EMAIL_MESSAGE =
|
||||
"""
|
||||
@@ -81,16 +77,9 @@ public class ConsoleRegistryLockActionTest {
|
||||
https://registrarconsole.tld/console/#/registry-lock-verify?lockVerificationCode=\
|
||||
123456789ABCDEFGHJKLMNPQRSTUVWXY""";
|
||||
|
||||
private final FakeClock fakeClock = new FakeClock(DateTime.parse("2024-04-18T12:00:00.000Z"));
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationTestExtension();
|
||||
|
||||
@Mock GmailClient gmailClient;
|
||||
private ConsoleRegistryLockAction action;
|
||||
private Domain defaultDomain;
|
||||
private FakeResponse response;
|
||||
private User user;
|
||||
|
||||
@BeforeEach
|
||||
@@ -118,15 +107,15 @@ public class ConsoleRegistryLockActionTest {
|
||||
|
||||
@Test
|
||||
void testGet_simpleLock() {
|
||||
saveRegistryLock(createDefaultLockBuilder().setLockCompletionTime(fakeClock.nowUtc()).build());
|
||||
saveRegistryLock(createDefaultLockBuilder().setLockCompletionTime(clock.nowUtc()).build());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo(
|
||||
"""
|
||||
[{"domainName":"example.test","registrarPocId":"johndoe@theregistrar.com","lockRequestTime":\
|
||||
{"creationTime":"2024-04-18T12:00:00.000Z"},"unlockRequestTime":"null","lockCompletionTime":\
|
||||
"2024-04-18T12:00:00.000Z","unlockCompletionTime":"null","isSuperuser":false}]\
|
||||
{"creationTime":"2024-04-15T00:00:00.000Z"},"unlockRequestTime":"null","lockCompletionTime":\
|
||||
"2024-04-15T00:00:00.000Z","unlockCompletionTime":"null","isSuperuser":false}]\
|
||||
""");
|
||||
}
|
||||
|
||||
@@ -148,11 +137,11 @@ public class ConsoleRegistryLockActionTest {
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setVerificationCode("123456789ABCDEFGHJKLMNPQRSTUVWXY")
|
||||
.setRegistrarPocId("johndoe@theregistrar.com")
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setUnlockRequestTime(fakeClock.nowUtc())
|
||||
.setLockCompletionTime(clock.nowUtc())
|
||||
.setUnlockRequestTime(clock.nowUtc())
|
||||
.build();
|
||||
saveRegistryLock(expiredUnlock);
|
||||
fakeClock.advanceBy(Duration.standardDays(1));
|
||||
clock.advanceBy(Duration.standardDays(1));
|
||||
|
||||
RegistryLock regularLock =
|
||||
new RegistryLock.Builder()
|
||||
@@ -161,9 +150,9 @@ public class ConsoleRegistryLockActionTest {
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setVerificationCode("123456789ABCDEFGHJKLMNPQRSTUVWXY")
|
||||
.setRegistrarPocId("johndoe@theregistrar.com")
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setLockCompletionTime(clock.nowUtc())
|
||||
.build();
|
||||
fakeClock.advanceOneMilli();
|
||||
clock.advanceOneMilli();
|
||||
RegistryLock adminLock =
|
||||
new RegistryLock.Builder()
|
||||
.setRepoId("repoId")
|
||||
@@ -171,7 +160,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setVerificationCode("122222222ABCDEFGHJKLMNPQRSTUVWXY")
|
||||
.isSuperuser(true)
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setLockCompletionTime(clock.nowUtc())
|
||||
.build();
|
||||
RegistryLock incompleteLock =
|
||||
new RegistryLock.Builder()
|
||||
@@ -189,8 +178,8 @@ public class ConsoleRegistryLockActionTest {
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setVerificationCode("123456789ABCDEFGHJKLMNPQRSTUVWXY")
|
||||
.setRegistrarPocId("johndoe@theregistrar.com")
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setUnlockRequestTime(fakeClock.nowUtc())
|
||||
.setLockCompletionTime(clock.nowUtc())
|
||||
.setUnlockRequestTime(clock.nowUtc())
|
||||
.build();
|
||||
|
||||
RegistryLock unlockedLock =
|
||||
@@ -200,9 +189,9 @@ public class ConsoleRegistryLockActionTest {
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setRegistrarPocId("johndoe@theregistrar.com")
|
||||
.setVerificationCode("123456789ABCDEFGHJKLMNPQRSTUUUUU")
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setUnlockRequestTime(fakeClock.nowUtc())
|
||||
.setUnlockCompletionTime(fakeClock.nowUtc())
|
||||
.setLockCompletionTime(clock.nowUtc())
|
||||
.setUnlockRequestTime(clock.nowUtc())
|
||||
.setUnlockCompletionTime(clock.nowUtc())
|
||||
.build();
|
||||
|
||||
saveRegistryLock(regularLock);
|
||||
@@ -218,24 +207,24 @@ public class ConsoleRegistryLockActionTest {
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo(
|
||||
"""
|
||||
[{"domainName":"adminexample.test","lockRequestTime":{"creationTime":"2024-04-19T12:00:00.001Z"},\
|
||||
"unlockRequestTime":"null","lockCompletionTime":"2024-04-19T12:00:00.001Z","unlockCompletionTime":\
|
||||
[{"domainName":"adminexample.test","lockRequestTime":{"creationTime":"2024-04-16T00:00:00.001Z"},\
|
||||
"unlockRequestTime":"null","lockCompletionTime":"2024-04-16T00:00:00.001Z","unlockCompletionTime":\
|
||||
"null","isSuperuser":true},\
|
||||
\
|
||||
{"domainName":"example.test","registrarPocId":"johndoe@theregistrar.com","lockRequestTime":\
|
||||
{"creationTime":"2024-04-19T12:00:00.001Z"},"unlockRequestTime":"null","lockCompletionTime":\
|
||||
"2024-04-19T12:00:00.000Z","unlockCompletionTime":"null","isSuperuser":false},\
|
||||
{"creationTime":"2024-04-16T00:00:00.001Z"},"unlockRequestTime":"null","lockCompletionTime":\
|
||||
"2024-04-16T00:00:00.000Z","unlockCompletionTime":"null","isSuperuser":false},\
|
||||
\
|
||||
{"domainName":"expiredunlock.test","registrarPocId":"johndoe@theregistrar.com","lockRequestTime":\
|
||||
{"creationTime":"2024-04-18T12:00:00.000Z"},"unlockRequestTime":"2024-04-18T12:00:00.000Z",\
|
||||
"lockCompletionTime":"2024-04-18T12:00:00.000Z","unlockCompletionTime":"null","isSuperuser":false},\
|
||||
{"creationTime":"2024-04-15T00:00:00.000Z"},"unlockRequestTime":"2024-04-15T00:00:00.000Z",\
|
||||
"lockCompletionTime":"2024-04-15T00:00:00.000Z","unlockCompletionTime":"null","isSuperuser":false},\
|
||||
\
|
||||
{"domainName":"incompleteunlock.test","registrarPocId":"johndoe@theregistrar.com","lockRequestTime":\
|
||||
{"creationTime":"2024-04-19T12:00:00.001Z"},"unlockRequestTime":"2024-04-19T12:00:00.001Z",\
|
||||
"lockCompletionTime":"2024-04-19T12:00:00.001Z","unlockCompletionTime":"null","isSuperuser":false},\
|
||||
{"creationTime":"2024-04-16T00:00:00.001Z"},"unlockRequestTime":"2024-04-16T00:00:00.001Z",\
|
||||
"lockCompletionTime":"2024-04-16T00:00:00.001Z","unlockCompletionTime":"null","isSuperuser":false},\
|
||||
\
|
||||
{"domainName":"pending.test","registrarPocId":"johndoe@theregistrar.com","lockRequestTime":\
|
||||
{"creationTime":"2024-04-19T12:00:00.001Z"},"unlockRequestTime":"null","lockCompletionTime":"null",\
|
||||
{"creationTime":"2024-04-16T00:00:00.001Z"},"unlockRequestTime":"null","lockCompletionTime":"null",\
|
||||
"unlockCompletionTime":"null","isSuperuser":false}]""");
|
||||
}
|
||||
|
||||
@@ -288,7 +277,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
|
||||
@Test
|
||||
void testPost_unlock() throws Exception {
|
||||
saveRegistryLock(createDefaultLockBuilder().setLockCompletionTime(fakeClock.nowUtc()).build());
|
||||
saveRegistryLock(createDefaultLockBuilder().setLockCompletionTime(clock.nowUtc()).build());
|
||||
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
action = createDefaultPostAction(false);
|
||||
action.run();
|
||||
@@ -301,7 +290,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
|
||||
@Test
|
||||
void testPost_unlock_relockDuration() throws Exception {
|
||||
saveRegistryLock(createDefaultLockBuilder().setLockCompletionTime(fakeClock.nowUtc()).build());
|
||||
saveRegistryLock(createDefaultLockBuilder().setLockCompletionTime(clock.nowUtc()).build());
|
||||
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
action =
|
||||
createPostAction(
|
||||
@@ -318,10 +307,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
@Test
|
||||
void testPost_adminUnlockingAdmin() throws Exception {
|
||||
saveRegistryLock(
|
||||
createDefaultLockBuilder()
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.isSuperuser(true)
|
||||
.build());
|
||||
createDefaultLockBuilder().setLockCompletionTime(clock.nowUtc()).isSuperuser(true).build());
|
||||
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
user =
|
||||
user.asBuilder()
|
||||
@@ -387,10 +373,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
@Test
|
||||
void testPost_failure_nonAdminUnlockingAdmin() throws Exception {
|
||||
saveRegistryLock(
|
||||
createDefaultLockBuilder()
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.isSuperuser(true)
|
||||
.build());
|
||||
createDefaultLockBuilder().setLockCompletionTime(clock.nowUtc()).isSuperuser(true).build());
|
||||
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
action = createDefaultPostAction(false);
|
||||
action.run();
|
||||
@@ -464,9 +447,9 @@ public class ConsoleRegistryLockActionTest {
|
||||
void testPost_failure_alreadyUnlocked() throws Exception {
|
||||
saveRegistryLock(
|
||||
createDefaultLockBuilder()
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setUnlockRequestTime(fakeClock.nowUtc())
|
||||
.setUnlockCompletionTime(fakeClock.nowUtc())
|
||||
.setLockCompletionTime(clock.nowUtc())
|
||||
.setUnlockRequestTime(clock.nowUtc())
|
||||
.setUnlockCompletionTime(clock.nowUtc())
|
||||
.build());
|
||||
action = createDefaultPostAction(false);
|
||||
action.run();
|
||||
@@ -501,7 +484,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
new DomainLockUtils(
|
||||
new DeterministicStringGenerator(StringGenerator.Alphabets.BASE_58),
|
||||
"adminreg",
|
||||
new CloudTasksHelper(fakeClock).getTestCloudTasksUtils());
|
||||
new CloudTasksHelper(clock).getTestCloudTasksUtils());
|
||||
response = (FakeResponse) params.response();
|
||||
return new ConsoleRegistryLockAction(
|
||||
params, domainLockUtils, gmailClient, optionalPostInput, "TheRegistrar");
|
||||
|
||||
@@ -30,12 +30,10 @@ import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.tools.DomainLockUtils;
|
||||
import google.registry.util.StringGenerator;
|
||||
@@ -43,19 +41,11 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link ConsoleRegistryLockVerifyAction}. */
|
||||
public class ConsoleRegistryLockVerifyActionTest {
|
||||
public class ConsoleRegistryLockVerifyActionTest extends ConsoleActionBaseTestCase {
|
||||
|
||||
private static final String DEFAULT_CODE = "123456789ABCDEFGHJKLMNPQRSTUUUUU";
|
||||
private final FakeClock fakeClock = new FakeClock();
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationTestExtension();
|
||||
|
||||
private FakeResponse response;
|
||||
private Domain defaultDomain;
|
||||
private User user;
|
||||
|
||||
@@ -96,8 +86,8 @@ public class ConsoleRegistryLockVerifyActionTest {
|
||||
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
saveRegistryLock(
|
||||
createDefaultLockBuilder()
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setUnlockRequestTime(fakeClock.nowUtc())
|
||||
.setLockCompletionTime(clock.nowUtc())
|
||||
.setUnlockRequestTime(clock.nowUtc())
|
||||
.build());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
@@ -130,8 +120,8 @@ public class ConsoleRegistryLockVerifyActionTest {
|
||||
saveRegistryLock(
|
||||
createDefaultLockBuilder()
|
||||
.isSuperuser(true)
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setUnlockRequestTime(fakeClock.nowUtc())
|
||||
.setLockCompletionTime(clock.nowUtc())
|
||||
.setUnlockRequestTime(clock.nowUtc())
|
||||
.build());
|
||||
user =
|
||||
user.asBuilder()
|
||||
@@ -159,7 +149,7 @@ public class ConsoleRegistryLockVerifyActionTest {
|
||||
@Test
|
||||
void testFailure_expiredLock() {
|
||||
saveRegistryLock(createDefaultLockBuilder().build());
|
||||
fakeClock.advanceBy(Duration.standardDays(1));
|
||||
clock.advanceBy(Duration.standardDays(1));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload()).isEqualTo("The pending lock has expired; please try again");
|
||||
@@ -181,8 +171,8 @@ public class ConsoleRegistryLockVerifyActionTest {
|
||||
saveRegistryLock(
|
||||
createDefaultLockBuilder()
|
||||
.isSuperuser(true)
|
||||
.setLockCompletionTime(fakeClock.nowUtc())
|
||||
.setUnlockRequestTime(fakeClock.nowUtc())
|
||||
.setLockCompletionTime(clock.nowUtc())
|
||||
.setUnlockRequestTime(clock.nowUtc())
|
||||
.build());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
|
||||
@@ -209,7 +199,7 @@ public class ConsoleRegistryLockVerifyActionTest {
|
||||
new DomainLockUtils(
|
||||
new DeterministicStringGenerator(StringGenerator.Alphabets.BASE_58),
|
||||
"adminreg",
|
||||
new CloudTasksHelper(fakeClock).getTestCloudTasksUtils());
|
||||
new CloudTasksHelper(clock).getTestCloudTasksUtils());
|
||||
response = (FakeResponse) params.response();
|
||||
return new ConsoleRegistryLockVerifyAction(params, domainLockUtils, verificationCode);
|
||||
}
|
||||
|
||||
@@ -28,21 +28,17 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.testing.ConsoleApiParamsUtils;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.testing.SystemPropertyExtension;
|
||||
import google.registry.tools.GsonUtils;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
@@ -51,17 +47,14 @@ import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link google.registry.ui.server.console.ConsoleUpdateRegistrarAction}. */
|
||||
class ConsoleUpdateRegistrarActionTest {
|
||||
private static final Gson GSON = GsonUtils.provideGson();
|
||||
|
||||
private ConsoleApiParams consoleApiParams;
|
||||
private FakeResponse response;
|
||||
class ConsoleUpdateRegistrarActionTest extends ConsoleActionBaseTestCase {
|
||||
|
||||
private Registrar registrar;
|
||||
|
||||
@@ -92,13 +85,8 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
.setEmailAddress("user@registrarId.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build());
|
||||
consoleApiParams = createParams();
|
||||
}
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
@Test
|
||||
void testSuccess_updatesRegistrar() throws IOException {
|
||||
var action =
|
||||
@@ -108,17 +96,44 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
"TheRegistrar",
|
||||
"app, dev",
|
||||
false,
|
||||
"\"2025-01-01T00:00:00.000Z\""));
|
||||
"\"2023-12-12T00:00:00.000Z\""));
|
||||
action.run();
|
||||
Registrar newRegistrar = Registrar.loadByRegistrarId("TheRegistrar").get();
|
||||
assertThat(newRegistrar.getAllowedTlds()).containsExactly("app", "dev");
|
||||
assertThat(newRegistrar.isRegistryLockAllowed()).isFalse();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
ConsoleUpdateHistory history = loadSingleton(ConsoleUpdateHistory.class).get();
|
||||
assertThat(history.getType()).isEqualTo(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE);
|
||||
assertThat(history.getDescription()).hasValue("TheRegistrar");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_updatesNullPocVerificationDate() throws IOException {
|
||||
var action =
|
||||
createAction(
|
||||
String.format(registrarPostData, "TheRegistrar", "app, dev", false, "\"null\""));
|
||||
action.run();
|
||||
Registrar newRegistrar = Registrar.loadByRegistrarId("TheRegistrar").get();
|
||||
assertThat(newRegistrar.getLastPocVerificationDate())
|
||||
.isEqualTo(DateTime.parse("1970-01-01T00:00:00.000Z"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_pocVerificationInTheFuture() throws IOException {
|
||||
var action =
|
||||
createAction(
|
||||
String.format(
|
||||
registrarPostData,
|
||||
"TheRegistrar",
|
||||
"app, dev",
|
||||
false,
|
||||
"\"2025-02-01T00:00:00.000Z\""));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat((String) response.getPayload())
|
||||
.contains("Invalid value of LastPocVerificationDate - value is in the future");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFails_missingWhoisContact() throws IOException {
|
||||
RegistryEnvironment.PRODUCTION.setup(systemPropertyExtension);
|
||||
@@ -129,10 +144,10 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
"TheRegistrar",
|
||||
"app, dev",
|
||||
false,
|
||||
"\"2025-01-01T00:00:00.000Z\""));
|
||||
"\"2024-12-12T00:00:00.000Z\""));
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat((String) ((FakeResponse) consoleApiParams.response()).getPayload())
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat((String) response.getPayload())
|
||||
.contains("Cannot modify allowed TLDs if there is no WHOIS abuse contact set");
|
||||
}
|
||||
|
||||
@@ -159,12 +174,12 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
"TheRegistrar",
|
||||
"app, dev",
|
||||
false,
|
||||
"\"2025-01-01T00:00:00.000Z\""));
|
||||
"\"2023-12-12T00:00:00.000Z\""));
|
||||
action.run();
|
||||
Registrar newRegistrar = Registrar.loadByRegistrarId("TheRegistrar").get();
|
||||
assertThat(newRegistrar.getAllowedTlds()).containsExactly("app", "dev");
|
||||
assertThat(newRegistrar.isRegistryLockAllowed()).isFalse();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -176,7 +191,7 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
"TheRegistrar",
|
||||
"app, dev",
|
||||
false,
|
||||
"\"2025-01-01T00:00:00.000Z\""));
|
||||
"\"2023-12-12T00:00:00.000Z\""));
|
||||
action.run();
|
||||
verify(consoleApiParams.sendEmailUtils().gmailClient, times(1))
|
||||
.sendEmail(
|
||||
@@ -186,11 +201,11 @@ class ConsoleUpdateRegistrarActionTest {
|
||||
+ " environment")
|
||||
.setBody(
|
||||
"The following changes were made in registry unittest environment to the"
|
||||
+ " registrar TheRegistrar by user user@registrarId.com:\n"
|
||||
+ " registrar TheRegistrar by admin fte@email.tld:\n"
|
||||
+ "\n"
|
||||
+ "allowedTlds: null -> [app, dev]\n"
|
||||
+ "lastPocVerificationDate: 1970-01-01T00:00:00.000Z ->"
|
||||
+ " 2025-01-01T00:00:00.000Z\n")
|
||||
+ " 2023-12-12T00:00:00.000Z\n")
|
||||
.setRecipients(ImmutableList.of(new InternetAddress("notification@test.example")))
|
||||
.build());
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user