1
0
mirror of https://github.com/google/nomulus synced 2026-07-06 08:06:33 +00:00

Compare commits

...

18 Commits

Author SHA1 Message Date
gbrodman 7b34659a8f Add registryLockEmailAddress field to User object (#2418)
We've added the field in the database in a previous PR. This is only
used in the old console for now because the new console does not have
registry lock functionality yet
2024-05-09 21:42:45 +00:00
sarahcaseybot 808432e709 Remove the createBillingCost field from Tld (#2425)
* Remove the createBillingCost field from Tld

* fix spacing

* Change field name of map

* Rename getter

* Fix formatting

* Fix todo

* unchange column name
2024-05-08 18:14:03 +00:00
Lai Jiang 73d3b76a89 Remove more usage of AutoValue (#2432)
This PR also removes `SerializedForm` used to serialize
`PendingDeposit`, as it is now a simple record.
2024-05-08 00:50:01 +00:00
Weimin Yu ca072b4861 Add log traces to Nomulus service on GKE (#2427)
* Add log traces to Nomulus service on GKE

Add request-scope log traces to Nomulus on GKE which, unlike
AppEngine and Cloud Run etc, does not generate traces for hosted
applications. This change only affects the GKE image. It does not affect
the AppEngine services.

Log traces are added to Nomulus-generated logs in request-processing
threads. Forked threads are not covered yet. The single relevant use
case (TimeLimiter) will be addressed in a followup PR.

The main change is in the logging configuration:

*  Use gcp-cloud-logging's LoggingHandler

*  Add gcp-cloud-logging's TraceLoggingEnhancer to the handler.

*  Set a thread-local trace id through the TraceLoggingEnhancer in
   ServletBase on request's entry and clear it on completion.

Also removed an unused class (`RequestLogId`).

* CR

* CR
2024-05-07 19:15:46 +00:00
Pavlo Tkach 54c896cbb9 Add console epp password integration (#2426) 2024-05-06 18:32:54 +00:00
Pavlo Tkach 2c7bf2cfdb Update cloudbuild-release.yaml with nokeycheck option (#2431) 2024-05-06 18:01:31 +00:00
gbrodman 49d2e34e12 Add a separate RegistryLock action for the console (#2411)
This handles both GET and POST requests. For POST requests it doesn't
actually change anything about the domains because we will need to add a
verification action (this will be done in a future PR).
2024-05-03 22:37:22 +00:00
Weimin Yu 5511b41f93 Avoid contention over the RefreshDnsRequest table (#2428)
* Avoid contention over the RefreshDnsRequest table

This table can be small at times, when PSQL may use table scan in
queries by keys. At the SERIALIZABLE isolation level, updates to
unrelated rows may conflict due to this `optimization`.

Lower the isolation level to repeatable read.

* Code review
2024-05-03 20:31:54 +00:00
gbrodman 147cdff555 Add registry lock email address col to UserUpdateHistory (#2424) 2024-05-02 15:51:48 +00:00
Ben McIlwain 4b6ade0b14 Bring codebase up to more recent Java standards (#2422)
This includes using the new switch format (though IntelliJ does not yet
understand patterns including default so those aren't used), multiline strings,
replacing some unnecessary type declarations with <>, converting some classes to
records, replacing some Guava predicates with native Java code, and some other
miscellaneous Code Inspection fixes.
2024-05-01 20:48:38 +00:00
Pavlo Tkach 570618705e Allow console access for FTE globar role (#2419) 2024-05-01 16:19:29 +00:00
sarahcaseybot e791608098 Add more indexes to speed up deleteProberDataAction (#2423)
This adds an index on transfer_billing_cancellation_id to Domain and superordinate_domain to Host. When tested on crash with the action limited to only delete 10,000 domains, before these indexes were added the action took about 2 hours to delete 10,000 domains. Once these indexes were added, the action was able to delete the 10,000 domains in a little under 2 minutes.
2024-05-01 15:44:08 +00:00
gbrodman 03b358726a Add Java classes for console history objects (#2350)
This also creates base classes for the objects contained within the
history classes, e.g. RegistrarBase. This is the same way that objects
stored in the HistoryEntry subclasses have base classes, e.g.
DomainBase.
2024-04-30 20:42:40 +00:00
gbrodman d121f8f547 Generate fake XSRF token in FakeConsoleApiParams for tests (#2421) 2024-04-30 17:47:53 +00:00
gbrodman b27218d799 Fix a couple Checkstyle warnings (#2420) 2024-04-29 20:08:53 +00:00
Pavlo Tkach e78ce42dd5 Add console DUM download (#2402)
* Add console DUM download

* Add console DUM download
2024-04-26 15:56:50 +00:00
Ben McIlwain 55fade497d Convert a bunch more @AutoValues to records (#2412) 2024-04-25 16:59:31 +00:00
gbrodman e7501b621a Add DB column for separate rlock email address (#2413)
We cannot rely on the user checking their login email, so we'll want to
send the emails to the other address if configured. This is already the
case in RegistrarPoc.
2024-04-25 15:38:57 +00:00
348 changed files with 9440 additions and 7277 deletions
+5 -5
View File
@@ -54,11 +54,11 @@ org.jacoco:org.jacoco.ant:0.8.11=jacocoAnt
org.jacoco:org.jacoco.core:0.8.11=jacocoAnt
org.jacoco:org.jacoco.report:0.8.11=jacocoAnt
org.javassist:javassist:3.28.0-GA=checkstyle
org.junit.jupiter:junit-jupiter-api:5.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.10.2=testCompileClasspath,testRuntimeClasspath
org.junit:junit-bom:5.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit:junit-bom:5.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
org.ow2.asm:asm-commons:9.6=jacocoAnt
org.ow2.asm:asm-tree:9.6=jacocoAnt
@@ -175,14 +175,7 @@ public class TextDiffSubject extends Subject {
.orElse(0);
}
private static class SideBySideRowFormatter {
private final int maxExpectedLineLength;
private final int maxActualLineLength;
private SideBySideRowFormatter(int maxExpectedLineLength, int maxActualLineLength) {
this.maxExpectedLineLength = maxExpectedLineLength;
this.maxActualLineLength = maxActualLineLength;
}
private record SideBySideRowFormatter(int maxExpectedLineLength, int maxActualLineLength) {
public String formatRow(String expected, String actual, char padChar) {
return String.format(
@@ -262,6 +262,9 @@
{
"moduleLicense": "Unicode/ICU License"
},
{
"moduleLicense": "Unicode-3.0"
},
{
"moduleLicense": "Public Domain",
"moduleName": "aopalliance:aopalliance"
+12
View File
@@ -178,6 +178,18 @@ PRESUBMITS = {
{"/node_modules/"},
):
"Do not use shaded dependencies from testcontainers.",
PresubmitCheck(
r".*autovalue\.shaded.*",
"java",
{"/node_modules/"},
):
"Do not use shaded dependencies from autovalue.",
PresubmitCheck(
r".*avro\.shaded.*",
"java",
{"/node_modules/"},
):
"Do not use shaded dependencies from avro.",
PresubmitCheck(
r".*com\.google\.common\.truth\.Truth8.*",
"java",
+1
View File
@@ -36,6 +36,7 @@ yarn-error.log
/libpeerconnection.log
testem.log
/typings
.nx/
# System files
.DS_Store
@@ -1,6 +1,6 @@
<app-selected-registrar-wrapper>
<h1 class="mat-headline-4">Domains</h1>
<div class="console-domains">
<h1 class="mat-headline-4">Domains</h1>
@if (totalResults === 0) {
<div class="console-app__empty-domains">
<h1>
@@ -13,6 +13,17 @@
} @else if(isLoading) {
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
} @else {
<a
mat-stroked-button
color="primary"
href="/console-api/dum-download?registrarId={{
registrarService.registrarId()
}}"
class="console-app-domains__download"
>
<mat-icon>download</mat-icon>
Download domains (.csv)
</a>
<mat-form-field class="console-app__domains-filter">
<mat-label>Filter</mat-label>
<input
@@ -12,6 +12,15 @@
}
}
&-domains {
position: relative;
&__download {
position: absolute;
top: 0;
right: 0;
}
}
&__domains {
width: 100%;
overflow: auto;
@@ -54,7 +54,7 @@ export class DomainListComponent {
constructor(
private backendService: BackendService,
private domainListService: DomainListService,
private registrarService: RegistrarService,
protected registrarService: RegistrarService,
private _snackBar: MatSnackBar
) {
effect(() => {
@@ -7,7 +7,7 @@
*matTreeNodeDef="let node"
matTreeNodeToggle
(click)="onClick(node)"
[class.active]="router.url.endsWith(node.path)"
[class.active]="router.url.includes(node.path)"
>
<mat-icon class="console-app__nav-icon" *ngIf="node.iconName">
{{ node.iconName }}
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { HttpErrorResponse } from '@angular/common/http';
import { Component } from '@angular/core';
import {
AbstractControl,
@@ -68,9 +69,12 @@ export default class EppPasswordEditComponent {
) {
this.passwordUpdateForm?.get('newPasswordRepeat')?.setErrors(null);
} else {
this.passwordUpdateForm
?.get('newPasswordRepeat')
?.setErrors({ passwordsDontMatch: control.value });
// latest angular just won't detect the error without setTimeout
setTimeout(() => {
this.passwordUpdateForm
?.get('newPasswordRepeat')
?.setErrors({ passwordsDontMatch: control.value });
});
}
return null;
};
@@ -92,15 +96,24 @@ export default class EppPasswordEditComponent {
});
save() {
debugger;
// this.securityService.saveEppPassword().subscribe({
// complete: () => {
// this.goBack();
// },
// error: (err: HttpErrorResponse) => {
// this._snackBar.open(err.error);
// },
// });
const { oldPassword, newPassword, newPasswordRepeat } =
this.passwordUpdateForm.value;
if (!oldPassword || !newPassword || !newPasswordRepeat) return;
this.securityService
.saveEppPassword({
registrarId: this.registrarService.registrarId(),
oldPassword,
newPassword,
newPasswordRepeat,
})
.subscribe({
complete: () => {
this.goBack();
},
error: (err: HttpErrorResponse) => {
this._snackBar.open(err.error);
},
});
}
goBack() {
@@ -22,6 +22,13 @@ import {
} from 'src/app/registrar/registrar.service';
import { BackendService } from 'src/app/shared/services/backend.service';
export interface EppPasswordBackendModel {
registrarId: string;
oldPassword: string;
newPassword: string;
newPasswordRepeat: string;
}
export function apiToUiConverter(
securitySettings: SecuritySettingsBackendModel = {}
): SecuritySettings {
@@ -68,5 +75,11 @@ export class SecurityService {
);
}
saveEppPassword() {}
saveEppPassword(data: EppPasswordBackendModel) {
return this.backend.postEppPasswordUpdate(data).pipe(
switchMap(() => {
return this.registrarService.loadRegistrars();
})
);
}
}
@@ -17,12 +17,14 @@ import { Injectable } from '@angular/core';
import { Observable, catchError, of, throwError } from 'rxjs';
import { DomainListResult } from 'src/app/domains/domainList.service';
import { environment } from 'src/environments/environment';
import {
Registrar,
SecuritySettingsBackendModel,
WhoisRegistrarFields,
} from '../../registrar/registrar.service';
import { Contact } from '../../settings/contact/contact.service';
import { EppPasswordBackendModel } from '../../settings/security/security.service';
import { UserData } from './userData.service';
@Injectable()
@@ -33,6 +35,15 @@ export class BackendService {
error: HttpErrorResponse,
mockData?: Type
): Observable<Type> {
// This is a temporary redirect to the old console untill the new console
// is fully released and enabled
if (
environment.production &&
error.url &&
window.location.href.indexOf(error.url) < 0
) {
window.location.href = error.url;
}
if (error.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
@@ -134,6 +145,15 @@ export class BackendService {
);
}
postEppPasswordUpdate(
data: EppPasswordBackendModel
): Observable<EppPasswordBackendModel> {
return this.http.post<EppPasswordBackendModel>(
`/console-api/eppPassword`,
data
);
}
getUserData(): Observable<UserData> {
return this.http
.get<UserData>('/console-api/userdata')
@@ -30,7 +30,7 @@ export interface GlobalLoader {
providedIn: 'root',
})
export class GlobalLoaderService {
private static readonly TIMEOUT_MS = 3000;
private static readonly TIMEOUT_MS = 10000;
private loaders = new Map<GlobalLoader, Subscription>();
public isLoading: boolean = false;
+1
View File
@@ -159,6 +159,7 @@ dependencies {
implementation deps['com.google.cloud.bigdataoss:util']
implementation deps['com.google.cloud.sql:jdbc-socket-factory-core']
runtimeOnly deps['com.google.cloud.sql:postgres-socket-factory']
implementation deps['com.google.cloud:google-cloud-logging']
implementation deps['com.google.cloud:google-cloud-secretmanager']
implementation deps['com.google.code.gson:gson']
implementation deps['com.google.auto.service:auto-service-annotations']
+82 -63
View File
@@ -8,14 +8,22 @@ args4j:args4j:2.0.26=css
args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
cglib:cglib-nodep:2.2=css
com.charleskorn.kaml:kaml:0.20.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-annotations:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-core:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-databind:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson:jackson-bom:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-annotations:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.fasterxml.jackson.core:jackson-annotations:2.17.0=testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-core:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.fasterxml.jackson.core:jackson-core:2.17.0=testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-databind:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.fasterxml.jackson.core:jackson-databind:2.17.0=testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.17.0=testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.0=testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.17.0=testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.0=testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson:jackson-bom:2.16.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.fasterxml.jackson:jackson-bom:2.17.0=testCompileClasspath,testRuntimeClasspath
com.fasterxml:classmate:1.5.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
com.github.ben-manes.caffeine:caffeine:3.1.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -32,12 +40,13 @@ com.github.jnr:jnr-unixsocket:0.38.22=compileClasspath,deploy_jar,nonprodCompile
com.github.jnr:jnr-x86asm:1.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.android:annotations:4.1.1.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-appengine:2.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-appengine:2.4.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-jackson2:2.0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,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.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api-client:google-api-client-servlet:2.4.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api-client:google-api-client:1.35.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.32.1-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.32.1-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api.grpc:gapic-google-cloud-storage-v2:2.37.0-alpha=testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.173.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.173.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -47,8 +56,9 @@ com.google.api.grpc:grpc-google-cloud-pubsublite-v1:1.12.22=compileClasspath,dep
com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.58.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.58.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.58.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.32.1-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-common-protos:2.37.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.32.1-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api.grpc:grpc-google-cloud-storage-v2:2.37.0-alpha=testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:grpc-google-common-protos:2.38.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.173.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.173.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -56,43 +66,46 @@ com.google.api.grpc:proto-google-cloud-bigtable-admin-v2:2.33.0=compileClasspath
com.google.api.grpc:proto-google-cloud-bigtable-v2:2.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-datastore-v1:0.109.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-firestore-v1:3.16.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-logging-v2:0.106.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-monitoring-v3:1.64.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.api.grpc:proto-google-cloud-monitoring-v3:3.36.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-pubsub-v1:1.108.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-pubsublite-v1:1.12.22=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.41.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.41.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.42.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.42.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.58.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.58.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-executor-v1:6.58.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-spanner-v1:6.58.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.32.1-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2:2.41.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.131.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.131.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-common-protos:2.37.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.32.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:api-common:2.29.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax-grpc:2.46.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax-httpjson:2.46.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax:2.46.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-admin-directory:directory_v1-rev20240304-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-appengine:v1-rev20240226-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.32.1-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.api.grpc:proto-google-cloud-storage-v2:2.37.0-alpha=testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2:2.42.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.132.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.132.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-common-protos:2.38.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api.grpc:proto-google-iam-v1:1.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:api-common:2.30.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax-grpc:2.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax-httpjson:2.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.api:gax:2.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-admin-directory:directory_v1-rev20240429-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-appengine:v1-rev20240415-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-bigquery:v2-rev20240124-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20240128-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dataflow:v1b3-rev20240113-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-dns:v2beta1-rev99-1.25.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-drive:v2-rev393-1.25.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-gmail:v1-rev20231218-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-gmail:v1-rev20240422-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-groupssettings:v1-rev20210624-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-rev20240314-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-rev20240414-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-rev20240402-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-sheets:v4-rev20240423-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-sqladmin:v1beta4-rev20240324-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-storage:v1-rev20240205-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.apis:google-api-services-storage:v1-rev20240205-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.apis:google-api-services-storage:v1-rev20240319-2.0.0=testCompileClasspath,testRuntimeClasspath
com.google.appengine:appengine-api-1.0-sdk:2.0.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.appengine:appengine-api-stubs:2.0.26=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.appengine:appengine-remote-api:2.0.26=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -117,19 +130,22 @@ com.google.cloud.sql:postgres-socket-factory:1.18.0=deploy_jar,runtimeClasspath,
com.google.cloud:google-cloud-bigquerystorage:3.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigtable-stats:2.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-bigtable:2.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-grpc:2.32.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-http:2.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core:2.32.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-grpc:2.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core-http:2.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.cloud:google-cloud-core-http:2.37.0=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-core:2.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-firestore:3.16.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-logging:3.17.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-monitoring:1.82.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
com.google.cloud:google-cloud-monitoring:3.36.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-nio:0.127.15=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-nio:0.127.16=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-pubsub:1.126.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-pubsublite:1.12.22=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-secretmanager:2.41.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-secretmanager:2.42.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-spanner:6.58.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-storage:2.32.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-tasks:2.41.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-storage:2.32.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.cloud:google-cloud-storage:2.37.0=testCompileClasspath,testRuntimeClasspath
com.google.cloud:google-cloud-tasks:2.42.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:grpc-gcp:1.5.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.16.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.code.findbugs:jsr305:3.0.1=css
@@ -167,10 +183,12 @@ com.google.guava:guava:33.0.0-jre=annotationProcessor,testAnnotationProcessor
com.google.guava:guava:33.1.0-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,soy,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.43.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-apache-v2:1.43.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.http-client:google-http-client-apache-v2:1.44.1=testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-appengine:1.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-gson:1.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-jackson2:1.43.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-jackson2:1.43.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
com.google.http-client:google-http-client-jackson2:1.44.1=testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client-protobuf:1.43.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.http-client:google-http-client:1.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.inject.extensions:guice-multibindings:4.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
@@ -192,11 +210,11 @@ com.google.oauth-client:google-oauth-client-java6:1.35.0=compileClasspath,deploy
com.google.oauth-client:google-oauth-client-jetty:1.35.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.oauth-client:google-oauth-client-servlet:1.35.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.oauth-client:google-oauth-client:1.35.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.protobuf:protobuf-java-util:3.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.protobuf:protobuf-java-util:3.25.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.protobuf:protobuf-java:2.5.0=css
com.google.protobuf:protobuf-java:3.13.0=soy
com.google.protobuf:protobuf-java:3.19.6=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
com.google.protobuf:protobuf-java:3.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.protobuf:protobuf-java:3.25.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.re2j:re2j:1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.google.template:soy:2021-02-01=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
com.google.truth:truth:1.4.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -265,7 +283,8 @@ io.grpc:grpc-netty-shaded:1.62.2=compileClasspath,deploy_jar,nonprodCompileClass
io.grpc:grpc-netty:1.61.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf-lite:1.62.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-protobuf:1.62.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.grpc:grpc-rls:1.61.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-rls:1.61.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
io.grpc:grpc-rls:1.62.2=testRuntimeClasspath
io.grpc:grpc-services:1.61.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
io.grpc:grpc-services:1.62.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
io.grpc:grpc-stub:1.62.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -298,8 +317,8 @@ io.opencensus:opencensus-exporter-stats-stackdriver:0.31.0=compileClasspath,depl
io.opencensus:opencensus-impl-core:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-impl:0.31.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opencensus:opencensus-proto:0.2.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-api:1.36.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-context:1.36.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-api:1.37.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
io.opentelemetry:opentelemetry-context:1.37.0=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
jakarta-regexp:jakarta-regexp:1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -375,10 +394,10 @@ org.apache.sshd:sshd-scp:2.12.1=testCompileClasspath,testRuntimeClasspath
org.apache.sshd:sshd-sftp:2.12.1=testCompileClasspath,testRuntimeClasspath
org.apache.tomcat:tomcat-annotations-api:11.0.0-M19=testCompileClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.bouncycastle:bcpg-jdk18on:1.78=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcpkix-jdk18on:1.78=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcprov-jdk18on:1.78=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcutil-jdk18on:1.78=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcpg-jdk18on:1.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcpkix-jdk18on:1.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcprov-jdk18on:1.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.bouncycastle:bcutil-jdk18on:1.78.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.checkerframework:checker-compat-qual:2.5.3=compileClasspath,nonprodCompileClasspath,testCompileClasspath
org.checkerframework:checker-compat-qual:2.5.5=annotationProcessor,testAnnotationProcessor
org.checkerframework:checker-compat-qual:2.5.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
@@ -402,8 +421,8 @@ org.eclipse.jetty:jetty-server:12.0.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-session:12.0.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-util:12.0.8=testCompileClasspath,testRuntimeClasspath
org.eclipse.jetty:jetty-xml:12.0.8=testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-core:10.11.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-database-postgresql:10.11.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-core:10.12.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.flywaydb:flyway-database-postgresql:10.12.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.glassfish.jaxb:jaxb-runtime:2.3.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.glassfish.jaxb:txw2:2.3.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.gwtproject:gwt-user:2.10.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
@@ -442,23 +461,23 @@ 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.25.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jline:jline:3.26.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.joda:joda-money:1.0.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.json:json:20160212=soy
org.json:json:20231013=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.jsoup:jsoup:1.17.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.junit-pioneer:junit-pioneer:2.2.0=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-migrationsupport:5.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:5.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:1.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-runner:1.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-api:1.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-commons:1.10.2=testRuntimeClasspath
org.junit:junit-bom:5.10.2=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-api:5.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:5.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-migrationsupport:5.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:5.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:1.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:1.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-launcher:1.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-runner:1.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-api:1.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-suite-commons:1.11.0-M1=testRuntimeClasspath
org.junit:junit-bom:5.11.0-M1=testCompileClasspath,testRuntimeClasspath
org.jvnet.staxex:stax-ex:1.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.mockito:mockito-core:1.10.19=css
org.mockito:mockito-core:5.11.0=testCompileClasspath,testRuntimeClasspath
@@ -505,7 +524,7 @@ org.testcontainers:junit-jupiter:1.19.7=testCompileClasspath,testRuntimeClasspat
org.testcontainers:postgresql:1.19.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.testcontainers:selenium:1.19.7=testCompileClasspath,testRuntimeClasspath
org.testcontainers:testcontainers:1.19.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.threeten:threetenbp:1.6.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.threeten:threetenbp:1.6.9=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.tukaani:xz:1.5=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
@@ -187,18 +187,12 @@ public class CheckBulkComplianceAction implements Runnable {
.getLastNotificationSent()
.map(sentDate -> Days.daysBetween(sentDate, clock.nowUtc()).getDays())
.orElse(Integer.MAX_VALUE);
if (daysSinceLastNotification < THIRTY_DAYS) {
// Don't send an email if notification was already sent within the last 30
// days
continue;
} else if (daysSinceLastNotification < FORTY_DAYS) {
// Send an upgrade email if last email was between 30 and 40 days ago
// Send a warning email if 30-39 days since last notification and an upgrade email if 40+ days
if (daysSinceLastNotification >= THIRTY_DAYS) {
sendActiveDomainOverageEmail(
/* warning= */ false, bulkPricingPackage, overageList.get(bulkPricingPackage));
} else {
// Send a warning email
sendActiveDomainOverageEmail(
/* warning= */ true, bulkPricingPackage, overageList.get(bulkPricingPackage));
/* warning= */ daysSinceLastNotification >= FORTY_DAYS,
bulkPricingPackage,
overageList.get(bulkPricingPackage));
}
}
}
@@ -66,11 +66,15 @@ public class RelockDomainAction implements Runnable {
private static final Duration ONE_HOUR = Duration.standardHours(1);
private static final String RELOCK_SUCCESS_EMAIL_TEMPLATE =
"The domain %s was successfully re-locked.\n\nPlease contact support at %s if you have any "
+ "questions.";
"""
The domain %s was successfully re-locked.
Please contact support at %s if you have any questions.""";
private static final String RELOCK_NON_RETRYABLE_FAILURE_EMAIL_TEMPLATE =
"There was an error when automatically re-locking %s. Error message: %s\n\nPlease contact "
+ "support at %s if you have any questions.";
"""
There was an error when automatically re-locking %s. Error message: %s
Please contact support at %s if you have any questions.""";
private static final String RELOCK_TRANSIENT_FAILURE_EMAIL_TEMPLATE =
"There was an unexpected error when automatically re-locking %s. We will continue retrying "
+ "the lock for five hours. Please contact support at %s if you have any questions";
@@ -33,7 +33,7 @@ import google.registry.flows.certs.CertificateChecker;
import google.registry.groups.GmailClient;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.registrar.RegistrarPoc.Type;
import google.registry.model.registrar.RegistrarPocBase.Type;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -206,7 +206,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
() -> {
Registrar.Builder newRegistrar = tm().loadByEntity(registrar).asBuilder();
switch (certificateType) {
case PRIMARY:
case PRIMARY -> {
newRegistrar.setLastExpiringCertNotificationSentDate(now);
tm().put(newRegistrar.build());
logger.atInfo().log(
@@ -215,8 +215,8 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
DATE_FORMATTER.print(now),
certificateType.getDisplayName(),
registrar.getRegistrarName());
break;
case FAILOVER:
}
case FAILOVER -> {
newRegistrar.setLastExpiringFailoverCertNotificationSentDate(now);
tm().put(newRegistrar.build());
logger.atInfo().log(
@@ -225,13 +225,13 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
DATE_FORMATTER.print(now),
certificateType.getDisplayName(),
registrar.getRegistrarName());
break;
default:
throw new IllegalArgumentException(
String.format(
"Unsupported certificate type: %s being passed in when updating "
+ "the last notification sent date to registrar %s.",
certificateType.toString(), registrar.getRegistrarName()));
}
default ->
throw new IllegalArgumentException(
String.format(
"Unsupported certificate type: %s being passed in when updating "
+ "the last notification sent date to registrar %s.",
certificateType.toString(), registrar.getRegistrarName()));
}
});
} catch (Exception e) {
@@ -14,7 +14,6 @@
package google.registry.beam.billing;
import com.google.auto.value.AutoValue;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import google.registry.reporting.billing.BillingModule;
@@ -34,9 +33,39 @@ import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/** A POJO representing a single billable event, parsed from a {@code SchemaAndRecord}. */
@AutoValue
public abstract class BillingEvent {
/**
* A record representing a single billable event, parsed from a {@code SchemaAndRecord}.
*
* @param id The unique ID for the {@code BillingEvent} associated with this event.
* @param billingTime The DateTime (in UTC) this event becomes billable.
* @param eventTime The DateTime (in UTC) this event was generated.
* @param registrarId The billed registrar's name.
* @param billingId The billed registrar's billing account key.
* @param poNumber The Purchase Order number.
* @param tld The TLD this event was generated for.
* @param action The billable action this event was generated for (CREATE, RENEW, TRANSFER...).
* @param domain The fully qualified domain name this event was generated for.
* @param repositoryId The unique RepoID associated with the billed domain.
* @param years The number of years this billing event is made out for.
* @param currency The 3-letter currency code for the billing event (USD or JPY).
* @param amount The total cost associated with this billing event.
* @param flags A list of space-delimited flags associated with the event.
*/
public record BillingEvent(
long id,
DateTime billingTime,
DateTime eventTime,
String registrarId,
String billingId,
String poNumber,
String tld,
String action,
String domain,
String repositoryId,
int years,
String currency,
double amount,
String flags) {
private static final DateTimeFormatter DATE_TIME_FORMATTER =
DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss zzz");
@@ -60,48 +89,6 @@ public abstract class BillingEvent {
"amount",
"flags");
/** Returns the unique ID for the {@code BillingEvent} associated with this event. */
abstract long id();
/** Returns the UTC DateTime this event becomes billable. */
abstract DateTime billingTime();
/** Returns the UTC DateTime this event was generated. */
abstract DateTime eventTime();
/** Returns the billed registrar's name. */
abstract String registrarId();
/** Returns the billed registrar's billing account key. */
abstract String billingId();
/** Returns the Purchase Order number. */
abstract String poNumber();
/** Returns the tld this event was generated for. */
abstract String tld();
/** Returns the billable action this event was generated for (i.e., RENEW, CREATE, TRANSFER...) */
abstract String action();
/** Returns the fully qualified domain name this event was generated for. */
abstract String domain();
/** Returns the unique RepoID associated with the billed domain. */
abstract String repositoryId();
/** Returns the number of years this billing event is made out for. */
abstract int years();
/** Returns the 3-letter currency code for the billing event (i.e., USD or JPY.) */
abstract String currency();
/** Returns the cost associated with this billing event. */
abstract double amount();
/** Returns a list of space-delimited flags associated with the event. */
abstract String flags();
/** Creates a concrete {@link BillingEvent}. */
static BillingEvent create(
long id,
@@ -118,7 +105,7 @@ public abstract class BillingEvent {
String currency,
double amount,
String flags) {
return new AutoValue_BillingEvent(
return new BillingEvent(
id,
billingTime,
eventTime,
@@ -338,7 +325,7 @@ public abstract class BillingEvent {
@Override
public BillingEvent decode(InputStream inStream) throws IOException {
return new AutoValue_BillingEvent(
return new BillingEvent(
longCoder.decode(inStream),
DATE_TIME_FORMATTER.parseDateTime(stringCoder.decode(inStream)),
DATE_TIME_FORMATTER.parseDateTime(stringCoder.decode(inStream)),
@@ -50,16 +50,17 @@ public class RegistryPipelineWorkerInitializer implements JvmInitializer {
environment.setup();
RegistryPipelineComponent registryPipelineComponent =
toRegistryPipelineComponent(registryOptions);
Lazy<JpaTransactionManager> transactionManagerLazy;
switch (registryOptions.getJpaTransactionManagerType()) {
case READ_ONLY_REPLICA:
transactionManagerLazy =
registryPipelineComponent.getReadOnlyReplicaJpaTransactionManager();
break;
case REGULAR:
default:
transactionManagerLazy = registryPipelineComponent.getJpaTransactionManager();
}
Lazy<JpaTransactionManager> transactionManagerLazy =
switch (registryOptions.getJpaTransactionManagerType()) {
case READ_ONLY_REPLICA ->
registryPipelineComponent.getReadOnlyReplicaJpaTransactionManager();
case REGULAR -> registryPipelineComponent.getJpaTransactionManager();
default ->
throw new IllegalStateException(
String.format(
"Unknown JPA transaction manager type: %s",
registryOptions.getJpaTransactionManagerType()));
};
TransactionManagerFactory.setJpaTmOnBeamWorker(transactionManagerLazy::get);
SystemPropertySetter.PRODUCTION_IMPL.setProperty(PROPERTY, "true");
}
@@ -58,7 +58,7 @@ import google.registry.model.host.Host;
import google.registry.model.host.HostHistory;
import google.registry.model.rde.RdeMode;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.Type;
import google.registry.model.registrar.RegistrarBase.Type;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
@@ -688,27 +688,26 @@ public class RdePipeline implements Serializable {
protected abstract static class TupleTags {
protected static final TupleTag<KV<PendingDeposit, DepositFragment>> DOMAIN_FRAGMENTS =
new TupleTag<KV<PendingDeposit, DepositFragment>>() {};
new TupleTag<>() {};
protected static final TupleTag<KV<String, PendingDeposit>> REFERENCED_CONTACTS =
new TupleTag<KV<String, PendingDeposit>>() {};
new TupleTag<>() {};
protected static final TupleTag<KV<String, PendingDeposit>> REFERENCED_HOSTS =
new TupleTag<KV<String, PendingDeposit>>() {};
new TupleTag<>() {};
protected static final TupleTag<KV<String, KV<String, CoGbkResult>>> SUPERORDINATE_DOMAINS =
new TupleTag<KV<String, KV<String, CoGbkResult>>>() {};
new TupleTag<>() {};
protected static final TupleTag<KV<PendingDeposit, DepositFragment>> EXTERNAL_HOST_FRAGMENTS =
new TupleTag<KV<PendingDeposit, DepositFragment>>() {};
new TupleTag<>() {};
protected static final TupleTag<PendingDeposit> PENDING_DEPOSIT =
new TupleTag<PendingDeposit>() {};
protected static final TupleTag<PendingDeposit> PENDING_DEPOSIT = new TupleTag<>() {};
protected static final TupleTag<KV<String, CoGbkResult>> HOST_TO_PENDING_DEPOSIT =
new TupleTag<KV<String, CoGbkResult>>() {};
new TupleTag<>() {};
protected static final TupleTag<Long> REVISION_ID = new TupleTag<Long>() {};
protected static final TupleTag<Long> REVISION_ID = new TupleTag<>() {};
}
@Singleton
@@ -14,30 +14,23 @@
package google.registry.beam.spec11;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import java.io.Serializable;
/**
* A POJO representing a domain name and associated info, parsed from a {@code SchemaAndRecord}.
* A record representing a domain name and associated info, parsed from a {@code SchemaAndRecord}.
*
* <p>This is a trivially serializable class that allows Beam to transform the results of a SQL
* query into a standard Java representation.
*
* @param domainName The fully qualified domain name.
* @param domainRepoId The domain repo ID (the primary key of the domain table).
* @param registrarId The registrar ID of the associated registrar for this domain.
* @param registrarEmailAddress The email address of the registrar associated with this domain.
*/
@AutoValue
public abstract class DomainNameInfo implements Serializable {
/** Returns the fully qualified domain name. */
abstract String domainName();
/** Returns the domain repo ID (the primary key of the domain table). */
abstract String domainRepoId();
/** Returns the registrar ID of the associated registrar for this domain. */
abstract String registrarId();
/** Returns the email address of the registrar associated with this domain. */
abstract String registrarEmailAddress();
public record DomainNameInfo(
String domainName, String domainRepoId, String registrarId, String registrarEmailAddress)
implements Serializable {
/**
* Creates a concrete {@link DomainNameInfo}.
@@ -45,7 +38,6 @@ public abstract class DomainNameInfo implements Serializable {
@VisibleForTesting
static DomainNameInfo create(
String domainName, String domainRepoId, String registrarId, String registrarEmailAddress) {
return new AutoValue_DomainNameInfo(
domainName, domainRepoId, registrarId, registrarEmailAddress);
return new DomainNameInfo(domainName, domainRepoId, registrarId, registrarEmailAddress);
}
}
@@ -14,28 +14,26 @@
package google.registry.beam.spec11;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import java.io.Serializable;
import org.json.JSONException;
import org.json.JSONObject;
/** A POJO representing a threat match response from the {@code SafeBrowsing API}. */
@AutoValue
public abstract class ThreatMatch implements Serializable {
/**
* A record representing a threat match response from the {@code SafeBrowsing API}.
*
* @param threatType What kind of threat this is (malware, phishing, etc.).
* @param domainName The fully qualified domain name of the matched threat.
*/
public record ThreatMatch(String threatType, String domainName) implements Serializable {
private static final String THREAT_TYPE_FIELD = "threatType";
private static final String DOMAIN_NAME_FIELD = "domainName";
private static final String OUTDATED_NAME_FIELD = "fullyQualifiedDomainName";
/** Returns what kind of threat it is (malware, phishing etc.) */
public abstract String threatType();
/** Returns the fully qualified domain name [SLD].[TLD] of the matched threat. */
public abstract String domainName();
@VisibleForTesting
static ThreatMatch create(String threatType, String domainName) {
return new AutoValue_ThreatMatch(threatType, domainName);
return new ThreatMatch(threatType, domainName);
}
/** Returns a {@link JSONObject} representing a subset of this object's data. */
@@ -49,7 +47,7 @@ public abstract class ThreatMatch implements Serializable {
public static ThreatMatch fromJSON(JSONObject threatMatch) throws JSONException {
// TODO: delete OUTDATED_NAME_FIELD once we no longer process reports saved with
// fullyQualifiedDomainName in them, likely 2023
return new AutoValue_ThreatMatch(
return new ThreatMatch(
threatMatch.getString(THREAT_TYPE_FIELD),
threatMatch.has(OUTDATED_NAME_FIELD)
? threatMatch.getString(OUTDATED_NAME_FIELD)
@@ -62,7 +62,7 @@ class BsaDiffCreator {
this.gcsClient = gcsClient;
}
private <K, V extends Comparable> Multimap<K, V> listBackedMultiMap() {
private <K, V extends Comparable<?>> Multimap<K, V> listBackedMultiMap() {
return newListMultimap(newHashMap(), Lists::newArrayList);
}
@@ -80,7 +80,7 @@ public class IdnChecker {
}
private static ImmutableMap<IdnTableEnum, ImmutableSet<Tld>> getIdnToTldMap(DateTime now) {
ImmutableMultimap.Builder<IdnTableEnum, Tld> idnToTldMap = new ImmutableMultimap.Builder();
var idnToTldMap = new ImmutableMultimap.Builder<IdnTableEnum, Tld>();
Tlds.getTldEntitiesOfType(TldType.REAL).stream()
.filter(tld -> isEnrolledWithBsa(tld, now))
.forEach(
@@ -34,7 +34,7 @@ public record BlockOrder(long orderId, OrderType orderType) {
public static BlockOrder deserialize(String text) {
List<String> items = SPLITTER.splitToList(text);
try {
return create(Long.valueOf(items.get(0)), OrderType.valueOf(items.get(1)));
return create(Long.parseLong(items.get(0)), OrderType.valueOf(items.get(1)));
} catch (NumberFormatException ne) {
throw new IllegalArgumentException(text);
}
@@ -96,10 +96,9 @@ class BsaDomainRefresh {
if (this == o) {
return true;
}
if (!(o instanceof BsaDomainRefresh)) {
if (!(o instanceof BsaDomainRefresh that)) {
return false;
}
BsaDomainRefresh that = (BsaDomainRefresh) o;
return Objects.equal(jobId, that.jobId)
&& Objects.equal(creationTime, that.creationTime)
&& Objects.equal(updateTime, that.updateTime)
@@ -120,10 +120,9 @@ class BsaDownload {
if (this == o) {
return true;
}
if (!(o instanceof BsaDownload)) {
if (!(o instanceof BsaDownload that)) {
return false;
}
BsaDownload that = (BsaDownload) o;
return Objects.equal(creationTime, that.creationTime)
&& Objects.equal(updateTime, that.updateTime)
&& Objects.equal(blockListChecksums, that.blockListChecksums)
@@ -136,6 +135,6 @@ class BsaDownload {
}
static VKey<BsaDownload> vKey(long jobId) {
return VKey.create(BsaDownload.class, Long.valueOf(jobId));
return VKey.create(BsaDownload.class, jobId);
}
}
@@ -61,10 +61,9 @@ final class BsaLabel {
if (this == o) {
return true;
}
if (!(o instanceof BsaLabel)) {
if (!(o instanceof BsaLabel label1)) {
return false;
}
BsaLabel label1 = (BsaLabel) o;
return Objects.equal(label, label1.label) && Objects.equal(creationTime, label1.creationTime);
}
@@ -84,10 +84,9 @@ class BsaUnblockableDomain {
if (this == o) {
return true;
}
if (!(o instanceof BsaUnblockableDomain)) {
if (!(o instanceof BsaUnblockableDomain that)) {
return false;
}
BsaUnblockableDomain that = (BsaUnblockableDomain) o;
return Objects.equal(label, that.label)
&& Objects.equal(tld, that.tld)
&& reason == that.reason
@@ -142,10 +141,9 @@ class BsaUnblockableDomain {
if (this == o) {
return true;
}
if (!(o instanceof BsaUnblockableDomainId)) {
if (!(o instanceof BsaUnblockableDomainId that)) {
return false;
}
BsaUnblockableDomainId that = (BsaUnblockableDomainId) o;
return Objects.equal(label, that.label) && Objects.equal(tld, that.tld);
}
@@ -72,7 +72,7 @@ public final class LabelDiffUpdates {
for (Map.Entry<LabelType, ImmutableList<BlockLabel>> entry :
labelsByType.entrySet()) {
switch (entry.getKey()) {
case CREATE:
case CREATE -> {
// With current Cloud SQL, label upsert throughput is about 200/second. If
// better performance is needed, consider bulk insert in native SQL.
tm().putAll(
@@ -86,8 +86,8 @@ public final class LabelDiffUpdates {
// cached BsaLabels. Eventually will be consistent.
nonBlockedDomains.addAll(
tallyUnblockableDomainsForNewLabels(entry.getValue(), idnChecker, now));
break;
case DELETE:
}
case DELETE -> {
ImmutableSet<String> deletedLabels =
entry.getValue().stream()
.filter(label -> isValidInAtLeastOneTld(label, idnChecker))
@@ -100,8 +100,8 @@ public final class LabelDiffUpdates {
"Only found %s entities among the %s labels: [%s]",
nDeleted, deletedLabels.size(), deletedLabels);
}
break;
case NEW_ORDER_ASSOCIATION:
}
case NEW_ORDER_ASSOCIATION -> {
ImmutableSet<String> affectedLabels =
entry.getValue().stream()
.filter(label -> isValidInAtLeastOneTld(label, idnChecker))
@@ -120,13 +120,12 @@ public final class LabelDiffUpdates {
Queries.queryBsaUnblockableDomainByLabels(affectedLabels)
.map(BsaUnblockableDomain::toUnblockableDomain)
.forEach(nonBlockedDomains::add);
for (BlockLabel label : entry.getValue()) {
getInvalidTldsForLabel(label, idnChecker)
.map(tld -> UnblockableDomain.of(label.label(), tld, Reason.INVALID))
.forEach(nonBlockedDomains::add);
}
break;
}
}
}
});
@@ -103,7 +103,7 @@ public final class Queries {
static ImmutableList<BsaUnblockableDomain> batchReadUnblockables(
Optional<BsaUnblockableDomain> lastRead, int batchSize) {
return ImmutableList.copyOf(
return ImmutableList.<BsaUnblockableDomain>copyOf(
bsaQuery(
() ->
tm().getEntityManager()
@@ -203,6 +203,17 @@ public final class RegistryConfig {
return config.registrarConsole.announcementsEmailAddress;
}
/**
* The DUM file name, used as a file name base for DUM csv file
*
* @see google.registry.ui.server.console.ConsoleDumDownloadAction
*/
@Provides
@Config("dumFileName")
public static String provideDumFileName(RegistryConfigSettings config) {
return config.registrarConsole.dumFileName;
}
/**
* The contact phone number. Used in the "contact-us" section of the registrar console.
*
@@ -185,6 +185,7 @@ public class RegistryConfigSettings {
/** Configuration for the web-based registrar console. */
public static class RegistrarConsole {
public String dumFileName;
public String logoFilename;
public String supportPhoneNumber;
public String supportEmailAddress;
@@ -396,6 +396,9 @@ rde:
sshIdentityEmailAddress: rde@example.com
registrarConsole:
# DUM download file name, excluding the extension
dumFileName: dum_file_name
# Filename of the logo to use in the header of the console. This filename is
# relative to ui/assets/images/
logoFilename: logo.png
@@ -15,6 +15,7 @@
package google.registry.dns;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.collect.ImmutableCollection;
@@ -98,7 +99,9 @@ public final class DnsUtils {
*/
public static ImmutableList<DnsRefreshRequest> readAndUpdateRequestsWithLatestProcessTime(
String tld, Duration cooldown, int batchSize) {
// It is critical that below query use repeatable-read. See b/337894387.
return tm().transact(
TRANSACTION_REPEATABLE_READ,
() -> {
DateTime transactionTime = tm().getTransactionTime();
ImmutableList<DnsRefreshRequest> requests =
@@ -131,7 +134,9 @@ public final class DnsUtils {
* error because all we care about is that it no longer exists after the method runs.
*/
public static void deleteRequests(Collection<DnsRefreshRequest> requests) {
// It is critical that below query use repeatable-read. See b/337894387.
tm().transact(
TRANSACTION_REPEATABLE_READ,
() ->
tm().delete(
requests.stream()
@@ -38,7 +38,7 @@ public final class DnsWriterProxy {
/**
* Returns the instance of {@link DnsWriter} by class name.
*
* If the DnsWriter doesn't belong to this TLD, will return null.
* <p>If the DnsWriter doesn't belong to this TLD, will return null.
*/
public DnsWriter getByClassNameForTld(String className, String tld) {
if (!Tld.get(tld).getDnsWriters().contains(className)) {
@@ -50,6 +50,7 @@ import google.registry.model.domain.Domain;
import google.registry.model.host.Host;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.registrar.RegistrarPocBase;
import google.registry.model.tld.Tld;
import google.registry.request.Action;
import google.registry.request.Action.Service;
@@ -295,7 +296,7 @@ public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
ImmutableList<InternetAddress> recipients =
registrar.get().getContacts().stream()
.filter(c -> c.getTypes().contains(RegistrarPoc.Type.ADMIN))
.filter(c -> c.getTypes().contains(RegistrarPocBase.Type.ADMIN))
.map(RegistrarPoc::getEmailAddress)
.map(PublishDnsUpdatesAction::emailToInternetAddress)
.collect(toImmutableList());
@@ -63,16 +63,15 @@ public final class RefreshDnsAction implements Runnable {
tm().transact(
() -> {
switch (type) {
case DOMAIN:
case DOMAIN -> {
loadAndVerifyExistence(Domain.class, domainOrHostName);
requestDomainDnsRefresh(domainOrHostName);
break;
case HOST:
}
case HOST -> {
verifyHostIsSubordinate(loadAndVerifyExistence(Host.class, domainOrHostName));
requestHostDnsRefresh(domainOrHostName);
break;
default:
throw new BadRequestException("Unsupported type: " + type);
}
default -> throw new BadRequestException("Unsupported type: " + type);
}
});
}
@@ -55,14 +55,14 @@ import org.xbill.DNS.Type;
import org.xbill.DNS.Update;
/**
* A DnsWriter that implements the DNS UPDATE protocol as specified in
* <a href="https://tools.ietf.org/html/rfc2136">RFC 2136</a>. Publishes changes in the
* domain-registry to a (capable) external DNS server, sometimes called a "hidden master". DNS
* UPDATE messages are sent via a supplied "transport" class.
* A DnsWriter that implements the DNS UPDATE protocol as specified in <a
* href="https://tools.ietf.org/html/rfc2136">RFC 2136</a>. Publishes changes in the domain-registry
* to a (capable) external DNS server, sometimes called a "hidden master". DNS UPDATE messages are
* sent via a supplied "transport" class.
*
* On call to {@link #commit()}, a single UPDATE message is created containing the records required
* to "synchronize" the DNS with the current (at the time of processing) state of the registry, for
* the supplied domain/host.
* <p>On call to {@link #commit()}, a single UPDATE message is created containing the records
* required to "synchronize" the DNS with the current (at the time of processing) state of the
* registry, for the supplied domain/host.
*
* <p>The general strategy of the publish methods is to delete <em>all</em> resource records of any
* <em>type</em> that match the exact domain/host name supplied. And then for create/update cases,
@@ -73,8 +73,8 @@ import org.xbill.DNS.Update;
* <p>Only NS, DS, A, and AAAA records are published, and in particular no DNSSEC signing is done
* assuming that this will be done by a third party DNS provider.
*
* <p>Each commit call is treated as an atomic update to the DNS. If a commit fails an exception
* is thrown. The SOA record serial number is implicitly incremented by the server on each UPDATE
* <p>Each commit call is treated as an atomic update to the DNS. If a commit fails an exception is
* thrown. The SOA record serial number is implicitly incremented by the server on each UPDATE
* message, as required by RFC 2136. Care must be taken to make sure the SOA serial number does not
* go backwards if the entire TLD (zone) is "reset" to empty and republished.
*/
@@ -33,6 +33,7 @@ import google.registry.groups.GroupsConnection;
import google.registry.groups.GroupsConnection.Role;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.registrar.RegistrarPocBase;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
@@ -99,7 +100,7 @@ public final class SyncGroupMembersAction implements Runnable {
* Returns the Google Groups email address for the given registrar ID and RegistrarContact.Type.
*/
public static String getGroupEmailAddressForContactType(
String registrarId, RegistrarPoc.Type type, String gSuiteDomainName) {
String registrarId, RegistrarPocBase.Type type, String gSuiteDomainName) {
// Take the registrar's ID, make it lowercase, and remove all characters that aren't
// alphanumeric, hyphens, or underscores.
return String.format(
@@ -174,7 +175,7 @@ public final class SyncGroupMembersAction implements Runnable {
Set<RegistrarPoc> registrarPocs = registrar.getContacts();
long totalAdded = 0;
long totalRemoved = 0;
for (final RegistrarPoc.Type type : RegistrarPoc.Type.values()) {
for (final RegistrarPocBase.Type type : RegistrarPocBase.Type.values()) {
groupKey =
getGroupEmailAddressForContactType(registrar.getRegistrarId(), type, gSuiteDomainName);
Set<String> currentMembers = groupsConnection.getMembersOfGroup(groupKey);
@@ -17,13 +17,13 @@ package google.registry.export.sheet;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.model.common.Cursor.CursorType.SYNC_REGISTRAR_SHEET;
import static google.registry.model.registrar.RegistrarPoc.Type.ABUSE;
import static google.registry.model.registrar.RegistrarPoc.Type.ADMIN;
import static google.registry.model.registrar.RegistrarPoc.Type.BILLING;
import static google.registry.model.registrar.RegistrarPoc.Type.LEGAL;
import static google.registry.model.registrar.RegistrarPoc.Type.MARKETING;
import static google.registry.model.registrar.RegistrarPoc.Type.TECH;
import static google.registry.model.registrar.RegistrarPoc.Type.WHOIS;
import static google.registry.model.registrar.RegistrarPocBase.Type.ABUSE;
import static google.registry.model.registrar.RegistrarPocBase.Type.ADMIN;
import static google.registry.model.registrar.RegistrarPocBase.Type.BILLING;
import static google.registry.model.registrar.RegistrarPocBase.Type.LEGAL;
import static google.registry.model.registrar.RegistrarPocBase.Type.MARKETING;
import static google.registry.model.registrar.RegistrarPocBase.Type.TECH;
import static google.registry.model.registrar.RegistrarPocBase.Type.WHOIS;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
@@ -36,6 +36,7 @@ import google.registry.model.common.Cursor;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.registrar.RegistrarPocBase;
import google.registry.util.Clock;
import google.registry.util.DateTimeUtils;
import java.io.IOException;
@@ -173,7 +174,7 @@ class SyncRegistrarsSheet {
return result.toString();
}
private static Predicate<RegistrarPoc> byType(final RegistrarPoc.Type type) {
private static Predicate<RegistrarPoc> byType(final RegistrarPocBase.Type type) {
return contact -> contact.getTypes().contains(type);
}
@@ -22,9 +22,9 @@ public interface TransportCredentials {
/**
* Check that these credentials are valid for the registrar and optionally check the password.
*
* Called by {@link google.registry.flows.session.LoginFlow LoginFlow} to check the transport
* credentials against the stored registrar's credentials. If they do not match, throw an
* {@link AuthenticationErrorException}.
* <p>Called by {@link google.registry.flows.session.LoginFlow LoginFlow} to check the transport
* credentials against the stored registrar's credentials. If they do not match, throw an {@link
* AuthenticationErrorException}.
*/
void validate(Registrar registrar, String password) throws AuthenticationErrorException;
@@ -16,12 +16,10 @@ package google.registry.flows.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
import static com.google.common.collect.Iterables.any;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.intersection;
import static com.google.common.collect.Sets.union;
@@ -95,6 +93,7 @@ import google.registry.model.domain.DomainCommand.Update;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.ForeignKeyedDesignatedContact;
import google.registry.model.domain.Period;
import google.registry.model.domain.Period.Unit;
import google.registry.model.domain.fee.BaseFee;
import google.registry.model.domain.fee.BaseFee.FeeType;
import google.registry.model.domain.fee.Credit;
@@ -121,9 +120,9 @@ import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
import google.registry.model.eppcommon.StatusValue;
import google.registry.model.eppoutput.EppResponse.ResponseExtension;
import google.registry.model.host.Host;
import google.registry.model.poll.PollMessage;
import google.registry.model.poll.PollMessage.Autorenew;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State;
import google.registry.model.registrar.RegistrarBase.State;
import google.registry.model.reporting.DomainTransactionRecord;
import google.registry.model.reporting.DomainTransactionRecord.TransactionReportField;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
@@ -206,7 +205,7 @@ public class DomainFlowUtils {
if (parts.size() <= 1) {
throw new BadDomainNamePartsCountException();
}
if (any(parts, equalTo(""))) {
if (parts.stream().anyMatch(String::isEmpty)) {
throw new EmptyDomainNamePartException();
}
validateFirstLabel(parts.get(0));
@@ -329,7 +328,7 @@ public class DomainFlowUtils {
/** Check if the registrar running the flow has access to the TLD in question. */
public static void checkAllowedAccessToTld(String registrarId, String tld) throws EppException {
if (!Registrar.loadByRegistrarIdCached(registrarId).get().getAllowedTlds().contains(tld)) {
throw new DomainFlowUtils.NotAuthorizedForTldException(tld);
throw new NotAuthorizedForTldException(tld);
}
}
@@ -344,7 +343,7 @@ public class DomainFlowUtils {
.get()
.getBillingAccountMap()
.containsKey(tld.getCurrency())) {
throw new DomainFlowUtils.MissingBillingAccountMapException(tld.getCurrency());
throw new MissingBillingAccountMapException(tld.getCurrency());
}
}
@@ -405,7 +404,7 @@ public class DomainFlowUtils {
/** We only allow specifying years in a period. */
static Period verifyUnitIsYears(Period period) throws EppException {
if (!checkNotNull(period).getUnit().equals(Period.Unit.YEARS)) {
if (!checkNotNull(period).getUnit().equals(Unit.YEARS)) {
throw new BadPeriodUnitException();
}
return period;
@@ -534,7 +533,7 @@ public class DomainFlowUtils {
public static boolean isReserved(InternetDomainName domainName, boolean isSunrise) {
ImmutableSet<ReservationType> types = getReservationTypes(domainName);
return !Sets.intersection(types, RESERVED_TYPES).isEmpty()
return !intersection(types, RESERVED_TYPES).isEmpty()
|| !(isSunrise || intersection(TYPES_ALLOWED_FOR_CREATE_ONLY_IN_SUNRISE, types).isEmpty());
}
@@ -601,8 +600,8 @@ public class DomainFlowUtils {
* Fills in a builder with the data needed for an autorenew poll message for this domain. This
* does not copy over the id of the current autorenew poll message.
*/
public static PollMessage.Autorenew.Builder newAutorenewPollMessage(Domain domain) {
return new PollMessage.Autorenew.Builder()
public static Autorenew.Builder newAutorenewPollMessage(Domain domain) {
return new Autorenew.Builder()
.setTargetId(domain.getDomainName())
.setRegistrarId(domain.getCurrentSponsorRegistrarId())
.setEventTime(domain.getRegistrationExpirationTime())
@@ -623,7 +622,7 @@ public class DomainFlowUtils {
BillingRecurrence existingBillingRecurrence,
DateTime newEndTime,
@Nullable HistoryEntryId historyId) {
Optional<PollMessage.Autorenew> autorenewPollMessage =
Optional<Autorenew> autorenewPollMessage =
tm().loadByKeyIfPresent(domain.getAutorenewPollMessage());
// Construct an updated autorenew poll message. If the autorenew poll message no longer exists,
@@ -632,7 +631,7 @@ public class DomainFlowUtils {
// message to be deleted), and then subsequently the transfer was canceled, rejected, or deleted
// (which would cause the poll message to be recreated here). In the latter case, the history id
// of the event that created the new poll message will also be used.
PollMessage.Autorenew updatedAutorenewPollMessage;
Autorenew updatedAutorenewPollMessage;
if (autorenewPollMessage.isPresent()) {
updatedAutorenewPollMessage =
autorenewPollMessage.get().asBuilder().setAutorenewEndTime(newEndTime).build();
@@ -706,7 +705,7 @@ public class DomainFlowUtils {
String feeClass = null;
ImmutableList<Fee> fees = ImmutableList.of();
switch (feeRequest.getCommandName()) {
case CREATE:
case CREATE -> {
// Don't return a create price for reserved names.
if (isReserved(domainName, isSunrise) && !isAvailable) {
feeClass = "reserved";
@@ -726,16 +725,16 @@ public class DomainFlowUtils {
allocationToken)
.getFees();
}
break;
case RENEW:
}
case RENEW -> {
builder.setAvailIfSupported(true);
fees =
pricingLogic
.getRenewPrice(
tld, domainNameString, now, years, billingRecurrence, allocationToken)
.getFees();
break;
case RESTORE:
}
case RESTORE -> {
// The minimum allowable period per the EPP spec is 1, so, strangely, 1 year still has to be
// passed in as the period for a restore even if the domain would *not* be renewed as part
// of a restore. This is fixed in RFC 8748 (which is a more recent version of the fee
@@ -751,21 +750,20 @@ public class DomainFlowUtils {
boolean isExpired =
domain.isPresent() && domain.get().getRegistrationExpirationTime().isBefore(now);
fees = pricingLogic.getRestorePrice(tld, domainNameString, now, isExpired).getFees();
break;
case TRANSFER:
}
case TRANSFER -> {
if (years != 1) {
throw new TransfersAreAlwaysForOneYearException();
}
builder.setAvailIfSupported(true);
fees =
pricingLogic.getTransferPrice(tld, domainNameString, now, billingRecurrence).getFees();
break;
case UPDATE:
}
case UPDATE -> {
builder.setAvailIfSupported(true);
fees = pricingLogic.getUpdatePrice(tld, domainNameString, now).getFees();
break;
default:
throw new UnknownFeeCommandException(feeRequest.getUnparsedCommandName());
}
default -> throw new UnknownFeeCommandException(feeRequest.getUnparsedCommandName());
}
if (feeClass == null) {
@@ -138,14 +138,14 @@ public final class DomainPricingLogic {
isRenewCostPremiumPrice = domainPrices.isPremium();
} else {
switch (billingRecurrence.getRenewalPriceBehavior()) {
case DEFAULT:
case DEFAULT -> {
renewCost =
getDomainRenewCostWithDiscount(tld, domainPrices, dateTime, years, allocationToken);
isRenewCostPremiumPrice = domainPrices.isPremium();
break;
}
// if the renewal price behavior is specified, then the renewal price should be the same
// as the creation price, which is stored in the billing event as the renewal price
case SPECIFIED:
case SPECIFIED -> {
checkArgumentPresent(
billingRecurrence.getRenewalPrice(),
"Unexpected behavior: renewal price cannot be null when renewal behavior is"
@@ -153,20 +153,20 @@ public final class DomainPricingLogic {
// Don't apply allocation token to renewal price when SPECIFIED
renewCost = billingRecurrence.getRenewalPrice().get().multipliedBy(years);
isRenewCostPremiumPrice = false;
break;
}
// if the renewal price behavior is nonpremium, it means that the domain should be renewed
// at standard price of domains at the time, even if the domain is premium
case NONPREMIUM:
case NONPREMIUM -> {
renewCost =
getDomainCostWithDiscount(
false, years, allocationToken, tld.getStandardRenewCost(dateTime));
isRenewCostPremiumPrice = false;
break;
default:
throw new IllegalArgumentException(
String.format(
"Unknown RenewalPriceBehavior enum value: %s",
billingRecurrence.getRenewalPriceBehavior()));
}
default ->
throw new IllegalArgumentException(
String.format(
"Unknown RenewalPriceBehavior enum value: %s",
billingRecurrence.getRenewalPriceBehavior()));
}
}
return customLogic.customizeRenewPrice(
@@ -124,22 +124,21 @@ public class FlowPicker {
}};
/** Poll flows have an {@link InnerCommand} of type {@link Poll}. */
private static final FlowProvider POLL_FLOW_PROVIDER = new FlowProvider() {
@Override
Class<? extends Flow> get(
EppInput eppInput, InnerCommand innerCommand, ResourceCommand resourceCommand) {
if (!(innerCommand instanceof Poll)) {
return null;
}
switch (((Poll) innerCommand).getPollOp()) {
case ACK:
return PollAckFlow.class;
case REQUEST:
return PollRequestFlow.class;
default:
return UnimplementedFlow.class;
}
}};
private static final FlowProvider POLL_FLOW_PROVIDER =
new FlowProvider() {
@Override
Class<? extends Flow> get(
EppInput eppInput, InnerCommand innerCommand, ResourceCommand resourceCommand) {
if (!(innerCommand instanceof Poll)) {
return null;
}
return switch (((Poll) innerCommand).getPollOp()) {
case ACK -> PollAckFlow.class;
case REQUEST -> PollRequestFlow.class;
default -> UnimplementedFlow.class;
};
}
};
/**
* The domain restore command is technically a domain {@literal <update>}, but logically a totally
@@ -54,8 +54,7 @@ public final class PollFlowUtils {
if (pollMessage instanceof PollMessage.OneTime) {
// One-time poll messages are deleted once acked.
tm().delete(pollMessage.createVKey());
} else if (pollMessage instanceof PollMessage.Autorenew) {
PollMessage.Autorenew autorenewPollMessage = (PollMessage.Autorenew) pollMessage;
} else if (pollMessage instanceof PollMessage.Autorenew autorenewPollMessage) {
// Move the eventTime of this autorenew poll message forward by a year.
DateTime nextEventTime = autorenewPollMessage.getEventTime().plusYears(1);
@@ -130,23 +130,22 @@ public final class PgpHelper {
while (keys.hasNext()) {
PGPPublicKey key = keys.next();
switch (want) {
case ENCRYPT:
case ENCRYPT -> {
if (key.isEncryptionKey()) {
return Optional.of(key);
}
break;
case SIGN:
}
case SIGN -> {
if (isSigningKey(key)) {
return Optional.of(key);
}
break;
case ENCRYPT_SIGN:
}
case ENCRYPT_SIGN -> {
if (key.isEncryptionKey() && isSigningKey(key)) {
return Optional.of(key);
}
break;
default:
throw new AssertionError();
}
default -> throw new AssertionError();
}
}
return Optional.empty();
@@ -154,14 +153,9 @@ public final class PgpHelper {
/** Returns {@code true} if this key can be used for signing. */
public static boolean isSigningKey(PGPPublicKey key) {
switch (key.getAlgorithm()) {
case RSA_GENERAL:
case RSA_SIGN:
case DSA:
case ELGAMAL_GENERAL:
return true;
default:
return false;
}
return switch (key.getAlgorithm()) {
case RSA_GENERAL, RSA_SIGN, DSA, ELGAMAL_GENERAL -> true;
default -> false;
};
}
}
@@ -109,16 +109,18 @@ public class ModelUtils {
if (value != null && value.getClass().isArray()) {
// It's surprisingly difficult to convert arrays into lists if the array might be primitive.
final Object arrayValue = value;
value = new AbstractList<Object>() {
@Override
public Object get(int index) {
return Array.get(arrayValue, index);
}
value =
new AbstractList<>() {
@Override
public Object get(int index) {
return Array.get(arrayValue, index);
}
@Override
public int size() {
return Array.getLength(arrayValue);
}};
@Override
public int size() {
return Array.getLength(arrayValue);
}
};
}
values.put(field, value);
}
@@ -0,0 +1,99 @@
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.console;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.reporting.HistoryEntry;
import google.registry.model.reporting.HistoryEntry.HistoryEntryId;
import google.registry.persistence.VKey;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.Table;
/**
* A persisted history object representing an EPP action via the console.
*
* <p>In addition to the generic history fields (time, URL, etc.) we also persist a reference to the
* history entry so that we can refer to it if necessary.
*/
@Access(AccessType.FIELD)
@Entity
@Table(
indexes = {
@Index(columnList = "historyActingUser"),
@Index(columnList = "repoId"),
@Index(columnList = "revisionId")
})
public class ConsoleEppActionHistory extends ConsoleUpdateHistory {
@AttributeOverride(name = "repoId", column = @Column(nullable = false))
HistoryEntryId historyEntryId;
@Column(nullable = false)
Class<? extends HistoryEntry> historyEntryClass;
public HistoryEntryId getHistoryEntryId() {
return historyEntryId;
}
public Class<? extends HistoryEntry> getHistoryEntryClass() {
return historyEntryClass;
}
/** Creates a {@link VKey} instance for this entity. */
@Override
public VKey<ConsoleEppActionHistory> createVKey() {
return VKey.create(ConsoleEppActionHistory.class, getRevisionId());
}
@Override
public Builder asBuilder() {
return new Builder(clone(this));
}
/** Builder for the immutable UserUpdateHistory. */
public static class Builder
extends ConsoleUpdateHistory.Builder<ConsoleEppActionHistory, Builder> {
public Builder() {}
public Builder(ConsoleEppActionHistory instance) {
super(instance);
}
@Override
public ConsoleEppActionHistory build() {
checkArgumentNotNull(getInstance().historyEntryId, "History entry ID must be specified");
checkArgumentNotNull(
getInstance().historyEntryClass, "History entry class must be specified");
return super.build();
}
public Builder setHistoryEntryId(HistoryEntryId historyEntryId) {
getInstance().historyEntryId = historyEntryId;
return this;
}
public Builder setHistoryEntryClass(Class<? extends HistoryEntry> historyEntryClass) {
getInstance().historyEntryClass = historyEntryClass;
return this;
}
}
}
@@ -0,0 +1,168 @@
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.console;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.annotations.IdAllocation;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import org.joda.time.DateTime;
/**
* A record of a resource that was updated through the console.
*
* <p>This abstract class has several subclasses that (mostly) include the modified resource itself
* so that the entire object history is persisted to SQL.
*/
@Access(AccessType.FIELD)
@MappedSuperclass
public abstract class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
public enum Type {
EPP_ACTION,
POC_CREATE,
POC_UPDATE,
POC_DELETE,
REGISTRAR_UPDATE,
USER_CREATE,
USER_DELETE,
USER_UPDATE
}
/** Autogenerated ID of this event. */
@Id
@IdAllocation
@Column(nullable = false, name = "historyRevisionId")
protected Long revisionId;
/** The user that performed the modification. */
@JoinColumn(name = "historyActingUser", referencedColumnName = "emailAddress", nullable = false)
@ManyToOne
User actingUser;
/** The URL of the action that was used to make the modification. */
@Column(nullable = false, name = "historyUrl")
String url;
/** The HTTP method (e.g. POST, PUT) used to make this modification. */
@Column(nullable = false, name = "historyMethod")
String method;
/** The raw body of the request that was used to make this modification. */
@Column(name = "historyRequestBody")
String requestBody;
/** The time at which the modification was mode. */
@Column(nullable = false, name = "historyModificationTime")
DateTime modificationTime;
/** The type of modification. */
@Column(nullable = false, name = "historyType")
@Enumerated(EnumType.STRING)
Type type;
public long getRevisionId() {
return revisionId;
}
public User getActingUser() {
return actingUser;
}
public String getUrl() {
return url;
}
public String getMethod() {
return method;
}
public String getRequestBody() {
return requestBody;
}
public DateTime getModificationTime() {
return modificationTime;
}
public Type getType() {
return type;
}
@Override
public abstract Builder<? extends ConsoleUpdateHistory, ?> asBuilder();
/** Builder for the immutable ConsoleUpdateHistory. */
public abstract static class Builder<
T extends ConsoleUpdateHistory, B extends ConsoleUpdateHistory.Builder<?, ?>>
extends GenericBuilder<T, B> {
protected Builder() {}
protected Builder(T instance) {
super(instance);
}
@Override
public T build() {
checkArgumentNotNull(getInstance().actingUser, "Acting user must be specified");
checkArgumentNotNull(getInstance().url, "URL must be specified");
checkArgumentNotNull(getInstance().method, "HTTP method must be specified");
checkArgumentNotNull(getInstance().modificationTime, "modificationTime must be specified");
checkArgumentNotNull(getInstance().type, "Console History type must be specified");
return super.build();
}
public B setActingUser(User actingUser) {
getInstance().actingUser = actingUser;
return thisCastToDerived();
}
public B setUrl(String url) {
getInstance().url = url;
return thisCastToDerived();
}
public B setMethod(String method) {
getInstance().method = method;
return thisCastToDerived();
}
public B setRequestBody(String requestBody) {
getInstance().requestBody = requestBody;
return thisCastToDerived();
}
public B setModificationTime(DateTime modificationTime) {
getInstance().modificationTime = modificationTime;
return thisCastToDerived();
}
public B setType(Type type) {
getInstance().type = type;
return thisCastToDerived();
}
}
}
@@ -0,0 +1,99 @@
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.console;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.model.registrar.RegistrarPocBase;
import google.registry.persistence.VKey;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.PostLoad;
import javax.persistence.Table;
/**
* A persisted history object representing an update to a RegistrarPoc.
*
* <p>In addition to the generic history fields (time, URL, etc.) we also persist a copy of the
* modified RegistrarPoc object at this point in time.
*/
@Access(AccessType.FIELD)
@Entity
@Table(
indexes = {
@Index(columnList = "historyActingUser"),
@Index(columnList = "emailAddress"),
@Index(columnList = "registrarId")
})
public class RegistrarPocUpdateHistory extends ConsoleUpdateHistory {
RegistrarPocBase registrarPoc;
// These fields exist so that they can be populated in the SQL table
@Column(nullable = false)
String emailAddress;
@Column(nullable = false)
String registrarId;
public RegistrarPocBase getRegistrarPoc() {
return registrarPoc;
}
@PostLoad
void postLoad() {
registrarPoc.setEmailAddress(emailAddress);
registrarPoc.setRegistrarId(registrarId);
}
/** Creates a {@link VKey} instance for this entity. */
@Override
public VKey<RegistrarPocUpdateHistory> createVKey() {
return VKey.create(RegistrarPocUpdateHistory.class, getRevisionId());
}
@Override
public Builder asBuilder() {
return new Builder(clone(this));
}
/** Builder for the immutable UserUpdateHistory. */
public static class Builder
extends ConsoleUpdateHistory.Builder<RegistrarPocUpdateHistory, Builder> {
public Builder() {}
public Builder(RegistrarPocUpdateHistory instance) {
super(instance);
}
@Override
public RegistrarPocUpdateHistory build() {
checkArgumentNotNull(getInstance().registrarPoc, "Registrar POC must be specified");
return super.build();
}
public Builder setRegistrarPoc(RegistrarPoc registrarPoc) {
getInstance().registrarPoc = registrarPoc;
getInstance().registrarId = registrarPoc.getRegistrarId();
getInstance().emailAddress = registrarPoc.getEmailAddress();
return this;
}
}
}
@@ -0,0 +1,89 @@
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.console;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.registrar.RegistrarBase;
import google.registry.persistence.VKey;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.PostLoad;
import javax.persistence.Table;
/**
* A persisted history object representing an update to a Registrar.
*
* <p>In addition to the generic history fields (time, URL, etc.) we also persist a copy of the
* modified Registrar object at this point in time.
*/
@Access(AccessType.FIELD)
@Entity
@Table(indexes = {@Index(columnList = "historyActingUser"), @Index(columnList = "registrarId")})
public class RegistrarUpdateHistory extends ConsoleUpdateHistory {
RegistrarBase registrar;
// This field exists so that it exists in the SQL table
@Column(nullable = false)
@SuppressWarnings("unused")
private String registrarId;
public RegistrarBase getRegistrar() {
return registrar;
}
@PostLoad
void postLoad() {
registrar.setRegistrarId(registrarId);
}
/** Creates a {@link VKey} instance for this entity. */
@Override
public VKey<RegistrarUpdateHistory> createVKey() {
return VKey.create(RegistrarUpdateHistory.class, getRevisionId());
}
@Override
public Builder asBuilder() {
return new RegistrarUpdateHistory.Builder(clone(this));
}
/** Builder for the immutable UserUpdateHistory. */
public static class Builder
extends ConsoleUpdateHistory.Builder<RegistrarUpdateHistory, Builder> {
public Builder() {}
public Builder(RegistrarUpdateHistory instance) {
super(instance);
}
@Override
public RegistrarUpdateHistory build() {
checkArgumentNotNull(getInstance().registrar, "Registrar must be specified");
return super.build();
}
public Builder setRegistrar(RegistrarBase registrar) {
getInstance().registrar = registrar;
getInstance().registrarId = registrar.getRegistrarId();
return this;
}
}
}
@@ -14,19 +14,10 @@
package google.registry.model.console;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.io.BaseEncoding.base64;
import static google.registry.model.registrar.Registrar.checkValidEmail;
import static google.registry.util.PasswordUtils.SALT_SUPPLIER;
import static google.registry.util.PasswordUtils.hashPassword;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.Buildable;
import google.registry.model.UpdateAutoTimestampEntity;
import google.registry.persistence.VKey;
import google.registry.util.PasswordUtils;
import javax.persistence.Column;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
@@ -35,74 +26,17 @@ import javax.persistence.Index;
import javax.persistence.Table;
/** A console user, either a registry employee or a registrar partner. */
@Embeddable
@Entity
@Table(indexes = {@Index(columnList = "emailAddress", name = "user_email_address_idx")})
public class User extends UpdateAutoTimestampEntity implements Buildable {
public class User extends UserBase {
private static final long serialVersionUID = 6936728603828566721L;
/** Autogenerated unique ID of this user. */
@Override
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/** Email address of the user in question. */
@Column(nullable = false)
private String emailAddress;
/** Roles (which grant permissions) associated with this user. */
@Column(nullable = false)
private UserRoles userRoles;
/**
* A hashed password that exists iff this contact is registry-lock-enabled. The hash is a base64
* encoded SHA256 string.
*/
String registryLockPasswordHash;
/** Randomly generated hash salt. */
String registryLockPasswordSalt;
@Access(AccessType.PROPERTY)
public Long getId() {
return id;
}
public String getEmailAddress() {
return emailAddress;
}
public UserRoles getUserRoles() {
return userRoles;
}
public boolean hasRegistryLockPassword() {
return !isNullOrEmpty(registryLockPasswordHash) && !isNullOrEmpty(registryLockPasswordSalt);
}
public boolean verifyRegistryLockPassword(String registryLockPassword) {
if (isNullOrEmpty(registryLockPassword)
|| isNullOrEmpty(registryLockPasswordSalt)
|| isNullOrEmpty(registryLockPasswordHash)) {
return false;
}
return PasswordUtils.verifyPassword(
registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt);
}
/**
* Whether the user has the registry lock permission on any registrar or globally.
*
* <p>If so, they should be allowed to (re)set their registry lock password.
*/
public boolean hasAnyRegistryLockPermission() {
if (userRoles == null) {
return false;
}
if (userRoles.isAdmin() || userRoles.hasGlobalPermission(ConsolePermission.REGISTRY_LOCK)) {
return true;
}
return userRoles.getRegistrarRoles().values().stream()
.anyMatch(role -> role.hasPermission(ConsolePermission.REGISTRY_LOCK));
return super.getId();
}
@Override
@@ -116,49 +50,12 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
}
/** Builder for constructing immutable {@link User} objects. */
public static class Builder extends Buildable.Builder<User> {
public static class Builder extends UserBase.Builder<User, Builder> {
public Builder() {}
public Builder(User user) {
super(user);
}
@Override
public User build() {
checkArgumentNotNull(getInstance().emailAddress, "Email address cannot be null");
checkArgumentNotNull(getInstance().userRoles, "User roles cannot be null");
return super.build();
}
public Builder setEmailAddress(String emailAddress) {
getInstance().emailAddress = checkValidEmail(emailAddress);
return this;
}
public Builder setUserRoles(UserRoles userRoles) {
checkArgumentNotNull(userRoles, "User roles cannot be null");
getInstance().userRoles = userRoles;
return this;
}
public Builder removeRegistryLockPassword() {
getInstance().registryLockPasswordHash = null;
getInstance().registryLockPasswordSalt = null;
return this;
}
public Builder setRegistryLockPassword(String registryLockPassword) {
checkArgument(
getInstance().hasAnyRegistryLockPermission(), "User has no registry lock permission");
checkArgument(
!getInstance().hasRegistryLockPassword(), "User already has a password, remove it first");
checkArgument(
!isNullOrEmpty(registryLockPassword), "Registry lock password was null or empty");
byte[] salt = SALT_SUPPLIER.get();
getInstance().registryLockPasswordSalt = base64().encode(salt);
getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt);
return this;
}
}
}
@@ -0,0 +1,193 @@
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.console;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.io.BaseEncoding.base64;
import static google.registry.model.registrar.Registrar.checkValidEmail;
import static google.registry.util.PasswordUtils.SALT_SUPPLIER;
import static google.registry.util.PasswordUtils.hashPassword;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.model.Buildable;
import google.registry.model.UpdateAutoTimestampEntity;
import google.registry.util.PasswordUtils;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
/**
* A console user, either a registry employee or a registrar partner.
*
* <p>This class deliberately does not include an {@link Id} so that any foreign-keyed fields can
* refer to the proper parent entity's ID, whether we're storing this in the DB itself or as part of
* another entity.
*/
@Access(AccessType.FIELD)
@Embeddable
@MappedSuperclass
public class UserBase extends UpdateAutoTimestampEntity implements Buildable {
private static final long serialVersionUID = 6936728603828566721L;
/** Autogenerated unique ID of this user. */
@Transient private Long id;
/** Email address of the user in question. */
@Column(nullable = false)
String emailAddress;
/** Optional external email address to use for registry lock confirmation emails. */
@Column String registryLockEmailAddress;
/** Roles (which grant permissions) associated with this user. */
@Column(nullable = false)
UserRoles userRoles;
/**
* A hashed password that exists iff this contact is registry-lock-enabled. The hash is a base64
* encoded SHA256 string.
*/
String registryLockPasswordHash;
/** Randomly generated hash salt. */
String registryLockPasswordSalt;
public Long getId() {
return id;
}
/**
* Sets the user ID.
*
* <p>This should only be used for restoring the user id of an object being loaded in a PostLoad
* method (effectively, when it is still under construction by Hibernate). In all other cases, the
* object should be regarded as immutable and changes should go through a Builder.
*
* <p>In addition to this special case use, this method must exist to satisfy Hibernate.
*/
@SuppressWarnings("unused")
void setId(Long id) {
this.id = id;
}
public String getEmailAddress() {
return emailAddress;
}
public Optional<String> getRegistryLockEmailAddress() {
return Optional.ofNullable(registryLockEmailAddress);
}
public UserRoles getUserRoles() {
return userRoles;
}
public boolean hasRegistryLockPassword() {
return !isNullOrEmpty(registryLockPasswordHash) && !isNullOrEmpty(registryLockPasswordSalt);
}
public boolean verifyRegistryLockPassword(String registryLockPassword) {
if (isNullOrEmpty(registryLockPassword)
|| isNullOrEmpty(registryLockPasswordSalt)
|| isNullOrEmpty(registryLockPasswordHash)) {
return false;
}
return PasswordUtils.verifyPassword(
registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt);
}
/**
* Whether the user has the registry lock permission on any registrar or globally.
*
* <p>If so, they should be allowed to (re)set their registry lock password.
*/
public boolean hasAnyRegistryLockPermission() {
if (userRoles == null) {
return false;
}
if (userRoles.isAdmin() || userRoles.hasGlobalPermission(ConsolePermission.REGISTRY_LOCK)) {
return true;
}
return userRoles.getRegistrarRoles().values().stream()
.anyMatch(role -> role.hasPermission(ConsolePermission.REGISTRY_LOCK));
}
@Override
public Builder<? extends UserBase, ?> asBuilder() {
return new Builder<>(clone(this));
}
/** Builder for constructing immutable {@link UserBase} objects. */
public static class Builder<T extends UserBase, B extends Builder<T, B>>
extends GenericBuilder<T, B> {
public Builder() {}
public Builder(T abstractUser) {
super(abstractUser);
}
@Override
public T build() {
checkArgumentNotNull(getInstance().emailAddress, "Email address cannot be null");
checkArgumentNotNull(getInstance().userRoles, "User roles cannot be null");
return super.build();
}
public B setEmailAddress(String emailAddress) {
getInstance().emailAddress = checkValidEmail(emailAddress);
return thisCastToDerived();
}
public B setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
getInstance().registryLockEmailAddress =
registryLockEmailAddress == null ? null : checkValidEmail(registryLockEmailAddress);
return thisCastToDerived();
}
public B setUserRoles(UserRoles userRoles) {
checkArgumentNotNull(userRoles, "User roles cannot be null");
getInstance().userRoles = userRoles;
return thisCastToDerived();
}
public B removeRegistryLockPassword() {
getInstance().registryLockPasswordHash = null;
getInstance().registryLockPasswordSalt = null;
return thisCastToDerived();
}
public B setRegistryLockPassword(String registryLockPassword) {
checkArgument(
getInstance().hasAnyRegistryLockPermission(), "User has no registry lock permission");
checkArgument(
!getInstance().hasRegistryLockPassword(), "User already has a password, remove it first");
checkArgument(
!isNullOrEmpty(registryLockPassword), "Registry lock password was null or empty");
byte[] salt = SALT_SUPPLIER.get();
getInstance().registryLockPasswordSalt = base64().encode(salt);
getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt);
return thisCastToDerived();
}
}
}
@@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableMap;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import java.util.Map;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.EnumType;
@@ -32,6 +34,7 @@ import javax.persistence.Enumerated;
* <p>See <a href="https://go/nomulus-console-authz">go/nomulus-console-authz</a> for more
* information.
*/
@Access(AccessType.FIELD)
@Embeddable
public class UserRoles extends ImmutableObject implements Buildable {
@@ -0,0 +1,86 @@
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.console;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import google.registry.persistence.VKey;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Index;
import javax.persistence.PostLoad;
import javax.persistence.Table;
/**
* A persisted history object representing an update to a User.
*
* <p>In addition to the generic history fields (time, URL, etc.) we also persist a copy of the
* modified User object at this point in time.
*/
@Access(AccessType.FIELD)
@Entity
@Table(indexes = {@Index(columnList = "historyActingUser"), @Index(columnList = "emailAddress")})
public class UserUpdateHistory extends ConsoleUpdateHistory {
UserBase user;
// This field exists so that it's populated in the SQL table
@Column(nullable = false, name = "userId")
Long id;
public UserBase getUser() {
return user;
}
@PostLoad
void postLoad() {
user.setId(id);
}
/** Creates a {@link VKey} instance for this entity. */
@Override
public VKey<UserUpdateHistory> createVKey() {
return VKey.create(UserUpdateHistory.class, getRevisionId());
}
@Override
public Builder asBuilder() {
return new Builder(clone(this));
}
/** Builder for the immutable UserUpdateHistory. */
public static class Builder extends ConsoleUpdateHistory.Builder<UserUpdateHistory, Builder> {
public Builder() {}
public Builder(UserUpdateHistory instance) {
super(instance);
}
@Override
public UserUpdateHistory build() {
checkArgumentNotNull(getInstance().user, "User must be specified");
return super.build();
}
public Builder setUser(User user) {
getInstance().user = user;
getInstance().id = user.getId();
return this;
}
}
}
@@ -61,7 +61,8 @@ import org.joda.time.DateTime;
@Index(columnList = "lordnPhase"),
@Index(columnList = "billing_recurrence_id"),
@Index(columnList = "transfer_billing_event_id"),
@Index(columnList = "transfer_billing_recurrence_id")
@Index(columnList = "transfer_billing_recurrence_id"),
@Index(columnList = "transfer_billing_cancellation_id")
})
@WithVKey(String.class)
@ExternalMessagingName("domain")
@@ -659,22 +659,17 @@ public class DomainBase extends EppResource
contact.getType());
contactsDiscovered.add(contact.getType());
switch (contact.getType()) {
case BILLING:
billingContact = contact.getContactKey();
break;
case TECH:
techContact = contact.getContactKey();
break;
case ADMIN:
adminContact = contact.getContactKey();
break;
case REGISTRANT:
case BILLING -> billingContact = contact.getContactKey();
case TECH -> techContact = contact.getContactKey();
case ADMIN -> adminContact = contact.getContactKey();
case REGISTRANT -> {
if (includeRegistrant) {
registrantContact = contact.getContactKey();
}
break;
default:
throw new IllegalArgumentException("Unknown contact resource type: " + contact.getType());
}
default ->
throw new IllegalArgumentException(
"Unknown contact resource type: " + contact.getType());
}
}
}
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.util.DateTimeUtils.isBeforeOrAt;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import com.google.gson.annotations.Expose;
import google.registry.model.Buildable;
import google.registry.model.CreateAutoTimestamp;
import google.registry.model.UpdateAutoTimestampEntity;
@@ -90,6 +91,7 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
// TODO (b/140568328): remove this when everything is in Cloud SQL and we can join on "domain"
@Column(nullable = false)
@Expose
private String domainName;
/**
@@ -100,7 +102,7 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
private String registrarId;
/** The POC that performed the action, or null if it was a superuser. */
private String registrarPocId;
@Expose private String registrarPocId;
/** When the lock is first requested. */
@AttributeOverrides({
@@ -108,22 +110,23 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
name = "creationTime",
column = @Column(name = "lockRequestTime", nullable = false))
})
@Expose
private final CreateAutoTimestamp lockRequestTime = CreateAutoTimestamp.create(null);
/** When the unlock is first requested. */
private DateTime unlockRequestTime;
@Expose private DateTime unlockRequestTime;
/**
* When the user has verified the lock. If this field is null, it means the lock has not been
* verified yet (and thus not been put into effect).
*/
private DateTime lockCompletionTime;
@Expose private DateTime lockCompletionTime;
/**
* When the user has verified the unlock of this lock. If this field is null, it means the unlock
* action has not been verified yet (and has not been put into effect).
*/
private DateTime unlockCompletionTime;
@Expose private DateTime unlockCompletionTime;
/** The user must provide the random verification code in order to complete the action. */
@Column(nullable = false)
@@ -134,6 +137,7 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
* this case, the action was performed by a registry admin rather than a registrar.
*/
@Column(nullable = false)
@Expose
private boolean isSuperuser;
/** The lock that undoes this lock, if this lock has been unlocked and the domain locked again. */
@@ -103,7 +103,7 @@ public enum GracePeriodStatus implements EppEnum {
/**
* Maps from xmlName to {@link GracePeriodStatus}.
*
* If no match is found for xmlName, null is returned.
* <p>If no match is found for xmlName, null is returned.
*/
@Nullable
public static GracePeriodStatus fromXmlName(String xmlName) {
@@ -34,7 +34,7 @@ public class Result extends ImmutableObject {
* [RFC5321]. EPP uses four decimal digits to describe the success or failure of each EPP command.
* Each of the digits of the reply have special significance."
*
* "The first digit denotes command success or failure. The second digit denotes the response
* <p>"The first digit denotes command success or failure. The second digit denotes the response
* category, such as command syntax or security. The third and fourth digits provide explicit
* response detail within each response category."
*/
@@ -49,6 +49,7 @@ import javax.persistence.Table;
@Index(columnList = "creationTime"),
@Index(columnList = "deletionTime"),
@Index(columnList = "currentSponsorRegistrarId"),
@Index(columnList = "superordinateDomain")
})
@ExternalMessagingName("host")
@WithVKey(String.class)
@@ -529,8 +529,7 @@ public abstract class PollMessage extends ImmutableObject
// Set the identifier according to the TransferResponse type.
if (instance.transferResponse instanceof ContactTransferResponse) {
instance.contactId = ((ContactTransferResponse) instance.transferResponse).getContactId();
} else if (instance.transferResponse instanceof DomainTransferResponse) {
DomainTransferResponse response = (DomainTransferResponse) instance.transferResponse;
} else if (instance.transferResponse instanceof DomainTransferResponse response) {
instance.domainName = response.getDomainName();
instance.extendedRegistrationExpirationTime =
response.getExtendedRegistrationExpirationTime();
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -14,35 +14,14 @@
package google.registry.model.registrar;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.io.BaseEncoding.base64;
import static google.registry.model.registrar.Registrar.checkValidEmail;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy;
import static google.registry.util.PasswordUtils.SALT_SUPPLIER;
import static google.registry.util.PasswordUtils.hashPassword;
import static java.util.stream.Collectors.joining;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.gson.annotations.Expose;
import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.JsonMapBuilder;
import google.registry.model.Jsonifiable;
import google.registry.model.UnsafeSerializable;
import google.registry.model.registrar.RegistrarPoc.RegistrarPocId;
import google.registry.persistence.VKey;
import google.registry.util.PasswordUtils;
import java.io.Serializable;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
@@ -60,258 +39,20 @@ import javax.persistence.Table;
@Entity
@Table(indexes = @Index(columnList = "loginEmailAddress", name = "registrarpoc_login_email_idx"))
@IdClass(RegistrarPocId.class)
public class RegistrarPoc extends ImmutableObject implements Jsonifiable, UnsafeSerializable {
/**
* Registrar contacts types for partner communication tracking.
*
* <p><b>Note:</b> These types only matter to the registry. They are not meant to be used for
* WHOIS or RDAP results.
*/
public enum Type {
ABUSE("abuse", true),
ADMIN("primary", true),
BILLING("billing", true),
LEGAL("legal", true),
MARKETING("marketing", false),
TECH("technical", true),
WHOIS("whois-inquiry", true);
private final String displayName;
private final boolean required;
public String getDisplayName() {
return displayName;
}
public boolean isRequired() {
return required;
}
Type(String display, boolean required) {
displayName = display;
this.required = required;
}
}
/** The name of the contact. */
@Expose String name;
/**
* The contact email address of the contact.
*
* <p>This is different from the login email which is assgined to the regstrar and cannot be
* changed.
*/
@Expose @Id String emailAddress;
@Expose @Id public String registrarId;
/** External email address of this contact used for registry lock confirmations. */
String registryLockEmailAddress;
/** The voice number of the contact. */
@Expose String phoneNumber;
/** The fax number of the contact. */
@Expose String faxNumber;
/**
* Multiple types are used to associate the registrar contact with various mailing groups. This
* data is internal to the registry.
*/
@Expose Set<Type> types;
/** A GAIA email address that was assigned to the registrar for console login purpose. */
String loginEmailAddress;
/**
* Whether this contact is publicly visible in WHOIS registrar query results as an Admin contact.
*/
@Expose boolean visibleInWhoisAsAdmin = false;
/**
* Whether this contact is publicly visible in WHOIS registrar query results as a Technical
* contact.
*/
@Expose boolean visibleInWhoisAsTech = false;
/**
* Whether this contact's phone number and email address is publicly visible in WHOIS domain query
* results as registrar abuse contact info.
*/
@Expose boolean visibleInDomainWhoisAsAbuse = false;
/**
* Whether the contact is allowed to set their registry lock password through the registrar
* console. This will be set to false on contact creation and when the user sets a password.
*/
boolean allowedToSetRegistryLockPassword = false;
/**
* A hashed password that exists iff this contact is registry-lock-enabled. The hash is a base64
* encoded SHA256 string.
*/
String registryLockPasswordHash;
/** Randomly generated hash salt. */
String registryLockPasswordSalt;
/**
* Helper to update the contacts associated with a Registrar. This requires querying for the
* existing contacts, deleting existing contacts that are not part of the given {@code contacts}
* set, and then saving the given {@code contacts}.
*
* <p>IMPORTANT NOTE: If you call this method then it is your responsibility to also persist the
* relevant Registrar entity with the {@link Registrar#contactsRequireSyncing} field set to true.
*/
public static void updateContacts(
final Registrar registrar, final ImmutableSet<RegistrarPoc> contacts) {
tm().transact(
() -> {
ImmutableSet<String> emailAddressesToKeep =
contacts.stream().map(RegistrarPoc::getEmailAddress).collect(toImmutableSet());
tm().query(
"DELETE FROM RegistrarPoc WHERE registrarId = :registrarId AND "
+ "emailAddress NOT IN :emailAddressesToKeep")
.setParameter("registrarId", registrar.getRegistrarId())
.setParameter("emailAddressesToKeep", emailAddressesToKeep)
.executeUpdate();
tm().putAll(contacts);
});
}
public String getName() {
return name;
}
@Access(AccessType.FIELD)
public class RegistrarPoc extends RegistrarPocBase {
@Id
@Access(AccessType.PROPERTY)
@Override
public String getEmailAddress() {
return emailAddress;
}
public Optional<String> getRegistryLockEmailAddress() {
return Optional.ofNullable(registryLockEmailAddress);
}
public String getPhoneNumber() {
return phoneNumber;
}
public String getFaxNumber() {
return faxNumber;
}
public ImmutableSortedSet<Type> getTypes() {
return nullToEmptyImmutableSortedCopy(types);
}
public boolean getVisibleInWhoisAsAdmin() {
return visibleInWhoisAsAdmin;
}
public boolean getVisibleInWhoisAsTech() {
return visibleInWhoisAsTech;
}
public boolean getVisibleInDomainWhoisAsAbuse() {
return visibleInDomainWhoisAsAbuse;
}
public String getLoginEmailAddress() {
return loginEmailAddress;
}
public Builder asBuilder() {
return new Builder(clone(this));
}
public boolean isAllowedToSetRegistryLockPassword() {
return allowedToSetRegistryLockPassword;
}
public boolean isRegistryLockAllowed() {
return !isNullOrEmpty(registryLockPasswordHash) && !isNullOrEmpty(registryLockPasswordSalt);
}
public boolean verifyRegistryLockPassword(String registryLockPassword) {
if (isNullOrEmpty(registryLockPassword)
|| isNullOrEmpty(registryLockPasswordSalt)
|| isNullOrEmpty(registryLockPasswordHash)) {
return false;
}
return PasswordUtils.verifyPassword(
registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt);
}
/**
* Returns a string representation that's human friendly.
*
* <p>The output will look something like this:
*
* <pre>{@code
* Some Person
* person@example.com
* Tel: +1.2125650666
* Types: [ADMIN, WHOIS]
* Visible in WHOIS as Admin contact: Yes
* Visible in WHOIS as Technical contact: No
* Registrar-Console access: Yes
* Login Email Address: person@registry.example
* }</pre>
*/
public String toStringMultilinePlainText() {
StringBuilder result = new StringBuilder(256);
result.append(getName()).append('\n');
result.append(getEmailAddress()).append('\n');
if (phoneNumber != null) {
result.append("Tel: ").append(getPhoneNumber()).append('\n');
}
if (faxNumber != null) {
result.append("Fax: ").append(getFaxNumber()).append('\n');
}
result.append("Types: ").append(getTypes()).append('\n');
result
.append("Visible in registrar WHOIS query as Admin contact: ")
.append(getVisibleInWhoisAsAdmin() ? "Yes" : "No")
.append('\n');
result
.append("Visible in registrar WHOIS query as Technical contact: ")
.append(getVisibleInWhoisAsTech() ? "Yes" : "No")
.append('\n');
result
.append(
"Phone number and email visible in domain WHOIS query as "
+ "Registrar Abuse contact info: ")
.append(getVisibleInDomainWhoisAsAbuse() ? "Yes" : "No")
.append('\n');
result
.append("Registrar-Console access: ")
.append(getLoginEmailAddress() != null ? "Yes" : "No")
.append('\n');
if (getLoginEmailAddress() != null) {
result.append("Login Email Address: ").append(getLoginEmailAddress()).append('\n');
}
return result.toString();
}
@Override
public Map<String, Object> toJsonMap() {
return new JsonMapBuilder()
.put("name", name)
.put("emailAddress", emailAddress)
.put("registryLockEmailAddress", registryLockEmailAddress)
.put("phoneNumber", phoneNumber)
.put("faxNumber", faxNumber)
.put("types", getTypes().stream().map(Object::toString).collect(joining(",")))
.put("visibleInWhoisAsAdmin", visibleInWhoisAsAdmin)
.put("visibleInWhoisAsTech", visibleInWhoisAsTech)
.put("visibleInDomainWhoisAsAbuse", visibleInDomainWhoisAsAbuse)
.put("allowedToSetRegistryLockPassword", allowedToSetRegistryLockPassword)
.put("registryLockAllowed", isRegistryLockAllowed())
.put("loginEmailAddress", loginEmailAddress)
.build();
@Id
@Access(AccessType.PROPERTY)
public String getRegistrarId() {
return registrarId;
}
@Override
@@ -319,6 +60,11 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
return VKey.create(RegistrarPoc.class, new RegistrarPocId(emailAddress, registrarId));
}
@Override
public Builder asBuilder() {
return new Builder(clone(this));
}
/** Class to represent the composite primary key for {@link RegistrarPoc} entity. */
@VisibleForTesting
public static class RegistrarPocId extends ImmutableObject implements Serializable {
@@ -336,112 +82,24 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
this.emailAddress = emailAddress;
this.registrarId = registrarId;
}
@Id
public String getEmailAddress() {
return emailAddress;
}
@Id
public String getRegistrarId() {
return registrarId;
}
}
/** A builder for constructing a {@link RegistrarPoc}, since it is immutable. */
public static class Builder extends Buildable.Builder<RegistrarPoc> {
public static class Builder extends RegistrarPocBase.Builder<RegistrarPoc, Builder> {
public Builder() {}
private Builder(RegistrarPoc instance) {
super(instance);
}
/** Build the registrar, nullifying empty fields. */
@Override
public RegistrarPoc build() {
checkNotNull(getInstance().registrarId, "Registrar ID cannot be null");
checkValidEmail(getInstance().emailAddress);
// Check allowedToSetRegistryLockPassword here because if we want to allow the user to set
// a registry lock password, we must also set up the correct registry lock email concurrently
// or beforehand.
if (getInstance().allowedToSetRegistryLockPassword) {
checkArgument(
!isNullOrEmpty(getInstance().registryLockEmailAddress),
"Registry lock email must not be null if allowing registry lock access");
}
return cloneEmptyToNull(super.build());
}
public Builder setName(String name) {
getInstance().name = name;
return this;
}
public Builder setEmailAddress(String emailAddress) {
getInstance().emailAddress = emailAddress;
return this;
}
public Builder setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
getInstance().registryLockEmailAddress = registryLockEmailAddress;
return this;
}
public Builder setPhoneNumber(String phoneNumber) {
getInstance().phoneNumber = phoneNumber;
return this;
}
public Builder setRegistrarId(String registrarId) {
getInstance().registrarId = registrarId;
return this;
}
public Builder setRegistrar(Registrar registrar) {
getInstance().registrarId = registrar.getRegistrarId();
return this;
}
public Builder setFaxNumber(String faxNumber) {
getInstance().faxNumber = faxNumber;
return this;
}
public Builder setTypes(Iterable<Type> types) {
getInstance().types = ImmutableSet.copyOf(types);
return this;
}
public Builder setVisibleInWhoisAsAdmin(boolean visible) {
getInstance().visibleInWhoisAsAdmin = visible;
return this;
}
public Builder setVisibleInWhoisAsTech(boolean visible) {
getInstance().visibleInWhoisAsTech = visible;
return this;
}
public Builder setVisibleInDomainWhoisAsAbuse(boolean visible) {
getInstance().visibleInDomainWhoisAsAbuse = visible;
return this;
}
public Builder setLoginEmailAddress(String loginEmailAddress) {
getInstance().loginEmailAddress = loginEmailAddress;
return this;
}
public Builder setAllowedToSetRegistryLockPassword(boolean allowedToSetRegistryLockPassword) {
if (allowedToSetRegistryLockPassword) {
getInstance().registryLockPasswordSalt = null;
getInstance().registryLockPasswordHash = null;
}
getInstance().allowedToSetRegistryLockPassword = allowedToSetRegistryLockPassword;
return this;
}
public Builder setRegistryLockPassword(String registryLockPassword) {
checkArgument(
getInstance().allowedToSetRegistryLockPassword,
"Not allowed to set registry lock password for this contact");
checkArgument(
!isNullOrEmpty(registryLockPassword), "Registry lock password was null or empty");
byte[] salt = SALT_SUPPLIER.get();
getInstance().registryLockPasswordSalt = base64().encode(salt);
getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt);
getInstance().allowedToSetRegistryLockPassword = false;
return this;
public Builder(RegistrarPoc registrarPoc) {
super(registrarPoc);
}
}
}
@@ -0,0 +1,441 @@
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.registrar;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.io.BaseEncoding.base64;
import static google.registry.model.registrar.RegistrarBase.checkValidEmail;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.CollectionUtils.nullToEmptyImmutableSortedCopy;
import static google.registry.util.PasswordUtils.SALT_SUPPLIER;
import static google.registry.util.PasswordUtils.hashPassword;
import static java.util.stream.Collectors.joining;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.gson.annotations.Expose;
import google.registry.model.Buildable.GenericBuilder;
import google.registry.model.ImmutableObject;
import google.registry.model.JsonMapBuilder;
import google.registry.model.Jsonifiable;
import google.registry.model.UnsafeSerializable;
import google.registry.util.PasswordUtils;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Embeddable;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;
import javax.persistence.Transient;
/**
* A contact for a Registrar. Note, equality, hashCode and comparable have been overridden to only
* enable key equality.
*
* <p>IMPORTANT NOTE: Any time that you change, update, or delete RegistrarContact entities, you
* *MUST* also modify the persisted Registrar entity with {@link Registrar#contactsRequireSyncing}
* set to true.
*
* <p>This class deliberately does not include an {@link Id} so that any foreign-keyed fields can
* refer to the proper parent entity's ID, whether we're storing this in the DB itself or as part of
* another entity.
*/
@Access(AccessType.FIELD)
@Embeddable
@MappedSuperclass
public class RegistrarPocBase extends ImmutableObject implements Jsonifiable, UnsafeSerializable {
/**
* Registrar contacts types for partner communication tracking.
*
* <p><b>Note:</b> These types only matter to the registry. They are not meant to be used for
* WHOIS or RDAP results.
*/
public enum Type {
ABUSE("abuse", true),
ADMIN("primary", true),
BILLING("billing", true),
LEGAL("legal", true),
MARKETING("marketing", false),
TECH("technical", true),
WHOIS("whois-inquiry", true);
private final String displayName;
private final boolean required;
public String getDisplayName() {
return displayName;
}
public boolean isRequired() {
return required;
}
Type(String display, boolean required) {
displayName = display;
this.required = required;
}
}
/** The name of the contact. */
@Expose String name;
/**
* The contact email address of the contact.
*
* <p>This is different from the login email which is assgined to the regstrar and cannot be
* changed.
*/
@Expose @Transient String emailAddress;
@Expose @Transient public String registrarId;
/** External email address of this contact used for registry lock confirmations. */
String registryLockEmailAddress;
/** The voice number of the contact. */
@Expose String phoneNumber;
/** The fax number of the contact. */
@Expose String faxNumber;
/**
* Multiple types are used to associate the registrar contact with various mailing groups. This
* data is internal to the registry.
*/
@Expose Set<Type> types;
/** A GAIA email address that was assigned to the registrar for console login purpose. */
String loginEmailAddress;
/**
* Whether this contact is publicly visible in WHOIS registrar query results as an Admin contact.
*/
@Expose boolean visibleInWhoisAsAdmin = false;
/**
* Whether this contact is publicly visible in WHOIS registrar query results as a Technical
* contact.
*/
@Expose boolean visibleInWhoisAsTech = false;
/**
* Whether this contact's phone number and email address is publicly visible in WHOIS domain query
* results as registrar abuse contact info.
*/
@Expose boolean visibleInDomainWhoisAsAbuse = false;
/**
* Whether the contact is allowed to set their registry lock password through the registrar
* console. This will be set to false on contact creation and when the user sets a password.
*/
boolean allowedToSetRegistryLockPassword = false;
/**
* A hashed password that exists iff this contact is registry-lock-enabled. The hash is a base64
* encoded SHA256 string.
*/
String registryLockPasswordHash;
/** Randomly generated hash salt. */
String registryLockPasswordSalt;
/**
* Helper to update the contacts associated with a Registrar. This requires querying for the
* existing contacts, deleting existing contacts that are not part of the given {@code contacts}
* set, and then saving the given {@code contacts}.
*
* <p>IMPORTANT NOTE: If you call this method then it is your responsibility to also persist the
* relevant Registrar entity with the {@link Registrar#contactsRequireSyncing} field set to true.
*/
public static void updateContacts(
final Registrar registrar, final ImmutableSet<RegistrarPoc> contacts) {
tm().transact(
() -> {
ImmutableSet<String> emailAddressesToKeep =
contacts.stream().map(RegistrarPoc::getEmailAddress).collect(toImmutableSet());
tm().query(
"DELETE FROM RegistrarPoc WHERE registrarId = :registrarId AND "
+ "emailAddress NOT IN :emailAddressesToKeep")
.setParameter("registrarId", registrar.getRegistrarId())
.setParameter("emailAddressesToKeep", emailAddressesToKeep)
.executeUpdate();
tm().putAll(contacts);
});
}
public String getName() {
return name;
}
public String getEmailAddress() {
return emailAddress;
}
public Optional<String> getRegistryLockEmailAddress() {
return Optional.ofNullable(registryLockEmailAddress);
}
public String getPhoneNumber() {
return phoneNumber;
}
public String getFaxNumber() {
return faxNumber;
}
public ImmutableSortedSet<Type> getTypes() {
return nullToEmptyImmutableSortedCopy(types);
}
public boolean getVisibleInWhoisAsAdmin() {
return visibleInWhoisAsAdmin;
}
public boolean getVisibleInWhoisAsTech() {
return visibleInWhoisAsTech;
}
public boolean getVisibleInDomainWhoisAsAbuse() {
return visibleInDomainWhoisAsAbuse;
}
public String getLoginEmailAddress() {
return loginEmailAddress;
}
public Builder<? extends RegistrarPocBase, ?> asBuilder() {
return new Builder<>(clone(this));
}
public boolean isAllowedToSetRegistryLockPassword() {
return allowedToSetRegistryLockPassword;
}
public boolean isRegistryLockAllowed() {
return !isNullOrEmpty(registryLockPasswordHash) && !isNullOrEmpty(registryLockPasswordSalt);
}
public boolean verifyRegistryLockPassword(String registryLockPassword) {
if (isNullOrEmpty(registryLockPassword)
|| isNullOrEmpty(registryLockPasswordSalt)
|| isNullOrEmpty(registryLockPasswordHash)) {
return false;
}
return PasswordUtils.verifyPassword(
registryLockPassword, registryLockPasswordHash, registryLockPasswordSalt);
}
/**
* Returns a string representation that's human friendly.
*
* <p>The output will look something like this:
*
* <pre>{@code
* Some Person
* person@example.com
* Tel: +1.2125650666
* Types: [ADMIN, WHOIS]
* Visible in WHOIS as Admin contact: Yes
* Visible in WHOIS as Technical contact: No
* Registrar-Console access: Yes
* Login Email Address: person@registry.example
* }</pre>
*/
public String toStringMultilinePlainText() {
StringBuilder result = new StringBuilder(256);
result.append(getName()).append('\n');
result.append(getEmailAddress()).append('\n');
if (phoneNumber != null) {
result.append("Tel: ").append(getPhoneNumber()).append('\n');
}
if (faxNumber != null) {
result.append("Fax: ").append(getFaxNumber()).append('\n');
}
result.append("Types: ").append(getTypes()).append('\n');
result
.append("Visible in registrar WHOIS query as Admin contact: ")
.append(getVisibleInWhoisAsAdmin() ? "Yes" : "No")
.append('\n');
result
.append("Visible in registrar WHOIS query as Technical contact: ")
.append(getVisibleInWhoisAsTech() ? "Yes" : "No")
.append('\n');
result
.append(
"Phone number and email visible in domain WHOIS query as "
+ "Registrar Abuse contact info: ")
.append(getVisibleInDomainWhoisAsAbuse() ? "Yes" : "No")
.append('\n');
result
.append("Registrar-Console access: ")
.append(getLoginEmailAddress() != null ? "Yes" : "No")
.append('\n');
if (getLoginEmailAddress() != null) {
result.append("Login Email Address: ").append(getLoginEmailAddress()).append('\n');
}
return result.toString();
}
@Override
public Map<String, Object> toJsonMap() {
return new JsonMapBuilder()
.put("name", name)
.put("emailAddress", emailAddress)
.put("registryLockEmailAddress", registryLockEmailAddress)
.put("phoneNumber", phoneNumber)
.put("faxNumber", faxNumber)
.put("types", getTypes().stream().map(Object::toString).collect(joining(",")))
.put("visibleInWhoisAsAdmin", visibleInWhoisAsAdmin)
.put("visibleInWhoisAsTech", visibleInWhoisAsTech)
.put("visibleInDomainWhoisAsAbuse", visibleInDomainWhoisAsAbuse)
.put("allowedToSetRegistryLockPassword", allowedToSetRegistryLockPassword)
.put("registryLockAllowed", isRegistryLockAllowed())
.put("loginEmailAddress", loginEmailAddress)
.build();
}
/**
* These methods set the email address and registrar ID
*
* <p>This should only be used for restoring the fields of an object being loaded in a PostLoad
* method (effectively, when it is still under construction by Hibernate). In all other cases, the
* object should be regarded as immutable and changes should go through a Builder.
*
* <p>In addition to this special case use, this method must exist to satisfy Hibernate.
*/
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public void setRegistrarId(String registrarId) {
this.registrarId = registrarId;
}
/** A builder for constructing a {@link RegistrarPoc}, since it is immutable. */
public static class Builder<T extends RegistrarPocBase, B extends Builder<T, B>>
extends GenericBuilder<T, B> {
public Builder() {}
protected Builder(T instance) {
super(instance);
}
/** Build the registrar, nullifying empty fields. */
@Override
public T build() {
checkNotNull(getInstance().registrarId, "Registrar ID cannot be null");
checkValidEmail(getInstance().emailAddress);
// Check allowedToSetRegistryLockPassword here because if we want to allow the user to set
// a registry lock password, we must also set up the correct registry lock email concurrently
// or beforehand.
if (getInstance().allowedToSetRegistryLockPassword) {
checkArgument(
!isNullOrEmpty(getInstance().registryLockEmailAddress),
"Registry lock email must not be null if allowing registry lock access");
}
return cloneEmptyToNull(super.build());
}
public B setName(String name) {
getInstance().name = name;
return thisCastToDerived();
}
public B setEmailAddress(String emailAddress) {
getInstance().emailAddress = emailAddress;
return thisCastToDerived();
}
public B setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
getInstance().registryLockEmailAddress = registryLockEmailAddress;
return thisCastToDerived();
}
public B setPhoneNumber(String phoneNumber) {
getInstance().phoneNumber = phoneNumber;
return thisCastToDerived();
}
public B setRegistrarId(String registrarId) {
getInstance().registrarId = registrarId;
return thisCastToDerived();
}
public B setRegistrar(Registrar registrar) {
getInstance().registrarId = registrar.getRegistrarId();
return thisCastToDerived();
}
public B setFaxNumber(String faxNumber) {
getInstance().faxNumber = faxNumber;
return thisCastToDerived();
}
public B setTypes(Iterable<Type> types) {
getInstance().types = ImmutableSet.copyOf(types);
return thisCastToDerived();
}
public B setVisibleInWhoisAsAdmin(boolean visible) {
getInstance().visibleInWhoisAsAdmin = visible;
return thisCastToDerived();
}
public B setVisibleInWhoisAsTech(boolean visible) {
getInstance().visibleInWhoisAsTech = visible;
return thisCastToDerived();
}
public B setVisibleInDomainWhoisAsAbuse(boolean visible) {
getInstance().visibleInDomainWhoisAsAbuse = visible;
return thisCastToDerived();
}
public B setLoginEmailAddress(String loginEmailAddress) {
getInstance().loginEmailAddress = loginEmailAddress;
return thisCastToDerived();
}
public B setAllowedToSetRegistryLockPassword(boolean allowedToSetRegistryLockPassword) {
if (allowedToSetRegistryLockPassword) {
getInstance().registryLockPasswordSalt = null;
getInstance().registryLockPasswordHash = null;
}
getInstance().allowedToSetRegistryLockPassword = allowedToSetRegistryLockPassword;
return thisCastToDerived();
}
public B setRegistryLockPassword(String registryLockPassword) {
checkArgument(
getInstance().allowedToSetRegistryLockPassword,
"Not allowed to set registry lock password for this contact");
checkArgument(
!isNullOrEmpty(registryLockPassword), "Registry lock password was null or empty");
byte[] salt = SALT_SUPPLIER.get();
getInstance().registryLockPasswordSalt = base64().encode(salt);
getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt);
getInstance().allowedToSetRegistryLockPassword = false;
return thisCastToDerived();
}
}
}
@@ -458,20 +458,6 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
@JsonDeserialize(using = CurrencyDeserializer.class)
CurrencyUnit currency = DEFAULT_CURRENCY;
// TODO(sarahbot@): Remove this field once all saved configuration files have this field removed
/** The per-year billing cost for registering a new domain name. */
@Deprecated
@JsonIgnore
@Type(type = JodaMoneyType.TYPE_NAME)
@Columns(
columns = {
@Column(name = "create_billing_cost_amount"),
@Column(name = "create_billing_cost_currency")
})
Money createBillingCost = DEFAULT_CREATE_BILLING_COST;
// TODO(sarahbot@): Rename this field to createBillingCost once the old createBillingCost has been
// removed
/** A property that transitions to different create billing costs at different times. */
@Column(nullable = false)
@JsonDeserialize(using = TimedTransitionPropertyMoneyDeserializer.class)
@@ -682,19 +668,10 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
* Use {@code PricingEngineProxy.getDomainCreateCost} instead of this to find the cost for a
* domain create.
*/
@VisibleForTesting
public Money getCreateBillingCost(DateTime now) {
return createBillingCostTransitions.getValueAtTime(now);
}
// This getter is still necessary for the Jackson deserialization in the ConfigureTldCommand
// TODO(sarahbot@): Remove this getter once the deprecated createBillingCost field is removed from
// the schema
@Deprecated
public Money getCreateBillingCost() {
return createBillingCost;
}
public ImmutableSortedMap<DateTime, Money> getCreateBillingCostTransitions() {
return createBillingCostTransitions.toValueMap();
}
@@ -976,12 +953,6 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
return this;
}
public Builder setCreateBillingCost(Money amount) {
checkArgument(amount.isPositiveOrZero(), "createBillingCost cannot be negative");
getInstance().createBillingCost = amount;
return this;
}
public Builder setCreateBillingCostTransitions(
ImmutableSortedMap<DateTime, Money> createCostsMap) {
checkArgumentNotNull(createCostsMap, "Create billing costs map cannot be null");
@@ -14,7 +14,6 @@
package google.registry.model.tld.label;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.monitoring.metrics.EventMetric;
@@ -43,16 +42,11 @@ class DomainLabelMetrics {
UNCACHED_POSITIVE
}
@AutoValue
abstract static class MetricsReservedListMatch {
record MetricsReservedListMatch(String reservedListName, ReservationType reservationType) {
static MetricsReservedListMatch create(
String reservedListName, ReservationType reservationType) {
return new AutoValue_DomainLabelMetrics_MetricsReservedListMatch(
reservedListName, reservationType);
return new MetricsReservedListMatch(reservedListName, reservationType);
}
abstract String reservedListName();
abstract ReservationType reservationType();
}
/**
@@ -23,7 +23,6 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import static google.registry.util.CollectionUtils.isNullOrEmpty;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -236,14 +235,10 @@ public final class PremiumListDao {
.collect(toImmutableList());
}
@AutoValue
abstract static class RevisionIdAndLabel {
abstract long revisionId();
abstract String label();
record RevisionIdAndLabel(long revisionId, String label) {
static RevisionIdAndLabel create(long revisionId, String label) {
return new AutoValue_PremiumListDao_RevisionIdAndLabel(revisionId, label);
return new RevisionIdAndLabel(revisionId, label);
}
}
@@ -67,12 +67,13 @@ public enum ReservationType {
return messageForCheck;
}
private static final Ordering<ReservationType> ORDERING = new Ordering<ReservationType>() {
@Override
public int compare(ReservationType left, ReservationType right) {
return Integer.compare(left.ordinal(), right.ordinal());
}
};
private static final Ordering<ReservationType> ORDERING =
new Ordering<>() {
@Override
public int compare(ReservationType left, ReservationType right) {
return Integer.compare(left.ordinal(), right.ordinal());
}
};
/**
* Returns the {@code ReservationType} with the highest severity, used when a label has multiple
@@ -60,17 +60,17 @@ public final class TmchCrl extends CrossTldSingleton {
}
/** ASCII-armored X.509 certificate revocation list. */
public final String getCrl() {
public String getCrl() {
return crl;
}
/** Returns the URL that the CRL was downloaded from. */
public final String getUrl() {
public String getUrl() {
return crl;
}
/** Time we last updated the Database with a newer ICANN CRL. */
public final DateTime getUpdated() {
public DateTime getUpdated() {
return updated;
}
}
@@ -23,6 +23,7 @@ import google.registry.batch.BatchModule;
import google.registry.bigquery.BigqueryModule;
import google.registry.config.CloudTasksUtilsModule;
import google.registry.config.CredentialModule;
import google.registry.config.RegistryConfig.Config;
import google.registry.config.RegistryConfig.ConfigModule;
import google.registry.dns.writer.VoidDnsWriterModule;
import google.registry.export.DriveModule;
@@ -96,6 +97,9 @@ interface RegistryComponent {
Lazy<MetricReporter> metricReporter();
@Config("projectId")
String projectId();
@Module
class RegistryModule {
@Provides
@@ -14,17 +14,51 @@
package google.registry.module;
import static com.google.cloud.logging.TraceLoggingEnhancer.setCurrentTraceId;
import static google.registry.util.RandomStringGenerator.insecureRandomStringGenerator;
import static google.registry.util.StringGenerator.Alphabets.HEX_DIGITS_ONLY;
import com.google.monitoring.metrics.MetricReporter;
import dagger.Lazy;
import google.registry.request.RequestHandler;
import google.registry.util.RandomStringGenerator;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/** Servlet that handles all requests. */
public class RegistryServlet extends ServletBase {
// Length of a log trace_id, arbitrarily chosen.
private static final int LOG_TRACE_ID_LENGTH = 32;
// GCP log trace pattern. Fill in project_id and trace id
private static final String LOG_TRACE_PATTERN = "projects/%s/traces/%s";
private static final RandomStringGenerator LOG_TRACE_ID_GENERATOR =
insecureRandomStringGenerator(HEX_DIGITS_ONLY);
private static final RegistryComponent component = DaggerRegistryComponent.create();
private static final RequestHandler<RequestComponent> requestHandler = component.requestHandler();
private static final Lazy<MetricReporter> metricReporter = component.metricReporter();
private final String projectId;
public RegistryServlet() {
super(requestHandler, metricReporter);
this.projectId = component.projectId();
}
@Override
public void service(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
setCurrentTraceId(traceId());
try {
super.service(req, rsp);
} finally {
setCurrentTraceId(null);
}
}
String traceId() {
return String.format(
LOG_TRACE_PATTERN, projectId, LOG_TRACE_ID_GENERATOR.createString(LOG_TRACE_ID_LENGTH));
}
}
@@ -110,7 +110,9 @@ import google.registry.tools.server.ToolsServerModule;
import google.registry.tools.server.VerifyOteAction;
import google.registry.ui.server.console.ConsoleDomainGetAction;
import google.registry.ui.server.console.ConsoleDomainListAction;
import google.registry.ui.server.console.ConsoleDumDownloadAction;
import google.registry.ui.server.console.ConsoleEppPasswordAction;
import google.registry.ui.server.console.ConsoleRegistryLockAction;
import google.registry.ui.server.console.ConsoleUserDataAction;
import google.registry.ui.server.console.RegistrarsAction;
import google.registry.ui.server.console.settings.ContactAction;
@@ -185,10 +187,14 @@ interface RequestComponent {
ConsoleRegistrarCreatorAction consoleRegistrarCreatorAction();
ConsoleRegistryLockAction consoleRegistryLockAction();
ConsoleUiAction consoleUiAction();
ConsoleUserDataAction consoleUserDataAction();
ConsoleDumDownloadAction consoleDumDownloadAction();
ContactAction contactAction();
CopyDetailReportsAction copyDetailReportAction();
@@ -27,7 +27,9 @@ import google.registry.request.RequestModule;
import google.registry.request.RequestScope;
import google.registry.ui.server.console.ConsoleDomainGetAction;
import google.registry.ui.server.console.ConsoleDomainListAction;
import google.registry.ui.server.console.ConsoleDumDownloadAction;
import google.registry.ui.server.console.ConsoleEppPasswordAction;
import google.registry.ui.server.console.ConsoleRegistryLockAction;
import google.registry.ui.server.console.ConsoleUserDataAction;
import google.registry.ui.server.console.RegistrarsAction;
import google.registry.ui.server.console.settings.ContactAction;
@@ -63,10 +65,15 @@ public interface FrontendRequestComponent {
ConsoleOteSetupAction consoleOteSetupAction();
ConsoleRegistrarCreatorAction consoleRegistrarCreatorAction();
ConsoleRegistryLockAction consoleRegistryLockAction();
ConsoleUiAction consoleUiAction();
ConsoleUserDataAction consoleUserDataAction();
ConsoleDumDownloadAction consoleDumDownloadAction();
ContactAction contactAction();
EppTlsAction eppTlsAction();
@@ -16,14 +16,18 @@ package google.registry.monitoring.whitebox;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.auto.value.AutoValue;
import com.google.auto.value.AutoBuilder;
import google.registry.util.Clock;
import java.util.Optional;
import org.joda.time.DateTime;
/** A value class for recording attributes of a domain check metric. */
@AutoValue
public abstract class CheckApiMetric {
/** A record for recording attributes of a domain check metric. */
public record CheckApiMetric(
DateTime startTimestamp,
DateTime endTimestamp,
Status status,
Optional<Tier> tier,
Optional<Availability> availability) {
/** Price tier of a domain name. */
public enum Tier {
@@ -77,24 +81,13 @@ public abstract class CheckApiMetric {
}
}
public abstract DateTime startTimestamp();
public abstract DateTime endTimestamp();
public abstract Status status();
public abstract Optional<Tier> tier();
public abstract Optional<Availability> availability();
public static Builder builder(Clock clock) {
return new AutoValue_CheckApiMetric.Builder().startTimestamp(clock.nowUtc()).setClock(clock);
return new AutoBuilder_CheckApiMetric_Builder().startTimestamp(clock.nowUtc()).setClock(clock);
}
CheckApiMetric() {}
/** Builder for {@link CheckApiMetric}. */
@AutoValue.Builder
@AutoBuilder
public abstract static class Builder {
private Clock clock;
@@ -16,7 +16,7 @@ package google.registry.monitoring.whitebox;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.auto.value.AutoValue;
import com.google.auto.value.AutoBuilder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import google.registry.model.eppoutput.Result.Code;
@@ -25,30 +25,23 @@ import google.registry.util.Clock;
import java.util.Optional;
import org.joda.time.DateTime;
/** A value class for recording attributes of an EPP metric. */
@AutoValue
public abstract class EppMetric {
/** A record for recording attributes of an EPP metric. */
public record EppMetric(
DateTime startTimestamp,
DateTime endTimestamp,
Optional<String> commandName,
Optional<String> registrarId,
Optional<String> tld,
Optional<Code> status) {
public abstract DateTime getStartTimestamp();
public abstract DateTime getEndTimestamp();
public abstract Optional<String> getCommandName();
public abstract Optional<String> getRegistrarId();
public abstract Optional<String> getTld();
public abstract Optional<Code> getStatus();
/** Create an {@link EppMetric.Builder}. */
/** Create an {@link Builder}. */
public static Builder builder() {
return new AutoValue_EppMetric.Builder();
return new AutoBuilder_EppMetric_Builder();
}
/**
* Create an {@link EppMetric.Builder} for a request context, with the given request ID and
* with start and end timestamps taken from the given clock.
* Create an {@link Builder} for a request context, with the given request ID and with start and
* end timestamps taken from the given clock.
*
* <p>The start timestamp is recorded now, and the end timestamp at {@code build()}.
*/
@@ -58,8 +51,32 @@ public abstract class EppMetric {
.setClock(clock);
}
public DateTime getStartTimestamp() {
return startTimestamp;
}
public DateTime getEndTimestamp() {
return endTimestamp;
}
public Optional<String> getCommandName() {
return commandName;
}
public Optional<String> getRegistrarId() {
return registrarId;
}
public Optional<String> getTld() {
return tld;
}
public Optional<Code> getStatus() {
return status;
}
/** A builder to create instances of {@link EppMetric}. */
@AutoValue.Builder
@AutoBuilder
public abstract static class Builder {
/** Builder-only clock to support automatic recording of endTimestamp on {@link #build()}. */
@@ -97,18 +114,14 @@ public abstract class EppMetric {
*/
public Builder setTlds(ImmutableSet<String> tlds) {
switch (tlds.size()) {
case 0:
setTld(Optional.empty());
break;
case 1:
case 0 -> setTld(Optional.empty());
case 1 -> {
String tld = Iterables.getOnlyElement(tlds);
// Only record TLDs that actually exist, otherwise we can blow up cardinality by recording
// an arbitrarily large number of strings.
setTld(Optional.ofNullable(Tlds.getTlds().contains(tld) ? tld : "_invalid"));
break;
default:
setTld("_various");
break;
}
default -> setTld("_various");
}
return this;
}
@@ -100,7 +100,7 @@ public class IntervalDescriptor extends AbstractTypeDescriptor<PGInterval>
@Override
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>(javaTypeDescriptor, this) {
return new BasicBinder<>(javaTypeDescriptor, this) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
@@ -117,7 +117,7 @@ public class IntervalDescriptor extends AbstractTypeDescriptor<PGInterval>
@Override
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>(javaTypeDescriptor, this) {
return new BasicExtractor<>(javaTypeDescriptor, this) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap(rs.getObject(name), options);
@@ -14,7 +14,7 @@
package google.registry.persistence.converter;
import google.registry.model.registrar.RegistrarPoc.Type;
import google.registry.model.registrar.RegistrarPocBase.Type;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@@ -115,7 +115,7 @@ public class StringCollectionDescriptor extends AbstractTypeDescriptor<StringCol
@Override
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>(javaTypeDescriptor, this) {
return new BasicBinder<>(javaTypeDescriptor, this) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
@@ -123,8 +123,7 @@ public class StringCollectionDescriptor extends AbstractTypeDescriptor<StringCol
st.setArray(index, null);
return;
}
if (value instanceof StringCollection) {
StringCollection stringCollection = (StringCollection) value;
if (value instanceof StringCollection stringCollection) {
if (stringCollection.getCollection() == null) {
st.setArray(index, null);
} else {
@@ -154,7 +153,7 @@ public class StringCollectionDescriptor extends AbstractTypeDescriptor<StringCol
@Override
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>(javaTypeDescriptor, this) {
return new BasicExtractor<>(javaTypeDescriptor, this) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap(rs.getArray(name), options);
@@ -105,7 +105,7 @@ public class StringMapDescriptor extends AbstractTypeDescriptor<StringMap>
@Override
public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicBinder<X>(javaTypeDescriptor, this) {
return new BasicBinder<>(javaTypeDescriptor, this) {
@Override
protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
throws SQLException {
@@ -136,7 +136,7 @@ public class StringMapDescriptor extends AbstractTypeDescriptor<StringMap>
@Override
public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
return new BasicExtractor<X>(javaTypeDescriptor, this) {
return new BasicExtractor<>(javaTypeDescriptor, this) {
@Override
protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
return javaTypeDescriptor.wrap(rs.getObject(name), options);
@@ -14,9 +14,9 @@
package google.registry.persistence.transaction;
import autovalue.shaded.com.google.common.collect.ImmutableList;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import java.sql.SQLException;
import java.util.Optional;
import javax.persistence.PersistenceException;
@@ -78,8 +78,7 @@ public class DatabaseException extends PersistenceException {
static String getSqlError(Throwable t) {
ImmutableList.Builder<String> errMessageBuilder = new ImmutableList.Builder<>();
do {
if (t instanceof SQLException) {
SQLException e = (SQLException) t;
if (t instanceof SQLException e) {
getSqlExceptionDetails(e).ifPresent(errMessageBuilder::add);
}
t = t.getCause();
@@ -544,15 +544,7 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
return emf.getMetamodel().entity(clazz);
}
private static class EntityId {
private final String name;
private final Object value;
private EntityId(String name, Object value) {
this.name = name;
this.value = value;
}
}
private record EntityId(String name, Object value) {}
private static ImmutableSet<EntityId> getEntityIdsFromEntity(
EntityType<?> entityType, Object entity) {
@@ -14,7 +14,6 @@
package google.registry.privileges.secretmanager;
import com.google.auto.value.AutoValue;
import com.google.cloud.secretmanager.v1.SecretVersion;
import com.google.common.collect.Streams;
import java.util.Optional;
@@ -116,18 +115,11 @@ public interface SecretManagerClient {
void deleteSecret(String secretId);
/** Contains the {@link SecretVersion.State State} of an secret version. */
@AutoValue
abstract class SecretVersionState {
record SecretVersionState(String secretId, String version, SecretVersion.State state) {
public abstract String secretId();
public abstract String version();
public abstract SecretVersion.State state();
public static SecretVersionState of(
public static SecretVersionState create(
String secretId, String version, SecretVersion.State state) {
return new AutoValue_SecretManagerClient_SecretVersionState(secretId, version, state);
return new SecretVersionState(secretId, version, state);
}
}
@@ -94,7 +94,7 @@ public class SecretManagerClientImpl implements SecretManagerClient {
private static SecretVersionState toSecretVersionState(SecretVersion secretVersion) {
SecretVersionName name = SecretVersionName.parse(secretVersion.getName());
return SecretVersionState.of(
return SecretVersionState.create(
name.getSecret(), name.getSecretVersion(), secretVersion.getState());
}
@@ -165,12 +165,9 @@ public class SecretManagerClientImpl implements SecretManagerClient {
return retrier.callWithRetry(callable, SecretManagerClientImpl::isRetryableException);
} catch (ApiException e) {
switch (e.getStatusCode().getCode()) {
case ALREADY_EXISTS:
throw new SecretAlreadyExistsException(e);
case NOT_FOUND:
throw new NoSuchSecretResourceException(e);
default:
throw new SecretManagerException(e);
case ALREADY_EXISTS -> throw new SecretAlreadyExistsException(e);
case NOT_FOUND -> throw new NoSuchSecretResourceException(e);
default -> throw new SecretManagerException(e);
}
}
}
@@ -14,9 +14,8 @@
package google.registry.privileges.secretmanager;
import static avro.shaded.com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Preconditions.checkState;
import com.google.auto.value.AutoValue;
import java.util.List;
/**
@@ -24,32 +23,27 @@ import java.util.List;
*
* <p>User must take care not to include the {@link #SEPARATOR} in property values.
*/
@AutoValue
public abstract class SqlCredential {
public record SqlCredential(String login, String password) {
public static final Character SEPARATOR = ' ';
public abstract String login();
public abstract String password();
@Override
public final String toString() {
// Use Object.toString(), which does not show object data.
return super.toString();
public String toString() {
// Use the Object.toString() implementation, which does not show the sensitive date in fields.
return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode());
}
public final String toFormattedString() {
public String toFormattedString() {
return String.format("%s%c%s", login(), SEPARATOR, password());
}
public static SqlCredential fromFormattedString(String sqlCredential) {
List<String> items = com.google.common.base.Splitter.on(SEPARATOR).splitToList(sqlCredential);
checkState(items.size() == 2, "Invalid SqlCredential string.");
return of(items.get(0), items.get(1));
return create(items.get(0), items.get(1));
}
public static SqlCredential of(String login, String password) {
return new AutoValue_SqlCredential(login, password);
public static SqlCredential create(String login, String password) {
return new SqlCredential(login, password);
}
}
@@ -79,7 +79,7 @@ public class SqlCredentialStore {
String credentialVersion =
csmClient.addSecretVersion(
credentialDataSecretId,
SqlCredential.of(createDatabaseLoginName(user), password).toFormattedString());
SqlCredential.create(createDatabaseLoginName(user), password).toFormattedString());
return SecretVersionName.of(csmClient.getProject(), credentialDataSecretId, credentialVersion);
}
@@ -52,18 +52,13 @@ import org.joda.time.DateTime;
*
* <p>This implementation is geared towards RDAP replies, and hence has RDAP-specific quirks.
* Specifically:
*
* - Fields with empty arrays are not shown at all
*
* - VCards are a built-in special case (Not implemented yet)
*
* - DateTime conversion is specifically supported as if it were a primitive
*
* - Arrays are considered to be SETS rather than lists, meaning repeated values are removed and the
* order isn't guaranteed
*
* Usage:
*
* {@link JsonableElement}
* -----------------------
*
@@ -324,8 +319,7 @@ abstract class AbstractJsonableObject implements Jsonable {
/** Converts an Object to a JsonElement. */
private static JsonElement toJsonElement(String name, Member member, Object object) {
if (object instanceof Jsonable) {
Jsonable jsonable = (Jsonable) object;
if (object instanceof Jsonable jsonable) {
verifyAllowedJsonKeyName(name, member, jsonable.getClass());
return jsonable.toJson();
}
@@ -414,8 +408,7 @@ abstract class AbstractJsonableObject implements Jsonable {
object, "Member '%s' is null. If you want an optional member - use Optional", member);
// We ignore any Optional that are empty, as if they didn't exist at all
if (object instanceof Optional) {
Optional<?> optional = (Optional<?>) object;
if (object instanceof Optional<?> optional) {
if (optional.isEmpty()) {
return;
}
@@ -14,13 +14,16 @@
package google.registry.rdap;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableSet;
import google.registry.model.ImmutableObject;
/** Authorization information for RDAP data access. */
@AutoValue
public abstract class RdapAuthorization extends ImmutableObject {
/**
* Authorization information for RDAP data access.
*
* @param role The role to be used for access.
* @param registrarIds The registrar client IDs for which access is granted (used only if the role
* is REGISTRAR.
*/
public record RdapAuthorization(Role role, ImmutableSet<String> registrarIds) {
enum Role {
ADMINISTRATOR,
@@ -28,29 +31,20 @@ public abstract class RdapAuthorization extends ImmutableObject {
PUBLIC
}
/** The role to be used for access. */
public abstract Role role();
/** The registrar client IDs for which access is granted (used only if the role is REGISTRAR. */
public abstract ImmutableSet<String> registrarIds();
static RdapAuthorization create(Role role, String registrarId) {
return new AutoValue_RdapAuthorization(role, ImmutableSet.of(registrarId));
return create(role, ImmutableSet.of(registrarId));
}
static RdapAuthorization create(Role role, ImmutableSet<String> clientIds) {
return new AutoValue_RdapAuthorization(role, clientIds);
return new RdapAuthorization(role, clientIds);
}
boolean isAuthorizedForRegistrar(String registrarId) {
switch (role()) {
case ADMINISTRATOR:
return true;
case REGISTRAR:
return registrarIds().contains(registrarId);
default:
return false;
}
return switch (role()) {
case ADMINISTRATOR -> true;
case REGISTRAR -> registrarIds().contains(registrarId);
default -> false;
};
}
public static final RdapAuthorization PUBLIC_AUTHORIZATION =
@@ -79,7 +79,7 @@ public class RdapIcannStandardInformation {
/**
* Required by ICANN RDAP Profile section 1.4.9, as corrected by Gustavo Lozano of ICANN.
*
* Also mentioned in the RDAP Technical Implementation Guide 3.6.
* <p>Also mentioned in the RDAP Technical Implementation Guide 3.6.
*
* @see <a href="http://mm.icann.org/pipermail/gtld-tech/2016-October/000822.html">Questions about
* the ICANN RDAP Profile</a>
@@ -95,7 +95,7 @@ public class RdapIcannStandardInformation {
/**
* Required by ICANN RDAP Profile section 1.4.8, as corrected by Gustavo Lozano of ICANN.
*
* Also mentioned in the RDAP Technical Implementation Guide 3.5.
* <p>Also mentioned in the RDAP Technical Implementation Guide 3.5.
*
* @see <a href="http://mm.icann.org/pipermail/gtld-tech/2016-October/000822.html">Questions about
* the ICANN RDAP Profile</a>
@@ -808,17 +808,12 @@ public class RdapJsonFormatter {
/** Converts a domain registry contact type into a role as defined by RFC 9083. */
private static RdapEntity.Role convertContactTypeToRdapRole(DesignatedContact.Type contactType) {
switch (contactType) {
case REGISTRANT:
return RdapEntity.Role.REGISTRANT;
case TECH:
return RdapEntity.Role.TECH;
case BILLING:
return RdapEntity.Role.BILLING;
case ADMIN:
return RdapEntity.Role.ADMIN;
}
throw new AssertionError();
return switch (contactType) {
case REGISTRANT -> RdapEntity.Role.REGISTRANT;
case TECH -> RdapEntity.Role.TECH;
case BILLING -> RdapEntity.Role.BILLING;
case ADMIN -> RdapEntity.Role.ADMIN;
};
}
/**
@@ -14,7 +14,7 @@
package google.registry.rdap;
import com.google.auto.value.AutoValue;
import com.google.auto.value.AutoBuilder;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.monitoring.metrics.DistributionFitter;
@@ -102,10 +102,7 @@ public class RdapMetrics {
static final IncrementableMetric requests =
MetricRegistryImpl.getDefault()
.newIncrementableMetric(
"/rdap/requests",
"Count of RDAP Requests",
"count",
LABEL_DESCRIPTORS_FOR_REQUESTS);
"/rdap/requests", "Count of RDAP Requests", "count", LABEL_DESCRIPTORS_FOR_REQUESTS);
@VisibleForTesting
static final IncrementableMetric responses =
@@ -163,8 +160,7 @@ public class RdapMetrics {
* ways of looking at the data, since cardinality constraints prevent us from saving all the
* information in a single metric.
*/
public void updateMetrics(
RdapMetricInformation rdapMetricInformation) {
public void updateMetrics(RdapMetricInformation rdapMetricInformation) {
requests.increment(
rdapMetricInformation.endpointType().toString(),
rdapMetricInformation.includeDeleted() ? "YES" : "NO",
@@ -206,95 +202,76 @@ public class RdapMetrics {
}
}
@AutoValue
abstract static class RdapMetricInformation {
/**
* Information on RDAP metrics.
*
* @param endpointType The type of RDAP endpoint (domain, domains, nameserver, etc.).
* @param searchType The search type (by domain name, by nameserver name, etc.).
* @param wildcardType The type of wildcarding requested (prefix, suffix, etc.).
* @param prefixLength The length of the prefix string before the wildcard, if any; any length
* longer than MAX_RECORDED_PREFIX_LENGTH is limited to MAX_RECORDED_PREFIX_LENGTH when
* recording the metric, to avoid cardinality problems.
* @param includeDeleted Whether the search included deleted records.
* @param registrarSpecified Whether the search requested a specific registrar.
* @param role Type of authentication/authorization: public, admin or registrar.
* @param requestMethod Http request method (GET, POST, HEAD, etc.).
* @param statusCode Http status code.
* @param incompletenessWarningType Incompleteness warning type (e.g. truncated).
* @param numDomainsRetrieved Number of domains retrieved from the database; this might be more
* 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(
EndpointType endpointType,
SearchType searchType,
WildcardType wildcardType,
int prefixLength,
boolean includeDeleted,
boolean registrarSpecified,
RdapAuthorization.Role role,
Action.Method requestMethod,
int statusCode,
IncompletenessWarningType incompletenessWarningType,
Optional<Long> numDomainsRetrieved,
Optional<Long> numHostsRetrieved,
Optional<Long> numContactsRetrieved) {
/** The type of RDAP endpoint (domain, domains, nameserver, etc.). */
abstract EndpointType endpointType();
@AutoBuilder
interface Builder {
Builder setEndpointType(EndpointType endpointType);
/** The search type (by domain name, by nameserver name, etc.). */
abstract SearchType searchType();
Builder setSearchType(SearchType searchType);
/** The type of wildcarding requested (prefix, suffix, etc.). */
abstract WildcardType wildcardType();
Builder setWildcardType(WildcardType wildcardType);
/**
* The length of the prefix string before the wildcard, if any; any length longer than
* MAX_RECORDED_PREFIX_LENGTH is limited to MAX_RECORDED_PREFIX_LENGTH when recording the
* metric, to avoid cardinality problems.
*/
abstract int prefixLength();
Builder setPrefixLength(int prefixLength);
/** Whether the search included deleted records. */
abstract boolean includeDeleted();
Builder setIncludeDeleted(boolean includeDeleted);
/** Whether the search requested a specific registrar. */
abstract boolean registrarSpecified();
Builder setRegistrarSpecified(boolean registrarSpecified);
/** Type of authentication/authorization: public, admin or registrar. */
abstract RdapAuthorization.Role role();
Builder setRole(RdapAuthorization.Role role);
/** Http request method (GET, POST, HEAD, etc.). */
abstract Action.Method requestMethod();
Builder setRequestMethod(Action.Method requestMethod);
/** Http status code. */
abstract int statusCode();
Builder setStatusCode(int statusCode);
/** Incompleteness warning type (e.g. truncated). */
abstract IncompletenessWarningType incompletenessWarningType();
Builder setIncompletenessWarningType(IncompletenessWarningType incompletenessWarningType);
/**
* Number of domains retrieved from the database; this might be more than were actually returned
* in the response; absent if a search was not performed.
*/
abstract Optional<Long> numDomainsRetrieved();
Builder setNumDomainsRetrieved(long numDomainsRetrieved);
/**
* 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.
*/
abstract Optional<Long> numHostsRetrieved();
Builder setNumHostsRetrieved(long numHostsRetrieved);
/**
* 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.
*/
abstract Optional<Long> numContactsRetrieved();
Builder setNumContactsRetrieved(long numContactRetrieved);
@AutoValue.Builder
abstract static class Builder {
abstract Builder setEndpointType(EndpointType endpointType);
abstract Builder setSearchType(SearchType searchType);
abstract Builder setWildcardType(WildcardType wildcardType);
abstract Builder setPrefixLength(int prefixLength);
abstract Builder setIncludeDeleted(boolean includeDeleted);
abstract Builder setRegistrarSpecified(boolean registrarSpecified);
abstract Builder setRole(RdapAuthorization.Role role);
abstract Builder setRequestMethod(Action.Method requestMethod);
abstract Builder setStatusCode(int statusCode);
abstract Builder setIncompletenessWarningType(
IncompletenessWarningType incompletenessWarningType);
abstract Builder setNumDomainsRetrieved(long numDomainsRetrieved);
abstract Builder setNumHostsRetrieved(long numHostsRetrieved);
abstract Builder setNumContactsRetrieved(long numContactRetrieved);
abstract RdapMetricInformation build();
RdapMetricInformation build();
}
static Builder builder() {
return new AutoValue_RdapMetrics_RdapMetricInformation.Builder()
return new AutoBuilder_RdapMetrics_RdapMetricInformation_Builder()
.setSearchType(SearchType.NONE)
.setWildcardType(WildcardType.INVALID)
.setPrefixLength(0)
@@ -47,7 +47,7 @@ final class RdapObjectClasses {
/**
* Temporary implementation of VCards.
*
* Will create a better implementation soon.
* <p>Will create a better implementation soon.
*/
@RestrictJsonNames({})
@AutoValue
@@ -146,7 +146,7 @@ final class RdapObjectClasses {
/**
* An object that can be used to create a TopLevelReply.
*
* All Actions need to return an object of this type.
* <p>All Actions need to return an object of this type.
*/
abstract static class ReplyPayloadBase extends AbstractJsonableObject {
final BoilerplateType boilerplateType;
@@ -173,15 +173,14 @@ final class RdapObjectClasses {
@JsonableElement("notices[]") abstract Notice aTosNotice();
@JsonableElement("notices") ImmutableList<Notice> boilerplateNotices() {
switch (aAreplyObject().boilerplateType) {
case DOMAIN:
return RdapIcannStandardInformation.domainBoilerplateNotices;
case NAMESERVER:
case ENTITY:
return RdapIcannStandardInformation.nameserverAndEntityBoilerplateNotices;
default: // things other than domains, nameservers and entities do not yet have boilerplate
return ImmutableList.of();
}
return switch (aAreplyObject().boilerplateType) {
case DOMAIN -> RdapIcannStandardInformation.domainBoilerplateNotices;
case NAMESERVER, ENTITY ->
RdapIcannStandardInformation.nameserverAndEntityBoilerplateNotices;
default -> // things other than domains, nameservers and entities do not yet have
// boilerplate
ImmutableList.of();
};
}
static TopLevelReplyObject create(ReplyPayloadBase replyObject, Notice tosNotice) {
@@ -14,14 +14,23 @@
package google.registry.rdap;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import google.registry.model.EppResource;
import google.registry.rdap.RdapSearchResults.IncompletenessWarningType;
import java.util.List;
@AutoValue
abstract class RdapResultSet<T extends EppResource> {
/**
* RDAP result set.
*
* @param resources List of EPP resources.
* @param incompletenessWarningType Type of warning to display regarding possible incomplete data.
* @param numResourcesRetrieved Number of resources retrieved from the database in the process of
* assembling the data set.
*/
record RdapResultSet<T extends EppResource>(
ImmutableList<T> resources,
IncompletenessWarningType incompletenessWarningType,
int numResourcesRetrieved) {
static <S extends EppResource> RdapResultSet<S> create(List<S> resources) {
return create(resources, IncompletenessWarningType.COMPLETE, resources.size());
@@ -31,16 +40,7 @@ abstract class RdapResultSet<T extends EppResource> {
List<S> resources,
IncompletenessWarningType incompletenessWarningType,
int numResourcesRetrieved) {
return new AutoValue_RdapResultSet<>(
return new RdapResultSet<>(
ImmutableList.copyOf(resources), incompletenessWarningType, numResourcesRetrieved);
}
/** List of EPP resources. */
abstract ImmutableList<T> resources();
/** Type of warning to display regarding possible incomplete data. */
abstract IncompletenessWarningType incompletenessWarningType();
/** Number of resources retrieved from the database in the process of assembling the data set. */
abstract int numResourcesRetrieved();
}
@@ -14,22 +14,16 @@
package google.registry.rde;
import com.google.auto.value.AutoValue;
import java.io.Serial;
import java.io.Serializable;
/** Container of RDE resource marshalled by {@link RdeMarshaller}. */
@AutoValue
public abstract class DepositFragment implements Serializable {
public record DepositFragment(RdeResourceType type, String xml, String error)
implements Serializable {
private static final long serialVersionUID = -5241410684255467454L;
public abstract RdeResourceType type();
public abstract String xml();
public abstract String error();
@Serial private static final long serialVersionUID = -5241410684255467454L;
public static DepositFragment create(RdeResourceType type, String xml, String error) {
return new AutoValue_DepositFragment(type, xml, error);
return new DepositFragment(type, xml, error);
}
DepositFragment() {}
}
@@ -14,17 +14,13 @@
package google.registry.rde;
import static com.google.common.base.Preconditions.checkState;
import com.google.auto.value.AutoValue;
import google.registry.model.common.Cursor.CursorType;
import google.registry.model.rde.RdeMode;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.OutputStream;
import java.io.Serial;
import java.io.Serializable;
import java.util.Optional;
import javax.annotation.Nullable;
@@ -46,52 +42,46 @@ import org.joda.time.Duration;
* arguments (see {@code RdePipeline#decodePendingDeposits}). The latter requires safe
* deserialization because the data crosses credential boundaries (See {@code
* SafeObjectInputStream}).
*
* @param manual True if deposits should be generated via manual operation, which does not update
* the cursor, and saves the generated deposits in a special manual subdirectory tree.
* @param tld TLD for which a deposit should be generated.
* @param watermarkStr String representation of the watermark date for which a deposit should be
* generated.
* @param mode Which type of deposit to generate: full (RDE) or thin (BRDA).
* @param cursor The cursor type to update (not used in manual operation).
* @param intervalStr String representation of the amount of time to increment the cursor (not used
* in manual operation).
* @param directoryWithTrailingSlash Subdirectory of bucket/manual in which files should be placed,
* including a trailing slash (used only in manual operation).
* @param revision Revision number for generated files; if absent, use the next available in the
* sequence (used only in manual operation).
*/
@AutoValue
public abstract class PendingDeposit implements Serializable {
public record PendingDeposit(
boolean manual,
String tld,
String watermarkStr,
RdeMode mode,
@Nullable CursorType cursor,
@Nullable String intervalStr,
@Nullable String directoryWithTrailingSlash,
@Nullable Integer revision)
implements Serializable {
private static final long serialVersionUID = 3141095605225904433L;
public DateTime watermark() {
return DateTime.parse(watermarkStr);
}
/**
* True if deposits should be generated via manual operation, which does not update the cursor,
* and saves the generated deposits in a special manual subdirectory tree.
*/
public abstract boolean manual();
public Duration interval() {
return intervalStr == null ? null : Duration.parse(intervalStr);
}
/** TLD for which a deposit should be generated. */
public abstract String tld();
/** Watermark date for which a deposit should be generated. */
public abstract DateTime watermark();
/** Which type of deposit to generate: full (RDE) or thin (BRDA). */
public abstract RdeMode mode();
/** The cursor type to update (not used in manual operation). */
@Nullable
public abstract CursorType cursor();
/** Amount of time to increment the cursor (not used in manual operation). */
@Nullable
public abstract Duration interval();
/**
* Subdirectory of bucket/manual in which files should be placed, including a trailing slash (used
* only in manual operation).
*/
@Nullable
public abstract String directoryWithTrailingSlash();
/**
* Revision number for generated files; if absent, use the next available in the sequence (used
* only in manual operation).
*/
@Nullable
public abstract Integer revision();
@Serial private static final long serialVersionUID = 3141095605225904433L;
public static PendingDeposit create(
String tld, DateTime watermark, RdeMode mode, CursorType cursor, Duration interval) {
return new AutoValue_PendingDeposit(false, tld, watermark, mode, cursor, interval, null, null);
return new PendingDeposit(
false, tld, watermark.toString(), mode, cursor, interval.toString(), null, null);
}
public static PendingDeposit createInManualOperation(
@@ -100,58 +90,8 @@ public abstract class PendingDeposit implements Serializable {
RdeMode mode,
String directoryWithTrailingSlash,
@Nullable Integer revision) {
return new AutoValue_PendingDeposit(
true, tld, watermark, mode, null, null, directoryWithTrailingSlash, revision);
}
PendingDeposit() {}
/**
* Specifies that {@link SerializedForm} be used for {@code SafeObjectInputStream}-compatible
* custom-serialization of {@link AutoValue_PendingDeposit the AutoValue implementation class}.
*
* <p>This method is package-protected so that the AutoValue implementation class inherits this
* behavior.
*
* <p>This method leverages {@link PendingDepositCoder} to serializes an instance. However, it is
* not invoked in Beam pipelines.
*/
Object writeReplace() throws ObjectStreamException {
return new SerializedForm(this);
}
/**
* Proxy for custom-serialization of {@link PendingDeposit}. This is necessary because the actual
* class to be (de)serialized is the generated AutoValue implementation. See also {@link
* #writeReplace}.
*
* <p>This class leverages {@link PendingDepositCoder} to safely deserializes an instance.
* However, it is not used in Beam pipelines.
*/
private static class SerializedForm implements Serializable {
private static final long serialVersionUID = 3141095605225904433L;
private PendingDeposit value;
private SerializedForm(PendingDeposit value) {
this.value = value;
}
private void writeObject(ObjectOutputStream os) throws IOException {
checkState(value != null, "Non-null value expected for serialization.");
PendingDepositCoder.INSTANCE.encode(value, os);
}
private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
checkState(value == null, "Non-null value unexpected for deserialization.");
this.value = PendingDepositCoder.INSTANCE.decode(is);
}
@SuppressWarnings("unused")
private Object readResolve() throws ObjectStreamException {
return this.value;
}
return new PendingDeposit(
true, tld, watermark.toString(), mode, null, null, directoryWithTrailingSlash, revision);
}
/**
@@ -178,32 +118,27 @@ public abstract class PendingDeposit implements Serializable {
public void encode(PendingDeposit value, OutputStream outStream) throws IOException {
BooleanCoder.of().encode(value.manual(), outStream);
StringUtf8Coder.of().encode(value.tld(), outStream);
StringUtf8Coder.of().encode(value.watermark().toString(), outStream);
StringUtf8Coder.of().encode(value.watermarkStr(), outStream);
StringUtf8Coder.of().encode(value.mode().name(), outStream);
NullableCoder.of(StringUtf8Coder.of())
.encode(
Optional.ofNullable(value.cursor()).map(CursorType::name).orElse(null), outStream);
NullableCoder.of(StringUtf8Coder.of())
.encode(
Optional.ofNullable(value.interval()).map(Duration::toString).orElse(null),
outStream);
NullableCoder.of(StringUtf8Coder.of()).encode(value.intervalStr(), outStream);
NullableCoder.of(StringUtf8Coder.of()).encode(value.directoryWithTrailingSlash(), outStream);
NullableCoder.of(VarIntCoder.of()).encode(value.revision(), outStream);
}
@Override
public PendingDeposit decode(InputStream inStream) throws IOException {
return new AutoValue_PendingDeposit(
return new PendingDeposit(
BooleanCoder.of().decode(inStream),
StringUtf8Coder.of().decode(inStream),
DateTime.parse(StringUtf8Coder.of().decode(inStream)),
StringUtf8Coder.of().decode(inStream),
RdeMode.valueOf(StringUtf8Coder.of().decode(inStream)),
Optional.ofNullable(NullableCoder.of(StringUtf8Coder.of()).decode(inStream))
.map(CursorType::valueOf)
.orElse(null),
Optional.ofNullable(NullableCoder.of(StringUtf8Coder.of()).decode(inStream))
.map(Duration::parse)
.orElse(null),
NullableCoder.of(StringUtf8Coder.of()).decode(inStream),
NullableCoder.of(StringUtf8Coder.of()).decode(inStream),
NullableCoder.of(VarIntCoder.of()).decode(inStream));
}
@@ -17,7 +17,6 @@ package google.registry.rde;
import static google.registry.model.EppResourceUtils.loadAtPointInTime;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
import google.registry.model.EppResource;
import google.registry.model.contact.Contact;
@@ -71,8 +70,7 @@ public class RdeFragmenter {
cache.put(WatermarkModePair.create(watermark, RdeMode.FULL), result);
cache.put(WatermarkModePair.create(watermark, RdeMode.THIN), result);
return result;
} else if (resource instanceof Host) {
Host host = (Host) resource;
} else if (resource instanceof Host host) {
result =
Optional.of(
host.isSubordinate()
@@ -96,14 +94,10 @@ public class RdeFragmenter {
}
/** Map key for {@link RdeFragmenter} cache. */
@AutoValue
abstract static class WatermarkModePair {
abstract DateTime watermark();
abstract RdeMode mode();
record WatermarkModePair(DateTime watermark, RdeMode mode) {
static WatermarkModePair create(DateTime watermark, RdeMode mode) {
return new AutoValue_RdeFragmenter_WatermarkModePair(watermark, mode);
return new WatermarkModePair(watermark, mode);
}
}
}
@@ -19,8 +19,9 @@ import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableMap;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.Registrar.State;
import google.registry.model.registrar.RegistrarAddress;
import google.registry.model.registrar.RegistrarBase;
import google.registry.model.registrar.RegistrarBase.State;
import google.registry.xjc.contact.XjcContactE164Type;
import google.registry.xjc.rderegistrar.XjcRdeRegistrar;
import google.registry.xjc.rderegistrar.XjcRdeRegistrarAddrType;
@@ -40,7 +41,7 @@ final class RegistrarToXjcConverter {
private static final String UNKNOWN_CC = "US";
/** A conversion map between internal Registrar states and external RDE states. */
private static final ImmutableMap<Registrar.State, XjcRdeRegistrarStatusType>
private static final ImmutableMap<RegistrarBase.State, XjcRdeRegistrarStatusType>
REGISTRAR_STATUS_CONVERSIONS =
ImmutableMap.of(
State.ACTIVE, XjcRdeRegistrarStatusType.OK,

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