1
0
mirror of https://github.com/google/nomulus synced 2026-01-16 02:33:16 +00:00

Compare commits

...

32 Commits

Author SHA1 Message Date
gbrodman
ee5a2d3916 Include internal registrars in the console (#2821)
This allows us to also check / modify the CharlestonRoad registrar in
the console, and also allows us to test actions (like password reset)
using that registrar in the prod environment.
2025-09-05 20:37:23 +00:00
gbrodman
2b5643df4c Sort registrars list in console (#2820)
This was bugging me slightly
2025-09-05 18:44:17 +00:00
Pavlo Tkach
6bbd7a2290 Update proxy resources, increase ssl handshake timeout (#2819) 2025-09-05 18:09:55 +00:00
Weimin Yu
77ab80f3dc Fix OOM in UploadBsaUnavailableDomains action (#2817)
* Fix OOM in UploadBsaUnavailableDomains action

The action was using string concatenation to generate the upload content.
This causes an OOM when string length exceeds 25MB on our current VM.

This PR witches to streaming upload.

Also added an HTTP upload test.

* Fix OOM in UploadBsaUnavailableDomains action

The action was using string concatenation to generate the upload content.
This causes an OOM when string length exceeds 25MB on our current VM.

This PR witches to streaming upload.

Also added an HTTP upload test.
2025-09-03 18:25:56 +00:00
Pavlo Tkach
5e1cd0120f Adjust proxy resource allocation and update nomulus compute class (#2814) 2025-08-28 18:49:16 +00:00
Weimin Yu
0167dad85f Fix OOM error in BsaValidation (#2813)
Error happened in the case that an unblockable name reported with
'Registered' as reason has been deregistered. We tried to check the
deletion time of the domain to decide if this is a transient error
that is no worth reporting. However, we forgot that we do not have
the domain key in this case.

As best-effort action, and with a case that rarely happens, we decide
not to make the optimization (staleness check) in thise case.
2025-08-27 15:47:13 +00:00
Weimin Yu
1eaf3d4aa8 Fix the schema-verifier in Cloud Build (#2812)
This is the same problem as the one that broke the Java test:

pg_dump upgrade to 17.6 added new meta command lines.
2025-08-25 17:17:49 +00:00
Pavlo Tkach
d9c46170dd Add time-limited logs to Epp Login flow to test increased latency (#2810) 2025-08-22 20:30:53 +00:00
gbrodman
e8a475f48b Remove Contact objects in RDAP output (#2811)
Note: this still includes "contacts" for registrars, which are actually
a different concept that we call RegistrarPoc. That's different from
"Contact" objects, e.g. registrant.
2025-08-22 20:25:20 +00:00
gbrodman
bdaab9daa5 Tag more junit versions to <6.0 (#2809)
We're still picking up some 6.0.0-M2 jars which are causing failures at
least for me when trying to run individual tests in IDEA.
2025-08-22 02:55:59 +00:00
gbrodman
7e07fabf7e Return RDAP 404 for domain w/nonexistent TLD (#2808)
The TLD is technically valid but it doesn't exist for us -- we should
return 404 instead of 400 in these situations according to the RDAP
conformance docs
2025-08-21 15:31:51 +00:00
gbrodman
16859bb36a Use https in RDAP URLs provided (#2807)
Load balancer / internal redirections can result in the final request
URL lacking "https" when finally getting to the servlet. As a result,
even if you use https in the request, the resulting URL can be plain
http.

We need to include the actual (HTTPS) URL in the output, so replace it.
2025-08-20 14:15:54 +00:00
Weimin Yu
7c92928f2c Update gradle dependency locks (#2806)
Also emoved Junit-4.
2025-08-19 16:17:47 +00:00
Pavlo Tkach
de8d205657 Make k8s adjustments (#2803)
This increases hikari fetch size to 40 from 20 in order to decrease the
amount of round trips
This also sets lower CPU as we seem to have overshot CPU consumtion
This also set min replicas to 8 for EPP and max to 16 as we've been
running on 8-10 for the last week
2025-08-19 14:24:11 +00:00
gbrodman
4738b979e4 Add FE for password-reset verification (#2795)
Tested locally and on alpha with dummy values (and throwing an
exception).

I was able to reuse a bit of code from the EPP password reset, but not
all of it.
2025-08-19 03:00:44 +00:00
Ben McIlwain
a61a667992 Add expiry_access_period_enabled boolean column to Tld table (#2804)
This is the first in a series of PRs to implement the expiry access period
(XAP).  The overall fee schedules will be set in YAML config files, so the only
DB change necessary should be this single new boolean column on the Tld entity,
which defaults to false so as to require XAP explicitly being turned on for a
given TLD.

BUG=http://b/437398822
2025-08-18 22:32:46 +00:00
Weimin Yu
1164070576 Update golden schema comparison in schema test (#2805)
Postgresql-17.6 introduces two new lines in pg_dump output as a
security feature: `\restricted {HASH}` and `\unrestricted {HASH}`.

We filter out lines starting with these two prefixes when comparing
schemas.

The db upgrade also adds two empty lines to the pg_dump output. We
know ignore all empty lines when comparing schemas.
2025-08-18 20:41:47 +00:00
gbrodman
d23640a54f Update LoadTestAction for GKE and no-contacts (#2800)
It's necessary to remove the GAE-related code (and use GKE launch
commands instead), and we might as well remove contact-related fields
and actions because of the upcoming move to the minimum data set.
2025-08-13 18:35:46 +00:00
Pavlo Tkach
cc347264f1 Revert "Remove nodeSelector from k8s deployments (#2798)" (#2799)
This reverts commit 5cef2dd8b5.
We faced CPU quota issue with standard machine type, so rolling back to c4
for now to monitor server performance and decide if we want to try to
downgrade again in the future.
2025-08-13 15:05:54 +00:00
Pavlo Tkach
e5d4cbb9fc Increase hikari maximum pool size (#2802) 2025-08-12 21:01:28 +00:00
gbrodman
8c1e0ff4de Chage run-time of DeleteExpiredDomainsAction (#2801)
We probably want this to run before the billing recurrence expansion
pipeline just in case there are any domains that should be deleted
before their billing recurrence gets expanded.
2025-08-12 20:48:04 +00:00
Pavlo Tkach
5cef2dd8b5 Remove nodeSelector from k8s deployments (#2798)
nodeSelector can limit scheduling capabilities of k8s, which leads to delays in assigning new workloads. Since we do not require and particular machine for execution it can be removed.
2025-08-10 16:16:48 +00:00
gbrodman
62b2585220 Fix load-testing URL in backend routing k8s file (#2797) 2025-08-08 20:20:40 +00:00
sharma1210
8692fe35db Provide specific reason for invalid SSL certificate (#2792)
* Fix: Robustly parse certs and provide specific errors

* Add test for expired certificate failure

* fixing indentation

* fixing indentation

* Update SecurityActionTest.java

* Update SecurityActionTest.java for correcting the testcase

* Fix: Provide indentation fix

* Fixing Deduplication in test
2025-08-08 18:41:14 +00:00
Pavlo Tkach
18614ba11e Update resource allocation for all Nomulus GKE deployments (#2796)
ALl deployments received update to averageUtilization cpu. This should allow us to stay ahead of the curve of traffic and create instances before we cpu reached the limit.
Frontend cpu allocation has caused "noise neighbors" problem with pods assigned to nodes where there's not enough bursting capacity, so I increased it.
Adjusted rest of the deployments according to their utilization.
2025-08-08 17:55:08 +00:00
Pavlo Tkach
427f6db820 Update resource allocation for proxy deployment (#2794)
Missing resource requests as well as metrics for when to evict resource
produced situation when under load k8s struggled to assign pods. This
adds default resource requirements based on 2 weeks metrics and
instructions when resource should be evicted.
2025-08-08 15:35:22 +00:00
Weimin Yu
5aa40b2208 Fix error handling in CopyDetailReportsAction (#2793)
* Fix error handling in CopyDetailReportsAction

The action tries to record errors per registrar in an ImmutableMap, without realizing that
there may be duplicate keys due to retries.

Switched to the `buildKeepingLast` method to build the map.

* Addressing comments and rebase
2025-08-06 16:43:29 +00:00
Pavlo Tkach
95c89bc856 Add registrar id header to proxy requests (#2791) 2025-08-05 17:57:04 +00:00
gbrodman
c21b66f0fb Add reset-EPP-password frontend component (#2786) 2025-08-01 19:53:20 +00:00
gbrodman
b070c46231 Allow superuser status to override EPP resource delete prohibited status (#2789) 2025-08-01 19:51:44 +00:00
Weimin Yu
a20eb8d1e9 Fix failures in retries when inserting new objects (#2788)
Given an entity with auto-filled id fields (annotated with
@GeneratedValue, with null as initial value), when inserting it
using Hibernate, the id fields will be filled with non-nulls even
if the transaction fails.

If the same entity instance is used again in a retry, Hibernate mistakes
it as a detached entity and raises an error.

The work around is to make a new copy of the entity in each transaction.
This PR applies this pattern to affected entity types.

We considered applying this pattern to JpaTransactionManagerImpl's insert
method so that individual call sites do not have to change. However, we
decided against it because:

- It is unnecessary for entity types that do not have auto-filled id

- The JpaTransactionManager cannot tell if copying is cheap or
  expensive. It is better exposing this to the user.

- The JpaTransactionManager needs to know how to clone entities. A new
  interface may need to be introduced just for a handful of use cases.
2025-08-01 18:53:50 +00:00
Ben McIlwain
338b8edb97 Make the contacts prohibited feature flag for min reg data set more lenient (#2787)
It will now only throw errors on domain updates if a new contact/registrant has
been specified where none was previously present. This means that domain updates
on unrelated fields (e.g. nameserver changes) will succeed even if there is
existing contact data that the update is not removing.

This is a follow-up to #2781.

BUG=http://b/434958659
2025-07-29 20:54:58 +00:00
190 changed files with 6414 additions and 10876 deletions

View File

@@ -3,7 +3,7 @@
# This file is expected to be part of source control.
aopalliance:aopalliance:1.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
com.github.ben-manes.caffeine:caffeine:3.2.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
com.github.ben-manes.caffeine:caffeine:3.2.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
com.google.auto.value:auto-value-annotations:1.11.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
@@ -12,13 +12,13 @@ com.google.auto:auto-common:1.2.1=annotationProcessor,errorprone,testAnnotationP
com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,errorprone,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath,testing,testingAnnotationProcessor,testingCompileClasspath
com.google.errorprone:error_prone_annotation:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
com.google.errorprone:error_prone_annotations:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
com.google.errorprone:error_prone_annotations:2.36.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
com.google.errorprone:error_prone_annotations:2.40.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
com.google.errorprone:error_prone_annotations:2.7.1=checkstyle
com.google.errorprone:error_prone_check_api:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
com.google.errorprone:error_prone_core:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
com.google.errorprone:error_prone_type_annotations:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
com.google.errorprone:javac:9+181-r4173-1=errorproneJavac
com.google.flogger:flogger:0.8=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
com.google.flogger:flogger:0.9=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
com.google.guava:failureaccess:1.0.1=annotationProcessor,checkstyle,errorprone,testAnnotationProcessor,testingAnnotationProcessor
com.google.guava:failureaccess:1.0.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
com.google.guava:guava-parent:32.1.1-jre=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
@@ -37,15 +37,14 @@ commons-collections:commons-collections:3.2.2=checkstyle
info.picocli:picocli:4.6.2=checkstyle
io.github.eisop:dataflow-errorprone:3.34.0-eisop1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
io.github.java-diff-utils:java-diff-utils:4.15=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
io.github.java-diff-utils:java-diff-utils:4.16=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
jakarta.inject:jakarta.inject-api:2.0.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
javax.inject:javax.inject:1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
joda-time:joda-time:2.13.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
joda-time:joda-time:2.14.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
net.sf.saxon:Saxon-HE:10.6=checkstyle
org.antlr:antlr4-runtime:4.9.3=checkstyle
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.checkerframework:checker-compat-qual:2.5.3=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
org.checkerframework:checker-qual:3.12.0=checkstyle
org.checkerframework:checker-qual:3.33.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
org.checkerframework:checker-qual:3.42.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
@@ -56,12 +55,12 @@ org.jacoco:org.jacoco.core:0.8.12=jacocoAnt
org.jacoco:org.jacoco.report:0.8.12=jacocoAnt
org.javassist:javassist:3.28.0-GA=checkstyle
org.jspecify:jspecify:1.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
org.junit.jupiter:junit-jupiter-api:5.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:1.12.1=testCompileClasspath,testRuntimeClasspath
org.junit:junit-bom:5.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:1.13.4=testCompileClasspath,testRuntimeClasspath
org.junit:junit-bom:5.13.4=testCompileClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
org.ow2.asm:asm-commons:9.7=jacocoAnt
org.ow2.asm:asm-tree:9.7=jacocoAnt

View File

@@ -92,17 +92,19 @@ public class TextDiffSubject extends Subject {
private ImmutableList<String> filterComments(List<String> lines) {
return lines.stream()
.filter(line -> !line.isBlank())
.filter(line -> comments.stream().noneMatch(line::startsWith))
.collect(ImmutableList.toImmutableList());
}
public void hasSameContentAs(List<String> expectedContent) {
checkNotNull(expectedContent, "expectedContent");
ImmutableList<String> expected = filterComments(expectedContent);
if (filterComments(expected).equals(filterComments(actual))) {
ImmutableList<String> filteredExpected = filterComments(expectedContent);
ImmutableList<String> filteredActual = filterComments(actual);
if (filteredExpected.equals(filteredActual)) {
return;
}
String diffString = diffFormat.generateDiff(expected, actual);
String diffString = diffFormat.generateDiff(filteredExpected, filteredActual);
failWithoutActual(
Fact.simpleFact(
Joiner.on('\n')

View File

@@ -26,6 +26,7 @@ import SecurityComponent from './settings/security/security.component';
import { SettingsComponent } from './settings/settings.component';
import { SupportComponent } from './support/support.component';
import RdapComponent from './settings/rdap/rdap.component';
import { PasswordResetVerifyComponent } from './shared/components/passwordReset/passwordResetVerify.component';
export interface RouteWithIcon extends Route {
iconName?: string;
@@ -38,6 +39,10 @@ export const PATHS = {
};
export const routes: RouteWithIcon[] = [
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{
path: PasswordResetVerifyComponent.PATH,
component: PasswordResetVerifyComponent,
},
{
path: RegistryLockVerifyComponent.PATH,
component: RegistryLockVerifyComponent,

View File

@@ -61,6 +61,8 @@ import { ForceFocusDirective } from './shared/directives/forceFocus.directive';
import RdapComponent from './settings/rdap/rdap.component';
import RdapEditComponent from './settings/rdap/rdapEdit.component';
import { PocReminderComponent } from './shared/components/pocReminder/pocReminder.component';
import { PasswordResetVerifyComponent } from './shared/components/passwordReset/passwordResetVerify.component';
import { PasswordInputForm } from './shared/components/passwordReset/passwordInputForm.component';
@NgModule({
declarations: [SelectedRegistrarWrapper],
@@ -84,10 +86,12 @@ export class SelectedRegistrarModule {}
NavigationComponent,
NewRegistrarComponent,
NotificationsComponent,
PasswordInputForm,
PasswordResetVerifyComponent,
PocReminderComponent,
RdapComponent,
RdapEditComponent,
ReasonDialogComponent,
PocReminderComponent,
RegistrarComponent,
RegistrarDetailsComponent,
RegistrarSelectorComponent,

View File

@@ -25,7 +25,10 @@ export class RegistrarSelectorComponent {
registrarInput = signal<string>(this.registrarService.registrarId());
filteredOptions?: string[];
allRegistrarIds = computed(() =>
this.registrarService.registrars().map((r) => r.registrarId)
this.registrarService
.registrars()
.map((r) => r.registrarId)
.sort()
);
constructor(protected registrarService: RegistrarService) {

View File

@@ -16,65 +16,22 @@
<p class="secondary-text">
Passwords must be between 6 and 16 alphanumeric characters
</p>
<form
(ngSubmit)="save()"
<password-input-form-component
[displayOldPasswordField]="true"
[formGroup]="passwordUpdateForm"
class="settings-security__edit-password-form"
>
<div class="settings-security__edit-password-field">
<mat-form-field appearance="outline">
<mat-label>Old password: </mat-label>
<input
matInput
type="text"
formControlName="oldPassword"
required
autocomplete="current-password"
/>
<mat-error *ngIf="hasError('oldPassword') as errorText">{{
errorText
}}</mat-error>
</mat-form-field>
</div>
<div class="settings-security__edit-password-field">
<mat-form-field appearance="outline">
<mat-label>New password: </mat-label>
<input
matInput
type="text"
formControlName="newPassword"
required
autocomplete="new-password"
/>
<mat-error *ngIf="hasError('newPassword') as errorText">{{
errorText
}}</mat-error>
</mat-form-field>
</div>
<div class="settings-security__edit-password-field">
<mat-form-field appearance="outline">
<mat-label>Confirm new password: </mat-label>
<input
matInput
type="text"
formControlName="newPasswordRepeat"
required
autocomplete="new-password"
/>
<mat-error *ngIf="hasError('newPasswordRepeat') as errorText">{{
errorText
}}</mat-error>
</mat-form-field>
</div>
(submitResults)="save($event)"
/>
@if(userDataService.userData()?.isAdmin) {
<div class="settings-security__reset-password-field">
<h2>Need to reset your EPP password?</h2>
<button
mat-flat-button
color="primary"
[disabled]="!passwordUpdateForm.valid"
aria-label="Save epp password update"
type="submit"
class="settings-security__edit-password-save"
aria-label="Reset EPP password via email"
(click)="requestEppPasswordReset()"
>
Save
Reset EPP password via email
</button>
</form>
</div>
}
</div>

View File

@@ -1,16 +1,19 @@
.settings-security__edit-password {
max-width: 616px;
&-field {
width: 100%;
mat-form-field {
margin-bottom: 20px;
width: 100%;
}
}
&-form {
margin-top: 30px;
}
&-save {
margin-top: 30px;
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
.settings-security {
&__reset-password-field {
margin-top: 60px;
}
}

View File

@@ -14,20 +14,46 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Component } from '@angular/core';
import {
AbstractControl,
FormControl,
FormGroup,
ValidatorFn,
Validators,
} from '@angular/forms';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { RegistrarService } from 'src/app/registrar/registrar.service';
import { SecurityService } from './security.service';
import { UserDataService } from 'src/app/shared/services/userData.service';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { CommonModule } from '@angular/common';
import { MaterialModule } from 'src/app/material.module';
import { filter, switchMap, take } from 'rxjs';
import { BackendService } from 'src/app/shared/services/backend.service';
import {
PasswordInputForm,
PasswordResults,
} from 'src/app/shared/components/passwordReset/passwordInputForm.component';
type errorCode = 'required' | 'maxlength' | 'minlength' | 'passwordsDontMatch';
@Component({
selector: 'app-reset-epp-password-dialog',
template: `
<h2 mat-dialog-title>Please confirm the password reset:</h2>
<mat-dialog-content>
This will send an EPP password reset email to the admin POC.
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button (click)="onCancel()">Cancel</button>
<button mat-button color="warn" (click)="onSave()">Confirm</button>
</mat-dialog-actions>
`,
imports: [CommonModule, MaterialModule],
})
export class ResetEppPasswordComponent {
constructor(public dialogRef: MatDialogRef<ResetEppPasswordComponent>) {}
type errorFriendlyText = { [type in errorCode]: String };
onSave(): void {
this.dialogRef.close(true);
}
onCancel(): void {
this.dialogRef.close(false);
}
}
@Component({
selector: 'app-epp-password-edit',
@@ -36,76 +62,38 @@ type errorFriendlyText = { [type in errorCode]: String };
standalone: false,
})
export default class EppPasswordEditComponent {
MIN_MAX_LENGHT = new String(
'Passwords must be between 6 and 16 alphanumeric characters'
);
errorTextMap: errorFriendlyText = {
required: "This field can't be empty",
maxlength: this.MIN_MAX_LENGHT,
minlength: this.MIN_MAX_LENGHT,
passwordsDontMatch: "Passwords don't match",
};
constructor(
public securityService: SecurityService,
private _snackBar: MatSnackBar,
public registrarService: RegistrarService
) {}
hasError(controlName: string) {
const maybeErrors = this.passwordUpdateForm.get(controlName)?.errors;
const maybeError =
maybeErrors && (Object.keys(maybeErrors)[0] as errorCode);
if (maybeError) {
return this.errorTextMap[maybeError];
}
return '';
}
newPasswordsMatch: ValidatorFn = (control: AbstractControl) => {
if (
this.passwordUpdateForm?.get('newPassword')?.value ===
this.passwordUpdateForm?.get('newPasswordRepeat')?.value
) {
this.passwordUpdateForm?.get('newPasswordRepeat')?.setErrors(null);
} else {
// latest angular just won't detect the error without setTimeout
setTimeout(() => {
this.passwordUpdateForm
?.get('newPasswordRepeat')
?.setErrors({ passwordsDontMatch: control.value });
});
}
return null;
};
static EPP_VALIDATORS = [
Validators.required,
Validators.minLength(6),
Validators.maxLength(16),
PasswordInputForm.newPasswordsMatch,
];
passwordUpdateForm = new FormGroup({
oldPassword: new FormControl('', [Validators.required]),
newPassword: new FormControl('', [
Validators.required,
Validators.minLength(6),
Validators.maxLength(16),
this.newPasswordsMatch,
]),
newPasswordRepeat: new FormControl('', [
Validators.required,
Validators.minLength(6),
Validators.maxLength(16),
this.newPasswordsMatch,
]),
newPassword: new FormControl('', EppPasswordEditComponent.EPP_VALIDATORS),
newPasswordRepeat: new FormControl(
'',
EppPasswordEditComponent.EPP_VALIDATORS
),
});
save() {
const { oldPassword, newPassword, newPasswordRepeat } =
this.passwordUpdateForm.value;
if (!oldPassword || !newPassword || !newPasswordRepeat) return;
constructor(
public registrarService: RegistrarService,
public securityService: SecurityService,
protected userDataService: UserDataService,
private backendService: BackendService,
private resetPasswordDialog: MatDialog,
private _snackBar: MatSnackBar
) {}
save(passwordResults: PasswordResults) {
this.securityService
.saveEppPassword({
registrarId: this.registrarService.registrarId(),
oldPassword,
newPassword,
newPasswordRepeat,
oldPassword: passwordResults.oldPassword!,
newPassword: passwordResults.newPassword,
newPasswordRepeat: passwordResults.newPasswordRepeat,
})
.subscribe({
complete: () => {
@@ -120,4 +108,26 @@ export default class EppPasswordEditComponent {
goBack() {
this.securityService.isEditingPassword = false;
}
sendEppPasswordResetRequest() {
return this.backendService.requestEppPasswordReset(
this.registrarService.registrarId()
);
}
requestEppPasswordReset() {
const dialogRef = this.resetPasswordDialog.open(ResetEppPasswordComponent);
dialogRef
.afterClosed()
.pipe(
take(1),
filter((result) => !!result)
)
.pipe(switchMap((_) => this.sendEppPasswordResetRequest()))
.subscribe({
next: (_) => this.goBack(),
error: (err: HttpErrorResponse) =>
this._snackBar.open(err.error || err.message),
});
}
}

View File

@@ -0,0 +1,63 @@
<form
(ngSubmit)="save()"
[formGroup]="formGroup()!"
class="console-app__password-input-form"
>
@if (displayOldPasswordField()) {
<div class="console-app__password-input-form-field">
<mat-form-field appearance="outline">
<mat-label>Old password: </mat-label>
<input
matInput
type="text"
formControlName="oldPassword"
required
autocomplete="current-password"
/>
<mat-error *ngIf="hasError('oldPassword') as errorText">{{
errorText
}}</mat-error>
</mat-form-field>
</div>
}
<div class="console-app__password-input-form-field">
<mat-form-field appearance="outline">
<mat-label>New password: </mat-label>
<input
matInput
type="text"
formControlName="newPassword"
required
autocomplete="new-password"
/>
<mat-error *ngIf="hasError('newPassword') as errorText">{{
errorText
}}</mat-error>
</mat-form-field>
</div>
<div class="console-app__password-input-form-field">
<mat-form-field appearance="outline">
<mat-label>Confirm new password: </mat-label>
<input
matInput
type="text"
formControlName="newPasswordRepeat"
required
autocomplete="new-password"
/>
<mat-error *ngIf="hasError('newPasswordRepeat') as errorText">{{
errorText
}}</mat-error>
</mat-form-field>
</div>
<button
mat-flat-button
color="primary"
[disabled]="!formGroup()?.valid"
aria-label="Save new password"
type="submit"
class="console-app__password-input-form-save"
>
Save
</button>
</form>

View File

@@ -0,0 +1,30 @@
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
.console-app__password-input-form {
max-width: 450px;
&-field {
width: 100%;
mat-form-field {
margin-bottom: 20px;
width: 100%;
}
}
&-form {
margin-top: 30px;
}
&-save {
margin-top: 30px;
}
}

View File

@@ -0,0 +1,82 @@
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, EventEmitter, input, Output } from '@angular/core';
import { AbstractControl, FormGroup, ValidatorFn } from '@angular/forms';
type errorCode = 'required' | 'maxlength' | 'minlength' | 'passwordsDontMatch';
type errorFriendlyText = { [type in errorCode]: String };
export interface PasswordResults {
oldPassword: string | null;
newPassword: string;
newPasswordRepeat: string;
}
@Component({
selector: 'password-input-form-component',
templateUrl: './passwordInputForm.component.html',
styleUrls: ['./passwordInputForm.component.scss'],
standalone: false,
})
export class PasswordInputForm {
static newPasswordsMatch: ValidatorFn = (control: AbstractControl) => {
const parent = control.parent;
if (
parent?.get('newPassword')?.value ===
parent?.get('newPasswordRepeat')?.value
) {
parent?.get('newPasswordRepeat')?.setErrors(null);
} else {
// latest angular just won't detect the error without setTimeout
setTimeout(() => {
parent
?.get('newPasswordRepeat')
?.setErrors({ passwordsDontMatch: control.value });
});
}
return null;
};
MIN_MAX_LENGTH = 'Passwords must be between 6 and 16 alphanumeric characters';
errorTextMap: errorFriendlyText = {
required: "This field can't be empty",
maxlength: this.MIN_MAX_LENGTH,
minlength: this.MIN_MAX_LENGTH,
passwordsDontMatch: "Passwords don't match",
};
displayOldPasswordField = input<boolean>(false);
formGroup = input<FormGroup>();
@Output() submitResults = new EventEmitter<PasswordResults>();
hasError(controlName: string) {
const maybeErrors = this.formGroup()!.get(controlName)?.errors;
const maybeError =
maybeErrors && (Object.keys(maybeErrors)[0] as errorCode);
if (maybeError) {
return this.errorTextMap[maybeError];
}
return '';
}
save() {
const results: PasswordResults = this.formGroup()!.value;
if (this.displayOldPasswordField() && !results.oldPassword) return;
if (!results.newPassword || !results.newPasswordRepeat) return;
this.submitResults.emit(results);
}
}

View File

@@ -0,0 +1,27 @@
<p>
<button mat-icon-button aria-label="Go home" [routerLink]="['']">
<mat-icon>arrow_back</mat-icon>
</button>
</p>
@if (isLoading) {
<div class="console-app__password-reset-verify-spinner">
<mat-spinner />
</div>
} @else if (errorMessage) {
<h1 class="mat-headline-4">Failure</h1>
<div class="console-app__password-reset-content">
<div class="console-app__password-reset-subhead">
An error occurred: {{ errorMessage }}.<br /><br />Please double-check the
verification code and try again.
</div>
</div>
} @else {
<div class="console-app__password-reset-verify">
<h1 class="mat-headline-4">{{ type }} password reset</h1>
<password-input-form-component
[displayOldPasswordField]="false"
[formGroup]="passwordUpdateForm!"
(submitResults)="save($event)"
/>
</div>
}

View File

@@ -0,0 +1,105 @@
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component } from '@angular/core';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { HttpErrorResponse } from '@angular/common/http';
import { take } from 'rxjs';
import { RegistrarService } from 'src/app/registrar/registrar.service';
import { BackendService } from '../../services/backend.service';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import {
PasswordInputForm,
PasswordResults,
} from './passwordInputForm.component';
import EppPasswordEditComponent from 'src/app/settings/security/eppPasswordEdit.component';
export interface PasswordResetVerifyResponse {
registrarId: string;
type: string;
}
@Component({
selector: 'app-password-reset-verify',
templateUrl: './passwordResetVerify.component.html',
standalone: false,
})
export class PasswordResetVerifyComponent {
public static PATH = 'password-reset-verify';
REGISTRY_LOCK_PASSWORD_VALIDATORS = [
Validators.required,
PasswordInputForm.newPasswordsMatch,
];
isLoading = true;
type?: string;
errorMessage?: string;
requestVerificationCode = '';
passwordUpdateForm: FormGroup<any> | null = null;
constructor(
protected backendService: BackendService,
protected registrarService: RegistrarService,
private route: ActivatedRoute,
private router: Router
) {}
ngOnInit() {
this.route.queryParamMap.pipe(take(1)).subscribe((params: ParamMap) => {
this.requestVerificationCode =
params.get('resetRequestVerificationCode') || '';
this.backendService
.getPasswordResetInformation(this.requestVerificationCode)
.subscribe({
error: (err: HttpErrorResponse) => {
this.isLoading = false;
this.errorMessage = err.error;
},
next: this.presentData.bind(this),
});
});
}
presentData(verificationResponse: PasswordResetVerifyResponse) {
this.type = verificationResponse.type === 'EPP' ? 'EPP' : 'Registry lock';
this.registrarService.registrarId.set(verificationResponse.registrarId);
const validators =
verificationResponse.type === 'EPP'
? EppPasswordEditComponent.EPP_VALIDATORS
: this.REGISTRY_LOCK_PASSWORD_VALIDATORS;
this.passwordUpdateForm = new FormGroup({
newPassword: new FormControl('', validators),
newPasswordRepeat: new FormControl('', validators),
});
this.isLoading = false;
}
save(passwordResults: PasswordResults) {
this.backendService
.finalizePasswordReset(
this.requestVerificationCode,
passwordResults.newPassword
)
.subscribe({
error: (err: HttpErrorResponse) => {
this.isLoading = false;
this.errorMessage = err.error;
},
next: (_) => this.router.navigate(['']),
});
}
}

View File

@@ -30,6 +30,7 @@ import {
import { Contact } from '../../settings/contact/contact.service';
import { EppPasswordBackendModel } from '../../settings/security/security.service';
import { UserData } from './userData.service';
import { PasswordResetVerifyResponse } from '../components/passwordReset/passwordResetVerify.component';
@Injectable()
export class BackendService {
@@ -291,4 +292,26 @@ export class BackendService {
registryLockEmail,
});
}
requestEppPasswordReset(registrarId: string) {
return this.http.post('/console-api/password-reset-request', {
type: 'EPP',
registrarId,
});
}
getPasswordResetInformation(
verificationCode: string
): Observable<PasswordResetVerifyResponse> {
return this.http.get<PasswordResetVerifyResponse>(
`/console-api/password-reset-verify?resetRequestVerificationCode=${verificationCode}`
);
}
finalizePasswordReset(verificationCode: string, newPassword: string) {
return this.http.post(
`/console-api/password-reset-verify?resetRequestVerificationCode=${verificationCode}`,
newPassword
);
}
}

View File

@@ -22,7 +22,6 @@ import { RegistrarService } from '../registrar/registrar.service';
import { SnackBarModule } from '../snackbar.module';
import { UserDetailsComponent } from './userDetails.component';
import { User, UsersService } from './users.service';
import { UserDataService } from '../shared/services/userData.service';
import { FormsModule } from '@angular/forms';
import { UsersListComponent } from './usersList.component';
import { MatSelectChange } from '@angular/material/select';
@@ -55,7 +54,6 @@ export class UsersComponent {
constructor(
protected registrarService: RegistrarService,
protected usersService: UsersService,
private userDataService: UserDataService,
private _snackBar: MatSnackBar
) {
effect(() => {

View File

@@ -58,6 +58,8 @@ def fragileTestPatterns = [
// Changes cache timeouts and for some reason appears to have contention
// with other tests.
"google/registry/whois/WhoisCommandFactoryTest.*",
// Breaks random other tests when running with standardTests.
"google/registry/bsa/UploadBsaUnavailableDomainsActionTest.*",
// Currently changes a global configuration parameter that for some reason
// results in timestamp inversions for other tests. TODO(mmuller): fix.
"google/registry/flows/host/HostInfoFlowTest.*",
@@ -272,7 +274,6 @@ dependencies {
testImplementation deps['org.hamcrest:hamcrest']
testImplementation deps['org.hamcrest:hamcrest-core']
testImplementation deps['org.hamcrest:hamcrest-library']
testImplementation deps['junit:junit']
testImplementation deps['org.junit.jupiter:junit-jupiter-api']
testImplementation deps['org.junit.jupiter:junit-jupiter-engine']
testImplementation deps['org.junit.jupiter:junit-jupiter-migrationsupport']

View File

@@ -4,20 +4,19 @@
aopalliance:aopalliance:1.0=annotationProcessor,compileClasspath,deploy_jar,errorprone,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
com.charleskorn.kaml:kaml:0.20.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-annotations:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-core:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-databind:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson:jackson-bom:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-annotations:2.20-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-core:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-databind:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson:jackson-bom:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml:classmate:1.5.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
com.github.ben-manes.caffeine:caffeine:3.2.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-api:3.4.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-transport-zerodep:3.4.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-transport:3.4.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.ben-manes.caffeine:caffeine:3.2.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-api:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-transport-zerodep:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.docker-java:docker-java-transport:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.jnr:jffi:1.3.13=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.jnr:jnr-a64asm:1.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.jnr:jnr-constants:0.10.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -32,85 +31,80 @@ com.google.api-client:google-api-client-jackson2:2.0.1=compileClasspath,nonprodC
com.google.api-client:google-api-client-jackson2:2.7.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-java6:2.1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-servlet:2.7.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-servlet:2.7.2=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api-client:google-api-client:2.7.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-servlet:2.8.1=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api-client:google-api-client:2.8.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.44.1-beta=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.50.0=testCompileClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.183.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.183.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigtable-v2:2.51.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.118.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-pubsublite-v1:1.15.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.55.0=testCompileClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.15.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.187.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.187.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigtable-v2:2.60.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.122.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-pubsublite-v1:1.15.9=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-control-v2:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.44.1-beta=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.50.0=testCompileClasspath
com.google.api.grpc:grpc-google-common-protos:2.50.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.183.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.183.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigtable-admin-v2:2.51.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigtable-v2:2.51.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-compute-v1:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-datastore-v1:0.116.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-firestore-v1:3.30.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-monitoring-v3:3.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-pubsub-v1:1.118.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-pubsublite-v1:1.15.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.55.0=testCompileClasspath
com.google.api.grpc:grpc-google-common-protos:2.58.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.15.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.15.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.187.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.187.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta:3.15.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigtable-admin-v2:2.60.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigtable-v2:2.60.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-compute-v1:1.82.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-datastore-v1:0.120.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-firestore-v1:3.31.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-monitoring-v3:3.65.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-pubsub-v1:1.122.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-pubsublite-v1:1.15.9=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-storage-control-v2:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.44.1-beta=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.50.0=testCompileClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.55.0=testCompileClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.141.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.149.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.162.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.141.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.149.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-common-protos:2.53.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-common-protos:2.54.1=testCompileClasspath
com.google.api.grpc:proto-google-iam-v1:1.45.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.48.0=compileClasspath,nonprodCompileClasspath
com.google.api.grpc:proto-google-iam-v1:1.49.1=testCompileClasspath
com.google.api:api-common:2.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api:api-common:2.46.1=testCompileClasspath
com.google.api:gax-grpc:2.59.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api:gax-grpc:2.62.0=compileClasspath,nonprodCompileClasspath
com.google.api:gax-grpc:2.63.1=testCompileClasspath
com.google.api:gax-httpjson:2.62.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api:gax-httpjson:2.63.1=testCompileClasspath
com.google.api:gax:2.62.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api:gax:2.63.1=testCompileClasspath
com.google.apis:google-api-services-admin-directory:directory_v1-rev20250217-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-bigquery:v2-rev20241222-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.162.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-common-protos:2.60.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.53.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.55.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api:api-common:2.52.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax-grpc:2.67.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api:gax-grpc:2.69.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api:gax-httpjson:2.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax:2.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-admin-directory:directory_v1-rev20250804-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-bigquery:v2-rev20250511-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20240310-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20250310-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v1-rev20250227-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-drive:v3-rev20250220-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-gmail:v1-rev20240520-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20250812-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v1-rev20250411-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-drive:v3-rev20250723-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-gmail:v1-rev20250630-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-groupssettings:v1-rev20220614-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-healthcare:v1-rev20240130-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-iam:v2-rev20250213-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-iam:v2-rev20250502-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-iamcredentials:v1-rev20211203-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-monitoring:v3-rev20250227-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-monitoring:v3-rev20250731-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-pubsub:v1-rev20220904-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-sheets:v4-rev20250211-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-sqladmin:v1beta4-rev20250205-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-storage:v1-rev20241206-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.apis:google-api-services-storage:v1-rev20250224-2.0.0=testCompileClasspath
com.google.auth:google-auth-library-credentials:1.33.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.auth:google-auth-library-oauth2-http:1.33.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-sheets:v4-rev20250616-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-sqladmin:v1beta4-rev20250613-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-storage:v1-rev20250524-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.apis:google-api-services-storage:v1-rev20250718-2.0.0=testCompileClasspath
com.google.auth:google-auth-library-credentials:1.37.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.auth:google-auth-library-oauth2-http:1.37.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.auto.service:auto-service-annotations:1.0.1=errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.auto.service:auto-service:1.1.1=annotationProcessor
@@ -121,50 +115,51 @@ com.google.auto:auto-common:1.2.1=annotationProcessor,errorprone,nonprodAnnotati
com.google.cloud.bigdataoss:gcsio:2.2.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.bigdataoss:util:2.2.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.bigtable:bigtable-client-core-config:1.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.datastore:datastore-v1-proto-client:2.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.datastore:datastore-v1-proto-client:2.29.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.opentelemetry:detector-resources-support:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.opentelemetry:exporter-metrics:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.opentelemetry:shared-resourcemapping:0.33.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud.sql:jdbc-socket-factory-core:1.23.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.sql:postgres-socket-factory:1.23.1=deploy_jar,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigquerystorage:3.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigtable:2.51.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-compute:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-grpc:2.49.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-grpc:2.53.1=testCompileClasspath
com.google.cloud.sql:jdbc-socket-factory-core:1.25.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud.sql:postgres-socket-factory:1.25.3=deploy_jar,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigquerystorage:3.15.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigtable:2.60.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-compute:1.82.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-grpc:2.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-grpc:2.59.0=testCompileClasspath
com.google.cloud:google-cloud-core-http:2.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-http:2.53.1=testCompileClasspath
com.google.cloud:google-cloud-core:2.49.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core:2.53.1=testCompileClasspath
com.google.cloud:google-cloud-firestore:3.30.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-monitoring:3.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-http:2.59.0=testCompileClasspath
com.google.cloud:google-cloud-core:2.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core:2.59.0=testCompileClasspath
com.google.cloud:google-cloud-firestore:3.31.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-monitoring:3.65.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-nio:0.127.24=testRuntimeClasspath
com.google.cloud:google-cloud-nio:0.127.33=testCompileClasspath
com.google.cloud:google-cloud-pubsub:1.136.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-pubsublite:1.15.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-nio:0.128.2=testCompileClasspath
com.google.cloud:google-cloud-pubsub:1.140.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-pubsublite:1.15.9=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-secretmanager:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-secretmanager:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:google-cloud-spanner:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-secretmanager:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:google-cloud-spanner:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-storage-control:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-storage:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-storage:2.50.0=testCompileClasspath
com.google.cloud:google-cloud-storage:2.55.0=testCompileClasspath
com.google.cloud:google-cloud-tasks:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-tasks:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:google-cloud-tasks:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:grpc-gcp:1.6.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:libraries-bom:26.48.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.30.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.31.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,errorprone,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.code.gson:gson:2.10.1=soy
com.google.code.gson:gson:2.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.common.html.types:types:1.0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
com.google.dagger:dagger-compiler:2.55=annotationProcessor,testAnnotationProcessor
com.google.dagger:dagger-spi:2.55=annotationProcessor,testAnnotationProcessor
com.google.dagger:dagger:2.55=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.devtools.ksp:symbol-processing-api:2.0.21-1.0.28=annotationProcessor,testAnnotationProcessor
com.google.dagger:dagger-compiler:2.57.1=annotationProcessor,testAnnotationProcessor
com.google.dagger:dagger-spi:2.57.1=annotationProcessor,testAnnotationProcessor
com.google.dagger:dagger:2.57=deploy_jar
com.google.dagger:dagger:2.57.1=annotationProcessor,compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.devtools.ksp:symbol-processing-api:2.1.21-2.0.2=annotationProcessor,testAnnotationProcessor
com.google.errorprone:error_prone_annotation:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.errorprone:error_prone_annotations:2.20.0=soy
com.google.errorprone:error_prone_annotations:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.errorprone:error_prone_annotations:2.36.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.errorprone:error_prone_annotations:2.41.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.errorprone:error_prone_annotations:2.7.1=checkstyle
com.google.errorprone:error_prone_check_api:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.errorprone:error_prone_core:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
@@ -181,25 +176,25 @@ com.google.flogger:google-extensions:0.7.4=soy
com.google.flogger:google-extensions:0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.googlejavaformat:google-java-format:1.5=annotationProcessor,testAnnotationProcessor
com.google.guava:failureaccess:1.0.1=checkstyle,errorprone,nonprodAnnotationProcessor,soy
com.google.guava:failureaccess:1.0.2=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.guava:failureaccess:1.0.2=annotationProcessor,testAnnotationProcessor
com.google.guava:failureaccess:1.0.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.guava:guava-parent:32.1.1-jre=errorprone,nonprodAnnotationProcessor,soy
com.google.guava:guava-testlib:33.3.0-jre=testRuntimeClasspath
com.google.guava:guava-testlib:33.4.0-jre=testCompileClasspath
com.google.guava:guava-testlib:33.4.8-jre=testCompileClasspath
com.google.guava:guava:31.0.1-jre=checkstyle
com.google.guava:guava:32.1.1-jre=errorprone,nonprodAnnotationProcessor,soy
com.google.guava:guava:33.0.0-jre=annotationProcessor,testAnnotationProcessor
com.google.guava:guava:33.4.0-jre=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.guava:guava:33.4.8-jre=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
com.google.gwt:gwt-user:2.10.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-apache-v2:1.45.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-apache-v2:1.46.3=testCompileClasspath
com.google.http-client:google-http-client-apache-v2:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-appengine:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-appengine:1.46.3=testCompileClasspath
com.google.http-client:google-http-client-gson:1.46.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-appengine:1.47.1=testCompileClasspath
com.google.http-client:google-http-client-gson:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-jackson2:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-jackson2:1.46.3=testCompileClasspath
com.google.http-client:google-http-client-protobuf:1.45.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client:1.46.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-jackson2:1.47.1=testCompileClasspath
com.google.http-client:google-http-client-protobuf:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.inject:guice:5.1.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.inject:guice:7.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
com.google.j2objc:j2objc-annotations:1.3=checkstyle
@@ -215,13 +210,12 @@ com.google.oauth-client:google-oauth-client-jetty:1.36.0=deploy_jar,nonprodRunti
com.google.oauth-client:google-oauth-client-jetty:1.39.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.oauth-client:google-oauth-client-servlet:1.36.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.oauth-client:google-oauth-client-servlet:1.39.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.oauth-client:google-oauth-client:1.37.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.oauth-client:google-oauth-client:1.39.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.protobuf:protobuf-java-util:4.29.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.oauth-client:google-oauth-client:1.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.protobuf:protobuf-java-util:4.29.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.protobuf:protobuf-java:3.19.6=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.protobuf:protobuf-java:3.21.7=soy
com.google.protobuf:protobuf-java:3.25.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.re2j:re2j:1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.protobuf:protobuf-java:3.25.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.re2j:re2j:1.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.template:soy:2024-02-26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
com.google.truth:truth:1.4.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.googlecode.json-simple:json-simple:1.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -255,11 +249,12 @@ com.sun.istack:istack-commons-tools:4.1.2=jaxb
com.sun.xml.bind.external:relaxng-datatype:4.0.5=jaxb
com.sun.xml.bind.external:rngom:4.0.5=jaxb
com.sun.xml.dtd-parser:dtd-parser:1.5.1=jaxb
com.zaxxer:HikariCP:6.2.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.zaxxer:HikariCP:7.0.1=deploy_jar
com.zaxxer:HikariCP:7.0.2=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
commons-beanutils:commons-beanutils:1.9.4=checkstyle
commons-codec:commons-codec:1.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
commons-codec:commons-codec:1.19.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
commons-collections:commons-collections:3.2.2=checkstyle
commons-io:commons-io:2.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
commons-io:commons-io:2.20.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
commons-logging:commons-logging:1.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
dnsjava:dnsjava:3.6.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
guru.nidi.com.eclipsesource.j2v8:j2v8_linux_x86_64:4.6.0=testRuntimeClasspath
@@ -274,37 +269,27 @@ io.apicurio:apicurio-registry-protobuf-schema-utilities:3.0.0.M2=compileClasspat
io.github.classgraph:classgraph:4.8.162=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.github.eisop:dataflow-errorprone:3.34.0-eisop1=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
io.github.java-diff-utils:java-diff-utils:4.15=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-alts:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-alts:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-api:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-auth:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-auth:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-census:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-context:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-core:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-core:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-googleapis:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-grpclb:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-grpclb:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-inprocess:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-inprocess:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-netty-shaded:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-netty-shaded:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-netty:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-opentelemetry:1.67.1=compileClasspath,nonprodCompileClasspath
io.grpc:grpc-opentelemetry:1.68.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-opentelemetry:1.70.0=testCompileClasspath
io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-alts:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-api:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-auth:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-census:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-context:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-core:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-googleapis:1.71.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-grpclb:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-inprocess:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-netty-shaded:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-netty:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-opentelemetry:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf-lite:1.67.1=compileClasspath,nonprodCompileClasspath
io.grpc:grpc-protobuf-lite:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-rls:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-services:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-stub:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-stub:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-util:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-xds:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf-lite:1.71.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-rls:1.71.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-services:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-stub:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-util:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-xds:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-buffer:4.1.110.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-http2:4.1.110.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.netty:netty-codec-http:4.1.110.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -334,38 +319,29 @@ io.opentelemetry.contrib:opentelemetry-gcp-resources:1.37.0-alpha=compileClasspa
io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry.semconv:opentelemetry-semconv:1.27.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry.semconv:opentelemetry-semconv:1.28.0-alpha=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-api-incubator:1.45.0-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-api-incubator:1.46.0-alpha=testRuntimeClasspath
io.opentelemetry:opentelemetry-api:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-api:1.46.0=testRuntimeClasspath
io.opentelemetry:opentelemetry-api:1.47.0=testCompileClasspath
io.opentelemetry.semconv:opentelemetry-semconv:1.29.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-api-incubator:1.42.1-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-api:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-api:1.53.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-bom:1.42.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-context:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-context:1.46.0=testRuntimeClasspath
io.opentelemetry:opentelemetry-context:1.47.0=testCompileClasspath
io.opentelemetry:opentelemetry-exporter-logging:1.46.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-common:1.53.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-context:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-context:1.53.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-exporter-logging:1.53.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-extension-incubator:1.35.0-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-common:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-common:1.46.0=testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-common:1.47.0=testCompileClasspath
io.opentelemetry:opentelemetry-sdk-common:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-common:1.53.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.42.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.46.0=testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.47.0=testCompileClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.46.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.46.0=testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.47.0=testCompileClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.46.0=testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.47.0=testCompileClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.46.0=testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.47.0=testCompileClasspath
io.opentelemetry:opentelemetry-sdk:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk:1.46.0=testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk:1.47.0=testCompileClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.53.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.53.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-logs:1.53.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-metrics:1.53.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk-trace:1.53.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-sdk:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
io.opentelemetry:opentelemetry-sdk:1.53.0=testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-semconv:1.26.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.outfoxx:swiftpoet:1.3.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.perfmark:perfmark-api:0.27.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -387,10 +363,10 @@ javax.validation:validation-api:1.0.0.GA=compileClasspath,deploy_jar,nonprodComp
joda-time:joda-time:2.12.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
junit:junit:4.13.2=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
net.arnx:nashorn-promise:0.1.1=testRuntimeClasspath
net.bytebuddy:byte-buddy-agent:1.15.11=testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy-agent:1.17.6=testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.14.12=compileClasspath,nonprodCompileClasspath
net.bytebuddy:byte-buddy:1.14.15=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
net.bytebuddy:byte-buddy:1.15.11=testCompileClasspath,testRuntimeClasspath
net.bytebuddy:byte-buddy:1.17.6=testCompileClasspath,testRuntimeClasspath
net.java.dev.jna:jna:5.13.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
net.ltgt.gradle.incap:incap:0.2=annotationProcessor,testAnnotationProcessor
net.sf.saxon:Saxon-HE:10.6=checkstyle
@@ -404,33 +380,33 @@ org.apache.arrow:arrow-format:15.0.2=compileClasspath,deploy_jar,nonprodCompileC
org.apache.arrow:arrow-memory-core:15.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.arrow:arrow-vector:15.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.avro:avro:1.11.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-fn-execution:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-job-management:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-pipeline:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-fn-execution:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-job-management:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-model-pipeline:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-core-construction-java:2.54.0=testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-core-java:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-direct-java:2.63.0=testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-google-cloud-dataflow-java:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-java-fn-execution:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-core:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-expansion-service:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-arrow:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-avro:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-protobuf:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-core-java:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-direct-java:2.67.0=testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-google-cloud-dataflow-java:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-runners-java-fn-execution:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-core:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-expansion-service:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-arrow:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-avro:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-extensions-protobuf:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-fn-execution:2.54.0=testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-harness:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-transform-service-launcher:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-harness:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-sdks-java-transform-service-launcher:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-vendor-grpc-1_60_1:0.1=testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-vendor-grpc-1_69_0:0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.beam:beam-vendor-guava-32_1_2-jre:0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.commons:commons-compress:1.26.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.commons:commons-csv:1.13.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.commons:commons-exec:1.4.0=testCompileClasspath,testRuntimeClasspath
org.apache.commons:commons-csv:1.14.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apache.commons:commons-exec:1.5.0=testRuntimeClasspath
org.apache.commons:commons-lang3:3.14.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
org.apache.commons:commons-lang3:3.17.0=testCompileClasspath,testRuntimeClasspath
org.apache.commons:commons-text:1.13.0=testCompileClasspath,testRuntimeClasspath
org.apache.commons:commons-lang3:3.18.0=testCompileClasspath,testRuntimeClasspath
org.apache.commons:commons-text:1.14.0=testCompileClasspath,testRuntimeClasspath
org.apache.ftpserver:ftplet-api:1.2.1=testCompileClasspath,testRuntimeClasspath
org.apache.ftpserver:ftpserver-core:1.2.1=testCompileClasspath,testRuntimeClasspath
org.apache.httpcomponents:httpclient:4.5.14=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -440,37 +416,39 @@ org.apache.sshd:sshd-common:2.15.0=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-core:2.15.0=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-scp:2.15.0=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-sftp:2.15.0=testCompileClasspath,testRuntimeClasspath
org.apache.tomcat:tomcat-annotations-api:11.0.5=testCompileClasspath,testRuntimeClasspath
org.apache.tomcat:tomcat-annotations-api:11.0.10=testCompileClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.bouncycastle:bcpg-jdk18on:1.80=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcpkix-jdk18on:1.80=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcprov-jdk18on:1.80=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcutil-jdk18on:1.80=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.checkerframework:checker-compat-qual:2.5.3=compileClasspath,nonprodCompileClasspath,soy,testCompileClasspath
org.checkerframework:checker-compat-qual:2.5.5=annotationProcessor,testAnnotationProcessor
org.bouncycastle:bcpg-jdk18on:1.81=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcpkix-jdk18on:1.81=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcprov-jdk18on:1.81=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcutil-jdk18on:1.81=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.checkerframework:checker-compat-qual:2.5.3=annotationProcessor,compileClasspath,nonprodCompileClasspath,soy,testAnnotationProcessor,testCompileClasspath
org.checkerframework:checker-compat-qual:2.5.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.checkerframework:checker-qual:3.12.0=checkstyle
org.checkerframework:checker-qual:3.33.0=errorprone,nonprodAnnotationProcessor,soy
org.checkerframework:checker-qual:3.41.0=annotationProcessor,testAnnotationProcessor
org.checkerframework:checker-qual:3.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.checkerframework:checker-qual:3.49.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
org.checkerframework:checker-qual:3.49.3=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.codehaus.mojo:animal-sniffer-annotations:1.24=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.conscrypt:conscrypt-openjdk-uber:2.5.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.eclipse.angus:angus-activation:2.0.2=deploy_jar,jaxb,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.eclipse.angus:jakarta.mail:2.0.3=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.eclipse.angus:jakarta.mail:2.0.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.eclipse.collections:eclipse-collections-api:11.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.eclipse.collections:eclipse-collections:11.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-ee:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-http:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-io:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-security:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-server:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-session:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-util:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-xml:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-core:11.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-database-postgresql:11.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.0=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.0=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty.ee:jetty-ee-webapp:12.1.0=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-http:12.1.0=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-io:12.1.0=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-security:12.1.0=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-server:12.1.0=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-session:12.1.0=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-util:12.1.0=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-xml:12.1.0=testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-core:11.11.1=deploy_jar
org.flywaydb:flyway-core:11.11.2=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-database-postgresql:11.11.1=deploy_jar
org.flywaydb:flyway-database-postgresql:11.11.2=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.glassfish.jaxb:codemodel:4.0.5=jaxb
org.glassfish.jaxb:jaxb-core:4.0.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.glassfish.jaxb:jaxb-core:4.0.5=jaxb
@@ -498,6 +476,7 @@ org.javassist:javassist:3.28.0-GA=checkstyle
org.jboss.logging:jboss-logging:3.5.0.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jcommander:jcommander:2.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-bom:1.4.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-metadata-jvm:2.1.21=annotationProcessor,testAnnotationProcessor
org.jetbrains.kotlin:kotlin-reflect:1.6.10=annotationProcessor,testAnnotationProcessor
org.jetbrains.kotlin:kotlin-reflect:1.9.20=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib-common:1.9.20=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -506,7 +485,7 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10=compileClasspath,deploy_jar,nonpr
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0=annotationProcessor,testAnnotationProcessor
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib:1.9.20=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jetbrains.kotlin:kotlin-stdlib:2.0.21=annotationProcessor,testAnnotationProcessor
org.jetbrains.kotlin:kotlin-stdlib:2.1.21=annotationProcessor,testAnnotationProcessor
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -515,26 +494,26 @@ org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.0.1=deploy_jar,nonprodRun
org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.jetbrains:annotations:13.0=annotationProcessor,testAnnotationProcessor
org.jetbrains:annotations:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jline:jline:3.29.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.joda:joda-money:2.0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jline:jline:3.30.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.joda:joda-money:2.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.json:json:20230618=soy
org.json:json:20250107=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jsoup:jsoup:1.19.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jsoup:jsoup:1.21.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jspecify:jspecify:1.0.0=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
org.junit-pioneer:junit-pioneer:2.3.0=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-migrationsupport:5.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:5.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:1.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-runner:1.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-api:1.12.1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-commons:1.12.1=testRuntimeClasspath
org.junit:junit-bom:5.12.1=testCompileClasspath,testRuntimeClasspath
org.mockito:mockito-core:5.16.0=testCompileClasspath,testRuntimeClasspath
org.mockito:mockito-junit-jupiter:5.16.0=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-migrationsupport:5.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:5.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:1.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-runner:1.13.3=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-api:1.13.4=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-commons:1.13.4=testRuntimeClasspath
org.junit:junit-bom:5.13.4=testCompileClasspath,testRuntimeClasspath
org.mockito:mockito-core:5.19.0=testCompileClasspath,testRuntimeClasspath
org.mockito:mockito-junit-jupiter:5.19.0=testCompileClasspath,testRuntimeClasspath
org.objenesis:objenesis:3.3=testRuntimeClasspath
org.ogce:xpp3:1.1.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
@@ -552,50 +531,58 @@ org.ow2.asm:asm:9.5=soy
org.ow2.asm:asm:9.7=jacocoAnt
org.ow2.asm:asm:9.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.pcollections:pcollections:3.1.4=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
org.postgresql:postgresql:42.7.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.postgresql:postgresql:42.7.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.reflections:reflections:0.10.2=checkstyle
org.rnorth.duct-tape:duct-tape:1.0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-api:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chrome-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chromium-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v131:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v132:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v133:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v85:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-edge-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-firefox-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-http:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-ie-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-java:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-json:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-manager:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-os:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-remote-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-safari-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-support:4.29.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-api:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chrome-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-chromium-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v137:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v138:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-devtools-v139:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-edge-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-firefox-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-http:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-ie-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-java:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-json:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-manager:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-os:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-remote-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-safari-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
org.seleniumhq.selenium:selenium-support:4.35.0=testCompileClasspath,testRuntimeClasspath
org.slf4j:jcl-over-slf4j:1.7.36=testCompileClasspath,testRuntimeClasspath
org.slf4j:jul-to-slf4j:1.7.30=testRuntimeClasspath
org.slf4j:slf4j-api:2.0.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-jdk14:2.0.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-api:2.0.17=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-jdk14:2.0.17=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.snakeyaml:snakeyaml-engine:2.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
org.testcontainers:database-commons:1.20.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:jdbc:1.20.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:junit-jupiter:1.20.6=testCompileClasspath,testRuntimeClasspath
org.testcontainers:postgresql:1.20.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:selenium:1.20.6=testCompileClasspath,testRuntimeClasspath
org.testcontainers:testcontainers:1.20.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:database-commons:1.21.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:jdbc:1.21.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:junit-jupiter:1.21.3=testCompileClasspath,testRuntimeClasspath
org.testcontainers:postgresql:1.21.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:selenium:1.21.3=testCompileClasspath,testRuntimeClasspath
org.testcontainers:testcontainers:1.21.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.threeten:threetenbp:1.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.w3c.css:sac:1.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.webjars.npm:viz.js-graphviz-java:2.1.3=testRuntimeClasspath
org.xerial.snappy:snappy-java:1.1.10.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.yaml:snakeyaml:2.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-api:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-diagram:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-loader:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-postgresql:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-text:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-tools:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-utility:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.yaml:snakeyaml:2.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-api:16.26.3=deploy_jar
us.fatehi:schemacrawler-api:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-diagram:16.26.3=deploy_jar
us.fatehi:schemacrawler-diagram:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-loader:16.26.3=deploy_jar
us.fatehi:schemacrawler-loader:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-operations:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-postgresql:16.26.3=deploy_jar
us.fatehi:schemacrawler-postgresql:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-text:16.26.3=deploy_jar
us.fatehi:schemacrawler-text:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-tools:16.26.3=deploy_jar
us.fatehi:schemacrawler-tools:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler-utility:16.26.3=deploy_jar
us.fatehi:schemacrawler-utility:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
us.fatehi:schemacrawler:16.26.3=deploy_jar
us.fatehi:schemacrawler:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
xerces:xmlParserAPIs:2.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
empty=devtool,nomulus_test

View File

@@ -275,7 +275,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
*/
@VisibleForTesting
ImmutableSet<InternetAddress> getEmailAddresses(Registrar registrar, Type contactType) {
ImmutableSortedSet<RegistrarPoc> contacts = registrar.getContactsOfType(contactType);
ImmutableSortedSet<RegistrarPoc> contacts = registrar.getPocsOfType(contactType);
ImmutableSet.Builder<InternetAddress> recipientEmails = new ImmutableSet.Builder<>();
for (RegistrarPoc contact : contacts) {
try {

View File

@@ -215,10 +215,12 @@ public class BsaValidateAction implements Runnable {
if (Objects.equals(expectedReason, domain.reason())) {
return Optional.empty();
}
if (isRegistered || domain.reason().equals(Reason.REGISTERED)) {
if (isStalenessAllowed(isRegistered, activeDomains.get(domain.domainName()))) {
// Registered name still reported with other reasons: Don't report if registration is recent.
// Note that staleness is not tolerated if deregistered name is still reported as registered:
// in this case we do not have the VKey on hand, and it is not worth the effort to find it
// out.
if (isRegistered && isStalenessAllowed(activeDomains.get(domain.domainName()))) {
return Optional.empty();
}
}
return Optional.of(
String.format(
@@ -228,15 +230,10 @@ public class BsaValidateAction implements Runnable {
domain.reason()));
}
boolean isStalenessAllowed(boolean isNewDomain, VKey<Domain> domainVKey) {
boolean isStalenessAllowed(VKey<Domain> domainVKey) {
Domain domain = bsaQuery(() -> replicaTm().loadByKey(domainVKey));
var now = clock.nowUtc();
if (isNewDomain) {
return domain.getCreationTime().plus(maxStaleness).isAfter(now);
} else {
return domain.getDeletionTime().isBefore(now)
&& domain.getDeletionTime().plus(maxStaleness).isAfter(now);
}
return domain.getCreationTime().plus(maxStaleness).isAfter(now);
}
/** Returns unique labels across all block lists in the download specified by {@code jobName}. */

View File

@@ -25,16 +25,16 @@ import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.POST;
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.cloud.storage.BlobId;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import com.google.common.flogger.FluentLogger;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteSource;
import google.registry.bsa.api.BsaCredential;
import google.registry.config.RegistryConfig.Config;
import google.registry.gcs.GcsUtils;
@@ -47,10 +47,13 @@ import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import jakarta.inject.Inject;
import jakarta.persistence.TypedQuery;
import java.io.ByteArrayOutputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.Writer;
import java.util.Optional;
import java.util.zip.GZIPOutputStream;
@@ -60,14 +63,17 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okio.BufferedSink;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joda.time.DateTime;
/**
* Daily action that uploads unavailable domain names on applicable TLDs to BSA.
*
* <p>The upload is a single zipped text file containing combined details for all BSA-enrolled TLDs.
* The text is a newline-delimited list of punycoded fully qualified domain names, and contains all
* domains on each TLD that are registered and/or reserved.
* The text is a newline-delimited list of punycoded fully qualified domain names with a trailing
* newline at the end, and contains all domains on each TLD that are registered and/or reserved.
*
* <p>The file is also uploaded to GCS to preserve it as a record for ourselves.
*/
@@ -118,7 +124,7 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
// TODO(mcilwain): Implement a date Cursor, have the cronjob run frequently, and short-circuit
// the run if the daily upload is already completed.
DateTime runTime = clock.nowUtc();
String unavailableDomains = Joiner.on("\n").join(getUnavailableDomains(runTime));
ImmutableSortedSet<String> unavailableDomains = getUnavailableDomains(runTime);
if (unavailableDomains.isEmpty()) {
logger.atWarning().log("No unavailable domains found; terminating.");
emailSender.sendNotification(
@@ -136,12 +142,16 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
}
/** Uploads the unavailable domains list to GCS in the unavailable domains bucket. */
boolean uploadToGcs(String unavailableDomains, DateTime runTime) {
boolean uploadToGcs(ImmutableSortedSet<String> unavailableDomains, DateTime runTime) {
logger.atInfo().log("Uploading unavailable names file to GCS in bucket %s", gcsBucket);
BlobId blobId = BlobId.of(gcsBucket, createFilename(runTime));
// `gcsUtils.openOutputStream` returns a buffered stream
try (OutputStream gcsOutput = gcsUtils.openOutputStream(blobId);
Writer osWriter = new OutputStreamWriter(gcsOutput, US_ASCII)) {
osWriter.write(unavailableDomains);
for (var domainName : unavailableDomains) {
osWriter.write(domainName);
osWriter.write("\n");
}
return true;
} catch (Exception e) {
logger.atSevere().withCause(e).log(
@@ -150,10 +160,14 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
}
}
boolean uploadToBsa(String unavailableDomains, DateTime runTime) {
boolean uploadToBsa(ImmutableSortedSet<String> unavailableDomains, DateTime runTime) {
try {
byte[] gzippedContents = gzipUnavailableDomains(unavailableDomains);
String sha512Hash = ByteSource.wrap(gzippedContents).hash(Hashing.sha512()).toString();
Hasher sha512Hasher = Hashing.sha512().newHasher();
unavailableDomains.stream()
.map(name -> name + "\n")
.forEachOrdered(line -> sha512Hasher.putString(line, UTF_8));
String sha512Hash = sha512Hasher.hash().toString();
String filename = createFilename(runTime);
OkHttpClient client = new OkHttpClient().newBuilder().build();
@@ -169,7 +183,9 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
.addFormDataPart(
"file",
String.format("%s.gz", filename),
RequestBody.create(gzippedContents, MediaType.parse("application/octet-stream")))
new StreamingRequestBody(
gzippedStream(unavailableDomains),
MediaType.parse("application/octet-stream")))
.build();
Request request =
@@ -196,15 +212,6 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
}
}
private byte[] gzipUnavailableDomains(String unavailableDomains) throws IOException {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
gzipOutputStream.write(unavailableDomains.getBytes(US_ASCII));
}
return byteArrayOutputStream.toByteArray();
}
}
private static String createFilename(DateTime runTime) {
return String.format("unavailable_domains_%s.txt", runTime.toString());
}
@@ -280,4 +287,65 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
private static String toDomain(String domainLabel, Tld tld) {
return String.format("%s.%s", domainLabel, tld.getTldStr());
}
private InputStream gzippedStream(ImmutableSortedSet<String> unavailableDomains)
throws IOException {
PipedInputStream inputStream = new PipedInputStream();
PipedOutputStream outputStream = new PipedOutputStream(inputStream);
new Thread(
() -> {
try {
gzipUnavailableDomains(outputStream, unavailableDomains);
} catch (Throwable e) {
logger.atSevere().withCause(e).log("Failed to gzip unavailable domains.");
try {
// This will cause the next read to throw an IOException.
inputStream.close();
} catch (IOException ignore) {
// Won't happen for `PipedInputStream.close()`
}
}
})
.start();
return inputStream;
}
private void gzipUnavailableDomains(
PipedOutputStream outputStream, ImmutableSortedSet<String> unavailableDomains)
throws IOException {
// `GZIPOutputStream` is buffered.
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream)) {
for (String name : unavailableDomains) {
var line = name + "\n";
gzipOutputStream.write(line.getBytes(US_ASCII));
}
}
}
private static class StreamingRequestBody extends RequestBody {
private final BufferedInputStream inputStream;
private final MediaType mediaType;
StreamingRequestBody(InputStream inputStream, MediaType mediaType) {
this.inputStream = new BufferedInputStream(inputStream);
this.mediaType = mediaType;
}
@Nullable
@Override
public MediaType contentType() {
return mediaType;
}
@Override
public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException {
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
bufferedSink.write(buffer, 0, bytesRead);
}
}
}
}

View File

@@ -243,7 +243,7 @@ hibernate:
# that BEAM pipelines are not subject to the maximumPoolSize value defined
# here. See PersistenceModule.java for more information.
hikariMinimumIdle: 1
hikariMaximumPoolSize: 20
hikariMaximumPoolSize: 40
hikariIdleTimeout: 300000
# The batch size is basically the number of insertions / updates in a single
# transaction that will be batched together into one INSERT/UPDATE statement.
@@ -255,7 +255,7 @@ hibernate:
# The fetch size is the number of entities retrieved at a time from the
# database cursor. Here we set a small default geared toward Nomulus server
# transactions. Large queries can override the defaults on a per-query basis.
jdbcFetchSize: 20
jdbcFetchSize: 40
cloudSql:
# jdbc url for the Cloud SQL database.

View File

@@ -88,6 +88,7 @@
should exist between the RECURRING_BILLING cursor's time and the execution
time of the action.
</description>
<!-- Runs shortly after DeleteExpiredDomainsAction so it can delete domains before they renew -->
<schedule>0 3 * * *</schedule>
</task>
@@ -98,7 +99,8 @@
This job runs an action that deletes domains that are past their
autorenew end date.
</description>
<schedule>7 3 * * *</schedule>
<!-- Runs shortly before ExpandBillingRecurrencesPipeline to catch and delete domains before they renew -->
<schedule>45 2 * * *</schedule>
</task>
<task>

View File

@@ -146,6 +146,7 @@
This job runs an action that deletes domains that are past their
autorenew end date.
</description>
<schedule>7 3 * * *</schedule>
<!-- Runs shortly before ExpandBillingRecurrencesPipeline to catch and delete domains before they renew -->
<schedule>45 2 * * *</schedule>
</task>
</entries>

View File

@@ -130,6 +130,7 @@
should exist between the RECURRING_BILLING cursor's time and the execution
time of the action.
</description>
<!-- Runs shortly after DeleteExpiredDomainsAction so it can delete domains before they renew -->
<schedule>0 3 * * *</schedule>
</task>
@@ -140,7 +141,8 @@
This job runs an action that deletes domains that are past their
autorenew end date.
</description>
<schedule>7 3 * * *</schedule>
<!-- Runs shortly before ExpandBillingRecurrencesPipeline to catch and delete domains before they renew -->
<schedule>45 2 * * *</schedule>
</task>
<task>

View File

@@ -57,6 +57,7 @@
This job runs an action that deletes domains that are past their
autorenew end date.
</description>
<schedule>7 3 * * *</schedule>
<!-- Runs shortly before ExpandBillingRecurrencesPipeline to catch and delete domains before they renew -->
<schedule>45 2 * * *</schedule>
</task>
</entries>

View File

@@ -90,6 +90,7 @@
should exist between the RECURRING_BILLING cursor's time and the execution
time of the action.
</description>
<!-- Runs shortly after DeleteExpiredDomainsAction so it can delete domains before they renew -->
<schedule>0 3 * * *</schedule>
</task>
@@ -113,7 +114,8 @@
This job runs an action that deletes domains that are past their
autorenew end date.
</description>
<schedule>7 3 * * *</schedule>
<!-- Runs shortly before ExpandBillingRecurrencesPipeline to catch and delete domains before they renew -->
<schedule>45 2 * * *</schedule>
</task>
<task>

View File

@@ -26,6 +26,7 @@ import com.google.common.net.MediaType;
import google.registry.model.eppoutput.EppOutput;
import google.registry.request.Response;
import google.registry.util.ProxyHttpHeaders;
import google.registry.util.StopwatchLogger;
import jakarta.inject.Inject;
/** Handle an EPP request and response. */
@@ -55,7 +56,10 @@ public class EppRequestHandler {
eppController.handleEppCommand(
sessionMetadata, credentials, eppRequestSource, isDryRun, isSuperuser, inputXmlBytes);
response.setContentType(APPLICATION_EPP_XML);
final StopwatchLogger stopwatch = new StopwatchLogger();
byte[] eppResponseXmlBytes = marshalWithLenientRetry(eppOutput);
stopwatch.tick("Completed EPP output marshaling.");
response.setPayload(new String(eppResponseXmlBytes, UTF_8));
logger.atInfo().log(
"EPP response: %s", prettyPrint(EppXmlSanitizer.sanitizeEppXml(eppResponseXmlBytes)));

View File

@@ -29,6 +29,7 @@ import google.registry.model.eppoutput.EppOutput;
import google.registry.monitoring.whitebox.EppMetric;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
import google.registry.persistence.transaction.JpaTransactionManager;
import google.registry.util.StopwatchLogger;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import java.util.Optional;
@@ -77,23 +78,32 @@ public class FlowRunner {
flowReporter.recordToLogs();
}
eppMetricBuilder.setCommandNameFromFlow(flowClass.getSimpleName());
final StopwatchLogger stopwatch = new StopwatchLogger();
// We may already be in a transaction, e.g., when invoked by DeleteExpiredDomainsAction.
if (!isTransactional || jpaTransactionManager.inTransaction()) {
stopwatch.tick("We're in transaction, running the flow now.");
return EppOutput.create(flowProvider.get().run());
}
stopwatch.tick("We're not in transaction, calling transact.");
try {
return jpaTransactionManager.transact(
isolationLevelOverride.orElse(null),
() -> {
try {
stopwatch.tick("Running the flow in transaction.");
EppOutput output = EppOutput.create(flowProvider.get().run());
stopwatch.tick("Completed the flow in transaction.");
if (isDryRun) {
throw new DryRunException(output);
}
if (flowClass.equals(LoginFlow.class)) {
// In LoginFlow, registrarId isn't known until after the flow executes, so save
// it then.
stopwatch.tick("Login flow started setting registrar id.");
eppMetricBuilder.setRegistrarId(sessionMetadata.getRegistrarId());
stopwatch.tick("Login flow finished setting registrar id.");
}
return output;
} catch (EppException e) {

View File

@@ -21,6 +21,7 @@ import static google.registry.xml.ValidationMode.STRICT;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import google.registry.flows.EppException.CommandUseErrorException;
import google.registry.flows.EppException.ParameterValueRangeErrorException;
@@ -30,6 +31,7 @@ import google.registry.flows.custom.EntityChanges;
import google.registry.model.EppResource;
import google.registry.model.adapters.CurrencyUnitAdapter.UnknownCurrencyException;
import google.registry.model.eppcommon.EppXmlTransformer;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppinput.EppInput.WrongProtocolVersionException;
import google.registry.model.eppoutput.EppOutput;
import google.registry.model.host.InetAddressAdapter.IpVersionMismatchException;
@@ -40,6 +42,9 @@ import java.util.List;
/** Static utility functions for flows. */
public final class FlowUtils {
public static final ImmutableSet<StatusValue> DELETE_PROHIBITED_STATUSES =
ImmutableSet.of(StatusValue.CLIENT_DELETE_PROHIBITED, StatusValue.SERVER_DELETE_PROHIBITED);
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private FlowUtils() {}

View File

@@ -14,6 +14,7 @@
package google.registry.flows.contact;
import static google.registry.flows.FlowUtils.DELETE_PROHIBITED_STATUSES;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.checkLinkedDomains;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
@@ -65,12 +66,6 @@ import org.joda.time.DateTime;
@ReportingSpec(ActivityReportField.CONTACT_DELETE)
public final class ContactDeleteFlow implements MutatingFlow {
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES =
ImmutableSet.of(
StatusValue.CLIENT_DELETE_PROHIBITED,
StatusValue.PENDING_DELETE,
StatusValue.SERVER_DELETE_PROHIBITED);
@Inject ExtensionManager extensionManager;
@Inject @RegistrarId String registrarId;
@Inject @TargetId String targetId;
@@ -91,9 +86,10 @@ public final class ContactDeleteFlow implements MutatingFlow {
DateTime now = tm().getTransactionTime();
checkLinkedDomains(targetId, now, Contact.class);
Contact existingContact = loadAndVerifyExistence(Contact.class, targetId, now);
verifyNoDisallowedStatuses(existingContact, DISALLOWED_STATUSES);
verifyOptionalAuthInfo(authInfo, existingContact);
verifyNoDisallowedStatuses(existingContact, ImmutableSet.of(StatusValue.PENDING_DELETE));
if (!isSuperuser) {
verifyNoDisallowedStatuses(existingContact, DELETE_PROHIBITED_STATUSES);
verifyResourceOwnership(registrarId, existingContact);
}
// Handle pending transfers on contact deletion.

View File

@@ -17,6 +17,7 @@ package google.registry.flows.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
import static google.registry.flows.FlowUtils.DELETE_PROHIBITED_STATUSES;
import static google.registry.flows.FlowUtils.createHistoryEntryId;
import static google.registry.flows.FlowUtils.persistEntityChanges;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
@@ -122,11 +123,6 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of(
StatusValue.CLIENT_DELETE_PROHIBITED,
StatusValue.PENDING_DELETE,
StatusValue.SERVER_DELETE_PROHIBITED);
@Inject ExtensionManager extensionManager;
@Inject EppInput eppInput;
@Inject SessionMetadata sessionMetadata;
@@ -304,9 +300,10 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
private void verifyDeleteAllowed(Domain existingDomain, Tld tld, DateTime now)
throws EppException {
verifyNoDisallowedStatuses(existingDomain, DISALLOWED_STATUSES);
verifyOptionalAuthInfo(authInfo, existingDomain);
verifyNoDisallowedStatuses(existingDomain, ImmutableSet.of(StatusValue.PENDING_DELETE));
if (!isSuperuser) {
verifyNoDisallowedStatuses(existingDomain, DELETE_PROHIBITED_STATUSES);
verifyResourceOwnership(registrarId, existingDomain);
verifyNotInPredelegation(tld, now);
checkAllowedAccessToTld(registrarId, tld.getTld().toString());

View File

@@ -481,10 +481,10 @@ public class DomainFlowUtils {
}
/**
* Enforces the presence/absence of contact data depending on the minimum data set migration
* schedule.
* Enforces the presence/absence of contact data on domain creates depending on the minimum data
* set migration schedule.
*/
static void validateContactDataPresence(
static void validateCreateContactData(
Optional<VKey<Contact>> registrant, Set<DesignatedContact> contacts)
throws RequiredParameterMissingException, ParameterValuePolicyErrorException {
// TODO(b/353347632): Change these flag checks to a registry config check once minimum data set
@@ -514,6 +514,45 @@ public class DomainFlowUtils {
}
}
/**
* Enforces the presence/absence of contact data on domain updates depending on the minimum data
* set migration schedule.
*/
static void validateUpdateContactData(
Optional<VKey<Contact>> existingRegistrant,
Optional<VKey<Contact>> newRegistrant,
Set<DesignatedContact> existingContacts,
Set<DesignatedContact> newContacts)
throws RequiredParameterMissingException, ParameterValuePolicyErrorException {
// TODO(b/353347632): Change these flag checks to a registry config check once minimum data set
// migration is completed.
if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) {
// Throw if the update specifies a new registrant that is different from the existing one.
if (newRegistrant.isPresent() && !newRegistrant.equals(existingRegistrant)) {
throw new RegistrantProhibitedException();
}
// Throw if the update specifies any new contacts that weren't already present on the domain.
if (!Sets.difference(newContacts, existingContacts).isEmpty()) {
throw new ContactsProhibitedException();
}
} else if (!FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) {
// Throw if the update empties out a registrant that had been present.
if (newRegistrant.isEmpty() && existingRegistrant.isPresent()) {
throw new MissingRegistrantException();
}
// Throw if the update contains no admin contact when one had been present.
if (existingContacts.stream().anyMatch(c -> c.getType().equals(Type.ADMIN))
&& newContacts.stream().noneMatch(c -> c.getType().equals(Type.ADMIN))) {
throw new MissingAdminContactException();
}
// Throw if the update contains no tech contact when one had been present.
if (existingContacts.stream().anyMatch(c -> c.getType().equals(Type.TECH))
&& newContacts.stream().noneMatch(c -> c.getType().equals(Type.TECH))) {
throw new MissingTechnicalContactException();
}
}
}
static void validateRegistrantAllowedOnTld(String tld, Optional<String> registrantContactId)
throws RegistrantNotAllowedException {
ImmutableSet<String> allowedRegistrants = Tld.get(tld).getAllowedRegistrantContactIds();
@@ -1054,7 +1093,7 @@ public class DomainFlowUtils {
String tldStr = tld.getTldStr();
validateRegistrantAllowedOnTld(tldStr, command.getRegistrantContactId());
validateNoDuplicateContacts(command.getContacts());
validateContactDataPresence(command.getRegistrant(), command.getContacts());
validateCreateContactData(command.getRegistrant(), command.getContacts());
ImmutableSet<String> hostNames = command.getNameserverHostNames();
validateNameserversCountForTld(tldStr, domainName, hostNames.size());
validateNameserversAllowedOnTld(tldStr, hostNames);
@@ -1358,7 +1397,7 @@ public class DomainFlowUtils {
}
/** Domain name is under tld which doesn't exist. */
static class TldDoesNotExistException extends ParameterValueRangeErrorException {
public static class TldDoesNotExistException extends ParameterValueRangeErrorException {
public TldDoesNotExistException(String tld) {
super(String.format("Domain name is under tld %s which doesn't exist", tld));
}

View File

@@ -30,7 +30,6 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
import static google.registry.flows.domain.DomainFlowUtils.updateDsData;
import static google.registry.flows.domain.DomainFlowUtils.validateContactDataPresence;
import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
import static google.registry.flows.domain.DomainFlowUtils.validateFeesAckedIfPresent;
@@ -38,6 +37,7 @@ import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAl
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld;
import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld;
import static google.registry.flows.domain.DomainFlowUtils.validateUpdateContactData;
import static google.registry.flows.domain.DomainFlowUtils.verifyClientUpdateNotProhibited;
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
@@ -186,7 +186,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
Domain newDomain = performUpdate(command, existingDomain, now);
DomainHistory domainHistory =
historyBuilder.setType(DOMAIN_UPDATE).setDomain(newDomain).build();
validateNewState(newDomain);
validateNewState(existingDomain, newDomain);
if (requiresDnsUpdate(existingDomain, newDomain)) {
requestDomainDnsRefresh(targetId);
}
@@ -328,8 +328,13 @@ public final class DomainUpdateFlow implements MutatingFlow {
* compliant with the additions or amendments, otherwise existing data can become invalid and
* cause Domain update failure.
*/
private static void validateNewState(Domain newDomain) throws EppException {
validateContactDataPresence(newDomain.getRegistrant(), newDomain.getContacts());
private static void validateNewState(Domain existingDomain, Domain newDomain)
throws EppException {
validateUpdateContactData(
existingDomain.getRegistrant(),
newDomain.getRegistrant(),
existingDomain.getContacts(),
newDomain.getContacts());
validateDsData(newDomain.getDsData());
validateNameserversCountForTld(
newDomain.getTld(),

View File

@@ -15,6 +15,7 @@
package google.registry.flows.host;
import static google.registry.dns.DnsUtils.requestHostDnsRefresh;
import static google.registry.flows.FlowUtils.DELETE_PROHIBITED_STATUSES;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.checkLinkedDomains;
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
@@ -65,12 +66,6 @@ import org.joda.time.DateTime;
@ReportingSpec(ActivityReportField.HOST_DELETE)
public final class HostDeleteFlow implements MutatingFlow {
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES =
ImmutableSet.of(
StatusValue.CLIENT_DELETE_PROHIBITED,
StatusValue.PENDING_DELETE,
StatusValue.SERVER_DELETE_PROHIBITED);
@Inject ExtensionManager extensionManager;
@Inject @RegistrarId String registrarId;
@Inject @TargetId String targetId;
@@ -91,8 +86,9 @@ public final class HostDeleteFlow implements MutatingFlow {
validateHostName(targetId);
checkLinkedDomains(targetId, now, Host.class);
Host existingHost = loadAndVerifyExistence(Host.class, targetId, now);
verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES);
verifyNoDisallowedStatuses(existingHost, ImmutableSet.of(StatusValue.PENDING_DELETE));
if (!isSuperuser) {
verifyNoDisallowedStatuses(existingHost, DELETE_PROHIBITED_STATUSES);
// Hosts transfer with their superordinate domains, so for hosts with a superordinate domain,
// the client id, needs to be read off of it.
EppResource owningResource =

View File

@@ -47,6 +47,7 @@ import google.registry.model.eppinput.EppInput.Options;
import google.registry.model.eppinput.EppInput.Services;
import google.registry.model.eppoutput.EppResponse;
import google.registry.model.registrar.Registrar;
import google.registry.util.StopwatchLogger;
import jakarta.inject.Inject;
import java.util.Optional;
import java.util.Set;
@@ -97,19 +98,25 @@ public class LoginFlow implements MutatingFlow {
/** Run the flow without bothering to log errors. The {@link #run} method will do that for us. */
private EppResponse runWithoutLogging() throws EppException {
final StopwatchLogger stopwatch = new StopwatchLogger();
extensionManager.validate(); // There are no legal extensions for this flow.
stopwatch.tick("LoginFlow extension validate");
Login login = (Login) eppInput.getCommandWrapper().getCommand();
stopwatch.tick("LoginFlow getCommand");
if (!registrarId.isEmpty()) {
throw new AlreadyLoggedInException();
}
Options options = login.getOptions();
stopwatch.tick("LoginFlow getOptions");
if (!ProtocolDefinition.LANGUAGE.equals(options.getLanguage())) {
throw new UnsupportedLanguageException();
}
Services services = login.getServices();
stopwatch.tick("LoginFlow getServices");
Set<String> unsupportedObjectServices = difference(
nullToEmpty(services.getObjectServices()),
ProtocolDefinition.SUPPORTED_OBJECT_SERVICES);
stopwatch.tick("LoginFlow difference unsupportedObjectServices");
if (!unsupportedObjectServices.isEmpty()) {
throw new UnimplementedObjectServiceException();
}
@@ -121,11 +128,12 @@ public class LoginFlow implements MutatingFlow {
}
serviceExtensionUrisBuilder.add(uri);
}
stopwatch.tick("LoginFlow serviceExtensionUrisBuilder");
Optional<Registrar> registrar = Registrar.loadByRegistrarIdCached(login.getClientId());
if (registrar.isEmpty()) {
throw new BadRegistrarIdException(login.getClientId());
}
stopwatch.tick("LoginFlow loadByRegistrarIdCached");
// AuthenticationErrorExceptions will propagate up through here.
try {
credentials.validate(registrar.get(), login.getPassword());
@@ -137,6 +145,7 @@ public class LoginFlow implements MutatingFlow {
throw e;
}
}
stopwatch.tick("LoginFlow credentials.validate");
if (!registrar.get().isLive()) {
throw new RegistrarAccountNotActiveException();
}
@@ -145,17 +154,24 @@ public class LoginFlow implements MutatingFlow {
String newPassword = login.getNewPassword().get();
// Load fresh from database (bypassing the cache) to ensure we don't save stale data.
Optional<Registrar> freshRegistrar = Registrar.loadByRegistrarId(login.getClientId());
stopwatch.tick("LoginFlow reload freshRegistrar");
if (freshRegistrar.isEmpty()) {
throw new BadRegistrarIdException(login.getClientId());
}
tm().put(freshRegistrar.get().asBuilder().setPassword(newPassword).build());
stopwatch.tick("LoginFlow updated password");
}
// We are in!
sessionMetadata.resetFailedLoginAttempts();
stopwatch.tick("LoginFlow resetFailedLoginAttempts");
sessionMetadata.setRegistrarId(login.getClientId());
stopwatch.tick("LoginFlow setRegistrarId");
sessionMetadata.setServiceExtensionUris(serviceExtensionUrisBuilder.build());
return responseBuilder.setIsLoginResponse().build();
stopwatch.tick("LoginFlow setServiceExtensionUris");
EppResponse eppResponse = responseBuilder.setIsLoginResponse().build();
stopwatch.tick("LoginFlow eppResponse build()");
return eppResponse;
}
/** Registrar with this ID could not be found. */

View File

@@ -17,10 +17,7 @@ package google.registry.loadtest;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.Lists.partition;
import static google.registry.security.XsrfTokenManager.X_CSRF_TOKEN;
import static google.registry.util.ResourceUtils.readResourceUtf8;
import static java.util.Arrays.asList;
import static org.joda.time.DateTimeZone.UTC;
import com.google.cloud.tasks.v2.Task;
import com.google.common.collect.ImmutableList;
@@ -34,7 +31,7 @@ import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.security.XsrfTokenManager;
import google.registry.util.Clock;
import google.registry.util.RegistryEnvironment;
import jakarta.inject.Inject;
import java.time.Instant;
@@ -67,11 +64,9 @@ public class LoadTestAction implements Runnable {
private static final int NUM_QUEUES = 10;
private static final int MAX_TASKS_PER_LOAD = 100;
private static final int ARBITRARY_VALID_HOST_LENGTH = 40;
private static final int MAX_CONTACT_LENGTH = 13;
private static final int MAX_DOMAIN_LABEL_LENGTH = 63;
private static final String EXISTING_DOMAIN = "testdomain";
private static final String EXISTING_CONTACT = "contact";
private static final String EXISTING_HOST = "ns1";
private static final Random random = new Random();
@@ -85,8 +80,8 @@ public class LoadTestAction implements Runnable {
/**
* The number of seconds to delay the execution of the first load testing tasks by. Preparatory
* work of creating independent contacts and hosts that will be used for later domain creation
* testing occurs during this period, so make sure that it is long enough.
* work of creating independent hosts that will be used for later domain creation testing occurs
* during this period, so make sure that it is long enough.
*/
@Inject
@Parameter("delaySeconds")
@@ -120,21 +115,6 @@ public class LoadTestAction implements Runnable {
@Parameter("domainChecks")
int domainChecksPerSecond;
/** The number of successful contact creates to enqueue per second over the length of the test. */
@Inject
@Parameter("successfulContactCreates")
int successfulContactCreatesPerSecond;
/** The number of failed contact creates to enqueue per second over the length of the test. */
@Inject
@Parameter("failedContactCreates")
int failedContactCreatesPerSecond;
/** The number of successful contact infos to enqueue per second over the length of the test. */
@Inject
@Parameter("contactInfos")
int contactInfosPerSecond;
/** The number of successful host creates to enqueue per second over the length of the test. */
@Inject
@Parameter("successfulHostCreates")
@@ -152,9 +132,8 @@ public class LoadTestAction implements Runnable {
@Inject CloudTasksUtils cloudTasksUtils;
private final String xmlContactCreateTmpl;
private final String xmlContactCreateFail;
private final String xmlContactInfo;
@Inject Clock clock;
private final String xmlDomainCheck;
private final String xmlDomainCreateTmpl;
private final String xmlDomainCreateFail;
@@ -163,53 +142,35 @@ public class LoadTestAction implements Runnable {
private final String xmlHostCreateFail;
private final String xmlHostInfo;
/**
* The XSRF token to be used for making requests to the epptool endpoint.
*
* <p>Note that the email address is set to empty, because the logged-in user hitting this
* endpoint will not be the same as when the tasks themselves fire and hit the epptool endpoint.
*/
private final String xsrfToken;
@Inject
LoadTestAction(@Parameter("tld") String tld, XsrfTokenManager xsrfTokenManager) {
xmlContactCreateTmpl = loadXml("contact_create");
xmlContactCreateFail = xmlContactCreateTmpl.replace("%contact%", EXISTING_CONTACT);
xmlContactInfo = loadXml("contact_info").replace("%contact%", EXISTING_CONTACT);
LoadTestAction(@Parameter("tld") String tld) {
xmlDomainCheck =
loadXml("domain_check").replace("%tld%", tld).replace("%domain%", EXISTING_DOMAIN);
xmlDomainCreateTmpl = loadXml("domain_create").replace("%tld%", tld);
xmlDomainCreateFail =
xmlDomainCreateTmpl
.replace("%domain%", EXISTING_DOMAIN)
.replace("%contact%", EXISTING_CONTACT)
.replace("%host%", EXISTING_HOST);
xmlDomainInfo =
loadXml("domain_info").replace("%tld%", tld).replace("%domain%", EXISTING_DOMAIN);
xmlHostCreateTmpl = loadXml("host_create");
xmlHostCreateFail = xmlHostCreateTmpl.replace("%host%", EXISTING_HOST);
xmlHostInfo = loadXml("host_info").replace("%host%", EXISTING_HOST);
xsrfToken = xsrfTokenManager.generateToken("");
}
@Override
public void run() {
validateAndLogRequest();
DateTime initialStartSecond = DateTime.now(UTC).plusSeconds(delaySeconds);
DateTime initialStartSecond = clock.nowUtc().plusSeconds(delaySeconds);
ImmutableList.Builder<String> preTaskXmls = new ImmutableList.Builder<>();
ImmutableList.Builder<String> contactNamesBuilder = new ImmutableList.Builder<>();
ImmutableList.Builder<String> hostPrefixesBuilder = new ImmutableList.Builder<>();
for (int i = 0; i < successfulDomainCreatesPerSecond; i++) {
String contactName = getRandomLabel(MAX_CONTACT_LENGTH);
String hostPrefix = getRandomLabel(ARBITRARY_VALID_HOST_LENGTH);
contactNamesBuilder.add(contactName);
hostPrefixesBuilder.add(hostPrefix);
preTaskXmls.add(
xmlContactCreateTmpl.replace("%contact%", contactName),
xmlHostCreateTmpl.replace("%host%", hostPrefix));
}
enqueue(createTasks(preTaskXmls.build(), DateTime.now(UTC)));
ImmutableList<String> contactNames = contactNamesBuilder.build();
enqueue(createTasks(preTaskXmls.build(), clock.nowUtc()));
ImmutableList<String> hostPrefixes = hostPrefixesBuilder.build();
ImmutableList.Builder<Task> tasks = new ImmutableList.Builder<>();
@@ -217,30 +178,17 @@ public class LoadTestAction implements Runnable {
DateTime startSecond = initialStartSecond.plusSeconds(offsetSeconds);
// The first "failed" creates might actually succeed if the object doesn't already exist, but
// that shouldn't affect the load numbers.
tasks.addAll(
createTasks(
createNumCopies(xmlContactCreateFail, failedContactCreatesPerSecond), startSecond));
tasks.addAll(
createTasks(createNumCopies(xmlHostCreateFail, failedHostCreatesPerSecond), startSecond));
tasks.addAll(
createTasks(
createNumCopies(xmlDomainCreateFail, failedDomainCreatesPerSecond), startSecond));
// We can do infos on the known existing objects.
tasks.addAll(
createTasks(createNumCopies(xmlContactInfo, contactInfosPerSecond), startSecond));
tasks.addAll(createTasks(createNumCopies(xmlHostInfo, hostInfosPerSecond), startSecond));
tasks.addAll(createTasks(createNumCopies(xmlDomainInfo, domainInfosPerSecond), startSecond));
// The domain check template uses "example.TLD" which won't exist, and one existing domain.
tasks.addAll(
createTasks(createNumCopies(xmlDomainCheck, domainChecksPerSecond), startSecond));
// Do successful creates on random names
tasks.addAll(
createTasks(
createNumCopies(xmlContactCreateTmpl, successfulContactCreatesPerSecond)
.stream()
.map(randomNameReplacer("%contact%", MAX_CONTACT_LENGTH))
.collect(toImmutableList()),
startSecond));
tasks.addAll(
createTasks(
createNumCopies(xmlHostCreateTmpl, successfulHostCreatesPerSecond)
@@ -253,7 +201,6 @@ public class LoadTestAction implements Runnable {
createNumCopies(xmlDomainCreateTmpl, successfulDomainCreatesPerSecond)
.stream()
.map(randomNameReplacer("%domain%", MAX_DOMAIN_LABEL_LENGTH))
.map(listNameReplacer("%contact%", contactNames))
.map(listNameReplacer("%host%", hostPrefixes))
.collect(toImmutableList()),
startSecond));
@@ -272,9 +219,6 @@ public class LoadTestAction implements Runnable {
|| failedDomainCreatesPerSecond > 0
|| domainInfosPerSecond > 0
|| domainChecksPerSecond > 0
|| successfulContactCreatesPerSecond > 0
|| failedContactCreatesPerSecond > 0
|| contactInfosPerSecond > 0
|| successfulHostCreatesPerSecond > 0
|| failedHostCreatesPerSecond > 0
|| hostInfosPerSecond > 0,
@@ -282,8 +226,7 @@ public class LoadTestAction implements Runnable {
logger.atInfo().log(
"Running load test with the following params. registrarId: %s, delaySeconds: %d, "
+ "runSeconds: %d, successful|failed domain creates/s: %d|%d, domain infos/s: %d, "
+ "domain checks/s: %d, successful|failed contact creates/s: %d|%d, "
+ "contact infos/s: %d, successful|failed host creates/s: %d|%d, host infos/s: %d.",
+ "domain checks/s: %d, successful|failed host creates/s: %d|%d, host infos/s: %d.",
registrarId,
delaySeconds,
runSeconds,
@@ -291,9 +234,6 @@ public class LoadTestAction implements Runnable {
failedDomainCreatesPerSecond,
domainInfosPerSecond,
domainChecksPerSecond,
successfulContactCreatesPerSecond,
failedContactCreatesPerSecond,
contactInfosPerSecond,
successfulHostCreatesPerSecond,
failedHostCreatesPerSecond,
hostInfosPerSecond);
@@ -303,10 +243,10 @@ public class LoadTestAction implements Runnable {
return readResourceUtf8(LoadTestAction.class, String.format("templates/%s.xml", name));
}
private List<String> createNumCopies(String xml, int numCopies) {
private ImmutableList<String> createNumCopies(String xml, int numCopies) {
String[] xmls = new String[numCopies];
Arrays.fill(xmls, xml);
return asList(xmls);
return ImmutableList.copyOf(xmls);
}
private Function<String, String> listNameReplacer(final String toReplace, List<String> choices) {
@@ -326,35 +266,27 @@ public class LoadTestAction implements Runnable {
return name.toString();
}
private List<Task> createTasks(List<String> xmls, DateTime start) {
private ImmutableList<Task> createTasks(ImmutableList<String> xmls, DateTime start) {
ImmutableList.Builder<Task> tasks = new ImmutableList.Builder<>();
for (int i = 0; i < xmls.size(); i++) {
// Space tasks evenly within across a second.
Instant scheduleTime =
Instant.ofEpochMilli(start.plusMillis((int) (1000.0 / xmls.size() * i)).getMillis());
tasks.add(
Task.newBuilder()
.setAppEngineHttpRequest(
cloudTasksUtils
.createTask(
EppToolAction.class,
Action.Method.POST,
ImmutableMultimap.of(
"clientId",
registrarId,
"superuser",
Boolean.FALSE.toString(),
"dryRun",
Boolean.FALSE.toString(),
"xml",
xmls.get(i)))
.toBuilder()
.getAppEngineHttpRequest()
.toBuilder()
// TODO: investigate if the following is necessary now that
// LegacyAuthenticationMechanism is gone.
.putHeaders(X_CSRF_TOKEN, xsrfToken)
.build())
cloudTasksUtils
.createTask(
EppToolAction.class,
Action.Method.POST,
ImmutableMultimap.of(
"clientId",
registrarId,
"superuser",
Boolean.FALSE.toString(),
"dryRun",
Boolean.FALSE.toString(),
"xml",
xmls.get(i)))
.toBuilder()
.setScheduleTime(
Timestamp.newBuilder()
.setSeconds(scheduleTime.getEpochSecond())
@@ -365,7 +297,7 @@ public class LoadTestAction implements Runnable {
return tasks.build();
}
private void enqueue(List<Task> tasks) {
private void enqueue(ImmutableList<Task> tasks) {
List<List<Task>> chunks = partition(tasks, MAX_TASKS_PER_LOAD);
// Farm out tasks to multiple queues to work around queue qps quotas.
for (int i = 0; i < chunks.size(); i++) {

View File

@@ -1,33 +0,0 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>%contact%</contact:id>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example Inc.</contact:org>
<contact:addr>
<contact:street>123 Example Dr.</contact:street>
<contact:street>Suite 100</contact:street>
<contact:city>Dulles</contact:city>
<contact:sp>VA</contact:sp>
<contact:pc>20166-6503</contact:pc>
<contact:cc>US</contact:cc>
</contact:addr>
</contact:postalInfo>
<contact:voice x="1234">+1.7035555555</contact:voice>
<contact:fax>+1.7035555556</contact:fax>
<contact:email>jdoe@example.com</contact:email>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
<contact:disclose flag="1">
<contact:voice/>
<contact:email/>
</contact:disclose>
</contact:create>
</create>
<clTRID>trid</clTRID>
</command>
</epp>

View File

@@ -1,14 +0,0 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<info>
<contact:info
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>%contact%</contact:id>
<contact:authInfo>
<contact:pw>2fooBAR</contact:pw>
</contact:authInfo>
</contact:info>
</info>
<clTRID>trid</clTRID>
</command>
</epp>

View File

@@ -8,9 +8,6 @@
<domain:ns>
<domain:hostObj>%host%.example.com</domain:hostObj>
</domain:ns>
<domain:registrant>%contact%</domain:registrant>
<domain:contact type="admin">%contact%</domain:contact>
<domain:contact type="tech">%contact%</domain:contact>
<domain:authInfo>
<domain:pw>2fooBAR</domain:pw>
</domain:authInfo>

View File

@@ -4,6 +4,7 @@
<host:create
xmlns:host="urn:ietf:params:xml:ns:host-1.0">
<host:name>%host%.example.com</host:name>
<host:addr ip="v4">8.8.8.8</host:addr>
</host:create>
</create>
<clTRID>trid</clTRID>

View File

@@ -578,7 +578,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
* address.
*/
public ImmutableSortedSet<RegistrarPoc> getContacts() {
return getContactPocs(tm()).stream()
return getPocs(tm()).stream()
.filter(Objects::nonNull)
.collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR));
}
@@ -590,8 +590,8 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
* <p>This method queries the replica database. It is reserved for use cases that can tolerate
* slightly stale data.
*/
public ImmutableSortedSet<RegistrarPoc> getContactsFromReplica() {
return getContactPocs(replicaTm()).stream()
public ImmutableSortedSet<RegistrarPoc> getPocsFromReplica() {
return getPocs(replicaTm()).stream()
.filter(Objects::nonNull)
.collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR));
}
@@ -600,8 +600,8 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
* Returns a list of {@link RegistrarPoc} objects of a given type for this registrar sorted by
* their email address.
*/
public ImmutableSortedSet<RegistrarPoc> getContactsOfType(final RegistrarPoc.Type type) {
return getContactPocs(tm()).stream()
public ImmutableSortedSet<RegistrarPoc> getPocsOfType(final RegistrarPoc.Type type) {
return getPocs(tm()).stream()
.filter(Objects::nonNull)
.filter((@Nullable RegistrarPoc contact) -> contact.getTypes().contains(type))
.collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR));
@@ -615,7 +615,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
return getContacts().stream().filter(RegistrarPoc::getVisibleInDomainWhoisAsAbuse).findFirst();
}
private ImmutableList<RegistrarPoc> getContactPocs(TransactionManager txnManager) {
private ImmutableList<RegistrarPoc> getPocs(TransactionManager txnManager) {
return txnManager.transact(() -> RegistrarPoc.loadForRegistrar(registrarId));
}

View File

@@ -95,10 +95,4 @@ public class SignedMarkRevocationList extends ImmutableObject {
public int size() {
return revokes.size();
}
/** Save this list to Cloud SQL. Returns {@code this}. */
public SignedMarkRevocationList save() {
SignedMarkRevocationListDao.save(this);
return this;
}
}

View File

@@ -44,11 +44,25 @@ public class SignedMarkRevocationListDao {
return smdrl.orElseGet(() -> SignedMarkRevocationList.create(START_OF_TIME, ImmutableMap.of()));
}
/** Save the given {@link SignedMarkRevocationList} */
static void save(SignedMarkRevocationList signedMarkRevocationList) {
tm().transact(() -> tm().insert(signedMarkRevocationList));
/**
* Persists a {@link SignedMarkRevocationList} instance and returns the persisted entity.
*
* <p>Note that the input parameter is untouched. Use the returned object if metadata fields like
* {@code revisionId} are needed.
*/
public static SignedMarkRevocationList save(SignedMarkRevocationList signedMarkRevocationList) {
var persisted =
tm().transact(
() -> {
var entity =
SignedMarkRevocationList.create(
signedMarkRevocationList.getCreationTime(),
ImmutableMap.copyOf(signedMarkRevocationList.revokes));
tm().insert(entity);
return entity;
});
logger.atInfo().log(
"Inserted %,d signed mark revocations into Cloud SQL.",
signedMarkRevocationList.revokes.size());
"Inserted %,d signed mark revocations into Cloud SQL.", persisted.revokes.size());
return persisted;
}
}

View File

@@ -133,24 +133,30 @@ public final class PremiumListDao {
}
/** Saves the given premium list (and its premium list entries) to Cloud SQL. */
public static PremiumList save(PremiumList premiumList) {
tm().transact(
() -> {
tm().insert(premiumList);
tm().getEntityManager().flush(); // This populates the revisionId.
long revisionId = premiumList.getRevisionId();
public static PremiumList save(PremiumList premiumListToPersist) {
PremiumList persisted =
tm().transact(
() -> {
// Make a new copy in each attempt to insert. See javadoc of the insert method for
// more information.
PremiumList premiumList = premiumListToPersist.asBuilder().build();
tm().insert(premiumList);
tm().getEntityManager().flush(); // This populates the revisionId.
long revisionId = premiumList.getRevisionId();
if (!isNullOrEmpty(premiumList.getLabelsToPrices())) {
ImmutableSet.Builder<PremiumEntry> entries = new ImmutableSet.Builder<>();
premiumList
.getLabelsToPrices()
.forEach(
(key, value) -> entries.add(PremiumEntry.create(revisionId, value, key)));
tm().insertAll(entries.build());
}
});
premiumListCache.invalidate(premiumList.getName());
return premiumList;
if (!isNullOrEmpty(premiumList.getLabelsToPrices())) {
ImmutableSet.Builder<PremiumEntry> entries = new ImmutableSet.Builder<>();
premiumList
.getLabelsToPrices()
.forEach(
(key, value) ->
entries.add(PremiumEntry.create(revisionId, value, key)));
tm().insertAll(entries.build());
}
return premiumList;
});
premiumListCache.invalidate(persisted.getName());
return persisted;
}
public static void delete(PremiumList premiumList) {

View File

@@ -27,14 +27,26 @@ public class ReservedListDao {
private ReservedListDao() {}
/** Persist a new reserved list to Cloud SQL. */
public static void save(ReservedList reservedList) {
/**
* Persists a new reserved list to Cloud SQL and returns the persisted entity.
*
* <p>Note that the input parameter is untouched. Use the returned object if metadata fields like
* {@code revisionId} are needed.
*/
public static ReservedList save(ReservedList reservedList) {
checkArgumentNotNull(reservedList, "Must specify reservedList");
logger.atInfo().log("Saving reserved list %s to Cloud SQL.", reservedList.getName());
tm().transact(() -> tm().insert(reservedList));
var persisted =
tm().transact(
() -> {
var entity = reservedList.asBuilder().build();
tm().insert(entity);
return entity;
});
logger.atInfo().log(
"Saved reserved list %s with %d entries to Cloud SQL.",
reservedList.getName(), reservedList.getReservedListEntries().size());
return persisted;
}
/** Deletes a reserved list from Cloud SQL. */

View File

@@ -49,10 +49,23 @@ public class ClaimsListDao {
return CacheUtils.newCacheBuilder(expiry).build(ignored -> ClaimsListDao.getUncached());
}
/** Saves the given {@link ClaimsList} to Cloud SQL. */
public static void save(ClaimsList claimsList) {
tm().transact(() -> tm().insert(claimsList));
CACHE.put(ClaimsListDao.class, claimsList);
/**
* Persists a {@link ClaimsList} instance and returns the persisted entity.
*
* <p>Note that the input parameter is untouched. Use the returned object if metadata fields like
* {@code revisionId} are needed.
*/
public static ClaimsList save(ClaimsList claimsList) {
var persisted =
tm().transact(
() -> {
var entity =
ClaimsList.create(claimsList.tmdbGenerationTime, claimsList.labelsToKeys);
tm().insert(entity);
return entity;
});
CACHE.put(ClaimsListDao.class, persisted);
return persisted;
}
/** Returns the most recent revision of the {@link ClaimsList} from the cache. */

View File

@@ -344,6 +344,18 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
return txnInfo.transactionTime;
}
/**
* Inserts an object into the database.
*
* <p>If {@code entity} has an auto-generated identity field (i.e., a field annotated with {@link
* jakarta.persistence.GeneratedValue}), the caller must not assign a value to this field,
* otherwise Hibernate would mistake the entity as detached and raise an error.
*
* <p>The practical implication of the above is that when inserting such an entity using a
* retriable transaction , the entity should be instantiated inside the transaction body. A failed
* attempt may still assign and ID to the entity, therefore reusing the same entity would cause
* retries to fail.
*/
@Override
public void insert(Object entity) {
checkArgumentNotNull(entity, "entity must be specified");

View File

@@ -333,7 +333,6 @@ final class RdapDataStructures {
*/
@RestrictJsonNames("status[]")
enum RdapStatus implements Jsonable {
// Status values specified in RFC 9083 § 10.2.2.
VALIDATED("validated"),
RENEW_PROHIBITED("renew prohibited"),

View File

@@ -57,6 +57,9 @@ public class RdapDomainAction extends RdapActionBase {
InternetDomainName domainName;
try {
domainName = validateDomainName(pathSearchString);
} catch (DomainFlowUtils.TldDoesNotExistException e) {
// A special case where a valid domain name on a nonexistent TLD should return 404
throw new NotFoundException(pathSearchString + " not found");
} catch (EppException e) {
throw new BadRequestException(
String.format(

View File

@@ -14,18 +14,13 @@
package google.registry.rdap;
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
import static google.registry.rdap.RdapUtils.getRegistrarByIanaIdentifier;
import static google.registry.rdap.RdapUtils.getRegistrarByName;
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.HEAD;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Longs;
import com.google.re2j.Pattern;
import google.registry.model.contact.Contact;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.VKey;
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapObjectClasses.RdapEntity;
@@ -37,8 +32,8 @@ import jakarta.inject.Inject;
import java.util.Optional;
/**
* RDAP action for entity (contact and registrar) requests. the ICANN operational profile dictates
* that the "handle" for registrars is to be the IANA registrar ID:
* RDAP action for entity (i.e. registrar) requests. the ICANN operational profile dictates that the
* "handle" for registrars is to be the IANA registrar ID:
*
* <p>2.4.1.Registry RDAP servers MUST support Registrar object lookup using an entity path request
* for entities with the registrar role using the handle (as described in 3.1.5 of RFC9082) where
@@ -52,8 +47,6 @@ import java.util.Optional;
auth = Auth.AUTH_PUBLIC)
public class RdapEntityAction extends RdapActionBase {
private static final Pattern ROID_PATTERN = Pattern.compile("[-_.a-zA-Z0-9]+");
@Inject public RdapEntityAction() {
super("entity", EndpointType.ENTITY);
}
@@ -61,24 +54,6 @@ public class RdapEntityAction extends RdapActionBase {
@Override
public RdapEntity getJsonObjectForResource(
String pathSearchString, boolean isHeadRequest) {
// The query string is not used; the RDAP syntax is /rdap/entity/handle (the handle is the roid
// for contacts and the client identifier/fn for registrars). Since RDAP's concept of an entity
// includes both contacts and registrars, search for one first, then the other.
// RDAP Technical Implementation Guide 2.3.1 - MUST support contact entity lookup using the
// handle
if (ROID_PATTERN.matcher(pathSearchString).matches()) {
VKey<Contact> contactVKey = VKey.create(Contact.class, pathSearchString);
Optional<Contact> contact =
replicaTm().transact(() -> replicaTm().loadByKeyIfPresent(contactVKey));
// As per Andy Newton on the regext mailing list, contacts by themselves have no role, since
// they are global, and might have different roles for different domains.
if (contact.isPresent() && isAuthorized(contact.get())) {
return rdapJsonFormatter.createRdapContactEntity(
contact.get(), ImmutableSet.of(), OutputDataType.FULL);
}
}
// RDAP Technical Implementation Guide 2.4.1 - MUST support registrar entity lookup using the
// IANA ID as handle
Long ianaIdentifier = Longs.tryParse(pathSearchString);
@@ -96,7 +71,7 @@ public class RdapEntityAction extends RdapActionBase {
return rdapJsonFormatter.createRdapRegistrarEntity(registrar.get(), OutputDataType.FULL);
}
// At this point, we have failed to find either a contact or a registrar.
// At this point, we have failed to find a registrar.
//
// RFC7480 5.3 - if the server wishes to respond that it doesn't have data satisfying the
// query, it MUST reply with 404 response code.

View File

@@ -15,22 +15,16 @@
package google.registry.rdap;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
import static google.registry.rdap.RdapUtils.getRegistrarByIanaIdentifier;
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.HEAD;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
import com.google.common.primitives.Booleans;
import com.google.common.primitives.Longs;
import google.registry.model.contact.Contact;
import google.registry.model.registrar.Registrar;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.CriteriaQueryBuilder;
import google.registry.rdap.RdapAuthorization.Role;
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.rdap.RdapMetrics.EndpointType;
import google.registry.rdap.RdapMetrics.SearchType;
@@ -49,27 +43,12 @@ import java.util.List;
import java.util.Optional;
/**
* RDAP action for entity (contact and registrar) search requests.
* RDAP action for entity (i.e. registrar) search requests.
*
* <p>All commands and responses conform to the RDAP spec as defined in STD 95 and its RFCs.
*
* <p>The RDAP specification lumps contacts and registrars together and calls them "entities", which
* is confusing for us, because "entity" means something else in SQL. But here, when we use the
* term, it means either a contact or registrar. When searching for entities, we always start by
* returning all matching contacts, and after that all matching registrars.
*
* <p>There are two ways to search for entities: by full name (for contacts, the search name, for
* registrars, the registrar name) or by handle (for contacts, the ROID, for registrars, the IANA
* number). The ICANN operational profile document specifies this meaning for handle searches.
*
* <p>Cursors are complicated by the fact that we are essentially doing two independent searches:
* one for contacts, and one for registrars. To accommodate this, the cursor has a prefix indicating
* the type of the last returned item. If the last item was a contact, we return c:{value}, where
* the value is either the search name or the ROID. If the last item was a registrar, we return
* r:{value}, where the value is either the registrar name or the IANA number. If we get a c:
* cursor, we use it to weed out contacts, and fetch all registrars. If we get an r: cursor, we know
* that we can skip the contact search altogether (because we returned a registrar, and all
* registrars come after all contacts).
* <p>There are two ways to search for entities: by full name (the registrar name) or by handle (the
* IANA number). The ICANN operational profile document specifies this meaning for handle searches.
*
* @see <a href="http://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
* (RDAP) Query Format</a>
@@ -85,31 +64,10 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
@Inject @Parameter("fn") Optional<String> fnParam;
@Inject @Parameter("handle") Optional<String> handleParam;
@Inject @Parameter("subtype") Optional<String> subtypeParam;
@Inject public RdapEntitySearchAction() {
super("entity search", EndpointType.ENTITIES);
}
private enum QueryType {
FULL_NAME,
HANDLE
}
private enum Subtype {
ALL,
CONTACTS,
REGISTRARS
}
private enum CursorType {
NONE,
CONTACT,
REGISTRAR
}
private static final String CONTACT_CURSOR_PREFIX = "c:";
private static final String REGISTRAR_CURSOR_PREFIX = "r:";
/** Parses the parameters and calls the appropriate search function. */
@Override
public EntitySearchResponse getSearchResponse(boolean isHeadRequest) {
@@ -118,61 +76,23 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
throw new BadRequestException("You must specify either fn=XXXX or handle=YYYY");
}
// Check the subtype.
Subtype subtype;
if (subtypeParam.isEmpty() || subtypeParam.get().equalsIgnoreCase("all")) {
subtype = Subtype.ALL;
} else if (subtypeParam.get().equalsIgnoreCase("contacts")) {
subtype = Subtype.CONTACTS;
} else if (subtypeParam.get().equalsIgnoreCase("registrars")) {
subtype = Subtype.REGISTRARS;
} else {
throw new BadRequestException("Subtype parameter must specify contacts, registrars or all");
}
CursorType cursorType;
Optional<String> cursorQueryString;
if (cursorString.isEmpty()) {
cursorType = CursorType.NONE;
cursorQueryString = Optional.empty();
} else {
if (cursorString.get().startsWith(CONTACT_CURSOR_PREFIX)) {
cursorType = CursorType.CONTACT;
cursorQueryString =
Optional.of(cursorString.get().substring(CONTACT_CURSOR_PREFIX.length()));
} else if (cursorString.get().startsWith(REGISTRAR_CURSOR_PREFIX)) {
cursorType = CursorType.REGISTRAR;
cursorQueryString =
Optional.of(cursorString.get().substring(REGISTRAR_CURSOR_PREFIX.length()));
} else {
throw new BadRequestException(String.format("invalid cursor: %s", cursorTokenParam));
}
}
// Search by name.
EntitySearchResponse results;
if (fnParam.isPresent()) {
metricInformationBuilder.setSearchType(SearchType.BY_FULL_NAME);
// syntax: /rdap/entities?fn=Bobby%20Joe*
// The name is the contact name or registrar name (not registrar contact name).
// The name is the registrar name (not registrar contact name).
results =
searchByName(
recordWildcardType(RdapSearchPattern.createFromUnicodeString(fnParam.get())),
cursorType,
cursorQueryString,
subtype);
// Search by handle.
recordWildcardType(RdapSearchPattern.createFromUnicodeString(fnParam.get())));
} else {
// Search by handle.
metricInformationBuilder.setSearchType(SearchType.BY_HANDLE);
// syntax: /rdap/entities?handle=12345-*
// The handle is either the contact roid or the registrar clientId.
// The handle is the registrar ID.
results =
searchByHandle(
recordWildcardType(RdapSearchPattern.createFromUnicodeString(handleParam.get())),
cursorType,
cursorQueryString,
subtype);
recordWildcardType(RdapSearchPattern.createFromUnicodeString(handleParam.get())));
}
// Build the result object and return it.
@@ -194,91 +114,32 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
* <p>According to RFC 9082 section 6.1, punycode is only used for domain name labels, so we can
* assume that entity names are regular unicode.
*
* <p>The includeDeleted flag is ignored when searching for contacts, because contact names are
* set to null when the contact is deleted, so a deleted contact can never have a name.
*
* <p>Since we are restricting access to contact names, we don't want name searches to return
* contacts whose names are not visible. That would allow unscrupulous users to query by name and
* infer that all returned contacts contain that name string. So we check the authorization level
* to determine what to do.
*
* @see <a
* href="https://newgtlds.icann.org/sites/default/files/agreements/agreement-approved-09jan14-en.htm">1.6
* of Section 4 of the Base Registry Agreement</a>
*/
private EntitySearchResponse searchByName(
final RdapSearchPattern partialStringQuery,
CursorType cursorType,
Optional<String> cursorQueryString,
Subtype subtype) {
private EntitySearchResponse searchByName(final RdapSearchPattern partialStringQuery) {
// Don't allow wildcard suffixes when searching for entities.
if (partialStringQuery.getHasWildcard() && (partialStringQuery.getSuffix() != null)) {
throw new UnprocessableEntityException(
"Suffixes not allowed in wildcard entity name searches");
}
// For wildcards, make sure the initial string is long enough, except in the special case of
// searching for all registrars, where we aren't worried about inefficient searches.
if (partialStringQuery.getHasWildcard()
&& (subtype != Subtype.REGISTRARS)
&& (partialStringQuery.getInitialString().length()
< RdapSearchPattern.MIN_INITIAL_STRING_LENGTH)) {
throw new UnprocessableEntityException(
"Initial search string required in wildcard entity name searches");
}
// Get the registrar matches. If we have a registrar cursor, weed out registrars up to and
// including the one we ended with last time. We can skip registrars if subtype is CONTACTS.
ImmutableList<Registrar> registrars;
if (subtype == Subtype.CONTACTS) {
registrars = ImmutableList.of();
} else {
registrars =
Streams.stream(Registrar.loadAllCached())
.sorted(
Comparator.comparing(Registrar::getRegistrarName, String.CASE_INSENSITIVE_ORDER))
.filter(
registrar ->
partialStringQuery.matches(registrar.getRegistrarName())
&& ((cursorType != CursorType.REGISTRAR)
|| (registrar.getRegistrarName().compareTo(cursorQueryString.get())
> 0))
&& shouldBeVisible(registrar))
.limit(rdapResultSetMaxSize + 1)
.collect(toImmutableList());
}
// Get the contact matches and return the results, fetching an additional contact to detect
// truncation. Don't bother searching for contacts by name if the request would not be able to
// see any names anyway. Also, if a registrar cursor is present, we have already moved past the
// contacts, and don't need to fetch them this time. We can skip contacts if subtype is
// REGISTRARS.
RdapResultSet<Contact> resultSet;
if (subtype == Subtype.REGISTRARS) {
resultSet = RdapResultSet.create(ImmutableList.of());
} else {
if ((rdapAuthorization.role() == RdapAuthorization.Role.PUBLIC)
|| (cursorType == CursorType.REGISTRAR)) {
resultSet = RdapResultSet.create(ImmutableList.of());
} else {
resultSet =
replicaTm()
.transact(
() -> {
CriteriaQueryBuilder<Contact> builder =
queryItems(
Contact.class,
"searchName",
partialStringQuery,
cursorQueryString,
DeletedItemHandling.EXCLUDE);
if (!rdapAuthorization.role().equals(Role.ADMINISTRATOR)) {
builder =
builder.whereFieldIsIn(
"currentSponsorRegistrarId", rdapAuthorization.registrarIds());
}
return getMatchingResources(builder, false, rdapResultSetMaxSize + 1);
});
}
}
return makeSearchResults(resultSet, registrars, QueryType.FULL_NAME);
// Get the registrar matches. If we have a cursor, weed out registrars up to and including the
// one we ended with last time.
ImmutableList<Registrar> registrars =
Streams.stream(Registrar.loadAllCached())
.sorted(
Comparator.comparing(Registrar::getRegistrarName, String.CASE_INSENSITIVE_ORDER))
.filter(
registrar ->
partialStringQuery.matches(registrar.getRegistrarName())
&& (cursorString.isEmpty()
|| (registrar.getRegistrarName().compareTo(cursorString.get()) > 0))
&& shouldBeVisible(registrar))
.limit(rdapResultSetMaxSize + 1)
.collect(toImmutableList());
return makeSearchResults(registrars);
}
/**
@@ -288,101 +149,41 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
*
* <p>We don't allow suffixes after a wildcard in entity searches. Suffixes are used in domain
* searches to specify a TLD, and in nameserver searches to specify a locally managed domain name.
* In both cases, the suffix can be turned into an additional query filter field. For contacts,
* there is no equivalent string suffix that can be used as a query filter, so we disallow use.
* In both cases, the suffix can be turned into an additional query filter field.
*/
private EntitySearchResponse searchByHandle(
final RdapSearchPattern partialStringQuery,
CursorType cursorType,
Optional<String> cursorQueryString,
Subtype subtype) {
private EntitySearchResponse searchByHandle(final RdapSearchPattern partialStringQuery) {
if (partialStringQuery.getSuffix() != null) {
throw new UnprocessableEntityException("Suffixes not allowed in entity handle searches");
}
// Handle queries without a wildcard (and not including deleted) -- load by ID.
if (!partialStringQuery.getHasWildcard() && !shouldIncludeDeleted()) {
ImmutableList<Contact> contactList;
if (subtype == Subtype.REGISTRARS) {
contactList = ImmutableList.of();
} else {
Optional<Contact> contact =
replicaTm()
.transact(
() ->
replicaTm()
.loadByKeyIfPresent(
VKey.create(Contact.class, partialStringQuery.getInitialString())));
contactList =
(contact.isPresent() && shouldBeVisible(contact.get()))
? ImmutableList.of(contact.get())
: ImmutableList.of();
}
ImmutableList<Registrar> registrarList;
if (subtype == Subtype.CONTACTS) {
registrarList = ImmutableList.of();
} else {
registrarList = getMatchingRegistrars(partialStringQuery.getInitialString());
}
return makeSearchResults(
contactList,
IncompletenessWarningType.COMPLETE,
contactList.size(),
registrarList,
QueryType.HANDLE);
return makeSearchResults(getMatchingRegistrars(partialStringQuery.getInitialString()));
}
// Handle queries with a wildcard (or including deleted), but no suffix. Because the handle
// for registrars is the IANA identifier number, don't allow wildcard searches for registrars,
// by simply not searching for registrars if a wildcard is present (unless the request is for
// all registrars, in which case we know what to do). Fetch an extra contact to detect result
// set truncation.
// all registrars, in which case we know what to do).
ImmutableList<Registrar> registrars;
if (partialStringQuery.getHasWildcard() && partialStringQuery.getInitialString().isEmpty()) {
// Even though we are searching by IANA identifier, we should still sort by name, because
// the IANA identifier can by missing, and sorting on that would screw up our cursors.
registrars =
Streams.stream(Registrar.loadAllCached())
.sorted(
Comparator.comparing(Registrar::getRegistrarName, String.CASE_INSENSITIVE_ORDER))
.filter(
registrar ->
(cursorString.isEmpty()
|| (registrar.getRegistrarName().compareTo(cursorString.get()) > 0))
&& shouldBeVisible(registrar))
.limit(rdapResultSetMaxSize + 1)
.collect(toImmutableList());
} else if (partialStringQuery.getHasWildcard()) {
registrars = ImmutableList.of();
} else {
ImmutableList<Registrar> registrars;
if ((subtype == Subtype.REGISTRARS)
&& partialStringQuery.getHasWildcard()
&& partialStringQuery.getInitialString().isEmpty()) {
// Even though we are searching by IANA identifier, we should still sort by name, because
// the IANA identifier can by missing, and sorting on that would screw up our cursors.
registrars =
Streams.stream(Registrar.loadAllCached())
.sorted(
Comparator.comparing(
Registrar::getRegistrarName, String.CASE_INSENSITIVE_ORDER))
.filter(
registrar ->
((cursorType != CursorType.REGISTRAR)
|| (registrar.getRegistrarName().compareTo(cursorQueryString.get())
> 0))
&& shouldBeVisible(registrar))
.limit(rdapResultSetMaxSize + 1)
.collect(toImmutableList());
} else if ((subtype == Subtype.CONTACTS) || partialStringQuery.getHasWildcard()) {
registrars = ImmutableList.of();
} else {
registrars = getMatchingRegistrars(partialStringQuery.getInitialString());
}
// Get the contact matches and return the results, fetching an additional contact to detect
// truncation. If we are including deleted entries, we must fetch more entries, in case some
// get excluded due to permissioning. Any cursor present must be a contact cursor, because we
// would never return a registrar for this search.
int querySizeLimit = getStandardQuerySizeLimit();
RdapResultSet<Contact> contactResultSet;
if (subtype == Subtype.REGISTRARS) {
contactResultSet = RdapResultSet.create(ImmutableList.of());
} else {
contactResultSet =
replicaTm()
.transact(
() ->
getMatchingResources(
queryItemsByKey(
Contact.class,
partialStringQuery,
cursorQueryString,
getDeletedItemHandling()),
shouldIncludeDeleted(),
querySizeLimit));
}
return makeSearchResults(contactResultSet, registrars, QueryType.HANDLE);
registrars = getMatchingRegistrars(partialStringQuery.getInitialString());
}
return makeSearchResults(registrars);
}
/** Looks up registrars by handle (i.e. IANA identifier). */
@@ -397,90 +198,25 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
: ImmutableList.of();
}
/**
* Builds a JSON array of entity info maps based on the specified contacts and registrars.
*
* <p>This is a convenience wrapper for the four-argument makeSearchResults; it unpacks the
* properties of the {@link RdapResultSet} structure and passes them as separate arguments.
*/
private EntitySearchResponse makeSearchResults(
RdapResultSet<Contact> resultSet, List<Registrar> registrars, QueryType queryType) {
return makeSearchResults(
resultSet.resources(),
resultSet.incompletenessWarningType(),
resultSet.numResourcesRetrieved(),
registrars,
queryType);
}
/**
* Builds a JSON array of entity info maps based on the specified contacts and registrars.
*
* <p>The number of contacts retrieved is recorded for use by the metrics.
*
* @param contacts the list of contacts which can be returned
* @param incompletenessWarningType MIGHT_BE_INCOMPLETE if the list of contacts might be
* incomplete; this only matters if the total count of contacts and registrars combined is
* less than a full result set's worth
* @param numContactsRetrieved the number of contacts retrieved in the process of generating the
* results
* @param registrars the list of registrars which can be returned
* @param queryType whether the query was by full name or by handle
* @return an {@link RdapSearchResults} object
*/
private EntitySearchResponse makeSearchResults(
List<Contact> contacts,
IncompletenessWarningType incompletenessWarningType,
int numContactsRetrieved,
List<Registrar> registrars,
QueryType queryType) {
metricInformationBuilder.setNumContactsRetrieved(numContactsRetrieved);
/** Builds a JSON array of entity info maps based on the specified registrars. */
private EntitySearchResponse makeSearchResults(List<Registrar> registrars) {
// Determine what output data type to use, depending on whether more than one entity will be
// returned.
OutputDataType outputDataType =
(contacts.size() + registrars.size() > 1) ? OutputDataType.SUMMARY : OutputDataType.FULL;
// There can be more results than our max size, partially because we have two pools to draw from
// (contacts and registrars), and partially because we try to fetch one more than the max size,
// so we can tell whether to display the truncation notification.
//
// Each time we add a contact or registrar to the output data set, remember what the appropriate
// cursor would be if it were the last item returned. When we stop adding items, the last cursor
// value we remembered will be the right one to pass back.
EntitySearchResponse.Builder builder =
EntitySearchResponse.builder()
.setIncompletenessWarningType(incompletenessWarningType);
Optional<String> newCursor = Optional.empty();
for (Contact contact : Iterables.limit(contacts, rdapResultSetMaxSize)) {
// As per Andy Newton on the regext mailing list, contacts by themselves have no role, since
// they are global, and might have different roles for different domains.
registrars.size() > 1 ? OutputDataType.SUMMARY : OutputDataType.FULL;
EntitySearchResponse.Builder builder = EntitySearchResponse.builder();
Iterable<Registrar> limitedRegistrars = Iterables.limit(registrars, rdapResultSetMaxSize);
for (Registrar registrar : limitedRegistrars) {
builder
.entitySearchResultsBuilder()
.add(
rdapJsonFormatter.createRdapContactEntity(
contact, ImmutableSet.of(), outputDataType));
newCursor =
Optional.of(
CONTACT_CURSOR_PREFIX
+ ((queryType == QueryType.FULL_NAME)
? contact.getSearchName()
: contact.getRepoId()));
.add(rdapJsonFormatter.createRdapRegistrarEntity(registrar, outputDataType));
}
if (rdapResultSetMaxSize > contacts.size()) {
for (Registrar registrar :
Iterables.limit(registrars, rdapResultSetMaxSize - contacts.size())) {
builder
.entitySearchResultsBuilder()
.add(rdapJsonFormatter.createRdapRegistrarEntity(registrar, outputDataType));
newCursor = Optional.of(REGISTRAR_CURSOR_PREFIX + registrar.getRegistrarName());
}
}
if (rdapResultSetMaxSize < contacts.size() + registrars.size()) {
builder.setNextPageUri(createNavigationUri(newCursor.get()));
if (rdapResultSetMaxSize < registrars.size()) {
builder.setNextPageUri(
createNavigationUri(Iterables.getLast(limitedRegistrars).getRegistrarName()));
builder.setIncompletenessWarningType(IncompletenessWarningType.TRUNCATED);
return builder.build();
} else {
builder.setIncompletenessWarningType(IncompletenessWarningType.COMPLETE);
}
return builder.build();
}

View File

@@ -117,44 +117,4 @@ public class RdapIcannStandardInformation {
/** Possibly incomplete notice as a singleton list, for easy use. */
static final ImmutableList<Notice> POSSIBLY_INCOMPLETE_NOTICES =
ImmutableList.of(POSSIBLY_INCOMPLETE_RESULT_SET_NOTICE);
/**
* Included when requester is not logged in as the owner of the contact being returned.
*
* <p>>Note: if we were keeping this around, we'd want/need to implement the <a
* href="https://datatracker.ietf.org/doc/rfc9537/">official RDAP redaction spec</a> for contacts.
* We are getting rid of contacts in 2025 though so this should be unnecessary.
*/
static final Remark CONTACT_PERSONAL_DATA_HIDDEN_DATA_REMARK =
Remark.builder()
.setTitle("REDACTED FOR PRIVACY")
.setDescription(
"Some of the data in this object has been removed.",
"Contact personal data is visible only to the owning registrar.")
.setType(Remark.Type.OBJECT_REDACTED_AUTHORIZATION)
.addLink(
Link.builder()
.setRel("alternate")
.setHref(
"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication")
.setType("text/html")
.build())
.build();
/**
* Included in ALL contact responses, even if the user is authorized.
*
* <p>>Note: if we were keeping this around, we'd want/need to implement the <a
* href="https://datatracker.ietf.org/doc/rfc9537/">official RDAP redaction spec</a> for contacts.
* We are getting rid of contacts in 2025 though so this should be unnecessary.
*/
static final Remark CONTACT_EMAIL_REDACTED_FOR_DOMAIN =
Remark.builder()
.setTitle("EMAIL REDACTED FOR PRIVACY")
.setDescription(
"Please query the RDDS service of the Registrar of Record identifies in this output"
+ " for information on how to contact the Registrant of the queried domain"
+ " name.")
.setType(Remark.Type.OBJECT_REDACTED_AUTHORIZATION)
.build();
}

View File

@@ -18,17 +18,14 @@ import static com.google.common.base.Predicates.not;
import static com.google.common.base.Strings.nullToEmpty;
import static com.google.common.collect.ImmutableList.toImmutableList;
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.util.CollectionUtils.union;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Ordering;
import com.google.common.collect.Sets;
import com.google.common.collect.Streams;
@@ -40,11 +37,6 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.model.CacheUtils;
import google.registry.model.EppResource;
import google.registry.model.adapters.EnumToAttributeAdapter.EppEnum;
import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactPhoneNumber;
import google.registry.model.contact.PostalInfo;
import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.DesignatedContact.Type;
import google.registry.model.domain.Domain;
import google.registry.model.domain.rgp.GracePeriodStatus;
import google.registry.model.eppcommon.Address;
@@ -55,19 +47,17 @@ import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntryDao;
import google.registry.persistence.VKey;
import google.registry.rdap.RdapDataStructures.Event;
import google.registry.rdap.RdapDataStructures.EventAction;
import google.registry.rdap.RdapDataStructures.Link;
import google.registry.rdap.RdapDataStructures.Notice;
import google.registry.rdap.RdapDataStructures.PublicId;
import google.registry.rdap.RdapDataStructures.RdapStatus;
import google.registry.rdap.RdapObjectClasses.RdapContactEntity;
import google.registry.rdap.RdapObjectClasses.RdapDomain;
import google.registry.rdap.RdapObjectClasses.RdapEntity;
import google.registry.rdap.RdapObjectClasses.RdapEntity.Role;
import google.registry.rdap.RdapObjectClasses.RdapNameserver;
import google.registry.rdap.RdapObjectClasses.RdapRegistrarEntity;
import google.registry.rdap.RdapObjectClasses.RdapRegistrarPocEntity;
import google.registry.rdap.RdapObjectClasses.SecureDns;
import google.registry.rdap.RdapObjectClasses.Vcard;
import google.registry.rdap.RdapObjectClasses.VcardArray;
@@ -134,8 +124,8 @@ public class RdapJsonFormatter {
* What type of data to generate.
*
* <p>Summary data includes only information about the object itself, while full data includes
* associated items (e.g. for domains, full data includes the hosts, contacts and history entries
* connected with the domain).
* associated items (e.g. for domains, full data includes the hosts and history entries connected
* with the domain).
*
* <p>Summary data is appropriate for search queries which return many results, to avoid load on
* the system. According to the ICANN operational profile, a remark must be attached to the
@@ -260,10 +250,6 @@ public class RdapJsonFormatter {
private static final Ordering<Host> HOST_RESOURCE_ORDERING =
Ordering.natural().onResultOf(Host::getHostName);
/** Sets the ordering for designated contacts; order them in a fixed order by contact type. */
private static final Ordering<DesignatedContact> DESIGNATED_CONTACT_ORDERING =
Ordering.natural().onResultOf(DesignatedContact::getType);
/** Creates the TOS notice that is added to every reply. */
Notice createTosNotice() {
String linkValue = makeRdapServletRelativeUrl("help", RdapHelpAction.TOS_PATH);
@@ -387,43 +373,6 @@ public class RdapJsonFormatter {
.transact(
() ->
ImmutableSet.copyOf(replicaTm().loadByKeys(domain.getNameservers()).values()));
// Load the registrant and other contacts and add them to the data.
ImmutableSet<VKey<Contact>> contacts = domain.getReferencedContacts();
ImmutableMap<VKey<? extends Contact>, Contact> loadedContacts =
contacts.isEmpty()
? ImmutableMap.of()
: replicaTm().transact(() -> replicaTm().loadByKeysIfPresent(contacts));
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts. 2.7.4 discusses redaction of
// fields we don't want to show (as opposed to not having contacts at all) because of GDPR etc.
//
// The GDPR redaction is handled in createRdapContactEntity.
// Load all contacts that are present and group them by type (it is common for a single contact
// entity to be used across multiple contact types on domain, e.g. registrant and admin).
ImmutableSetMultimap<VKey<Contact>, Type> contactsToRoles =
domain.getAllContacts().stream()
.sorted(DESIGNATED_CONTACT_ORDERING)
.collect(
toImmutableSetMultimap(
DesignatedContact::getContactKey, DesignatedContact::getType));
// Convert the contact entities to RDAP output contacts (this also converts the contact types
// to RDAP roles).
for (VKey<Contact> contactKey : contactsToRoles.keySet()) {
Set<Role> roles =
contactsToRoles.get(contactKey).stream()
.map(RdapJsonFormatter::convertContactTypeToRdapRole)
.collect(toImmutableSet());
if (roles.isEmpty()) {
continue;
}
builder
.entitiesBuilder()
.add(
createRdapContactEntity(
loadedContacts.get(contactKey), roles, OutputDataType.INTERNAL));
}
// Add the nameservers to the data; the load was kicked off above for efficiency.
// RDAP Response Profile 2.8: we MUST have the nameservers
@@ -523,134 +472,6 @@ public class RdapJsonFormatter {
return builder.build();
}
/**
* Creates a JSON object for a {@link Contact} and associated contact type.
*
* <p>If the contact isn't present (i.e. because of minimum registration data set), then always
* show all of its fields as if they were redacted, and always deny RDAP authorization.
*
* @param contact the contact resource object from which the JSON object should be created
* @param roles the roles of this contact
* @param outputDataType whether to generate full or summary data
*/
RdapContactEntity createRdapContactEntity(
Contact contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
RdapContactEntity.Builder contactBuilder = RdapContactEntity.builder();
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts
boolean isAuthorized =
rdapAuthorization.isAuthorizedForRegistrar(contact.getCurrentSponsorRegistrarId());
VcardArray.Builder vcardBuilder = VcardArray.builder();
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);
contactBuilder.setHandle("");
// The VCard format requires a "fn" entry even if it is empty (redacted)
vcardBuilder.add(Vcard.create("fn", "text", ""));
}
contactBuilder.setVcardArray(vcardBuilder.build());
contactBuilder.rolesBuilder().addAll(roles);
// 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 1.5 must have "last update of RDAP database" response. But this is
// only for direct query responses and not for internal objects. I'm not sure why it's in that
// section at all...
contactBuilder.setLastUpdateOfRdapDatabaseEvent(
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(
makeStatusValueList(
isLinked(contact.createVKey(), getRequestTime())
? union(contact.getStatusValues(), StatusValue.LINKED)
: 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);
}
// Adding the VCard members when not redacted.
//
// RDAP Response Profile 2.7.3 - we MUST have FN, ADR, TEL, EMAIL.
//
// Note that 2.7.5 also says the EMAIL must be omitted, so we'll omit it
PostalInfo postalInfo = contact.getInternationalizedPostalInfo();
if (postalInfo == null) {
postalInfo = contact.getLocalizedPostalInfo();
}
if (postalInfo != null) {
if (postalInfo.getName() != null) {
vcardBuilder.add(Vcard.create("fn", "text", postalInfo.getName()));
}
if (postalInfo.getOrg() != null) {
vcardBuilder.add(Vcard.create("org", "text", postalInfo.getOrg()));
}
addVCardAddressEntry(vcardBuilder, postalInfo.getAddress());
}
ContactPhoneNumber voicePhoneNumber = contact.getVoiceNumber();
if (voicePhoneNumber != null) {
vcardBuilder.add(makePhoneEntry(PHONE_TYPE_VOICE, makePhoneString(voicePhoneNumber)));
}
ContactPhoneNumber faxPhoneNumber = contact.getFaxNumber();
if (faxPhoneNumber != null) {
vcardBuilder.add(makePhoneEntry(PHONE_TYPE_FAX, makePhoneString(faxPhoneNumber)));
}
}
/**
* Creates a JSON object for a {@link Registrar}.
*
@@ -740,38 +561,37 @@ public class RdapJsonFormatter {
builder.setVcardArray(vcardBuilder.build());
// Registrar contacts are a bit complicated.
// Registrar POCs are a bit complicated.
//
// Rdap Response Profile 3.2, we SHOULD have at least ADMIN and TECH contacts. It says
// Rdap Response Profile 3.2, we SHOULD have at least ADMIN and TECH POCs. It says
// nothing about ABUSE at all.
//
// Rdap Response Profile 4.3 doesn't mention contacts at all, meaning probably we don't have to
// have any contacts there. But the Registrar itself is Optional in that case, so we will just
// Rdap Response Profile 4.3 doesn't mention POCs at all, meaning probably we don't have to
// have any POCs there. But the Registrar itself is Optional in that case, so we will just
// skip it completely.
//
// Rdap Response Profile 2.4.5 says the Registrar inside a Domain response MUST include the
// ABUSE contact, but doesn't require any other contact.
// ABUSE POC, but doesn't require any other POCs.
//
// Write the minimum, meaning only ABUSE for INTERNAL registrars, nothing for SUMMARY and
// everything for FULL.
if (outputDataType != OutputDataType.SUMMARY) {
ImmutableList<RdapContactEntity> registrarContacts =
registrar.getContactsFromReplica().stream()
.map(RdapJsonFormatter::makeRdapJsonForRegistrarContact)
ImmutableList<RdapRegistrarPocEntity> registrarPocs =
registrar.getPocsFromReplica().stream()
.map(RdapJsonFormatter::makeRdapJsonForRegistrarPoc)
.filter(Optional::isPresent)
.map(Optional::get)
.filter(
contact ->
poc ->
outputDataType == OutputDataType.FULL
|| contact.roles().contains(RdapEntity.Role.ABUSE))
|| poc.roles().contains(RdapEntity.Role.ABUSE))
.collect(toImmutableList());
if (registrarContacts.stream()
.noneMatch(contact -> contact.roles().contains(RdapEntity.Role.ABUSE))) {
if (registrarPocs.stream().noneMatch(poc -> poc.roles().contains(RdapEntity.Role.ABUSE))) {
logger.atWarning().log(
"Registrar '%s' (IANA ID %s) is missing ABUSE contact.",
"Registrar '%s' (IANA ID %s) is missing ABUSE POC.",
registrar.getRegistrarId(), registrar.getIanaIdentifier());
}
builder.entitiesBuilder().addAll(registrarContacts);
builder.entitiesBuilder().addAll(registrarPocs);
}
// Rdap Response Profile 1.5, must have "last update of RDAP database" response. But this is
@@ -789,7 +609,7 @@ public class RdapJsonFormatter {
/**
* Creates a JSON object for a {@link RegistrarPoc}.
*
* <p>Returns empty if this contact shouldn't be visible (doesn't have a role).
* <p>Returns empty if this POC shouldn't be visible (doesn't have a role).
*
* <p>NOTE that registrar locations in the response require different roles and different VCard
* members according to the spec. Currently, this function returns all the rolls and all the
@@ -799,19 +619,18 @@ public class RdapJsonFormatter {
* <p>Specifically:
* <li>Registrar inside a Domain only requires the ABUSE role, and only the TEL and EMAIL members
* (RDAP Response Profile 2.4.5)
* <li>Registrar responses to direct query don't require any contact, but *should* have the TECH
* and ADMIN roles, but require the FN, TEL and EMAIL members
* <li>Registrar inside a Nameserver isn't required at all, and if given doesn't require any
* contacts
* <li>Registrar responses to direct query don't require any POCs, but *should* have the TECH and
* ADMIN roles, but require the FN, TEL and EMAIL members
* <li>Registrar inside a Nameserver isn't required at all, and if given doesn't require any POCs
*
* @param registrarPoc the registrar contact for which the JSON object should be created
* @param registrarPoc the registrar POC for which the JSON object should be created
*/
static Optional<RdapContactEntity> makeRdapJsonForRegistrarContact(RegistrarPoc registrarPoc) {
static Optional<RdapRegistrarPocEntity> makeRdapJsonForRegistrarPoc(RegistrarPoc registrarPoc) {
ImmutableList<RdapEntity.Role> roles = makeRdapRoleList(registrarPoc);
if (roles.isEmpty()) {
return Optional.empty();
}
RdapContactEntity.Builder builder = RdapContactEntity.builder();
RdapRegistrarPocEntity.Builder builder = RdapRegistrarPocEntity.builder();
builder.statusBuilder().addAll(STATUS_LIST_ACTIVE);
builder.rolesBuilder().addAll(roles);
// Create the vCard.
@@ -838,20 +657,10 @@ public class RdapJsonFormatter {
return Optional.of(builder.build());
}
/** Converts a domain registry contact type into a role as defined by RFC 9083. */
private static RdapEntity.Role convertContactTypeToRdapRole(DesignatedContact.Type contactType) {
return switch (contactType) {
case REGISTRANT -> RdapEntity.Role.REGISTRANT;
case TECH -> RdapEntity.Role.TECH;
case BILLING -> RdapEntity.Role.BILLING;
case ADMIN -> RdapEntity.Role.ADMIN;
};
}
/**
* Creates the list of RDAP roles for a registrar contact, using the visibleInWhoisAs* flags.
* Creates the list of RDAP roles for a registrar POC, using the visibleInWhoisAs* flags.
*
* <p>Only contacts with a non-empty role list should be visible.
* <p>Only POCs with a non-empty role list should be visible.
*
* <p>The RDAP response profile only mandates the "abuse" entity:
*
@@ -929,7 +738,7 @@ public class RdapJsonFormatter {
}
/**
* Creates the list of optional events to list in domain, nameserver, or contact replies.
* Creates the list of optional events to list in domain or nameserver replies.
*
* <p>Only has entries for optional events that won't be shown in "SUMMARY" versions of these
* objects. These are either stated as optional in the RDAP Response Profile, or not mentioned at
@@ -1055,15 +864,6 @@ public class RdapJsonFormatter {
return Vcard.create("tel", type, "uri", phoneNumber);
}
/** Creates a phone string in URI format, as per the vCard spec. */
private static String makePhoneString(ContactPhoneNumber phoneNumber) {
String phoneString = String.format("tel:%s", phoneNumber.getPhoneNumber());
if (phoneNumber.getExtension() != null) {
phoneString = phoneString + ";ext=" + phoneNumber.getExtension();
}
return phoneString;
}
/**
* Creates a string array of status values.
*

View File

@@ -133,16 +133,6 @@ public class RdapMetrics {
LABEL_DESCRIPTORS_FOR_RETRIEVAL_COUNTS,
FIBONACCI_FITTER);
@VisibleForTesting
static final EventMetric numberOfContactsRetrieved =
MetricRegistryImpl.getDefault()
.newEventMetric(
"/rdap/num_contacts_retrieved",
"Number of contacts retrieved",
"count",
LABEL_DESCRIPTORS_FOR_RETRIEVAL_COUNTS,
FIBONACCI_FITTER);
@Inject
public RdapMetrics() {}
@@ -191,15 +181,6 @@ public class RdapMetrics {
getLabelStringForPrefixLength(rdapMetricInformation.prefixLength()),
rdapMetricInformation.includeDeleted() ? "YES" : "NO");
}
if (rdapMetricInformation.numContactsRetrieved().isPresent()) {
numberOfContactsRetrieved.record(
rdapMetricInformation.numContactsRetrieved().get(),
rdapMetricInformation.endpointType().toString(),
rdapMetricInformation.searchType().toString(),
rdapMetricInformation.wildcardType().toString(),
getLabelStringForPrefixLength(rdapMetricInformation.prefixLength()),
rdapMetricInformation.includeDeleted() ? "YES" : "NO");
}
}
/**
@@ -221,10 +202,8 @@ public class RdapMetrics {
* than were actually returned in the response; absent if a search was not performed.
* @param numHostsRetrieved Number of hosts retrieved from the database; this might be more than
* were actually returned in the response; absent if a search was not performed.
* @param numContactsRetrieved Number of contacts retrieved from the database; this might be more
* than were actually returned in the response; absent if a search was not performed.
*/
record RdapMetricInformation(
public record RdapMetricInformation(
EndpointType endpointType,
SearchType searchType,
WildcardType wildcardType,
@@ -236,8 +215,7 @@ public class RdapMetrics {
int statusCode,
IncompletenessWarningType incompletenessWarningType,
Optional<Long> numDomainsRetrieved,
Optional<Long> numHostsRetrieved,
Optional<Long> numContactsRetrieved) {
Optional<Long> numHostsRetrieved) {
@AutoBuilder
interface Builder {
@@ -265,8 +243,6 @@ public class RdapMetrics {
Builder setNumHostsRetrieved(long numHostsRetrieved);
Builder setNumContactsRetrieved(long numContactRetrieved);
RdapMetricInformation build();
}

View File

@@ -253,8 +253,8 @@ final class RdapObjectClasses {
/**
* The Entity Object Class defined in 5.1 of RFC 9083.
*
* <p>Entities are used both for Contacts and for Registrars. We will create different subobjects
* for each one for type safety.
* <p>Entities are used both for Registrar POCs and for Registrars. We will create different
* subobjects for each one for type safety.
*
* <p>We're missing the "autnums" and "networks" fields
*/
@@ -309,8 +309,8 @@ final class RdapObjectClasses {
/**
* Registrar version of the Entity Object Class defined in 5.1 of RFC 9083.
*
* <p>Entities are used both for Contacts and for Registrars. We will create different subobjects
* for each one for type safety.
* <p>Entities are used both for Registrar POCs and for Registrars. We will create different
* subobjects for each one for type safety.
*/
@AutoValue
public abstract static class RdapRegistrarEntity extends RdapEntity {
@@ -326,21 +326,21 @@ final class RdapObjectClasses {
}
/**
* Contact version of the Entity Object Class defined in 5.1 of RFC 9083.
* RegistrarPoc version of the Entity Object Class defined in 5.1 of RFC 9083.
*
* <p>Entities are used both for Contacts and for Registrars. We will create different subobjects
* for each one for type safety.
* <p>Entities are used both for Registrar POCs and for Registrars. We will create different
* subobjects for each one for type safety.
*/
@AutoValue
public abstract static class RdapContactEntity extends RdapEntity {
public abstract static class RdapRegistrarPocEntity extends RdapEntity {
static Builder builder() {
return new AutoValue_RdapObjectClasses_RdapContactEntity.Builder();
return new AutoValue_RdapObjectClasses_RdapRegistrarPocEntity.Builder();
}
@AutoValue.Builder
abstract static class Builder extends RdapEntity.Builder<Builder> {
abstract RdapContactEntity build();
abstract RdapRegistrarPocEntity build();
}
}

View File

@@ -120,7 +120,7 @@ abstract class RdapSearchResults {
}
@AutoValue
abstract static class EntitySearchResponse extends BaseSearchResponse {
public abstract static class EntitySearchResponse extends BaseSearchResponse {
@JsonableElement public abstract ImmutableList<RdapEntity> entitySearchResults();

View File

@@ -18,11 +18,12 @@ import static com.google.common.base.Throwables.getRootCause;
import static google.registry.request.Action.Method.POST;
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
import static java.util.stream.Collectors.joining;
import com.google.cloud.storage.BlobId;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Iterables;
import com.google.common.flogger.FluentLogger;
import com.google.common.io.ByteStreams;
@@ -41,7 +42,6 @@ import jakarta.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.stream.Collectors;
/** Copy all registrar detail reports in a given bucket's subdirectory from GCS to Drive. */
@Action(
@@ -98,7 +98,8 @@ public final class CopyDetailReportsAction implements Runnable {
response.setPayload(String.format("Failure, encountered %s", e.getMessage()));
return;
}
ImmutableMap.Builder<String, Throwable> copyErrorsBuilder = new ImmutableMap.Builder<>();
ImmutableMultimap.Builder<String, Throwable> copyErrorsBuilder =
new ImmutableMultimap.Builder<>();
for (String detailReportName : detailReportObjectNames) {
// The standard report format is "invoice_details_yyyy-MM_registrarId_tld.csv
// TODO(larryruili): Determine a safer way of enforcing this.
@@ -145,17 +146,18 @@ public final class CopyDetailReportsAction implements Runnable {
response.setStatus(SC_OK);
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
StringBuilder payload = new StringBuilder().append("Copied detail reports.\n");
ImmutableMap<String, Throwable> copyErrors = copyErrorsBuilder.build();
ImmutableMultimap<String, Throwable> copyErrors = copyErrorsBuilder.build();
if (!copyErrors.isEmpty()) {
payload.append("The following errors were encountered:\n");
payload.append(
copyErrors.entrySet().stream()
.map(
entrySet ->
String.format(
"Registrar: %s\nError: %s\n",
entrySet.getKey(), entrySet.getValue().getMessage()))
.collect(Collectors.joining()));
for (var registrarId : copyErrors.keySet()) {
payload.append(
String.format(
"Registrar: %s\nError: %s\n",
registrarId,
copyErrors.get(registrarId).stream()
.map(Throwable::getMessage)
.collect(joining("\n\t"))));
}
}
response.setPayload(payload.toString());
emailUtils.sendAlertEmail(payload.toString());

View File

@@ -135,7 +135,8 @@ public final class RequestModule {
@Provides
@RequestUrl
static String provideRequestUrl(HttpServletRequest req) {
return req.getRequestURL().toString();
String url = req.getRequestURL().toString();
return url.startsWith("https") ? url : url.replaceFirst("http", "https");
}
@Provides

View File

@@ -30,6 +30,7 @@ import google.registry.request.auth.AuthModule.IapOidc;
import google.registry.request.auth.AuthModule.RegularOidc;
import google.registry.request.auth.AuthSettings.AuthLevel;
import google.registry.util.RegistryEnvironment;
import google.registry.util.StopwatchLogger;
import jakarta.inject.Inject;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Optional;
@@ -77,7 +78,9 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
logger.atWarning().log("Using AuthResult %s for testing.", authResultForTesting);
return authResultForTesting;
}
final StopwatchLogger stopwatch = new StopwatchLogger();
String rawIdToken = tokenExtractor.extract(request);
stopwatch.tick("OidcTokenAuthenticationMechanism tokenExtractor extracted");
if (rawIdToken == null) {
return AuthResult.NOT_AUTHENTICATED;
}
@@ -99,7 +102,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
? "Raw token redacted in prod"
: rawIdToken);
}
stopwatch.tick("OidcTokenAuthenticationMechanism token verified");
if (token == null) {
return AuthResult.NOT_AUTHENTICATED;
}
@@ -111,6 +114,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
}
Optional<User> maybeUser =
tm().transact(() -> tm().loadByKeyIfPresent(VKey.create(User.class, email)));
stopwatch.tick("OidcTokenAuthenticationMechanism maybeUser loaded");
if (maybeUser.isPresent()) {
return AuthResult.createUser(maybeUser.get());
}

View File

@@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.flogger.FluentLogger;
import google.registry.keyring.api.KeyModule.Key;
import google.registry.model.smd.SignedMarkRevocationList;
import google.registry.model.smd.SignedMarkRevocationListDao;
import google.registry.request.Action;
import google.registry.request.Action.GaeService;
import google.registry.request.auth.Auth;
@@ -57,7 +58,7 @@ public final class TmchSmdrlAction implements Runnable {
} catch (GeneralSecurityException | IOException | PGPException e) {
throw new RuntimeException(e);
}
smdrl.save();
smdrl = SignedMarkRevocationListDao.save(smdrl);
logger.atInfo().log(
"Inserted %,d smd revocations into the database, created at %s.",
smdrl.size(), smdrl.getCreationTime());

View File

@@ -14,7 +14,6 @@
package google.registry.tools;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.collect.ImmutableMap;
@@ -52,11 +51,6 @@ class LoadTestCommand extends ConfirmingCommand implements CommandWithConnection
description = "Number of domains to create per second.")
int successfulDomainCreates = 1;
@Parameter(
names = {"--successful_contact_creates"},
description = "Number of contact records to create per second.")
int successfulContactCreates = 1;
@Parameter(
names = {"--host_infos"},
description = "Number of successful host:info commands to send per second.")
@@ -67,11 +61,6 @@ class LoadTestCommand extends ConfirmingCommand implements CommandWithConnection
description = "Number of successful domain:info commands to send per second.")
int domainInfos = 1;
@Parameter(
names = {"--contact_infos"},
description = "Number of successful contact:info commands to send per second.")
int contactInfos = 1;
@Parameter(
names = {"--run_seconds"},
description = "Time to run the load test in seconds.")
@@ -120,10 +109,8 @@ class LoadTestCommand extends ConfirmingCommand implements CommandWithConnection
.put("clientId", clientId)
.put("successfulHostCreates", successfulHostCreates)
.put("successfulDomainCreates", successfulDomainCreates)
.put("successfulContactCreates", successfulContactCreates)
.put("hostInfos", hostInfos)
.put("domainInfos", domainInfos)
.put("contactInfos", contactInfos)
.put("runSeconds", runSeconds)
.build();

View File

@@ -35,7 +35,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.CharStreams;
import com.google.common.net.MediaType;
import com.google.re2j.Pattern;
import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action.GaeService;
import google.registry.request.Action.GkeService;
@@ -56,9 +55,6 @@ import org.json.simple.JSONValue;
*/
public class ServiceConnection {
/** Pattern to heuristically extract title tag contents in HTML responses. */
protected static final Pattern HTML_TITLE_TAG_PATTERN = Pattern.compile("<title>(.*?)</title>");
private final Service service;
private final boolean useCanary;
private final HttpRequestFactory requestFactory;

View File

@@ -63,6 +63,7 @@ public class PasswordResetVerifyAction extends ConsoleApiAction {
// Temporary flag when testing email sending etc
if (!user.getUserRoles().isAdmin()) {
setFailedResponse("", HttpServletResponse.SC_FORBIDDEN);
return;
}
PasswordResetRequest request = tm().transact(() -> loadAndValidateResetRequest(user));
ImmutableMap<String, ?> result =
@@ -76,6 +77,7 @@ public class PasswordResetVerifyAction extends ConsoleApiAction {
// Temporary flag when testing email sending etc
if (!user.getUserRoles().isAdmin()) {
setFailedResponse("", HttpServletResponse.SC_FORBIDDEN);
return;
}
checkArgument(!Strings.isNullOrEmpty(newPassword.orElse(null)), "Password must be provided");
tm().transact(

View File

@@ -53,8 +53,10 @@ import java.util.Optional;
public class RegistrarsAction extends ConsoleApiAction {
private static final int PASSWORD_LENGTH = 16;
private static final int PASSCODE_LENGTH = 5;
private static final ImmutableList<Registrar.Type> allowedRegistrarTypes =
ImmutableList.of(Registrar.Type.REAL, Registrar.Type.OTE);
private static final ImmutableSet<Registrar.Type> TYPES_ALLOWED_FOR_USERS =
ImmutableSet.of(Registrar.Type.REAL, Registrar.Type.OTE);
private static final ImmutableSet<Registrar.Type> TYPES_ALLOWED_FOR_ADMINS =
ImmutableSet.of(Registrar.Type.INTERNAL, Registrar.Type.REAL, Registrar.Type.OTE);
private static final String SQL_TEMPLATE =
"""
SELECT * FROM "Registrar"
@@ -80,6 +82,8 @@ public class RegistrarsAction extends ConsoleApiAction {
@Override
protected void getHandler(User user) {
if (user.getUserRoles().hasGlobalPermission(ConsolePermission.VIEW_REGISTRARS)) {
ImmutableSet<Registrar.Type> allowedRegistrarTypes =
user.getUserRoles().isAdmin() ? TYPES_ALLOWED_FOR_ADMINS : TYPES_ALLOWED_FOR_USERS;
ImmutableList<Registrar> registrars =
Streams.stream(Registrar.loadAll())
.filter(r -> allowedRegistrarTypes.contains(r.getType()))
@@ -94,6 +98,7 @@ public class RegistrarsAction extends ConsoleApiAction {
.map(Map.Entry::getKey)
.collect(toImmutableSet());
@SuppressWarnings("unchecked")
List<Registrar> registrars =
tm().transact(
() ->

View File

@@ -120,7 +120,7 @@ public class SecurityAction extends ConsoleApiAction {
}
}
} catch (InsecureCertificateException e) {
setFailedResponse("Invalid certificate in parameter", SC_BAD_REQUEST);
setFailedResponse(e.getMessage(), SC_BAD_REQUEST);
return;
}

View File

@@ -81,7 +81,7 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
domain.getCurrentSponsorRegistrarId());
Registrar registrar = registrarOptional.get();
Optional<RegistrarPoc> abuseContact =
registrar.getContactsFromReplica().stream()
registrar.getPocsFromReplica().stream()
.filter(RegistrarPoc::getVisibleInDomainWhoisAsAbuse)
.findFirst();
return WhoisResponseResults.create(

View File

@@ -44,7 +44,7 @@ class RegistrarWhoisResponse extends WhoisResponseImpl {
@Override
public WhoisResponseResults getResponse(boolean preferUnicode, String disclaimer) {
Set<RegistrarPoc> contacts = registrar.getContactsFromReplica();
Set<RegistrarPoc> contacts = registrar.getPocsFromReplica();
String plaintext =
new RegistrarEmitter()
.emitField("Registrar", registrar.getRegistrarName())

View File

@@ -24,7 +24,6 @@ import static google.registry.bsa.persistence.BsaTestingUtils.persistDownloadSch
import static google.registry.bsa.persistence.BsaTestingUtils.persistUnblockableDomain;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistDeletedDomain;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.mockito.ArgumentMatchers.any;
@@ -218,7 +217,8 @@ public class BsaValidateActionTest {
test1,1;2
test2,3
""";
String blockPlusContent = """
String blockPlusContent =
"""
domainLabel,orderIDs
test2,4
""";
@@ -239,7 +239,7 @@ public class BsaValidateActionTest {
persistBsaLabel("label");
Domain domain = persistActiveDomain("label.app", fakeClock.nowUtc());
fakeClock.advanceBy(MAX_STALENESS.minus(Duration.standardSeconds(1)));
assertThat(action.isStalenessAllowed(true, domain.createVKey())).isTrue();
assertThat(action.isStalenessAllowed(domain.createVKey())).isTrue();
}
@Test
@@ -247,23 +247,7 @@ public class BsaValidateActionTest {
persistBsaLabel("label");
Domain domain = persistActiveDomain("label.app", fakeClock.nowUtc());
fakeClock.advanceBy(MAX_STALENESS);
assertThat(action.isStalenessAllowed(true, domain.createVKey())).isFalse();
}
@Test
void isStalenessAllowed_deletedDomain_allowed() {
persistBsaLabel("label");
Domain domain = persistDeletedDomain("label.app", fakeClock.nowUtc());
fakeClock.advanceBy(MAX_STALENESS.minus(Duration.standardSeconds(1)));
assertThat(action.isStalenessAllowed(false, domain.createVKey())).isTrue();
}
@Test
void isStalenessAllowed_deletedDomain_notAllowed() {
persistBsaLabel("label");
Domain domain = persistDeletedDomain("label.app", fakeClock.nowUtc());
fakeClock.advanceBy(MAX_STALENESS);
assertThat(action.isStalenessAllowed(false, domain.createVKey())).isFalse();
assertThat(action.isStalenessAllowed(domain.createVKey())).isFalse();
}
@Test
@@ -405,10 +389,11 @@ public class BsaValidateActionTest {
assertThat(message.body())
.isEqualTo(
"""
Most recent download is 2023-11-09t020857.880z.
Most recent download is 2023-11-09t020857.880z.
Error line 1.
Error line 2""");
Error line 1.
Error line 2\
""");
}
@Test

View File

@@ -20,13 +20,24 @@ import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistDeletedDomain;
import static google.registry.testing.DatabaseHelper.persistReservedList;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.LogsSubject.assertAboutLogs;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static google.registry.util.NetworkUtils.pickUnusedPort;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import com.google.cloud.storage.BlobId;
import com.google.cloud.storage.contrib.nio.testing.LocalStorageHelper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.flogger.FluentLogger;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteStreams;
import com.google.common.net.HostAndPort;
import com.google.common.testing.TestLogHandler;
import com.google.gson.Gson;
import google.registry.bsa.api.BsaCredential;
import google.registry.gcs.GcsUtils;
import google.registry.model.tld.Tld;
@@ -35,9 +46,25 @@ import google.registry.model.tld.label.ReservedList;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.request.UrlConnectionService;
import google.registry.server.Route;
import google.registry.server.TestServer;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.util.Map;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -102,13 +129,112 @@ public class UploadBsaUnavailableDomainsActionTest {
BlobId existingFile =
BlobId.of(BUCKET, String.format("unavailable_domains_%s.txt", clock.nowUtc()));
String blockList = new String(gcsUtils.readBytesFrom(existingFile), UTF_8);
assertThat(blockList).isEqualTo("ace.tld\nflagrant.tld\nfoobar.tld\njimmy.tld\ntine.tld");
assertThat(blockList).isEqualTo("ace.tld\nflagrant.tld\nfoobar.tld\njimmy.tld\ntine.tld\n");
assertThat(blockList).doesNotContain("not-blocked.tld");
// This test currently fails in the upload-to-bsa step.
verify(emailSender, times(1))
.sendNotification("BSA daily upload completed with errors", "Please see logs for details.");
}
// TODO(mcilwain): Add test of BSA API upload as well.
@Test
void uploadToBsaTest() throws Exception {
TestLogHandler logHandler = new TestLogHandler();
Logger loggerToIntercept =
Logger.getLogger(UploadBsaUnavailableDomainsAction.class.getCanonicalName());
loggerToIntercept.addHandler(logHandler);
persistActiveDomain("foobar.tld");
persistActiveDomain("ace.tld");
persistDeletedDomain("not-blocked.tld", clock.nowUtc().minusDays(1));
var testServer = startTestServer();
action.apiUrl = testServer.getUrl("/upload").toURI().toString();
try {
action.run();
} finally {
testServer.stop();
}
String dataSent = "ace.tld\nflagrant.tld\nfoobar.tld\njimmy.tld\ntine.tld\n";
String checkSum = Hashing.sha512().hashString(dataSent, UTF_8).toString();
String expectedResponse =
"Received response with code 200 from server: "
+ String.format("Checksum: [%s]\n%s\n", checkSum, dataSent);
assertAboutLogs().that(logHandler).hasLogAtLevelWithMessage(Level.INFO, expectedResponse);
verify(emailSender, times(1)).sendNotification("BSA daily upload completed successfully", "");
}
private TestServer startTestServer() throws Exception {
TestServer testServer =
new TestServer(
HostAndPort.fromParts(InetAddress.getLocalHost().getHostAddress(), pickUnusedPort()),
ImmutableMap.of(),
ImmutableList.of(Route.route("/upload", Servelet.class)));
testServer.start();
newSingleThreadExecutor()
.execute(
() -> {
try {
while (true) {
testServer.process();
}
} catch (InterruptedException e) {
// Expected
}
});
return testServer;
}
@MultipartConfig(
location = "", // Directory for storing uploaded files. Use default when blank
maxFileSize = 10485760L, // 10MB
maxRequestSize = 20971520L, // 20MB
fileSizeThreshold = 1048576 // Save in memory if file size < 1MB
)
public static class Servelet extends HttpServlet {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String checkSum = null;
String content = null;
try {
for (Part part : req.getParts()) {
switch (part.getName()) {
case "zone" -> checkSum = readChecksum(part);
case "file" -> content = readGzipped(part);
}
}
} catch (Exception e) {
logger.atInfo().withCause(e).log("");
}
int status = checkSum == null || content == null ? 400 : 200;
resp.setStatus(status);
resp.setContentType("text/plain");
try (PrintWriter writer = resp.getWriter()) {
writer.printf("Checksum: [%s]\n%s\n", checkSum, content);
}
}
private String readChecksum(Part part) {
try (InputStream is = part.getInputStream()) {
return new Gson()
.fromJson(new String(ByteStreams.toByteArray(is), UTF_8), Map.class)
.getOrDefault("checkSum", "Not found")
.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private String readGzipped(Part part) {
try (InputStream is = part.getInputStream();
GZIPInputStream gis = new GZIPInputStream(is)) {
return new String(ByteStreams.toByteArray(gis), UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@@ -182,6 +182,43 @@ class ContactDeleteFlowTest extends ResourceFlowTestCase<ContactDeleteFlow, Cont
assertAboutEppExceptions().that(thrown).marshalsToXml();
}
@Test
void testSuccess_clientDeleteProhibited_superuser() throws Exception {
persistResource(
persistActiveContact(getUniqueIdFromCommand())
.asBuilder()
.addStatusValue(StatusValue.CLIENT_DELETE_PROHIBITED)
.build());
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("contact_delete_response.xml"));
}
@Test
void testSuccess_serverDeleteProhibited_superuser() throws Exception {
persistResource(
persistActiveContact(getUniqueIdFromCommand())
.asBuilder()
.addStatusValue(StatusValue.SERVER_DELETE_PROHIBITED)
.build());
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("contact_delete_response.xml"));
}
@Test
void testFailure_pendingDelete_superuser() throws Exception {
persistResource(
persistActiveContact(getUniqueIdFromCommand())
.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
assertAboutEppExceptions()
.that(
assertThrows(
ResourceStatusProhibitsOperationException.class,
() -> runFlow(CommitMode.LIVE, UserPrivileges.SUPERUSER)))
.marshalsToXml();
}
@Test
void testFailure_unauthorizedClient() throws Exception {
sessionMetadata.setRegistrarId("NewRegistrar");

View File

@@ -179,6 +179,7 @@ import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.model.smd.SignedMarkRevocationListDao;
import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldState;
import google.registry.model.tld.Tld.TldType;
@@ -2727,8 +2728,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testFail_startDateSunriseRegistration_revokedSignedMark() throws Exception {
SmdrlCsvParser.parse(TmchTestData.loadFile("smd/smdrl.csv").lines().collect(toImmutableList()))
.save();
SignedMarkRevocationListDao.save(
SmdrlCsvParser.parse(
TmchTestData.loadFile("smd/smdrl.csv").lines().collect(toImmutableList())));
createTld("tld", START_DATE_SUNRISE);
clock.setTo(SMD_VALID_TIME);
String revokedSmd =
@@ -2753,9 +2755,9 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
if (labels.isEmpty()) {
return;
}
SmdrlCsvParser.parse(
TmchTestData.loadFile("idn/idn_smdrl.csv").lines().collect(toImmutableList()))
.save();
SignedMarkRevocationListDao.save(
SmdrlCsvParser.parse(
TmchTestData.loadFile("idn/idn_smdrl.csv").lines().collect(toImmutableList())));
createTld("tld", START_DATE_SUNRISE);
clock.setTo(SMD_VALID_TIME);
String revokedSmd =

View File

@@ -917,6 +917,52 @@ class DomainDeleteFlowTest extends ResourceFlowTestCase<DomainDeleteFlow, Domain
assertThat(thrown).hasMessageThat().contains("pendingDelete");
}
@Test
void testSuccess_clientDeleteProhibited_superuser() throws Exception {
eppRequestSource = EppRequestSource.TOOL;
setEppInput(
"domain_delete_superuser_extension.xml",
ImmutableMap.of("REDEMPTION_GRACE_PERIOD_DAYS", "15", "PENDING_DELETE_DAYS", "0"));
setUpSuccessfulTest();
persistResource(
domain.asBuilder().addStatusValue(StatusValue.CLIENT_DELETE_PROHIBITED).build());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("domain_delete_response_pending.xml"));
}
@Test
void testSuccess_serverDeleteProhibited_superuser() throws Exception {
eppRequestSource = EppRequestSource.TOOL;
setEppInput(
"domain_delete_superuser_extension.xml",
ImmutableMap.of("REDEMPTION_GRACE_PERIOD_DAYS", "15", "PENDING_DELETE_DAYS", "0"));
setUpSuccessfulTest();
persistResource(
domain.asBuilder().addStatusValue(StatusValue.SERVER_DELETE_PROHIBITED).build());
clock.advanceOneMilli();
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("domain_delete_response_pending.xml"));
}
@Test
void testFailure_pendingDelete_superuser() throws Exception {
eppRequestSource = EppRequestSource.TOOL;
setEppInput(
"domain_delete_superuser_extension.xml",
ImmutableMap.of("REDEMPTION_GRACE_PERIOD_DAYS", "15", "PENDING_DELETE_DAYS", "0"));
setUpSuccessfulTest();
persistResource(domain.asBuilder().addStatusValue(StatusValue.PENDING_DELETE).build());
ResourceStatusProhibitsOperationException thrown =
assertThrows(
ResourceStatusProhibitsOperationException.class,
() -> runFlow(CommitMode.LIVE, UserPrivileges.SUPERUSER));
assertThat(thrown).hasMessageThat().contains("pendingDelete");
}
@Test
void testSuccess_metadata() throws Exception {
eppRequestSource = EppRequestSource.TOOL;

View File

@@ -346,18 +346,18 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
}
@Test
void testFailure_minimumDatasetPhase2_nonRegistrantContactsStillExist() throws Exception {
void testFailure_minimumDatasetPhase2_whenAddingNewContacts() throws Exception {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
.setStatusMap(
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
.build());
// This EPP adds a new technical contact mak21 that wasn't already present.
setEppInput("domain_update_empty_registrant.xml");
persistReferencedEntities();
persistDomain();
// Fails because after the update the domain would still have some contacts on it even though
// the registrant has been removed.
// Fails because the update adds some new contacts, although the registrant has been removed.
ContactsProhibitedException thrown =
assertThrows(ContactsProhibitedException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -1574,14 +1574,13 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
}
@Test
void testFailure_minimumDatasetPhase2_registrantStillExists() throws Exception {
void testFailure_minimumDatasetPhase2_addingNewRegistrantFails() throws Exception {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
.setStatusMap(
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
.build());
setEppInput("domain_update_remove_admin.xml");
persistReferencedEntities();
persistResource(
DatabaseHelper.newDomain(getUniqueIdFromCommand())
@@ -1590,7 +1589,10 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
.setRegistrant(Optional.empty())
.build());
// This EPP sets the registrant to sh8013, whereas in our test setup it is absent.
setEppInput("domain_update_registrant.xml");
RegistrantProhibitedException thrown =
assertThrows(RegistrantProhibitedException.class, this::runFlow);
assertAboutEppExceptions().that(thrown).marshalsToXml();
@@ -1657,6 +1659,32 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
assertThat(updatedDomain.getContacts()).isEmpty();
}
@Test
void testSuccess_minimumDatasetPhase2_removeOneContact() throws Exception {
persistResource(
new FeatureFlag.Builder()
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
.setStatusMap(
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
.build());
setEppInput("domain_update_remove_admin.xml");
persistReferencedEntities();
persistResource(
DatabaseHelper.newDomain(getUniqueIdFromCommand())
.asBuilder()
.setContacts(
ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
.build());
assertThat(reloadResourceByForeignKey().getRegistrant()).isPresent();
assertThat(reloadResourceByForeignKey().getContacts()).hasSize(2);
runFlowAssertResponse(loadFile("generic_success_response.xml"));
Domain updatedDomain = reloadResourceByForeignKey();
assertThat(updatedDomain.getRegistrant()).isPresent();
assertThat(updatedDomain.getContacts()).hasSize(1);
}
@Test
void testFailure_addPendingDeleteContact() throws Exception {
persistReferencedEntities();

View File

@@ -153,6 +153,43 @@ class HostDeleteFlowTest extends ResourceFlowTestCase<HostDeleteFlow, Host> {
assertSqlDeleteSuccess();
}
@Test
void testSuccess_clientDeleteProhibited_superuser() throws Exception {
persistResource(
persistActiveHost("ns1.example.tld")
.asBuilder()
.addStatusValue(StatusValue.CLIENT_DELETE_PROHIBITED)
.build());
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("host_delete_response.xml"));
}
@Test
void testSuccess_serverDeleteProhibited_superuser() throws Exception {
persistResource(
persistActiveHost("ns1.example.tld")
.asBuilder()
.addStatusValue(StatusValue.SERVER_DELETE_PROHIBITED)
.build());
runFlowAssertResponse(
CommitMode.LIVE, UserPrivileges.SUPERUSER, loadFile("host_delete_response.xml"));
}
@Test
void testFailure_pendingDelete_superuser() throws Exception {
persistResource(
persistActiveHost("ns1.example.tld")
.asBuilder()
.addStatusValue(StatusValue.PENDING_DELETE)
.build());
assertAboutEppExceptions()
.that(
assertThrows(
ResourceStatusProhibitsOperationException.class,
() -> runFlow(CommitMode.LIVE, UserPrivileges.SUPERUSER)))
.marshalsToXml();
}
@Test
void testSuccess_authorizedClientReadFromSuperordinate() throws Exception {
sessionMetadata.setRegistrarId("TheRegistrar");

View File

@@ -341,11 +341,10 @@ class RegistrarTest extends EntityTestCase {
.setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH, RegistrarPoc.Type.ABUSE))
.build());
abuseAdminContact = DatabaseHelper.loadByKey(abuseAdminContact.createVKey());
ImmutableSortedSet<RegistrarPoc> techContacts =
registrar.getContactsOfType(RegistrarPoc.Type.TECH);
ImmutableSortedSet<RegistrarPoc> techContacts = registrar.getPocsOfType(RegistrarPoc.Type.TECH);
assertThat(techContacts).containsExactly(newTechContact, newTechAbuseContact).inOrder();
ImmutableSortedSet<RegistrarPoc> abuseContacts =
registrar.getContactsOfType(RegistrarPoc.Type.ABUSE);
registrar.getPocsOfType(RegistrarPoc.Type.ABUSE);
assertThat(abuseContacts).containsExactly(newTechAbuseContact, abuseAdminContact).inOrder();
}

View File

@@ -16,9 +16,12 @@ package google.registry.model.smd;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.collect.ImmutableMap;
import google.registry.model.EntityTestCase;
import jakarta.persistence.OptimisticLockException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.Test;
public class SignedMarkRevocationListDaoTest extends EntityTestCase {
@@ -32,11 +35,29 @@ public class SignedMarkRevocationListDaoTest extends EntityTestCase {
SignedMarkRevocationList list =
SignedMarkRevocationList.create(
fakeClock.nowUtc(), ImmutableMap.of("mark", fakeClock.nowUtc().minusHours(1)));
SignedMarkRevocationListDao.save(list);
list = SignedMarkRevocationListDao.save(list);
SignedMarkRevocationList fromDb = SignedMarkRevocationListDao.load();
assertAboutImmutableObjects().that(fromDb).isEqualExceptFields(list);
}
@Test
void testSave_retrySuccess() {
SignedMarkRevocationList list =
SignedMarkRevocationList.create(
fakeClock.nowUtc(), ImmutableMap.of("mark", fakeClock.nowUtc().minusHours(1)));
AtomicBoolean isFirstAttempt = new AtomicBoolean(true);
tm().transact(
() -> {
SignedMarkRevocationListDao.save(list);
if (isFirstAttempt.get()) {
isFirstAttempt.set(false);
throw new OptimisticLockException();
}
});
SignedMarkRevocationList fromDb = SignedMarkRevocationListDao.load();
assertAboutImmutableObjects().that(fromDb).isEqualExceptFields(list, "revisionId");
}
@Test
void testSaveAndLoad_emptyList() {
SignedMarkRevocationList list =

View File

@@ -48,7 +48,8 @@ public class SignedMarkRevocationListTest {
for (int i = 0; i < rows; i++) {
revokes.put(Integer.toString(i), clock.nowUtc());
}
SignedMarkRevocationList.create(clock.nowUtc(), revokes.build()).save();
SignedMarkRevocationListDao.save(
SignedMarkRevocationList.create(clock.nowUtc(), revokes.build()));
SignedMarkRevocationList res = SignedMarkRevocationList.get();
assertThat(res.size()).isEqualTo(rows);
return res;

View File

@@ -31,10 +31,12 @@ import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.TestCacheExtension;
import jakarta.persistence.OptimisticLockException;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.IntStream;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
@@ -93,6 +95,27 @@ public class PremiumListDaoTest {
});
}
@Test
void saveNew_retry_success() {
AtomicBoolean isFirstAttempt = new AtomicBoolean(true);
tm().transact(
() -> {
PremiumListDao.save(testList);
if (isFirstAttempt.get()) {
isFirstAttempt.set(false);
throw new OptimisticLockException();
}
});
tm().transact(
() -> {
Optional<PremiumList> persistedListOpt = PremiumListDao.getLatestRevision("testname");
assertThat(persistedListOpt).isPresent();
PremiumList persistedList = persistedListOpt.get();
assertThat(persistedList.getLabelsToPrices()).containsExactlyEntriesIn(TEST_PRICES);
assertThat(persistedList.getCreationTimestamp()).isEqualTo(fakeClock.nowUtc());
});
}
@Test
void update_worksSuccessfully() {
PremiumListDao.save(testList);

View File

@@ -22,6 +22,8 @@ import google.registry.model.tld.label.ReservedList.ReservedListEntry;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
import google.registry.testing.FakeClock;
import jakarta.persistence.OptimisticLockException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -71,11 +73,34 @@ public class ReservedListDaoTest {
});
}
@Test
void save_withRetry_worksSuccessfully() {
AtomicBoolean isFirstAttempt = new AtomicBoolean(true);
tm().transact(
() -> {
ReservedListDao.save(testReservedList);
if (isFirstAttempt.get()) {
isFirstAttempt.set(false);
throw new OptimisticLockException();
}
});
tm().transact(
() -> {
ReservedList persistedList =
tm().query("FROM ReservedList WHERE name = :name", ReservedList.class)
.setParameter("name", "testlist")
.getSingleResult();
assertThat(persistedList.getReservedListEntries())
.containsExactlyEntriesIn(testReservations);
assertThat(persistedList.getCreationTimestamp()).isEqualTo(fakeClock.nowUtc());
});
}
@Test
void delete_worksSuccessfully() {
ReservedListDao.save(testReservedList);
var persisted = ReservedListDao.save(testReservedList);
assertThat(ReservedListDao.checkExists("testlist")).isTrue();
ReservedListDao.delete(testReservedList);
ReservedListDao.delete(persisted);
assertThat(ReservedListDao.checkExists("testlist")).isFalse();
}

View File

@@ -16,15 +16,15 @@ package google.registry.model.tmch;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.google.common.collect.ImmutableMap;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
import google.registry.testing.FakeClock;
import google.registry.testing.TestCacheExtension;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.OptimisticLockException;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -49,27 +49,36 @@ public class ClaimsListDaoTest {
void save_insertsClaimsListSuccessfully() {
ClaimsList claimsList =
ClaimsList.create(fakeClock.nowUtc(), ImmutableMap.of("label1", "key1", "label2", "key2"));
ClaimsListDao.save(claimsList);
claimsList = ClaimsListDao.save(claimsList);
ClaimsList insertedClaimsList = ClaimsListDao.get();
assertClaimsListEquals(claimsList, insertedClaimsList);
assertThat(insertedClaimsList.getCreationTimestamp()).isEqualTo(fakeClock.nowUtc());
}
@Test
void save_fail_duplicateId() {
void save_insertsClaimsListSuccessfully_withRetries() {
ClaimsList claimsList =
ClaimsList.create(fakeClock.nowUtc(), ImmutableMap.of("label1", "key1", "label2", "key2"));
ClaimsListDao.save(claimsList);
AtomicBoolean isFirstAttempt = new AtomicBoolean(true);
tm().transact(
() -> {
ClaimsListDao.save(claimsList);
if (isFirstAttempt.get()) {
isFirstAttempt.set(false);
throw new OptimisticLockException();
}
});
ClaimsList insertedClaimsList = ClaimsListDao.get();
assertClaimsListEquals(claimsList, insertedClaimsList);
// Save ClaimsList with existing revisionId should fail because revisionId is the primary key.
assertThrows(PersistenceException.class, () -> ClaimsListDao.save(insertedClaimsList));
assertThat(insertedClaimsList.getTmdbGenerationTime())
.isEqualTo(claimsList.getTmdbGenerationTime());
assertThat(insertedClaimsList.getLabelsToKeys()).isEqualTo(claimsList.getLabelsToKeys());
assertThat(insertedClaimsList.getCreationTimestamp()).isEqualTo(fakeClock.nowUtc());
}
@Test
void save_claimsListWithNoEntries() {
ClaimsList claimsList = ClaimsList.create(fakeClock.nowUtc(), ImmutableMap.of());
ClaimsListDao.save(claimsList);
claimsList = ClaimsListDao.save(claimsList);
ClaimsList insertedClaimsList = ClaimsListDao.get();
assertClaimsListEquals(claimsList, insertedClaimsList);
assertThat(insertedClaimsList.getLabelsToKeys()).isEmpty();
@@ -86,8 +95,8 @@ public class ClaimsListDaoTest {
ClaimsList.create(fakeClock.nowUtc(), ImmutableMap.of("label1", "key1", "label2", "key2"));
ClaimsList newClaimsList =
ClaimsList.create(fakeClock.nowUtc(), ImmutableMap.of("label3", "key3", "label4", "key4"));
ClaimsListDao.save(oldClaimsList);
ClaimsListDao.save(newClaimsList);
oldClaimsList = ClaimsListDao.save(oldClaimsList);
newClaimsList = ClaimsListDao.save(newClaimsList);
assertClaimsListEquals(newClaimsList, ClaimsListDao.get());
}
@@ -96,11 +105,11 @@ public class ClaimsListDaoTest {
assertThat(ClaimsListDao.CACHE.getIfPresent(ClaimsListDao.class)).isNull();
ClaimsList oldList =
ClaimsList.create(fakeClock.nowUtc(), ImmutableMap.of("label1", "key1", "label2", "key2"));
ClaimsListDao.save(oldList);
oldList = ClaimsListDao.save(oldList);
assertThat(ClaimsListDao.CACHE.getIfPresent(ClaimsListDao.class)).isEqualTo(oldList);
ClaimsList newList =
ClaimsList.create(fakeClock.nowUtc(), ImmutableMap.of("label3", "key3", "label4", "key4"));
ClaimsListDao.save(newList);
newList = ClaimsListDao.save(newList);
assertThat(ClaimsListDao.CACHE.getIfPresent(ClaimsListDao.class)).isEqualTo(newList);
}

View File

@@ -319,27 +319,6 @@ have failed to comply with these terms.",
return putNext("REGISTRAR_HANDLE_", handle, "STATUS_", status);
}
JsonFileBuilder addContact(String handle) {
return putNext("CONTACT_HANDLE_", handle);
}
JsonFileBuilder addFullContact(
String handle,
@Nullable String status,
@Nullable String fullName,
@Nullable String address) {
if (fullName != null) {
putNext("CONTACT_FULLNAME_", fullName);
}
if (address != null) {
putNext("CONTACT_ADDRESS_", address);
}
if (status != null) {
putNext("STATUS_", status);
}
return putNext("CONTACT_HANDLE_", handle);
}
JsonFileBuilder setNextQuery(String nextQuery) {
return put("NEXT_QUERY", nextQuery);
}

View File

@@ -16,7 +16,6 @@ package google.registry.rdap;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.bsa.persistence.BsaTestingUtils.persistBsaLabel;
import static google.registry.model.EppResourceUtils.loadByForeignKey;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
import static google.registry.testing.DatabaseHelper.persistDomainWithDependentResources;
@@ -35,7 +34,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonObject;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.domain.GracePeriod;
import google.registry.model.domain.Period;
@@ -50,65 +48,35 @@ import google.registry.rdap.RdapMetrics.SearchType;
import google.registry.rdap.RdapMetrics.WildcardType;
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
import google.registry.request.Action;
import google.registry.testing.FullFieldsTestEntityHelper;
import java.util.Optional;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link RdapDomainAction}.
*
* <p>TODO(b/26872828): The next time we do any work on RDAP, consider adding the APNIC RDAP
* conformance checker to the unit test suite.
*/
/** Unit tests for {@link RdapDomainAction}. */
class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
RdapDomainActionTest() {
super(RdapDomainAction.class);
}
private Contact registrantLol;
private Host host1;
@BeforeEach
void beforeEach() {
// lol
createTld("lol");
Registrar registrarLol = persistResource(makeRegistrar(
"evilregistrar", "Yes Virginia <script>", Registrar.State.ACTIVE));
Registrar registrarLol =
persistResource(
makeRegistrar("evilregistrar", "Yes Virginia <script>", Registrar.State.ACTIVE));
persistResources(makeRegistrarPocs(registrarLol));
registrantLol =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-ERL",
"Goblin Market",
"lol@cat.lol",
clock.nowUtc().minusYears(1),
registrarLol);
Contact adminContactLol =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-IRL",
"Santa Claus",
"BOFH@cat.lol",
clock.nowUtc().minusYears(2),
registrarLol);
Contact techContactLol =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-TRL", "The Raven", "bog@cat.lol", clock.nowUtc().minusYears(3), registrarLol);
host1 = makeAndPersistHost("ns1.cat.lol", "1.2.3.4", null, clock.nowUtc().minusYears(1));
Host host2 =
makeAndPersistHost(
"ns2.cat.lol", "bad:f00d:cafe:0:0:0:15:beef", clock.nowUtc().minusYears(2));
persistResource(
makeDomain(
"cat.lol",
registrantLol,
adminContactLol,
techContactLol,
host1,
host2,
registrarLol)
makeDomain("cat.lol", null, null, null, host1, host2, registrarLol)
.asBuilder()
.setCreationTimeForTest(clock.nowUtc().minusYears(3))
.setCreationRegistrarId("TheRegistrar")
@@ -120,29 +88,7 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
"ns2.dodo.lol", "bad:f00d:cafe:0:0:0:15:beef", clock.nowUtc().minusYears(2));
Domain domainDeleted =
persistResource(
makeDomain(
"dodo.lol",
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-ERL",
"Goblin Market",
"lol@cat.lol",
clock.nowUtc().minusYears(1),
registrarLol),
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-IRL",
"Santa Claus",
"BOFH@cat.lol",
clock.nowUtc().minusYears(2),
registrarLol),
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-TRL",
"The Raven",
"bog@cat.lol",
clock.nowUtc().minusYears(3),
registrarLol),
host1,
hostDodo2,
registrarLol)
makeDomain("dodo.lol", null, null, null, host1, hostDodo2, registrarLol)
.asBuilder()
.setCreationTimeForTest(clock.nowUtc().minusYears(3))
.setCreationRegistrarId("TheRegistrar")
@@ -153,32 +99,8 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
Registrar registrarIdn =
persistResource(makeRegistrar("idnregistrar", "IDN Registrar", Registrar.State.ACTIVE));
persistResources(makeRegistrarPocs(registrarIdn));
Contact registrantIdn =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-ERL",
"Goblin Market",
"lol@cat.lol",
clock.nowUtc().minusYears(1),
registrarIdn);
Contact adminContactIdn =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-IRL",
"Santa Claus",
"BOFH@cat.lol",
clock.nowUtc().minusYears(2),
registrarIdn);
Contact techContactIdn =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-TRL", "The Raven", "bog@cat.lol", clock.nowUtc().minusYears(3), registrarIdn);
persistResource(
makeDomain(
"cat.みんな",
registrantIdn,
adminContactIdn,
techContactIdn,
host1,
host2,
registrarIdn)
makeDomain("cat.みんな", null, null, null, host1, host2, registrarIdn)
.asBuilder()
.setCreationTimeForTest(clock.nowUtc().minusYears(3))
.setCreationRegistrarId("TheRegistrar")
@@ -186,35 +108,12 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
// 1.tld
createTld("1.tld");
Registrar registrar1Tld = persistResource(
makeRegistrar("1tldregistrar", "Multilevel Registrar", Registrar.State.ACTIVE));
Registrar registrar1Tld =
persistResource(
makeRegistrar("1tldregistrar", "Multilevel Registrar", Registrar.State.ACTIVE));
persistResources(makeRegistrarPocs(registrar1Tld));
Contact registrant1Tld =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-ERL",
"Goblin Market",
"lol@cat.lol",
clock.nowUtc().minusYears(1),
registrar1Tld);
Contact adminContact1Tld =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-IRL",
"Santa Claus",
"BOFH@cat.lol",
clock.nowUtc().minusYears(2),
registrar1Tld);
Contact techContact1Tld =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-TRL", "The Raven", "bog@cat.lol", clock.nowUtc().minusYears(3), registrar1Tld);
persistResource(
makeDomain(
"cat.1.tld",
registrant1Tld,
adminContact1Tld,
techContact1Tld,
host1,
host2,
registrar1Tld)
makeDomain("cat.1.tld", null, null, null, host1, host2, registrar1Tld)
.asBuilder()
.setCreationTimeForTest(clock.nowUtc().minusYears(3))
.setCreationRegistrarId("TheRegistrar")
@@ -236,12 +135,9 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
.isEqualTo(
addDomainBoilerplateNotices(
jsonFileBuilder()
.addDomain("cat.lol", "C-LOL")
.addContact("4-ROID")
.addContact("6-ROID")
.addContact("2-ROID")
.addNameserver("ns1.cat.lol", "8-ROID")
.addNameserver("ns2.cat.lol", "A-ROID")
.addDomain("cat.lol", "6-LOL")
.addNameserver("ns1.cat.lol", "2-ROID")
.addNameserver("ns2.cat.lol", "4-ROID")
.addRegistrar("Yes Virginia <script>")
.load(expectedOutputFile)));
assertThat(response.getStatus()).isEqualTo(200);
@@ -260,15 +156,11 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
}
@Test
void testUnknownDomain_returns400() {
void testUnknownDomain_returns404() {
assertAboutJson()
.that(generateActualJson("missingdomain.com"))
.isEqualTo(
generateExpectedJsonError(
"missingdomain.com is not a valid domain name: Domain name is under tld com which"
+ " doesn't exist",
400));
assertThat(response.getStatus()).isEqualTo(400);
.isEqualTo(generateExpectedJsonError("missingdomain.com not found", 404));
assertThat(response.getStatus()).isEqualTo(404);
}
@Test
@@ -283,67 +175,19 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
assertProperResponseForCatLol("cat.lol", "rdap_domain.json");
}
@Test
void testValidDomain_notLoggedIn_redactsAllContactInfo() {
assertProperResponseForCatLol("cat.lol", "rdap_domain_redacted_contacts_with_remark.json");
}
@Test
void testValidDomain_notLoggedIn_showsNoRegistrant_whenRegistrantDoesntExist() {
persistResource(
loadByForeignKey(Domain.class, "cat.lol", clock.nowUtc())
.get()
.asBuilder()
.setRegistrant(Optional.empty())
.build());
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_registrant_with_remark.json");
}
@Test
void testValidDomain_notLoggedIn_containsNoContactEntities_whenNoContactsExist() {
persistResource(
loadByForeignKey(Domain.class, "cat.lol", clock.nowUtc())
.get()
.asBuilder()
.setRegistrant(Optional.empty())
.setContacts(ImmutableSet.of())
.build());
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_exist_with_remark.json");
}
@Test
void testValidDomain_loggedIn_containsNoContactEntities_whenNoContactsExist() {
login("evilregistrar");
persistResource(
loadByForeignKey(Domain.class, "cat.lol", clock.nowUtc())
.get()
.asBuilder()
.setRegistrant(Optional.empty())
.setContacts(ImmutableSet.of())
.build());
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_exist_with_remark.json");
}
@Test
void testValidDomain_loggedInAsOtherRegistrar_redactsAllContactInfo() {
login("idnregistrar");
assertProperResponseForCatLol("cat.lol", "rdap_domain_redacted_contacts_with_remark.json");
}
@Test
void testUpperCase_ignored() {
assertProperResponseForCatLol("CaT.lOl", "rdap_domain_redacted_contacts_with_remark.json");
assertProperResponseForCatLol("CaT.lOl", "rdap_domain.json");
}
@Test
void testTrailingDot_ignored() {
assertProperResponseForCatLol("cat.lol.", "rdap_domain_redacted_contacts_with_remark.json");
assertProperResponseForCatLol("cat.lol.", "rdap_domain.json");
}
@Test
void testQueryParameter_ignored() {
assertProperResponseForCatLol(
"cat.lol?key=value", "rdap_domain_redacted_contacts_with_remark.json");
assertProperResponseForCatLol("cat.lol?key=value", "rdap_domain.json");
}
@Test
@@ -354,12 +198,9 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
.isEqualTo(
addDomainBoilerplateNotices(
jsonFileBuilder()
.addDomain("cat.みんな", "1D-Q9JYB4C")
.addContact("19-ROID")
.addContact("1B-ROID")
.addContact("17-ROID")
.addNameserver("ns1.cat.lol", "8-ROID")
.addNameserver("ns2.cat.lol", "A-ROID")
.addDomain("cat.みんな", "B-Q9JYB4C")
.addNameserver("ns1.cat.lol", "2-ROID")
.addNameserver("ns2.cat.lol", "4-ROID")
.addRegistrar("IDN Registrar")
.load("rdap_domain_unicode.json")));
assertThat(response.getStatus()).isEqualTo(200);
@@ -373,12 +214,9 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
.isEqualTo(
addDomainBoilerplateNotices(
jsonFileBuilder()
.addDomain("cat.みんな", "1D-Q9JYB4C")
.addContact("19-ROID")
.addContact("1B-ROID")
.addContact("17-ROID")
.addNameserver("ns1.cat.lol", "8-ROID")
.addNameserver("ns2.cat.lol", "A-ROID")
.addDomain("cat.みんな", "B-Q9JYB4C")
.addNameserver("ns1.cat.lol", "2-ROID")
.addNameserver("ns2.cat.lol", "4-ROID")
.addRegistrar("IDN Registrar")
.load("rdap_domain_unicode.json")));
assertThat(response.getStatus()).isEqualTo(200);
@@ -392,12 +230,9 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
.isEqualTo(
addDomainBoilerplateNotices(
jsonFileBuilder()
.addDomain("cat.みんな", "1D-Q9JYB4C")
.addContact("19-ROID")
.addContact("1B-ROID")
.addContact("17-ROID")
.addNameserver("ns1.cat.lol", "8-ROID")
.addNameserver("ns2.cat.lol", "A-ROID")
.addDomain("cat.みんな", "B-Q9JYB4C")
.addNameserver("ns1.cat.lol", "2-ROID")
.addNameserver("ns2.cat.lol", "4-ROID")
.addRegistrar("IDN Registrar")
.load("rdap_domain_unicode.json")));
assertThat(response.getStatus()).isEqualTo(200);
@@ -411,12 +246,9 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
.isEqualTo(
addDomainBoilerplateNotices(
jsonFileBuilder()
.addDomain("cat.1.tld", "25-1_TLD")
.addContact("21-ROID")
.addContact("23-ROID")
.addContact("1F-ROID")
.addNameserver("ns1.cat.lol", "8-ROID")
.addNameserver("ns2.cat.lol", "A-ROID")
.addDomain("cat.1.tld", "D-1_TLD")
.addNameserver("ns1.cat.lol", "2-ROID")
.addNameserver("ns2.cat.lol", "4-ROID")
.addRegistrar("Multilevel Registrar")
.load("rdap_domain.json")));
assertThat(response.getStatus()).isEqualTo(200);
@@ -470,12 +302,9 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
.isEqualTo(
addDomainBoilerplateNotices(
jsonFileBuilder()
.addDomain("dodo.lol", "15-LOL")
.addContact("11-ROID")
.addContact("13-ROID")
.addContact("F-ROID")
.addNameserver("ns1.cat.lol", "8-ROID")
.addNameserver("ns2.dodo.lol", "D-ROID")
.addDomain("dodo.lol", "9-LOL")
.addNameserver("ns1.cat.lol", "2-ROID")
.addNameserver("ns2.dodo.lol", "7-ROID")
.addRegistrar("Yes Virginia <script>")
.load("rdap_domain_deleted.json")));
assertThat(response.getStatus()).isEqualTo(200);
@@ -490,12 +319,9 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
.isEqualTo(
addDomainBoilerplateNotices(
jsonFileBuilder()
.addDomain("dodo.lol", "15-LOL")
.addContact("11-ROID")
.addContact("13-ROID")
.addContact("F-ROID")
.addNameserver("ns1.cat.lol", "8-ROID")
.addNameserver("ns2.dodo.lol", "D-ROID")
.addDomain("dodo.lol", "9-LOL")
.addNameserver("ns1.cat.lol", "2-ROID")
.addNameserver("ns2.dodo.lol", "7-ROID")
.addRegistrar("Yes Virginia <script>")
.load("rdap_domain_deleted.json")));
assertThat(response.getStatus()).isEqualTo(200);
@@ -647,7 +473,7 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
String label, String tld, DateTime creationTime, DateTime expirationTime) {
return persistResource(
persistDomainWithDependentResources(
label, tld, registrantLol, clock.nowUtc(), creationTime, expirationTime)
label, tld, null, clock.nowUtc(), creationTime, expirationTime)
.asBuilder()
.addNameserver(host1.createVKey())
.build());

View File

@@ -34,7 +34,6 @@ import com.google.common.collect.Range;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.domain.Period;
import google.registry.model.host.Host;
@@ -68,9 +67,6 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
private Domain domainCatExample;
private Domain domainIdn;
private Domain domainMultipart;
private Contact contact1;
private Contact contact2;
private Contact contact3;
private Host hostNs1CatLol;
private Host hostNs2CatLol;
private HashMap<String, Host> hostNameToHostMap = new HashMap<>();
@@ -131,15 +127,6 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
persistResource(
makeRegistrar("evilregistrar", "Yes Virginia <script>", Registrar.State.ACTIVE));
persistResources(makeRegistrarPocs(registrar));
contact1 =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-ERL", "Goblin Market", "lol@cat.lol", clock.nowUtc().minusYears(1), registrar);
contact2 =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-IRL", "Santa Claus", "BOFH@cat.lol", clock.nowUtc().minusYears(2), registrar);
contact3 =
FullFieldsTestEntityHelper.makeAndPersistContact(
"5372808-TRL", "The Raven", "bog@cat.lol", clock.nowUtc().minusYears(3), registrar);
hostNs1CatLol =
addHostToMap(
FullFieldsTestEntityHelper.makeAndPersistHost(
@@ -150,14 +137,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
"ns2.cat.lol", "bad:f00d:cafe:0:0:0:15:beef", clock.nowUtc().minusYears(2)));
domainCatLol =
persistResource(
makeDomain(
"cat.lol",
contact1,
contact2,
contact3,
hostNs1CatLol,
hostNs2CatLol,
registrar)
makeDomain("cat.lol", null, null, null, hostNs1CatLol, hostNs2CatLol, registrar)
.asBuilder()
.setSubordinateHosts(ImmutableSet.of("ns1.cat.lol", "ns2.cat.lol"))
.setCreationTimeForTest(clock.nowUtc().minusYears(3))
@@ -172,24 +152,9 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
persistResource(
makeDomain(
"cat2.lol",
FullFieldsTestEntityHelper.makeAndPersistContact(
"6372808-ERL",
"Siegmund",
"siegmund@cat2.lol",
clock.nowUtc().minusYears(1),
registrar),
FullFieldsTestEntityHelper.makeAndPersistContact(
"6372808-IRL",
"Sieglinde",
"sieglinde@cat2.lol",
clock.nowUtc().minusYears(2),
registrar),
FullFieldsTestEntityHelper.makeAndPersistContact(
"6372808-TRL",
"Siegfried",
"siegfried@cat2.lol",
clock.nowUtc().minusYears(3),
registrar),
null,
null,
null,
addHostToMap(
FullFieldsTestEntityHelper.makeAndPersistHost(
"ns1.cat.example", "10.20.30.40", clock.nowUtc().minusYears(1))),
@@ -213,24 +178,9 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
persistResource(
makeDomain(
"cat.example",
FullFieldsTestEntityHelper.makeAndPersistContact(
"7372808-ERL",
"Matthew",
"lol@cat.lol",
clock.nowUtc().minusYears(1),
registrar),
FullFieldsTestEntityHelper.makeAndPersistContact(
"7372808-IRL",
"Mark",
"BOFH@cat.lol",
clock.nowUtc().minusYears(2),
registrar),
FullFieldsTestEntityHelper.makeAndPersistContact(
"7372808-TRL",
"Luke",
"bog@cat.lol",
clock.nowUtc().minusYears(3),
registrar),
null,
null,
null,
hostNs1CatLol,
addHostToMap(
FullFieldsTestEntityHelper.makeAndPersistHost(
@@ -250,24 +200,9 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
persistResource(
makeDomain(
"cat.みんな",
FullFieldsTestEntityHelper.makeAndPersistContact(
"8372808-ERL",
"(◕‿◕)",
"lol@cat.みんな",
clock.nowUtc().minusYears(1),
registrar),
FullFieldsTestEntityHelper.makeAndPersistContact(
"8372808-IRL",
"Santa Claus",
"BOFH@cat.みんな",
clock.nowUtc().minusYears(2),
registrar),
FullFieldsTestEntityHelper.makeAndPersistContact(
"8372808-TRL",
"The Raven",
"bog@cat.みんな",
clock.nowUtc().minusYears(3),
registrar),
null,
null,
null,
addHostToMap(
FullFieldsTestEntityHelper.makeAndPersistHost(
"ns1.cat.みんな", "1.2.3.5", clock.nowUtc().minusYears(1))),
@@ -289,24 +224,9 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
persistResource(
makeDomain(
"cat.1.test",
FullFieldsTestEntityHelper.makeAndPersistContact(
"9372808-ERL",
"(◕‿◕)",
"lol@cat.みんな",
clock.nowUtc().minusYears(1),
registrar),
FullFieldsTestEntityHelper.makeAndPersistContact(
"9372808-IRL",
"Santa Claus",
"BOFH@cat.みんな",
clock.nowUtc().minusYears(2),
registrar),
FullFieldsTestEntityHelper.makeAndPersistContact(
"9372808-TRL",
"The Raven",
"bog@cat.みんな",
clock.nowUtc().minusYears(3),
registrar),
null,
null,
null,
addHostToMap(
FullFieldsTestEntityHelper.makeAndPersistHost(
"ns1.cat.1.test", "1.2.3.5", clock.nowUtc().minusYears(1))),
@@ -371,15 +291,15 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
private JsonObject generateExpectedJsonForTwoDomainsNsReply() {
return jsonFileBuilder()
.addDomain("cat.example", "21-EXAMPLE")
.addDomain("cat.lol", "C-LOL")
.addDomain("cat.example", "F-EXAMPLE")
.addDomain("cat.lol", "6-LOL")
.load("rdap_domains_two.json");
}
private JsonObject generateExpectedJsonForTwoDomainsCatStarReplySql() {
return jsonFileBuilder()
.addDomain("cat.lol", "C-LOL")
.addDomain("cat2.lol", "17-LOL")
.addDomain("cat.lol", "6-LOL")
.addDomain("cat2.lol", "B-LOL")
.load("rdap_domains_two.json");
}
@@ -416,7 +336,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
for (int i = numActiveDomains * numTotalDomainsPerActiveDomain; i >= 1; i--) {
String domainName = String.format("domain%d.lol", i);
Domain.Builder builder =
makeDomain(domainName, contact1, contact2, contact3, null, null, registrar)
makeDomain(domainName, null, null, null, null, null, registrar)
.asBuilder()
.setNameservers(hostKeys)
.setCreationTimeForTest(clock.nowUtc().minusYears(3))
@@ -442,13 +362,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
requestType,
queryString,
jsonFileBuilder()
.addDomain("cat.lol", "C-LOL")
.addDomain("cat.lol", "6-LOL")
.addRegistrar("Yes Virginia <script>")
.addNameserver("ns1.cat.lol", "8-ROID")
.addNameserver("ns2.cat.lol", "A-ROID")
.addContact("4-ROID")
.addContact("6-ROID")
.addContact("2-ROID")
.addNameserver("ns1.cat.lol", "2-ROID")
.addNameserver("ns2.cat.lol", "4-ROID")
.setNextQuery(queryString)
.load(filename));
}
@@ -459,13 +376,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
requestType,
queryString,
jsonFileBuilder()
.addDomain("cat2.lol", "17-LOL")
.addDomain("cat2.lol", "B-LOL")
.addRegistrar("Yes Virginia <script>")
.addNameserver("ns1.cat.example", "13-ROID")
.addNameserver("ns2.dog.lol", "15-ROID")
.addContact("F-ROID")
.addContact("11-ROID")
.addContact("D-ROID")
.addNameserver("ns1.cat.example", "7-ROID")
.addNameserver("ns2.dog.lol", "9-ROID")
.setNextQuery(queryString)
.load(filename));
}
@@ -573,7 +487,6 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
action.registrarParam.isPresent(),
numDomainsRetrieved,
numHostsRetrieved,
Optional.empty(),
incompletenessWarningType);
}
@@ -747,8 +660,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
@Test
void testDomainMatch_found_loggedInAsOtherRegistrar() {
login("otherregistrar");
runSuccessfulTestWithCatLol(
RequestType.NAME, "cat.lol", "rdap_domain_redacted_contacts_with_remark.json");
runSuccessfulTestWithCatLol(RequestType.NAME, "cat.lol", "rdap_domain.json");
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(1L));
}
@@ -776,11 +688,11 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
RequestType.NAME,
"cat.example",
jsonFileBuilder()
.addDomain("cat.example", "21-EXAMPLE")
.addDomain("cat.example", "F-EXAMPLE")
.addRegistrar("St. John Chrysostom")
.addNameserver("ns1.cat.lol", "8-ROID")
.addNameserver("ns2.external.tld", "1F-ROID")
.load("rdap_domain_redacted_contacts_with_remark.json"));
.addNameserver("ns1.cat.lol", "2-ROID")
.addNameserver("ns2.external.tld", "D-ROID")
.load("rdap_domain.json"));
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(1L));
}
@@ -790,11 +702,11 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
RequestType.NAME,
"cat.みんな",
jsonFileBuilder()
.addDomain("cat.みんな", "2D-Q9JYB4C")
.addDomain("cat.みんな", "15-Q9JYB4C")
.addRegistrar("みんな")
.addNameserver("ns1.cat.みんな", "29-ROID")
.addNameserver("ns2.cat.みんな", "2B-ROID")
.load("rdap_domain_unicode_no_contacts_with_remark.json"));
.addNameserver("ns1.cat.みんな", "11-ROID")
.addNameserver("ns2.cat.みんな", "13-ROID")
.load("rdap_domain_unicode_with_unicode_nameservers.json"));
// The unicode gets translated to ASCII before getting parsed into a search pattern.
metricPrefixLength = 15;
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(1L));
@@ -806,11 +718,11 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
RequestType.NAME,
"cat.xn--q9jyb4c",
jsonFileBuilder()
.addDomain("cat.みんな", "2D-Q9JYB4C")
.addDomain("cat.みんな", "15-Q9JYB4C")
.addRegistrar("みんな")
.addNameserver("ns1.cat.みんな", "29-ROID")
.addNameserver("ns2.cat.みんな", "2B-ROID")
.load("rdap_domain_unicode_no_contacts_with_remark.json"));
.addNameserver("ns1.cat.みんな", "11-ROID")
.addNameserver("ns2.cat.みんな", "13-ROID")
.load("rdap_domain_unicode_with_unicode_nameservers.json"));
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(1L));
}
@@ -820,11 +732,11 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
RequestType.NAME,
"cat.1.test",
jsonFileBuilder()
.addDomain("cat.1.test", "39-1_TEST")
.addDomain("cat.1.test", "1B-1_TEST")
.addRegistrar("1.test")
.addNameserver("ns1.cat.1.test", "35-ROID")
.addNameserver("ns2.cat.2.test", "37-ROID")
.load("rdap_domain_redacted_contacts_with_remark.json"));
.addNameserver("ns1.cat.1.test", "17-ROID")
.addNameserver("ns2.cat.2.test", "19-ROID")
.load("rdap_domain.json"));
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(1L));
}
@@ -834,11 +746,11 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
RequestType.NAME,
"ca*.1.test",
jsonFileBuilder()
.addDomain("cat.1.test", "39-1_TEST")
.addDomain("cat.1.test", "1B-1_TEST")
.addRegistrar("1.test")
.addNameserver("ns1.cat.1.test", "35-ROID")
.addNameserver("ns2.cat.2.test", "37-ROID")
.load("rdap_domain_redacted_contacts_with_remark.json"));
.addNameserver("ns1.cat.1.test", "17-ROID")
.addNameserver("ns2.cat.2.test", "19-ROID")
.load("rdap_domain.json"));
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(1L));
}
@@ -910,10 +822,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
.that(generateActualJson(RequestType.NAME, "cat.*"))
.isEqualTo(
jsonFileBuilder()
.addDomain("cat.1.test", "39-1_TEST")
.addDomain("cat.example", "21-EXAMPLE")
.addDomain("cat.lol", "C-LOL")
.addDomain("cat.みんな", "2D-Q9JYB4C")
.addDomain("cat.1.test", "1B-1_TEST")
.addDomain("cat.example", "F-EXAMPLE")
.addDomain("cat.lol", "6-LOL")
.addDomain("cat.みんな", "15-Q9JYB4C")
.load("rdap_domains_four_with_one_unicode.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(4L));
@@ -948,10 +860,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
.that(generateActualJson(RequestType.NAME, "cat*"))
.isEqualTo(
jsonFileBuilder()
.addDomain("cat.1.test", "39-1_TEST")
.addDomain("cat.example", "21-EXAMPLE")
.addDomain("cat.lol", "C-LOL")
.addDomain("cat.みんな", "2D-Q9JYB4C")
.addDomain("cat.1.test", "1B-1_TEST")
.addDomain("cat.example", "F-EXAMPLE")
.addDomain("cat.lol", "6-LOL")
.addDomain("cat.みんな", "15-Q9JYB4C")
.setNextQuery("name=cat*&cursor=Y2F0LnhuLS1xOWp5YjRj")
.load("rdap_domains_four_with_one_unicode_truncated.json"));
assertThat(response.getStatus()).isEqualTo(200);
@@ -1031,7 +943,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
verifyErrorMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(2L), 404);
}
// TODO(b/27378695): reenable or delete this test
// TODO(b/27376E-95): reenable or delete this test
@Disabled
@Test
void testDomainMatchDomainInTestTld_notFound() {
@@ -1073,9 +985,9 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
.that(generateActualJson(RequestType.NAME, "domain*.lol"))
.isEqualTo(
jsonFileBuilder()
.addDomain("domain100.lol", "AC-LOL")
.addDomain("domain150.lol", "7A-LOL")
.addDomain("domain200.lol", "48-LOL")
.addDomain("domain100.lol", "8E-LOL")
.addDomain("domain150.lol", "5C-LOL")
.addDomain("domain200.lol", "2A-LOL")
.addDomain("domainunused.lol", "unused-LOL")
.load("rdap_incomplete_domain_result_set.json"));
assertThat(response.getStatus()).isEqualTo(200);
@@ -1091,10 +1003,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
runSuccessfulTestWithFourDomains(
RequestType.NAME,
"domain*.lol",
"4B-LOL",
"4A-LOL",
"49-LOL",
"48-LOL",
"2D-LOL",
"2C-LOL",
"2B-LOL",
"2A-LOL",
"rdap_nontruncated_domains.json");
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(4L));
}
@@ -1105,10 +1017,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
runSuccessfulTestWithFourDomains(
RequestType.NAME,
"domain*.lol",
"4C-LOL",
"4B-LOL",
"4A-LOL",
"49-LOL",
"2E-LOL",
"2D-LOL",
"2C-LOL",
"2B-LOL",
"name=domain*.lol&cursor=ZG9tYWluNC5sb2w%3D",
"rdap_domains_four_truncated.json");
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(5L), IncompletenessWarningType.TRUNCATED);
@@ -1122,10 +1034,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
.that(generateActualJson(RequestType.NAME, "*.lol"))
.isEqualTo(
jsonFileBuilder()
.addDomain("cat.lol", "C-LOL")
.addDomain("cat2.lol", "17-LOL")
.addDomain("domain1.lol", "4B-LOL")
.addDomain("domain2.lol", "4A-LOL")
.addDomain("cat.lol", "6-LOL")
.addDomain("cat2.lol", "B-LOL")
.addDomain("domain1.lol", "2D-LOL")
.addDomain("domain2.lol", "2C-LOL")
.setNextQuery("name=*.lol&cursor=ZG9tYWluMi5sb2w%3D")
.load("rdap_domains_four_truncated.json"));
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(5L), IncompletenessWarningType.TRUNCATED);
@@ -1139,10 +1051,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
runSuccessfulTestWithFourDomains(
RequestType.NAME,
"domain*.lol",
"50-LOL",
"4F-LOL",
"4E-LOL",
"4D-LOL",
"32-LOL",
"31-LOL",
"30-LOL",
"2F-LOL",
"name=domain*.lol&cursor=ZG9tYWluNC5sb2w%3D",
"rdap_domains_four_truncated.json");
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(5L), IncompletenessWarningType.TRUNCATED);
@@ -1156,10 +1068,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
.that(generateActualJson(RequestType.NAME, "domain*.lol"))
.isEqualTo(
jsonFileBuilder()
.addDomain("domain12.lol", "5A-LOL")
.addDomain("domain18.lol", "54-LOL")
.addDomain("domain24.lol", "4E-LOL")
.addDomain("domain30.lol", "48-LOL")
.addDomain("domain12.lol", "3C-LOL")
.addDomain("domain18.lol", "36-LOL")
.addDomain("domain24.lol", "30-LOL")
.addDomain("domain30.lol", "2A-LOL")
.setNextQuery("name=domain*.lol&cursor=ZG9tYWluMzAubG9s")
.load("rdap_domains_four_truncated.json"));
assertThat(response.getStatus()).isEqualTo(200);
@@ -1358,11 +1270,11 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
RequestType.NS_LDH_NAME,
"ns1.cat.xn--q9jyb4c",
jsonFileBuilder()
.addDomain("cat.みんな", "2D-Q9JYB4C")
.addDomain("cat.みんな", "15-Q9JYB4C")
.addRegistrar("みんな")
.addNameserver("ns1.cat.みんな", "29-ROID")
.addNameserver("ns2.cat.みんな", "2B-ROID")
.load("rdap_domain_unicode_no_contacts_with_remark.json"));
.addNameserver("ns1.cat.みんな", "11-ROID")
.addNameserver("ns2.cat.みんな", "13-ROID")
.load("rdap_domain_unicode_with_unicode_nameservers.json"));
verifyMetrics(SearchType.BY_NAMESERVER_NAME, 1, 1);
}
@@ -1372,11 +1284,11 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
RequestType.NS_LDH_NAME,
"ns1.cat.1.test",
jsonFileBuilder()
.addDomain("cat.1.test", "39-1_TEST")
.addDomain("cat.1.test", "1B-1_TEST")
.addRegistrar("1.test")
.addNameserver("ns1.cat.1.test", "35-ROID")
.addNameserver("ns2.cat.2.test", "37-ROID")
.load("rdap_domain_redacted_contacts_with_remark.json"));
.addNameserver("ns1.cat.1.test", "17-ROID")
.addNameserver("ns2.cat.2.test", "19-ROID")
.load("rdap_domain.json"));
verifyMetrics(SearchType.BY_NAMESERVER_NAME, 1, 1);
}
@@ -1386,11 +1298,11 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
RequestType.NS_LDH_NAME,
"ns*.cat.1.test",
jsonFileBuilder()
.addDomain("cat.1.test", "39-1_TEST")
.addDomain("cat.1.test", "1B-1_TEST")
.addRegistrar("1.test")
.addNameserver("ns1.cat.1.test", "35-ROID")
.addNameserver("ns2.cat.2.test", "37-ROID")
.load("rdap_domain_redacted_contacts_with_remark.json"));
.addNameserver("ns1.cat.1.test", "17-ROID")
.addNameserver("ns2.cat.2.test", "19-ROID")
.load("rdap_domain.json"));
verifyMetrics(SearchType.BY_NAMESERVER_NAME, 1, 1);
}
@@ -1408,7 +1320,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
verifyErrorMetrics(SearchType.BY_NAMESERVER_NAME, Optional.empty(), Optional.of(0L), 404);
}
// TODO(b/27378695): reenable or delete this test
// TODO(b/27376E-95): reenable or delete this test
@Disabled
@Test
void testNameserverMatchDomainsInTestTld_notFound() {
@@ -1531,10 +1443,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
runSuccessfulTestWithFourDomains(
RequestType.NS_LDH_NAME,
"ns1.domain1.lol",
"4B-LOL",
"4A-LOL",
"49-LOL",
"48-LOL",
"2D-LOL",
"2C-LOL",
"2B-LOL",
"2A-LOL",
"rdap_nontruncated_domains.json");
verifyMetrics(SearchType.BY_NAMESERVER_NAME, Optional.of(4L), Optional.of(1L));
}
@@ -1545,10 +1457,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
runSuccessfulTestWithFourDomains(
RequestType.NS_LDH_NAME,
"ns1.domain1.lol",
"4C-LOL",
"4B-LOL",
"4A-LOL",
"49-LOL",
"2E-LOL",
"2D-LOL",
"2C-LOL",
"2B-LOL",
"nsLdhName=ns1.domain1.lol&cursor=ZG9tYWluNC5sb2w%3D",
"rdap_domains_four_truncated.json");
verifyMetrics(
@@ -1564,10 +1476,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
runSuccessfulTestWithFourDomains(
RequestType.NS_LDH_NAME,
"ns1.domain1.lol",
"50-LOL",
"4F-LOL",
"4E-LOL",
"4D-LOL",
"32-LOL",
"31-LOL",
"30-LOL",
"2F-LOL",
"nsLdhName=ns1.domain1.lol&cursor=ZG9tYWluNC5sb2w%3D",
"rdap_domains_four_truncated.json");
verifyMetrics(
@@ -1587,10 +1499,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
.that(generateActualJson(RequestType.NS_LDH_NAME, "ns*.domain1.lol"))
.isEqualTo(
jsonFileBuilder()
.addDomain("domain1.lol", "8F-LOL")
.addDomain("domain2.lol", "8E-LOL")
.addDomain("domain3.lol", "8D-LOL")
.addDomain("domain4.lol", "8C-LOL")
.addDomain("domain1.lol", "71-LOL")
.addDomain("domain2.lol", "70-LOL")
.addDomain("domain3.lol", "6F-LOL")
.addDomain("domain4.lol", "6E-LOL")
.load("rdap_nontruncated_domains.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(SearchType.BY_NAMESERVER_NAME, Optional.of(4L), Optional.of(36L));
@@ -1604,8 +1516,8 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
.that(generateActualJson(RequestType.NS_LDH_NAME, "ns*.domain1.lol"))
.isEqualTo(
jsonFileBuilder()
.addDomain("domain1.lol", "97-LOL")
.addDomain("domain2.lol", "96-LOL")
.addDomain("domain1.lol", "79-LOL")
.addDomain("domain2.lol", "78-LOL")
.load("rdap_incomplete_domains.json"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(
@@ -1671,9 +1583,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
@Test
void testAddressMatchV6Address_foundOne() {
runSuccessfulTestWithCatLol(
RequestType.NS_IP,
"bad:f00d:cafe:0:0:0:15:beef",
"rdap_domain_redacted_contacts_with_remark.json");
RequestType.NS_IP, "bad:f00d:cafe:0:0:0:15:beef", "rdap_domain.json");
verifyMetrics(SearchType.BY_NAMESERVER_ADDRESS, 1, 1);
}
@@ -1683,7 +1593,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
verifyErrorMetrics(SearchType.BY_NAMESERVER_ADDRESS, Optional.empty(), Optional.of(0L), 404);
}
// TODO(b/27378695): reenable or delete this test
// TODO(b/27376E-95): reenable or delete this test
@Disabled
@Test
void testAddressMatchDomainsInTestTld_notFound() {
@@ -1740,13 +1650,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
.isEqualTo(
wrapInSearchReply(
jsonFileBuilder()
.addDomain("cat.lol", "C-LOL")
.addDomain("cat.lol", "6-LOL")
.addRegistrar("Yes Virginia <script>")
.addNameserver("ns1.cat.lol", "8-ROID")
.addNameserver("ns2.cat.lol", "A-ROID")
.addContact("4-ROID")
.addContact("6-ROID")
.addContact("2-ROID")
.addNameserver("ns1.cat.lol", "2-ROID")
.addNameserver("ns2.cat.lol", "4-ROID")
.load("rdap_domain.json")));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(SearchType.BY_NAMESERVER_ADDRESS, 1, 1);
@@ -1773,10 +1680,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
runSuccessfulTestWithFourDomains(
RequestType.NS_IP,
"5.5.5.1",
"4B-LOL",
"4A-LOL",
"49-LOL",
"48-LOL",
"2D-LOL",
"2C-LOL",
"2B-LOL",
"2A-LOL",
"rdap_nontruncated_domains.json");
verifyMetrics(SearchType.BY_NAMESERVER_ADDRESS, 4, 1);
}
@@ -1787,10 +1694,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
runSuccessfulTestWithFourDomains(
RequestType.NS_IP,
"5.5.5.1",
"4C-LOL",
"4B-LOL",
"4A-LOL",
"49-LOL",
"2E-LOL",
"2D-LOL",
"2C-LOL",
"2B-LOL",
"nsIp=5.5.5.1&cursor=ZG9tYWluNC5sb2w%3D",
"rdap_domains_four_truncated.json");
verifyMetrics(
@@ -1806,10 +1713,10 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
runSuccessfulTestWithFourDomains(
RequestType.NS_IP,
"5.5.5.1",
"50-LOL",
"4F-LOL",
"4E-LOL",
"4D-LOL",
"32-LOL",
"31-LOL",
"30-LOL",
"2F-LOL",
"nsIp=5.5.5.1&cursor=ZG9tYWluNC5sb2w%3D",
"rdap_domains_four_truncated.json");
verifyMetrics(

View File

@@ -18,7 +18,6 @@ import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DatabaseHelper.persistResources;
import static google.registry.testing.FullFieldsTestEntityHelper.makeAndPersistDeletedContact;
import static google.registry.testing.FullFieldsTestEntityHelper.makeDomain;
import static google.registry.testing.FullFieldsTestEntityHelper.makeHost;
import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrar;
@@ -26,8 +25,6 @@ import static google.registry.testing.FullFieldsTestEntityHelper.makeRegistrarPo
import static google.registry.testing.GsonSubject.assertAboutJson;
import static org.mockito.Mockito.verify;
import com.google.common.collect.ImmutableList;
import google.registry.model.contact.Contact;
import google.registry.model.host.Host;
import google.registry.model.registrar.Registrar;
import google.registry.rdap.RdapMetrics.EndpointType;
@@ -35,7 +32,6 @@ import google.registry.rdap.RdapMetrics.SearchType;
import google.registry.rdap.RdapMetrics.WildcardType;
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
import google.registry.request.Action;
import google.registry.testing.FullFieldsTestEntityHelper;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -43,19 +39,11 @@ import org.junit.jupiter.api.Test;
/** Unit tests for {@link RdapEntityAction}. */
class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
private static final String CONTACT_NAME = "(◕‿◕)";
private static final String CONTACT_ADDRESS = "\"1 Smiley Row\", \"Suite みんな\"";
RdapEntityActionTest() {
super(RdapEntityAction.class);
}
private Registrar registrarLol;
private Contact registrant;
private Contact adminContact;
private Contact techContact;
private Contact disconnectedContact;
private Contact deletedContact;
@BeforeEach
void beforeEach() {
@@ -64,34 +52,9 @@ class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
registrarLol = persistResource(makeRegistrar(
"evilregistrar", "Yes Virginia <script>", Registrar.State.ACTIVE, 101L));
persistResources(makeRegistrarPocs(registrarLol));
registrant =
FullFieldsTestEntityHelper.makeAndPersistContact(
"8372808-REG",
CONTACT_NAME,
"lol@cat.みんな",
ImmutableList.of("1 Smiley Row", "Suite みんな"),
clock.nowUtc(),
registrarLol);
adminContact =
FullFieldsTestEntityHelper.makeAndPersistContact(
"8372808-ADM",
CONTACT_NAME,
"lol@cat.みんな",
ImmutableList.of("1 Smiley Row", "Suite みんな"),
clock.nowUtc(),
registrarLol);
techContact =
FullFieldsTestEntityHelper.makeAndPersistContact(
"8372808-TEC",
CONTACT_NAME,
"lol@cat.みんな",
ImmutableList.of("1 Smiley Row", "Suite みんな"),
clock.nowUtc(),
registrarLol);
Host host1 = persistResource(makeHost("ns1.cat.lol", "1.2.3.4"));
Host host2 = persistResource(makeHost("ns2.cat.lol", "bad:f00d:cafe:0:0:0:15:beef"));
persistResource(
makeDomain("cat.lol", registrant, adminContact, techContact, host1, host2, registrarLol));
persistResource(makeDomain("cat.lol", null, null, null, host1, host2, registrarLol));
// xn--q9jyb4c
createTld("xn--q9jyb4c");
Registrar registrarIdn = persistResource(
@@ -106,21 +69,6 @@ class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
Registrar registrarDeleted = persistResource(
makeRegistrar("deletedregistrar", "Yes Virginia <script>", Registrar.State.PENDING, 104L));
persistResources(makeRegistrarPocs(registrarDeleted));
// other contacts
disconnectedContact =
FullFieldsTestEntityHelper.makeAndPersistContact(
"8372808-DIS",
CONTACT_NAME,
"lol@cat.みんな",
ImmutableList.of("1 Smiley Row", "Suite みんな"),
clock.nowUtc(),
registrarLol);
deletedContact =
makeAndPersistDeletedContact(
"8372808-DEL",
clock.nowUtc().minusYears(1),
registrarLol,
clock.nowUtc().minusMonths(6));
}
@Test
@@ -148,156 +96,6 @@ class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
assertThat(response.getStatus()).isEqualTo(404);
}
@Test
void testValidRegistrantContact_works() {
login("evilregistrar");
assertAboutJson()
.that(generateActualJson(registrant.getRepoId()))
.isEqualTo(
addPermanentBoilerplateNotices(
jsonFileBuilder()
.addFullContact(registrant.getRepoId(), null, CONTACT_NAME, CONTACT_ADDRESS)
.load("rdap_associated_contact.json")));
}
@Test
void testValidRegistrantContact_found_asAdministrator() {
loginAsAdmin();
assertAboutJson()
.that(generateActualJson(registrant.getRepoId()))
.isEqualTo(
addPermanentBoilerplateNotices(
jsonFileBuilder()
.addFullContact(registrant.getRepoId(), null, CONTACT_NAME, CONTACT_ADDRESS)
.load("rdap_associated_contact.json")));
}
@Test
void testValidRegistrantContact_found_notLoggedIn() {
assertAboutJson()
.that(generateActualJson(registrant.getRepoId()))
.isEqualTo(
addPermanentBoilerplateNotices(
jsonFileBuilder()
.addFullContact(registrant.getRepoId(), "active", CONTACT_NAME, CONTACT_ADDRESS)
.load("rdap_associated_contact_no_personal_data.json")));
}
@Test
void testValidRegistrantContact_found_loggedInAsOtherRegistrar() {
login("otherregistrar");
assertAboutJson()
.that(generateActualJson(registrant.getRepoId()))
.isEqualTo(
addPermanentBoilerplateNotices(
jsonFileBuilder()
.addFullContact(registrant.getRepoId(), "active", CONTACT_NAME, CONTACT_ADDRESS)
.load("rdap_associated_contact_no_personal_data.json")));
}
@Test
void testValidAdminContact_works() {
login("evilregistrar");
assertAboutJson()
.that(generateActualJson(adminContact.getRepoId()))
.isEqualTo(
addPermanentBoilerplateNotices(
jsonFileBuilder()
.addFullContact(adminContact.getRepoId(), null, CONTACT_NAME, CONTACT_ADDRESS)
.load("rdap_associated_contact.json")));
}
@Test
void testValidTechContact_works() {
login("evilregistrar");
assertAboutJson()
.that(generateActualJson(techContact.getRepoId()))
.isEqualTo(
addPermanentBoilerplateNotices(
jsonFileBuilder()
.addFullContact(techContact.getRepoId(), null, CONTACT_NAME, CONTACT_ADDRESS)
.load("rdap_associated_contact.json")));
}
@Test
void testValidDisconnectedContact_works() {
login("evilregistrar");
assertAboutJson()
.that(generateActualJson(disconnectedContact.getRepoId()))
.isEqualTo(
addPermanentBoilerplateNotices(
jsonFileBuilder()
.addFullContact(
disconnectedContact.getRepoId(), "active", CONTACT_NAME, CONTACT_ADDRESS)
.load("rdap_contact.json")));
}
@Test
void testDeletedContact_notFound() {
String repoId = deletedContact.getRepoId();
assertAboutJson()
.that(generateActualJson(repoId))
.isEqualTo(generateExpectedJsonError(repoId + " not found", 404));
assertThat(response.getStatus()).isEqualTo(404);
}
@Test
void testDeletedContact_notFound_includeDeletedSetFalse() {
action.includeDeletedParam = Optional.of(false);
String repoId = deletedContact.getRepoId();
assertAboutJson()
.that(generateActualJson(repoId))
.isEqualTo(generateExpectedJsonError(repoId + " not found", 404));
assertThat(response.getStatus()).isEqualTo(404);
}
@Test
void testDeletedContact_notFound_notLoggedIn() {
action.includeDeletedParam = Optional.of(true);
String repoId = deletedContact.getRepoId();
assertAboutJson()
.that(generateActualJson(repoId))
.isEqualTo(generateExpectedJsonError(repoId + " not found", 404));
assertThat(response.getStatus()).isEqualTo(404);
}
@Test
void testDeletedContact_notFound_loggedInAsDifferentRegistrar() {
login("idnregistrar");
action.includeDeletedParam = Optional.of(true);
String repoId = deletedContact.getRepoId();
assertAboutJson()
.that(generateActualJson(repoId))
.isEqualTo(generateExpectedJsonError(repoId + " not found", 404));
assertThat(response.getStatus()).isEqualTo(404);
}
@Test
void testDeletedContact_found_loggedInAsCorrectRegistrar() {
login("evilregistrar");
action.includeDeletedParam = Optional.of(true);
assertAboutJson()
.that(generateActualJson(deletedContact.getRepoId()))
.isEqualTo(
addPermanentBoilerplateNotices(
jsonFileBuilder()
.addContact(deletedContact.getRepoId())
.load("rdap_contact_deleted.json")));
}
@Test
void testDeletedContact_found_loggedInAsAdmin() {
loginAsAdmin();
action.includeDeletedParam = Optional.of(true);
assertAboutJson()
.that(generateActualJson(deletedContact.getRepoId()))
.isEqualTo(
addPermanentBoilerplateNotices(
jsonFileBuilder()
.addContact(deletedContact.getRepoId())
.load("rdap_contact_deleted.json")));
}
@Test
void testRegistrar_found() {
assertAboutJson()
@@ -412,18 +210,18 @@ class RdapEntityActionTest extends RdapActionBaseTestCase<RdapEntityAction> {
void testQueryParameter_ignored() {
login("evilregistrar");
assertAboutJson()
.that(generateActualJson(techContact.getRepoId() + "?key=value"))
.that(generateActualJson("101?key=value"))
.isEqualTo(
addPermanentBoilerplateNotices(
jsonFileBuilder()
.addFullContact(techContact.getRepoId(), null, CONTACT_NAME, CONTACT_ADDRESS)
.load("rdap_associated_contact.json")));
.addFullRegistrar("101", "Yes Virginia <script>", "active", null)
.load("rdap_registrar.json")));
assertThat(response.getStatus()).isEqualTo(200);
}
@Test
void testMetrics() {
generateActualJson(registrant.getRepoId());
generateActualJson("101");
verify(rdapMetrics)
.updateMetrics(
RdapMetrics.RdapMetricInformation.builder()

View File

@@ -33,7 +33,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import google.registry.model.contact.Contact;
import google.registry.model.domain.Domain;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.host.Host;
@@ -46,12 +45,9 @@ import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.rdap.RdapObjectClasses.BoilerplateType;
import google.registry.rdap.RdapObjectClasses.RdapEntity;
import google.registry.rdap.RdapObjectClasses.ReplyPayloadBase;
import google.registry.rdap.RdapObjectClasses.TopLevelReplyObject;
import google.registry.testing.FakeClock;
import google.registry.testing.FullFieldsTestEntityHelper;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -77,10 +73,6 @@ class RdapJsonFormatterTest {
private Host hostNoAddresses;
private Host hostNotLinked;
private Host hostSuperordinatePendingTransfer;
@Nullable private Contact contactRegistrant;
private Contact contactAdmin;
private Contact contactTech;
private Contact contactNotLinked;
@BeforeEach
void beforeEach() {
@@ -95,35 +87,8 @@ class RdapJsonFormatterTest {
clock.setTo(DateTime.parse("2000-01-01T00:00:00Z"));
registrar = persistResource(registrar);
persistResources(makeMoreRegistrarContacts(registrar));
persistResources(makeMoreRegistrarPocs(registrar));
contactRegistrant =
FullFieldsTestEntityHelper.makeAndPersistContact(
"8372808-ERL", "(◕‿◕)", "lol@cat.みんな", null, clock.nowUtc().minusYears(1), registrar);
contactAdmin =
FullFieldsTestEntityHelper.makeAndPersistContact(
"8372808-IRL",
"Santa Claus",
null,
ImmutableList.of("Santa Claus Tower", "41st floor", "Suite みんな"),
clock.nowUtc().minusYears(2),
registrar);
contactTech =
FullFieldsTestEntityHelper.makeAndPersistContact(
"8372808-TRL",
"The Raven",
"bog@cat.みんな",
ImmutableList.of("Chamber Door", "upper level"),
clock.nowUtc().minusYears(3),
registrar);
contactNotLinked =
FullFieldsTestEntityHelper.makeAndPersistContact(
"8372808-QRL",
"The Wizard",
"dog@cat.みんな",
ImmutableList.of("Somewhere", "Over the Rainbow"),
clock.nowUtc().minusYears(4),
registrar);
hostIpv4 =
makeAndPersistHost(
"ns1.cat.みんな", "1.2.3.4", null, clock.nowUtc().minusYears(1), "unicoderegistrar");
@@ -154,14 +119,7 @@ class RdapJsonFormatterTest {
.asBuilder()
.setSuperordinateDomain(
persistResource(
makeDomain(
"dog.みんな",
contactRegistrant,
contactAdmin,
contactTech,
null,
null,
registrar)
makeDomain("dog.みんな", null, null, null, null, null, registrar)
.asBuilder()
.addStatusValue(StatusValue.PENDING_TRANSFER)
.setTransferData(
@@ -180,43 +138,21 @@ class RdapJsonFormatterTest {
.build());
domainFull =
persistResource(
makeDomain(
"cat.みんな",
contactRegistrant,
contactAdmin,
contactTech,
hostIpv4,
hostIpv6,
registrar)
makeDomain("cat.みんな", null, null, null, hostIpv4, hostIpv6, registrar)
.asBuilder()
.setCreationTimeForTest(clock.nowUtc().minusMonths(4))
.setLastEppUpdateTime(clock.nowUtc().minusMonths(3))
.build());
domainNoNameserversNoTransfers =
persistResource(
makeDomain(
"fish.みんな",
contactRegistrant,
contactRegistrant,
contactRegistrant,
null,
null,
registrar)
makeDomain("fish.みんな", null, null, null, null, null, registrar)
.asBuilder()
.setCreationTimeForTest(clock.nowUtc())
.setLastEppUpdateTime(null)
.build());
// Create an unused domain that references hostBoth and hostNoAddresses so that
// they will have "associated" (ie, StatusValue.LINKED) status.
persistResource(
makeDomain(
"dog.みんな",
contactRegistrant,
contactAdmin,
contactTech,
hostBoth,
hostNoAddresses,
registrar));
persistResource(makeDomain("dog.みんな", null, null, null, hostBoth, hostNoAddresses, registrar));
// history entries
// We create 3 "transfer approved" entries, to make sure we only save the last one
@@ -252,7 +188,7 @@ class RdapJsonFormatterTest {
clock.nowUtc().minusMonths(3)));
}
static ImmutableList<RegistrarPoc> makeMoreRegistrarContacts(Registrar registrar) {
static ImmutableList<RegistrarPoc> makeMoreRegistrarPocs(Registrar registrar) {
return ImmutableList.of(
new RegistrarPoc.Builder()
.setRegistrar(registrar)
@@ -365,88 +301,6 @@ class RdapJsonFormatterTest {
.isEqualTo(loadJson("rdapjson_host_pending_transfer.json"));
}
@Test
void testRegistrant() {
assertAboutJson()
.that(
rdapJsonFormatter
.createRdapContactEntity(
contactRegistrant,
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
OutputDataType.FULL)
.toJson())
.isEqualTo(loadJson("rdapjson_registrant.json"));
}
@Test
void testRegistrant_summary() {
assertAboutJson()
.that(
rdapJsonFormatter
.createRdapContactEntity(
contactRegistrant,
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
OutputDataType.SUMMARY)
.toJson())
.isEqualTo(loadJson("rdapjson_registrant_summary.json"));
}
@Test
void testRegistrant_loggedOut() {
rdapJsonFormatter.rdapAuthorization = RdapAuthorization.PUBLIC_AUTHORIZATION;
assertAboutJson()
.that(
rdapJsonFormatter
.createRdapContactEntity(
contactRegistrant,
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
OutputDataType.FULL)
.toJson())
.isEqualTo(loadJson("rdapjson_registrant_logged_out.json"));
}
@Test
void testAdmin() {
assertAboutJson()
.that(
rdapJsonFormatter
.createRdapContactEntity(
contactAdmin, ImmutableSet.of(RdapEntity.Role.ADMIN), OutputDataType.FULL)
.toJson())
.isEqualTo(loadJson("rdapjson_admincontact.json"));
}
@Test
void testTech() {
assertAboutJson()
.that(
rdapJsonFormatter
.createRdapContactEntity(
contactTech, ImmutableSet.of(RdapEntity.Role.TECH), OutputDataType.FULL)
.toJson())
.isEqualTo(loadJson("rdapjson_techcontact.json"));
}
@Test
void testRolelessContact() {
assertAboutJson()
.that(
rdapJsonFormatter
.createRdapContactEntity(contactTech, ImmutableSet.of(), OutputDataType.FULL)
.toJson())
.isEqualTo(loadJson("rdapjson_rolelesscontact.json"));
}
@Test
void testUnlinkedContact() {
assertAboutJson()
.that(
rdapJsonFormatter
.createRdapContactEntity(contactNotLinked, ImmutableSet.of(), OutputDataType.FULL)
.toJson())
.isEqualTo(loadJson("rdapjson_unlinkedcontact.json"));
}
@Test
void testDomain_full() {
assertAboutJson()
@@ -481,7 +335,7 @@ class RdapJsonFormatterTest {
}
@Test
void testDomain_noNameserversNoTransfersMultipleRoleContact() {
void testDomain_noNameserversNoTransfers() {
assertAboutJson()
.that(
rdapJsonFormatter

View File

@@ -37,7 +37,6 @@ class RdapMetricsTest {
RdapMetrics.responses.reset();
RdapMetrics.numberOfDomainsRetrieved.reset();
RdapMetrics.numberOfHostsRetrieved.reset();
RdapMetrics.numberOfContactsRetrieved.reset();
}
private RdapMetrics.RdapMetricInformation.Builder getBuilder() {
@@ -188,7 +187,6 @@ class RdapMetricsTest {
.and()
.hasNoOtherValues();
assertThat(RdapMetrics.numberOfHostsRetrieved).hasNoOtherValues();
assertThat(RdapMetrics.numberOfContactsRetrieved).hasNoOtherValues();
}
/**
@@ -225,7 +223,6 @@ class RdapMetricsTest {
ImmutableSet.of(10), "DOMAINS", "BY_NAMESERVER_NAME", "PREFIX_AND_SUFFIX", "2", "YES")
.and()
.hasNoOtherValues();
assertThat(RdapMetrics.numberOfContactsRetrieved).hasNoOtherValues();
}
/** Tests what would happen in a nameserver search for "*.cat.lol", which found no matches. */
@@ -254,10 +251,8 @@ class RdapMetricsTest {
ImmutableSet.of(0), "NAMESERVERS", "BY_NAMESERVER_NAME", "SUFFIX", "0", "NO")
.and()
.hasNoOtherValues();
assertThat(RdapMetrics.numberOfContactsRetrieved).hasNoOtherValues();
}
/** Tests what would happen in an entity search for "Mike*" which found 50 contacts. */
@Test
void testEntitySearchByNameWithWildcard() {
rdapMetrics.updateMetrics(
@@ -266,7 +261,6 @@ class RdapMetricsTest {
.setSearchType(SearchType.BY_FULL_NAME)
.setWildcardType(WildcardType.PREFIX)
.setPrefixLength(4)
.setNumContactsRetrieved(50)
.build());
assertThat(RdapMetrics.requests)
.hasValueForLabels(1, "ENTITIES", "NO", "NO", "PUBLIC", "GET")
@@ -279,10 +273,5 @@ class RdapMetricsTest {
.hasNoOtherValues();
assertThat(RdapMetrics.numberOfDomainsRetrieved).hasNoOtherValues();
assertThat(RdapMetrics.numberOfHostsRetrieved).hasNoOtherValues();
assertThat(RdapMetrics.numberOfContactsRetrieved)
.hasDataSetForLabels(
ImmutableSet.of(50), "ENTITIES", "BY_FULL_NAME", "PREFIX", "4", "NO")
.and()
.hasNoOtherValues();
}
}

View File

@@ -131,20 +131,7 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
// create a domain so that we can use it as a test nameserver search string suffix
domainCatLol =
persistResource(
makeDomain(
"cat.lol",
persistResource(
FullFieldsTestEntityHelper.makeContact(
"5372808-ERL", "Goblin Market", "lol@cat.lol", registrar)),
persistResource(
FullFieldsTestEntityHelper.makeContact(
"5372808-IRL", "Santa Claus", "BOFH@cat.lol", registrar)),
persistResource(
FullFieldsTestEntityHelper.makeContact(
"5372808-TRL", "The Raven", "bog@cat.lol", registrar)),
hostNs1CatLol,
hostNs2CatLol,
registrar)
makeDomain("cat.lol", null, null, null, hostNs1CatLol, hostNs2CatLol, registrar)
.asBuilder()
.setSubordinateHosts(ImmutableSet.of("ns1.cat.lol", "ns2.cat.lol"))
.build());
@@ -203,7 +190,6 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
action.registrarParam.isPresent(),
Optional.empty(),
numHostsRetrieved,
Optional.empty(),
incompletenessWarningType);
}
@@ -802,7 +788,7 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
.that(generateActualJsonWithIp("5.5.5.1"))
.isEqualTo(
loadJsonFile(
"rdap_truncated_hosts.json", "QUERY", "ip=5.5.5.1&cursor=MTctUk9JRA%3D%3D"));
"rdap_truncated_hosts.json", "QUERY", "ip=5.5.5.1&cursor=MTQtUk9JRA%3D%3D"));
assertThat(response.getStatus()).isEqualTo(200);
verifyMetrics(5, IncompletenessWarningType.TRUNCATED);
}
@@ -814,7 +800,7 @@ class RdapNameserverSearchActionTest extends RdapSearchActionTestCase<RdapNamese
.that(generateActualJsonWithIp("5.5.5.1"))
.isEqualTo(
loadJsonFile(
"rdap_truncated_hosts.json", "QUERY", "ip=5.5.5.1&cursor=MTctUk9JRA%3D%3D"));
"rdap_truncated_hosts.json", "QUERY", "ip=5.5.5.1&cursor=MTQtUk9JRA%3D%3D"));
assertThat(response.getStatus()).isEqualTo(200);
// When searching by address and not including deleted, we don't need to search for extra
// matches.

View File

@@ -78,7 +78,6 @@ public abstract class RdapSearchActionTestCase<A extends RdapSearchActionBase>
boolean registrarSpecified,
Optional<Long> numDomainsRetrieved,
Optional<Long> numHostsRetrieved,
Optional<Long> numContactsRetrieved,
IncompletenessWarningType incompletenessWarningType) {
RdapMetrics.RdapMetricInformation.Builder builder =
RdapMetrics.RdapMetricInformation.builder()
@@ -94,7 +93,6 @@ public abstract class RdapSearchActionTestCase<A extends RdapSearchActionBase>
.setIncompletenessWarningType(incompletenessWarningType);
numDomainsRetrieved.ifPresent(builder::setNumDomainsRetrieved);
numHostsRetrieved.ifPresent(builder::setNumHostsRetrieved);
numContactsRetrieved.ifPresent(builder::setNumContactsRetrieved);
verify(rdapMetrics).updateMetrics(builder.build());
}
}

View File

@@ -39,12 +39,6 @@ class RdapTestHelper {
return GSON.fromJson(Joiner.on("\n").join(lines), JsonElement.class);
}
enum ContactNoticeType {
NONE,
DOMAIN,
CONTACT
}
static RdapJsonFormatter getTestRdapJsonFormatter(Clock clock) {
RdapJsonFormatter rdapJsonFormatter = new RdapJsonFormatter();
rdapJsonFormatter.rdapAuthorization = RdapAuthorization.PUBLIC_AUTHORIZATION;

View File

@@ -178,21 +178,56 @@ class CopyDetailReportsActionTest {
verify(emailUtils)
.sendAlertEmail(
"""
Copied detail reports.
The following errors were encountered:
Registrar: TheRegistrar
Error: java.io.IOException: expected
""");
Copied detail reports.
The following errors were encountered:
Registrar: TheRegistrar
Error: java.io.IOException: expected
""");
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8);
assertThat(response.getPayload())
.isEqualTo(
"""
Copied detail reports.
The following errors were encountered:
Registrar: TheRegistrar
Error: java.io.IOException: expected
""");
Copied detail reports.
The following errors were encountered:
Registrar: TheRegistrar
Error: java.io.IOException: expected
""");
}
@Test
void testFail_tooManyFailures_one_registrar_sendsAlertEmail_continues() throws IOException {
gcsUtils.createFromBytes(
BlobId.of("test-bucket", "results/invoice_details_2017-10_TheRegistrar_hello.csv"),
"hola,mundo\n3,4".getBytes(UTF_8));
gcsUtils.createFromBytes(
BlobId.of("test-bucket", "results/invoice_details_2017-10_TheRegistrar_test.csv"),
"hello,world\n1,2".getBytes(UTF_8));
when(driveConnection.createOrUpdateFile(any(), any(), any(), any()))
.thenThrow(new IOException("expected"));
action.run();
verify(emailUtils)
.sendAlertEmail(
"""
Copied detail reports.
The following errors were encountered:
Registrar: TheRegistrar
Error: java.io.IOException: expected
\tjava.io.IOException: expected
""");
assertThat(response.getStatus()).isEqualTo(SC_OK);
assertThat(response.getContentType()).isEqualTo(MediaType.PLAIN_TEXT_UTF_8);
assertThat(response.getPayload())
.isEqualTo(
"""
Copied detail reports.
The following errors were encountered:
Registrar: TheRegistrar
Error: java.io.IOException: expected
\tjava.io.IOException: expected
""");
}
@Test

View File

@@ -26,10 +26,16 @@ import com.google.common.net.HostAndPort;
import com.google.common.util.concurrent.SimpleTimeLimiter;
import google.registry.util.RegistryEnvironment;
import google.registry.util.UrlChecker;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.http.HttpServlet;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.FutureTask;
@@ -49,6 +55,8 @@ import org.eclipse.jetty.server.ServerConnector;
* {@link #stop()} methods. However, a {@link #process()} method was added, which is used to process
* requests made to servlets (not static files) in the calling thread.
*
* <p>A servlet that expects multi-part requests should be annotated with {@link MultipartConfig}.
*
* <p><b>Note:</b> This server is intended for development purposes. For the love all that is good,
* do not make this public-facing.
*
@@ -70,6 +78,7 @@ public final class TestServer {
private final HostAndPort urlAddress;
private final Server server = new Server();
private final BlockingQueue<FutureTask<Void>> requestQueue = new LinkedBlockingDeque<>();
private List<Path> multiPartTmpDirs = new ArrayList<>();
/**
* Creates a new instance, but does not begin serving.
@@ -134,6 +143,13 @@ public final class TestServer {
},
SHUTDOWN_TIMEOUT_MS,
TimeUnit.MILLISECONDS);
for (var dir : multiPartTmpDirs) {
try {
Files.delete(dir);
} catch (Exception e) {
// Ignore
}
}
} catch (Exception e) {
throwIfUnchecked(e);
throw new RuntimeException(e);
@@ -161,7 +177,27 @@ public final class TestServer {
StaticResourceServlet.configureServletHolder(holder, runfile.getKey(), runfile.getValue());
}
for (Route route : routes) {
context.addServlet(wrapServlet(route.servletClass()), route.path());
holder = context.addServlet(wrapServlet(route.servletClass()), route.path());
MultipartConfig multipartConfig = route.servletClass().getAnnotation(MultipartConfig.class);
if (multipartConfig != null) {
try {
var location = multipartConfig.location();
if (location == null || location.isBlank()) {
Path tmpDir = Files.createTempDirectory("TestServer_");
multiPartTmpDirs.add(tmpDir);
location = tmpDir.toString();
}
MultipartConfigElement multipartConfigElement =
new MultipartConfigElement(
location,
multipartConfig.maxFileSize(),
multipartConfig.maxRequestSize(),
multipartConfig.fileSizeThreshold());
holder.getRegistration().setMultipartConfig(multipartConfigElement);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
holder = context.addServlet(DefaultServlet.class, "/*");
holder.setInitParameter("aliases", "1");

View File

@@ -587,7 +587,7 @@ public final class DatabaseHelper {
public static Domain persistDomainWithDependentResources(
String label,
String tld,
Contact contact,
@Nullable Contact contact,
DateTime now,
DateTime creationTime,
DateTime expirationTime) {
@@ -601,13 +601,16 @@ public final class DatabaseHelper {
.setCreationRegistrarId("TheRegistrar")
.setCreationTimeForTest(creationTime)
.setRegistrationExpirationTime(expirationTime)
.setRegistrant(Optional.of(contact.createVKey()))
.setContacts(
ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, contact.createVKey()),
DesignatedContact.create(Type.TECH, contact.createVKey())))
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create("fooBAR")));
Duration addGracePeriodLength = Tld.get(tld).getAddGracePeriodLength();
if (contact != null) {
domainBuilder
.setRegistrant(Optional.of(contact.createVKey()))
.setContacts(
ImmutableSet.of(
DesignatedContact.create(Type.ADMIN, contact.createVKey()),
DesignatedContact.create(Type.TECH, contact.createVKey())));
}
if (creationTime.plus(addGracePeriodLength).isAfter(now)) {
domainBuilder.addGracePeriod(
GracePeriod.create(

View File

@@ -50,10 +50,8 @@ class LoadTestCommandTest extends CommandTestCase<LoadTestCommand> {
.put("clientId", "acme")
.put("successfulHostCreates", 1)
.put("successfulDomainCreates", 1)
.put("successfulContactCreates", 1)
.put("hostInfos", 1)
.put("domainInfos", 1)
.put("contactInfos", 1)
.put("runSeconds", 9200)
.build();
verify(connection)
@@ -69,10 +67,8 @@ class LoadTestCommandTest extends CommandTestCase<LoadTestCommand> {
"--client_id=NewRegistrar",
"--successful_host_creates=10",
"--successful_domain_creates=11",
"--successful_contact_creates=12",
"--host_infos=13",
"--domain_infos=14",
"--contact_infos=15",
"--run_seconds=16");
ImmutableMap<String, Object> params =
new ImmutableMap.Builder<String, Object>()
@@ -80,10 +76,8 @@ class LoadTestCommandTest extends CommandTestCase<LoadTestCommand> {
.put("clientId", "NewRegistrar")
.put("successfulHostCreates", 10)
.put("successfulDomainCreates", 11)
.put("successfulContactCreates", 12)
.put("hostInfos", 13)
.put("domainInfos", 14)
.put("contactInfos", 15)
.put("runSeconds", 16)
.build();
verify(connection)

View File

@@ -20,6 +20,7 @@ import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.loadSingleton;
import static google.registry.testing.SqlHelper.saveRegistrar;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
@@ -54,30 +55,52 @@ class SecurityActionTest extends ConsoleActionBaseTestCase {
SAMPLE_CERT2);
private Registrar testRegistrar;
private static final String VALIDITY_TOO_LONG_CERT_PEM =
"-----BEGIN CERTIFICATE-----\n"
+ "MIIDejCCAv+gAwIBAgIQHNcSEt4VENkSgtozEEoQLzAKBggqhkjOPQQDAzB8MQsw\n"
+ "CQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xGDAW\n"
+ "BgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBSb290IENl\n"
+ "cnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzAeFw0xOTAzMDcxOTQyNDJaFw0zNDAz\n"
+ "MDMxOTQyNDJaMG8xCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UE\n"
+ "BwwHSG91c3RvbjERMA8GA1UECgwIU1NMIENvcnAxKzApBgNVBAMMIlNTTC5jb20g\n"
+ "U1NMIEludGVybWVkaWF0ZSBDQSBFQ0MgUjIwdjAQBgcqhkjOPQIBBgUrgQQAIgNi\n"
+ "AASEOWn30uEYKDLFu4sCjFQ1VupFaeMtQjqVWyWSA7+KFljnsVaFQ2hgs4cQk1f/\n"
+ "RQ2INSwdVCYU0i5qsbom20rigUhDh9dM/r6bEZ75eFE899kSCI14xqThYVLPdLEl\n"
+ "+dyjggFRMIIBTTASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFILRhXMw\n"
+ "5zUE044CkvvlpNHEIejNMHgGCCsGAQUFBwEBBGwwajBGBggrBgEFBQcwAoY6aHR0\n"
+ "cDovL3d3dy5zc2wuY29tL3JlcG9zaXRvcnkvU1NMY29tLVJvb3RDQS1FQ0MtMzg0\n"
+ "LVIxLmNydDAgBggrBgEFBQcwAYYUaHR0cDovL29jc3BzLnNzbC5jb20wEQYDVR0g\n"
+ "BAowCDAGBgRVHSAAMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATA7BgNV\n"
+ "HR8ENDAyMDCgLqAshipodHRwOi8vY3Jscy5zc2wuY29tL3NzbC5jb20tZWNjLVJv\n"
+ "b3RDQS5jcmwwHQYDVR0OBBYEFA10Zgpen+Is7NXCXSUEf3Uyuv99MA4GA1UdDwEB\n"
+ "/wQEAwIBhjAKBggqhkjOPQQDAwNpADBmAjEAxYt6Ylk/N8Fch/3fgKYKwI5A011Q\n"
+ "MKW0h3F9JW/NX/F7oYtWrxljheH8n2BrkDybAjEAlCxkLE0vQTYcFzrR24oogyw6\n"
+ "VkgTm92+jiqJTO5SSA9QUa092S5cTKiHkH2cOM6m\n"
+ "-----END CERTIFICATE-----";
private AuthenticatedRegistrarAccessor registrarAccessor =
AuthenticatedRegistrarAccessor.createForTesting(
ImmutableSetMultimap.of("registrarId", AuthenticatedRegistrarAccessor.Role.ADMIN));
private CertificateChecker certificateChecker =
new CertificateChecker(
ImmutableSortedMap.of(START_OF_TIME, 20825, DateTime.parse("2020-09-01T00:00:00Z"), 398),
30,
15,
2048,
ImmutableSet.of("secp256r1", "secp384r1"),
clock);
@BeforeEach
void beforeEach() {
testRegistrar = saveRegistrar("registrarId");
}
@Test
void testSuccess_postRegistrarInfo() throws IOException {
CertificateChecker lenientChecker =
new CertificateChecker(
ImmutableSortedMap.of(
START_OF_TIME, 20825, DateTime.parse("2020-09-01T00:00:00Z"), 398),
30,
15,
2048,
ImmutableSet.of("secp256r1", "secp384r1"),
clock);
clock.setTo(DateTime.parse("2020-11-01T00:00:00Z"));
SecurityAction action =
createAction(
testRegistrar.getRegistrarId());
SecurityAction action = createAction(testRegistrar.getRegistrarId(), lenientChecker);
action.run();
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
Registrar r = loadRegistrar(testRegistrar.getRegistrarId());
@@ -90,9 +113,39 @@ class SecurityActionTest extends ConsoleActionBaseTestCase {
assertThat(history.getDescription()).hasValue("registrarId|IP_CHANGE,PRIMARY_SSL_CERT_CHANGE");
}
private SecurityAction createAction(String registrarId) throws IOException {
@Test
void testFailure_validityPeriodTooLong_returnsSpecificError() throws IOException {
CertificateChecker strictChecker =
new CertificateChecker(
ImmutableSortedMap.of(START_OF_TIME, 398),
30,
15,
2048,
ImmutableSet.of("secp256r1", "secp384r1"),
clock);
clock.setTo(DateTime.parse("2025-01-01T00:00:00Z"));
String escapedCert = VALIDITY_TOO_LONG_CERT_PEM.replace("\n", "\\n");
String jsonWithBadCert =
String.format(
"{\"registrarId\": \"registrarId\", \"clientCertificate\": \"%s\"}", escapedCert);
SecurityAction action =
createAction(testRegistrar.getRegistrarId(), jsonWithBadCert, strictChecker);
action.run();
String expectedError =
"Certificate validity period is too long; it must be less than or equal to 398 days.";
FakeResponse response = (FakeResponse) consoleApiParams.response();
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
assertThat(response.getPayload()).isEqualTo(expectedError);
}
private SecurityAction createAction(
String registrarId, String jsonBody, CertificateChecker certificateChecker)
throws IOException {
when(consoleApiParams.request().getMethod()).thenReturn(Action.Method.POST.toString());
doReturn(new BufferedReader(new StringReader(jsonRegistrar1)))
doReturn(new BufferedReader(new StringReader(jsonBody)))
.when(consoleApiParams.request())
.getReader();
Optional<Registrar> maybeRegistrar =
@@ -101,4 +154,9 @@ class SecurityActionTest extends ConsoleActionBaseTestCase {
return new SecurityAction(
consoleApiParams, certificateChecker, registrarAccessor, registrarId, maybeRegistrar);
}
private SecurityAction createAction(String registrarId, CertificateChecker certificateChecker)
throws IOException {
return createAction(registrarId, jsonRegistrar1, certificateChecker);
}
}

View File

@@ -21,11 +21,11 @@ import static org.apache.commons.text.StringEscapeUtils.escapeEcmaScript;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import java.net.URL;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
@@ -100,7 +100,7 @@ public final class WebDriverPlusScreenDifferExtension
webDriverPlusScreenDiffer =
new WebDriverScreenDiffer(driver, GOLDENS_PATH, MAX_COLOR_DIFF, MAX_PIXEL_DIFF);
// non-zero timeout so findByElement will wait for the element to appear
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));
driver.manage().window().setSize(DEFAULT_WINDOW_SIZE);
if (imageNamePrefix == null) {

View File

@@ -1,56 +0,0 @@
{
"rdapConformance": [
"rdap_level_0",
"icann_rdap_response_profile_1",
"icann_rdap_technical_implementation_guide_1"
],
"objectClassName": "entity",
"handle": "%CONTACT_HANDLE_1%",
"status": ["active", "associated"],
"links": [
{
"rel": "self",
"href": "https://example.tld/rdap/entity/%CONTACT_HANDLE_1%",
"type": "application/rdap+json",
"value": "%REQUEST_URL%"
}
],
"events": [
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"
}
],
"vcardArray":
[
"vcard",
[
["version", {}, "text", "4.0"],
["fn", {}, "text", "%CONTACT_FULLNAME_1%"],
["org", {}, "text", "GOOGLE INCORPORATED <script>"],
["adr", {}, "text",
[
"",
"",
[ %CONTACT_ADDRESS_1% ],
"KOKOMO",
"BM",
"31337",
"United States"
]
],
["tel", {"type": ["voice"]}, "uri", "tel:+1.2126660420"],
["tel", {"type": ["fax"]}, "uri", "tel:+1.2126660420"]
]
],
"remarks": [
{
"title": "EMAIL REDACTED FOR PRIVACY",
"type": "object redacted due to authorization",
"description": [
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
]
}
]
}

View File

@@ -1,47 +0,0 @@
{
"rdapConformance": [
"rdap_level_0",
"icann_rdap_response_profile_1",
"icann_rdap_technical_implementation_guide_1"
],
"objectClassName" : "entity",
"handle" : "",
"events": [
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"
}
],
"vcardArray": [
"vcard",
[
["version", {}, "text", "4.0"],
["fn", {}, "text", ""]
]
],
"remarks": [
{
"title": "REDACTED FOR PRIVACY",
"type": "object redacted due to authorization",
"links": [
{
"type":"text\/html",
"href": "https:\/\/github.com\/google\/nomulus\/blob\/master\/docs\/rdap.md#authentication",
"rel": "alternate",
"value": "%REQUEST_URL%"
}
],
"description": [
"Some of the data in this object has been removed.",
"Contact personal data is visible only to the owning registrar."
]
},
{
"title": "EMAIL REDACTED FOR PRIVACY",
"type": "object redacted due to authorization",
"description": [
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
]
}
]
}

View File

@@ -1,56 +0,0 @@
{
"rdapConformance": [
"rdap_level_0",
"icann_rdap_response_profile_1",
"icann_rdap_technical_implementation_guide_1"
],
"objectClassName" : "entity",
"handle" : "%CONTACT_HANDLE_1%",
"status" : ["%STATUS_1%"],
"links" :
[
{
"rel" : "self",
"href": "https://example.tld/rdap/entity/%CONTACT_HANDLE_1%",
"type" : "application/rdap+json",
"value": "%REQUEST_URL%"
}
],
"events": [
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"
}
],
"vcardArray" :
[
"vcard",
[
["version", {}, "text", "4.0"],
["fn", {}, "text", "%CONTACT_FULLNAME_1%"],
["org", {}, "text", "GOOGLE INCORPORATED <script>"],
["adr", {}, "text",
[
"",
"",
[ %CONTACT_ADDRESS_1% ],
"KOKOMO",
"BM",
"31337",
"United States"
]
],
["tel", {"type" : ["voice"]}, "uri", "tel:+1.2126660420"],
["tel", {"type" : ["fax"]}, "uri", "tel:+1.2126660420"]
]
],
"remarks": [
{
"title": "EMAIL REDACTED FOR PRIVACY",
"type": "object redacted due to authorization",
"description": [
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
]
}
]
}

View File

@@ -1,42 +0,0 @@
{
"rdapConformance": [
"rdap_level_0",
"icann_rdap_response_profile_1",
"icann_rdap_technical_implementation_guide_1"
],
"objectClassName" : "entity",
"handle" : "%CONTACT_HANDLE_1%",
"status" : ["inactive"],
"links" :
[
{
"rel" : "self",
"href": "https://example.tld/rdap/entity/%CONTACT_HANDLE_1%",
"type" : "application/rdap+json",
"value": "%REQUEST_URL%"
}
],
"events":
[
{
"eventAction": "last update of RDAP database",
"eventDate": "2000-01-01T00:00:00.000Z"
}
],
"vcardArray" :
[
"vcard",
[
["version", {}, "text", "4.0"]
]
],
"remarks": [
{
"title": "EMAIL REDACTED FOR PRIVACY",
"type": "object redacted due to authorization",
"description": [
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
]
}
]
}

Some files were not shown because too many files have changed in this diff Show More