mirror of
https://github.com/google/nomulus
synced 2026-07-09 01:27:09 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d22c2a8d8 | |||
| fe19f0fe78 | |||
| 599a55d5b1 | |||
| 845f792044 | |||
| ad68052524 | |||
| 04c6652793 | |||
| 5658fbe8bd | |||
| a4540a847a | |||
| fdfbb9572d | |||
| cf1a148208 | |||
| 6b54b69163 | |||
| a839ec434e | |||
| 86b62ebe76 | |||
| 952a92a5db |
+8
-2
@@ -505,6 +505,7 @@ task javaIncrementalFormatCheck {
|
||||
println("Omitting format check: not in a git directory.")
|
||||
}
|
||||
}
|
||||
dependsOn('console-webapp:checkFormatting')
|
||||
}
|
||||
|
||||
// Shows how modified lines in Java source files will change after formatting.
|
||||
@@ -522,6 +523,7 @@ task javaIncrementalFormatApply {
|
||||
doLast {
|
||||
invokeJavaDiffFormatScript("format")
|
||||
}
|
||||
dependsOn('console-webapp:applyFormatting')
|
||||
}
|
||||
|
||||
task javadoc(type: Javadoc) {
|
||||
@@ -564,14 +566,18 @@ task deployCloudSchedulerAndQueue {
|
||||
def env = environment
|
||||
if (!prodOrSandboxEnv) {
|
||||
exec {
|
||||
workingDir "${rootDir}/release/builder/"
|
||||
commandLine 'go', 'run',
|
||||
"${rootDir}/release/builder/deployCloudSchedulerAndQueue.go",
|
||||
"./deployCloudSchedulerAndQueue.go",
|
||||
"${rootDir}/core/src/main/java/google/registry/config/files/nomulus-config-${env}.yaml",
|
||||
"${rootDir}/core/src/main/java/google/registry/env/${env}/default/WEB-INF/cloud-scheduler-tasks.xml",
|
||||
"domain-registry-${env}"
|
||||
}
|
||||
exec {
|
||||
workingDir "${rootDir}/release/builder/"
|
||||
commandLine 'go', 'run',
|
||||
"${rootDir}/release/builder/deployCloudSchedulerAndQueue.go",
|
||||
"./deployCloudSchedulerAndQueue.go",
|
||||
"${rootDir}/core/src/main/java/google/registry/config/files/nomulus-config-${env}.yaml",
|
||||
"${rootDir}/core/src/main/java/google/registry/env/common/default/WEB-INF/cloud-tasks-queue.xml",
|
||||
"domain-registry-${env}"
|
||||
}
|
||||
|
||||
@@ -50,12 +50,19 @@ task buildConsoleWebappProd(type: Exec) {
|
||||
args 'run', 'build'
|
||||
}
|
||||
|
||||
task applyFormatting() {
|
||||
exec {
|
||||
workingDir "${consoleDir}/"
|
||||
commandLine 'npm', 'run', 'prettify'
|
||||
}
|
||||
task applyFormatting(type: Exec) {
|
||||
workingDir "${consoleDir}/"
|
||||
executable 'npm'
|
||||
args 'run', 'prettify'
|
||||
}
|
||||
|
||||
task checkFormatting(type: Exec) {
|
||||
workingDir "${consoleDir}/"
|
||||
executable 'npm'
|
||||
args 'run', 'prettify:check'
|
||||
}
|
||||
|
||||
tasks.runConsoleWebappUnitTests.dependsOn(tasks.npmInstallDeps)
|
||||
tasks.buildConsoleWebappProd.dependsOn(tasks.npmInstallDeps)
|
||||
tasks.applyFormatting.dependsOn(tasks.npmInstallDeps)
|
||||
tasks.checkFormatting.dependsOn(tasks.npmInstallDeps)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"/registrar":
|
||||
"/console-api":
|
||||
{
|
||||
"target": "http://localhost:8080/registrar",
|
||||
"secure": false
|
||||
"target": "http://localhost:8080",
|
||||
"secure": true
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+771
-958
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,7 @@
|
||||
"test": "ng test --browsers=ChromeHeadless --watch=false",
|
||||
"run:dev": "",
|
||||
"prettify": "npx prettier --write ./src/",
|
||||
"prettify:check": "npx prettier --check ./src/",
|
||||
"start:dev": "concurrently \"./../gradlew :core:runTestServer\" \"ng serve --proxy-config dev-proxy.config.json\"",
|
||||
"lint": "ng lint"
|
||||
},
|
||||
|
||||
@@ -22,14 +22,18 @@ import SettingsRegistrarsComponent from './settings/registrars/registrars.compon
|
||||
import SettingsWhoisComponent from './settings/whois/whois.component';
|
||||
import SettingsUsersComponent from './settings/users/users.component';
|
||||
import SettingsSecurityComponent from './settings/security/security.component';
|
||||
import { RegistrarGuard } from './registrar/registrar.guard';
|
||||
import { RegistrarComponent } from './registrar/registrar.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: '/settings/contact', pathMatch: 'full' },
|
||||
{ path: 'home', component: HomeComponent },
|
||||
{ path: 'tlds', component: TldsComponent },
|
||||
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
||||
{ path: 'registrars', component: RegistrarComponent },
|
||||
{ path: 'home', component: HomeComponent, canActivate: [RegistrarGuard] },
|
||||
{ path: 'tlds', component: TldsComponent, canActivate: [RegistrarGuard] },
|
||||
{
|
||||
path: 'settings',
|
||||
component: SettingsComponent,
|
||||
canActivate: [RegistrarGuard],
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
<a mat-list-item [routerLink]="'/settings'" routerLinkActive="active">
|
||||
Settings
|
||||
</a>
|
||||
<a mat-list-item [routerLink]="'/registrars'" routerLinkActive="active">
|
||||
Select Registrar
|
||||
</a>
|
||||
</mat-nav-list>
|
||||
</mat-sidenav>
|
||||
<mat-sidenav-content class="console-app__content">
|
||||
|
||||
@@ -31,6 +31,8 @@ import SettingsContactComponent, {
|
||||
ContactDetailsDialogComponent,
|
||||
} from './settings/contact/contact.component';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { RegistrarComponent } from './registrar/registrar.component';
|
||||
import { RegistrarGuard } from './registrar/registrar.guard';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -41,6 +43,7 @@ import { HttpClientModule } from '@angular/common/http';
|
||||
SettingsComponent,
|
||||
SettingsContactComponent,
|
||||
ContactDetailsDialogComponent,
|
||||
RegistrarComponent,
|
||||
],
|
||||
imports: [
|
||||
HttpClientModule,
|
||||
@@ -50,7 +53,7 @@ import { HttpClientModule } from '@angular/common/http';
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
],
|
||||
providers: [BackendService],
|
||||
providers: [BackendService, RegistrarGuard],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<div class="console-app__registrar">
|
||||
<h4 class="console-app__title">
|
||||
{{ registrarService.activeRegistrarId === "" ? "Select" : "Switch" }}
|
||||
registrar:
|
||||
</h4>
|
||||
<mat-form-field>
|
||||
<mat-label>Registrar</mat-label>
|
||||
<mat-select [(ngModel)]="registrarService.activeRegistrarId">
|
||||
<mat-option
|
||||
*ngFor="let registrar of registrarService.registrars"
|
||||
[value]="registrar"
|
||||
>
|
||||
{{ registrar }}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
@@ -0,0 +1,11 @@
|
||||
.console-app {
|
||||
&__registrar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
&__title {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2023 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 { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RegistrarComponent } from './registrar.component';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
|
||||
describe('RegistrarComponent', () => {
|
||||
let component: RegistrarComponent;
|
||||
let fixture: ComponentFixture<RegistrarComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [RegistrarComponent],
|
||||
imports: [HttpClientTestingModule],
|
||||
providers: [
|
||||
BackendService,
|
||||
{ provide: ActivatedRoute, useValue: {} as ActivatedRoute },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RegistrarComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2023 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 { RegistrarService } from './registrar.service';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-registrar',
|
||||
templateUrl: './registrar.component.html',
|
||||
styleUrls: ['./registrar.component.less'],
|
||||
})
|
||||
export class RegistrarComponent {
|
||||
private lastActiveRegistrarId: string;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
protected registrarService: RegistrarService,
|
||||
private router: Router
|
||||
) {
|
||||
this.lastActiveRegistrarId = registrarService.activeRegistrarId;
|
||||
}
|
||||
|
||||
ngDoCheck() {
|
||||
if (
|
||||
this.registrarService.activeRegistrarId &&
|
||||
this.registrarService.activeRegistrarId !== this.lastActiveRegistrarId &&
|
||||
this.route.snapshot.paramMap.get('nextUrl')
|
||||
) {
|
||||
this.lastActiveRegistrarId = this.registrarService.activeRegistrarId;
|
||||
this.router.navigate([this.route.snapshot.paramMap.get('nextUrl')]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2023 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 { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RegistrarGuard } from './registrar.guard';
|
||||
import { Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { RegistrarService } from './registrar.service';
|
||||
|
||||
describe('RegistrarGuard', () => {
|
||||
let guard: RegistrarGuard;
|
||||
let dummyRegistrarService: RegistrarService;
|
||||
let routeSpy: Router;
|
||||
let dummyRoute: RouterStateSnapshot = {} as RouterStateSnapshot;
|
||||
|
||||
beforeEach(() => {
|
||||
routeSpy = jasmine.createSpyObj<Router>('Router', ['navigate']);
|
||||
dummyRegistrarService = { activeRegistrarId: '' } as RegistrarService;
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
RegistrarGuard,
|
||||
{ provide: Router, useValue: routeSpy },
|
||||
{ provide: RegistrarService, useValue: dummyRegistrarService },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be able to activate when activeRegistrarId is empty', () => {
|
||||
guard = TestBed.inject(RegistrarGuard);
|
||||
const res = guard.canActivate(dummyRoute);
|
||||
expect(res).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should be able to activate when activeRegistrarId is not empty', () => {
|
||||
TestBed.overrideProvider(RegistrarService, {
|
||||
useValue: { activeRegistrarId: 'value' },
|
||||
});
|
||||
guard = TestBed.inject(RegistrarGuard);
|
||||
const res = guard.canActivate(dummyRoute);
|
||||
expect(res).toBeTrue();
|
||||
});
|
||||
|
||||
it('should navigate to registrars when activeRegistrarId is empty', () => {
|
||||
const dummyRoute = { url: '/value' } as RouterStateSnapshot;
|
||||
guard = TestBed.inject(RegistrarGuard);
|
||||
guard.canActivate(dummyRoute);
|
||||
expect(routeSpy.navigate).toHaveBeenCalledOnceWith([
|
||||
'/registrars',
|
||||
{ nextUrl: '/value' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2023 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 { Injectable } from '@angular/core';
|
||||
import { Router, RouterStateSnapshot } from '@angular/router';
|
||||
import { RegistrarService } from './registrar.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RegistrarGuard {
|
||||
constructor(
|
||||
private router: Router,
|
||||
private registrarService: RegistrarService
|
||||
) {}
|
||||
|
||||
canActivate(state: RouterStateSnapshot): Promise<boolean> | boolean {
|
||||
if (this.registrarService.activeRegistrarId) {
|
||||
return true;
|
||||
}
|
||||
return this.router.navigate([`/registrars`, { nextUrl: state.url }]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2023 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 { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RegistrarService } from './registrar.service';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
|
||||
describe('RegistrarService', () => {
|
||||
let service: RegistrarService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule],
|
||||
providers: [BackendService],
|
||||
});
|
||||
service = TestBed.inject(RegistrarService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2023 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 { Injectable } from '@angular/core';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RegistrarService {
|
||||
activeRegistrarId: string = '';
|
||||
registrars: string[] = [];
|
||||
constructor(private backend: BackendService) {
|
||||
this.backend.getRegistrars().subscribe((r) => {
|
||||
this.registrars = r;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
<h3 mat-dialog-title>Contact details</h3>
|
||||
<div mat-dialog-content>
|
||||
<form>
|
||||
<form (ngSubmit)="saveAndClose($event)">
|
||||
<div>
|
||||
<mat-form-field class="contact-details__input">
|
||||
<mat-label>Name: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
[required]="true"
|
||||
[(ngModel)]="contact.name"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
@@ -19,6 +20,7 @@
|
||||
type="email"
|
||||
matInput
|
||||
[email]="true"
|
||||
[required]="true"
|
||||
[(ngModel)]="contact.emailAddress"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
@@ -94,12 +96,9 @@
|
||||
(per CL&D requirements)</mat-checkbox
|
||||
>
|
||||
</section>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button (click)="onClose()">Cancel</button>
|
||||
<button type="submit" mat-button>Save</button>
|
||||
</mat-dialog-actions>
|
||||
</form>
|
||||
|
||||
<p></p>
|
||||
|
||||
<mat-dialog-actions>
|
||||
<button mat-button (click)="onClose()">Cancel</button>
|
||||
<button mat-button (click)="saveAndClose()">Save</button>
|
||||
</mat-dialog-actions>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +14,10 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ContactComponent } from './contact.component';
|
||||
import ContactComponent from './contact.component';
|
||||
import { MaterialModule } from 'src/app/material.module';
|
||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
|
||||
describe('ContactComponent', () => {
|
||||
let component: ContactComponent;
|
||||
@@ -23,8 +26,9 @@ describe('ContactComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ContactComponent],
|
||||
imports: [HttpClientTestingModule, MaterialModule],
|
||||
providers: [BackendService],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ContactComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -104,7 +104,11 @@ export class ContactDetailsDialogComponent {
|
||||
this.operation = data.operation;
|
||||
}
|
||||
|
||||
saveAndClose() {
|
||||
saveAndClose(e: any) {
|
||||
e.preventDefault();
|
||||
if (!e.target.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
let operationObservable;
|
||||
if (this.operation === Operations.ADD) {
|
||||
operationObservable = this.contactService.addContact(this.contact);
|
||||
@@ -143,7 +147,7 @@ export default class ContactComponent {
|
||||
) {
|
||||
// TODO: Refactor to registrarId service
|
||||
this.loading = true;
|
||||
this.contactService.fetchContacts('zoomco').subscribe(() => {
|
||||
this.contactService.fetchContacts().subscribe(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
@@ -160,7 +164,9 @@ export default class ContactComponent {
|
||||
}
|
||||
|
||||
deleteContact(contact: Contact) {
|
||||
this.contactService.deleteContact(contact);
|
||||
if (confirm(`Please confirm contact ${contact.name} delete`)) {
|
||||
this.contactService.deleteContact(contact).subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
openCreateNew(e: Event) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable, tap } from 'rxjs';
|
||||
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||
|
||||
export interface Contact {
|
||||
@@ -34,26 +35,30 @@ export interface Contact {
|
||||
export class ContactService {
|
||||
contacts: Contact[] = [];
|
||||
|
||||
constructor(private backend: BackendService) {}
|
||||
constructor(
|
||||
private backend: BackendService,
|
||||
private registrarService: RegistrarService
|
||||
) {}
|
||||
|
||||
// TODO: Come up with a better handling for registrarId
|
||||
fetchContacts(registrarId: string): Observable<Contact[]> {
|
||||
return this.backend.getContacts(registrarId).pipe(
|
||||
tap((contacts) => {
|
||||
this.contacts = contacts;
|
||||
})
|
||||
);
|
||||
fetchContacts(): Observable<Contact[]> {
|
||||
return this.backend
|
||||
.getContacts(this.registrarService.activeRegistrarId)
|
||||
.pipe(
|
||||
tap((contacts) => {
|
||||
this.contacts = contacts;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
saveContacts(
|
||||
contacts: Contact[],
|
||||
registrarId?: string
|
||||
): Observable<Contact[]> {
|
||||
return this.backend.postContacts(registrarId || 'default', contacts).pipe(
|
||||
tap((_) => {
|
||||
this.contacts = contacts;
|
||||
})
|
||||
);
|
||||
saveContacts(contacts: Contact[]): Observable<Contact[]> {
|
||||
return this.backend
|
||||
.postContacts(this.registrarService.activeRegistrarId, contacts)
|
||||
.pipe(
|
||||
tap((_) => {
|
||||
this.contacts = contacts;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
updateContact(index: number, contact: Contact) {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RegistrarsComponent } from './registrars.component';
|
||||
import RegistrarsComponent from './registrars.component';
|
||||
|
||||
describe('RegistrarsComponent', () => {
|
||||
let component: RegistrarsComponent;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SecurityComponent } from './security.component';
|
||||
import SecurityComponent from './security.component';
|
||||
|
||||
describe('SecurityComponent', () => {
|
||||
let component: SecurityComponent;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UsersComponent } from './users.component';
|
||||
import UsersComponent from './users.component';
|
||||
|
||||
describe('UsersComponent', () => {
|
||||
let component: UsersComponent;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { WhoisComponent } from './whois.component';
|
||||
import WhoisComponent from './whois.component';
|
||||
|
||||
describe('WhoisComponent', () => {
|
||||
let component: WhoisComponent;
|
||||
|
||||
@@ -41,35 +41,11 @@ export class BackendService {
|
||||
}
|
||||
|
||||
getContacts(registrarId: string): Observable<Contact[]> {
|
||||
const mockData = [
|
||||
{
|
||||
name: 'Name Lastname',
|
||||
emailAddress: 'test@google.com',
|
||||
registrarId: 'zoomco',
|
||||
types: ['ADMIN'],
|
||||
visibleInWhoisAsAdmin: false,
|
||||
visibleInWhoisAsTech: false,
|
||||
visibleInDomainWhoisAsAbuse: false,
|
||||
},
|
||||
{
|
||||
name: 'Testname testlastname',
|
||||
emailAddress: 'testasd@google.com',
|
||||
registrarId: 'zoomco',
|
||||
visibleInWhoisAsAdmin: false,
|
||||
visibleInWhoisAsTech: false,
|
||||
visibleInDomainWhoisAsAbuse: false,
|
||||
types: ['BILLING'],
|
||||
},
|
||||
];
|
||||
return this.http
|
||||
.get<Contact[]>(
|
||||
`/console-api/settings/contacts?registrarId=${registrarId}`
|
||||
)
|
||||
.pipe(
|
||||
catchError((err) =>
|
||||
this.errorCatcher<Contact[]>(err, <Contact[]>mockData)
|
||||
)
|
||||
);
|
||||
.pipe(catchError((err) => this.errorCatcher<Contact[]>(err)));
|
||||
}
|
||||
|
||||
postContacts(
|
||||
@@ -81,4 +57,10 @@ export class BackendService {
|
||||
{ contacts }
|
||||
);
|
||||
}
|
||||
|
||||
getRegistrars(): Observable<string[]> {
|
||||
return this.http
|
||||
.get<string[]>('/console-api/registrars')
|
||||
.pipe(catchError((err) => this.errorCatcher<string[]>(err)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,6 @@ import static google.registry.tools.ServiceConnection.getServer;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import com.google.api.gax.rpc.ApiException;
|
||||
import com.google.cloud.tasks.v2.AppEngineHttpRequest;
|
||||
import com.google.cloud.tasks.v2.AppEngineRouting;
|
||||
import com.google.cloud.tasks.v2.CloudTasksClient;
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.cloud.tasks.v2.HttpRequest;
|
||||
@@ -39,10 +37,12 @@ import com.google.common.net.MediaType;
|
||||
import com.google.common.net.UrlEscapers;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.util.Timestamps;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.CollectionUtils;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import google.registry.util.Retrier;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@@ -52,7 +52,6 @@ import java.util.Random;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
@@ -67,9 +66,8 @@ public class CloudTasksUtils implements Serializable {
|
||||
private final Clock clock;
|
||||
private final String projectId;
|
||||
private final String locationId;
|
||||
// defaultServiceAccount and iapClientId are nullable because Optional isn't serializable
|
||||
@Nullable private final String defaultServiceAccount;
|
||||
@Nullable private final String iapClientId;
|
||||
private final String oauthClientId;
|
||||
private final GoogleCredentialsBundle credential;
|
||||
private final SerializableCloudTasksClient client;
|
||||
|
||||
@Inject
|
||||
@@ -78,15 +76,15 @@ public class CloudTasksUtils implements Serializable {
|
||||
Clock clock,
|
||||
@Config("projectId") String projectId,
|
||||
@Config("locationId") String locationId,
|
||||
@Config("defaultServiceAccount") Optional<String> defaultServiceAccount,
|
||||
@Config("iapClientId") Optional<String> iapClientId,
|
||||
@Config("oauthClientId") String oauthClientId,
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credential,
|
||||
SerializableCloudTasksClient client) {
|
||||
this.retrier = retrier;
|
||||
this.clock = clock;
|
||||
this.projectId = projectId;
|
||||
this.locationId = locationId;
|
||||
this.defaultServiceAccount = defaultServiceAccount.orElse(null);
|
||||
this.iapClientId = iapClientId.orElse(null);
|
||||
this.oauthClientId = oauthClientId;
|
||||
this.credential = credential;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@@ -94,10 +92,7 @@ public class CloudTasksUtils implements Serializable {
|
||||
return retrier.callWithRetry(
|
||||
() -> {
|
||||
logger.atInfo().log(
|
||||
"Enqueuing queue='%s' endpoint='%s' service='%s'",
|
||||
queue,
|
||||
task.getAppEngineHttpRequest().getRelativeUri(),
|
||||
task.getAppEngineHttpRequest().getAppEngineRouting().getService());
|
||||
"Enqueuing queue='%s' endpoint='%s'", queue, task.getHttpRequest().getUrl());
|
||||
return client.enqueue(projectId, locationId, queue, task);
|
||||
},
|
||||
ApiException.class);
|
||||
@@ -124,7 +119,7 @@ public class CloudTasksUtils implements Serializable {
|
||||
*
|
||||
* @return the resulting path (unchanged for POST requests, with params added for GET requests)
|
||||
*/
|
||||
private String processRequestParameters(
|
||||
private static String processRequestParameters(
|
||||
String path,
|
||||
HttpMethod method,
|
||||
Multimap<String, String> params,
|
||||
@@ -152,43 +147,17 @@ public class CloudTasksUtils implements Serializable {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Task} that does not use AppEngine for submission.
|
||||
*
|
||||
* <p>This uses the standard Cloud Tasks auth format to create and send an OIDC ID token set to
|
||||
* the default service account. That account must have permission to submit tasks to Cloud Tasks.
|
||||
*/
|
||||
private Task createNonAppEngineTask(
|
||||
String path, HttpMethod method, Service service, Multimap<String, String> params) {
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder().setHttpMethod(method);
|
||||
path =
|
||||
processRequestParameters(
|
||||
path, method, params, requestBuilder::putHeaders, requestBuilder::setBody);
|
||||
OidcToken.Builder oidcTokenBuilder =
|
||||
OidcToken.newBuilder().setServiceAccountEmail(defaultServiceAccount);
|
||||
// If the service is using IAP, add that as the audience for the token so the request can be
|
||||
// appropriately authed. Otherwise, use the project name.
|
||||
if (iapClientId != null) {
|
||||
oidcTokenBuilder.setAudience(iapClientId);
|
||||
} else {
|
||||
oidcTokenBuilder.setAudience(projectId);
|
||||
}
|
||||
requestBuilder.setOidcToken(oidcTokenBuilder.build());
|
||||
String totalPath = String.format("%s%s", getServer(service), path);
|
||||
requestBuilder.setUrl(totalPath);
|
||||
return Task.newBuilder().setHttpRequest(requestBuilder.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link Task} to be enqueued.
|
||||
*
|
||||
* <p>This uses the standard Cloud Tasks auth format to create and send an OIDC ID token with the
|
||||
* default service account as the principal. That account must have permission to submit tasks to
|
||||
* Cloud Tasks.
|
||||
*
|
||||
* @param path the relative URI (staring with a slash and ending without one).
|
||||
* @param method the HTTP method to be used for the request, only GET and POST are supported.
|
||||
* @param service the App Engine service to route the request to. Note that with App Engine Task
|
||||
* Queue API if no service is specified, the service which enqueues the task will be used to
|
||||
* process the task. Cloud Tasks API does not support this feature so the service will always
|
||||
* needs to be explicitly specified.
|
||||
* @param params a multi-map of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* @param service the App Engine service to route the request to.
|
||||
* @param params a multimap of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* to the server to process the duplicate keys.
|
||||
* @return the enqueued task.
|
||||
* @see <a
|
||||
@@ -204,21 +173,18 @@ public class CloudTasksUtils implements Serializable {
|
||||
method.equals(HttpMethod.GET) || method.equals(HttpMethod.POST),
|
||||
"HTTP method %s is used. Only GET and POST are allowed.",
|
||||
method);
|
||||
// If the default service account is configured, send a standard non-AppEngine HTTP request
|
||||
if (defaultServiceAccount != null) {
|
||||
return createNonAppEngineTask(path, method, service, params);
|
||||
} else {
|
||||
AppEngineHttpRequest.Builder requestBuilder =
|
||||
AppEngineHttpRequest.newBuilder()
|
||||
.setHttpMethod(method)
|
||||
.setAppEngineRouting(
|
||||
AppEngineRouting.newBuilder().setService(service.toString()).build());
|
||||
path =
|
||||
processRequestParameters(
|
||||
path, method, params, requestBuilder::putHeaders, requestBuilder::setBody);
|
||||
requestBuilder.setRelativeUri(path);
|
||||
return Task.newBuilder().setAppEngineHttpRequest(requestBuilder.build()).build();
|
||||
}
|
||||
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder().setHttpMethod(method);
|
||||
path =
|
||||
processRequestParameters(
|
||||
path, method, params, requestBuilder::putHeaders, requestBuilder::setBody);
|
||||
OidcToken.Builder oidcTokenBuilder =
|
||||
OidcToken.newBuilder()
|
||||
.setServiceAccountEmail(credential.serviceAccount())
|
||||
.setAudience(oauthClientId);
|
||||
requestBuilder.setOidcToken(oidcTokenBuilder.build());
|
||||
String totalPath = String.format("%s%s", getServer(service), path);
|
||||
requestBuilder.setUrl(totalPath);
|
||||
return Task.newBuilder().setHttpRequest(requestBuilder.build()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,11 +192,8 @@ public class CloudTasksUtils implements Serializable {
|
||||
*
|
||||
* @param path the relative URI (staring with a slash and ending without one).
|
||||
* @param method the HTTP method to be used for the request, only GET and POST are supported.
|
||||
* @param service the App Engine service to route the request to. Note that with App Engine Task
|
||||
* Queue API if no service is specified, the service which enqueues the task will be used to
|
||||
* process the task. Cloud Tasks API does not support this feature so the service will always
|
||||
* needs to be explicitly specified.
|
||||
* @param params a multi-map of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* @param service the App Engine service to route the request to.
|
||||
* @param params a multimap of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* to the server to process the duplicate keys.
|
||||
* @param jitterSeconds the number of seconds that a task is randomly delayed up to.
|
||||
* @return the enqueued task.
|
||||
@@ -260,11 +223,8 @@ public class CloudTasksUtils implements Serializable {
|
||||
*
|
||||
* @param path the relative URI (staring with a slash and ending without one).
|
||||
* @param method the HTTP method to be used for the request, only GET and POST are supported.
|
||||
* @param service the App Engine service to route the request to. Note that with App Engine Task
|
||||
* Queue API if no service is specified, the service which enqueues the task will be used to
|
||||
* process the task. Cloud Tasks API does not support this feature so the service will always
|
||||
* needs to be explicitly specified.
|
||||
* @param params a multi-map of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* @param service the App Engine service to route the request to.
|
||||
* @param params a multimap of URL query parameters. Duplicate keys are saved as is, and it is up
|
||||
* to the server to process the duplicate keys.
|
||||
* @param delay the amount of time that a task needs to delayed for.
|
||||
* @return the enqueued task.
|
||||
@@ -330,6 +290,9 @@ public class CloudTasksUtils implements Serializable {
|
||||
}
|
||||
|
||||
public abstract static class SerializableCloudTasksClient implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7872861868968535498L;
|
||||
|
||||
public abstract Task enqueue(String projectId, String locationId, String queueName, Task task);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +102,8 @@ public final class RegistryConfig {
|
||||
@Module
|
||||
public static final class ConfigModule {
|
||||
|
||||
private ConfigModule() {}
|
||||
|
||||
@Provides
|
||||
@Config("projectId")
|
||||
public static String provideProjectId(RegistryConfigSettings config) {
|
||||
@@ -120,17 +122,6 @@ public final class RegistryConfig {
|
||||
return config.gcpProject.locationId;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("serviceAccountEmails")
|
||||
public static ImmutableList<String> provideServiceAccountEmails(RegistryConfigSettings config) {
|
||||
return ImmutableList.copyOf(config.gcpProject.serviceAccountEmails);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("defaultServiceAccount")
|
||||
public static Optional<String> provideDefaultServiceAccount(RegistryConfigSettings config) {
|
||||
return Optional.ofNullable(config.gcpProject.defaultServiceAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* The filename of the logo to be displayed in the header of the registrar console.
|
||||
@@ -257,7 +248,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("databaseRetention")
|
||||
public static Duration provideDatabaseRetention() {
|
||||
return RegistryConfig.getDatabaseRetention();
|
||||
return getDatabaseRetention();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,7 +295,7 @@ public final class RegistryConfig {
|
||||
* The maximum number of domain and host updates to batch together to send to
|
||||
* PublishDnsUpdatesAction, to avoid exceeding HTTP request timeout limits.
|
||||
*
|
||||
* @see google.registry.dns.ReadDnsRefreshRequestsAction
|
||||
* @see ReadDnsRefreshRequestsAction
|
||||
*/
|
||||
@Provides
|
||||
@Config("dnsTldUpdateBatchSize")
|
||||
@@ -1144,7 +1135,7 @@ public final class RegistryConfig {
|
||||
@Provides
|
||||
@Config("availableOauthScopes")
|
||||
public static ImmutableSet<String> provideAvailableOauthScopes(RegistryConfigSettings config) {
|
||||
return ImmutableSet.copyOf(config.oAuth.availableOauthScopes);
|
||||
return ImmutableSet.copyOf(config.auth.availableOauthScopes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1157,27 +1148,38 @@ public final class RegistryConfig {
|
||||
* API, which requires at least one of:
|
||||
*
|
||||
* <ul>
|
||||
* <li>https://www.googleapis.com/auth/appengine.apis
|
||||
* <li>https://www.googleapis.com/auth/cloud-platform
|
||||
* <li>{@code https://www.googleapis.com/auth/appengine.apis}
|
||||
* <li>{@code https://www.googleapis.com/auth/cloud-platform}
|
||||
* </ul>
|
||||
*/
|
||||
@Provides
|
||||
@Config("requiredOauthScopes")
|
||||
public static ImmutableSet<String> provideRequiredOauthScopes(RegistryConfigSettings config) {
|
||||
return ImmutableSet.copyOf(config.oAuth.requiredOauthScopes);
|
||||
return ImmutableSet.copyOf(config.auth.requiredOauthScopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides service account email addresses allowed to authenticate with the app at {@link
|
||||
* google.registry.request.auth.AuthSettings.AuthLevel#APP} level.
|
||||
*/
|
||||
@Provides
|
||||
@Config("allowedServiceAccountEmails")
|
||||
public static ImmutableSet<String> provideAllowedServiceAccountEmails(
|
||||
RegistryConfigSettings config) {
|
||||
return ImmutableSet.copyOf(config.auth.allowedServiceAccountEmails);
|
||||
}
|
||||
|
||||
/** Provides the allowed OAuth client IDs (could be multibinding). */
|
||||
@Provides
|
||||
@Config("allowedOauthClientIds")
|
||||
public static ImmutableSet<String> provideAllowedOauthClientIds(RegistryConfigSettings config) {
|
||||
return ImmutableSet.copyOf(config.oAuth.allowedOauthClientIds);
|
||||
return ImmutableSet.copyOf(config.auth.allowedOauthClientIds);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("iapClientId")
|
||||
public static Optional<String> provideIapClientId(RegistryConfigSettings config) {
|
||||
return Optional.ofNullable(config.oAuth.iapClientId);
|
||||
@Config("oauthClientId")
|
||||
public static String provideOauthClientId(RegistryConfigSettings config) {
|
||||
return config.auth.oauthClientId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1253,7 +1255,7 @@ public final class RegistryConfig {
|
||||
toImmutableSortedMap(
|
||||
naturalOrder(),
|
||||
e ->
|
||||
e.getKey().equals("START_OF_TIME")
|
||||
"START_OF_TIME".equals(e.getKey())
|
||||
? START_OF_TIME
|
||||
: DateTime.parse(e.getKey()),
|
||||
Entry::getValue));
|
||||
@@ -1374,6 +1376,12 @@ public final class RegistryConfig {
|
||||
public static String providePackageDomainLimitUpgradeEmailBody(RegistryConfigSettings config) {
|
||||
return config.packageMonitoring.packageDomainLimitUpgradeEmailBody;
|
||||
}
|
||||
|
||||
private static String formatComments(String text) {
|
||||
return Splitter.on('\n').omitEmptyStrings().trimResults().splitToList(text).stream()
|
||||
.map(s -> "# " + s)
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the App Engine project ID, which is based off the environment name. */
|
||||
@@ -1539,9 +1547,9 @@ public final class RegistryConfig {
|
||||
* one single INSERT statement which can dramatically increase speed in situations with many
|
||||
* inserts.
|
||||
*
|
||||
* <p>Hibernate docs, i.e.
|
||||
* https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html,
|
||||
* recommend between 10 and 50.
|
||||
* <p><a
|
||||
* href="https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html">Hibernate
|
||||
* User Guide</a> recommends between 10 and 50.
|
||||
*/
|
||||
public static int getHibernateJdbcBatchSize() {
|
||||
return CONFIG_SETTINGS.get().hibernate.jdbcBatchSize;
|
||||
@@ -1568,6 +1576,11 @@ public final class RegistryConfig {
|
||||
return Duration.standardDays(CONFIG_SETTINGS.get().registryPolicy.contactAutomaticTransferDays);
|
||||
}
|
||||
|
||||
/** A discount for all sunrise domain creates, between 0.0 (no discount) and 1.0 (free). */
|
||||
public static double getSunriseDomainCreateDiscount() {
|
||||
return CONFIG_SETTINGS.get().registryPolicy.sunriseDomainCreateDiscount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Memoizes loading of the {@link RegistryConfigSettings} POJO.
|
||||
*
|
||||
@@ -1578,11 +1591,7 @@ public final class RegistryConfig {
|
||||
public static final Supplier<RegistryConfigSettings> CONFIG_SETTINGS =
|
||||
memoize(RegistryConfig::getConfigSettings);
|
||||
|
||||
private static String formatComments(String text) {
|
||||
return Splitter.on('\n').omitEmptyStrings().trimResults().splitToList(text).stream()
|
||||
.map(s -> "# " + s)
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
|
||||
private static InternetAddress parseEmailAddress(String email) {
|
||||
try {
|
||||
|
||||
@@ -23,7 +23,7 @@ public class RegistryConfigSettings {
|
||||
|
||||
public GcpProject gcpProject;
|
||||
public GSuite gSuite;
|
||||
public OAuth oAuth;
|
||||
public Auth auth;
|
||||
public CredentialOAuth credentialOAuth;
|
||||
public RegistryPolicy registryPolicy;
|
||||
public Hibernate hibernate;
|
||||
@@ -54,16 +54,15 @@ public class RegistryConfigSettings {
|
||||
public String backendServiceUrl;
|
||||
public String toolsServiceUrl;
|
||||
public String pubapiServiceUrl;
|
||||
public List<String> serviceAccountEmails;
|
||||
public String defaultServiceAccount;
|
||||
}
|
||||
|
||||
/** Configuration options for OAuth settings for authenticating users. */
|
||||
public static class OAuth {
|
||||
/** Configuration options for authenticating users. */
|
||||
public static class Auth {
|
||||
public List<String> availableOauthScopes;
|
||||
public List<String> requiredOauthScopes;
|
||||
public List<String> allowedOauthClientIds;
|
||||
public String iapClientId;
|
||||
public List<String> allowedServiceAccountEmails;
|
||||
public String oauthClientId;
|
||||
}
|
||||
|
||||
/** Configuration options for accessing Google APIs. */
|
||||
@@ -108,6 +107,7 @@ public class RegistryConfigSettings {
|
||||
public String registryName;
|
||||
public List<String> spec11WebResources;
|
||||
public boolean requireSslCertificates;
|
||||
public double sunriseDomainCreateDiscount;
|
||||
}
|
||||
|
||||
/** Configuration for Hibernate. */
|
||||
|
||||
@@ -18,18 +18,10 @@ gcpProject:
|
||||
# whether to use local/test credentials when connecting to the servers
|
||||
isLocal: true
|
||||
# URLs of the services for the project.
|
||||
defaultServiceUrl: https://localhost
|
||||
backendServiceUrl: https://localhost
|
||||
toolsServiceUrl: https://localhost
|
||||
pubapiServiceUrl: https://localhost
|
||||
# Service accounts eligible for authorization (e.g. default service account,
|
||||
# account used by Cloud Scheduler) to send authenticated requests.
|
||||
serviceAccountEmails:
|
||||
- default-service-account-email@email.com
|
||||
- cloud-scheduler-email@email.com
|
||||
# The default service account with which the service is running. For example,
|
||||
# on GAE this would be {project-id}@appspot.gserviceaccount.com
|
||||
defaultServiceAccount: null
|
||||
defaultServiceUrl: https://default.example.com
|
||||
backendServiceUrl: https://backend.example.com
|
||||
toolsServiceUrl: https://tools.example.com
|
||||
pubapiServiceUrl: https://pubapi.example.com
|
||||
|
||||
gSuite:
|
||||
# Publicly accessible domain name of the running G Suite instance.
|
||||
@@ -188,6 +180,11 @@ registryPolicy:
|
||||
# should generally be true for production environments, for added security.
|
||||
requireSslCertificates: true
|
||||
|
||||
# A fractional discount, if any, to be provided to all sunrise domain creates.
|
||||
# 0 means no discount will be applied, and 1 means that all sunrise creates
|
||||
# will be free.
|
||||
sunriseDomainCreateDiscount: 0.15
|
||||
|
||||
hibernate:
|
||||
# Make 'SERIALIZABLE' the default isolation level to ensure correctness.
|
||||
#
|
||||
@@ -295,24 +292,41 @@ caching:
|
||||
# long duration is acceptable because claims lists don't change frequently.
|
||||
claimsListCachingSeconds: 21600 # six hours
|
||||
|
||||
oAuth:
|
||||
# Note: Only allowedServiceAccountEmails and oauthClientId should be configured.
|
||||
# Other fields are related to OAuth-based authentication and will be removed.
|
||||
auth:
|
||||
# Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth.
|
||||
# OAuth scopes to detect on access tokens. Superset of requiredOauthScopes.
|
||||
availableOauthScopes:
|
||||
- https://www.googleapis.com/auth/userinfo.email
|
||||
|
||||
# Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth.
|
||||
# OAuth scopes required for authenticating. Subset of availableOauthScopes.
|
||||
requiredOauthScopes:
|
||||
- https://www.googleapis.com/auth/userinfo.email
|
||||
|
||||
# Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth.
|
||||
# OAuth client IDs that are allowed to authenticate and communicate with
|
||||
# backend services, e. g. nomulus tool, EPP proxy, etc. The client_id value
|
||||
# used in registryTool.clientId field for associated tooling should be included
|
||||
# in this list. Client IDs are typically of the format
|
||||
# backend services, e.g. nomulus tool, EPP proxy, etc. The value in
|
||||
# registryTool.clientId field should be included in this list. Client IDs are
|
||||
# typically of the format
|
||||
# numbers-alphanumerics.apps.googleusercontent.com
|
||||
allowedOauthClientIds: []
|
||||
# GCP Identity-Aware Proxy client ID, if set up (note: this requires manual setup
|
||||
# of User objects in the database for Nomulus tool users)
|
||||
iapClientId: null
|
||||
|
||||
# Service accounts (e.g. default service account, account used by Cloud
|
||||
# Scheduler) allowed to send authenticated requests.
|
||||
allowedServiceAccountEmails:
|
||||
- default-service-account-email@email.com
|
||||
- cloud-scheduler-email@email.com
|
||||
|
||||
# OAuth 2.0 client ID that will be used as the audience in OIDC ID tokens sent
|
||||
# from clients (e.g. proxy, nomulus tool, cloud tasks) for authentication. The
|
||||
# same ID is the only one accepted by the regular OIDC or IAP authentication
|
||||
# mechanisms. In most cases we should use the client ID created for IAP here,
|
||||
# as it allows requests bearing a token with this audience to be accepted by
|
||||
# both IAP or regular OIDC. The clientId value in proxy config file should be
|
||||
# the same as this one.
|
||||
oauthClientId: iap-oauth-clientid
|
||||
|
||||
credentialOAuth:
|
||||
# OAuth scopes required for accessing Google APIs using the default
|
||||
|
||||
@@ -140,25 +140,13 @@ public final class TldFanoutAction implements Runnable {
|
||||
for (String tld : tlds) {
|
||||
Task task = createTask(tld, flowThruParams);
|
||||
Task createdTask = cloudTasksUtils.enqueue(queue, task);
|
||||
if (createdTask.hasAppEngineHttpRequest()) {
|
||||
outputPayload.append(
|
||||
String.format(
|
||||
"- Task: '%s', tld: '%s', endpoint: '%s'\n",
|
||||
createdTask.getName(),
|
||||
tld,
|
||||
createdTask.getAppEngineHttpRequest().getRelativeUri()));
|
||||
logger.atInfo().log(
|
||||
"Task: '%s', tld: '%s', endpoint: '%s'.",
|
||||
createdTask.getName(), tld, createdTask.getAppEngineHttpRequest().getRelativeUri());
|
||||
} else {
|
||||
outputPayload.append(
|
||||
String.format(
|
||||
"- Task: '%s', tld: '%s', endpoint: '%s'\n",
|
||||
createdTask.getName(), tld, createdTask.getHttpRequest().getUrl()));
|
||||
logger.atInfo().log(
|
||||
"Task: '%s', tld: '%s', endpoint: '%s'.",
|
||||
createdTask.getName(), tld, createdTask.getHttpRequest().getUrl());
|
||||
}
|
||||
outputPayload.append(
|
||||
String.format(
|
||||
"- Task: '%s', tld: '%s', endpoint: '%s'\n",
|
||||
createdTask.getName(), tld, createdTask.getHttpRequest().getUrl()));
|
||||
logger.atInfo().log(
|
||||
"Task: '%s', tld: '%s', endpoint: '%s'.",
|
||||
createdTask.getName(), tld, createdTask.getHttpRequest().getUrl());
|
||||
}
|
||||
response.setContentType(PLAIN_TEXT_UTF_8);
|
||||
response.setPayload(outputPayload.toString());
|
||||
|
||||
@@ -60,21 +60,18 @@ public final class RefreshDnsAction implements Runnable {
|
||||
if (!domainOrHostName.contains(".")) {
|
||||
throw new BadRequestException("URL parameter 'name' must be fully qualified");
|
||||
}
|
||||
tm().transact(
|
||||
() -> {
|
||||
switch (type) {
|
||||
case DOMAIN:
|
||||
loadAndVerifyExistence(Domain.class, domainOrHostName);
|
||||
requestDomainDnsRefresh(domainOrHostName);
|
||||
break;
|
||||
case HOST:
|
||||
verifyHostIsSubordinate(loadAndVerifyExistence(Host.class, domainOrHostName));
|
||||
requestHostDnsRefresh(domainOrHostName);
|
||||
break;
|
||||
default:
|
||||
throw new BadRequestException("Unsupported type: " + type);
|
||||
}
|
||||
});
|
||||
switch (type) {
|
||||
case DOMAIN:
|
||||
loadAndVerifyExistence(Domain.class, domainOrHostName);
|
||||
tm().transact(() -> requestDomainDnsRefresh(domainOrHostName));
|
||||
break;
|
||||
case HOST:
|
||||
verifyHostIsSubordinate(loadAndVerifyExistence(Host.class, domainOrHostName));
|
||||
tm().transact(() -> requestHostDnsRefresh(domainOrHostName));
|
||||
break;
|
||||
default:
|
||||
throw new BadRequestException("Unsupported type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends EppResource & ForeignKeyedEppResource>
|
||||
|
||||
@@ -144,15 +144,13 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
dsRrData.add(ds.toRrData());
|
||||
}
|
||||
|
||||
if (!dsRrData.isEmpty()) {
|
||||
domainRecords.add(
|
||||
new ResourceRecordSet()
|
||||
.setName(absoluteDomainName)
|
||||
.setTtl((int) tld.getDnsDsTtl().orElse(defaultDsTtl).getStandardSeconds())
|
||||
.setType("DS")
|
||||
.setKind("dns#resourceRecordSet")
|
||||
.setRrdatas(ImmutableList.copyOf(dsRrData)));
|
||||
}
|
||||
domainRecords.add(
|
||||
new ResourceRecordSet()
|
||||
.setName(absoluteDomainName)
|
||||
.setTtl((int) tld.getDnsDsTtl().orElse(defaultDsTtl).getStandardSeconds())
|
||||
.setType("DS")
|
||||
.setKind("dns#resourceRecordSet")
|
||||
.setRrdatas(ImmutableList.copyOf(dsRrData)));
|
||||
}
|
||||
|
||||
// Construct NS records (if any).
|
||||
@@ -169,15 +167,13 @@ public class CloudDnsWriter extends BaseDnsWriter {
|
||||
}
|
||||
}
|
||||
|
||||
if (!nsRrData.isEmpty()) {
|
||||
domainRecords.add(
|
||||
new ResourceRecordSet()
|
||||
.setName(absoluteDomainName)
|
||||
.setTtl((int) tld.getDnsNsTtl().orElse(defaultNsTtl).getStandardSeconds())
|
||||
.setType("NS")
|
||||
.setKind("dns#resourceRecordSet")
|
||||
.setRrdatas(ImmutableList.copyOf(nsRrData)));
|
||||
}
|
||||
domainRecords.add(
|
||||
new ResourceRecordSet()
|
||||
.setName(absoluteDomainName)
|
||||
.setTtl((int) tld.getDnsNsTtl().orElse(defaultNsTtl).getStandardSeconds())
|
||||
.setType("NS")
|
||||
.setKind("dns#resourceRecordSet")
|
||||
.setRrdatas(ImmutableList.copyOf(nsRrData)));
|
||||
}
|
||||
|
||||
desiredRecords.put(absoluteDomainName, domainRecords.build());
|
||||
|
||||
@@ -337,7 +337,8 @@ public final class DomainCreateFlow implements TransactionalFlow {
|
||||
Optional<FeeCreateCommandExtension> feeCreate =
|
||||
eppInput.getSingleExtension(FeeCreateCommandExtension.class);
|
||||
FeesAndCredits feesAndCredits =
|
||||
pricingLogic.getCreatePrice(tld, targetId, now, years, isAnchorTenant, allocationToken);
|
||||
pricingLogic.getCreatePrice(
|
||||
tld, targetId, now, years, isAnchorTenant, isSunriseCreate, allocationToken);
|
||||
validateFeeChallenge(feeCreate, feesAndCredits, defaultTokenUsed);
|
||||
Optional<SecDnsCreateExtension> secDnsCreate =
|
||||
validateSecDnsExtension(eppInput.getSingleExtension(SecDnsCreateExtension.class));
|
||||
|
||||
@@ -690,6 +690,7 @@ public class DomainFlowUtils {
|
||||
now,
|
||||
years,
|
||||
isAnchorTenant(domainName, allocationToken, Optional.empty()),
|
||||
isSunrise,
|
||||
allocationToken)
|
||||
.getFees();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.util.DomainNameUtils.getTldFromDomainName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.google.common.net.InternetDomainName;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.custom.DomainPricingCustomLogic;
|
||||
@@ -72,26 +73,33 @@ public final class DomainPricingLogic {
|
||||
DateTime dateTime,
|
||||
int years,
|
||||
boolean isAnchorTenant,
|
||||
boolean isSunriseCreate,
|
||||
Optional<AllocationToken> allocationToken)
|
||||
throws EppException {
|
||||
CurrencyUnit currency = tld.getCurrency();
|
||||
|
||||
BaseFee createFeeOrCredit;
|
||||
BaseFee createFee;
|
||||
// Domain create cost is always zero for anchor tenants
|
||||
if (isAnchorTenant) {
|
||||
createFeeOrCredit = Fee.create(zeroInCurrency(currency), FeeType.CREATE, false);
|
||||
createFee = Fee.create(zeroInCurrency(currency), FeeType.CREATE, false);
|
||||
} else {
|
||||
DomainPrices domainPrices = getPricesForDomainName(domainName, dateTime);
|
||||
Money domainCreateCost =
|
||||
getDomainCreateCostWithDiscount(domainPrices, years, allocationToken);
|
||||
createFeeOrCredit =
|
||||
// Apply a sunrise discount if configured and applicable
|
||||
if (isSunriseCreate) {
|
||||
domainCreateCost =
|
||||
domainCreateCost.multipliedBy(
|
||||
1.0d - RegistryConfig.getSunriseDomainCreateDiscount(), RoundingMode.HALF_EVEN);
|
||||
}
|
||||
createFee =
|
||||
Fee.create(domainCreateCost.getAmount(), FeeType.CREATE, domainPrices.isPremium());
|
||||
}
|
||||
|
||||
// Create fees for the cost and the EAP fee, if any.
|
||||
Fee eapFee = tld.getEapFeeFor(dateTime);
|
||||
FeesAndCredits.Builder feesBuilder =
|
||||
new FeesAndCredits.Builder().setCurrency(currency).addFeeOrCredit(createFeeOrCredit);
|
||||
new FeesAndCredits.Builder().setCurrency(currency).addFeeOrCredit(createFee);
|
||||
// Don't charge anchor tenants EAP fees.
|
||||
if (!isAnchorTenant && !eapFee.hasZeroCost()) {
|
||||
feesBuilder.addFeeOrCredit(eapFee);
|
||||
|
||||
@@ -475,6 +475,9 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
/** An allowlist of hosts allowed to be used on domains on this TLD (ignored if empty). */
|
||||
@Nullable Set<String> allowedFullyQualifiedHostNames;
|
||||
|
||||
@Column(nullable = false)
|
||||
boolean breakglassMode = false;
|
||||
|
||||
/**
|
||||
* References to allocation tokens that can be used on the TLD if no other token is passed in on a
|
||||
* domain create.
|
||||
@@ -701,6 +704,10 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
return nullToEmptyImmutableCopy(idnTables);
|
||||
}
|
||||
|
||||
public boolean getBreakglassMode() {
|
||||
return breakglassMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
@@ -1004,6 +1011,11 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setBreakglassMode(boolean breakglassMode) {
|
||||
getInstance().breakglassMode = breakglassMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tld build() {
|
||||
final Tld instance = getInstance();
|
||||
|
||||
@@ -367,7 +367,7 @@ final class RdapDataStructures {
|
||||
PENDING_RESTORE("pending restore"),
|
||||
REDEMPTION_PERIOD("redemption period"),
|
||||
RENEW_PERIOD("renew period"),
|
||||
SERVER_DELETE_PROHIBITED("server deleted prohibited"),
|
||||
SERVER_DELETE_PROHIBITED("server delete prohibited"),
|
||||
SERVER_RENEW_PROHIBITED("server renew prohibited"),
|
||||
SERVER_TRANSFER_PROHIBITED("server transfer prohibited"),
|
||||
SERVER_UPDATE_PROHIBITED("server update prohibited"),
|
||||
|
||||
@@ -54,12 +54,11 @@ public class RdapIcannStandardInformation {
|
||||
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://www.icann.org/wicf")
|
||||
.setDescription("URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf")
|
||||
.addLink(
|
||||
Link.builder()
|
||||
.setRel("alternate")
|
||||
.setHref("https://www.icann.org/wicf")
|
||||
.setHref("https://icann.org/wicf")
|
||||
.setType("text/html")
|
||||
.build())
|
||||
.build();
|
||||
@@ -150,14 +149,6 @@ public class RdapIcannStandardInformation {
|
||||
.build())
|
||||
.build();
|
||||
|
||||
/**
|
||||
* String that replaces GDPR redacted values.
|
||||
*
|
||||
* <p>GTLD Registration Data Temp Spec 17may18, Appendix A, 2.2: Fields required to be "redacted"
|
||||
* MUST privide in the value section text similar to "REDACTED FOR PRIVACY"
|
||||
*/
|
||||
static final String CONTACT_REDACTED_VALUE = "REDACTED FOR PRIVACY";
|
||||
|
||||
/**
|
||||
* Included in ALL contact responses, even if the user is authorized.
|
||||
*
|
||||
|
||||
@@ -21,7 +21,6 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
|
||||
import static google.registry.model.EppResourceUtils.isLinked;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.rdap.RdapIcannStandardInformation.CONTACT_REDACTED_VALUE;
|
||||
import static google.registry.util.CollectionUtils.union;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@@ -39,7 +38,6 @@ import com.google.gson.JsonArray;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactAddress;
|
||||
import google.registry.model.contact.ContactPhoneNumber;
|
||||
import google.registry.model.contact.PostalInfo;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
@@ -519,69 +517,68 @@ public class RdapJsonFormatter {
|
||||
boolean isAuthorized =
|
||||
rdapAuthorization.isAuthorizedForRegistrar(contact.getCurrentSponsorRegistrarId());
|
||||
|
||||
// ROID needs to be redacted if we aren't authorized, so we can't have a self-link for
|
||||
// unauthorized users
|
||||
VcardArray.Builder vcardBuilder = VcardArray.builder();
|
||||
|
||||
if (isAuthorized) {
|
||||
contactBuilder.linksBuilder().add(makeSelfLink("entity", contact.getRepoId()));
|
||||
}
|
||||
|
||||
// Only show the "summary data remark" if the user is authorized to see this data - because
|
||||
// unauthorized users don't have a self link meaning they can't navigate to the full data.
|
||||
if (outputDataType != OutputDataType.FULL && isAuthorized) {
|
||||
contactBuilder.remarksBuilder().add(RdapIcannStandardInformation.SUMMARY_DATA_REMARK);
|
||||
}
|
||||
|
||||
// GTLD Registration Data Temp Spec 17may18, Appendix A, 2.3, 2.4 and RDAP Response Profile
|
||||
// 2.7.4.1, 2.7.4.2 - the following fields must be redacted:
|
||||
// for REGISTRANT:
|
||||
// handle (ROID), FN (name), TEL (telephone/fax and extension), street, city, postal code
|
||||
// for ADMIN, TECH:
|
||||
// handle (ROID), FN (name), TEL (telephone/fax and extension), Organization, street, city,
|
||||
// state/province, postal code, country
|
||||
//
|
||||
// Note that in theory we have to show the Organization and state/province and country for the
|
||||
// REGISTRANT. For now, we won't do that until we make sure it's really OK for GDPR
|
||||
//
|
||||
if (!isAuthorized) {
|
||||
fillRdapContactEntityWhenAuthorized(contactBuilder, vcardBuilder, contact, outputDataType);
|
||||
} else {
|
||||
// GTLD Registration Data Temp Spec 17may18, Appendix A, 2.3, 2.4 and RDAP Response Profile
|
||||
// 2.7.4.1, 2.7.4.2 - the following fields must be redacted:
|
||||
// for REGISTRANT:
|
||||
// handle (ROID), FN (name), TEL (telephone/fax and extension), street, city, postal code
|
||||
// for ADMIN, TECH:
|
||||
// handle (ROID), FN (name), TEL (telephone/fax and extension), Organization, street, city,
|
||||
// state/province, postal code, country
|
||||
//
|
||||
// Note that in theory we have to show the Organization and state/province and country for the
|
||||
// REGISTRANT. For now, we won't do that until we make sure it's really OK for GDPR
|
||||
//
|
||||
// RDAP Response Profile 2.7.4.3: if we redact values from the contact, we MUST include a
|
||||
// remark
|
||||
contactBuilder
|
||||
.remarksBuilder()
|
||||
.add(RdapIcannStandardInformation.CONTACT_PERSONAL_DATA_HIDDEN_DATA_REMARK);
|
||||
// to make sure we don't accidentally display data we shouldn't - we replace the
|
||||
// contact with a safe resource. Then we can add any information we need (e.g. the
|
||||
// Organization / state / country of the registrant), although we currently don't do that.
|
||||
contact =
|
||||
new Contact.Builder()
|
||||
.setRepoId(CONTACT_REDACTED_VALUE)
|
||||
.setVoiceNumber(
|
||||
new ContactPhoneNumber.Builder().setPhoneNumber(CONTACT_REDACTED_VALUE).build())
|
||||
.setFaxNumber(
|
||||
new ContactPhoneNumber.Builder().setPhoneNumber(CONTACT_REDACTED_VALUE).build())
|
||||
.setInternationalizedPostalInfo(
|
||||
new PostalInfo.Builder()
|
||||
.setName(CONTACT_REDACTED_VALUE)
|
||||
.setOrg(CONTACT_REDACTED_VALUE)
|
||||
.setType(PostalInfo.Type.INTERNATIONALIZED)
|
||||
.setAddress(
|
||||
new ContactAddress.Builder()
|
||||
.setStreet(ImmutableList.of(CONTACT_REDACTED_VALUE))
|
||||
.setCity(CONTACT_REDACTED_VALUE)
|
||||
.setState(CONTACT_REDACTED_VALUE)
|
||||
.setZip(CONTACT_REDACTED_VALUE)
|
||||
.setCountryCode("XX")
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
contactBuilder.setHandle("");
|
||||
// The VCard format requires a "fn" entry even if it is empty (redacted)
|
||||
vcardBuilder.add(Vcard.create("fn", "text", ""));
|
||||
}
|
||||
|
||||
// RDAP Response Profile 2.7.3 - we MUST provide a handle set with the ROID, subject to the
|
||||
// redaction above.
|
||||
contactBuilder.setHandle(contact.getRepoId());
|
||||
contactBuilder.setVcardArray(vcardBuilder.build());
|
||||
contactBuilder.rolesBuilder().addAll(roles);
|
||||
|
||||
// RDAP Response Profile doesn't mention status for contacts, so we only show it if we're both
|
||||
// FULL and Authorized.
|
||||
if (outputDataType == OutputDataType.FULL && isAuthorized) {
|
||||
// RDAP Response Profile 2.7.5.1, 2.7.5.3:
|
||||
// email MUST be omitted, and we MUST have a Remark saying so
|
||||
contactBuilder
|
||||
.remarksBuilder()
|
||||
.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
|
||||
// only for direct query responses and not for internal objects. I'm not sure why it's in that
|
||||
// section at all...
|
||||
contactBuilder.setLastUpdateOfRdapDatabaseEvent(
|
||||
Event.builder()
|
||||
.setEventAction(EventAction.LAST_UPDATE_OF_RDAP_DATABASE)
|
||||
.setEventDate(getRequestTime())
|
||||
.build());
|
||||
}
|
||||
return contactBuilder.build();
|
||||
}
|
||||
|
||||
private void fillRdapContactEntityWhenAuthorized(
|
||||
RdapContactEntity.Builder contactBuilder,
|
||||
VcardArray.Builder vcardBuilder,
|
||||
Contact contact,
|
||||
OutputDataType outputDataType) {
|
||||
// ROID needs to be redacted if we aren't authorized, so we can't have a self-link for
|
||||
// unauthorized users
|
||||
contactBuilder.linksBuilder().add(makeSelfLink("entity", contact.getRepoId()));
|
||||
// RDAP Response Profile 2.7.3 - we MUST provide a handle set with the ROID, subject to
|
||||
// redaction.
|
||||
contactBuilder.setHandle(contact.getRepoId());
|
||||
if (outputDataType.equals(OutputDataType.FULL)) {
|
||||
// RDAP Response Profile doesn't mention status for contacts, so we only show it if we're both
|
||||
// FULL and Authorized.
|
||||
contactBuilder
|
||||
.statusBuilder()
|
||||
.addAll(
|
||||
@@ -591,12 +588,18 @@ public class RdapJsonFormatter {
|
||||
: contact.getStatusValues(),
|
||||
false,
|
||||
contact.getDeletionTime().isBefore(getRequestTime())));
|
||||
// If we are outputting all data (not just summary data), also add events taken from the
|
||||
// history entries. This isn't strictly required.
|
||||
//
|
||||
// We also only add it for authorized users because millisecond times can fingerprint a user
|
||||
// just as much as the handle can.
|
||||
contactBuilder.eventsBuilder().addAll(makeOptionalEvents(contact));
|
||||
} else {
|
||||
// Only show the "summary data remark" if the user is authorized to see this data - because
|
||||
// unauthorized users don't have a self link meaning they can't navigate to the full data.
|
||||
contactBuilder.remarksBuilder().add(RdapIcannStandardInformation.SUMMARY_DATA_REMARK);
|
||||
}
|
||||
|
||||
contactBuilder.rolesBuilder().addAll(roles);
|
||||
|
||||
VcardArray.Builder vcardBuilder = VcardArray.builder();
|
||||
// Adding the VCard members subject to the redaction above.
|
||||
// Adding the VCard members when not redacted.
|
||||
//
|
||||
// RDAP Response Profile 2.7.3 - we MUST have FN, ADR, TEL, EMAIL.
|
||||
//
|
||||
@@ -622,33 +625,6 @@ public class RdapJsonFormatter {
|
||||
if (faxPhoneNumber != null) {
|
||||
vcardBuilder.add(makePhoneEntry(PHONE_TYPE_FAX, makePhoneString(faxPhoneNumber)));
|
||||
}
|
||||
// RDAP Response Profile 2.7.5.1, 2.7.5.3:
|
||||
// email MUST be omitted, and we MUST have a Remark saying so
|
||||
contactBuilder
|
||||
.remarksBuilder()
|
||||
.add(RdapIcannStandardInformation.CONTACT_EMAIL_REDACTED_FOR_DOMAIN);
|
||||
contactBuilder.setVcardArray(vcardBuilder.build());
|
||||
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
// Rdap Response Profile 2.7.6 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(
|
||||
Event.builder()
|
||||
.setEventAction(EventAction.LAST_UPDATE_OF_RDAP_DATABASE)
|
||||
.setEventDate(getRequestTime())
|
||||
.build());
|
||||
}
|
||||
|
||||
// If we are outputting all data (not just summary data), also add events taken from the history
|
||||
// entries. This isn't strictly required.
|
||||
//
|
||||
// We also only add it for authorized users because millisecond times can fingerprint a user
|
||||
// just as much as the handle can.
|
||||
if (outputDataType == OutputDataType.FULL && isAuthorized) {
|
||||
contactBuilder.eventsBuilder().addAll(makeOptionalEvents(contact));
|
||||
}
|
||||
return contactBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1000,7 +976,7 @@ public class RdapJsonFormatter {
|
||||
// Gustavo Lozano of ICANN, the one we should use is an embedded array of street address lines
|
||||
// if there is more than one line:
|
||||
//
|
||||
// RFC7095 provides two examples of structured addresses, and one of the examples shows a
|
||||
// RFC 7095 provides two examples of structured addresses, and one of the examples shows a
|
||||
// street JSON element that contains several data elements. The example showing (see below)
|
||||
// several data elements is the expected output when two or more <contact:street> elements
|
||||
// exists in the contact object.
|
||||
|
||||
@@ -44,7 +44,7 @@ public class AuthModule {
|
||||
// See: https://cloud.google.com/iap/docs/signed-headers-howto#verifying_the_jwt_payload
|
||||
private static final String IAP_AUDIENCE_FORMAT = "/projects/%d/apps/%s";
|
||||
private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap";
|
||||
private static final String SA_ISSUER_URL = "https://accounts.google.com";
|
||||
private static final String REGULAR_ISSUER_URL = "https://accounts.google.com";
|
||||
|
||||
/** Provides the custom authentication mechanisms (including OAuth and OIDC). */
|
||||
@Provides
|
||||
@@ -82,8 +82,8 @@ public class AuthModule {
|
||||
@Provides
|
||||
@RegularOidc
|
||||
@Singleton
|
||||
TokenVerifier provideRegularTokenVerifier(@Config("projectId") String projectId) {
|
||||
return TokenVerifier.newBuilder().setAudience(projectId).setIssuer(SA_ISSUER_URL).build();
|
||||
TokenVerifier provideRegularTokenVerifier(@Config("oauthClientId") String clientId) {
|
||||
return TokenVerifier.newBuilder().setAudience(clientId).setIssuer(REGULAR_ISSUER_URL).build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -32,7 +32,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
/**
|
||||
* OAuth authentication mechanism, using the OAuthService interface.
|
||||
*
|
||||
* Only OAuth version 2 is supported.
|
||||
* <p>Only OAuth version 2 is supported.
|
||||
*/
|
||||
public class OAuthAuthenticationMechanism implements AuthenticationMechanism {
|
||||
|
||||
|
||||
+11
-6
@@ -19,7 +19,7 @@ import static google.registry.request.auth.AuthSettings.AuthLevel.APP;
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
@@ -57,10 +57,10 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
|
||||
protected final TokenExtractor tokenExtractor;
|
||||
|
||||
private final ImmutableList<String> serviceAccountEmails;
|
||||
private final ImmutableSet<String> serviceAccountEmails;
|
||||
|
||||
protected OidcTokenAuthenticationMechanism(
|
||||
ImmutableList<String> serviceAccountEmails,
|
||||
ImmutableSet<String> serviceAccountEmails,
|
||||
TokenVerifier tokenVerifier,
|
||||
TokenExtractor tokenExtractor) {
|
||||
this.serviceAccountEmails = serviceAccountEmails;
|
||||
@@ -83,7 +83,11 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
try {
|
||||
token = tokenVerifier.verify(rawIdToken);
|
||||
} catch (Exception e) {
|
||||
logger.atInfo().withCause(e).log("Error when verifying access token");
|
||||
logger.atInfo().withCause(e).log(
|
||||
"Failed OIDC verification attempt:\n%s",
|
||||
RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION)
|
||||
? "Raw token redacted in prod"
|
||||
: rawIdToken);
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
String email = (String) token.getPayload().get("email");
|
||||
@@ -95,6 +99,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
if (maybeUser.isPresent()) {
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(maybeUser.get()));
|
||||
}
|
||||
// TODO: implement caching so we don't have to look up the database for every request.
|
||||
logger.atInfo().log("No end user found for email address %s", email);
|
||||
if (serviceAccountEmails.stream().anyMatch(e -> e.equals(email))) {
|
||||
return AuthResult.create(APP);
|
||||
@@ -136,7 +141,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
|
||||
@Inject
|
||||
protected IapOidcAuthenticationMechanism(
|
||||
@Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails,
|
||||
@Config("allowedServiceAccountEmails") ImmutableSet<String> serviceAccountEmails,
|
||||
@IapOidc TokenVerifier tokenVerifier,
|
||||
@IapOidc TokenExtractor tokenExtractor) {
|
||||
super(serviceAccountEmails, tokenVerifier, tokenExtractor);
|
||||
@@ -164,7 +169,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
|
||||
@Inject
|
||||
protected RegularOidcAuthenticationMechanism(
|
||||
@Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails,
|
||||
@Config("allowedServiceAccountEmails") ImmutableSet<String> serviceAccountEmails,
|
||||
@RegularOidc TokenVerifier tokenVerifier,
|
||||
@RegularOidc TokenExtractor tokenExtractor) {
|
||||
super(serviceAccountEmails, tokenVerifier, tokenExtractor);
|
||||
|
||||
@@ -75,6 +75,11 @@ class CurlCommand implements CommandWithConnection {
|
||||
required = true)
|
||||
private Service service;
|
||||
|
||||
@Parameter(
|
||||
names = {"--canary"},
|
||||
description = "If set, use the canary end-point; otherwise use the regular end-point.")
|
||||
private Boolean canary = Boolean.FALSE;
|
||||
|
||||
@Override
|
||||
public void setConnection(ServiceConnection connection) {
|
||||
this.connection = connection;
|
||||
@@ -90,7 +95,7 @@ class CurlCommand implements CommandWithConnection {
|
||||
throw new IllegalArgumentException("You may not specify a body for a get method.");
|
||||
}
|
||||
|
||||
ServiceConnection connectionToService = connection.withService(service);
|
||||
ServiceConnection connectionToService = connection.withService(service, canary);
|
||||
String response =
|
||||
(method == Method.GET)
|
||||
? connectionToService.sendGetRequest(path, ImmutableMap.<String, String>of())
|
||||
|
||||
@@ -14,23 +14,17 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
import com.google.api.client.http.HttpRequest;
|
||||
import static com.google.common.net.HttpHeaders.PROXY_AUTHORIZATION;
|
||||
|
||||
import com.google.api.client.http.HttpRequestFactory;
|
||||
import com.google.api.client.http.HttpResponse;
|
||||
import com.google.api.client.http.UrlEncodedContent;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.util.GenericData;
|
||||
import com.google.auth.oauth2.UserCredentials;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.config.CredentialModule.ApplicationDefaultCredential;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Optional;
|
||||
import google.registry.util.OidcTokenUtils;
|
||||
|
||||
/**
|
||||
* Module for providing the HttpRequestFactory.
|
||||
@@ -39,25 +33,16 @@ import java.util.Optional;
|
||||
* connections in that they don't require OAuth2 credentials, but instead require a special cookie.
|
||||
*/
|
||||
@Module
|
||||
class RequestFactoryModule {
|
||||
final class RequestFactoryModule {
|
||||
|
||||
static final int REQUEST_TIMEOUT_MS = 10 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Server to use if we want to manually request an IAP ID token
|
||||
*
|
||||
* <p>If we need to have an IAP-enabled audience, we can use the existing refresh token and the
|
||||
* IAP client ID audience to request an IAP-enabled ID token. This token is read and used by
|
||||
* {@link IapHeaderAuthenticationMechanismMechanism}, and it requires that the user have a {@link
|
||||
* google.registry.model.console.User} object present in the database.
|
||||
*/
|
||||
private static final GenericUrl TOKEN_SERVER_URL =
|
||||
new GenericUrl(URI.create("https://oauth2.googleapis.com/token"));
|
||||
private RequestFactoryModule() {}
|
||||
|
||||
@Provides
|
||||
static HttpRequestFactory provideHttpRequestFactory(
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("iapClientId") Optional<String> iapClientId) {
|
||||
@Config("oauthClientId") String oauthClientId) {
|
||||
if (RegistryConfig.areServersLocal()) {
|
||||
return new NetHttpTransport()
|
||||
.createRequestFactory(
|
||||
@@ -71,14 +56,13 @@ class RequestFactoryModule {
|
||||
request -> {
|
||||
// Use the standard credential initializer to set the Authorization header
|
||||
credentialsBundle.getHttpRequestInitializer().initialize(request);
|
||||
// If using IAP, use the refresh token to acquire an IAP-enabled ID token and use
|
||||
// that for authentication.
|
||||
if (iapClientId.isPresent()) {
|
||||
String idToken = getIdToken(credentialsBundle, iapClientId.get());
|
||||
// Set the Proxy-Authentication header so that IAP can read from it, see
|
||||
// https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_proxy-authorization_header
|
||||
request.getHeaders().set("Proxy-Authorization", "Bearer " + idToken);
|
||||
}
|
||||
// Set OIDC token as the alternative bearer token.
|
||||
request
|
||||
.getHeaders()
|
||||
.set(
|
||||
PROXY_AUTHORIZATION,
|
||||
"Bearer "
|
||||
+ OidcTokenUtils.createOidcToken(credentialsBundle, oauthClientId));
|
||||
// GAE request times out after 10 min, so here we set the timeout to 10 min. This is
|
||||
// needed to support some nomulus commands like updating premium lists that take
|
||||
// a lot of time to complete.
|
||||
@@ -89,32 +73,4 @@ class RequestFactoryModule {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the saved desktop-app refresh token to acquire an IAP ID token.
|
||||
*
|
||||
* <p>This is lifted mostly from the Google Auth Library's {@link UserCredentials}
|
||||
* "doRefreshAccessToken" method (which is private and thus inaccessible) while adding in the
|
||||
* audience of the IAP client ID. That addition of the audience is what allows us to satisfy IAP
|
||||
* auth. See
|
||||
* https://cloud.google.com/iap/docs/authentication-howto#authenticating_from_a_desktop_app for
|
||||
* more details.
|
||||
*/
|
||||
private static String getIdToken(GoogleCredentialsBundle credentialsBundle, String iapClientId)
|
||||
throws IOException {
|
||||
UserCredentials credentials = (UserCredentials) credentialsBundle.getGoogleCredentials();
|
||||
GenericData tokenRequest = new GenericData();
|
||||
tokenRequest.set("client_id", credentials.getClientId());
|
||||
tokenRequest.set("client_secret", credentials.getClientSecret());
|
||||
tokenRequest.set("refresh_token", credentials.getRefreshToken());
|
||||
tokenRequest.set("audience", iapClientId);
|
||||
tokenRequest.set("grant_type", "refresh_token");
|
||||
UrlEncodedContent content = new UrlEncodedContent(tokenRequest);
|
||||
|
||||
HttpRequestFactory requestFactory = credentialsBundle.getHttpTransport().createRequestFactory();
|
||||
HttpRequest request = requestFactory.buildPostRequest(TOKEN_SERVER_URL, content);
|
||||
request.setParser(credentialsBundle.getJsonFactory().createJsonObjectParser());
|
||||
HttpResponse response = request.execute();
|
||||
return response.parseAs(GenericData.class).get("id_token").toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.common.base.Verify.verify;
|
||||
import static com.google.common.net.HttpHeaders.X_REQUESTED_WITH;
|
||||
import static com.google.common.net.MediaType.JSON_UTF_8;
|
||||
import static google.registry.security.JsonHttp.JSON_SAFETY_PREFIX;
|
||||
@@ -26,6 +28,7 @@ import com.google.api.client.http.HttpHeaders;
|
||||
import com.google.api.client.http.HttpRequest;
|
||||
import com.google.api.client.http.HttpRequestFactory;
|
||||
import com.google.api.client.http.HttpResponse;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.CharStreams;
|
||||
@@ -36,6 +39,7 @@ import google.registry.config.RegistryConfig;
|
||||
import google.registry.request.Action.Service;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -55,20 +59,23 @@ public class ServiceConnection {
|
||||
|
||||
@Inject HttpRequestFactory requestFactory;
|
||||
private final Service service;
|
||||
private final boolean useCanary;
|
||||
|
||||
@Inject
|
||||
ServiceConnection() {
|
||||
service = Service.TOOLS;
|
||||
useCanary = false;
|
||||
}
|
||||
|
||||
private ServiceConnection(Service service, HttpRequestFactory requestFactory) {
|
||||
private ServiceConnection(Service service, HttpRequestFactory requestFactory, boolean useCanary) {
|
||||
this.service = service;
|
||||
this.requestFactory = requestFactory;
|
||||
this.useCanary = useCanary;
|
||||
}
|
||||
|
||||
/** Returns a copy of this connection that talks to a different service. */
|
||||
public ServiceConnection withService(Service service) {
|
||||
return new ServiceConnection(service, requestFactory);
|
||||
/** Returns a copy of this connection that talks to a different service endpoint. */
|
||||
public ServiceConnection withService(Service service, boolean isCanary) {
|
||||
return new ServiceConnection(service, requestFactory, isCanary);
|
||||
}
|
||||
|
||||
/** Returns the contents of the title tag in the given HTML, or null if not found. */
|
||||
@@ -85,7 +92,7 @@ public class ServiceConnection {
|
||||
private String internalSend(
|
||||
String endpoint, Map<String, ?> params, MediaType contentType, @Nullable byte[] payload)
|
||||
throws IOException {
|
||||
GenericUrl url = new GenericUrl(String.format("%s%s", getServer(service), endpoint));
|
||||
GenericUrl url = new GenericUrl(String.format("%s%s", getServer(), endpoint));
|
||||
url.putAll(params);
|
||||
HttpRequest request =
|
||||
(payload != null)
|
||||
@@ -120,6 +127,20 @@ public class ServiceConnection {
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
URL getServer() {
|
||||
URL url = getServer(service);
|
||||
if (useCanary) {
|
||||
verify(!isNullOrEmpty(url.getHost()), "Null host in url");
|
||||
try {
|
||||
return new URL(url.getProtocol(), "nomulus-dot-" + url.getHost(), url.getFile());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
public String sendPostRequest(
|
||||
String endpoint, Map<String, ?> params, MediaType contentType, byte[] payload)
|
||||
throws IOException {
|
||||
|
||||
@@ -24,7 +24,6 @@ import google.registry.model.tld.label.PremiumList;
|
||||
import google.registry.model.tld.label.PremiumListDao;
|
||||
import google.registry.model.tld.label.PremiumListUtils;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Command to safely update {@link PremiumList} in Database for a given TLD. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a PremiumList in Database.")
|
||||
@@ -33,16 +32,19 @@ class UpdatePremiumListCommand extends CreateOrUpdatePremiumListCommand {
|
||||
@Override
|
||||
protected String prompt() throws Exception {
|
||||
name = Strings.isNullOrEmpty(name) ? convertFilePathToName(inputFile) : name;
|
||||
Optional<PremiumList> list = PremiumListDao.getLatestRevision(name);
|
||||
checkArgument(
|
||||
list.isPresent(),
|
||||
String.format("Could not update premium list %s because it doesn't exist.", name));
|
||||
PremiumList existingList =
|
||||
PremiumListDao.getLatestRevision(name)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalArgumentException(
|
||||
String.format(
|
||||
"Could not update premium list %s because it doesn't exist", name)));
|
||||
inputData = Files.readAllLines(inputFile, UTF_8);
|
||||
checkArgument(!inputData.isEmpty(), "New premium list data cannot be empty");
|
||||
currency = list.get().getCurrency();
|
||||
currency = existingList.getCurrency();
|
||||
PremiumList updatedPremiumList = PremiumListUtils.parseToPremiumList(name, currency, inputData);
|
||||
return String.format(
|
||||
"Update premium list for %s?\n Old List: %s\n New List: %s",
|
||||
name, list, updatedPremiumList);
|
||||
name, existingList, updatedPremiumList);
|
||||
}
|
||||
}
|
||||
|
||||
+80
-41
@@ -15,20 +15,27 @@
|
||||
package google.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.Iterables.getLast;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLDS;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.persistence.TypedQuery;
|
||||
import org.apache.arrow.util.VisibleForTesting;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
@@ -43,10 +50,8 @@ import org.joda.time.Duration;
|
||||
* run internally, or by pretending to be internal by setting the X-AppEngine-QueueName header,
|
||||
* which only admin users can do.
|
||||
*
|
||||
* <p>You must pass in a number of {@code smearMinutes} as a URL parameter so that the DNS queue
|
||||
* doesn't get overloaded. A rough rule of thumb for Cloud DNS is 1 minute per every 1,000 domains.
|
||||
* This smears the updates out over the next N minutes. For small TLDs consisting of fewer than
|
||||
* 1,000 domains, passing in 1 is fine (which will execute all the updates immediately).
|
||||
* <p>You may pass in a {@code batchSize} for the batched read of domains from the database. This is
|
||||
* recommended to be somewhere between 200 and 500. The default value is 250.
|
||||
*/
|
||||
@Action(
|
||||
service = Action.Service.TOOLS,
|
||||
@@ -56,47 +61,81 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@Inject Response response;
|
||||
private static final int DEFAULT_BATCH_SIZE = 250;
|
||||
|
||||
private final Response response;
|
||||
private final ImmutableSet<String> tlds;
|
||||
|
||||
// Recommended value for batch size is between 200 and 500
|
||||
private final int batchSize;
|
||||
private final Random random;
|
||||
|
||||
@Inject
|
||||
@Parameter(PARAM_TLDS)
|
||||
ImmutableSet<String> tlds;
|
||||
|
||||
@Inject
|
||||
@Parameter("smearMinutes")
|
||||
int smearMinutes;
|
||||
|
||||
@Inject Clock clock;
|
||||
@Inject Random random;
|
||||
|
||||
@Inject
|
||||
RefreshDnsForAllDomainsAction() {}
|
||||
RefreshDnsForAllDomainsAction(
|
||||
Response response,
|
||||
@Parameter(PARAM_TLDS) ImmutableSet<String> tlds,
|
||||
@Parameter("batchSize") Optional<Integer> batchSize,
|
||||
Random random) {
|
||||
this.response = response;
|
||||
this.tlds = tlds;
|
||||
this.batchSize = batchSize.orElse(DEFAULT_BATCH_SIZE);
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
assertTldsExist(tlds);
|
||||
checkArgument(smearMinutes > 0, "Must specify a positive number of smear minutes");
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().query(
|
||||
"SELECT domainName FROM Domain "
|
||||
+ "WHERE tld IN (:tlds) "
|
||||
+ "AND deletionTime > :now",
|
||||
String.class)
|
||||
.setParameter("tlds", tlds)
|
||||
.setParameter("now", clock.nowUtc())
|
||||
.getResultStream()
|
||||
.forEach(
|
||||
domainName -> {
|
||||
try {
|
||||
// Smear the task execution time over the next N minutes.
|
||||
requestDomainDnsRefresh(
|
||||
domainName, Duration.standardMinutes(random.nextInt(smearMinutes)));
|
||||
} catch (Throwable t) {
|
||||
logger.atSevere().withCause(t).log(
|
||||
"Error while enqueuing DNS refresh for domain '%s'.", domainName);
|
||||
response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}));
|
||||
checkArgument(batchSize > 0, "Must specify a positive number for batch size");
|
||||
int smearMinutes = tm().transact(this::calculateSmearMinutes);
|
||||
|
||||
ImmutableList<String> domainsBatch;
|
||||
@Nullable String lastInPreviousBatch = null;
|
||||
do {
|
||||
Optional<String> lastInPreviousBatchOpt = Optional.ofNullable(lastInPreviousBatch);
|
||||
domainsBatch = tm().transact(() -> refreshBatch(lastInPreviousBatchOpt, smearMinutes));
|
||||
lastInPreviousBatch = domainsBatch.isEmpty() ? null : getLast(domainsBatch);
|
||||
} while (domainsBatch.size() == batchSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of smear minutes to enqueue refreshes so that the DNS queue does not get
|
||||
* overloaded.
|
||||
*/
|
||||
private int calculateSmearMinutes() {
|
||||
Long activeDomains =
|
||||
tm().query(
|
||||
"SELECT COUNT(*) FROM Domain WHERE tld IN (:tlds) AND deletionTime = :endOfTime",
|
||||
Long.class)
|
||||
.setParameter("tlds", tlds)
|
||||
.setParameter("endOfTime", END_OF_TIME)
|
||||
.getSingleResult();
|
||||
return Math.max(activeDomains.intValue() / 1000, 1);
|
||||
}
|
||||
|
||||
private ImmutableList<String> getBatch(Optional<String> lastInPreviousBatch) {
|
||||
String sql =
|
||||
String.format(
|
||||
"SELECT domainName FROM Domain WHERE tld IN (:tlds) AND"
|
||||
+ " deletionTime = :endOfTime %s ORDER BY domainName ASC",
|
||||
lastInPreviousBatch.isPresent() ? "AND domainName > :lastInPreviousBatch" : "");
|
||||
TypedQuery<String> query =
|
||||
tm().query(sql, String.class)
|
||||
.setParameter("tlds", tlds)
|
||||
.setParameter("endOfTime", END_OF_TIME);
|
||||
lastInPreviousBatch.ifPresent(l -> query.setParameter("lastInPreviousBatch", l));
|
||||
return query.setMaxResults(batchSize).getResultStream().collect(toImmutableList());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ImmutableList<String> refreshBatch(Optional<String> lastInPreviousBatch, int smearMinutes) {
|
||||
ImmutableList<String> domainBatch = getBatch(lastInPreviousBatch);
|
||||
try {
|
||||
// Smear the task execution time over the next N minutes.
|
||||
requestDomainDnsRefresh(domainBatch, Duration.standardMinutes(random.nextInt(smearMinutes)));
|
||||
} catch (Throwable t) {
|
||||
logger.atSevere().withCause(t).log("Error while enqueuing DNS refresh batch");
|
||||
response.setStatus(HttpStatus.SC_OK);
|
||||
}
|
||||
return domainBatch;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.tools.server;
|
||||
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static google.registry.request.RequestParameters.extractIntParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalIntParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalParameter;
|
||||
import static google.registry.request.RequestParameters.extractRequiredParameter;
|
||||
|
||||
@@ -76,8 +77,8 @@ public class ToolsServerModule {
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("smearMinutes")
|
||||
static int provideSmearMinutes(HttpServletRequest req) {
|
||||
return extractIntParameter(req, "smearMinutes");
|
||||
@Parameter("batchSize")
|
||||
static Optional<Integer> provideBatchSize(HttpServletRequest req) {
|
||||
return extractOptionalIntParameter(req, "batchSize");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class AsyncTaskEnqueuerTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new CloudTasksHelper.TaskMatcher()
|
||||
.url(ResaveEntityAction.PATH)
|
||||
.path(ResaveEntityAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
@@ -93,7 +93,7 @@ public class AsyncTaskEnqueuerTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(ResaveEntityAction.PATH)
|
||||
.path(ResaveEntityAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.LinkedListMultimap;
|
||||
import google.registry.batch.CloudTasksUtils.SerializableCloudTasksClient;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.testing.CloudTasksHelper.FakeGoogleCredentialsBundle;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeSleeper;
|
||||
import google.registry.util.Retrier;
|
||||
@@ -47,14 +48,14 @@ public class CloudTasksUtilsTest {
|
||||
private final LinkedListMultimap<String, String> params = LinkedListMultimap.create();
|
||||
private final SerializableCloudTasksClient mockClient = mock(SerializableCloudTasksClient.class);
|
||||
private final FakeClock clock = new FakeClock(DateTime.parse("2021-11-08"));
|
||||
private CloudTasksUtils cloudTasksUtils =
|
||||
private final CloudTasksUtils cloudTasksUtils =
|
||||
new CloudTasksUtils(
|
||||
new Retrier(new FakeSleeper(clock), 1),
|
||||
clock,
|
||||
"project",
|
||||
"location",
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
"clientId",
|
||||
FakeGoogleCredentialsBundle.create(),
|
||||
mockClient);
|
||||
|
||||
@BeforeEach
|
||||
@@ -66,208 +67,6 @@ public class CloudTasksUtilsTest {
|
||||
.thenAnswer(invocation -> invocation.getArgument(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, params);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks() {
|
||||
Task task = cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, params);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withNullParams() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, null);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withNullParams() {
|
||||
Task task = cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, null);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8)).isEmpty();
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withEmptyParams() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, ImmutableMultimap.of());
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withEmptyParams() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, ImmutableMultimap.of());
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8)).isEmpty();
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ProtoTimestampGetSecondsGetNano")
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(100));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
|
||||
Instant scheduleTime = Instant.ofEpochSecond(task.getScheduleTime().getSeconds());
|
||||
Instant lowerBoundTime = Instant.ofEpochMilli(clock.nowUtc().getMillis());
|
||||
Instant upperBound = Instant.ofEpochMilli(clock.nowUtc().plusSeconds(100).getMillis());
|
||||
|
||||
assertThat(scheduleTime.isBefore(lowerBoundTime)).isFalse();
|
||||
assertThat(upperBound.isBefore(scheduleTime)).isFalse();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ProtoTimestampGetSecondsGetNano")
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(1));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isNotEqualTo(0);
|
||||
|
||||
Instant scheduleTime = Instant.ofEpochSecond(task.getScheduleTime().getSeconds());
|
||||
Instant lowerBoundTime = Instant.ofEpochMilli(clock.nowUtc().getMillis());
|
||||
Instant upperBound = Instant.ofEpochMilli(clock.nowUtc().plusSeconds(1).getMillis());
|
||||
|
||||
assertThat(scheduleTime.isBefore(lowerBoundTime)).isFalse();
|
||||
assertThat(upperBound.isBefore(scheduleTime)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withEmptyJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.empty());
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withEmptyJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.empty());
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withZeroJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(0));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withZeroJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(0));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.standardMinutes(10));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(Instant.ofEpochSecond(task.getScheduleTime().getSeconds()))
|
||||
.isEqualTo(Instant.ofEpochMilli(clock.nowUtc().plusMinutes(10).getMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.standardMinutes(10));
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isNotEqualTo(0);
|
||||
assertThat(Instant.ofEpochSecond(task.getScheduleTime().getSeconds()))
|
||||
.isEqualTo(Instant.ofEpochMilli(clock.nowUtc().plusMinutes(10).getMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_createGetTasks_withNegativeDelay() {
|
||||
IllegalArgumentException thrown =
|
||||
@@ -290,34 +89,6 @@ public class CloudTasksUtilsTest {
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Negative duration is not supported.");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createPostTasks_withZeroDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.ZERO);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri()).isEqualTo("/the/path");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getAppEngineHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
.isEqualTo("key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createGetTasks_withZeroDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithDelay("/the/path", Service.BACKEND, params, Duration.ZERO);
|
||||
assertThat(task.getAppEngineHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getAppEngineHttpRequest().getRelativeUri())
|
||||
.isEqualTo("/the/path?key1=val1&key2=val2&key1=val3");
|
||||
assertThat(task.getAppEngineHttpRequest().getAppEngineRouting().getService())
|
||||
.isEqualTo(Service.BACKEND.toString());
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_illegalPath() {
|
||||
assertThrows(
|
||||
@@ -357,22 +128,20 @@ public class CloudTasksUtilsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, params);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks() {
|
||||
Task task = cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, params);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
@@ -382,43 +151,39 @@ public class CloudTasksUtilsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withNullParams() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withNullParams() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, null);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withNullParams() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withNullParams() {
|
||||
Task task = cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, null);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8)).isEmpty();
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withEmptyParams() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withEmptyParams() {
|
||||
Task task = cloudTasksUtils.createGetTask("/the/path", Service.BACKEND, ImmutableMultimap.of());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withEmptyParams() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withEmptyParams() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTask("/the/path", Service.BACKEND, ImmutableMultimap.of());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8)).isEmpty();
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
@@ -426,14 +191,13 @@ public class CloudTasksUtilsTest {
|
||||
|
||||
@SuppressWarnings("ProtoTimestampGetSecondsGetNano")
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(100));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
|
||||
Instant scheduleTime = Instant.ofEpochSecond(task.getScheduleTime().getSeconds());
|
||||
@@ -446,13 +210,12 @@ public class CloudTasksUtilsTest {
|
||||
|
||||
@SuppressWarnings("ProtoTimestampGetSecondsGetNano")
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(1));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
@@ -469,13 +232,12 @@ public class CloudTasksUtilsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withEmptyJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withEmptyJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.empty());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
@@ -485,26 +247,24 @@ public class CloudTasksUtilsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withEmptyJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withEmptyJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.empty());
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withZeroJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withZeroJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(0));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
@@ -514,40 +274,37 @@ public class CloudTasksUtilsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withZeroJitterSeconds() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withZeroJitterSeconds() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithJitter(
|
||||
"/the/path", Service.BACKEND, params, Optional.of(0));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withDelay() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.standardMinutes(10));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(Instant.ofEpochSecond(task.getScheduleTime().getSeconds()))
|
||||
.isEqualTo(Instant.ofEpochMilli(clock.nowUtc().plusMinutes(10).getMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withDelay() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.standardMinutes(10));
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
@@ -559,13 +316,12 @@ public class CloudTasksUtilsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createPostTasks_withZeroDelay() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createPostTasks_withZeroDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createPostTaskWithDelay(
|
||||
"/the/path", Service.BACKEND, params, Duration.ZERO);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.POST);
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://localhost/the/path");
|
||||
assertThat(task.getHttpRequest().getUrl()).isEqualTo("https://backend.example.com/the/path");
|
||||
assertThat(task.getHttpRequest().getHeadersMap().get("Content-Type"))
|
||||
.isEqualTo("application/x-www-form-urlencoded");
|
||||
assertThat(task.getHttpRequest().getBody().toString(StandardCharsets.UTF_8))
|
||||
@@ -575,35 +331,22 @@ public class CloudTasksUtilsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_nonAppEngine_createGetTasks_withZeroDelay() {
|
||||
createOidcTasksUtils();
|
||||
void testSuccess_createGetTasks_withZeroDelay() {
|
||||
Task task =
|
||||
cloudTasksUtils.createGetTaskWithDelay("/the/path", Service.BACKEND, params, Duration.ZERO);
|
||||
assertThat(task.getHttpRequest().getHttpMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(task.getHttpRequest().getUrl())
|
||||
.isEqualTo("https://localhost/the/path?key1=val1&key2=val2&key1=val3");
|
||||
.isEqualTo("https://backend.example.com/the/path?key1=val1&key2=val2&key1=val3");
|
||||
verifyOidcToken(task);
|
||||
assertThat(task.getScheduleTime().getSeconds()).isEqualTo(0);
|
||||
}
|
||||
|
||||
private void createOidcTasksUtils() {
|
||||
cloudTasksUtils =
|
||||
new CloudTasksUtils(
|
||||
new Retrier(new FakeSleeper(clock), 1),
|
||||
clock,
|
||||
"project",
|
||||
"location",
|
||||
Optional.of("defaultServiceAccount"),
|
||||
Optional.of("iapClientId"),
|
||||
mockClient);
|
||||
}
|
||||
|
||||
private void verifyOidcToken(Task task) {
|
||||
private static void verifyOidcToken(Task task) {
|
||||
assertThat(task.getHttpRequest().getOidcToken())
|
||||
.isEqualTo(
|
||||
OidcToken.newBuilder()
|
||||
.setServiceAccountEmail("defaultServiceAccount")
|
||||
.setAudience("iapClientId")
|
||||
.setServiceAccountEmail("service@account.com")
|
||||
.setAudience("clientId")
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ public class RelockDomainActionTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(RelockDomainAction.PATH)
|
||||
.path(RelockDomainAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.param(
|
||||
RelockDomainAction.OLD_UNLOCK_REVISION_ID_PARAM,
|
||||
|
||||
@@ -136,7 +136,7 @@ public class ResaveEntityActionTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(ResaveEntityAction.PATH)
|
||||
.path(ResaveEntityAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
|
||||
@@ -464,7 +464,7 @@ public class RdePipelineTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"brda",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/brdaCopy")
|
||||
.path("/_dr/task/brdaCopy")
|
||||
.service("backend")
|
||||
.param("tld", "soy")
|
||||
.param("watermark", now.toString())
|
||||
@@ -472,7 +472,7 @@ public class RdePipelineTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-upload",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeUpload")
|
||||
.path("/_dr/task/rdeUpload")
|
||||
.service("backend")
|
||||
.param("tld", "soy")
|
||||
.param("prefix", "rde-job/"));
|
||||
|
||||
@@ -91,14 +91,14 @@ class TldFanoutActionTest {
|
||||
.map(
|
||||
namespace ->
|
||||
new TaskMatcher()
|
||||
.url(ENDPOINT)
|
||||
.path(ENDPOINT)
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
.param("tld", namespace))
|
||||
.collect(toImmutableList()));
|
||||
}
|
||||
|
||||
private void assertTaskWithoutTld() {
|
||||
cloudTasksHelper.assertTasksEnqueued(QUEUE, new TaskMatcher().url(ENDPOINT));
|
||||
cloudTasksHelper.assertTasksEnqueued(QUEUE, new TaskMatcher().path(ENDPOINT));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -211,7 +211,7 @@ class TldFanoutActionTest {
|
||||
void testSuccess_additionalArgsFlowThroughToPostParams() {
|
||||
run(getParamsMap("forEachTestTld", "", "newkey", "newval"));
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE, new TaskMatcher().url("/the/servlet").param("newkey", "newval"));
|
||||
QUEUE, new TaskMatcher().path("/the/servlet").param("newkey", "newval"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -224,9 +224,9 @@ class TldFanoutActionTest {
|
||||
String expectedResponse =
|
||||
String.format(
|
||||
"OK: Launched the following 3 tasks in queue the-queue\n"
|
||||
+ "- Task: '%s', tld: 'com', endpoint: '/the/servlet'\n"
|
||||
+ "- Task: '%s', tld: 'net', endpoint: '/the/servlet'\n"
|
||||
+ "- Task: '%s', tld: 'org', endpoint: '/the/servlet'\n",
|
||||
+ "- Task: '%s', tld: 'com', endpoint: 'https://backend.example.com/the/servlet'\n"
|
||||
+ "- Task: '%s', tld: 'net', endpoint: 'https://backend.example.com/the/servlet'\n"
|
||||
+ "- Task: '%s', tld: 'org', endpoint: 'https://backend.example.com/the/servlet'\n",
|
||||
taskList.get(0).getName(), taskList.get(1).getName(), taskList.get(2).getName());
|
||||
assertThat(response.getPayload()).isEqualTo(expectedResponse);
|
||||
}
|
||||
@@ -241,7 +241,7 @@ class TldFanoutActionTest {
|
||||
String expectedResponse =
|
||||
String.format(
|
||||
"OK: Launched the following 1 tasks in queue the-queue\n"
|
||||
+ "- Task: '%s', tld: '', endpoint: '/the/servlet'\n",
|
||||
+ "- Task: '%s', tld: '', endpoint: 'https://backend.example.com/the/servlet'\n",
|
||||
taskList.get(0).getName());
|
||||
assertThat(response.getPayload()).isEqualTo(expectedResponse);
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
@@ -305,7 +305,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
.param(PARAM_HOSTS, "")
|
||||
.header("content-type", "application/x-www-form-urlencoded"),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
@@ -333,7 +333,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
@@ -344,7 +344,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
.param(PARAM_HOSTS, "")
|
||||
.header("content-type", "application/x-www-form-urlencoded"),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
@@ -370,7 +370,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
DNS_PUBLISH_PUSH_QUEUE_NAME,
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
@@ -381,7 +381,7 @@ public class PublishDnsUpdatesActionTest {
|
||||
.param(PARAM_HOSTS, "")
|
||||
.header("content-type", "application/x-www-form-urlencoded"),
|
||||
new TaskMatcher()
|
||||
.url(PublishDnsUpdatesAction.PATH)
|
||||
.path(PublishDnsUpdatesAction.PATH)
|
||||
.param(PARAM_TLD, "xn--q9jyb4c")
|
||||
.param(PARAM_DNS_WRITER, "correctWriter")
|
||||
.param(PARAM_LOCK_INDEX, "1")
|
||||
|
||||
@@ -225,7 +225,7 @@ public class ReadDnsRefreshRequestsActionTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"dns-publish",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/publishDnsUpdates")
|
||||
.path("/_dr/task/publishDnsUpdates")
|
||||
.service("BACKEND")
|
||||
.param("tld", "tld")
|
||||
.param("dnsWriter", "FooWriter")
|
||||
@@ -236,7 +236,7 @@ public class ReadDnsRefreshRequestsActionTest {
|
||||
.param("domains", "domain.tld,future.tld")
|
||||
.param("hosts", "ns1.domain.tld"),
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/publishDnsUpdates")
|
||||
.path("/_dr/task/publishDnsUpdates")
|
||||
.service("BACKEND")
|
||||
.param("tld", "tld")
|
||||
.param("dnsWriter", "BarWriter")
|
||||
|
||||
@@ -276,16 +276,24 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
|
||||
boolean isAnchorTenant = expectedBillingFlags.contains(ANCHOR_TENANT);
|
||||
// Set up the creation cost.
|
||||
BigDecimal createCost =
|
||||
isDomainPremium(getUniqueIdFromCommand(), clock.nowUtc())
|
||||
? BigDecimal.valueOf(200)
|
||||
: BigDecimal.valueOf(26);
|
||||
if (isAnchorTenant) {
|
||||
createCost = BigDecimal.ZERO;
|
||||
}
|
||||
if (expectedBillingFlags.contains(SUNRISE)) {
|
||||
createCost =
|
||||
createCost.multiply(
|
||||
BigDecimal.valueOf(1 - RegistryConfig.getSunriseDomainCreateDiscount()));
|
||||
}
|
||||
FeesAndCredits feesAndCredits =
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
.addFeeOrCredit(
|
||||
Fee.create(
|
||||
isAnchorTenant
|
||||
? BigDecimal.valueOf(0)
|
||||
: isDomainPremium(getUniqueIdFromCommand(), clock.nowUtc())
|
||||
? BigDecimal.valueOf(200)
|
||||
: BigDecimal.valueOf(26),
|
||||
createCost,
|
||||
FeeType.CREATE,
|
||||
isDomainPremium(getUniqueIdFromCommand(), clock.nowUtc())))
|
||||
.build();
|
||||
|
||||
@@ -304,7 +304,7 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(ResaveEntityAction.PATH)
|
||||
.path(ResaveEntityAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
|
||||
@@ -19,6 +19,7 @@ import static google.registry.model.billing.BillingBase.Flag.AUTO_RENEW;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.DEFAULT;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
|
||||
import static google.registry.model.domain.fee.BaseFee.FeeType.CREATE;
|
||||
import static google.registry.model.domain.fee.BaseFee.FeeType.RENEW;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_CREATE;
|
||||
@@ -28,7 +29,6 @@ import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -49,6 +49,7 @@ import google.registry.model.domain.fee.Fee;
|
||||
import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.eppinput.EppInput;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
@@ -72,11 +73,11 @@ public class DomainPricingLogicTest {
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
@Inject Clock clock = new FakeClock(DateTime.now(UTC));
|
||||
@Inject Clock clock = new FakeClock(DateTime.parse("2023-05-13T00:00:00.000Z"));
|
||||
@Mock EppInput eppInput;
|
||||
SessionMetadata sessionMetadata;
|
||||
@Mock FlowMetadata flowMetadata;
|
||||
Tld registry;
|
||||
Tld tld;
|
||||
Domain domain;
|
||||
|
||||
@BeforeEach
|
||||
@@ -86,7 +87,7 @@ public class DomainPricingLogicTest {
|
||||
domainPricingLogic =
|
||||
new DomainPricingLogic(
|
||||
new DomainPricingCustomLogic(eppInput, sessionMetadata, flowMetadata));
|
||||
registry =
|
||||
tld =
|
||||
persistResource(
|
||||
Tld.get("example")
|
||||
.asBuilder()
|
||||
@@ -133,12 +134,32 @@ public class DomainPricingLogicTest {
|
||||
return billingRecurrence;
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainCreatePrice_sunrise_appliesDiscount() throws EppException {
|
||||
ImmutableSortedMap<DateTime, TldState> transitions =
|
||||
ImmutableSortedMap.<DateTime, TldState>naturalOrder()
|
||||
.put(START_OF_TIME, TldState.PREDELEGATION)
|
||||
.put(clock.nowUtc().minusHours(1), TldState.START_DATE_SUNRISE)
|
||||
.put(clock.nowUtc().plusHours(1), TldState.GENERAL_AVAILABILITY)
|
||||
.build();
|
||||
Tld sunriseTld = createTld("sunrise", transitions);
|
||||
assertThat(
|
||||
domainPricingLogic.getCreatePrice(
|
||||
sunriseTld, "domain.sunrise", clock.nowUtc(), 2, false, true, Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
// 13 * 2 * 0.85 == 22.1
|
||||
.addFeeOrCredit(Fee.create(Money.of(USD, 22.1).getAmount(), CREATE, false))
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainRenewPrice_oneYear_standardDomain_noBilling_isStandardPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry, "standard.example", clock.nowUtc(), 1, null, Optional.empty()))
|
||||
tld, "standard.example", clock.nowUtc(), 1, null, Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -151,7 +172,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry, "standard.example", clock.nowUtc(), 5, null, Optional.empty()))
|
||||
tld, "standard.example", clock.nowUtc(), 5, null, Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -164,7 +185,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry, "premium.example", clock.nowUtc(), 1, null, Optional.empty()))
|
||||
tld, "premium.example", clock.nowUtc(), 1, null, Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -177,7 +198,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry, "premium.example", clock.nowUtc(), 5, null, Optional.empty()))
|
||||
tld, "premium.example", clock.nowUtc(), 5, null, Optional.empty()))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -189,7 +210,7 @@ public class DomainPricingLogicTest {
|
||||
void testGetDomainRenewPrice_oneYear_premiumDomain_default_isPremiumPrice() throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -215,7 +236,7 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -244,7 +265,7 @@ public class DomainPricingLogicTest {
|
||||
AllocationTokenInvalidForPremiumNameException.class,
|
||||
() ->
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -256,7 +277,7 @@ public class DomainPricingLogicTest {
|
||||
void testGetDomainRenewPrice_multiYear_premiumDomain_default_isPremiumCost() throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
@@ -283,7 +304,7 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
@@ -313,7 +334,7 @@ public class DomainPricingLogicTest {
|
||||
AllocationTokenInvalidForPremiumNameException.class,
|
||||
() ->
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
@@ -326,7 +347,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -352,7 +373,7 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -370,7 +391,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
@@ -397,7 +418,7 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
@@ -415,7 +436,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -442,7 +463,7 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -460,7 +481,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
@@ -488,7 +509,7 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
@@ -506,7 +527,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -524,7 +545,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
@@ -542,7 +563,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -570,7 +591,7 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -601,7 +622,7 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -626,7 +647,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
@@ -654,7 +675,7 @@ public class DomainPricingLogicTest {
|
||||
.build());
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
@@ -673,7 +694,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
1,
|
||||
@@ -692,7 +713,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
5,
|
||||
@@ -713,15 +734,14 @@ public class DomainPricingLogicTest {
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
domainPricingLogic.getRenewPrice(
|
||||
registry, "standard.example", clock.nowUtc(), -1, null, Optional.empty()));
|
||||
tld, "standard.example", clock.nowUtc(), -1, null, Optional.empty()));
|
||||
assertThat(thrown).hasMessageThat().isEqualTo("Number of years must be positive");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetDomainTransferPrice_standardDomain_default_noBilling_defaultRenewalPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(registry, "standard.example", clock.nowUtc(), null))
|
||||
assertThat(domainPricingLogic.getTransferPrice(tld, "standard.example", clock.nowUtc(), null))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -732,8 +752,7 @@ public class DomainPricingLogicTest {
|
||||
@Test
|
||||
void testGetDomainTransferPrice_premiumDomain_default_noBilling_premiumRenewalPrice()
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(registry, "premium.example", clock.nowUtc(), null))
|
||||
assertThat(domainPricingLogic.getTransferPrice(tld, "premium.example", clock.nowUtc(), null))
|
||||
.isEqualTo(
|
||||
new FeesAndCredits.Builder()
|
||||
.setCurrency(USD)
|
||||
@@ -745,7 +764,7 @@ public class DomainPricingLogicTest {
|
||||
void testGetDomainTransferPrice_standardDomain_default_defaultRenewalPrice() throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurrence("standard.example", DEFAULT, Optional.empty())))
|
||||
@@ -760,7 +779,7 @@ public class DomainPricingLogicTest {
|
||||
void testGetDomainTransferPrice_premiumDomain_default_premiumRenewalPrice() throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurrence("premium.example", DEFAULT, Optional.empty())))
|
||||
@@ -776,7 +795,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurrence("standard.example", NONPREMIUM, Optional.empty())))
|
||||
@@ -792,7 +811,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurrence("premium.example", NONPREMIUM, Optional.empty())))
|
||||
@@ -808,7 +827,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
tld,
|
||||
"standard.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurrence(
|
||||
@@ -825,7 +844,7 @@ public class DomainPricingLogicTest {
|
||||
throws EppException {
|
||||
assertThat(
|
||||
domainPricingLogic.getTransferPrice(
|
||||
registry,
|
||||
tld,
|
||||
"premium.example",
|
||||
clock.nowUtc(),
|
||||
persistDomainAndSetRecurrence(
|
||||
|
||||
@@ -520,7 +520,7 @@ class DomainTransferRequestFlowTest
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(ResaveEntityAction.PATH)
|
||||
.path(ResaveEntityAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.header("content-type", "application/x-www-form-urlencoded")
|
||||
|
||||
@@ -207,7 +207,7 @@ class HostUpdateFlowTest extends ResourceFlowTestCase<HostUpdateFlow, Host> {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_HOST_RENAME,
|
||||
new TaskMatcher()
|
||||
.url(RefreshDnsOnHostRenameAction.PATH)
|
||||
.path(RefreshDnsOnHostRenameAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.param(PARAM_HOST_KEY, renamedHost.createVKey().stringify()));
|
||||
|
||||
@@ -139,13 +139,12 @@ class RdapTestHelper {
|
||||
"RDDS Inaccuracy Complaint Form",
|
||||
"description",
|
||||
ImmutableList.of(
|
||||
"URL of the ICANN RDDS Inaccuracy Complaint Form:"
|
||||
+ " https://www.icann.org/wicf"),
|
||||
"URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"),
|
||||
"links",
|
||||
ImmutableList.of(
|
||||
ImmutableMap.of(
|
||||
"rel", "alternate",
|
||||
"href", "https://www.icann.org/wicf",
|
||||
"href", "https://icann.org/wicf",
|
||||
"type", "text/html")))));
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ public class RdeUploadActionTest {
|
||||
action, Tld.get("lol"), standardSeconds(23), CursorType.RDE_UPLOAD, standardDays(1));
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher().url(RdeReportAction.PATH).param(RequestParameters.PARAM_TLD, "lol"));
|
||||
new TaskMatcher().path(RdeReportAction.PATH).param(RequestParameters.PARAM_TLD, "lol"));
|
||||
verifyNoMoreInteractions(runner);
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ public class RdeUploadActionTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url(RdeReportAction.PATH)
|
||||
.path(RdeReportAction.PATH)
|
||||
.param(RequestParameters.PARAM_TLD, "lol")
|
||||
.param(RdeModule.PARAM_PREFIX, "job-name/"));
|
||||
verifyNoMoreInteractions(runner);
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ class GenerateInvoicesActionTest extends BeamActionTestBase {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"beam-reporting",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/publishInvoices")
|
||||
.path("/_dr/task/publishInvoices")
|
||||
.method(HttpMethod.POST)
|
||||
.param("jobId", "jobid")
|
||||
.param("yearMonth", "2017-10")
|
||||
|
||||
@@ -83,7 +83,7 @@ class PublishInvoicesActionTest {
|
||||
verify(emailUtils).emailOverallInvoice();
|
||||
TaskMatcher matcher =
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/copyDetailReports")
|
||||
.path("/_dr/task/copyDetailReports")
|
||||
.method(HttpMethod.POST)
|
||||
.param("yearMonth", "2017-10");
|
||||
cloudTasksHelper.assertTasksEnqueued("retryable-cron-tasks", matcher);
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ class IcannReportingStagingActionTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"retryable-cron-tasks",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/icannReportingUpload")
|
||||
.path("/_dr/task/icannReportingUpload")
|
||||
.method(HttpMethod.POST)
|
||||
.scheduleTime(clock.nowUtc().plus(Duration.standardMinutes(2))));
|
||||
}
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ class GenerateSpec11ReportActionTest extends BeamActionTestBase {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"beam-reporting",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/publishSpec11")
|
||||
.path("/_dr/task/publishSpec11")
|
||||
.method(HttpMethod.POST)
|
||||
.param("jobId", "jobid")
|
||||
.param("date", "2018-06-11")
|
||||
|
||||
+12
-5
@@ -29,7 +29,7 @@ import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature.Header;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.auth.oauth2.TokenVerifier.VerificationException;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import dagger.Component;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -54,8 +54,8 @@ public class OidcTokenAuthenticationMechanismTest {
|
||||
private static final String rawToken = "this-token";
|
||||
private static final String email = "user@email.test";
|
||||
private static final String gaiaId = "gaia-id";
|
||||
private static final ImmutableList<String> serviceAccounts =
|
||||
ImmutableList.of("service@email.test", "email@service.goog");
|
||||
private static final ImmutableSet<String> serviceAccounts =
|
||||
ImmutableSet.of("service@email.test", "email@service.goog");
|
||||
|
||||
private final Payload payload = new Payload();
|
||||
private final User user =
|
||||
@@ -222,9 +222,16 @@ public class OidcTokenAuthenticationMechanismTest {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Config("serviceAccountEmails")
|
||||
ImmutableList<String> provideServiceAccountEmails() {
|
||||
@Config("allowedServiceAccountEmails")
|
||||
ImmutableSet<String> provideAllowedServiceAccountEmails() {
|
||||
return serviceAccounts;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Config("oauthClientId")
|
||||
String provideOauthClientId() {
|
||||
return "client-id";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ import static google.registry.util.DiffUtils.prettyPrintEntityDeepDiff;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.cloud.tasks.v2.HttpRequest;
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Joiner;
|
||||
@@ -45,6 +47,7 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import google.registry.util.Retrier;
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
@@ -58,12 +61,13 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.inject.Singleton;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -104,8 +108,8 @@ public class CloudTasksHelper implements Serializable {
|
||||
clock,
|
||||
PROJECT_ID,
|
||||
LOCATION_ID,
|
||||
Optional.empty(),
|
||||
Optional.empty(),
|
||||
"client.id",
|
||||
FakeGoogleCredentialsBundle.create(),
|
||||
new FakeCloudTasksClient());
|
||||
testTasks.put(instanceId, Multimaps.synchronizedListMultimap(LinkedListMultimap.create()));
|
||||
}
|
||||
@@ -219,18 +223,35 @@ public class CloudTasksHelper implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
public static class FakeGoogleCredentialsBundle extends GoogleCredentialsBundle {
|
||||
|
||||
private static final long serialVersionUID = -3251343247195058893L;
|
||||
|
||||
private static final FakeGoogleCredentialsBundle INSTANCE = new FakeGoogleCredentialsBundle();
|
||||
|
||||
public static GoogleCredentialsBundle create() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
private FakeGoogleCredentialsBundle() {
|
||||
super(new GoogleCredentials(null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serviceAccount() {
|
||||
return "service@account.com";
|
||||
}
|
||||
}
|
||||
|
||||
/** An adapter to clean up a {@link Task} for ease of matching. */
|
||||
private static class MatchableTask extends ImmutableObject {
|
||||
|
||||
private static final Pattern HOSTNAME_PATTERN =
|
||||
Pattern.compile("(?<=https://)[a-z]+(?=\\.example\\.com)");
|
||||
String taskName;
|
||||
String service;
|
||||
// App Engine TaskOption methods default to "POST". This isn't obvious from the code, so we
|
||||
// default it to POST here so that we don't accidentally create an entry with a GET method when
|
||||
// converting to CloudTasksUtils, which requires that the method be specified explicitly.
|
||||
// Should we ever actually want to do a GET, we'll likewise have to set this explicitly for the
|
||||
// tests.
|
||||
HttpMethod method = HttpMethod.POST;
|
||||
String url;
|
||||
HttpMethod method;
|
||||
String path;
|
||||
Timestamp scheduleTime;
|
||||
Multimap<String, String> headers = ArrayListMultimap.create();
|
||||
Multimap<String, String> params = ArrayListMultimap.create();
|
||||
@@ -238,24 +259,24 @@ public class CloudTasksHelper implements Serializable {
|
||||
MatchableTask() {}
|
||||
|
||||
MatchableTask(Task task) {
|
||||
HttpRequest request = task.getHttpRequest();
|
||||
taskName = task.getName();
|
||||
String url = request.getUrl();
|
||||
// URI class provides helper method to extract query parameters.
|
||||
URI uri;
|
||||
try {
|
||||
// Construct a fake full URI for parsing purpose. The relative URI must start with a slash.
|
||||
uri =
|
||||
new URI(
|
||||
String.format(
|
||||
"https://nomulus.foo%s", task.getAppEngineHttpRequest().getRelativeUri()));
|
||||
uri = new URI(url);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
taskName = task.getName();
|
||||
service =
|
||||
Ascii.toLowerCase(task.getAppEngineHttpRequest().getAppEngineRouting().getService());
|
||||
method = task.getAppEngineHttpRequest().getHttpMethod();
|
||||
url = uri.getPath();
|
||||
Matcher hostnameMatcher = HOSTNAME_PATTERN.matcher(url);
|
||||
assertThat(hostnameMatcher.find()).isTrue();
|
||||
service = Ascii.toLowerCase(hostnameMatcher.group());
|
||||
path = url.substring(String.format("https://%s.example.com", service).length());
|
||||
method = request.getHttpMethod();
|
||||
scheduleTime = task.getScheduleTime();
|
||||
ImmutableMultimap.Builder<String, String> headerBuilder = new ImmutableMultimap.Builder<>();
|
||||
task.getAppEngineHttpRequest()
|
||||
request
|
||||
.getHeadersMap()
|
||||
.forEach(
|
||||
(key, value) -> {
|
||||
@@ -269,14 +290,13 @@ public class CloudTasksHelper implements Serializable {
|
||||
// where parameters are not properly URL-encoded); it always does a best-effort parse.
|
||||
if (method == HttpMethod.GET && uri.getQuery() != null) {
|
||||
paramBuilder.putAll(UriParameters.parse(uri.getQuery()));
|
||||
} else if (method == HttpMethod.POST && !task.getAppEngineHttpRequest().getBody().isEmpty()) {
|
||||
} else if (method == HttpMethod.POST && !request.getBody().isEmpty()) {
|
||||
assertThat(
|
||||
headers.containsEntry(
|
||||
Ascii.toLowerCase(HttpHeaders.CONTENT_TYPE), MediaType.FORM_DATA.toString()))
|
||||
.isTrue();
|
||||
paramBuilder.putAll(
|
||||
UriParameters.parse(
|
||||
task.getAppEngineHttpRequest().getBody().toString(StandardCharsets.UTF_8)));
|
||||
UriParameters.parse(request.getBody().toString(StandardCharsets.UTF_8)));
|
||||
}
|
||||
params = paramBuilder.build();
|
||||
}
|
||||
@@ -286,7 +306,7 @@ public class CloudTasksHelper implements Serializable {
|
||||
builder.put("taskName", taskName);
|
||||
builder.put("method", method);
|
||||
builder.put("service", service);
|
||||
builder.put("url", url);
|
||||
builder.put("path", path);
|
||||
builder.put("headers", headers);
|
||||
builder.put("params", params);
|
||||
builder.put("scheduleTime", scheduleTime);
|
||||
@@ -310,8 +330,8 @@ public class CloudTasksHelper implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public TaskMatcher url(String url) {
|
||||
expected.url = url;
|
||||
public TaskMatcher path(String path) {
|
||||
expected.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -371,7 +391,7 @@ public class CloudTasksHelper implements Serializable {
|
||||
public boolean test(@Nonnull Task task) {
|
||||
MatchableTask actual = new MatchableTask(task);
|
||||
return (expected.taskName == null || Objects.equals(expected.taskName, actual.taskName))
|
||||
&& (expected.url == null || Objects.equals(expected.url, actual.url))
|
||||
&& (expected.path == null || Objects.equals(expected.path, actual.path))
|
||||
&& (expected.method == null || Objects.equals(expected.method, actual.method))
|
||||
&& (expected.service == null || Objects.equals(expected.service, actual.service))
|
||||
&& (expected.scheduleTime == null
|
||||
|
||||
@@ -1357,5 +1357,16 @@ public final class DatabaseHelper {
|
||||
.isEqualTo(1);
|
||||
}
|
||||
|
||||
public static void assertDnsRequestsWithRequestTime(DateTime requestTime, int numOfDomains) {
|
||||
assertThat(
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().createQueryComposer(DnsRefreshRequest.class)
|
||||
.where("type", EQ, DnsUtils.TargetType.DOMAIN)
|
||||
.where("requestTime", EQ, requestTime)
|
||||
.count()))
|
||||
.isEqualTo(numOfDomains);
|
||||
}
|
||||
|
||||
private DatabaseHelper() {}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ class NordnUploadActionTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
NordnVerifyAction.QUEUE,
|
||||
new TaskMatcher()
|
||||
.url(NordnVerifyAction.PATH)
|
||||
.path(NordnVerifyAction.PATH)
|
||||
.param(NordnVerifyAction.NORDN_URL_PARAM, LOCATION_URL)
|
||||
.param(RequestParameters.PARAM_TLD, "tld")
|
||||
.header(CONTENT_TYPE, FORM_DATA.toString()));
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.request.Action.Service.TOOLS;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
@@ -29,6 +30,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.request.Action.Service;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -46,7 +48,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.setConnection(connection);
|
||||
when(connection.withService(any())).thenReturn(connectionForService);
|
||||
when(connection.withService(any(Service.class), anyBoolean())).thenReturn(connectionForService);
|
||||
}
|
||||
|
||||
@Captor ArgumentCaptor<ImmutableMap<String, String>> urlParamCaptor;
|
||||
@@ -54,7 +56,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||
@Test
|
||||
void testGetInvocation() throws Exception {
|
||||
runCommand("--path=/foo/bar?a=1&b=2", "--service=TOOLS");
|
||||
verify(connection).withService(TOOLS);
|
||||
verify(connection).withService(eq(TOOLS), eq(false));
|
||||
verifyNoMoreInteractions(connection);
|
||||
verify(connectionForService)
|
||||
.sendGetRequest(eq("/foo/bar?a=1&b=2"), eq(ImmutableMap.<String, String>of()));
|
||||
@@ -63,7 +65,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||
@Test
|
||||
void testExplicitGetInvocation() throws Exception {
|
||||
runCommand("--path=/foo/bar?a=1&b=2", "--request=GET", "--service=BACKEND");
|
||||
verify(connection).withService(BACKEND);
|
||||
verify(connection).withService(eq(BACKEND), eq(false));
|
||||
verifyNoMoreInteractions(connection);
|
||||
verify(connectionForService)
|
||||
.sendGetRequest(eq("/foo/bar?a=1&b=2"), eq(ImmutableMap.<String, String>of()));
|
||||
@@ -72,7 +74,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||
@Test
|
||||
void testPostInvocation() throws Exception {
|
||||
runCommand("--path=/foo/bar?a=1&b=2", "--data=some data", "--service=DEFAULT");
|
||||
verify(connection).withService(DEFAULT);
|
||||
verify(connection).withService(eq(DEFAULT), eq(false));
|
||||
verifyNoMoreInteractions(connection);
|
||||
verify(connectionForService)
|
||||
.sendPostRequest(
|
||||
@@ -89,7 +91,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||
"--data=some data",
|
||||
"--service=DEFAULT",
|
||||
"--content-type=application/json");
|
||||
verify(connection).withService(DEFAULT);
|
||||
verify(connection).withService(eq(DEFAULT), eq(false));
|
||||
verifyNoMoreInteractions(connection);
|
||||
verify(connectionForService)
|
||||
.sendPostRequest(
|
||||
@@ -118,7 +120,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||
void testMultiDataPost() throws Exception {
|
||||
runCommand(
|
||||
"--path=/foo/bar?a=1&b=2", "--data=first=100", "-d", "second=200", "--service=PUBAPI");
|
||||
verify(connection).withService(PUBAPI);
|
||||
verify(connection).withService(eq(PUBAPI), eq(false));
|
||||
verifyNoMoreInteractions(connection);
|
||||
verify(connectionForService)
|
||||
.sendPostRequest(
|
||||
@@ -132,7 +134,7 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||
void testDataDoesntSplit() throws Exception {
|
||||
runCommand(
|
||||
"--path=/foo/bar?a=1&b=2", "--data=one,two", "--service=PUBAPI");
|
||||
verify(connection).withService(PUBAPI);
|
||||
verify(connection).withService(eq(PUBAPI), eq(false));
|
||||
verifyNoMoreInteractions(connection);
|
||||
verify(connectionForService)
|
||||
.sendPostRequest(
|
||||
@@ -145,7 +147,20 @@ class CurlCommandTest extends CommandTestCase<CurlCommand> {
|
||||
@Test
|
||||
void testExplicitPostInvocation() throws Exception {
|
||||
runCommand("--path=/foo/bar?a=1&b=2", "--request=POST", "--service=TOOLS");
|
||||
verify(connection).withService(TOOLS);
|
||||
verify(connection).withService(eq(TOOLS), eq(false));
|
||||
verifyNoMoreInteractions(connection);
|
||||
verify(connectionForService)
|
||||
.sendPostRequest(
|
||||
eq("/foo/bar?a=1&b=2"),
|
||||
eq(ImmutableMap.<String, String>of()),
|
||||
eq(MediaType.PLAIN_TEXT_UTF_8),
|
||||
eq("".getBytes(UTF_8)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCanaryInvocation() throws Exception {
|
||||
runCommand("--path=/foo/bar?a=1&b=2", "--request=POST", "--service=TOOLS", "--canary");
|
||||
verify(connection).withService(eq(TOOLS), eq(true));
|
||||
verifyNoMoreInteractions(connection);
|
||||
verify(connectionForService)
|
||||
.sendPostRequest(
|
||||
|
||||
@@ -259,7 +259,7 @@ public final class DomainLockUtilsTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new TaskMatcher()
|
||||
.url(RelockDomainAction.PATH)
|
||||
.path(RelockDomainAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.param(
|
||||
@@ -481,7 +481,7 @@ public final class DomainLockUtilsTest {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
QUEUE_ASYNC_ACTIONS,
|
||||
new CloudTasksHelper.TaskMatcher()
|
||||
.url(RelockDomainAction.PATH)
|
||||
.path(RelockDomainAction.PATH)
|
||||
.method(HttpMethod.POST)
|
||||
.service("backend")
|
||||
.param(
|
||||
|
||||
@@ -73,7 +73,7 @@ final class GcpProjectConnectionTest {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
getStreamingContent().writeTo(output);
|
||||
output.close();
|
||||
return new String(output.toByteArray(), UTF_8);
|
||||
return output.toString(UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ final class GcpProjectConnectionTest {
|
||||
.isEqualTo("MyContent");
|
||||
assertThat(httpTransport.method).isEqualTo("GET");
|
||||
assertThat(httpTransport.url)
|
||||
.isEqualTo("https://localhost/my/path?query&key1=value1&key2=value2");
|
||||
.isEqualTo("https://tools.example.com/my/path?query&key1=value1&key2=value2");
|
||||
assertThat(lowLevelHttpRequest.headers).containsEntry("Cache-Control", "no-cache");
|
||||
assertThat(lowLevelHttpRequest.headers).containsEntry("x-requested-with", "RegistryTool");
|
||||
}
|
||||
@@ -113,7 +113,7 @@ final class GcpProjectConnectionTest {
|
||||
.isEqualTo("MyContent");
|
||||
assertThat(httpTransport.method).isEqualTo("POST");
|
||||
assertThat(httpTransport.url)
|
||||
.isEqualTo("https://localhost/my/path?query&key1=value1&key2=value2");
|
||||
.isEqualTo("https://tools.example.com/my/path?query&key1=value1&key2=value2");
|
||||
assertThat(lowLevelHttpRequest.getContentType()).isEqualTo("text/plain; charset=utf-8");
|
||||
assertThat(lowLevelHttpRequest.getContentString()).isEqualTo("some data");
|
||||
assertThat(lowLevelHttpRequest.headers).containsEntry("Cache-Control", "no-cache");
|
||||
@@ -130,7 +130,7 @@ final class GcpProjectConnectionTest {
|
||||
"/my/path?query", ImmutableMap.of("string", "value1", "bool", true)))
|
||||
.containsExactly("key", "value");
|
||||
assertThat(httpTransport.method).isEqualTo("POST");
|
||||
assertThat(httpTransport.url).isEqualTo("https://localhost/my/path?query");
|
||||
assertThat(httpTransport.url).isEqualTo("https://tools.example.com/my/path?query");
|
||||
assertThat(lowLevelHttpRequest.getContentType()).isEqualTo("application/json; charset=utf-8");
|
||||
assertThat(lowLevelHttpRequest.getContentString())
|
||||
.isEqualTo("{\"string\":\"value1\",\"bool\":true}");
|
||||
|
||||
@@ -187,7 +187,7 @@ public class GenerateEscrowDepositCommandTest
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeStaging")
|
||||
.path("/_dr/task/rdeStaging")
|
||||
.param("mode", "THIN")
|
||||
.param("lenient", "true")
|
||||
.param("watermarks", "2017-01-01T00:00:00.000Z")
|
||||
@@ -204,7 +204,7 @@ public class GenerateEscrowDepositCommandTest
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeStaging")
|
||||
.path("/_dr/task/rdeStaging")
|
||||
.param("mode", "THIN")
|
||||
.param("lenient", "false")
|
||||
.param("watermarks", "2017-01-01T00:00:00.000Z")
|
||||
@@ -221,7 +221,7 @@ public class GenerateEscrowDepositCommandTest
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeStaging")
|
||||
.path("/_dr/task/rdeStaging")
|
||||
.param("lenient", "false")
|
||||
.param("mode", "THIN")
|
||||
.param("watermarks", "2017-01-01T00:00:00.000Z")
|
||||
@@ -237,7 +237,7 @@ public class GenerateEscrowDepositCommandTest
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeStaging")
|
||||
.path("/_dr/task/rdeStaging")
|
||||
.param("mode", "FULL")
|
||||
.param("lenient", "false")
|
||||
.param("watermarks", "2017-01-01T00:00:00.000Z")
|
||||
@@ -259,7 +259,7 @@ public class GenerateEscrowDepositCommandTest
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"rde-report",
|
||||
new TaskMatcher()
|
||||
.url("/_dr/task/rdeStaging")
|
||||
.path("/_dr/task/rdeStaging")
|
||||
.param("mode", "THIN")
|
||||
.param("lenient", "false")
|
||||
.param("watermarks", "2017-01-01T00:00:00.000Z,2017-01-02T00:00:00.000Z")
|
||||
|
||||
@@ -35,7 +35,6 @@ import com.google.auth.oauth2.UserCredentials;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.testing.SystemPropertyExtension;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -65,7 +64,7 @@ public class RequestFactoryModuleTest {
|
||||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = true;
|
||||
try {
|
||||
HttpRequestFactory factory =
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, Optional.empty());
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "client-id");
|
||||
HttpRequestInitializer initializer = factory.getInitializer();
|
||||
assertThat(initializer).isNotNull();
|
||||
HttpRequest request = factory.buildGetRequest(new GenericUrl("http://localhost"));
|
||||
@@ -79,29 +78,7 @@ public class RequestFactoryModuleTest {
|
||||
@Test
|
||||
void test_provideHttpRequestFactory_remote() throws Exception {
|
||||
when(credentialsBundle.getHttpRequestInitializer()).thenReturn(httpRequestInitializer);
|
||||
// Make sure that example.com creates a request factory with the UNITTEST client id but no
|
||||
boolean origIsLocal = RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal;
|
||||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = false;
|
||||
try {
|
||||
HttpRequestFactory factory =
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, Optional.empty());
|
||||
HttpRequestInitializer initializer = factory.getInitializer();
|
||||
assertThat(initializer).isNotNull();
|
||||
// HttpRequestFactory#buildGetRequest() calls initialize() once.
|
||||
HttpRequest request = factory.buildGetRequest(new GenericUrl("http://localhost"));
|
||||
verify(httpRequestInitializer).initialize(request);
|
||||
assertThat(request.getConnectTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
assertThat(request.getReadTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
verifyNoMoreInteractions(httpRequestInitializer);
|
||||
} finally {
|
||||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = origIsLocal;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_provideHttpRequestFactory_remote_withIap() throws Exception {
|
||||
when(credentialsBundle.getHttpRequestInitializer()).thenReturn(httpRequestInitializer);
|
||||
// Mock the request/response to/from the IAP server requesting an ID token
|
||||
// Mock the request/response to/from the OIDC server requesting an ID token
|
||||
UserCredentials mockUserCredentials = mock(UserCredentials.class);
|
||||
when(credentialsBundle.getGoogleCredentials()).thenReturn(mockUserCredentials);
|
||||
HttpTransport mockTransport = mock(HttpTransport.class);
|
||||
@@ -114,17 +91,16 @@ public class RequestFactoryModuleTest {
|
||||
HttpResponse mockResponse = mock(HttpResponse.class);
|
||||
when(mockPostRequest.execute()).thenReturn(mockResponse);
|
||||
GenericData genericDataResponse = new GenericData();
|
||||
genericDataResponse.set("id_token", "iapIdToken");
|
||||
genericDataResponse.set("id_token", "oidc.token");
|
||||
when(mockResponse.parseAs(GenericData.class)).thenReturn(genericDataResponse);
|
||||
|
||||
boolean origIsLocal = RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal;
|
||||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = false;
|
||||
try {
|
||||
HttpRequestFactory factory =
|
||||
RequestFactoryModule.provideHttpRequestFactory(
|
||||
credentialsBundle, Optional.of("iapClientId"));
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "clientId");
|
||||
HttpRequest request = factory.buildGetRequest(new GenericUrl("http://localhost"));
|
||||
assertThat(request.getHeaders().get("Proxy-Authorization")).isEqualTo("Bearer iapIdToken");
|
||||
assertThat(request.getHeaders().get("Proxy-Authorization")).isEqualTo("Bearer oidc.token");
|
||||
assertThat(request.getConnectTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
assertThat(request.getReadTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
verify(httpRequestInitializer).initialize(request);
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2023 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.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.request.Action.Service.DEFAULT;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link google.registry.tools.ServiceConnection}. */
|
||||
public class ServiceConnectionTest {
|
||||
|
||||
@Test
|
||||
void testServerUrl_notCanary() {
|
||||
ServiceConnection connection = new ServiceConnection().withService(DEFAULT, false);
|
||||
String serverUrl = connection.getServer().toString();
|
||||
assertThat(serverUrl).isEqualTo("https://default.example.com"); // See default-config.yaml
|
||||
}
|
||||
|
||||
@Test
|
||||
void testServerUrl_canary() {
|
||||
ServiceConnection connection = new ServiceConnection().withService(DEFAULT, true);
|
||||
String serverUrl = connection.getServer().toString();
|
||||
assertThat(serverUrl).isEqualTo("https://nomulus-dot-default.example.com");
|
||||
}
|
||||
}
|
||||
@@ -134,15 +134,13 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
|
||||
@Test
|
||||
void commandPrompt_failureNoPreviousVersion() {
|
||||
String fileName = "random";
|
||||
registry = createTld(fileName, null, null);
|
||||
registry = createTld("random", null, null);
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
command.name = fileName;
|
||||
command.name = "random";
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
String.format("Could not update premium list %s because it doesn't exist.", fileName));
|
||||
.isEqualTo("Could not update premium list random because it doesn't exist");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -153,27 +151,22 @@ class UpdatePremiumListCommandTest<C extends UpdatePremiumListCommand>
|
||||
|
||||
@Test
|
||||
void commandPrompt_failureTldFromNameDoesNotExist() {
|
||||
String fileName = "random";
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
command.name = fileName;
|
||||
command.name = "random2";
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
String.format("Could not update premium list %s because it doesn't exist.", fileName));
|
||||
.isEqualTo("Could not update premium list random2 because it doesn't exist");
|
||||
}
|
||||
|
||||
@Test
|
||||
void commandPrompt_failureTldFromInputFileDoesNotExist() {
|
||||
String fileName = "random";
|
||||
UpdatePremiumListCommand command = new UpdatePremiumListCommand();
|
||||
// using tld extracted from file name but this tld is not part of the registry
|
||||
command.inputFile =
|
||||
Paths.get(tmpDir.resolve(String.format("%s.txt", fileName)).toFile().getPath());
|
||||
command.inputFile = Paths.get(tmpDir.resolve("random3.txt").toFile().getPath());
|
||||
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, command::prompt);
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo(
|
||||
String.format("Could not update premium list %s because it doesn't exist.", fileName));
|
||||
.isEqualTo("Could not update premium list random3 because it doesn't exist");
|
||||
}
|
||||
}
|
||||
|
||||
+31
-23
@@ -15,18 +15,24 @@
|
||||
package google.registry.tools.server;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.QueryComposer.Comparator.EQ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.assertDnsRequestsWithRequestTime;
|
||||
import static google.registry.testing.DatabaseHelper.assertDomainDnsRequestWithRequestTime;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoDnsRequestsExcept;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistDeletedDomain;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.dns.DnsUtils;
|
||||
import google.registry.model.common.DnsRefreshRequest;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -46,20 +52,16 @@ public class RefreshDnsForAllDomainsActionTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
action = new RefreshDnsForAllDomainsAction();
|
||||
action.smearMinutes = 1;
|
||||
action.random = new Random();
|
||||
action.random.setSeed(123L);
|
||||
action.clock = clock;
|
||||
action.response = response;
|
||||
createTld("bar");
|
||||
action =
|
||||
new RefreshDnsForAllDomainsAction(
|
||||
response, ImmutableSet.of("bar"), Optional.of(10), new Random());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_runAction_successfullyEnqueuesDnsRefreshes() throws Exception {
|
||||
persistActiveDomain("foo.bar");
|
||||
persistActiveDomain("low.bar");
|
||||
action.tlds = ImmutableSet.of("bar");
|
||||
action.run();
|
||||
assertDomainDnsRequestWithRequestTime("foo.bar", clock.nowUtc());
|
||||
assertDomainDnsRequestWithRequestTime("low.bar", clock.nowUtc());
|
||||
@@ -69,18 +71,27 @@ public class RefreshDnsForAllDomainsActionTest {
|
||||
void test_runAction_smearsOutDnsRefreshes() throws Exception {
|
||||
persistActiveDomain("foo.bar");
|
||||
persistActiveDomain("low.bar");
|
||||
action.tlds = ImmutableSet.of("bar");
|
||||
action.smearMinutes = 1000;
|
||||
action.run();
|
||||
assertDomainDnsRequestWithRequestTime("foo.bar", clock.nowUtc().plusMinutes(450));
|
||||
assertDomainDnsRequestWithRequestTime("low.bar", clock.nowUtc().plusMinutes(782));
|
||||
// Set batch size to 1 since each batch will be enqueud at the same time
|
||||
action =
|
||||
new RefreshDnsForAllDomainsAction(
|
||||
response, ImmutableSet.of("bar"), Optional.of(1), new Random());
|
||||
tm().transact(() -> action.refreshBatch(Optional.empty(), 1000));
|
||||
tm().transact(() -> action.refreshBatch(Optional.empty(), 1000));
|
||||
ImmutableList<DnsRefreshRequest> refreshRequests =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().createQueryComposer(DnsRefreshRequest.class)
|
||||
.where("type", EQ, DnsUtils.TargetType.DOMAIN)
|
||||
.list());
|
||||
assertThat(refreshRequests.size()).isEqualTo(2);
|
||||
assertThat(refreshRequests.get(0).getRequestTime())
|
||||
.isNotEqualTo(refreshRequests.get(1).getRequestTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_runAction_doesntRefreshDeletedDomain() throws Exception {
|
||||
persistActiveDomain("foo.bar");
|
||||
persistDeletedDomain("deleted.bar", clock.nowUtc().minusYears(1));
|
||||
action.tlds = ImmutableSet.of("bar");
|
||||
action.run();
|
||||
assertDomainDnsRequestWithRequestTime("foo.bar", clock.nowUtc());
|
||||
assertNoDnsRequestsExcept("foo.bar");
|
||||
@@ -92,7 +103,6 @@ public class RefreshDnsForAllDomainsActionTest {
|
||||
persistActiveDomain("foo.bar");
|
||||
persistActiveDomain("low.bar");
|
||||
persistActiveDomain("ignore.baz");
|
||||
action.tlds = ImmutableSet.of("bar");
|
||||
action.run();
|
||||
assertDomainDnsRequestWithRequestTime("foo.bar", clock.nowUtc());
|
||||
assertDomainDnsRequestWithRequestTime("low.bar", clock.nowUtc());
|
||||
@@ -100,13 +110,11 @@ public class RefreshDnsForAllDomainsActionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_smearMinutesMustBeSpecified() {
|
||||
action.tlds = ImmutableSet.of("bar");
|
||||
action.smearMinutes = 0;
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(IllegalArgumentException.class, () -> action.run());
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Must specify a positive number of smear minutes");
|
||||
void test_successfullyBatchesNames() {
|
||||
for (int i = 0; i <= 10; i++) {
|
||||
persistActiveDomain(String.format("test%s.bar", i));
|
||||
}
|
||||
action.run();
|
||||
assertDnsRequestsWithRequestTime(clock.nowUtc(), 11);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ class RegistrarSettingsActionTest extends RegistrarSettingsActionTestCase {
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"sheet",
|
||||
new TaskMatcher()
|
||||
.url(SyncRegistrarsSheetAction.PATH)
|
||||
.path(SyncRegistrarsSheetAction.PATH)
|
||||
.service("Backend")
|
||||
.method(HttpMethod.GET));
|
||||
assertMetric(CLIENT_ID, "update", "[OWNER]", "SUCCESS");
|
||||
|
||||
+3
-3
@@ -55,7 +55,7 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="create">11.05</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
<fee:name>allowedinsunrise.tld</fee:name>
|
||||
@@ -83,7 +83,7 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="create">11.05</fee:fee>
|
||||
<fee:class>collision</fee:class>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
@@ -115,7 +115,7 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">70.00</fee:fee>
|
||||
<fee:fee description="create">59.50</fee:fee>
|
||||
<fee:class>premium-collision</fee:class>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
|
||||
+3
-3
@@ -59,7 +59,7 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="create">11.05</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
<fee:name>allowedinsunrise.tld</fee:name>
|
||||
@@ -88,7 +88,7 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="create">11.05</fee:fee>
|
||||
<fee:class>collision</fee:class>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
@@ -121,7 +121,7 @@
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:command>create</fee:command>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">70.00</fee:fee>
|
||||
<fee:fee description="create">59.50</fee:fee>
|
||||
<fee:class>premium-collision</fee:class>
|
||||
</fee:cd>
|
||||
<fee:cd xmlns:fee="urn:ietf:params:xml:ns:fee-0.6">
|
||||
|
||||
+3
-3
@@ -40,7 +40,7 @@
|
||||
<fee:command>create</fee:command>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="create">11.05</fee:fee>
|
||||
</fee:cd>
|
||||
<fee:cd avail="1">
|
||||
<fee:object>
|
||||
@@ -49,7 +49,7 @@
|
||||
<fee:command>create</fee:command>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="create">11.05</fee:fee>
|
||||
<fee:class>collision</fee:class>
|
||||
</fee:cd>
|
||||
<fee:cd avail="1">
|
||||
@@ -59,7 +59,7 @@
|
||||
<fee:command>create</fee:command>
|
||||
<fee:currency>USD</fee:currency>
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">70.00</fee:fee>
|
||||
<fee:fee description="create">59.50</fee:fee>
|
||||
<fee:class>premium-collision</fee:class>
|
||||
</fee:cd>
|
||||
</fee:chkData>
|
||||
|
||||
+3
-3
@@ -66,7 +66,7 @@
|
||||
</fee:object>
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="create">11.05</fee:fee>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
<fee:cd>
|
||||
@@ -102,7 +102,7 @@
|
||||
</fee:object>
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">13.00</fee:fee>
|
||||
<fee:fee description="create">11.05</fee:fee>
|
||||
<fee:class>collision</fee:class>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
@@ -142,7 +142,7 @@
|
||||
</fee:object>
|
||||
<fee:command name="create">
|
||||
<fee:period unit="y">1</fee:period>
|
||||
<fee:fee description="create">70.00</fee:fee>
|
||||
<fee:fee description="create">59.50</fee:fee>
|
||||
<fee:class>premium-collision</fee:class>
|
||||
</fee:command>
|
||||
</fee:cd>
|
||||
|
||||
+3
-20
@@ -5,7 +5,7 @@
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
],
|
||||
"objectClassName" : "entity",
|
||||
"handle" : "REDACTED FOR PRIVACY",
|
||||
"handle" : "",
|
||||
"events": [
|
||||
{
|
||||
"eventAction": "last update of RDAP database",
|
||||
@@ -15,25 +15,8 @@
|
||||
"vcardArray": [
|
||||
"vcard",
|
||||
[
|
||||
["version",{},"text","4.0"],
|
||||
["fn",{},"text","REDACTED FOR PRIVACY"],
|
||||
["org",{},"text","REDACTED FOR PRIVACY"],
|
||||
[
|
||||
"adr",
|
||||
{},
|
||||
"text",
|
||||
[
|
||||
"",
|
||||
"",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"XX"
|
||||
]
|
||||
],
|
||||
["tel",{"type":["voice"]},"uri","tel:REDACTED FOR PRIVACY"],
|
||||
["tel",{"type":["fax"]},"uri","tel:REDACTED FOR PRIVACY"]
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
],
|
||||
"remarks": [
|
||||
|
||||
+3
-20
@@ -5,7 +5,7 @@
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
],
|
||||
"objectClassName" : "entity",
|
||||
"handle" : "REDACTED FOR PRIVACY",
|
||||
"handle" : "",
|
||||
"events": [
|
||||
{
|
||||
"eventAction": "last update of RDAP database",
|
||||
@@ -15,25 +15,8 @@
|
||||
"vcardArray": [
|
||||
"vcard",
|
||||
[
|
||||
["version",{},"text","4.0"],
|
||||
["fn",{},"text","REDACTED FOR PRIVACY"],
|
||||
["org",{},"text","REDACTED FOR PRIVACY"],
|
||||
[
|
||||
"adr",
|
||||
{},
|
||||
"text",
|
||||
[
|
||||
"",
|
||||
"",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"XX"
|
||||
]
|
||||
],
|
||||
["tel",{"type":["voice"]},"uri","tel:REDACTED FOR PRIVACY"],
|
||||
["tel",{"type":["fax"]},"uri","tel:REDACTED FOR PRIVACY"]
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
],
|
||||
"remarks": [
|
||||
|
||||
+6
-18
@@ -150,7 +150,7 @@
|
||||
},
|
||||
{
|
||||
"objectClassName": "entity",
|
||||
"handle": "REDACTED FOR PRIVACY",
|
||||
"handle": "",
|
||||
"roles":["administrative"],
|
||||
"remarks": [
|
||||
{
|
||||
@@ -180,18 +180,14 @@
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["org", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["adr", {}, "text", ["", "", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "XX"]],
|
||||
["tel", {"type":["voice"]}, "uri", "tel:REDACTED FOR PRIVACY"],
|
||||
["tel", {"type":["fax"]}, "uri", "tel:REDACTED FOR PRIVACY"]
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"objectClassName":"entity",
|
||||
"handle":"REDACTED FOR PRIVACY",
|
||||
"handle":"",
|
||||
"remarks":[
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
@@ -221,18 +217,14 @@
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["org", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["adr", {}, "text", ["", "", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "XX"]],
|
||||
["tel", {"type":["voice"]}, "uri", "tel:REDACTED FOR PRIVACY"],
|
||||
["tel", {"type":["fax"]}, "uri", "tel:REDACTED FOR PRIVACY"]
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"objectClassName":"entity",
|
||||
"handle":"REDACTED FOR PRIVACY",
|
||||
"handle":"",
|
||||
"remarks":[
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
@@ -262,11 +254,7 @@
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["org", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["adr", {}, "text", ["", "", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "XX"]],
|
||||
["tel", {"type":["voice"]}, "uri", "tel:REDACTED FOR PRIVACY"],
|
||||
["tel", {"type":["fax"]}, "uri", "tel:REDACTED FOR PRIVACY"]
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
+6
-19
@@ -153,7 +153,7 @@
|
||||
},
|
||||
{
|
||||
"objectClassName": "entity",
|
||||
"handle": "REDACTED FOR PRIVACY",
|
||||
"handle": "",
|
||||
"roles":["administrative"],
|
||||
"remarks": [
|
||||
{
|
||||
@@ -183,18 +183,14 @@
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["org", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["adr", {}, "text", ["", "", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "XX"]],
|
||||
["tel", {"type":["voice"]}, "uri", "tel:REDACTED FOR PRIVACY"],
|
||||
["tel", {"type":["fax"]}, "uri", "tel:REDACTED FOR PRIVACY"]
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"objectClassName":"entity",
|
||||
"handle":"REDACTED FOR PRIVACY",
|
||||
"handle":"",
|
||||
"remarks":[
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
@@ -224,18 +220,13 @@
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["org", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["adr", {}, "text", ["", "", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "XX"]],
|
||||
["tel", {"type":["voice"]}, "uri", "tel:REDACTED FOR PRIVACY"],
|
||||
["tel", {"type":["fax"]}, "uri", "tel:REDACTED FOR PRIVACY"]
|
||||
]
|
||||
["fn", {}, "text", ""] ]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"objectClassName":"entity",
|
||||
"handle":"REDACTED FOR PRIVACY",
|
||||
"handle":"",
|
||||
"remarks":[
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
@@ -265,11 +256,7 @@
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["org", {}, "text", "REDACTED FOR PRIVACY"],
|
||||
["adr", {}, "text", ["", "", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "REDACTED FOR PRIVACY", "XX"]],
|
||||
["tel", {"type":["voice"]}, "uri", "tel:REDACTED FOR PRIVACY"],
|
||||
["tel", {"type":["fax"]}, "uri", "tel:REDACTED FOR PRIVACY"]
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -156,12 +156,12 @@
|
||||
},
|
||||
{
|
||||
"title": "RDDS Inaccuracy Complaint Form",
|
||||
"description" : ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://www.icann.org/wicf"],
|
||||
"description" : ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"],
|
||||
"links" :
|
||||
[
|
||||
{
|
||||
"rel" : "alternate",
|
||||
"href" : "https://www.icann.org/wicf",
|
||||
"href" : "https://icann.org/wicf",
|
||||
"type" : "text/html"
|
||||
}
|
||||
]
|
||||
|
||||
+2
-2
@@ -137,12 +137,12 @@
|
||||
},
|
||||
{
|
||||
"title": "RDDS Inaccuracy Complaint Form",
|
||||
"description" : ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://www.icann.org/wicf"],
|
||||
"description" : ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"],
|
||||
"links" :
|
||||
[
|
||||
{
|
||||
"rel" : "alternate",
|
||||
"href" : "https://www.icann.org/wicf",
|
||||
"href" : "https://icann.org/wicf",
|
||||
"type" : "text/html"
|
||||
}
|
||||
]
|
||||
|
||||
+2
-2
@@ -157,12 +157,12 @@
|
||||
},
|
||||
{
|
||||
"title": "RDDS Inaccuracy Complaint Form",
|
||||
"description" : ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://www.icann.org/wicf"],
|
||||
"description" : ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"],
|
||||
"links" :
|
||||
[
|
||||
{
|
||||
"rel" : "alternate",
|
||||
"href" : "https://www.icann.org/wicf",
|
||||
"href" : "https://icann.org/wicf",
|
||||
"type" : "text/html"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -95,12 +95,12 @@
|
||||
},
|
||||
{
|
||||
"title": "RDDS Inaccuracy Complaint Form",
|
||||
"description": ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://www.icann.org/wicf"],
|
||||
"description": ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"],
|
||||
"links":
|
||||
[
|
||||
{
|
||||
"rel": "alternate",
|
||||
"href": "https://www.icann.org/wicf",
|
||||
"href": "https://icann.org/wicf",
|
||||
"type": "text/html"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -130,12 +130,12 @@
|
||||
},
|
||||
{
|
||||
"title": "RDDS Inaccuracy Complaint Form",
|
||||
"description": ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://www.icann.org/wicf"],
|
||||
"description": ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"],
|
||||
"links":
|
||||
[
|
||||
{
|
||||
"rel":"alternate",
|
||||
"href":"https://www.icann.org/wicf",
|
||||
"href":"https://icann.org/wicf",
|
||||
"type":"text/html"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -98,12 +98,12 @@
|
||||
},
|
||||
{
|
||||
"title": "RDDS Inaccuracy Complaint Form",
|
||||
"description":["URL of the ICANN RDDS Inaccuracy Complaint Form: https://www.icann.org/wicf"],
|
||||
"description":["URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"],
|
||||
"links":[
|
||||
{
|
||||
"type":"text/html",
|
||||
"rel":"alternate",
|
||||
"href":"https://www.icann.org/wicf"
|
||||
"href":"https://icann.org/wicf"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -144,12 +144,12 @@
|
||||
},
|
||||
{
|
||||
"title": "RDDS Inaccuracy Complaint Form",
|
||||
"description" : ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://www.icann.org/wicf"],
|
||||
"description" : ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"],
|
||||
"links" :
|
||||
[
|
||||
{
|
||||
"rel" : "alternate",
|
||||
"href" : "https://www.icann.org/wicf",
|
||||
"href" : "https://icann.org/wicf",
|
||||
"type" : "text/html"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
},
|
||||
{
|
||||
"objectClassName": "entity",
|
||||
"handle": "REDACTED FOR PRIVACY",
|
||||
"handle": "",
|
||||
"roles": ["administrative"],
|
||||
"remarks": [
|
||||
{
|
||||
@@ -166,31 +166,14 @@
|
||||
"vcardArray": [
|
||||
"vcard",
|
||||
[
|
||||
["version",{},"text","4.0"],
|
||||
["fn",{},"text","REDACTED FOR PRIVACY"],
|
||||
["org",{},"text","REDACTED FOR PRIVACY"],
|
||||
[
|
||||
"adr",
|
||||
{},
|
||||
"text",
|
||||
[
|
||||
"",
|
||||
"",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"XX"
|
||||
]
|
||||
],
|
||||
["tel",{"type":["voice"]},"uri","tel:REDACTED FOR PRIVACY"],
|
||||
["tel",{"type":["fax"]},"uri","tel:REDACTED FOR PRIVACY"]
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"objectClassName": "entity",
|
||||
"handle": "REDACTED FOR PRIVACY",
|
||||
"handle": "",
|
||||
"roles": ["technical"],
|
||||
"remarks": [
|
||||
{
|
||||
@@ -219,31 +202,14 @@
|
||||
"vcardArray": [
|
||||
"vcard",
|
||||
[
|
||||
["version",{},"text","4.0"],
|
||||
["fn",{},"text","REDACTED FOR PRIVACY"],
|
||||
["org",{},"text","REDACTED FOR PRIVACY"],
|
||||
[
|
||||
"adr",
|
||||
{},
|
||||
"text",
|
||||
[
|
||||
"",
|
||||
"",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"XX"
|
||||
]
|
||||
],
|
||||
["tel",{"type":["voice"]},"uri","tel:REDACTED FOR PRIVACY"],
|
||||
["tel",{"type":["fax"]},"uri","tel:REDACTED FOR PRIVACY"]
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"objectClassName": "entity",
|
||||
"handle": "REDACTED FOR PRIVACY",
|
||||
"handle": "",
|
||||
"roles": ["registrant"],
|
||||
"remarks": [
|
||||
{
|
||||
@@ -272,25 +238,8 @@
|
||||
"vcardArray": [
|
||||
"vcard",
|
||||
[
|
||||
["version",{},"text","4.0"],
|
||||
["fn",{},"text","REDACTED FOR PRIVACY"],
|
||||
["org",{},"text","REDACTED FOR PRIVACY"],
|
||||
[
|
||||
"adr",
|
||||
{},
|
||||
"text",
|
||||
[
|
||||
"",
|
||||
"",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"XX"
|
||||
]
|
||||
],
|
||||
["tel",{"type":["voice"]},"uri","tel:REDACTED FOR PRIVACY"],
|
||||
["tel",{"type":["fax"]},"uri","tel:REDACTED FOR PRIVACY"]
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"objectClassName" : "entity",
|
||||
"handle" : "REDACTED FOR PRIVACY",
|
||||
"handle" : "",
|
||||
"roles" : ["registrant"],
|
||||
"events": [
|
||||
{
|
||||
@@ -11,25 +11,8 @@
|
||||
"vcardArray": [
|
||||
"vcard",
|
||||
[
|
||||
["version",{},"text","4.0"],
|
||||
["fn",{},"text","REDACTED FOR PRIVACY"],
|
||||
["org",{},"text","REDACTED FOR PRIVACY"],
|
||||
[
|
||||
"adr",
|
||||
{},
|
||||
"text",
|
||||
[
|
||||
"",
|
||||
"",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"REDACTED FOR PRIVACY",
|
||||
"XX"
|
||||
]
|
||||
],
|
||||
["tel",{"type":["voice"]},"uri","tel:REDACTED FOR PRIVACY"],
|
||||
["tel",{"type":["fax"]},"uri","tel:REDACTED FOR PRIVACY"]
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
],
|
||||
"remarks": [
|
||||
|
||||
@@ -59,12 +59,12 @@
|
||||
},
|
||||
{
|
||||
"title": "RDDS Inaccuracy Complaint Form",
|
||||
"description": ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://www.icann.org/wicf"],
|
||||
"description": ["URL of the ICANN RDDS Inaccuracy Complaint Form: https://icann.org/wicf"],
|
||||
"links":
|
||||
[
|
||||
{
|
||||
"rel": "alternate",
|
||||
"href": "https://www.icann.org/wicf",
|
||||
"href": "https://icann.org/wicf",
|
||||
"type": "text/html"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -704,6 +704,7 @@
|
||||
anchor_tenant_add_grace_period_length interval not null,
|
||||
auto_renew_grace_period_length interval not null,
|
||||
automatic_transfer_length interval not null,
|
||||
breakglass_mode boolean not null,
|
||||
claims_period_end timestamptz not null,
|
||||
create_billing_cost_amount numeric(19, 2),
|
||||
create_billing_cost_currency text,
|
||||
|
||||
@@ -16,7 +16,6 @@ package google.registry.proxy;
|
||||
|
||||
import static google.registry.util.ResourceUtils.readResourceBytes;
|
||||
|
||||
import com.google.auth.oauth2.GoogleCredentials;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -44,7 +43,6 @@ import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||
import java.io.IOException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.function.Supplier;
|
||||
@@ -116,7 +114,7 @@ public final class EppProtocolModule {
|
||||
config.epp.headerLengthBytes,
|
||||
// Adjustment applied to the header field value in order to obtain message length.
|
||||
-config.epp.headerLengthBytes,
|
||||
// Initial bytes to strip (i. e. strip the length header).
|
||||
// Initial bytes to strip (i.e. strip the length header).
|
||||
config.epp.headerLengthBytes);
|
||||
}
|
||||
|
||||
@@ -149,18 +147,12 @@ public final class EppProtocolModule {
|
||||
|
||||
@Provides
|
||||
static EppServiceHandler provideEppServiceHandler(
|
||||
Supplier<GoogleCredentials> refreshedCredentialsSupplier,
|
||||
@Named("iapClientId") Optional<String> iapClientId,
|
||||
@Named("idToken") Supplier<String> idTokenSupplier,
|
||||
@Named("hello") byte[] helloBytes,
|
||||
FrontendMetrics metrics,
|
||||
ProxyConfig config) {
|
||||
return new EppServiceHandler(
|
||||
config.epp.relayHost,
|
||||
config.epp.relayPath,
|
||||
refreshedCredentialsSupplier,
|
||||
iapClientId,
|
||||
helloBytes,
|
||||
metrics);
|
||||
config.epp.relayHost, config.epp.relayPath, idTokenSupplier, helloBytes, metrics);
|
||||
}
|
||||
|
||||
@Singleton
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user