mirror of
https://github.com/google/nomulus
synced 2026-01-16 18:53:18 +00:00
Compare commits
46 Commits
test
...
nomulus-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c5510f05d | ||
|
|
84884de77b | ||
|
|
d6c35df9bc | ||
|
|
7caa0ec9d6 | ||
|
|
ee3866ec4a | ||
|
|
97d0b7680f | ||
|
|
5700a008d6 | ||
|
|
dc9f5b99bc | ||
|
|
d3c6de7a38 | ||
|
|
3c3303c16a | ||
|
|
2a86a1bbe9 | ||
|
|
ea148ac13e | ||
|
|
06299ccb86 | ||
|
|
732c30b359 | ||
|
|
ee5a2d3916 | ||
|
|
2b5643df4c | ||
|
|
6bbd7a2290 | ||
|
|
77ab80f3dc | ||
|
|
5e1cd0120f | ||
|
|
0167dad85f | ||
|
|
1eaf3d4aa8 | ||
|
|
d9c46170dd | ||
|
|
e8a475f48b | ||
|
|
bdaab9daa5 | ||
|
|
7e07fabf7e | ||
|
|
16859bb36a | ||
|
|
7c92928f2c | ||
|
|
de8d205657 | ||
|
|
4738b979e4 | ||
|
|
a61a667992 | ||
|
|
1164070576 | ||
|
|
d23640a54f | ||
|
|
cc347264f1 | ||
|
|
e5d4cbb9fc | ||
|
|
8c1e0ff4de | ||
|
|
5cef2dd8b5 | ||
|
|
62b2585220 | ||
|
|
8692fe35db | ||
|
|
18614ba11e | ||
|
|
427f6db820 | ||
|
|
5aa40b2208 | ||
|
|
95c89bc856 | ||
|
|
c21b66f0fb | ||
|
|
b070c46231 | ||
|
|
a20eb8d1e9 | ||
|
|
338b8edb97 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -18,6 +18,13 @@ gjf.out
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
# Environment-specific configuration files
|
||||
core/src/main/java/google/registry/config/files/nomulus-config-alpha.yaml
|
||||
core/src/main/java/google/registry/config/files/nomulus-config-crash.yaml
|
||||
core/src/main/java/google/registry/config/files/nomulus-config-production.yaml
|
||||
core/src/main/java/google/registry/config/files/nomulus-config-qa.yaml
|
||||
core/src/main/java/google/registry/config/files/nomulus-config-sandbox.yaml
|
||||
|
||||
######################################################################
|
||||
# Eclipse Ignores
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# This file is expected to be part of source control.
|
||||
aopalliance:aopalliance:1.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
com.github.ben-manes.caffeine:caffeine:3.2.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
com.github.ben-manes.caffeine:caffeine:3.2.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
com.github.kevinstern:software-and-algorithms:1.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
com.google.auto.service:auto-service-annotations:1.0.1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
com.google.auto.value:auto-value-annotations:1.11.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
@@ -12,13 +12,13 @@ com.google.auto:auto-common:1.2.1=annotationProcessor,errorprone,testAnnotationP
|
||||
com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,errorprone,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath,testing,testingAnnotationProcessor,testingCompileClasspath
|
||||
com.google.errorprone:error_prone_annotation:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
com.google.errorprone:error_prone_annotations:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
com.google.errorprone:error_prone_annotations:2.36.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
com.google.errorprone:error_prone_annotations:2.40.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
com.google.errorprone:error_prone_annotations:2.7.1=checkstyle
|
||||
com.google.errorprone:error_prone_check_api:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
com.google.errorprone:error_prone_core:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
com.google.errorprone:error_prone_type_annotations:2.23.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
com.google.errorprone:javac:9+181-r4173-1=errorproneJavac
|
||||
com.google.flogger:flogger:0.8=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
com.google.flogger:flogger:0.9=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
com.google.guava:failureaccess:1.0.1=annotationProcessor,checkstyle,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
com.google.guava:failureaccess:1.0.2=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
com.google.guava:guava-parent:32.1.1-jre=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
@@ -37,15 +37,14 @@ commons-collections:commons-collections:3.2.2=checkstyle
|
||||
info.picocli:picocli:4.6.2=checkstyle
|
||||
io.github.eisop:dataflow-errorprone:3.34.0-eisop1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
io.github.java-diff-utils:java-diff-utils:4.15=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
io.github.java-diff-utils:java-diff-utils:4.16=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
jakarta.inject:jakarta.inject-api:2.0.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
javax.inject:javax.inject:1=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
joda-time:joda-time:2.13.1=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
joda-time:joda-time:2.14.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
junit:junit:4.13.2=testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
net.sf.saxon:Saxon-HE:10.6=checkstyle
|
||||
org.antlr:antlr4-runtime:4.9.3=checkstyle
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
|
||||
org.checkerframework:checker-compat-qual:2.5.3=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
org.checkerframework:checker-qual:3.12.0=checkstyle
|
||||
org.checkerframework:checker-qual:3.33.0=annotationProcessor,errorprone,testAnnotationProcessor,testingAnnotationProcessor
|
||||
org.checkerframework:checker-qual:3.42.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
@@ -56,12 +55,12 @@ org.jacoco:org.jacoco.core:0.8.12=jacocoAnt
|
||||
org.jacoco:org.jacoco.report:0.8.12=jacocoAnt
|
||||
org.javassist:javassist:3.28.0-GA=checkstyle
|
||||
org.jspecify:jspecify:1.0.0=compileClasspath,deploy_jar,runtimeClasspath,testCompileClasspath,testRuntimeClasspath,testing,testingCompileClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:5.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:5.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:1.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:1.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:1.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit:junit-bom:5.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:5.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:5.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:1.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:1.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:1.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit:junit-bom:5.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.ow2.asm:asm-commons:9.7=jacocoAnt
|
||||
org.ow2.asm:asm-tree:9.7=jacocoAnt
|
||||
|
||||
@@ -92,17 +92,19 @@ public class TextDiffSubject extends Subject {
|
||||
|
||||
private ImmutableList<String> filterComments(List<String> lines) {
|
||||
return lines.stream()
|
||||
.filter(line -> !line.isBlank())
|
||||
.filter(line -> comments.stream().noneMatch(line::startsWith))
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
}
|
||||
|
||||
public void hasSameContentAs(List<String> expectedContent) {
|
||||
checkNotNull(expectedContent, "expectedContent");
|
||||
ImmutableList<String> expected = filterComments(expectedContent);
|
||||
if (filterComments(expected).equals(filterComments(actual))) {
|
||||
ImmutableList<String> filteredExpected = filterComments(expectedContent);
|
||||
ImmutableList<String> filteredActual = filterComments(actual);
|
||||
if (filteredExpected.equals(filteredActual)) {
|
||||
return;
|
||||
}
|
||||
String diffString = diffFormat.generateDiff(expected, actual);
|
||||
String diffString = diffFormat.generateDiff(filteredExpected, filteredActual);
|
||||
failWithoutActual(
|
||||
Fact.simpleFact(
|
||||
Joiner.on('\n')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"/console-api":
|
||||
{
|
||||
"target": "http://localhost:8080",
|
||||
"target": "http://[::1]:8080",
|
||||
"secure": false,
|
||||
"logLevel": "debug",
|
||||
"changeOrigin": true
|
||||
|
||||
@@ -26,6 +26,8 @@ import SecurityComponent from './settings/security/security.component';
|
||||
import { SettingsComponent } from './settings/settings.component';
|
||||
import { SupportComponent } from './support/support.component';
|
||||
import RdapComponent from './settings/rdap/rdap.component';
|
||||
import { HistoryComponent } from './history/history.component';
|
||||
import { PasswordResetVerifyComponent } from './shared/components/passwordReset/passwordResetVerify.component';
|
||||
|
||||
export interface RouteWithIcon extends Route {
|
||||
iconName?: string;
|
||||
@@ -38,6 +40,10 @@ export const PATHS = {
|
||||
};
|
||||
export const routes: RouteWithIcon[] = [
|
||||
{ path: '', redirectTo: '/home', pathMatch: 'full' },
|
||||
{
|
||||
path: PasswordResetVerifyComponent.PATH,
|
||||
component: PasswordResetVerifyComponent,
|
||||
},
|
||||
{
|
||||
path: RegistryLockVerifyComponent.PATH,
|
||||
component: RegistryLockVerifyComponent,
|
||||
@@ -59,13 +65,18 @@ export const routes: RouteWithIcon[] = [
|
||||
title: 'Dashboard',
|
||||
iconName: 'view_comfy_alt',
|
||||
},
|
||||
// { path: 'tlds', component: TldsComponent, title: "TLDs", iconName: "event_list" },
|
||||
{
|
||||
path: DomainListComponent.PATH,
|
||||
component: DomainListComponent,
|
||||
title: 'Domains',
|
||||
iconName: 'view_list',
|
||||
},
|
||||
{
|
||||
path: HistoryComponent.PATH,
|
||||
component: HistoryComponent,
|
||||
// title: 'History',
|
||||
// iconName: 'history',
|
||||
},
|
||||
{
|
||||
path: SettingsComponent.PATH,
|
||||
component: SettingsComponent,
|
||||
|
||||
@@ -56,11 +56,14 @@ import { GlobalLoaderService } from './shared/services/globalLoader.service';
|
||||
import { UserDataService } from './shared/services/userData.service';
|
||||
import { SnackBarModule } from './snackbar.module';
|
||||
import { SupportComponent } from './support/support.component';
|
||||
import { TldsComponent } from './tlds/tlds.component';
|
||||
import { ForceFocusDirective } from './shared/directives/forceFocus.directive';
|
||||
import RdapComponent from './settings/rdap/rdap.component';
|
||||
import RdapEditComponent from './settings/rdap/rdapEdit.component';
|
||||
import { PocReminderComponent } from './shared/components/pocReminder/pocReminder.component';
|
||||
import { PasswordResetVerifyComponent } from './shared/components/passwordReset/passwordResetVerify.component';
|
||||
import { PasswordInputForm } from './shared/components/passwordReset/passwordInputForm.component';
|
||||
import { HistoryComponent } from './history/history.component';
|
||||
import { HistoryListComponent } from './history/historyList.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [SelectedRegistrarWrapper],
|
||||
@@ -79,15 +82,19 @@ export class SelectedRegistrarModule {}
|
||||
EppPasswordEditComponent,
|
||||
ForceFocusDirective,
|
||||
HeaderComponent,
|
||||
HistoryComponent,
|
||||
HistoryListComponent,
|
||||
HomeComponent,
|
||||
LocationBackDirective,
|
||||
NavigationComponent,
|
||||
NewRegistrarComponent,
|
||||
NotificationsComponent,
|
||||
PasswordInputForm,
|
||||
PasswordResetVerifyComponent,
|
||||
PocReminderComponent,
|
||||
RdapComponent,
|
||||
RdapEditComponent,
|
||||
ReasonDialogComponent,
|
||||
PocReminderComponent,
|
||||
RegistrarComponent,
|
||||
RegistrarDetailsComponent,
|
||||
RegistrarSelectorComponent,
|
||||
@@ -100,7 +107,6 @@ export class SelectedRegistrarModule {}
|
||||
SettingsComponent,
|
||||
SettingsContactComponent,
|
||||
SupportComponent,
|
||||
TldsComponent,
|
||||
UserLevelVisibility,
|
||||
],
|
||||
bootstrap: [AppComponent],
|
||||
|
||||
62
console-webapp/src/app/history/history.component.html
Normal file
62
console-webapp/src/app/history/history.component.html
Normal file
@@ -0,0 +1,62 @@
|
||||
<app-selected-registrar-wrapper>
|
||||
<div class="history-log">
|
||||
<h1 class="mat-headline-4" forceFocus>
|
||||
Registrar Console Activity History
|
||||
</h1>
|
||||
<mat-tab-group
|
||||
[elementId]="getElementIdForUserLog()"
|
||||
class="history-log__tabs"
|
||||
>
|
||||
<mat-tab label="Registrar Activity">
|
||||
<div class="spacer"></div>
|
||||
|
||||
<app-history-list
|
||||
[historyRecords]="historyService.historyRecordsRegistrar()"
|
||||
[isLoading]="isLoading"
|
||||
/>
|
||||
</mat-tab>
|
||||
<mat-tab label="User Activity">
|
||||
<div class="spacer"></div>
|
||||
<form (ngSubmit)="loadHistory()" #form="ngForm">
|
||||
<section>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Console User Email: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
id="email"
|
||||
type="email"
|
||||
name="consoleUserEmail"
|
||||
required
|
||||
email
|
||||
[(ngModel)]="consoleUserEmail"
|
||||
#emailControl="ngModel"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</section>
|
||||
<div class="spacer"></div>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
type="submit"
|
||||
aria-label="Search user history"
|
||||
[disabled]="!form.valid"
|
||||
>
|
||||
Search
|
||||
</button>
|
||||
</form>
|
||||
<div class="spacer"></div>
|
||||
<app-history-list
|
||||
[historyRecords]="historyService.historyRecordsUser()"
|
||||
[isLoading]="isLoading"
|
||||
/>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
|
||||
<app-history-list
|
||||
[elementId]="getElementIdForUserLog()"
|
||||
[isReverse]="true"
|
||||
[historyRecords]="historyService.historyRecordsUser()"
|
||||
[isLoading]="isLoading"
|
||||
/>
|
||||
</app-selected-registrar-wrapper>
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,17 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
.console-tlds {
|
||||
&__cards {
|
||||
display: flex;
|
||||
border-top: 1px solid #ddd;
|
||||
padding: 1rem;
|
||||
}
|
||||
&__card {
|
||||
max-width: 300px;
|
||||
}
|
||||
&__card-links {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.history-log {
|
||||
font-family: "Roboto", sans-serif;
|
||||
max-width: 760px;
|
||||
|
||||
.spacer {
|
||||
margin: 20px 0;
|
||||
}
|
||||
}
|
||||
80
console-webapp/src/app/history/history.component.ts
Normal file
80
console-webapp/src/app/history/history.component.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, effect } from '@angular/core';
|
||||
import { UserDataService } from '../shared/services/userData.service';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { RegistrarService } from '../registrar/registrar.service';
|
||||
import { HistoryService } from './history.service';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import {
|
||||
GlobalLoader,
|
||||
GlobalLoaderService,
|
||||
} from '../shared/services/globalLoader.service';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { RESTRICTED_ELEMENTS } from '../shared/directives/userLevelVisiblity.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'app-history',
|
||||
templateUrl: './history.component.html',
|
||||
styleUrls: ['./history.component.scss'],
|
||||
providers: [HistoryService],
|
||||
standalone: false,
|
||||
})
|
||||
export class HistoryComponent implements GlobalLoader {
|
||||
public static PATH = 'history';
|
||||
|
||||
consoleUserEmail: string = '';
|
||||
isLoading: boolean = false;
|
||||
|
||||
constructor(
|
||||
private backendService: BackendService,
|
||||
private registrarService: RegistrarService,
|
||||
protected historyService: HistoryService,
|
||||
protected globalLoader: GlobalLoaderService,
|
||||
protected userDataService: UserDataService,
|
||||
private _snackBar: MatSnackBar
|
||||
) {
|
||||
effect(() => {
|
||||
if (registrarService.registrarId()) {
|
||||
this.loadHistory();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getElementIdForUserLog() {
|
||||
return RESTRICTED_ELEMENTS.ACTIVITY_PER_USER;
|
||||
}
|
||||
|
||||
loadingTimeout() {
|
||||
this._snackBar.open('Timeout loading records history');
|
||||
}
|
||||
|
||||
loadHistory() {
|
||||
this.globalLoader.startGlobalLoader(this);
|
||||
this.isLoading = true;
|
||||
this.historyService
|
||||
.getHistoryLog(this.registrarService.registrarId(), this.consoleUserEmail)
|
||||
.subscribe({
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.error || err.message);
|
||||
this.isLoading = false;
|
||||
},
|
||||
next: () => {
|
||||
this.globalLoader.stopGlobalLoader(this);
|
||||
this.isLoading = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
46
console-webapp/src/app/history/history.service.ts
Normal file
46
console-webapp/src/app/history/history.service.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { tap } from 'rxjs';
|
||||
|
||||
export interface HistoryRecord {
|
||||
modificationTime: string;
|
||||
type: string;
|
||||
description: string;
|
||||
actingUser: {
|
||||
emailAddress: string;
|
||||
};
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class HistoryService {
|
||||
historyRecordsRegistrar = signal<HistoryRecord[]>([]);
|
||||
historyRecordsUser = signal<HistoryRecord[]>([]);
|
||||
|
||||
constructor(private backendService: BackendService) {}
|
||||
|
||||
getHistoryLog(registrarId: string, userEmail?: string) {
|
||||
return this.backendService.getHistoryLog(registrarId, userEmail).pipe(
|
||||
tap((historyRecords: HistoryRecord[]) => {
|
||||
if (userEmail) {
|
||||
this.historyRecordsUser.set(historyRecords);
|
||||
} else {
|
||||
this.historyRecordsRegistrar.set(historyRecords);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
50
console-webapp/src/app/history/historyList.component.html
Normal file
50
console-webapp/src/app/history/historyList.component.html
Normal file
@@ -0,0 +1,50 @@
|
||||
@if (!isLoading && historyRecords.length == 0) {
|
||||
<div class="history-list__no-records">
|
||||
<mat-icon class="history-list__no-records-icon secondary-text"
|
||||
>apps_outage</mat-icon
|
||||
>
|
||||
<h1>No records found</h1>
|
||||
</div>
|
||||
} @else {
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<mat-list role="list">
|
||||
<ng-container *ngFor="let item of historyRecords; let last = last">
|
||||
<mat-list-item class="history-list__item">
|
||||
<mat-icon
|
||||
[ngClass]="getIconClass(item.type)"
|
||||
class="history-list__icon"
|
||||
>
|
||||
{{ getIconForType(item.type) }}
|
||||
</mat-icon>
|
||||
|
||||
<div class="history-list__content">
|
||||
<div class="history-list__description">
|
||||
<span class="history-list__description--main">{{
|
||||
item.type
|
||||
}}</span>
|
||||
<div>
|
||||
<mat-chip
|
||||
*ngIf="parseDescription(item.description).detail"
|
||||
class="history-list__chip"
|
||||
>
|
||||
{{ parseDescription(item.description).detail }}
|
||||
</mat-chip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="history-list__user">
|
||||
<b>User - {{ item.actingUser.emailAddress }}</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="history-list__timestamp">
|
||||
{{ item.modificationTime | date : "MMM d, y, h:mm a" }}
|
||||
</span>
|
||||
</mat-list-item>
|
||||
|
||||
<mat-divider *ngIf="!last"></mat-divider>
|
||||
</ng-container>
|
||||
</mat-list>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
}
|
||||
81
console-webapp/src/app/history/historyList.component.scss
Normal file
81
console-webapp/src/app/history/historyList.component.scss
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
.history-list {
|
||||
font-family: "Roboto", sans-serif;
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
// Override default mat-list-item height to fit content
|
||||
height: auto !important;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
&__no-records {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
&__no-records-icon {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
font-size: 4rem;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
margin-right: 16px;
|
||||
|
||||
&--update {
|
||||
color: #1976d2;
|
||||
}
|
||||
|
||||
&--security {
|
||||
color: #d32f2f;
|
||||
}
|
||||
}
|
||||
|
||||
&__description {
|
||||
&--main {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.87);
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
&__chip {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
&__user {
|
||||
font-size: 0.9rem;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
&__timestamp {
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
66
console-webapp/src/app/history/historyList.component.ts
Normal file
66
console-webapp/src/app/history/historyList.component.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { HistoryRecord } from './history.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-history-list',
|
||||
templateUrl: './historyList.component.html',
|
||||
styleUrls: ['./historyList.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
standalone: false,
|
||||
})
|
||||
export class HistoryListComponent {
|
||||
@Input() historyRecords: HistoryRecord[] = [];
|
||||
@Input() isLoading: boolean = false;
|
||||
|
||||
getIconForType(type: string): string {
|
||||
switch (type) {
|
||||
case 'REGISTRAR_UPDATE':
|
||||
return 'edit';
|
||||
case 'REGISTRAR_SECURITY_UPDATE':
|
||||
return 'security';
|
||||
default:
|
||||
return 'history'; // A fallback icon
|
||||
}
|
||||
}
|
||||
|
||||
getIconClass(type: string): string {
|
||||
switch (type) {
|
||||
case 'REGISTRAR_UPDATE':
|
||||
return 'history-log__icon--update';
|
||||
case 'REGISTRAR_SECURITY_UPDATE':
|
||||
return 'history-log__icon--security';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
parseDescription(description: string): {
|
||||
main: string;
|
||||
detail: string | null;
|
||||
} {
|
||||
if (!description) {
|
||||
return { main: 'N/A', detail: null };
|
||||
}
|
||||
const parts = description.split('|');
|
||||
const detail = parts.length > 1 ? parts[1].replace(/_/g, ' ') : null;
|
||||
|
||||
return {
|
||||
main: parts[0],
|
||||
detail: detail,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,10 @@ export class RegistrarSelectorComponent {
|
||||
registrarInput = signal<string>(this.registrarService.registrarId());
|
||||
filteredOptions?: string[];
|
||||
allRegistrarIds = computed(() =>
|
||||
this.registrarService.registrars().map((r) => r.registrarId)
|
||||
this.registrarService
|
||||
.registrars()
|
||||
.map((r) => r.registrarId)
|
||||
.sort()
|
||||
);
|
||||
|
||||
constructor(protected registrarService: RegistrarService) {
|
||||
|
||||
@@ -16,65 +16,22 @@
|
||||
<p class="secondary-text">
|
||||
Passwords must be between 6 and 16 alphanumeric characters
|
||||
</p>
|
||||
<form
|
||||
(ngSubmit)="save()"
|
||||
<password-input-form-component
|
||||
[displayOldPasswordField]="true"
|
||||
[formGroup]="passwordUpdateForm"
|
||||
class="settings-security__edit-password-form"
|
||||
>
|
||||
<div class="settings-security__edit-password-field">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Old password: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
formControlName="oldPassword"
|
||||
required
|
||||
autocomplete="current-password"
|
||||
/>
|
||||
<mat-error *ngIf="hasError('oldPassword') as errorText">{{
|
||||
errorText
|
||||
}}</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="settings-security__edit-password-field">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>New password: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
formControlName="newPassword"
|
||||
required
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
<mat-error *ngIf="hasError('newPassword') as errorText">{{
|
||||
errorText
|
||||
}}</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="settings-security__edit-password-field">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Confirm new password: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
formControlName="newPasswordRepeat"
|
||||
required
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
<mat-error *ngIf="hasError('newPasswordRepeat') as errorText">{{
|
||||
errorText
|
||||
}}</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
(submitResults)="save($event)"
|
||||
/>
|
||||
@if(userDataService.userData()?.isAdmin) {
|
||||
<div class="settings-security__reset-password-field">
|
||||
<h2>Need to reset your EPP password?</h2>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
[disabled]="!passwordUpdateForm.valid"
|
||||
aria-label="Save epp password update"
|
||||
type="submit"
|
||||
class="settings-security__edit-password-save"
|
||||
aria-label="Reset EPP password via email"
|
||||
(click)="requestEppPasswordReset()"
|
||||
>
|
||||
Save
|
||||
Reset EPP password via email
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
.settings-security__edit-password {
|
||||
max-width: 616px;
|
||||
&-field {
|
||||
width: 100%;
|
||||
mat-form-field {
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
&-form {
|
||||
margin-top: 30px;
|
||||
}
|
||||
&-save {
|
||||
margin-top: 30px;
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
.settings-security {
|
||||
&__reset-password-field {
|
||||
margin-top: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,20 +14,46 @@
|
||||
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
AbstractControl,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
ValidatorFn,
|
||||
Validators,
|
||||
} from '@angular/forms';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
||||
import { SecurityService } from './security.service';
|
||||
import { UserDataService } from 'src/app/shared/services/userData.service';
|
||||
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MaterialModule } from 'src/app/material.module';
|
||||
import { filter, switchMap, take } from 'rxjs';
|
||||
import { BackendService } from 'src/app/shared/services/backend.service';
|
||||
import {
|
||||
PasswordInputForm,
|
||||
PasswordResults,
|
||||
} from 'src/app/shared/components/passwordReset/passwordInputForm.component';
|
||||
|
||||
type errorCode = 'required' | 'maxlength' | 'minlength' | 'passwordsDontMatch';
|
||||
@Component({
|
||||
selector: 'app-reset-epp-password-dialog',
|
||||
template: `
|
||||
<h2 mat-dialog-title>Please confirm the password reset:</h2>
|
||||
<mat-dialog-content>
|
||||
This will send an EPP password reset email to the admin POC.
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button (click)="onCancel()">Cancel</button>
|
||||
<button mat-button color="warn" (click)="onSave()">Confirm</button>
|
||||
</mat-dialog-actions>
|
||||
`,
|
||||
imports: [CommonModule, MaterialModule],
|
||||
})
|
||||
export class ResetEppPasswordComponent {
|
||||
constructor(public dialogRef: MatDialogRef<ResetEppPasswordComponent>) {}
|
||||
|
||||
type errorFriendlyText = { [type in errorCode]: String };
|
||||
onSave(): void {
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
|
||||
onCancel(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-epp-password-edit',
|
||||
@@ -36,76 +62,38 @@ type errorFriendlyText = { [type in errorCode]: String };
|
||||
standalone: false,
|
||||
})
|
||||
export default class EppPasswordEditComponent {
|
||||
MIN_MAX_LENGHT = new String(
|
||||
'Passwords must be between 6 and 16 alphanumeric characters'
|
||||
);
|
||||
|
||||
errorTextMap: errorFriendlyText = {
|
||||
required: "This field can't be empty",
|
||||
maxlength: this.MIN_MAX_LENGHT,
|
||||
minlength: this.MIN_MAX_LENGHT,
|
||||
passwordsDontMatch: "Passwords don't match",
|
||||
};
|
||||
|
||||
constructor(
|
||||
public securityService: SecurityService,
|
||||
private _snackBar: MatSnackBar,
|
||||
public registrarService: RegistrarService
|
||||
) {}
|
||||
|
||||
hasError(controlName: string) {
|
||||
const maybeErrors = this.passwordUpdateForm.get(controlName)?.errors;
|
||||
const maybeError =
|
||||
maybeErrors && (Object.keys(maybeErrors)[0] as errorCode);
|
||||
if (maybeError) {
|
||||
return this.errorTextMap[maybeError];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
newPasswordsMatch: ValidatorFn = (control: AbstractControl) => {
|
||||
if (
|
||||
this.passwordUpdateForm?.get('newPassword')?.value ===
|
||||
this.passwordUpdateForm?.get('newPasswordRepeat')?.value
|
||||
) {
|
||||
this.passwordUpdateForm?.get('newPasswordRepeat')?.setErrors(null);
|
||||
} else {
|
||||
// latest angular just won't detect the error without setTimeout
|
||||
setTimeout(() => {
|
||||
this.passwordUpdateForm
|
||||
?.get('newPasswordRepeat')
|
||||
?.setErrors({ passwordsDontMatch: control.value });
|
||||
});
|
||||
}
|
||||
return null;
|
||||
};
|
||||
static EPP_VALIDATORS = [
|
||||
Validators.required,
|
||||
Validators.minLength(6),
|
||||
Validators.maxLength(16),
|
||||
PasswordInputForm.newPasswordsMatch,
|
||||
];
|
||||
|
||||
passwordUpdateForm = new FormGroup({
|
||||
oldPassword: new FormControl('', [Validators.required]),
|
||||
newPassword: new FormControl('', [
|
||||
Validators.required,
|
||||
Validators.minLength(6),
|
||||
Validators.maxLength(16),
|
||||
this.newPasswordsMatch,
|
||||
]),
|
||||
newPasswordRepeat: new FormControl('', [
|
||||
Validators.required,
|
||||
Validators.minLength(6),
|
||||
Validators.maxLength(16),
|
||||
this.newPasswordsMatch,
|
||||
]),
|
||||
newPassword: new FormControl('', EppPasswordEditComponent.EPP_VALIDATORS),
|
||||
newPasswordRepeat: new FormControl(
|
||||
'',
|
||||
EppPasswordEditComponent.EPP_VALIDATORS
|
||||
),
|
||||
});
|
||||
|
||||
save() {
|
||||
const { oldPassword, newPassword, newPasswordRepeat } =
|
||||
this.passwordUpdateForm.value;
|
||||
if (!oldPassword || !newPassword || !newPasswordRepeat) return;
|
||||
constructor(
|
||||
public registrarService: RegistrarService,
|
||||
public securityService: SecurityService,
|
||||
protected userDataService: UserDataService,
|
||||
private backendService: BackendService,
|
||||
private resetPasswordDialog: MatDialog,
|
||||
private _snackBar: MatSnackBar
|
||||
) {}
|
||||
|
||||
save(passwordResults: PasswordResults) {
|
||||
this.securityService
|
||||
.saveEppPassword({
|
||||
registrarId: this.registrarService.registrarId(),
|
||||
oldPassword,
|
||||
newPassword,
|
||||
newPasswordRepeat,
|
||||
oldPassword: passwordResults.oldPassword!,
|
||||
newPassword: passwordResults.newPassword,
|
||||
newPasswordRepeat: passwordResults.newPasswordRepeat,
|
||||
})
|
||||
.subscribe({
|
||||
complete: () => {
|
||||
@@ -120,4 +108,26 @@ export default class EppPasswordEditComponent {
|
||||
goBack() {
|
||||
this.securityService.isEditingPassword = false;
|
||||
}
|
||||
|
||||
sendEppPasswordResetRequest() {
|
||||
return this.backendService.requestEppPasswordReset(
|
||||
this.registrarService.registrarId()
|
||||
);
|
||||
}
|
||||
|
||||
requestEppPasswordReset() {
|
||||
const dialogRef = this.resetPasswordDialog.open(ResetEppPasswordComponent);
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
filter((result) => !!result)
|
||||
)
|
||||
.pipe(switchMap((_) => this.sendEppPasswordResetRequest()))
|
||||
.subscribe({
|
||||
next: (_) => this.goBack(),
|
||||
error: (err: HttpErrorResponse) =>
|
||||
this._snackBar.open(err.error || err.message),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<form
|
||||
(ngSubmit)="save()"
|
||||
[formGroup]="formGroup()!"
|
||||
class="console-app__password-input-form"
|
||||
>
|
||||
@if (displayOldPasswordField()) {
|
||||
<div class="console-app__password-input-form-field">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Old password: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
formControlName="oldPassword"
|
||||
required
|
||||
autocomplete="current-password"
|
||||
/>
|
||||
<mat-error *ngIf="hasError('oldPassword') as errorText">{{
|
||||
errorText
|
||||
}}</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
<div class="console-app__password-input-form-field">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>New password: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
formControlName="newPassword"
|
||||
required
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
<mat-error *ngIf="hasError('newPassword') as errorText">{{
|
||||
errorText
|
||||
}}</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="console-app__password-input-form-field">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>Confirm new password: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
formControlName="newPasswordRepeat"
|
||||
required
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
<mat-error *ngIf="hasError('newPasswordRepeat') as errorText">{{
|
||||
errorText
|
||||
}}</mat-error>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
[disabled]="!formGroup()?.valid"
|
||||
aria-label="Save new password"
|
||||
type="submit"
|
||||
class="console-app__password-input-form-save"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2024 The Nomulus Authors. All Rights Reserved.
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,12 +12,19 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tlds',
|
||||
templateUrl: './tlds.component.html',
|
||||
styleUrls: ['./tlds.component.scss'],
|
||||
standalone: false,
|
||||
})
|
||||
export class TldsComponent {}
|
||||
.console-app__password-input-form {
|
||||
max-width: 450px;
|
||||
&-field {
|
||||
width: 100%;
|
||||
mat-form-field {
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
&-form {
|
||||
margin-top: 30px;
|
||||
}
|
||||
&-save {
|
||||
margin-top: 30px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, EventEmitter, input, Output } from '@angular/core';
|
||||
import { AbstractControl, FormGroup, ValidatorFn } from '@angular/forms';
|
||||
|
||||
type errorCode = 'required' | 'maxlength' | 'minlength' | 'passwordsDontMatch';
|
||||
|
||||
type errorFriendlyText = { [type in errorCode]: String };
|
||||
|
||||
export interface PasswordResults {
|
||||
oldPassword: string | null;
|
||||
newPassword: string;
|
||||
newPasswordRepeat: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'password-input-form-component',
|
||||
templateUrl: './passwordInputForm.component.html',
|
||||
styleUrls: ['./passwordInputForm.component.scss'],
|
||||
standalone: false,
|
||||
})
|
||||
export class PasswordInputForm {
|
||||
static newPasswordsMatch: ValidatorFn = (control: AbstractControl) => {
|
||||
const parent = control.parent;
|
||||
if (
|
||||
parent?.get('newPassword')?.value ===
|
||||
parent?.get('newPasswordRepeat')?.value
|
||||
) {
|
||||
parent?.get('newPasswordRepeat')?.setErrors(null);
|
||||
} else {
|
||||
// latest angular just won't detect the error without setTimeout
|
||||
setTimeout(() => {
|
||||
parent
|
||||
?.get('newPasswordRepeat')
|
||||
?.setErrors({ passwordsDontMatch: control.value });
|
||||
});
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
MIN_MAX_LENGTH = 'Passwords must be between 6 and 16 alphanumeric characters';
|
||||
|
||||
errorTextMap: errorFriendlyText = {
|
||||
required: "This field can't be empty",
|
||||
maxlength: this.MIN_MAX_LENGTH,
|
||||
minlength: this.MIN_MAX_LENGTH,
|
||||
passwordsDontMatch: "Passwords don't match",
|
||||
};
|
||||
|
||||
displayOldPasswordField = input<boolean>(false);
|
||||
formGroup = input<FormGroup>();
|
||||
@Output() submitResults = new EventEmitter<PasswordResults>();
|
||||
|
||||
hasError(controlName: string) {
|
||||
const maybeErrors = this.formGroup()!.get(controlName)?.errors;
|
||||
const maybeError =
|
||||
maybeErrors && (Object.keys(maybeErrors)[0] as errorCode);
|
||||
if (maybeError) {
|
||||
return this.errorTextMap[maybeError];
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
save() {
|
||||
const results: PasswordResults = this.formGroup()!.value;
|
||||
if (this.displayOldPasswordField() && !results.oldPassword) return;
|
||||
if (!results.newPassword || !results.newPasswordRepeat) return;
|
||||
this.submitResults.emit(results);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<p>
|
||||
<button mat-icon-button aria-label="Go home" [routerLink]="['']">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
</p>
|
||||
@if (isLoading) {
|
||||
<div class="console-app__password-reset-verify-spinner">
|
||||
<mat-spinner />
|
||||
</div>
|
||||
} @else if (errorMessage) {
|
||||
<h1 class="mat-headline-4">Failure</h1>
|
||||
<div class="console-app__password-reset-content">
|
||||
<div class="console-app__password-reset-subhead">
|
||||
An error occurred: {{ errorMessage }}.<br /><br />Please double-check the
|
||||
verification code and try again.
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="console-app__password-reset-verify">
|
||||
<h1 class="mat-headline-4">{{ type }} password reset</h1>
|
||||
<password-input-form-component
|
||||
[displayOldPasswordField]="false"
|
||||
[formGroup]="passwordUpdateForm!"
|
||||
(submitResults)="save($event)"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { take } from 'rxjs';
|
||||
import { RegistrarService } from 'src/app/registrar/registrar.service';
|
||||
import { BackendService } from '../../services/backend.service';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import {
|
||||
PasswordInputForm,
|
||||
PasswordResults,
|
||||
} from './passwordInputForm.component';
|
||||
import EppPasswordEditComponent from 'src/app/settings/security/eppPasswordEdit.component';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
|
||||
export interface PasswordResetVerifyResponse {
|
||||
registrarId: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-password-reset-verify',
|
||||
templateUrl: './passwordResetVerify.component.html',
|
||||
standalone: false,
|
||||
})
|
||||
export class PasswordResetVerifyComponent {
|
||||
public static PATH = 'password-reset-verify';
|
||||
|
||||
REGISTRY_LOCK_PASSWORD_VALIDATORS = [
|
||||
Validators.required,
|
||||
PasswordInputForm.newPasswordsMatch,
|
||||
];
|
||||
|
||||
isLoading = true;
|
||||
type?: string;
|
||||
errorMessage?: string;
|
||||
requestVerificationCode = '';
|
||||
|
||||
passwordUpdateForm: FormGroup<any> | null = null;
|
||||
|
||||
constructor(
|
||||
protected backendService: BackendService,
|
||||
protected registrarService: RegistrarService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private _snackBar: MatSnackBar
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.route.queryParamMap.pipe(take(1)).subscribe((params: ParamMap) => {
|
||||
this.requestVerificationCode =
|
||||
params.get('resetRequestVerificationCode') || '';
|
||||
this.backendService
|
||||
.getPasswordResetInformation(this.requestVerificationCode)
|
||||
.subscribe({
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this.isLoading = false;
|
||||
this.errorMessage = err.error;
|
||||
},
|
||||
next: this.presentData.bind(this),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
presentData(verificationResponse: PasswordResetVerifyResponse) {
|
||||
this.type = verificationResponse.type === 'EPP' ? 'EPP' : 'Registry lock';
|
||||
this.registrarService.registrarId.set(verificationResponse.registrarId);
|
||||
const validators =
|
||||
verificationResponse.type === 'EPP'
|
||||
? EppPasswordEditComponent.EPP_VALIDATORS
|
||||
: this.REGISTRY_LOCK_PASSWORD_VALIDATORS;
|
||||
|
||||
this.passwordUpdateForm = new FormGroup({
|
||||
newPassword: new FormControl('', validators),
|
||||
newPasswordRepeat: new FormControl('', validators),
|
||||
});
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
save(passwordResults: PasswordResults) {
|
||||
this.backendService
|
||||
.finalizePasswordReset(
|
||||
this.requestVerificationCode,
|
||||
passwordResults.newPassword
|
||||
)
|
||||
.subscribe({
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this.isLoading = false;
|
||||
this.errorMessage = err.error;
|
||||
},
|
||||
next: (_) => {
|
||||
this.router.navigate(['']);
|
||||
this._snackBar.open('Password reset completed successfully');
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import { Directive, ElementRef, Input, effect } from '@angular/core';
|
||||
import { UserDataService } from '../services/userData.service';
|
||||
|
||||
export enum RESTRICTED_ELEMENTS {
|
||||
ACTIVITY_PER_USER,
|
||||
REGISTRAR_ELEMENT,
|
||||
OTE,
|
||||
USERS,
|
||||
@@ -28,9 +29,10 @@ export const DISABLED_ELEMENTS_PER_ROLE = {
|
||||
RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT,
|
||||
RESTRICTED_ELEMENTS.OTE,
|
||||
RESTRICTED_ELEMENTS.SUSPEND,
|
||||
RESTRICTED_ELEMENTS.ACTIVITY_PER_USER,
|
||||
],
|
||||
SUPPORT_LEAD: [],
|
||||
SUPPORT_AGENT: [],
|
||||
SUPPORT_AGENT: [RESTRICTED_ELEMENTS.ACTIVITY_PER_USER],
|
||||
};
|
||||
|
||||
@Directive({
|
||||
@@ -40,6 +42,8 @@ export const DISABLED_ELEMENTS_PER_ROLE = {
|
||||
export class UserLevelVisibility {
|
||||
@Input() elementId!: RESTRICTED_ELEMENTS | null;
|
||||
|
||||
@Input() isReverse: boolean = false;
|
||||
|
||||
constructor(
|
||||
private userDataService: UserDataService,
|
||||
private el: ElementRef
|
||||
@@ -56,9 +60,9 @@ export class UserLevelVisibility {
|
||||
// @ts-ignore
|
||||
(DISABLED_ELEMENTS_PER_ROLE[globalRole] || []).includes(this.elementId)
|
||||
) {
|
||||
this.el.nativeElement.style.display = 'none';
|
||||
this.el.nativeElement.style.display = this.isReverse ? '' : 'none';
|
||||
} else {
|
||||
this.el.nativeElement.style.display = '';
|
||||
this.el.nativeElement.style.display = this.isReverse ? 'none' : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import {
|
||||
import { Contact } from '../../settings/contact/contact.service';
|
||||
import { EppPasswordBackendModel } from '../../settings/security/security.service';
|
||||
import { UserData } from './userData.service';
|
||||
import { PasswordResetVerifyResponse } from '../components/passwordReset/passwordResetVerify.component';
|
||||
import { HistoryRecord } from '../../history/history.service';
|
||||
|
||||
@Injectable()
|
||||
export class BackendService {
|
||||
@@ -122,6 +124,16 @@ export class BackendService {
|
||||
.pipe(catchError((err) => this.errorCatcher<DomainListResult>(err)));
|
||||
}
|
||||
|
||||
getHistoryLog(registrarId: string, userEmail?: string) {
|
||||
return this.http
|
||||
.get<HistoryRecord[]>(
|
||||
userEmail
|
||||
? `/console-api/history?registrarId=${registrarId}&consoleUserEmail=${userEmail}`
|
||||
: `/console-api/history?registrarId=${registrarId}`
|
||||
)
|
||||
.pipe(catchError((err) => this.errorCatcher<HistoryRecord[]>(err)));
|
||||
}
|
||||
|
||||
getRegistrars(): Observable<Registrar[]> {
|
||||
return this.http
|
||||
.get<Registrar[]>('/console-api/registrars')
|
||||
@@ -291,4 +303,26 @@ export class BackendService {
|
||||
registryLockEmail,
|
||||
});
|
||||
}
|
||||
|
||||
requestEppPasswordReset(registrarId: string) {
|
||||
return this.http.post('/console-api/password-reset-request', {
|
||||
type: 'EPP',
|
||||
registrarId,
|
||||
});
|
||||
}
|
||||
|
||||
getPasswordResetInformation(
|
||||
verificationCode: string
|
||||
): Observable<PasswordResetVerifyResponse> {
|
||||
return this.http.get<PasswordResetVerifyResponse>(
|
||||
`/console-api/password-reset-verify?resetRequestVerificationCode=${verificationCode}`
|
||||
);
|
||||
}
|
||||
|
||||
finalizePasswordReset(verificationCode: string, newPassword: string) {
|
||||
return this.http.post(
|
||||
`/console-api/password-reset-verify?resetRequestVerificationCode=${verificationCode}`,
|
||||
newPassword
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<div class="console-tlds__cards"></div>
|
||||
@@ -1,38 +0,0 @@
|
||||
// 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.
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TldsComponent } from './tlds.component';
|
||||
import { MaterialModule } from '../material.module';
|
||||
|
||||
describe('TldsComponent', () => {
|
||||
let component: TldsComponent;
|
||||
let fixture: ComponentFixture<TldsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [MaterialModule],
|
||||
declarations: [TldsComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TldsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -22,7 +22,6 @@ import { RegistrarService } from '../registrar/registrar.service';
|
||||
import { SnackBarModule } from '../snackbar.module';
|
||||
import { UserDetailsComponent } from './userDetails.component';
|
||||
import { User, UsersService } from './users.service';
|
||||
import { UserDataService } from '../shared/services/userData.service';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { UsersListComponent } from './usersList.component';
|
||||
import { MatSelectChange } from '@angular/material/select';
|
||||
@@ -55,7 +54,6 @@ export class UsersComponent {
|
||||
constructor(
|
||||
protected registrarService: RegistrarService,
|
||||
protected usersService: UsersService,
|
||||
private userDataService: UserDataService,
|
||||
private _snackBar: MatSnackBar
|
||||
) {
|
||||
effect(() => {
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.util.Optional
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id "org.flywaydb.flyway" version "9.22.3"
|
||||
id "org.flywaydb.flyway" version "11.0.1"
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ def fragileTestPatterns = [
|
||||
// Changes cache timeouts and for some reason appears to have contention
|
||||
// with other tests.
|
||||
"google/registry/whois/WhoisCommandFactoryTest.*",
|
||||
// Breaks random other tests when running with standardTests.
|
||||
"google/registry/bsa/UploadBsaUnavailableDomainsActionTest.*",
|
||||
// Currently changes a global configuration parameter that for some reason
|
||||
// results in timestamp inversions for other tests. TODO(mmuller): fix.
|
||||
"google/registry/flows/host/HostInfoFlowTest.*",
|
||||
@@ -272,7 +274,6 @@ dependencies {
|
||||
testImplementation deps['org.hamcrest:hamcrest']
|
||||
testImplementation deps['org.hamcrest:hamcrest-core']
|
||||
testImplementation deps['org.hamcrest:hamcrest-library']
|
||||
testImplementation deps['junit:junit']
|
||||
testImplementation deps['org.junit.jupiter:junit-jupiter-api']
|
||||
testImplementation deps['org.junit.jupiter:junit-jupiter-engine']
|
||||
testImplementation deps['org.junit.jupiter:junit-jupiter-migrationsupport']
|
||||
|
||||
@@ -4,20 +4,19 @@
|
||||
aopalliance:aopalliance:1.0=annotationProcessor,compileClasspath,deploy_jar,errorprone,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
args4j:args4j:2.33=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.charleskorn.kaml:kaml:0.20.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-annotations:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-core:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-databind:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson:jackson-bom:2.18.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-annotations:2.20-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-core:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.core:jackson-databind:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-joda:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml.jackson:jackson-bom:2.20.0-rc1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.fasterxml:classmate:1.5.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.github.ben-manes.caffeine:caffeine:3.0.5=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.github.ben-manes.caffeine:caffeine:3.2.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-api:3.4.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.4.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-transport:3.4.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.ben-manes.caffeine:caffeine:3.2.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-api:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-transport-zerodep:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.docker-java:docker-java-transport:3.4.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.jnr:jffi:1.3.13=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.jnr:jnr-a64asm:1.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.github.jnr:jnr-constants:0.10.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -32,85 +31,80 @@ com.google.api-client:google-api-client-jackson2:2.0.1=compileClasspath,nonprodC
|
||||
com.google.api-client:google-api-client-jackson2:2.7.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-java6:2.1.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-servlet:2.7.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-servlet:2.7.2=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api-client:google-api-client:2.7.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api-client:google-api-client-servlet:2.8.1=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api-client:google-api-client:2.8.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:gapic-google-cloud-storage-v2:2.44.1-beta=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:gapic-google-cloud-storage-v2:2.50.0=testCompileClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.183.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.183.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigtable-v2:2.51.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.118.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-pubsublite-v1:1.15.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:gapic-google-cloud-storage-v2:2.55.0=testCompileClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1:3.15.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta1:0.187.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigquerystorage-v1beta2:0.187.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-bigtable-v2:2.60.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-pubsub-v1:1.122.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-pubsublite-v1:1.15.9=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-admin-database-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-admin-instance-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-spanner-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-control-v2:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-v2:2.44.1-beta=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-v2:2.50.0=testCompileClasspath
|
||||
com.google.api.grpc:grpc-google-common-protos:2.50.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.183.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.183.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigtable-admin-v2:2.51.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigtable-v2:2.51.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-compute-v1:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-datastore-v1:0.116.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-firestore-v1:3.30.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-monitoring-v3:3.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-pubsub-v1:1.118.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-pubsublite-v1:1.15.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:grpc-google-cloud-storage-v2:2.55.0=testCompileClasspath
|
||||
com.google.api.grpc:grpc-google-common-protos:2.58.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1:3.15.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1alpha:3.15.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta1:0.187.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta2:0.187.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigquerystorage-v1beta:3.15.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigtable-admin-v2:2.60.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-bigtable-v2:2.60.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-compute-v1:1.82.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-datastore-v1:0.120.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-firestore-v1:3.31.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-monitoring-v3:3.65.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-pubsub-v1:1.122.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-pubsublite-v1:1.15.9=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta1:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-v1:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-secretmanager-v1beta2:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-admin-database-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-admin-instance-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-spanner-v1:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-control-v2:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-v2:2.44.1-beta=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-v2:2.50.0=testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-storage-v2:2.55.0=testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.141.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.149.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta2:0.162.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.141.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.149.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-common-protos:2.53.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-common-protos:2.54.1=testCompileClasspath
|
||||
com.google.api.grpc:proto-google-iam-v1:1.45.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-iam-v1:1.48.0=compileClasspath,nonprodCompileClasspath
|
||||
com.google.api.grpc:proto-google-iam-v1:1.49.1=testCompileClasspath
|
||||
com.google.api:api-common:2.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api:api-common:2.46.1=testCompileClasspath
|
||||
com.google.api:gax-grpc:2.59.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-grpc:2.62.0=compileClasspath,nonprodCompileClasspath
|
||||
com.google.api:gax-grpc:2.63.1=testCompileClasspath
|
||||
com.google.api:gax-httpjson:2.62.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-httpjson:2.63.1=testCompileClasspath
|
||||
com.google.api:gax:2.62.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api:gax:2.63.1=testCompileClasspath
|
||||
com.google.apis:google-api-services-admin-directory:directory_v1-rev20250217-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-bigquery:v2-rev20241222-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-cloud-tasks-v2beta3:0.162.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api.grpc:proto-google-common-protos:2.60.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-iam-v1:1.53.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api.grpc:proto-google-iam-v1:1.55.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api:api-common:2.52.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-grpc:2.67.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.api:gax-grpc:2.69.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.api:gax-httpjson:2.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.api:gax:2.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-admin-directory:directory_v1-rev20250804-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-bigquery:v2-rev20250511-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-cloudresourcemanager:v1-rev20240310-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dataflow:v1b3-rev20250310-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dns:v1-rev20250227-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-drive:v3-rev20250220-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-gmail:v1-rev20240520-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dataflow:v1b3-rev20250812-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-dns:v1-rev20250411-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-drive:v3-rev20250723-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-gmail:v1-rev20250630-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-groupssettings:v1-rev20220614-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-healthcare:v1-rev20240130-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-iam:v2-rev20250213-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-iam:v2-rev20250502-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-iamcredentials:v1-rev20211203-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-monitoring:v3-rev20250227-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-monitoring:v3-rev20250731-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-pubsub:v1-rev20220904-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sheets:v4-rev20250211-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev20250205-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-storage:v1-rev20241206-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-storage:v1-rev20250224-2.0.0=testCompileClasspath
|
||||
com.google.auth:google-auth-library-credentials:1.33.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-oauth2-http:1.33.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sheets:v4-rev20250616-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-sqladmin:v1beta4-rev20250613-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-storage:v1-rev20250524-2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.apis:google-api-services-storage:v1-rev20250718-2.0.0=testCompileClasspath
|
||||
com.google.auth:google-auth-library-credentials:1.37.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auth:google-auth-library-oauth2-http:1.37.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auto.service:auto-service-annotations:1.0.1=errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.auto.service:auto-service:1.1.1=annotationProcessor
|
||||
@@ -121,50 +115,51 @@ com.google.auto:auto-common:1.2.1=annotationProcessor,errorprone,nonprodAnnotati
|
||||
com.google.cloud.bigdataoss:gcsio:2.2.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.bigdataoss:util:2.2.26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.bigtable:bigtable-client-core-config:1.28.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:2.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.datastore:datastore-v1-proto-client:2.29.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:detector-resources-support:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:exporter-metrics:0.33.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.opentelemetry:shared-resourcemapping:0.33.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.23.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:postgres-socket-factory:1.23.1=deploy_jar,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigquerystorage:3.11.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigtable:2.51.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-compute:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-grpc:2.49.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-grpc:2.53.1=testCompileClasspath
|
||||
com.google.cloud.sql:jdbc-socket-factory-core:1.25.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud.sql:postgres-socket-factory:1.25.3=deploy_jar,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigquerystorage:3.15.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-bigtable:2.60.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-compute:1.82.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-grpc:2.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-grpc:2.59.0=testCompileClasspath
|
||||
com.google.cloud:google-cloud-core-http:2.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-http:2.53.1=testCompileClasspath
|
||||
com.google.cloud:google-cloud-core:2.49.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core:2.53.1=testCompileClasspath
|
||||
com.google.cloud:google-cloud-firestore:3.30.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-monitoring:3.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core-http:2.59.0=testCompileClasspath
|
||||
com.google.cloud:google-cloud-core:2.57.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-core:2.59.0=testCompileClasspath
|
||||
com.google.cloud:google-cloud-firestore:3.31.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-monitoring:3.65.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-nio:0.127.24=testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-nio:0.127.33=testCompileClasspath
|
||||
com.google.cloud:google-cloud-pubsub:1.136.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-pubsublite:1.15.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-nio:0.128.2=testCompileClasspath
|
||||
com.google.cloud:google-cloud-pubsub:1.140.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-pubsublite:1.15.9=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-secretmanager:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-secretmanager:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.cloud:google-cloud-spanner:6.85.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-secretmanager:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.cloud:google-cloud-spanner:6.95.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-storage-control:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-storage:2.44.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-storage:2.50.0=testCompileClasspath
|
||||
com.google.cloud:google-cloud-storage:2.55.0=testCompileClasspath
|
||||
com.google.cloud:google-cloud-tasks:2.51.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:google-cloud-tasks:2.59.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.cloud:google-cloud-tasks:2.72.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.cloud:grpc-gcp:1.6.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:libraries-bom:26.48.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.30.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.cloud:proto-google-cloud-firestore-bundle-v1:3.31.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,checkstyle,compileClasspath,deploy_jar,errorprone,nonprodAnnotationProcessor,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.code.gson:gson:2.10.1=soy
|
||||
com.google.code.gson:gson:2.12.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.common.html.types:types:1.0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.dagger:dagger-compiler:2.55=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger-spi:2.55=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger:2.55=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.devtools.ksp:symbol-processing-api:2.0.21-1.0.28=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger-compiler:2.57.1=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger-spi:2.57.1=annotationProcessor,testAnnotationProcessor
|
||||
com.google.dagger:dagger:2.57=deploy_jar
|
||||
com.google.dagger:dagger:2.57.1=annotationProcessor,compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.devtools.ksp:symbol-processing-api:2.1.21-2.0.2=annotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:error_prone_annotation:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:error_prone_annotations:2.20.0=soy
|
||||
com.google.errorprone:error_prone_annotations:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:error_prone_annotations:2.36.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.errorprone:error_prone_annotations:2.41.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.errorprone:error_prone_annotations:2.7.1=checkstyle
|
||||
com.google.errorprone:error_prone_check_api:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.errorprone:error_prone_core:2.23.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
@@ -181,25 +176,25 @@ com.google.flogger:google-extensions:0.7.4=soy
|
||||
com.google.flogger:google-extensions:0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.googlejavaformat:google-java-format:1.5=annotationProcessor,testAnnotationProcessor
|
||||
com.google.guava:failureaccess:1.0.1=checkstyle,errorprone,nonprodAnnotationProcessor,soy
|
||||
com.google.guava:failureaccess:1.0.2=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:failureaccess:1.0.2=annotationProcessor,testAnnotationProcessor
|
||||
com.google.guava:failureaccess:1.0.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:guava-parent:32.1.1-jre=errorprone,nonprodAnnotationProcessor,soy
|
||||
com.google.guava:guava-testlib:33.3.0-jre=testRuntimeClasspath
|
||||
com.google.guava:guava-testlib:33.4.0-jre=testCompileClasspath
|
||||
com.google.guava:guava-testlib:33.4.8-jre=testCompileClasspath
|
||||
com.google.guava:guava:31.0.1-jre=checkstyle
|
||||
com.google.guava:guava:32.1.1-jre=errorprone,nonprodAnnotationProcessor,soy
|
||||
com.google.guava:guava:33.0.0-jre=annotationProcessor,testAnnotationProcessor
|
||||
com.google.guava:guava:33.4.0-jre=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:guava:33.4.8-jre=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,checkstyle,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.gwt:gwt-user:2.10.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-apache-v2:1.45.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-apache-v2:1.46.3=testCompileClasspath
|
||||
com.google.http-client:google-http-client-apache-v2:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-appengine:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-appengine:1.46.3=testCompileClasspath
|
||||
com.google.http-client:google-http-client-gson:1.46.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-appengine:1.47.1=testCompileClasspath
|
||||
com.google.http-client:google-http-client-gson:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-jackson2:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-jackson2:1.46.3=testCompileClasspath
|
||||
com.google.http-client:google-http-client-protobuf:1.45.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client:1.46.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client-jackson2:1.47.1=testCompileClasspath
|
||||
com.google.http-client:google-http-client-protobuf:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.http-client:google-http-client:2.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.inject:guice:5.1.0=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.inject:guice:7.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.j2objc:j2objc-annotations:1.3=checkstyle
|
||||
@@ -215,13 +210,12 @@ com.google.oauth-client:google-oauth-client-jetty:1.36.0=deploy_jar,nonprodRunti
|
||||
com.google.oauth-client:google-oauth-client-jetty:1.39.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.oauth-client:google-oauth-client-servlet:1.36.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.oauth-client:google-oauth-client-servlet:1.39.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.oauth-client:google-oauth-client:1.37.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
com.google.oauth-client:google-oauth-client:1.39.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
com.google.protobuf:protobuf-java-util:4.29.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.oauth-client:google-oauth-client:1.39.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.protobuf:protobuf-java-util:4.29.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.protobuf:protobuf-java:3.19.6=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
com.google.protobuf:protobuf-java:3.21.7=soy
|
||||
com.google.protobuf:protobuf-java:3.25.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.re2j:re2j:1.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.protobuf:protobuf-java:3.25.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.re2j:re2j:1.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.template:soy:2024-02-26=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,soy,testCompileClasspath,testRuntimeClasspath
|
||||
com.google.truth:truth:1.4.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.googlecode.json-simple:json-simple:1.1.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -255,11 +249,12 @@ com.sun.istack:istack-commons-tools:4.1.2=jaxb
|
||||
com.sun.xml.bind.external:relaxng-datatype:4.0.5=jaxb
|
||||
com.sun.xml.bind.external:rngom:4.0.5=jaxb
|
||||
com.sun.xml.dtd-parser:dtd-parser:1.5.1=jaxb
|
||||
com.zaxxer:HikariCP:6.2.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
com.zaxxer:HikariCP:7.0.1=deploy_jar
|
||||
com.zaxxer:HikariCP:7.0.2=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
commons-beanutils:commons-beanutils:1.9.4=checkstyle
|
||||
commons-codec:commons-codec:1.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
commons-codec:commons-codec:1.19.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
commons-collections:commons-collections:3.2.2=checkstyle
|
||||
commons-io:commons-io:2.18.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
commons-io:commons-io:2.20.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
commons-logging:commons-logging:1.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
dnsjava:dnsjava:3.6.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
guru.nidi.com.eclipsesource.j2v8:j2v8_linux_x86_64:4.6.0=testRuntimeClasspath
|
||||
@@ -274,37 +269,27 @@ io.apicurio:apicurio-registry-protobuf-schema-utilities:3.0.0.M2=compileClasspat
|
||||
io.github.classgraph:classgraph:4.8.162=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.github.eisop:dataflow-errorprone:3.34.0-eisop1=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
io.github.java-diff-utils:java-diff-utils:4.12=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
io.github.java-diff-utils:java-diff-utils:4.15=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-alts:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-alts:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-api:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-auth:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-auth:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-census:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-context:1.70.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-core:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-core:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-googleapis:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-grpclb:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-grpclb:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-inprocess:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-inprocess:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-netty-shaded:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-netty-shaded:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-netty:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-opentelemetry:1.67.1=compileClasspath,nonprodCompileClasspath
|
||||
io.grpc:grpc-opentelemetry:1.68.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-opentelemetry:1.70.0=testCompileClasspath
|
||||
io.github.java-diff-utils:java-diff-utils:4.16=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-alts:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-api:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-auth:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-census:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-context:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-core:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-googleapis:1.71.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-grpclb:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-inprocess:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-netty-shaded:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-netty:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-opentelemetry:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-protobuf-lite:1.67.1=compileClasspath,nonprodCompileClasspath
|
||||
io.grpc:grpc-protobuf-lite:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-protobuf:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-protobuf:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-rls:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-services:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-stub:1.69.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-stub:1.70.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
io.grpc:grpc-util:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-xds:1.69.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-protobuf-lite:1.71.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-protobuf:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-rls:1.71.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-services:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-stub:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-util:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.grpc:grpc-xds:1.71.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-buffer:4.1.110.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec-http2:4.1.110.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.netty:netty-codec-http:4.1.110.Final=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -334,38 +319,29 @@ io.opentelemetry.contrib:opentelemetry-gcp-resources:1.37.0-alpha=compileClasspa
|
||||
io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-incubator:2.1.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry.instrumentation:opentelemetry-instrumentation-api:2.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry.semconv:opentelemetry-semconv:1.27.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry.semconv:opentelemetry-semconv:1.28.0-alpha=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-api-incubator:1.45.0-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-api-incubator:1.46.0-alpha=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.46.0=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.47.0=testCompileClasspath
|
||||
io.opentelemetry.semconv:opentelemetry-semconv:1.29.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-api-incubator:1.42.1-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-api:1.53.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-bom:1.42.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.46.0=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.47.0=testCompileClasspath
|
||||
io.opentelemetry:opentelemetry-exporter-logging:1.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-common:1.53.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-context:1.53.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-exporter-logging:1.53.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-extension-incubator:1.35.0-alpha=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.46.0=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.47.0=testCompileClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-common:1.53.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.42.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.46.0=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.47.0=testCompileClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.46.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.46.0=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.47.0=testCompileClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.46.0=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.47.0=testCompileClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.46.0=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.47.0=testCompileClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.45.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.46.0=testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.47.0=testCompileClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.53.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.53.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-logs:1.53.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-metrics:1.53.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk-trace:1.53.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.47.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
io.opentelemetry:opentelemetry-sdk:1.53.0=testCompileClasspath,testRuntimeClasspath
|
||||
io.opentelemetry:opentelemetry-semconv:1.26.0-alpha=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
io.outfoxx:swiftpoet:1.3.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
io.perfmark:perfmark-api:0.27.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
@@ -387,10 +363,10 @@ javax.validation:validation-api:1.0.0.GA=compileClasspath,deploy_jar,nonprodComp
|
||||
joda-time:joda-time:2.12.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
junit:junit:4.13.2=nonprodCompileClasspath,nonprodRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
net.arnx:nashorn-promise:0.1.1=testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy-agent:1.15.11=testCompileClasspath,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy-agent:1.17.6=testCompileClasspath,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.12=compileClasspath,nonprodCompileClasspath
|
||||
net.bytebuddy:byte-buddy:1.14.15=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.15.11=testCompileClasspath,testRuntimeClasspath
|
||||
net.bytebuddy:byte-buddy:1.17.6=testCompileClasspath,testRuntimeClasspath
|
||||
net.java.dev.jna:jna:5.13.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
net.ltgt.gradle.incap:incap:0.2=annotationProcessor,testAnnotationProcessor
|
||||
net.sf.saxon:Saxon-HE:10.6=checkstyle
|
||||
@@ -404,33 +380,33 @@ org.apache.arrow:arrow-format:15.0.2=compileClasspath,deploy_jar,nonprodCompileC
|
||||
org.apache.arrow:arrow-memory-core:15.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.arrow:arrow-vector:15.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.avro:avro:1.11.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-fn-execution:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-job-management:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-pipeline:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-fn-execution:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-job-management:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-model-pipeline:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-core-construction-java:2.54.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-core-java:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-direct-java:2.63.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-google-cloud-dataflow-java:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-java-fn-execution:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-core:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-expansion-service:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-arrow:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-avro:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-protobuf:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-core-java:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-direct-java:2.67.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-google-cloud-dataflow-java:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-runners-java-fn-execution:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-core:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-expansion-service:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-arrow:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-avro:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-google-cloud-platform-core:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-extensions-protobuf:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-fn-execution:2.54.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-harness:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-transform-service-launcher:2.63.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-harness:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-io-google-cloud-platform:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-sdks-java-transform-service-launcher:2.67.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-vendor-grpc-1_60_1:0.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-vendor-grpc-1_69_0:0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.beam:beam-vendor-guava-32_1_2-jre:0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-compress:1.26.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-csv:1.13.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-exec:1.4.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-csv:1.14.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-exec:1.5.0=testRuntimeClasspath
|
||||
org.apache.commons:commons-lang3:3.14.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath
|
||||
org.apache.commons:commons-lang3:3.17.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-text:1.13.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-lang3:3.18.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.commons:commons-text:1.14.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.ftpserver:ftplet-api:1.2.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.ftpserver:ftpserver-core:1.2.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.httpcomponents:httpclient:4.5.14=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -440,37 +416,39 @@ org.apache.sshd:sshd-common:2.15.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-core:2.15.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-scp:2.15.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.sshd:sshd-sftp:2.15.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.tomcat:tomcat-annotations-api:11.0.5=testCompileClasspath,testRuntimeClasspath
|
||||
org.apache.tomcat:tomcat-annotations-api:11.0.10=testCompileClasspath,testRuntimeClasspath
|
||||
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
|
||||
org.bouncycastle:bcpg-jdk18on:1.80=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcpkix-jdk18on:1.80=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcprov-jdk18on:1.80=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcutil-jdk18on:1.80=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.checkerframework:checker-compat-qual:2.5.3=compileClasspath,nonprodCompileClasspath,soy,testCompileClasspath
|
||||
org.checkerframework:checker-compat-qual:2.5.5=annotationProcessor,testAnnotationProcessor
|
||||
org.bouncycastle:bcpg-jdk18on:1.81=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcpkix-jdk18on:1.81=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcprov-jdk18on:1.81=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.bouncycastle:bcutil-jdk18on:1.81=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.checkerframework:checker-compat-qual:2.5.3=annotationProcessor,compileClasspath,nonprodCompileClasspath,soy,testAnnotationProcessor,testCompileClasspath
|
||||
org.checkerframework:checker-compat-qual:2.5.6=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.checkerframework:checker-qual:3.12.0=checkstyle
|
||||
org.checkerframework:checker-qual:3.33.0=errorprone,nonprodAnnotationProcessor,soy
|
||||
org.checkerframework:checker-qual:3.41.0=annotationProcessor,testAnnotationProcessor
|
||||
org.checkerframework:checker-qual:3.49.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.checkerframework:checker-qual:3.49.0=compileClasspath,nonprodCompileClasspath,testCompileClasspath
|
||||
org.checkerframework:checker-qual:3.49.3=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.codehaus.mojo:animal-sniffer-annotations:1.24=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.conscrypt:conscrypt-openjdk-uber:2.5.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.angus:angus-activation:2.0.2=deploy_jar,jaxb,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.eclipse.angus:jakarta.mail:2.0.3=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.eclipse.angus:jakarta.mail:2.0.4=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.eclipse.collections:eclipse-collections-api:11.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.collections:eclipse-collections:11.1.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-ee:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-http:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-io:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-security:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-server:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-session:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-util:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-xml:12.1.0.alpha1=testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-core:11.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-database-postgresql:11.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-servlet:12.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee10:jetty-ee10-webapp:12.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty.ee:jetty-ee-webapp:12.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-http:12.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-io:12.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-security:12.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-server:12.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-session:12.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-util:12.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.eclipse.jetty:jetty-xml:12.1.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-core:11.11.1=deploy_jar
|
||||
org.flywaydb:flyway-core:11.11.2=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.flywaydb:flyway-database-postgresql:11.11.1=deploy_jar
|
||||
org.flywaydb:flyway-database-postgresql:11.11.2=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.glassfish.jaxb:codemodel:4.0.5=jaxb
|
||||
org.glassfish.jaxb:jaxb-core:4.0.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.glassfish.jaxb:jaxb-core:4.0.5=jaxb
|
||||
@@ -498,6 +476,7 @@ org.javassist:javassist:3.28.0-GA=checkstyle
|
||||
org.jboss.logging:jboss-logging:3.5.0.Final=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jcommander:jcommander:2.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-bom:1.4.0=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-metadata-jvm:2.1.21=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.6.10=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains.kotlin:kotlin-reflect:1.9.20=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib-common:1.9.20=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -506,7 +485,7 @@ org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10=compileClasspath,deploy_jar,nonpr
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.10=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib:1.9.20=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlin:kotlin-stdlib:2.0.21=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains.kotlin:kotlin-stdlib:2.1.21=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains.kotlinx:kotlinx-datetime-jvm:0.4.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
@@ -515,26 +494,26 @@ org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.0.1=deploy_jar,nonprodRun
|
||||
org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.jetbrains:annotations:13.0=annotationProcessor,testAnnotationProcessor
|
||||
org.jetbrains:annotations:17.0.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jline:jline:3.29.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.joda:joda-money:2.0.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jline:jline:3.30.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.joda:joda-money:2.0.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.json:json:20230618=soy
|
||||
org.json:json:20250107=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jsoup:jsoup:1.19.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jsoup:jsoup:1.21.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.jspecify:jspecify:1.0.0=annotationProcessor,compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath
|
||||
org.junit-pioneer:junit-pioneer:2.3.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:5.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:5.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-migrationsupport:5.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-params:5.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:1.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:1.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:1.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-runner:1.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-api:1.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-commons:1.12.1=testRuntimeClasspath
|
||||
org.junit:junit-bom:5.12.1=testCompileClasspath,testRuntimeClasspath
|
||||
org.mockito:mockito-core:5.16.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.mockito:mockito-junit-jupiter:5.16.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-api:5.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-engine:5.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-migrationsupport:5.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.jupiter:junit-jupiter-params:5.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-commons:1.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-engine:1.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-launcher:1.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-runner:1.13.3=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-api:1.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.junit.platform:junit-platform-suite-commons:1.13.4=testRuntimeClasspath
|
||||
org.junit:junit-bom:5.13.4=testCompileClasspath,testRuntimeClasspath
|
||||
org.mockito:mockito-core:5.19.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.mockito:mockito-junit-jupiter:5.19.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.objenesis:objenesis:3.3=testRuntimeClasspath
|
||||
org.ogce:xpp3:1.1.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
|
||||
@@ -552,50 +531,58 @@ org.ow2.asm:asm:9.5=soy
|
||||
org.ow2.asm:asm:9.7=jacocoAnt
|
||||
org.ow2.asm:asm:9.7.1=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.pcollections:pcollections:3.1.4=annotationProcessor,errorprone,nonprodAnnotationProcessor,testAnnotationProcessor
|
||||
org.postgresql:postgresql:42.7.5=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.postgresql:postgresql:42.7.7=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.reflections:reflections:0.10.2=checkstyle
|
||||
org.rnorth.duct-tape:duct-tape:1.0.8=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-api:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-chrome-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-chromium-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v131:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v132:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v133:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v85:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-edge-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-firefox-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-http:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-ie-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-java:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-json:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-manager:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-os:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-remote-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-safari-driver:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-support:4.29.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-api:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-chrome-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-chromium-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v137:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v138:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-devtools-v139:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-edge-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-firefox-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-http:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-ie-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-java:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-json:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-manager:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-os:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-remote-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-safari-driver:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.seleniumhq.selenium:selenium-support:4.35.0=testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:jcl-over-slf4j:1.7.36=testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:jul-to-slf4j:1.7.30=testRuntimeClasspath
|
||||
org.slf4j:slf4j-api:2.0.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:slf4j-jdk14:2.0.16=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:slf4j-api:2.0.17=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.slf4j:slf4j-jdk14:2.0.17=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.snakeyaml:snakeyaml-engine:2.1=deploy_jar,nonprodRuntimeClasspath,runtimeClasspath,testRuntimeClasspath
|
||||
org.testcontainers:database-commons:1.20.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:jdbc:1.20.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:junit-jupiter:1.20.6=testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:postgresql:1.20.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:selenium:1.20.6=testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:testcontainers:1.20.6=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:database-commons:1.21.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:jdbc:1.21.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:junit-jupiter:1.21.3=testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:postgresql:1.21.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:selenium:1.21.3=testCompileClasspath,testRuntimeClasspath
|
||||
org.testcontainers:testcontainers:1.21.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.threeten:threetenbp:1.7.0=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.w3c.css:sac:1.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.webjars.npm:viz.js-graphviz-java:2.1.3=testRuntimeClasspath
|
||||
org.xerial.snappy:snappy-java:1.1.10.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.yaml:snakeyaml:2.3=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-api:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-diagram:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-loader:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-postgresql:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-text:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-tools:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-utility:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler:16.25.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
org.yaml:snakeyaml:2.4=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-api:16.26.3=deploy_jar
|
||||
us.fatehi:schemacrawler-api:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-diagram:16.26.3=deploy_jar
|
||||
us.fatehi:schemacrawler-diagram:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-loader:16.26.3=deploy_jar
|
||||
us.fatehi:schemacrawler-loader:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-operations:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-postgresql:16.26.3=deploy_jar
|
||||
us.fatehi:schemacrawler-postgresql:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-text:16.26.3=deploy_jar
|
||||
us.fatehi:schemacrawler-text:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-tools:16.26.3=deploy_jar
|
||||
us.fatehi:schemacrawler-tools:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler-utility:16.26.3=deploy_jar
|
||||
us.fatehi:schemacrawler-utility:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
us.fatehi:schemacrawler:16.26.3=deploy_jar
|
||||
us.fatehi:schemacrawler:16.27.1=compileClasspath,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
xerces:xmlParserAPIs:2.6.2=compileClasspath,deploy_jar,nonprodCompileClasspath,nonprodRuntimeClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
|
||||
empty=devtool,nomulus_test
|
||||
|
||||
@@ -29,9 +29,11 @@ import static google.registry.request.RequestParameters.extractRequiredParameter
|
||||
import static google.registry.request.RequestParameters.extractSetOfDatetimeParameters;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.request.Parameter;
|
||||
import jakarta.inject.Named;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -137,4 +139,18 @@ public class BatchModule {
|
||||
static boolean provideIsFast(HttpServletRequest req) {
|
||||
return extractBooleanParameter(req, PARAM_FAST);
|
||||
}
|
||||
|
||||
private static final int DEFAULT_MAX_QPS = 10;
|
||||
|
||||
@Provides
|
||||
@Parameter("maxQps")
|
||||
static int provideMaxQps(HttpServletRequest req) {
|
||||
return extractOptionalIntParameter(req, "maxQps").orElse(DEFAULT_MAX_QPS);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("removeAllDomainContacts")
|
||||
static RateLimiter provideRemoveAllDomainContactsRateLimiter(@Parameter("maxQps") int maxQps) {
|
||||
return RateLimiter.create(maxQps);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||
@@ -30,8 +29,6 @@ import google.registry.groups.GmailClient;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.tld.RegistryLockDao;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action;
|
||||
@@ -70,12 +67,14 @@ public class RelockDomainAction implements Runnable {
|
||||
"""
|
||||
The domain %s was successfully re-locked.
|
||||
|
||||
Please contact support at %s if you have any questions.""";
|
||||
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
|
||||
|
||||
Please contact support at %s if you have any questions.""";
|
||||
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";
|
||||
@@ -171,7 +170,7 @@ public class RelockDomainAction implements Runnable {
|
||||
domainLockUtils.administrativelyApplyLock(
|
||||
oldLock.getDomainName(),
|
||||
oldLock.getRegistrarId(),
|
||||
oldLock.getRegistrarPocId(),
|
||||
oldLock.getRegistryLockEmail(),
|
||||
oldLock.isSuperuser());
|
||||
logger.atInfo().log("Re-locked domain %s.", oldLock.getDomainName());
|
||||
response.setStatus(SC_OK);
|
||||
@@ -221,7 +220,7 @@ public class RelockDomainAction implements Runnable {
|
||||
EmailMessage.newBuilder()
|
||||
.setBody(body)
|
||||
.setSubject(String.format("Error re-locking domain %s", oldLock.getDomainName()))
|
||||
.setRecipients(getEmailRecipients(oldLock.getRegistrarId()))
|
||||
.setRecipients(ImmutableSet.of(getEmailRecipient(oldLock)))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -250,7 +249,7 @@ public class RelockDomainAction implements Runnable {
|
||||
EmailMessage.newBuilder()
|
||||
.setBody(body)
|
||||
.setSubject(String.format("Successful re-lock of domain %s", oldLock.getDomainName()))
|
||||
.setRecipients(getEmailRecipients(oldLock.getRegistrarId()))
|
||||
.setRecipients(ImmutableSet.of(getEmailRecipient(oldLock)))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -261,7 +260,7 @@ public class RelockDomainAction implements Runnable {
|
||||
// For an unexpected failure, notify both the lock-enabled contacts and our alerting email
|
||||
ImmutableSet<InternetAddress> allRecipients =
|
||||
new ImmutableSet.Builder<InternetAddress>()
|
||||
.addAll(getEmailRecipients(oldLock.getRegistrarId()))
|
||||
.add(getEmailRecipient(oldLock))
|
||||
.add(alertRecipientAddress)
|
||||
.build();
|
||||
gmailClient.sendEmail(
|
||||
@@ -281,31 +280,12 @@ public class RelockDomainAction implements Runnable {
|
||||
.build());
|
||||
}
|
||||
|
||||
private ImmutableSet<InternetAddress> getEmailRecipients(String registrarId) {
|
||||
Registrar registrar =
|
||||
Registrar.loadByRegistrarIdCached(registrarId)
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new IllegalStateException(String.format("Unknown registrar %s", registrarId)));
|
||||
|
||||
ImmutableSet<String> registryLockEmailAddresses =
|
||||
registrar.getContacts().stream()
|
||||
.filter(RegistrarPoc::isRegistryLockAllowed)
|
||||
.map(RegistrarPoc::getRegistryLockEmailAddress)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.collect(toImmutableSet());
|
||||
|
||||
ImmutableSet.Builder<InternetAddress> builder = new ImmutableSet.Builder<>();
|
||||
// can't use streams due to the 'throws' in the InternetAddress constructor
|
||||
for (String registryLockEmailAddress : registryLockEmailAddresses) {
|
||||
try {
|
||||
builder.add(new InternetAddress(registryLockEmailAddress));
|
||||
} catch (AddressException e) {
|
||||
// This shouldn't stop any other emails going out, so swallow it
|
||||
logger.atWarning().log("Invalid email address '%s'.", registryLockEmailAddress);
|
||||
}
|
||||
private InternetAddress getEmailRecipient(RegistryLock lock) {
|
||||
try {
|
||||
return new InternetAddress(lock.getRegistryLockEmail());
|
||||
} catch (AddressException e) {
|
||||
// this really shouldn't happen
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.batch;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
|
||||
import static google.registry.flows.FlowUtils.marshalWithLenientRetry;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_NO_CONTENT;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.util.concurrent.RateLimiter;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.flows.EppController;
|
||||
import google.registry.flows.EppRequestSource;
|
||||
import google.registry.flows.PasswordOnlyTransportCredentials;
|
||||
import google.registry.flows.StatelessRequestSessionMetadata;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.eppcommon.ProtocolDefinition;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Named;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
/**
|
||||
* An action that removes all contacts from all active (non-deleted) domains.
|
||||
*
|
||||
* <p>This implements part 1 of phase 3 of the Minimum Dataset migration, wherein we remove all uses
|
||||
* of contact objects in preparation for later removing all contact data from the system.
|
||||
*
|
||||
* <p>This runs as a singly threaded, resumable action that loads batches of domains still
|
||||
* containing contacts, and runs a superuser domain update on each one to remove the contacts,
|
||||
* leaving behind a record recording that update.
|
||||
*/
|
||||
@Action(
|
||||
service = GaeService.BACKEND,
|
||||
path = RemoveAllDomainContactsAction.PATH,
|
||||
method = Action.Method.POST,
|
||||
auth = Auth.AUTH_ADMIN)
|
||||
public class RemoveAllDomainContactsAction implements Runnable {
|
||||
|
||||
public static final String PATH = "/_dr/task/removeAllDomainContacts";
|
||||
private static final String LOCK_NAME = "Remove all domain contacts";
|
||||
private static final String CONTACT_FMT = "<domain:contact type=\"%s\">%s</domain:contact>";
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
private final EppController eppController;
|
||||
private final String registryAdminClientId;
|
||||
private final LockHandler lockHandler;
|
||||
private final RateLimiter rateLimiter;
|
||||
private final Response response;
|
||||
private final String updateDomainXml;
|
||||
private int successes = 0;
|
||||
private int failures = 0;
|
||||
|
||||
private static final int BATCH_SIZE = 10000;
|
||||
|
||||
@Inject
|
||||
RemoveAllDomainContactsAction(
|
||||
EppController eppController,
|
||||
@Config("registryAdminClientId") String registryAdminClientId,
|
||||
LockHandler lockHandler,
|
||||
@Named("removeAllDomainContacts") RateLimiter rateLimiter,
|
||||
Response response) {
|
||||
this.eppController = eppController;
|
||||
this.registryAdminClientId = registryAdminClientId;
|
||||
this.lockHandler = lockHandler;
|
||||
this.rateLimiter = rateLimiter;
|
||||
this.response = response;
|
||||
this.updateDomainXml =
|
||||
readResourceUtf8(RemoveAllDomainContactsAction.class, "domain_remove_contacts.xml");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
checkState(
|
||||
tm().transact(() -> FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)),
|
||||
"Minimum dataset migration must be completed prior to running this action");
|
||||
response.setContentType(PLAIN_TEXT_UTF_8);
|
||||
|
||||
Callable<Void> runner =
|
||||
() -> {
|
||||
try {
|
||||
runLocked();
|
||||
response.setStatus(SC_OK);
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log("Errored out during execution.");
|
||||
response.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
response.setPayload(String.format("Errored out with cause: %s", e));
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
if (!lockHandler.executeWithLocks(runner, null, Duration.standardHours(1), LOCK_NAME)) {
|
||||
// Send a 200-series status code to prevent this conflicting action from retrying.
|
||||
response.setStatus(SC_NO_CONTENT);
|
||||
response.setPayload("Could not acquire lock; already running?");
|
||||
}
|
||||
}
|
||||
|
||||
private void runLocked() {
|
||||
logger.atInfo().log("Removing contacts on all active domains.");
|
||||
|
||||
List<String> domainRepoIdsBatch;
|
||||
do {
|
||||
domainRepoIdsBatch =
|
||||
tm().<List<String>>transact(
|
||||
() ->
|
||||
tm().getEntityManager()
|
||||
.createQuery(
|
||||
"""
|
||||
SELECT repoId FROM Domain WHERE deletionTime = :end_of_time AND NOT (
|
||||
adminContact IS NULL AND billingContact IS NULL
|
||||
AND registrantContact IS NULL AND techContact IS NULL)
|
||||
""")
|
||||
.setParameter("end_of_time", END_OF_TIME)
|
||||
.setMaxResults(BATCH_SIZE)
|
||||
.getResultList());
|
||||
|
||||
for (String domainRepoId : domainRepoIdsBatch) {
|
||||
rateLimiter.acquire();
|
||||
runDomainUpdateFlow(domainRepoId);
|
||||
}
|
||||
} while (!domainRepoIdsBatch.isEmpty());
|
||||
String msg =
|
||||
String.format(
|
||||
"Finished; %d domains were successfully updated and %d errored out.",
|
||||
successes, failures);
|
||||
logger.at(failures == 0 ? Level.INFO : Level.WARNING).log(msg);
|
||||
response.setPayload(msg);
|
||||
}
|
||||
|
||||
private void runDomainUpdateFlow(String repoId) {
|
||||
// Create a new transaction that the flow's execution will be enlisted in that loads the domain
|
||||
// transactionally. This way we can ensure that nothing else has modified the domain in question
|
||||
// in the intervening period since the query above found it. If a single domain update fails
|
||||
// permanently, log it and move on to not block processing all the other domains.
|
||||
try {
|
||||
boolean success = tm().transact(() -> runDomainUpdateFlowInner(repoId));
|
||||
if (success) {
|
||||
successes++;
|
||||
} else {
|
||||
failures++;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
logger.atWarning().withCause(t).log(
|
||||
"Failed updating domain with repoId %s; skipping.", repoId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the actual domain update flow and returns whether the contact removals were successful.
|
||||
*/
|
||||
private boolean runDomainUpdateFlowInner(String repoId) {
|
||||
Domain domain = tm().loadByKey(VKey.create(Domain.class, repoId));
|
||||
if (!domain.getDeletionTime().equals(END_OF_TIME)) {
|
||||
// Domain has been deleted since the action began running; nothing further to be
|
||||
// done here.
|
||||
logger.atInfo().log("Nothing to process for deleted domain '%s'.", domain.getDomainName());
|
||||
return false;
|
||||
}
|
||||
logger.atInfo().log("Attempting to remove contacts on domain '%s'.", domain.getDomainName());
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
ImmutableMap<VKey<? extends Contact>, Contact> contacts =
|
||||
tm().loadByKeys(
|
||||
domain.getContacts().stream()
|
||||
.map(DesignatedContact::getContactKey)
|
||||
.collect(ImmutableSet.toImmutableSet()));
|
||||
|
||||
// Collect all the (non-registrant) contacts referenced by the domain and compile an EPP XML
|
||||
// string that removes each one.
|
||||
for (DesignatedContact designatedContact : domain.getContacts()) {
|
||||
@Nullable Contact contact = contacts.get(designatedContact.getContactKey());
|
||||
if (contact == null) {
|
||||
logger.atWarning().log(
|
||||
"Domain '%s' referenced contact with repo ID '%s' that couldn't be" + " loaded.",
|
||||
domain.getDomainName(), designatedContact.getContactKey().getKey());
|
||||
continue;
|
||||
}
|
||||
sb.append(
|
||||
String.format(
|
||||
CONTACT_FMT,
|
||||
Ascii.toLowerCase(designatedContact.getType().name()),
|
||||
contact.getContactId()))
|
||||
.append("\n");
|
||||
}
|
||||
|
||||
String compiledXml =
|
||||
updateDomainXml
|
||||
.replace("%DOMAIN%", domain.getDomainName())
|
||||
.replace("%CONTACTS%", sb.toString());
|
||||
EppOutput output =
|
||||
eppController.handleEppCommand(
|
||||
new StatelessRequestSessionMetadata(
|
||||
registryAdminClientId, ProtocolDefinition.getVisibleServiceExtensionUris()),
|
||||
new PasswordOnlyTransportCredentials(),
|
||||
EppRequestSource.BACKEND,
|
||||
false,
|
||||
true,
|
||||
compiledXml.getBytes(US_ASCII));
|
||||
if (output.isSuccess()) {
|
||||
logger.atInfo().log(
|
||||
"Successfully removed contacts from domain '%s'.", domain.getDomainName());
|
||||
} else {
|
||||
logger.atWarning().log(
|
||||
"Failed removing contacts from domain '%s' with error %s.",
|
||||
domain.getDomainName(), new String(marshalWithLenientRetry(output), US_ASCII));
|
||||
}
|
||||
return output.isSuccess();
|
||||
}
|
||||
}
|
||||
@@ -275,7 +275,7 @@ public class SendExpiringCertificateNotificationEmailAction implements Runnable
|
||||
*/
|
||||
@VisibleForTesting
|
||||
ImmutableSet<InternetAddress> getEmailAddresses(Registrar registrar, Type contactType) {
|
||||
ImmutableSortedSet<RegistrarPoc> contacts = registrar.getContactsOfType(contactType);
|
||||
ImmutableSortedSet<RegistrarPoc> contacts = registrar.getPocsOfType(contactType);
|
||||
ImmutableSet.Builder<InternetAddress> recipientEmails = new ImmutableSet.Builder<>();
|
||||
for (RegistrarPoc contact : contacts) {
|
||||
try {
|
||||
|
||||
@@ -215,10 +215,12 @@ public class BsaValidateAction implements Runnable {
|
||||
if (Objects.equals(expectedReason, domain.reason())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
if (isRegistered || domain.reason().equals(Reason.REGISTERED)) {
|
||||
if (isStalenessAllowed(isRegistered, activeDomains.get(domain.domainName()))) {
|
||||
// Registered name still reported with other reasons: Don't report if registration is recent.
|
||||
// Note that staleness is not tolerated if deregistered name is still reported as registered:
|
||||
// in this case we do not have the VKey on hand, and it is not worth the effort to find it
|
||||
// out.
|
||||
if (isRegistered && isStalenessAllowed(activeDomains.get(domain.domainName()))) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
return Optional.of(
|
||||
String.format(
|
||||
@@ -228,15 +230,10 @@ public class BsaValidateAction implements Runnable {
|
||||
domain.reason()));
|
||||
}
|
||||
|
||||
boolean isStalenessAllowed(boolean isNewDomain, VKey<Domain> domainVKey) {
|
||||
boolean isStalenessAllowed(VKey<Domain> domainVKey) {
|
||||
Domain domain = bsaQuery(() -> replicaTm().loadByKey(domainVKey));
|
||||
var now = clock.nowUtc();
|
||||
if (isNewDomain) {
|
||||
return domain.getCreationTime().plus(maxStaleness).isAfter(now);
|
||||
} else {
|
||||
return domain.getDeletionTime().isBefore(now)
|
||||
&& domain.getDeletionTime().plus(maxStaleness).isAfter(now);
|
||||
}
|
||||
return domain.getCreationTime().plus(maxStaleness).isAfter(now);
|
||||
}
|
||||
|
||||
/** Returns unique labels across all block lists in the download specified by {@code jobName}. */
|
||||
|
||||
@@ -25,16 +25,16 @@ import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.cloud.storage.BlobId;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.hash.Hasher;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.ByteSource;
|
||||
import google.registry.bsa.api.BsaCredential;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.gcs.GcsUtils;
|
||||
@@ -47,10 +47,13 @@ import google.registry.request.auth.Auth;
|
||||
import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.io.Writer;
|
||||
import java.util.Optional;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
@@ -60,14 +63,17 @@ import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okio.BufferedSink;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Daily action that uploads unavailable domain names on applicable TLDs to BSA.
|
||||
*
|
||||
* <p>The upload is a single zipped text file containing combined details for all BSA-enrolled TLDs.
|
||||
* The text is a newline-delimited list of punycoded fully qualified domain names, and contains all
|
||||
* domains on each TLD that are registered and/or reserved.
|
||||
* The text is a newline-delimited list of punycoded fully qualified domain names with a trailing
|
||||
* newline at the end, and contains all domains on each TLD that are registered and/or reserved.
|
||||
*
|
||||
* <p>The file is also uploaded to GCS to preserve it as a record for ourselves.
|
||||
*/
|
||||
@@ -118,7 +124,7 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
|
||||
// TODO(mcilwain): Implement a date Cursor, have the cronjob run frequently, and short-circuit
|
||||
// the run if the daily upload is already completed.
|
||||
DateTime runTime = clock.nowUtc();
|
||||
String unavailableDomains = Joiner.on("\n").join(getUnavailableDomains(runTime));
|
||||
ImmutableSortedSet<String> unavailableDomains = getUnavailableDomains(runTime);
|
||||
if (unavailableDomains.isEmpty()) {
|
||||
logger.atWarning().log("No unavailable domains found; terminating.");
|
||||
emailSender.sendNotification(
|
||||
@@ -136,12 +142,16 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
|
||||
}
|
||||
|
||||
/** Uploads the unavailable domains list to GCS in the unavailable domains bucket. */
|
||||
boolean uploadToGcs(String unavailableDomains, DateTime runTime) {
|
||||
boolean uploadToGcs(ImmutableSortedSet<String> unavailableDomains, DateTime runTime) {
|
||||
logger.atInfo().log("Uploading unavailable names file to GCS in bucket %s", gcsBucket);
|
||||
BlobId blobId = BlobId.of(gcsBucket, createFilename(runTime));
|
||||
// `gcsUtils.openOutputStream` returns a buffered stream
|
||||
try (OutputStream gcsOutput = gcsUtils.openOutputStream(blobId);
|
||||
Writer osWriter = new OutputStreamWriter(gcsOutput, US_ASCII)) {
|
||||
osWriter.write(unavailableDomains);
|
||||
for (var domainName : unavailableDomains) {
|
||||
osWriter.write(domainName);
|
||||
osWriter.write("\n");
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.atSevere().withCause(e).log(
|
||||
@@ -150,10 +160,14 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
boolean uploadToBsa(String unavailableDomains, DateTime runTime) {
|
||||
boolean uploadToBsa(ImmutableSortedSet<String> unavailableDomains, DateTime runTime) {
|
||||
try {
|
||||
byte[] gzippedContents = gzipUnavailableDomains(unavailableDomains);
|
||||
String sha512Hash = ByteSource.wrap(gzippedContents).hash(Hashing.sha512()).toString();
|
||||
Hasher sha512Hasher = Hashing.sha512().newHasher();
|
||||
unavailableDomains.stream()
|
||||
.map(name -> name + "\n")
|
||||
.forEachOrdered(line -> sha512Hasher.putString(line, UTF_8));
|
||||
String sha512Hash = sha512Hasher.hash().toString();
|
||||
|
||||
String filename = createFilename(runTime);
|
||||
OkHttpClient client = new OkHttpClient().newBuilder().build();
|
||||
|
||||
@@ -169,7 +183,9 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
|
||||
.addFormDataPart(
|
||||
"file",
|
||||
String.format("%s.gz", filename),
|
||||
RequestBody.create(gzippedContents, MediaType.parse("application/octet-stream")))
|
||||
new StreamingRequestBody(
|
||||
gzippedStream(unavailableDomains),
|
||||
MediaType.parse("application/octet-stream")))
|
||||
.build();
|
||||
|
||||
Request request =
|
||||
@@ -196,15 +212,6 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] gzipUnavailableDomains(String unavailableDomains) throws IOException {
|
||||
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
|
||||
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
|
||||
gzipOutputStream.write(unavailableDomains.getBytes(US_ASCII));
|
||||
}
|
||||
return byteArrayOutputStream.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
private static String createFilename(DateTime runTime) {
|
||||
return String.format("unavailable_domains_%s.txt", runTime.toString());
|
||||
}
|
||||
@@ -280,4 +287,65 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
|
||||
private static String toDomain(String domainLabel, Tld tld) {
|
||||
return String.format("%s.%s", domainLabel, tld.getTldStr());
|
||||
}
|
||||
|
||||
private InputStream gzippedStream(ImmutableSortedSet<String> unavailableDomains)
|
||||
throws IOException {
|
||||
PipedInputStream inputStream = new PipedInputStream();
|
||||
PipedOutputStream outputStream = new PipedOutputStream(inputStream);
|
||||
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
gzipUnavailableDomains(outputStream, unavailableDomains);
|
||||
} catch (Throwable e) {
|
||||
logger.atSevere().withCause(e).log("Failed to gzip unavailable domains.");
|
||||
try {
|
||||
// This will cause the next read to throw an IOException.
|
||||
inputStream.close();
|
||||
} catch (IOException ignore) {
|
||||
// Won't happen for `PipedInputStream.close()`
|
||||
}
|
||||
}
|
||||
})
|
||||
.start();
|
||||
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
private void gzipUnavailableDomains(
|
||||
PipedOutputStream outputStream, ImmutableSortedSet<String> unavailableDomains)
|
||||
throws IOException {
|
||||
// `GZIPOutputStream` is buffered.
|
||||
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream)) {
|
||||
for (String name : unavailableDomains) {
|
||||
var line = name + "\n";
|
||||
gzipOutputStream.write(line.getBytes(US_ASCII));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class StreamingRequestBody extends RequestBody {
|
||||
private final BufferedInputStream inputStream;
|
||||
private final MediaType mediaType;
|
||||
|
||||
StreamingRequestBody(InputStream inputStream, MediaType mediaType) {
|
||||
this.inputStream = new BufferedInputStream(inputStream);
|
||||
this.mediaType = mediaType;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return mediaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException {
|
||||
byte[] buffer = new byte[2048];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
bufferedSink.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
core/src/main/java/google/registry/config/files/README.md
Normal file
13
core/src/main/java/google/registry/config/files/README.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Nomulus Environment Configuration
|
||||
|
||||
The configuration files for the different Nomulus environments are not included in this repository. To configure and run a specific environment, you will need to create the corresponding YAML configuration file in this directory.
|
||||
|
||||
The following is a list of the environment configuration files that you may need to create:
|
||||
|
||||
* `nomulus-config-alpha.yaml`
|
||||
* `nomulus-config-crash.yaml`
|
||||
* `nomulus-config-qa.yaml`
|
||||
* `nomulus-config-sandbox.yaml`
|
||||
* `nomulus-config-production.yaml`
|
||||
|
||||
Please create the relevant file for the environment you intend to use and populate it with the necessary configuration details.
|
||||
@@ -243,7 +243,7 @@ hibernate:
|
||||
# that BEAM pipelines are not subject to the maximumPoolSize value defined
|
||||
# here. See PersistenceModule.java for more information.
|
||||
hikariMinimumIdle: 1
|
||||
hikariMaximumPoolSize: 20
|
||||
hikariMaximumPoolSize: 40
|
||||
hikariIdleTimeout: 300000
|
||||
# The batch size is basically the number of insertions / updates in a single
|
||||
# transaction that will be batched together into one INSERT/UPDATE statement.
|
||||
@@ -255,7 +255,7 @@ hibernate:
|
||||
# The fetch size is the number of entities retrieved at a time from the
|
||||
# database cursor. Here we set a small default geared toward Nomulus server
|
||||
# transactions. Large queries can override the defaults on a per-query basis.
|
||||
jdbcFetchSize: 20
|
||||
jdbcFetchSize: 40
|
||||
|
||||
cloudSql:
|
||||
# jdbc url for the Cloud SQL database.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# Add environment-specific configuration here.
|
||||
@@ -1 +0,0 @@
|
||||
# Add environment-specific configuration here.
|
||||
@@ -1 +0,0 @@
|
||||
# Add environment-specific configuration here.
|
||||
@@ -1 +0,0 @@
|
||||
# Add environment-specific configuration here.
|
||||
@@ -1 +0,0 @@
|
||||
# Add environment-specific configuration here.
|
||||
@@ -88,6 +88,7 @@
|
||||
should exist between the RECURRING_BILLING cursor's time and the execution
|
||||
time of the action.
|
||||
</description>
|
||||
<!-- Runs shortly after DeleteExpiredDomainsAction so it can delete domains before they renew -->
|
||||
<schedule>0 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
@@ -98,7 +99,8 @@
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
<!-- Runs shortly before ExpandBillingRecurrencesPipeline to catch and delete domains before they renew -->
|
||||
<schedule>45 2 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
|
||||
@@ -146,6 +146,7 @@
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
<!-- Runs shortly before ExpandBillingRecurrencesPipeline to catch and delete domains before they renew -->
|
||||
<schedule>45 2 * * *</schedule>
|
||||
</task>
|
||||
</entries>
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
should exist between the RECURRING_BILLING cursor's time and the execution
|
||||
time of the action.
|
||||
</description>
|
||||
<!-- Runs shortly after DeleteExpiredDomainsAction so it can delete domains before they renew -->
|
||||
<schedule>0 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
@@ -140,7 +141,8 @@
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
<!-- Runs shortly before ExpandBillingRecurrencesPipeline to catch and delete domains before they renew -->
|
||||
<schedule>45 2 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
<!-- Runs shortly before ExpandBillingRecurrencesPipeline to catch and delete domains before they renew -->
|
||||
<schedule>45 2 * * *</schedule>
|
||||
</task>
|
||||
</entries>
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
should exist between the RECURRING_BILLING cursor's time and the execution
|
||||
time of the action.
|
||||
</description>
|
||||
<!-- Runs shortly after DeleteExpiredDomainsAction so it can delete domains before they renew -->
|
||||
<schedule>0 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
@@ -113,7 +114,8 @@
|
||||
This job runs an action that deletes domains that are past their
|
||||
autorenew end date.
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
<!-- Runs shortly before ExpandBillingRecurrencesPipeline to catch and delete domains before they renew -->
|
||||
<schedule>45 2 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.google.common.net.MediaType;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.util.ProxyHttpHeaders;
|
||||
import google.registry.util.StopwatchLogger;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
/** Handle an EPP request and response. */
|
||||
@@ -55,7 +56,10 @@ public class EppRequestHandler {
|
||||
eppController.handleEppCommand(
|
||||
sessionMetadata, credentials, eppRequestSource, isDryRun, isSuperuser, inputXmlBytes);
|
||||
response.setContentType(APPLICATION_EPP_XML);
|
||||
final StopwatchLogger stopwatch = new StopwatchLogger();
|
||||
byte[] eppResponseXmlBytes = marshalWithLenientRetry(eppOutput);
|
||||
stopwatch.tick("Completed EPP output marshaling.");
|
||||
|
||||
response.setPayload(new String(eppResponseXmlBytes, UTF_8));
|
||||
logger.atInfo().log(
|
||||
"EPP response: %s", prettyPrint(EppXmlSanitizer.sanitizeEppXml(eppResponseXmlBytes)));
|
||||
|
||||
@@ -29,6 +29,7 @@ import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.monitoring.whitebox.EppMetric;
|
||||
import google.registry.persistence.PersistenceModule.TransactionIsolationLevel;
|
||||
import google.registry.persistence.transaction.JpaTransactionManager;
|
||||
import google.registry.util.StopwatchLogger;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
import java.util.Optional;
|
||||
@@ -77,23 +78,32 @@ public class FlowRunner {
|
||||
flowReporter.recordToLogs();
|
||||
}
|
||||
eppMetricBuilder.setCommandNameFromFlow(flowClass.getSimpleName());
|
||||
final StopwatchLogger stopwatch = new StopwatchLogger();
|
||||
|
||||
// We may already be in a transaction, e.g., when invoked by DeleteExpiredDomainsAction.
|
||||
if (!isTransactional || jpaTransactionManager.inTransaction()) {
|
||||
stopwatch.tick("We're in transaction, running the flow now.");
|
||||
return EppOutput.create(flowProvider.get().run());
|
||||
}
|
||||
|
||||
stopwatch.tick("We're not in transaction, calling transact.");
|
||||
try {
|
||||
return jpaTransactionManager.transact(
|
||||
isolationLevelOverride.orElse(null),
|
||||
() -> {
|
||||
try {
|
||||
stopwatch.tick("Running the flow in transaction.");
|
||||
EppOutput output = EppOutput.create(flowProvider.get().run());
|
||||
stopwatch.tick("Completed the flow in transaction.");
|
||||
if (isDryRun) {
|
||||
throw new DryRunException(output);
|
||||
}
|
||||
if (flowClass.equals(LoginFlow.class)) {
|
||||
// In LoginFlow, registrarId isn't known until after the flow executes, so save
|
||||
// it then.
|
||||
stopwatch.tick("Login flow started setting registrar id.");
|
||||
eppMetricBuilder.setRegistrarId(sessionMetadata.getRegistrarId());
|
||||
stopwatch.tick("Login flow finished setting registrar id.");
|
||||
}
|
||||
return output;
|
||||
} catch (EppException e) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import static google.registry.xml.ValidationMode.STRICT;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.flows.EppException.CommandUseErrorException;
|
||||
import google.registry.flows.EppException.ParameterValueRangeErrorException;
|
||||
@@ -30,6 +31,7 @@ import google.registry.flows.custom.EntityChanges;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.adapters.CurrencyUnitAdapter.UnknownCurrencyException;
|
||||
import google.registry.model.eppcommon.EppXmlTransformer;
|
||||
import google.registry.model.eppcommon.StatusValue;
|
||||
import google.registry.model.eppinput.EppInput.WrongProtocolVersionException;
|
||||
import google.registry.model.eppoutput.EppOutput;
|
||||
import google.registry.model.host.InetAddressAdapter.IpVersionMismatchException;
|
||||
@@ -40,6 +42,9 @@ import java.util.List;
|
||||
/** Static utility functions for flows. */
|
||||
public final class FlowUtils {
|
||||
|
||||
public static final ImmutableSet<StatusValue> DELETE_PROHIBITED_STATUSES =
|
||||
ImmutableSet.of(StatusValue.CLIENT_DELETE_PROHIBITED, StatusValue.SERVER_DELETE_PROHIBITED);
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private FlowUtils() {}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package google.registry.flows.contact;
|
||||
|
||||
import static google.registry.flows.FlowUtils.DELETE_PROHIBITED_STATUSES;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.checkLinkedDomains;
|
||||
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
|
||||
@@ -65,12 +66,6 @@ import org.joda.time.DateTime;
|
||||
@ReportingSpec(ActivityReportField.CONTACT_DELETE)
|
||||
public final class ContactDeleteFlow implements MutatingFlow {
|
||||
|
||||
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES =
|
||||
ImmutableSet.of(
|
||||
StatusValue.CLIENT_DELETE_PROHIBITED,
|
||||
StatusValue.PENDING_DELETE,
|
||||
StatusValue.SERVER_DELETE_PROHIBITED);
|
||||
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject @RegistrarId String registrarId;
|
||||
@Inject @TargetId String targetId;
|
||||
@@ -91,9 +86,10 @@ public final class ContactDeleteFlow implements MutatingFlow {
|
||||
DateTime now = tm().getTransactionTime();
|
||||
checkLinkedDomains(targetId, now, Contact.class);
|
||||
Contact existingContact = loadAndVerifyExistence(Contact.class, targetId, now);
|
||||
verifyNoDisallowedStatuses(existingContact, DISALLOWED_STATUSES);
|
||||
verifyOptionalAuthInfo(authInfo, existingContact);
|
||||
verifyNoDisallowedStatuses(existingContact, ImmutableSet.of(StatusValue.PENDING_DELETE));
|
||||
if (!isSuperuser) {
|
||||
verifyNoDisallowedStatuses(existingContact, DELETE_PROHIBITED_STATUSES);
|
||||
verifyResourceOwnership(registrarId, existingContact);
|
||||
}
|
||||
// Handle pending transfers on contact deletion.
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.flows.domain;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.flows.FlowUtils.DELETE_PROHIBITED_STATUSES;
|
||||
import static google.registry.flows.FlowUtils.createHistoryEntryId;
|
||||
import static google.registry.flows.FlowUtils.persistEntityChanges;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
@@ -122,11 +123,6 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES = ImmutableSet.of(
|
||||
StatusValue.CLIENT_DELETE_PROHIBITED,
|
||||
StatusValue.PENDING_DELETE,
|
||||
StatusValue.SERVER_DELETE_PROHIBITED);
|
||||
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject EppInput eppInput;
|
||||
@Inject SessionMetadata sessionMetadata;
|
||||
@@ -304,9 +300,10 @@ public final class DomainDeleteFlow implements MutatingFlow, SqlStatementLogging
|
||||
|
||||
private void verifyDeleteAllowed(Domain existingDomain, Tld tld, DateTime now)
|
||||
throws EppException {
|
||||
verifyNoDisallowedStatuses(existingDomain, DISALLOWED_STATUSES);
|
||||
verifyOptionalAuthInfo(authInfo, existingDomain);
|
||||
verifyNoDisallowedStatuses(existingDomain, ImmutableSet.of(StatusValue.PENDING_DELETE));
|
||||
if (!isSuperuser) {
|
||||
verifyNoDisallowedStatuses(existingDomain, DELETE_PROHIBITED_STATUSES);
|
||||
verifyResourceOwnership(registrarId, existingDomain);
|
||||
verifyNotInPredelegation(tld, now);
|
||||
checkAllowedAccessToTld(registrarId, tld.getTld().toString());
|
||||
|
||||
@@ -481,10 +481,10 @@ public class DomainFlowUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces the presence/absence of contact data depending on the minimum data set migration
|
||||
* schedule.
|
||||
* Enforces the presence/absence of contact data on domain creates depending on the minimum data
|
||||
* set migration schedule.
|
||||
*/
|
||||
static void validateContactDataPresence(
|
||||
static void validateCreateContactData(
|
||||
Optional<VKey<Contact>> registrant, Set<DesignatedContact> contacts)
|
||||
throws RequiredParameterMissingException, ParameterValuePolicyErrorException {
|
||||
// TODO(b/353347632): Change these flag checks to a registry config check once minimum data set
|
||||
@@ -514,6 +514,45 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enforces the presence/absence of contact data on domain updates depending on the minimum data
|
||||
* set migration schedule.
|
||||
*/
|
||||
static void validateUpdateContactData(
|
||||
Optional<VKey<Contact>> existingRegistrant,
|
||||
Optional<VKey<Contact>> newRegistrant,
|
||||
Set<DesignatedContact> existingContacts,
|
||||
Set<DesignatedContact> newContacts)
|
||||
throws RequiredParameterMissingException, ParameterValuePolicyErrorException {
|
||||
// TODO(b/353347632): Change these flag checks to a registry config check once minimum data set
|
||||
// migration is completed.
|
||||
if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) {
|
||||
// Throw if the update specifies a new registrant that is different from the existing one.
|
||||
if (newRegistrant.isPresent() && !newRegistrant.equals(existingRegistrant)) {
|
||||
throw new RegistrantProhibitedException();
|
||||
}
|
||||
// Throw if the update specifies any new contacts that weren't already present on the domain.
|
||||
if (!Sets.difference(newContacts, existingContacts).isEmpty()) {
|
||||
throw new ContactsProhibitedException();
|
||||
}
|
||||
} else if (!FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) {
|
||||
// Throw if the update empties out a registrant that had been present.
|
||||
if (newRegistrant.isEmpty() && existingRegistrant.isPresent()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
// Throw if the update contains no admin contact when one had been present.
|
||||
if (existingContacts.stream().anyMatch(c -> c.getType().equals(Type.ADMIN))
|
||||
&& newContacts.stream().noneMatch(c -> c.getType().equals(Type.ADMIN))) {
|
||||
throw new MissingAdminContactException();
|
||||
}
|
||||
// Throw if the update contains no tech contact when one had been present.
|
||||
if (existingContacts.stream().anyMatch(c -> c.getType().equals(Type.TECH))
|
||||
&& newContacts.stream().noneMatch(c -> c.getType().equals(Type.TECH))) {
|
||||
throw new MissingTechnicalContactException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void validateRegistrantAllowedOnTld(String tld, Optional<String> registrantContactId)
|
||||
throws RegistrantNotAllowedException {
|
||||
ImmutableSet<String> allowedRegistrants = Tld.get(tld).getAllowedRegistrantContactIds();
|
||||
@@ -1054,7 +1093,7 @@ public class DomainFlowUtils {
|
||||
String tldStr = tld.getTldStr();
|
||||
validateRegistrantAllowedOnTld(tldStr, command.getRegistrantContactId());
|
||||
validateNoDuplicateContacts(command.getContacts());
|
||||
validateContactDataPresence(command.getRegistrant(), command.getContacts());
|
||||
validateCreateContactData(command.getRegistrant(), command.getContacts());
|
||||
ImmutableSet<String> hostNames = command.getNameserverHostNames();
|
||||
validateNameserversCountForTld(tldStr, domainName, hostNames.size());
|
||||
validateNameserversAllowedOnTld(tldStr, hostNames);
|
||||
@@ -1358,7 +1397,7 @@ public class DomainFlowUtils {
|
||||
}
|
||||
|
||||
/** Domain name is under tld which doesn't exist. */
|
||||
static class TldDoesNotExistException extends ParameterValueRangeErrorException {
|
||||
public static class TldDoesNotExistException extends ParameterValueRangeErrorException {
|
||||
public TldDoesNotExistException(String tld) {
|
||||
super(String.format("Domain name is under tld %s which doesn't exist", tld));
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.cloneAndLinkReferences;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.updateDsData;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateContactDataPresence;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateContactsHaveTypes;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateDsData;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateFeesAckedIfPresent;
|
||||
@@ -38,6 +37,7 @@ import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAl
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateUpdateContactData;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyClientUpdateNotProhibited;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
@@ -186,7 +186,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
Domain newDomain = performUpdate(command, existingDomain, now);
|
||||
DomainHistory domainHistory =
|
||||
historyBuilder.setType(DOMAIN_UPDATE).setDomain(newDomain).build();
|
||||
validateNewState(newDomain);
|
||||
validateNewState(existingDomain, newDomain);
|
||||
if (requiresDnsUpdate(existingDomain, newDomain)) {
|
||||
requestDomainDnsRefresh(targetId);
|
||||
}
|
||||
@@ -328,8 +328,13 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
* compliant with the additions or amendments, otherwise existing data can become invalid and
|
||||
* cause Domain update failure.
|
||||
*/
|
||||
private static void validateNewState(Domain newDomain) throws EppException {
|
||||
validateContactDataPresence(newDomain.getRegistrant(), newDomain.getContacts());
|
||||
private static void validateNewState(Domain existingDomain, Domain newDomain)
|
||||
throws EppException {
|
||||
validateUpdateContactData(
|
||||
existingDomain.getRegistrant(),
|
||||
newDomain.getRegistrant(),
|
||||
existingDomain.getContacts(),
|
||||
newDomain.getContacts());
|
||||
validateDsData(newDomain.getDsData());
|
||||
validateNameserversCountForTld(
|
||||
newDomain.getTld(),
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.flows.host;
|
||||
|
||||
import static google.registry.dns.DnsUtils.requestHostDnsRefresh;
|
||||
import static google.registry.flows.FlowUtils.DELETE_PROHIBITED_STATUSES;
|
||||
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
|
||||
import static google.registry.flows.ResourceFlowUtils.checkLinkedDomains;
|
||||
import static google.registry.flows.ResourceFlowUtils.loadAndVerifyExistence;
|
||||
@@ -65,12 +66,6 @@ import org.joda.time.DateTime;
|
||||
@ReportingSpec(ActivityReportField.HOST_DELETE)
|
||||
public final class HostDeleteFlow implements MutatingFlow {
|
||||
|
||||
private static final ImmutableSet<StatusValue> DISALLOWED_STATUSES =
|
||||
ImmutableSet.of(
|
||||
StatusValue.CLIENT_DELETE_PROHIBITED,
|
||||
StatusValue.PENDING_DELETE,
|
||||
StatusValue.SERVER_DELETE_PROHIBITED);
|
||||
|
||||
@Inject ExtensionManager extensionManager;
|
||||
@Inject @RegistrarId String registrarId;
|
||||
@Inject @TargetId String targetId;
|
||||
@@ -91,8 +86,9 @@ public final class HostDeleteFlow implements MutatingFlow {
|
||||
validateHostName(targetId);
|
||||
checkLinkedDomains(targetId, now, Host.class);
|
||||
Host existingHost = loadAndVerifyExistence(Host.class, targetId, now);
|
||||
verifyNoDisallowedStatuses(existingHost, DISALLOWED_STATUSES);
|
||||
verifyNoDisallowedStatuses(existingHost, ImmutableSet.of(StatusValue.PENDING_DELETE));
|
||||
if (!isSuperuser) {
|
||||
verifyNoDisallowedStatuses(existingHost, DELETE_PROHIBITED_STATUSES);
|
||||
// Hosts transfer with their superordinate domains, so for hosts with a superordinate domain,
|
||||
// the client id, needs to be read off of it.
|
||||
EppResource owningResource =
|
||||
|
||||
@@ -47,6 +47,7 @@ import google.registry.model.eppinput.EppInput.Options;
|
||||
import google.registry.model.eppinput.EppInput.Services;
|
||||
import google.registry.model.eppoutput.EppResponse;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.util.StopwatchLogger;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -97,19 +98,25 @@ public class LoginFlow implements MutatingFlow {
|
||||
|
||||
/** Run the flow without bothering to log errors. The {@link #run} method will do that for us. */
|
||||
private EppResponse runWithoutLogging() throws EppException {
|
||||
final StopwatchLogger stopwatch = new StopwatchLogger();
|
||||
extensionManager.validate(); // There are no legal extensions for this flow.
|
||||
stopwatch.tick("LoginFlow extension validate");
|
||||
Login login = (Login) eppInput.getCommandWrapper().getCommand();
|
||||
stopwatch.tick("LoginFlow getCommand");
|
||||
if (!registrarId.isEmpty()) {
|
||||
throw new AlreadyLoggedInException();
|
||||
}
|
||||
Options options = login.getOptions();
|
||||
stopwatch.tick("LoginFlow getOptions");
|
||||
if (!ProtocolDefinition.LANGUAGE.equals(options.getLanguage())) {
|
||||
throw new UnsupportedLanguageException();
|
||||
}
|
||||
Services services = login.getServices();
|
||||
stopwatch.tick("LoginFlow getServices");
|
||||
Set<String> unsupportedObjectServices = difference(
|
||||
nullToEmpty(services.getObjectServices()),
|
||||
ProtocolDefinition.SUPPORTED_OBJECT_SERVICES);
|
||||
stopwatch.tick("LoginFlow difference unsupportedObjectServices");
|
||||
if (!unsupportedObjectServices.isEmpty()) {
|
||||
throw new UnimplementedObjectServiceException();
|
||||
}
|
||||
@@ -121,11 +128,12 @@ public class LoginFlow implements MutatingFlow {
|
||||
}
|
||||
serviceExtensionUrisBuilder.add(uri);
|
||||
}
|
||||
stopwatch.tick("LoginFlow serviceExtensionUrisBuilder");
|
||||
Optional<Registrar> registrar = Registrar.loadByRegistrarIdCached(login.getClientId());
|
||||
if (registrar.isEmpty()) {
|
||||
throw new BadRegistrarIdException(login.getClientId());
|
||||
}
|
||||
|
||||
stopwatch.tick("LoginFlow loadByRegistrarIdCached");
|
||||
// AuthenticationErrorExceptions will propagate up through here.
|
||||
try {
|
||||
credentials.validate(registrar.get(), login.getPassword());
|
||||
@@ -137,6 +145,7 @@ public class LoginFlow implements MutatingFlow {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
stopwatch.tick("LoginFlow credentials.validate");
|
||||
if (!registrar.get().isLive()) {
|
||||
throw new RegistrarAccountNotActiveException();
|
||||
}
|
||||
@@ -145,17 +154,24 @@ public class LoginFlow implements MutatingFlow {
|
||||
String newPassword = login.getNewPassword().get();
|
||||
// Load fresh from database (bypassing the cache) to ensure we don't save stale data.
|
||||
Optional<Registrar> freshRegistrar = Registrar.loadByRegistrarId(login.getClientId());
|
||||
stopwatch.tick("LoginFlow reload freshRegistrar");
|
||||
if (freshRegistrar.isEmpty()) {
|
||||
throw new BadRegistrarIdException(login.getClientId());
|
||||
}
|
||||
tm().put(freshRegistrar.get().asBuilder().setPassword(newPassword).build());
|
||||
stopwatch.tick("LoginFlow updated password");
|
||||
}
|
||||
|
||||
// We are in!
|
||||
sessionMetadata.resetFailedLoginAttempts();
|
||||
stopwatch.tick("LoginFlow resetFailedLoginAttempts");
|
||||
sessionMetadata.setRegistrarId(login.getClientId());
|
||||
stopwatch.tick("LoginFlow setRegistrarId");
|
||||
sessionMetadata.setServiceExtensionUris(serviceExtensionUrisBuilder.build());
|
||||
return responseBuilder.setIsLoginResponse().build();
|
||||
stopwatch.tick("LoginFlow setServiceExtensionUris");
|
||||
EppResponse eppResponse = responseBuilder.setIsLoginResponse().build();
|
||||
stopwatch.tick("LoginFlow eppResponse build()");
|
||||
return eppResponse;
|
||||
}
|
||||
|
||||
/** Registrar with this ID could not be found. */
|
||||
|
||||
661
core/src/main/java/google/registry/idn/Latin-IDN.xml
Normal file
661
core/src/main/java/google/registry/idn/Latin-IDN.xml
Normal file
@@ -0,0 +1,661 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<lgr xmlns="urn:ietf:params:xml:ns:lgr-1.0">
|
||||
<meta>
|
||||
<version comment="Latin LGR">1</version>
|
||||
<date>2025-10-01</date>
|
||||
<language>und-Latn</language>
|
||||
<unicode-version>2</unicode-version>
|
||||
<description type="text/html"><![CDATA[
|
||||
<div class="instructions">
|
||||
<h2>INSTRUCTIONS</h2>
|
||||
<ul>
|
||||
<li>These instructions cover how to adopt an LGR based on this reference LGR for a given
|
||||
zone and how to prepare the file for deposit in the IANA Repository of IDN Practices.</li>
|
||||
<li>As described the IANA procedure (https://www.iana.org/help/idn-repository-procedure) an
|
||||
LGR MUST contain the following elements in its header:
|
||||
<ul style="list-style-type:square;">
|
||||
<li>Script or Language Designator (see below for guidance) </li>
|
||||
<li>Version Number (this must increase with each amendment to the LGR, even if the updates
|
||||
are limited to the header itself) </li>
|
||||
<li>Effective Date (the date at which the policy becomes applicable in operational use) </li>
|
||||
<li>Registry Contact Details (contact name, email address, and/or phone number)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>The following information is optional:
|
||||
<ul style="list-style-type:square;">
|
||||
<li>Document creation date</li>
|
||||
<li>Applicable Domain(s)</li>
|
||||
<li>Changes made to the Reference LGR before adopting</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Please add or modify the following items in the <b>XML source code for this file</b> before
|
||||
depositing the document in the IANA Repository. (https://www.iana.org/domains/idn-tables)</p>
|
||||
<h3>Meta Data</h3>
|
||||
<p>Note: version numbers start at 1. RFC 7940 recommends using simple integers. The version comment is optional,
|
||||
please replace or delete the default comment. Version comments may be used by some tools as part of the page header.</p>
|
||||
<p><code><version comment="</code>[Please replace (or delete) the optional comment]<code>"></code>[Please fill in version number, starting at 1]<code></version></code></p>
|
||||
<p><code><date></code>2025-10-01<code></date></code></p>
|
||||
<p><code><validity-start></code>2025-10-01<code></validity-start></code></p>
|
||||
<p>Note: the scope element may be repeated, so that the same document can serve for multiple domains.</p>
|
||||
<p><code><scope type="domain"></code>[Please provide, in ".domain" format]<code></scope></code></p>
|
||||
<p><strong>Registry Contact Information:</strong></p>
|
||||
<p>Please fill in the <a href="#registry_contact_details">Registry Contact Details</a>.</p>
|
||||
<p><strong>Change History</strong></p>
|
||||
<p>If you made technical modifications to the LGR, please summarize them in the <a href="#change_history">Change History</a> (and also note the details in the appropriate section of the description).</p>
|
||||
|
||||
|
||||
<section id="registry_contact_details">
|
||||
<h2>Registry Contact Details</h2>
|
||||
<ul style="list-style:none;">
|
||||
<li><b>Contact Name:</b> Ben McIlwain</li>
|
||||
<li><b>Email address:</b> nomulus-discuss@google.com</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<h1>Label Generation Rules for the Latin Script</h1>
|
||||
|
||||
<h2>Overview</h2>
|
||||
|
||||
<p>This document specifies a set of Label Generation Rules (LGR) for the Latin script for the second level domain or domains identified above.
|
||||
The starting point for the development of this LGR can be found in the related Root Zone LGR [RZ-LGR-Latn].
|
||||
The format of this file follows [RFC 7940].
|
||||
This LGR is adapted from the “Reference LGR for the Second Level for the Latin Script” [Ref-LGR-und-Latn], for details, see <a href="#change_history">Change History</a> below.</p>
|
||||
|
||||
<p>For details and additional background on the Latin script, see “Proposal for a Latin Script Root Zone Label Generation Rule-Set (LGR)” [Proposal-Latin].</p>
|
||||
|
||||
<h2>Repertoire</h2>
|
||||
|
||||
<p>The repertoire contains the 164 letters needed to write hundreds of languages in the Latin script.
|
||||
The repertoire is a subset of [Unicode 11.0.0]. For details, see Section 5, “Repertoire” in [Proposal-Latin].
|
||||
(The proposal cited has been adopted for the Latin script portion of the Root Zone LGR.)</p>
|
||||
|
||||
<p>For the second level, the repertoire has been augmented with the ASCII digits, U+0030 to U+0039, plus U+002D HYPHEN-MINUS, for a total of 175 repertoire elements.</p>
|
||||
|
||||
<p>Any code points outside the Latin Script repertoire that are targets for
|
||||
out-of-repertoire variants would be included here only if the variant is listed
|
||||
in this file. In this case they are identified as a reflexive (identity) variant
|
||||
of type “out-of-repertoire-var”. Whether or not they are listed, they do not
|
||||
form part of the repertoire.</p>
|
||||
|
||||
<p><b>Repertoire Listing:</b> Each code point or range is tagged with the script or scripts with which the code point is used and one or more other character categories. For each repertoire element,
|
||||
one or more references document sufficient justification for inclusion in the repertoire; see the <a href="#ref_desc_sec_References">“References”</a> below.
|
||||
For code points that are part of the repertoire, comments identify the languages using the code point along with their [EGIDS] level.</p>
|
||||
|
||||
<h3>Background on Script and Principal Languages Using It</h3>
|
||||
<p>The Latin script is a major writing system of the world, and the most widely used in terms of
|
||||
the number of languages and speakers, with circa 70% of the world’s readers and writers making use of
|
||||
this script. From a list of 1,189 languages using the Latin script [Omniglot]
|
||||
the 212 languages that were taken into consideration contain all 182 languages with [EGIDS]
|
||||
level 1–4 together with many languages with EGIDS level 5, each spoken by more than
|
||||
1 million estimated speakers. Altogether over 100 languages are cited here to justify
|
||||
specific additions to the repertoire, but many other languages may also be written using
|
||||
some subset of the repertoire of this LGR. In a few cases, code points were excluded in [MSR-5] due to
|
||||
security concerns; for the affected languages, only a subrepertoire could be supported.
|
||||
More details in Section 3, “Background on Script and Principal Languages Using It”
|
||||
of [Proposal-Latin].</p>
|
||||
|
||||
<h2>Variants</h2>
|
||||
|
||||
<p>The variants defined in this LGR are limited to those required for use in zones not shared with any other script.
|
||||
As such, this LGR does not define cross-script variants. However, using this LGR concurrently with any LGR for Armenian, Cyrillic, and Greek in the same zone will introduce some in-script variants due to cross-script variant transitivity. This will also create potential cross-script issues when used with the same LGRs.
|
||||
For details, see Section 6, “Variants” in [Proposal-Latin].
|
||||
Mitigation of these in-script and cross-script variants can be addressed by using the Common LGR.
|
||||
For details, see Section 3, “Use of Multiple Reference LGRs in the Same Zone” in [Level-2-Overview].
|
||||
In addition to variants defined by this LGR, the full variant information related to this script and added by concurrent use with the Armenian, Cyrillic, and Greek LGR(s) can be found
|
||||
in the following LGR: [Ref-LGR-Latin-Full-Variant-Script]
|
||||
</p>
|
||||
|
||||
<p>In particular, the Latin LGR contains a number of in-script variants resulting from transitivity with
|
||||
variants needed if the LGR is used concurrently with other LGRs from the related scripts, primarily
|
||||
due to variants with the Greek script.
|
||||
These variant definitions are required when using this LGR together with the Common LGR in label processing,
|
||||
but they also reflect a certain consistency in the approach to variants across typographically related scripts.
|
||||
They can be removed if the LGR is used strictly as standalone.<p>
|
||||
|
||||
<p>All other in-script variants defined in this LGR largely follow the methodology defined in
|
||||
Section 6, “Variants”, in [Proposal-Latin]. In a separate appendix that proposal identifies additional
|
||||
candidates for visually confusable code points (see [Proposal-Latin-Appendices]). They provide data
|
||||
on the basis of which additional variants, or fallback variants (see following) might be defined. However,
|
||||
doing so would require additional review and analysis that has not been carried out for this version of the
|
||||
LGR.</p>
|
||||
|
||||
<p>The LGR defines certain allocatable fallback variants as
|
||||
described in Section 4.5.5 “Allocatable Fallback Variants” in [Level-2-Overview]. A fallback variant is a variant label that uses
|
||||
substitute code points for code points or sequences not available (or not allowed) in some contexts, that would
|
||||
otherwise be required for a linguistically accurate rendering of some label.</p>
|
||||
|
||||
<p>When “fallback” variants are defined, two labels may be allocated: a single label with the spelling preferred by the
|
||||
applicant, plus a single fallback variant for that label.
|
||||
The fallback exclusively uses the fallback characters for any characters for which fallbacks are defined, while the
|
||||
“preferred” label may use any otherwise valid mix of code points.
|
||||
If the fallback variant is the one applied for, no other variant label is allocatable.</p>
|
||||
|
||||
<p>An allocatable fallback variant exists for the following pairs where the second element of each pair is the fallback:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<h3>In-script Variant Mapping Types</h3>
|
||||
<p>In each of the fallback variant pairs defined above, the mapping type from the first element to the second is of type
|
||||
“fallback”, while the variant type for the other direction is “blocked”. In addition, the first element of each pair uses the
|
||||
reflexive mapping “r-original”.
|
||||
(By convention, the prefix “r-” marks a type used in a reflexive variant mapping, that is, it represents an instance
|
||||
of the original code point at that location in a variant label, see Section 5.3.4 in [RFC 7940].)</p>
|
||||
|
||||
<p><b>Variant Disposition:</b> Except for limited exceptions for the fallback variants defined above, variants defined here result
|
||||
in a variant label disposition of “blocked”.</p>
|
||||
|
||||
<p>The specification of variants in this LGR follows the guidelines in [RFC 8228].</p>
|
||||
|
||||
<h2>Character Classes</h2>
|
||||
<p>This LGR does not define named character classes.</p>
|
||||
|
||||
<h2>Whole Label Evaluation (WLE) and Context rules</h2>
|
||||
|
||||
<h3>Default Whole Label Evaluation Rules and Actions</h3>
|
||||
|
||||
<p>By default, the LGR includes the rules and actions to implement the following restrictions mandated by the IDNA protocol. They are marked with ⍟.</p>
|
||||
<ul>
|
||||
<li><b>Hyphen Restrictions</b> — restrictions on the allowable placement of hyphens (no leading/ending hyphen
|
||||
and no hyphen in positions 3 and 4). These restrictions are described in Section 4.2.3.1 of RFC 5891 [320].
|
||||
They are implemented here as context rule on U+002D (-) HYPHEN-MINUS.</li>
|
||||
<li><b>Leading Combining Marks</b> — restrictions on the allowable placement of combining marks
|
||||
(no leading combining mark). This rule is described in Section 4.2.3.2 of RFC 5891 [320].</li>
|
||||
</ul>
|
||||
|
||||
<h3>Latin-specific Rules</h3>
|
||||
|
||||
<h2>Actions</h2>
|
||||
|
||||
<h3>Default Actions</h3>
|
||||
|
||||
<p>This LGR includes the default actions for LGRs as well as the action needed to
|
||||
invalidate labels with misplaced combining marks. They are marked with ⍟.
|
||||
For a description see [RFC 7940].</p>
|
||||
|
||||
<p>Default actions that are
|
||||
triggered by the LGR-specific variant types described above limit the “allocatable” variant
|
||||
labels to those containing only “ss”, dotted “i” or hyphen variants, while
|
||||
disallowing mixed use of “ss” and “ß”, Dotless i and “i”, or middle-dot and hyphen respectively, except
|
||||
as in the original applied-for label.</p>
|
||||
|
||||
<p>Note that the mapping types for variants are not symmetric: they depend on which code point is considered
|
||||
the source or the target in a given mapping. As specified in [RFC 7940], when mapping types are evaluated
|
||||
code points in a label that are unchanged use the type of their “reflexive” mapping.
|
||||
Per [RFC 7940] the actions are always applied one after the other, and the evaluation stops at the first
|
||||
action that assigns a disposition to a given label.</p>
|
||||
|
||||
<h3>Script-specific Actions</h3>
|
||||
<p>An action has been defined to invalidate labels containing the sequence U+00B7 U+006C U+00B7 which could otherwise result in two Ela Geminada sequences overlapping.</p>
|
||||
|
||||
<h2>Methodology and Contributors</h2>
|
||||
|
||||
<p>The LGR in this document has been adapted from the corresponding Reference LGR for the Second Level. The Second Level Reference LGR for the Latin Script was developed by Michel Suignard and Asmus Freytag, based on the Root Zone LGR for the Latin
|
||||
script and information contained or referenced therein; see [RZ-LGR-Latn]. Suitable extensions for the second level have been applied according to the [Guidelines] and with community input.
|
||||
The original proposal for a Root Zone LGR for the Latin script, that this LGR is based on, was developed by the Latin Generation Panel.
|
||||
For more information on methodology and contributors to the underlying Root Zone LGR, see Sections 4 and 8 in [Proposal-Latin], as well as [RZ-LGR-Overview].</p>
|
||||
|
||||
<section id="change_history">
|
||||
<h3>Changes from Version Dated 25 October 2024</h3>
|
||||
<p>Adopted from the Second Level Reference LGR for the Latin Script [Ref-LGR-und-Latn] with security improvements implemented by removing confusable variants.</p>
|
||||
</section>
|
||||
|
||||
<h2>References</h2>
|
||||
<p>The following general references are cited in this document:</p>
|
||||
<dl class="references">
|
||||
<dt>[EGIDS]</dt>
|
||||
<dd>Lewis and Simons, “EGIDS: Expanded Graded Intergenerational Disruption Scale,”
|
||||
documented in [SIL-Ethnologue] and summarized here:
|
||||
https://en.wikipedia.org/wiki/Expanded_Graded_Intergenerational_Disruption_Scale_(EGIDS)</dd>
|
||||
|
||||
<dt>[Guidelines]</dt>
|
||||
<dd>ICANN, “Guidelines for Developing Reference LGRs for the Second Level”, (Los Angeles, California: ICANN, 27 May 2020), https://www.icann.org/en/system/files/files/lgr-guidelines-second-level-27may20-en.pdf</dd>
|
||||
|
||||
<dt>[Level-2-Overview]</dt>
|
||||
<dd>Internet Corporation for Assigned Names and Numbers, (ICANN),“Reference Label Generation Rules (LGR) for the Second Level: Overview and Summary” (PDF),
|
||||
(Los Angeles, California: ICANN, 25 October 2024), https://www.icann.org/en/system/files/files/level2-lgr-overview-summary-25oct24-en.pdf
|
||||
</dd>
|
||||
|
||||
<dt>[MSR-5]</dt>
|
||||
<dd>Integration Panel, “Maximal Starting Repertoire — MSR-5 Overview and Rationale”, 24 June 2021,
|
||||
https://www.icann.org/en/system/files/files/msr-5-overview-24jun21-en.pdf</dd>
|
||||
|
||||
<dt>[Omniglot]</dt>
|
||||
<dd>Omniglot, “Writing Systems by Language”, https://www.omniglot.com/writing/langalph.htm (accessed on 13 January 2022)</dd>
|
||||
|
||||
<dt>[Proposal-Latin]</dt>
|
||||
<dd>Latin Generation Panel, “Proposal for a Latin Script Root Zone Label Generation Rule-Set (LGR)”, 27 January 2022 (PDF), https://www.icann.org/en/system/files/files/proposal-latin-lgr-27jan22-en.pdf</dd>
|
||||
|
||||
<dt>[Proposal-Latin-Appendices]</dt>
|
||||
<dd>Appendices to “Proposal for a Latin Script Root Zone Label Generation Rule-Set (LGR)”,
|
||||
27 January 2022 (ZIP), https://www.icann.org/en/system/files/files/proposal-latin-lgr-appendices-27jan22-en.zip</dd>
|
||||
|
||||
<dt>[Ref-LGR-und-Latn]</dt>
|
||||
<dd>ICANN, Second Level Reference Label Generation Rules for the Latin Script (und-Latn), 25 October 2024 (XML)
|
||||
https://www.icann.org/sites/default/files/packages/lgr/lgr-second-level-latin-script-25oct24-en.xml
|
||||
non-normative HTML presentation: https://www.icann.org/sites/default/files/packages/lgr/lgr-second-level-latin-script-25oct24-en.html</dd>
|
||||
|
||||
<dt>[Ref-LGR-Latin-Full-Variant-Script]</dt>
|
||||
<dd>ICANN, Second Level Reference Label Generation Rules for the Latin Script (und-Latn), 25 October 2024 (XML)
|
||||
https://www.icann.org/sites/default/files/packages/lgr/lgr-second-level-latin-full-variant-script-25oct24-en.xml
|
||||
non-normative HTML presentation: https://www.icann.org/sites/default/files/packages/lgr/lgr-second-level-latin-full-variant-script-25oct24-en.html</dd>
|
||||
|
||||
<dt>[RFC 7940]</dt>
|
||||
<dd>Davies, K. and A. Freytag, “Representing Label Generation Rulesets Using XML”,
|
||||
RFC 7940, August 2016, https://www.rfc-editor.org/info/rfc7940</dd>
|
||||
|
||||
<dt>[RFC 8228]</dt>
|
||||
<dd>A. Freytag, “Guidance on Designing Label Generation Rulesets (LGRs) Supporting Variant Labels”, RFC 8228, August 2017,
|
||||
https://www.rfc-editor.org/info/rfc8228</dd>
|
||||
|
||||
<dt>[RZ-LGR-Overview]</dt>
|
||||
<dd>Integration Panel, “Root Zone Label Generation Rules (RZ LGR-5): Overview and Summary”, 26 May 2022 (PDF), https://www.icann.org/sites/default/files/lgr/rz-lgr-5-overview-26may22-en.pdf</dd>
|
||||
|
||||
<dt>[RZ-LGR-5]</dt>
|
||||
<dd>Integration Panel, “Root Zone Label Generation Rules (RZ-LGR-5)”, 26 May 2022 (XML), https://www.icann.org/sites/default/files/lgr/rz-lgr-5-common-26may22-en.xml <br/>
|
||||
<i>non-normative HTML presentation: https://www.icann.org/sites/default/files/lgr/rz-lgr-5-common-26may22-en.html</i></dd>
|
||||
|
||||
<dt>[RZ-LGR-Latn]</dt>
|
||||
<dd>ICANN, Root Zone Label Generation Rules for the Latin Script (und-Latn), 26 May 2022 (XML)
|
||||
https://www.icann.org/sites/default/files/lgr/rz-lgr-5-latin-script-26may22-en.xml</dd>
|
||||
|
||||
<dt>[SIL-Ethnologue]</dt>
|
||||
<dd>David M. Eberhard, Gary F. Simons & Charles D. Fennig (eds.). 2021.
|
||||
Ethnologue: Languages of the World, Twenty fourth edition. Dallas, Texas: SIL
|
||||
International. Online version available as https://www.ethnologue.com</dd>
|
||||
|
||||
<dt>[Unicode 11.0.0]</dt>
|
||||
<dd>The Unicode Consortium. The Unicode Standard, Version 11.0.0, (Mountain View, CA: The Unicode Consortium, 2018. ISBN 978-1-936213-19-1)
|
||||
https://www.unicode.org/versions/Unicode11.0.0/</dd>
|
||||
</dl>
|
||||
|
||||
<p>For references consulted particularly in designing the repertoire for the Latin Script for the second level
|
||||
please see details in the <a href="#table_of_references">Table of References</a> below.</p>
|
||||
|
||||
<p>References [0] and up refer to the Unicode Standard versions in which corresponding code points
|
||||
were initially encoded. References [101] and up correspond to a source given in [Proposal-Latin] for justifying
|
||||
the inclusion of the corresponding code points. In the listing of <a href="#whole_label_evaluation_and_context_rules">whole label evaluation and context rules</a>,
|
||||
reference [320] indicates the source for common rules.</p>
|
||||
|
||||
]]></description>
|
||||
<references>
|
||||
<reference id="0" comment="Any code point originally encoded in Unicode Version 1.1">The Unicode Standard, Version 1.1</reference>
|
||||
<reference id="3" comment="Any code point originally encoded in Unicode Version 3.0">The Unicode Standard, Version 3.0</reference>
|
||||
<reference id="8" comment="Any code point originally encoded in Unicode Version 5.0">The Unicode Standard, Version 5.0</reference>
|
||||
<reference id="99" comment="Any code point cited is part of the Basic Latin (ASCII) set">C0 Controls and Basic Latin, The Unicode Standard https://unicode.org/charts/PDF/U0000.pdf</reference>
|
||||
<reference id="100">ICANN, Second Level Reference Label Generation Rules for Spanish https://www.icann.org/sites/default/files/packages/lgr/lgr-second-level-spanish-30aug16-en.html (Accessed on 31 August 2018)</reference>
|
||||
<reference id="101">Omniglot, Czech (čeština) https://www.omniglot.com/writing/czech.htm (Accessed on 31 August 2018)</reference>
|
||||
<reference id="102">Omniglot, Icelandic (Íslenska) https://www.omniglot.com/writing/icelandic.htm (Accessed on 31 August 2018)</reference>
|
||||
<reference id="103">Omniglot, Faroese (føroyskt mál) https://www.omniglot.com/writing/faroese.htm (Accessed on 31 August 2018)</reference>
|
||||
<reference id="105">Omniglot, Chuukese (Chuuk) https://www.omniglot.com/writing/chuukese.htm (Accessed on 31 August 2018)</reference>
|
||||
<reference id="106">ScriptSource, Galician written with Latin script https://scriptsource.org/cms/scripts/page.php?item_id=wrSys_detail&key=gl-Latn (Accessed on 1 May 2023)</reference>
|
||||
<reference id="107">Omniglot, Lule Sámi (julevsámegiella) https://www.omniglot.com/writing/lulesami.htm (Accessed on 31 August 2018)</reference>
|
||||
<reference id="108">Wikipedia, Northern Sami https://en.wikipedia.org/wiki/Northern_Sami (Accessed on 4 September 2018)</reference>
|
||||
<reference id="109">Omniglot, Vietnamese (tiếng việt / 㗂越) https://www.omniglot.com/writing/vietnamese.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="110">Omniglot, Romanian (limba română) https://www.omniglot.com/writing/romanian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="113">Omniglot, Skolt Sámi (Sääˊmǩiõll / Nuõrttsää’m) https://www.omniglot.com/writing/skoltsami.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="114">Omniglot, French (français) https://www.omniglot.com/writing/french.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="115">Omniglot, West Frisian (Frysk) https://www.omniglot.com/writing/westfrisian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="116">Omniglot, Friulian (furlan/marilenghe) https://www.omniglot.com/writing/friulian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="117">Summer Institute of Linguistics, Pequeno dicionário: Xavante-Português, Português-Xavante https://www.sil.org/resources/archives/17019 (Accessed on 1 October 2020)</reference>
|
||||
<reference id="119">Omniglot, German (Deutsch) https://www.omniglot.com/writing/german.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="120">Omniglot, Finnish (suomi) https://www.omniglot.com/writing/finnish.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="121">Omniglot, Turkmen (Türkmen dili / Түркмен дили) https://www.omniglot.com/writing/turkmen.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="122">Omniglot, Estonian (eesti keel) https://www.omniglot.com/writing/estonian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="123">Omniglot, Swedish (svenska) https://www.omniglot.com/writing/swedish.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="124">Omniglot, Yapese (Waab) https://www.omniglot.com/writing/yapese.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="125">Omniglot, Dinka (Thuɔŋjäŋ) https://www.omniglot.com/writing/dinka.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="126">Omniglot, Kaqchikel (Kaqchikel Ch’ab’äl) https://www.omniglot.com/writing/kaqchikel.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="127">Omniglot, Bashkir/Bashkort (Башҡорт теле / Başqort tele) https://www.omniglot.com/writing/bashkir.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="128">Omniglot, Alsatian (Ëlsässisch) https://www.omniglot.com/writing/alsatian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="129">Wikipedia, Nuer language https://en.wikipedia.org/wiki/Nuer_language (Accessed on 4 September 2018)</reference>
|
||||
<reference id="130">Omniglot, Italian (italiano) https://www.omniglot.com/writing/italian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="131">Wikipedia, Italian orthography https://en.wikipedia.org/wiki/Italian_orthography (Accessed on 4 September 2018)</reference>
|
||||
<reference id="132">Omniglot, Wolof (Wollof) https://www.omniglot.com/writing/wolof.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="133">Omniglot, Latvian (latviešu valoda) https://www.omniglot.com/writing/latvian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="134">Omniglot, Tongan (Faka-Tonga) https://www.omniglot.com/writing/tongan.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="135">Omniglot, Hawaiian (ʻŌlelo Hawaiʻi) https://www.omniglot.com/writing/hawaiian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="136">Omniglot, Marshallese (kajin m̧ajeļ) https://www.omniglot.com/writing/marshallese.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="137">Omniglot, Polish (polski) https://www.omniglot.com/writing/polish.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="138">Omniglot, Lithuanian (lietuvių kalba) https://www.omniglot.com/writing/lithuanian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="139">Omniglot, Danish (dansk) https://www.omniglot.com/writing/danish.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="140">Omniglot, Chamorro (chamoru) https://www.omniglot.com/writing/chamorro.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="141">Omniglot, Umbundu (Úmbúndú) https://www.omniglot.com/writing/umbundu.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="142">Omniglot, Guaraní (Avañe’ẽ) https://www.omniglot.com/writing/guarani.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="143">Wikipedia, Guarani alphabet https://en.wikipedia.org/wiki/Guarani_alphabet (Accessed on 4 September 2018)</reference>
|
||||
<reference id="144">Omniglot, Nauruan (Ekaiairũ Naoero) https://www.omniglot.com/writing/nauruan.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="145">Omniglot, Khoekhoe (Khoekhoegowab) https://www.omniglot.com/writing/khoekhoe.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="146">Omniglot, Nuer (Naath) https://www.omniglot.com/writing/nuer.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="147">Omniglot, Hausa (Harshen Hausa / هَرْشَن هَوْسَ) https://www.omniglot.com/writing/hausa.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="148">Omniglot, Dagaare https://www.omniglot.com/writing/dagaare.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="149">Omniglot, Fula (Fulfulde, Pulaar, Pular’Fulaare) https://www.omniglot.com/writing/fula.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="150">Omniglot, Croatian (Hrvatski) https://www.omniglot.com/writing/croatian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="151">Omniglot, Serbian (српски / srpski) https://www.omniglot.com/writing/serbian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="152">Wikipedia, Polish language https://en.wikipedia.org/wiki/Polish_language (Accessed on 4 September 2018)</reference>
|
||||
<reference id="153">Omniglot, Slovak (slovenčina) https://www.omniglot.com/writing/slovak.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="154">Evertype Publishing, Lithuanian lietuvių kalba Version 1.1 https://www.evertype.com/alphabets/lithuanian.pdf (Accessed on 4 September 2018)</reference>
|
||||
<reference id="157">Omniglot, Turkish (Türkçe) https://www.omniglot.com/writing/turkish.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="158">Omniglot, Kurdish (Kurdî / کوردی) https://www.omniglot.com/writing/kurdish.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="159">Omniglot, Azerbaijani (آذربايجانجا ديلي / Азәрбајҹан дили / Azərbaycan dili) https://www.omniglot.com/writing/azeri.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="160">Omniglot, Basque (euskara) https://www.omniglot.com/writing/basque.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="161">Wikipedia, Basque language https://en.wikipedia.org/wiki/Basque_language#Writing_system (Accessed on 4 September 2018)</reference>
|
||||
<reference id="163">Omniglot, Maltese (Malti) https://www.omniglot.com/writing/maltese.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="164">Omniglot, Venda (Tshivenḓa / Luvenḓa) https://www.omniglot.com/writing/venda.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="168">Omniglot, Brahui (Bráhuí / براوی) https://www.omniglot.com/writing/brahui.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="169">Wikipedia, Fon language https://en.wikipedia.org/wiki/Fon_language (Accessed on 4 September 2018)</reference>
|
||||
<reference id="170">Omniglot, Ewe (Eʋegbe) https://www.omniglot.com/writing/ewe.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="172">Omniglot, Sorbian (hornjoserbsce/dolnoserbski) https://www.omniglot.com/writing/sorbian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="173">Peace corps, Botswana, An Introduction to Setswana Language https://files.peacecorps.gov/multimedia/audio/languagelessons/botswana/Bw_Setswana_Language_Lessons.pdf (Accessed on 4 September 2018)</reference>
|
||||
<reference id="174">Omniglot, Tswana (Setswana) https://www.omniglot.com/writing/tswana.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="175">Wikipedia, Afrikaans https://en.wikipedia.org/wiki/Afrikaans (Accessed on 4 September 2018)</reference>
|
||||
<reference id="176">Omniglot, Albanian (shqip / gjuha shqipe) https://www.omniglot.com/writing/albanian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="177">Wikipedia, Albanian alphabet https://en.wikipedia.org/wiki/Albanian_alphabet (Accessed on 4 September 2018)</reference>
|
||||
<reference id="179">Wikipedia, Uyghur Latin alphabet https://en.wikipedia.org/wiki/Uyghur_Latin_alphabet (Accessed on 4 September 2018)</reference>
|
||||
<reference id="180">Omniglot, Drehu (Deʼu) https://www.omniglot.com/writing/drehu.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="182">Omniglot, Haitian Creole (Kreyòl ayisyen) https://www.omniglot.com/writing/haitiancreole.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="183">Wikipedia, Haitian Creole https://en.wikipedia.org/wiki/Haitian_Creole#Orthography (Accessed on 4 September 2018)</reference>
|
||||
<reference id="184">Omniglot, Minangkabau (Baso Minangkabau / باسو مينڠكاباو) https://www.omniglot.com/writing/minangkabau.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="185">Omniglot, Palauan (a tekoi er a Belau) https://www.omniglot.com/writing/palauan.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="186">Omniglot, Cubeo (pãmié) https://www.omniglot.com/writing/cubeo.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="187">Editorial Alberto Lleras Camargo, Diccionario Ilustrado Bilingüe cubeo-español español-cubeo https://www.sil.org/system/files/reapdata/10/58/27/10582785843693992331766506069073895620/40337_01.pdf (Accessed on 4 September 2018)</reference>
|
||||
<reference id="188">Omniglot, Inari Saami (Anarâškielâ) https://www.omniglot.com/writing/inarisami.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="189">Omniglot, Compiled by Wolfram Siegel, DAGBANI https://www.omniglot.com/charts/dagbani.pdf (Accessed on 4 September 2018)</reference>
|
||||
<reference id="190">Omniglot, Ewondo https://www.omniglot.com/writing/ewondo.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="191">Omniglot, Luganda (Oluganda) https://www.omniglot.com/writing/ganda.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="192">Omniglot, Adzera https://www.omniglot.com/writing/adzera.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="193">Omniglot, Ga (Gã) https://www.omniglot.com/writing/ga.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="194">Omniglot, Duala (Duálá) https://www.omniglot.com/writing/duala.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="195">Omniglot, Soga (Lusoga) https://www.omniglot.com/writing/soga.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="196">Omniglot, Alur (Lur) https://www.omniglot.com/writing/alur.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="197">Omniglot, Mandinka (Mandi’nka kango / لغة مندنكا) https://www.omniglot.com/writing/mandinka.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="198">Omniglot, Acholi (Lwo) https://www.omniglot.com/writing/acholi.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="199">Omniglot, Bambara (Bamanankan) https://www.omniglot.com/writing/bambara.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="200">Omniglot, Raga (Hano) https://www.omniglot.com/writing/raga.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="201">Omniglot, Tatar (tatarça / татарча / تاتارچا) https://www.omniglot.com/writing/tatar.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="202">Omniglot, Zaza (Zazaki / زازاکی) https://www.omniglot.com/writing/zazaki.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="203">Wikipedia, Turkish alphabet https://en.wikipedia.org/wiki/Turkish_alphabet (Accessed on 4 September 2018)</reference>
|
||||
<reference id="204">School of English, Adam Michiewicz University, Poznań, Poland, Poznań Studies in Contemporary Linguistics 43(1),2007, pp. 169-180, A Demographic Igbo Orthography https://www.degruyter.com/downloadpdf/j/psicl.2007.43.issue-1/v10010-007-0009-0/v10010-007-0009-0.pdf (Accessed on 4 September 2018)</reference>
|
||||
<reference id="205">Omniglot, Igbo (Asụsụ Igbo) https://www.omniglot.com/writing/igbo.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="206">ItalianPod101, Italian Accents and Proper Italian Pronunciation https://www.italianpod101.com/italian-accents (Accessed on 4 September 2018)</reference>
|
||||
<reference id="208">Reverso Dictionary, venerdì translation | Italian-English dictionary https://dictionary.reverso.net/italian-english/venerd%C3%AC (Accessed on 4 September 2018)</reference>
|
||||
<reference id="209">Omniglot, Kikuyu (Gĩkũyũ) https://www.omniglot.com/writing/kikuyu.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="210">Omniglot, Hixkaryána https://www.omniglot.com/writing/hixkaryana.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="211">Omniglot, Maasai (ɔl Maa) https://www.omniglot.com/writing/maasai.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="212">Omniglot, Mossi (Mòoré) https://www.omniglot.com/writing/mossi.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="213">Omniglot, Jenesis. The Bible in Marshallese, 2009., Contributed by Wolfgang Kuhl https://www.omniglot.com/babel/marshallese.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="214">Wikipedia, Cedilla https://en.wikipedia.org/wiki/Cedilla#Marshallese (Accessed on 4 September 2018)</reference>
|
||||
<reference id="215">Wikipedia, Marshallese language https://en.wikipedia.org/wiki/Marshallese_language#Display_issues (Accessed on 4 September 2018)</reference>
|
||||
<reference id="216">Trussel, Marshallese-English Online Dictionary https://www.trussel2.com/MOD/ (Accessed on 4 September 2018)</reference>
|
||||
<reference id="218">Omniglot, Susu (Sosoxi) https://www.omniglot.com/writing/susu.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="219">Omniglot, Zarma (Zarmaciine) https://www.omniglot.com/writing/zarma.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="220">Omniglot, Pitjantjatjara https://www.omniglot.com/writing/pitjantjatjara.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="221">Omniglot, Spanish (español/castellano) https://www.omniglot.com/writing/spanish.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="222">Omniglot, Filipino (wikang Filipino) https://www.omniglot.com/writing/filipino.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="223">Omniglot, Chavacano https://www.omniglot.com/writing/chavacano.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="224">Wikipedia, Ilocano language https://en.wikipedia.org/wiki/Ilocano_language#Modern_alphabet (Accessed on 4 September 2018)</reference>
|
||||
<reference id="225">Omniglot, Quechua (Runasimi) https://www.omniglot.com/writing/quechua.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="226">Wikipedia, Quechua alphabet https://en.wikipedia.org/wiki/Quechua_alphabet (Accessed on 4 September 2018)</reference>
|
||||
<reference id="227">Omniglot, Cape Verdean Creole (Kriolu) https://www.omniglot.com/writing/kriol.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="228">Omniglot, Waray-Waray https://www.omniglot.com/writing/waray.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="229">Omniglot, Lozi (siLozi) https://www.omniglot.com/writing/lozi.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="230">africanlanguages.com, Sesotho sa Leboa (Northern Sotho) https://africanlanguages.com/northern_sotho/ (Accessed on 4 September 2018)</reference>
|
||||
<reference id="232">Wikipedia, Chechen language https://en.wikipedia.org/wiki/Chechen_language (Accessed on 4 September 2018)</reference>
|
||||
<reference id="233">Omniglot, Hungarian (magyar) https://www.omniglot.com/writing/hungarian.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="234">Wikipedia, Hungarian alphabet https://en.wikipedia.org/wiki/Hungarian_alphabet (Accessed on 4 September 2018)</reference>
|
||||
<reference id="236">Omniglot, Lingala https://www.omniglot.com/writing/lingala.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="237">Omniglot, Akan https://www.omniglot.com/writing/akan.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="238">Wikipedia, Mossi language https://en.wikipedia.org/wiki/Mossi_language (Accessed on 4 September 2018)</reference>
|
||||
<reference id="239">SIL-Sudan, OCCASIONAL PAPERS in the study of SUDANESE LANGUAGES No. 9 (p. 75) https://www.sil.org/system/files/reapdata/10/06/46/100646256099282892829790816212446104791/OPSL_9.pdf (Accessed on 4 September 2018)</reference>
|
||||
<reference id="240">Omniglot, Kanuri https://www.omniglot.com/writing/kanuri.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="241">Omniglot, Bugis (Basa Ugi ) https://www.omniglot.com/writing/bugis.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="242">Omniglot, Mizo (Mizo ṭawng) https://www.omniglot.com/writing/mizo.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="243">Omniglot, Miskito (Mískitu) https://www.omniglot.com/writing/miskito.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="245">Wikipedia, Papiamento https://en.wikipedia.org/wiki/Papiamento (Accessed on 4 September 2018)</reference>
|
||||
<reference id="246">Omniglot, Papiamento (Papiamentu) https://www.omniglot.com/writing/papiamento.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="247">Omniglot, Chichewa (Chicheŵa) https://www.omniglot.com/writing/chichewa.php (Accessed on 4 September 2018)</reference>
|
||||
<reference id="248">Native Languages of the Americas website, Vocabulary in Native American Languages: Mam Words https://www.native-languages.org/mam_words.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="249">Omniglot, Mam (Qyol Mam) https://www.omniglot.com/writing/mam.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="250">Wikipedia, Pulaar language https://en.wikipedia.org/wiki/Pulaar_language (Accessed on 4 September 2018)</reference>
|
||||
<reference id="251">Wikipedia, Fula language https://en.wikipedia.org/wiki/Fula_language#Writing_systems (Accessed on 4 September 2018)</reference>
|
||||
<reference id="252">Wikipedia, Polish alphabet https://en.wikipedia.org/wiki/Polish_alphabet (Accessed on 4 September 2018)</reference>
|
||||
<reference id="253">Wikipedia, French orthography https://en.wikipedia.org/wiki/French_orthography (Accessed on 4 September 2018)</reference>
|
||||
<reference id="254">Omniglot, Yoruba (Èdè Yorùbá) https://www.omniglot.com/writing/yoruba.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="255">Omniglot, Esperanto https://www.omniglot.com/writing/esperanto.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="256">Omniglot, Welsh (Cymraeg) https://www.omniglot.com/writing/welsh.htm (Accessed on 4 September 2018)</reference>
|
||||
<reference id="257">Wikipedia, List of Latin-script letters https://en.wikipedia.org/wiki/List_of_Latin-script_letters (Accessed on 4 September 2018)</reference>
|
||||
<reference id="258">Omniglot, Montenegrin https://www.omniglot.com/writing/montenegrin.htm (Accessed on 20 March 2019)</reference>
|
||||
<reference id="275">Omniglot, Shavante https://www.omniglot.com/writing/shavante.php (Accessed on 24 September 2020)</reference>
|
||||
<reference id="276">Wikipedia, Malagasy Language https://en.wikipedia.org/wiki/Malagasy_language (Accessed on 24 September 2020)</reference>
|
||||
<reference id="277">Wikipedia, Serer language, https://en.wikipedia.org/wiki/Serer_language, accessed on 6 April 2021</reference>
|
||||
<reference id="278">Wikipedia, Kpelle language, https://en.wikipedia.org/wiki/Kpelle_language, accessed on 6 April 2021</reference>
|
||||
<reference id="279">Wikipedia, Catalan Orthography, https://en.wikipedia.org/wiki/Catalan_orthography, accessed on 28 November 2022</reference>
|
||||
<reference id="280">Fundacio PuntCAT registry (.CAT), IDN table for CAT_ca Version 1.0, 12 February 2006, https://www.iana.org/domains/idn-tables/tables/cat_ca_1.0.html</reference>
|
||||
<reference id="281">Fundacio PuntCAT registry (.CAT), Rules of the .cat domain, https://domini.cat/en/rules-of-the-cat-domain/</reference>
|
||||
<reference id="320">RFC 5891, Internationalized Domain Names in Applications (IDNA): Protocol https://tools.ietf.org/html/rfc5891</reference>
|
||||
</references>
|
||||
</meta>
|
||||
<data>
|
||||
<char cp="002D" not-when="hyphen-minus-disallowed" tag="sc:Zyyy" ref="0" comment="HYPHEN-MINUS; ⍟" />
|
||||
<char cp="0030" tag="Common-digit sc:Zyyy" ref="0" comment="DIGIT ZERO; ⍟" />
|
||||
<char cp="0031" tag="Common-digit sc:Zyyy" ref="0" comment="DIGIT ONE; ⍟" />
|
||||
<char cp="0032" tag="Common-digit sc:Zyyy" ref="0" comment="DIGIT TWO; ⍟" />
|
||||
<char cp="0033" tag="Common-digit sc:Zyyy" ref="0" comment="DIGIT THREE; ⍟" />
|
||||
<char cp="0034" tag="Common-digit sc:Zyyy" ref="0" comment="DIGIT FOUR; ⍟" />
|
||||
<char cp="0035" tag="Common-digit sc:Zyyy" ref="0" comment="DIGIT FIVE; ⍟" />
|
||||
<char cp="0036" tag="Common-digit sc:Zyyy" ref="0" comment="DIGIT SIX; ⍟" />
|
||||
<char cp="0037" tag="Common-digit sc:Zyyy" ref="0" comment="DIGIT SEVEN; ⍟" />
|
||||
<char cp="0038" tag="Common-digit sc:Zyyy" ref="0" comment="DIGIT EIGHT; ⍟" />
|
||||
<char cp="0039" tag="Common-digit sc:Zyyy" ref="0" comment="DIGIT NINE; ⍟" />
|
||||
<char cp="0061" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0062" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0063" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0064" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0065" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0066" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0067" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0068" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0069" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="006A" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="006B" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="006C" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="006D" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="006E" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="006F" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0070" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0071" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0072" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0073" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0074" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0075" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0076" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0077" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0078" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="0079" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="007A" tag="sc:Latn" ref="0 99" comment="Basic Latin" />
|
||||
<char cp="00E0" tag="sc:Latn" ref="0 106 114 130 131 132" comment="Italian (1), French (1), Galician (2), Wolof (4)" />
|
||||
<char cp="00E2" tag="sc:Latn" ref="0 106 109 110 113 114 115 116 117 275" comment="Vietnamese (1), Romanian (1), Skolt Sami (2), French (1), Galician (2), West Frisian (1), Friulian (4), Xavante (4)" />
|
||||
<char cp="00E3" tag="sc:Latn" ref="0 141 142 143 144 145" comment="Umbundu (3), Guarani (1), Nauruan (3), Khoekhoe (4)" />
|
||||
<char cp="00E4" tag="sc:Latn" ref="0 107 119 120 121 122 123 124 125 126 127 128 129" comment="German (1), Finnish (1), Turkmen (1), Estonian (1), Swedish (1), Lule Sami (2), Yapese (2), Dinka (4), Kaqchikel (4), Bashkir (4), Alsatian (5), Nuer (4)" />
|
||||
<char cp="00E5" tag="sc:Latn" ref="0 107 120 123 139 140" comment="Danish (1), Finnish (1), Chamorro (1), Swedish (1), Lule Sami (2)" />
|
||||
<char cp="00E6" tag="sc:Latn" ref="0 102 103 139" comment="Danish (1), Icelandic (1), Faroese (2)" />
|
||||
<char cp="00E7" tag="sc:Latn" ref="0 106 114 116 121 127 157 158 159 160 161" comment="Turkish (1), Turkmen (1), Kurdish (2), French (1), Azerbaijani (1), Basque (1), Galician (2), Friulian (4), Bashkir (4)" />
|
||||
<char cp="00E8" tag="sc:Latn" ref="0 114 130 175 182 183" comment="French (1), Italian (1), Afrikaans (1), Haitian Creole (1), French (1)" />
|
||||
<char cp="00E9" tag="sc:Latn" ref="0 100 101 102 105 106 114 115 117 130 132 275" comment="French (1), Italian (1), Spanish (1), Czech (1), Icelandic (1), Chuukese (2), Galician (2), Wolof (4), Xavante (4), West Frisian (2)" />
|
||||
<char cp="00EA" tag="sc:Latn" ref="0 109 114 115 116 158 173 174 175" comment="French (1), Tswana (1), Afrikaans (1), Vietnamese (1), Kurdish (2), West Frisian (2), Friulian (4)" />
|
||||
<char cp="00EB" tag="sc:Latn" ref="0 114 115 124 126 129 132 175 176 177 179 180" comment="Afrikaans (1), Albanian (1), French (1), Uyghur (2), Yapese (2), Wolof (4), Drehu (4), Kaqchikel (4), West Frisian (2), Nuer (4)" />
|
||||
<char cp="00EC" tag="sc:Latn" ref="0 130 206 208" comment="Italian (1)" />
|
||||
<char cp="00EE" tag="sc:Latn" ref="0 110 114 116 158 175" comment="Afrikaans (1), Romanian (1), Kurdish (2), French (1), Friulian (4)" />
|
||||
<char cp="00F0" tag="sc:Latn" ref="0 102 103" comment="Faroese (2), Icelandic (1)" />
|
||||
<char cp="00F2" tag="sc:Latn" ref="0 130 182 183" comment="Italian (1), Haitian Creole (1)" />
|
||||
<char cp="00F4" tag="sc:Latn" ref="0 106 109 114 115 116 117 173 174 175 230 275" comment="Tswana (1), Afrikaans (1), Vietnamese (1), French (1), Northern Sotho (1), West Frisian (2), Galician (2), Friulian (4), Xavante (4)" />
|
||||
<char cp="00F5" tag="sc:Latn" ref="0 113 117 122 141 142 143 144 145 275" comment="Estonian (1), Skolt Sami (2), Umbundu (3), Guarani (1), Nauruan (3), Xavante (4), Khoekhoe (4)" />
|
||||
<char cp="00F6" tag="sc:Latn" ref="0 115 119 120 123 124 125 126 127 129 157 175 179 180 232" comment="German (1), Finnish (1), Afrikaans (1), Turkish (1), Swedish (1), Uygur (2), Yapese (2), Drehu (4), Kaqchikel (4), Dinka (4), Bashkir (4), Chechen (2), 1992 Version, West Frisian (2), Nuer (4)" />
|
||||
<char cp="00F8" tag="sc:Latn" ref="0 103 139" comment="Danish (1), Faroese (2)" />
|
||||
<char cp="00F9" tag="sc:Latn" ref="0 114 130 206 245 246 253" comment="Italian (1), French (1), Papiamento (1)" />
|
||||
<char cp="00FB" tag="sc:Latn" ref="0 114 115 116 158 175 202 243" comment="Afrikaans (1), Kurdish (2), French (1), Miskito (2), West Frisian (2), Friulian (4), Zazaki (4)" />
|
||||
<char cp="00FD" tag="sc:Latn" ref="0 101 102 103 121 142 143" comment="Turkmen (1), Czech (1), Icelandic (1), Faroese (2), Guarani (1)" />
|
||||
<char cp="00FE" tag="sc:Latn" ref="0 102" comment="Icelandic (1)" />
|
||||
<char cp="00FF" tag="sc:Latn" ref="0 114 253 257" comment="French (1)" />
|
||||
<char cp="0103" tag="sc:Latn" ref="0 109 110" comment="Vietnamese (1), Romanian (1)" />
|
||||
<char cp="0105" tag="sc:Latn" ref="0 137 138" comment="Polish (1), Lithuanian (1)" />
|
||||
<char cp="0107" tag="sc:Latn" ref="0 150 151 152" comment="Croatian (1), Serbian (1), Polish (1)" />
|
||||
<char cp="0109" tag="sc:Latn" ref="0 255" comment="Esperanto (3)" />
|
||||
<char cp="010D" tag="sc:Latn" ref="0 108 133 150 151 153 154" comment="Croatian (1), Serbian (1), Latvian (1), Slovak (1), Northern Sami (2), Lithuanian (1)" />
|
||||
<char cp="010F" tag="sc:Latn" ref="0 101 153" comment="Czech (1), Slovak (1)" />
|
||||
<char cp="0111" tag="sc:Latn" ref="0 108 109 150 151 168" comment="Croatian (1), Serbian (1), Vietnamese (1), Northern Sami (2), Brahui (5)" />
|
||||
<char cp="0113" tag="sc:Latn" ref="0 133 134 135 184" comment="Latvian (1), Hawaiian (2), Tongan (1), Minangkabau (5)" />
|
||||
<char cp="0117" tag="sc:Latn" ref="0 138 154" comment="Lithuanian (1)" />
|
||||
<char cp="0119" tag="sc:Latn" ref="0 138 152 154 185" comment="Polish (1), Palauan (2), Lithuanian (1)" />
|
||||
<char cp="011B" tag="sc:Latn" ref="0 101 172" comment="Czech (1), Sorbian (4)" />
|
||||
<char cp="011D" tag="sc:Latn" ref="0 255" comment="Esperanto (3)" />
|
||||
<char cp="011F" tag="sc:Latn" ref="0 127 157 159 201 202" comment="Turkish (1), Tatar (2), Azeri (1), Bashkir (4), Zaza (5)" />
|
||||
<char cp="0123" tag="sc:Latn" ref="0 133 168" comment="Latvian (1), Brahui (5)" />
|
||||
<char cp="0125" tag="sc:Latn" ref="0 255" comment="Esperanto (3)" />
|
||||
<char cp="0127" tag="sc:Latn" ref="0 163" comment="Maltese (1)" />
|
||||
<char cp="012B" tag="sc:Latn" ref="0 133 134 135 138" comment="Latvian (1), Lithuanian (1), Hawaiian (2), Tongan (1)" />
|
||||
<char cp="012F" tag="sc:Latn" ref="0 154" comment="Lithuanian (1)" />
|
||||
<char cp="0135" tag="sc:Latn" ref="0 255" comment="Esperanto (3)" />
|
||||
<char cp="0137" tag="sc:Latn" ref="0 133" comment="Latvian (1)" />
|
||||
<char cp="013A" tag="sc:Latn" ref="0 153" comment="Slovak (1)" />
|
||||
<char cp="013C" tag="sc:Latn" ref="0 133 168 213 214" comment="Latvian (1), Marshallese (1), Brahui (5)" />
|
||||
<char cp="013E" tag="sc:Latn" ref="0 153" comment="Slovak (1)" />
|
||||
<char cp="0142" tag="sc:Latn" ref="0 152" comment="Polish (1)" />
|
||||
<char cp="0146" tag="sc:Latn" ref="0 133 136" comment="Latvian (1), Marshallese (1)" />
|
||||
<char cp="0148" tag="sc:Latn" ref="0 101 121 153" comment="Turkmen (1), Czech (1), Slovak (1)" />
|
||||
<char cp="0151" tag="sc:Latn" ref="0 233 234" comment="Hungarian (1)" />
|
||||
<char cp="0153" tag="sc:Latn" ref="0 114 253" comment="French (1)" />
|
||||
<char cp="0155" tag="sc:Latn" ref="0 153 168" comment="Slovak (1), Brahui (5)" />
|
||||
<char cp="0159" tag="sc:Latn" ref="0 101 172" comment="Czech (1), Sorbian (4)" />
|
||||
<char cp="015B" tag="sc:Latn" ref="0 152 258" comment="Polish (1), Montenegrin (1)" />
|
||||
<char cp="015D" tag="sc:Latn" ref="0 255" comment="Esperanto (3)" />
|
||||
<char cp="015F" tag="sc:Latn" ref="0 121 127 157 158 159 168 201 202" comment="Turkish (1), Turkmen (1), Kurdish (2), Tatar (2), Azeri (1), Bashkir (4), Brahui (5), Zaza (5)" />
|
||||
<char cp="0161" tag="sc:Latn" ref="0 108 133 150 151 154 174 230" comment="Tswana (1), Croatian (1), Serbian (1), Latvian (1), Northern Sotho (1), Northern Sami (2), Lithuanian (1)" />
|
||||
<char cp="0165" tag="sc:Latn" ref="0 101 153" comment="Czech (1), Slovak (1)" />
|
||||
<char cp="0167" tag="sc:Latn" ref="0 108 168" comment="Northern Sami (2), Brahui (5)" />
|
||||
<char cp="016B" tag="sc:Latn" ref="0 133 134 135 136 138 154" comment="Latvian (1), Hawaiian (2), Lithuanian (1), Marshallese (1), Tongan (1)" />
|
||||
<char cp="016D" tag="sc:Latn" ref="0 255" comment="Esperanto (3)" />
|
||||
<char cp="016F" tag="sc:Latn" ref="0 101" comment="Czech (1)" />
|
||||
<char cp="0171" tag="sc:Latn" ref="0 233 234" comment="Hungarian (1)" />
|
||||
<char cp="0173" tag="sc:Latn" ref="0 138 154" comment="Lithuanian (1)" />
|
||||
<char cp="0175" tag="sc:Latn" ref="0 247 256" comment="Chichewa (3), Welsh (2)" />
|
||||
<char cp="0177" tag="sc:Latn" ref="0 256" comment="Welsh (2)" />
|
||||
<char cp="017A" tag="sc:Latn" ref="0 152 168 172 252 258" comment="Polish (1), Brahui (5), Sorbian (4), Montenegrin (1)" />
|
||||
<char cp="017E" tag="sc:Latn" ref="0 108 121 133 150 151 153 154 232" comment="Lithuanian (1), Croatian (1), Serbian (1), Turkmen (1), Latvian (1), Slovak (1), Northern Sami (2), Chechen (2) 1925 Version" />
|
||||
<char cp="0188" tag="sc:Latn" ref="0 277" comment="Serer (5)" />
|
||||
<char cp="0199" tag="sc:Latn" ref="0 147" comment="Hausa (2)" />
|
||||
<char cp="01A1" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="01A5" tag="sc:Latn" ref="0 277" comment="Serer (5)" />
|
||||
<char cp="01AD" tag="sc:Latn" ref="0 277" comment="Serer (5)" />
|
||||
<char cp="01B0" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="01B4" tag="sc:Latn" ref="0 148 149 251" comment="Dagaare - Burkina Faso (4), Fula (3)" />
|
||||
<char cp="01E9" tag="sc:Latn" ref="0 113" comment="Skolt Sami (2)" />
|
||||
<char cp="01EF" tag="sc:Latn" ref="0 113" comment="Skolt Sami (2)" />
|
||||
<char cp="0219" tag="sc:Latn" ref="3 110" comment="Romanian (1)" />
|
||||
<char cp="021B" tag="sc:Latn" ref="3 110" comment="Romanian (1)" />
|
||||
<char cp="024D" tag="sc:Latn" ref="8 240" comment="Kanuri (3)" />
|
||||
<char cp="0253" tag="sc:Latn" ref="0 147 148 250" comment="Hausa (2), Dagaare - Burkina Faso (4), Pulaar (3)" />
|
||||
<char cp="0254" tag="sc:Latn" ref="0 129 146 148 169 170 189 190 193 194 236 237" comment="Dagaare - Burkina Faso (4), Dagbani (Dagomba) (4), Lingala (2), Akan (3), Ewondo (3), Fon (3), Nuer (4), Ga (4), Duala (3), EWE (3), Nuer (4)" />
|
||||
<char cp="0256" tag="sc:Latn" ref="0 169 170" comment="Fon (3), Ewe (3)" />
|
||||
<char cp="0257" tag="sc:Latn" ref="0 147 149 250" comment="Hausa (2), Fula (3)" />
|
||||
<char cp="0259" tag="sc:Latn" ref="0 159 170 190 241" comment="Azeri, Azerbaijani (1), Ewondo (3), Ewe (3), Bugis (3)" />
|
||||
<char cp="025B" tag="sc:Latn" ref="0 129 148 169 170 189 190 193 194 199 212 236 237 238" comment="Dagaare - Burkina Faso (4), Lingala (2), Akan (3), Ewondo (3), Dagbani (Dagomba), (4), Fon (3), Mossi (3), Ga (4), Ewe (3), Duala (3), Bambara (4), Nuer (4)" />
|
||||
<char cp="0260" tag="sc:Latn" ref="0 278" comment="Kpelle (5)" />
|
||||
<char cp="0268" tag="sc:Latn" ref="0 186 189 210 211" comment="Cubeo (3), Dagbani (Dagomba) (4), HIxkaryána (4), Maasai (5)" />
|
||||
<char cp="0272" tag="sc:Latn" ref="0 199 218 219" comment="Susu (4), Zarma (4), Bambara (4)" />
|
||||
<char cp="0289" tag="sc:Latn" ref="0 186 187 211" comment="Cubeo (3), Maasai (5)" />
|
||||
<char cp="0292" tag="sc:Latn" ref="0 113 189" comment="Skolt Sami (2), Dagbani (Dagomba) (4)" />
|
||||
<char cp="1E13" tag="sc:Latn" ref="0 164 257" comment="Venda (1)" />
|
||||
<char cp="1E3D" tag="sc:Latn" ref="0 164 257" comment="Venda (1)" />
|
||||
<char cp="1E49" tag="sc:Latn" ref="0 220" comment="Pitjantjatjara (4)" />
|
||||
<char cp="1E4B" tag="sc:Latn" ref="0 164 257" comment="Venda (1)" />
|
||||
<char cp="1E63" tag="sc:Latn" ref="0 254" comment="Yoruba (2)" />
|
||||
<char cp="1E6D" tag="sc:Latn" ref="0 242" comment="Mizo (4)" />
|
||||
<char cp="1E71" tag="sc:Latn" ref="0 164 257" comment="Venda (1)" />
|
||||
<char cp="1E8D" tag="sc:Latn" ref="0 248 249" comment="Mam (4)" />
|
||||
<char cp="1EA1" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EA5" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EA7" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EA9" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EAB" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EAD" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EAF" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EB1" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EB3" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EB5" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EB7" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EB9" tag="sc:Latn" ref="0 254" comment="Yoruba (2)" />
|
||||
<char cp="1EBB" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EBF" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EC1" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EC3" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EC5" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EC7" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1ECB" tag="sc:Latn" ref="0 205" comment="Igbo (2)" />
|
||||
<char cp="1ECD" tag="sc:Latn" ref="0 136 204 205 215 216 254" comment="Igbo (2), Yoruba (2), Marshallese (1)" />
|
||||
<char cp="1ED1" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1ED3" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1ED5" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1ED7" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1ED9" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EDB" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EDD" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EDF" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EE1" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EE3" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EE5" tag="sc:Latn" ref="0 109 204 205" comment="Vietnamese (1), Igbo (2)" />
|
||||
<char cp="1EE9" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EEB" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EED" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EEF" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EF1" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EF5" tag="sc:Latn" ref="0 109" comment="Vietnamese (1)" />
|
||||
<char cp="1EF9" tag="sc:Latn" ref="0 109 142" comment="Vietnamese (1), Guarani (1)" />
|
||||
</data>
|
||||
<!--Rules section goes here-->
|
||||
<rules>
|
||||
<!--Character class definitions go here-->
|
||||
<!--Whole label evaluation and context rules go here-->
|
||||
<rule name="leading-combining-mark" ref="320" comment="RFC 5891 restrictions on placement of combining marks ⍟">
|
||||
<start />
|
||||
<union>
|
||||
<class property="gc:Mn" />
|
||||
<class property="gc:Mc" />
|
||||
</union>
|
||||
</rule>
|
||||
<rule name="hyphen-minus-disallowed" ref="320" comment="RFC 5891 restrictions on placement of U+002D HYPHEN-MINUS ⍟">
|
||||
<choice>
|
||||
<rule comment="no leading hyphen">
|
||||
<look-behind>
|
||||
<start />
|
||||
</look-behind>
|
||||
<anchor />
|
||||
</rule>
|
||||
<rule comment="no trailing hyphen">
|
||||
<anchor />
|
||||
<look-ahead>
|
||||
<end />
|
||||
</look-ahead>
|
||||
</rule>
|
||||
<rule comment="no consecutive hyphens in third and fourth">
|
||||
<look-behind>
|
||||
<start />
|
||||
<any />
|
||||
<any />
|
||||
<char cp="002D" comment="hyphen-minus" />
|
||||
</look-behind>
|
||||
<anchor />
|
||||
</rule>
|
||||
</choice>
|
||||
</rule>
|
||||
<!--Action elements go here - order defines precedence-->
|
||||
<action disp="invalid" match="leading-combining-mark" comment="labels with leading combining marks are invalid ⍟" />
|
||||
<action disp="invalid" any-variant="out-of-repertoire-var" comment="any variant label with a code point out of repertoire is invalid ⍟" />
|
||||
<action disp="invalid" match="dot-L-dot" comment="labels with one L sharing two middle dots are invalid" />
|
||||
<action disp="blocked" any-variant="blocked" comment="any variant label containing blocked variants is blocked ⍟" />
|
||||
<action disp="allocatable" all-variants="allocatable" comment="variant labels with all variants allocatable are allocatable ⍟" />
|
||||
<action disp="allocatable" all-variants="fallback" comment="any label with all variants of type fallback is allocatable ⍟" />
|
||||
<action disp="blocked" any-variant="fallback" comment="any variant label with a mix of variant forms is blocked ⍟" />
|
||||
<action disp="valid" all-variants="r-original" comment="any remaining label containing only original code points is valid ⍟" />
|
||||
<action disp="valid" comment="catch all (default action) ⍟" />
|
||||
</rules>
|
||||
</lgr>
|
||||
@@ -17,10 +17,7 @@ package google.registry.loadtest;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.Lists.partition;
|
||||
import static google.registry.security.XsrfTokenManager.X_CSRF_TOKEN;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -34,7 +31,7 @@ import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.util.Clock;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.Instant;
|
||||
@@ -67,11 +64,9 @@ public class LoadTestAction implements Runnable {
|
||||
private static final int NUM_QUEUES = 10;
|
||||
private static final int MAX_TASKS_PER_LOAD = 100;
|
||||
private static final int ARBITRARY_VALID_HOST_LENGTH = 40;
|
||||
private static final int MAX_CONTACT_LENGTH = 13;
|
||||
private static final int MAX_DOMAIN_LABEL_LENGTH = 63;
|
||||
|
||||
private static final String EXISTING_DOMAIN = "testdomain";
|
||||
private static final String EXISTING_CONTACT = "contact";
|
||||
private static final String EXISTING_HOST = "ns1";
|
||||
|
||||
private static final Random random = new Random();
|
||||
@@ -85,8 +80,8 @@ public class LoadTestAction implements Runnable {
|
||||
|
||||
/**
|
||||
* The number of seconds to delay the execution of the first load testing tasks by. Preparatory
|
||||
* work of creating independent contacts and hosts that will be used for later domain creation
|
||||
* testing occurs during this period, so make sure that it is long enough.
|
||||
* work of creating independent hosts that will be used for later domain creation testing occurs
|
||||
* during this period, so make sure that it is long enough.
|
||||
*/
|
||||
@Inject
|
||||
@Parameter("delaySeconds")
|
||||
@@ -120,21 +115,6 @@ public class LoadTestAction implements Runnable {
|
||||
@Parameter("domainChecks")
|
||||
int domainChecksPerSecond;
|
||||
|
||||
/** The number of successful contact creates to enqueue per second over the length of the test. */
|
||||
@Inject
|
||||
@Parameter("successfulContactCreates")
|
||||
int successfulContactCreatesPerSecond;
|
||||
|
||||
/** The number of failed contact creates to enqueue per second over the length of the test. */
|
||||
@Inject
|
||||
@Parameter("failedContactCreates")
|
||||
int failedContactCreatesPerSecond;
|
||||
|
||||
/** The number of successful contact infos to enqueue per second over the length of the test. */
|
||||
@Inject
|
||||
@Parameter("contactInfos")
|
||||
int contactInfosPerSecond;
|
||||
|
||||
/** The number of successful host creates to enqueue per second over the length of the test. */
|
||||
@Inject
|
||||
@Parameter("successfulHostCreates")
|
||||
@@ -152,9 +132,8 @@ public class LoadTestAction implements Runnable {
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
private final String xmlContactCreateTmpl;
|
||||
private final String xmlContactCreateFail;
|
||||
private final String xmlContactInfo;
|
||||
@Inject Clock clock;
|
||||
|
||||
private final String xmlDomainCheck;
|
||||
private final String xmlDomainCreateTmpl;
|
||||
private final String xmlDomainCreateFail;
|
||||
@@ -163,53 +142,35 @@ public class LoadTestAction implements Runnable {
|
||||
private final String xmlHostCreateFail;
|
||||
private final String xmlHostInfo;
|
||||
|
||||
/**
|
||||
* The XSRF token to be used for making requests to the epptool endpoint.
|
||||
*
|
||||
* <p>Note that the email address is set to empty, because the logged-in user hitting this
|
||||
* endpoint will not be the same as when the tasks themselves fire and hit the epptool endpoint.
|
||||
*/
|
||||
private final String xsrfToken;
|
||||
|
||||
@Inject
|
||||
LoadTestAction(@Parameter("tld") String tld, XsrfTokenManager xsrfTokenManager) {
|
||||
xmlContactCreateTmpl = loadXml("contact_create");
|
||||
xmlContactCreateFail = xmlContactCreateTmpl.replace("%contact%", EXISTING_CONTACT);
|
||||
xmlContactInfo = loadXml("contact_info").replace("%contact%", EXISTING_CONTACT);
|
||||
LoadTestAction(@Parameter("tld") String tld) {
|
||||
xmlDomainCheck =
|
||||
loadXml("domain_check").replace("%tld%", tld).replace("%domain%", EXISTING_DOMAIN);
|
||||
xmlDomainCreateTmpl = loadXml("domain_create").replace("%tld%", tld);
|
||||
xmlDomainCreateFail =
|
||||
xmlDomainCreateTmpl
|
||||
.replace("%domain%", EXISTING_DOMAIN)
|
||||
.replace("%contact%", EXISTING_CONTACT)
|
||||
.replace("%host%", EXISTING_HOST);
|
||||
xmlDomainInfo =
|
||||
loadXml("domain_info").replace("%tld%", tld).replace("%domain%", EXISTING_DOMAIN);
|
||||
xmlHostCreateTmpl = loadXml("host_create");
|
||||
xmlHostCreateFail = xmlHostCreateTmpl.replace("%host%", EXISTING_HOST);
|
||||
xmlHostInfo = loadXml("host_info").replace("%host%", EXISTING_HOST);
|
||||
xsrfToken = xsrfTokenManager.generateToken("");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
validateAndLogRequest();
|
||||
DateTime initialStartSecond = DateTime.now(UTC).plusSeconds(delaySeconds);
|
||||
DateTime initialStartSecond = clock.nowUtc().plusSeconds(delaySeconds);
|
||||
ImmutableList.Builder<String> preTaskXmls = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<String> contactNamesBuilder = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<String> hostPrefixesBuilder = new ImmutableList.Builder<>();
|
||||
for (int i = 0; i < successfulDomainCreatesPerSecond; i++) {
|
||||
String contactName = getRandomLabel(MAX_CONTACT_LENGTH);
|
||||
String hostPrefix = getRandomLabel(ARBITRARY_VALID_HOST_LENGTH);
|
||||
contactNamesBuilder.add(contactName);
|
||||
hostPrefixesBuilder.add(hostPrefix);
|
||||
preTaskXmls.add(
|
||||
xmlContactCreateTmpl.replace("%contact%", contactName),
|
||||
xmlHostCreateTmpl.replace("%host%", hostPrefix));
|
||||
}
|
||||
enqueue(createTasks(preTaskXmls.build(), DateTime.now(UTC)));
|
||||
ImmutableList<String> contactNames = contactNamesBuilder.build();
|
||||
enqueue(createTasks(preTaskXmls.build(), clock.nowUtc()));
|
||||
ImmutableList<String> hostPrefixes = hostPrefixesBuilder.build();
|
||||
|
||||
ImmutableList.Builder<Task> tasks = new ImmutableList.Builder<>();
|
||||
@@ -217,30 +178,17 @@ public class LoadTestAction implements Runnable {
|
||||
DateTime startSecond = initialStartSecond.plusSeconds(offsetSeconds);
|
||||
// The first "failed" creates might actually succeed if the object doesn't already exist, but
|
||||
// that shouldn't affect the load numbers.
|
||||
tasks.addAll(
|
||||
createTasks(
|
||||
createNumCopies(xmlContactCreateFail, failedContactCreatesPerSecond), startSecond));
|
||||
tasks.addAll(
|
||||
createTasks(createNumCopies(xmlHostCreateFail, failedHostCreatesPerSecond), startSecond));
|
||||
tasks.addAll(
|
||||
createTasks(
|
||||
createNumCopies(xmlDomainCreateFail, failedDomainCreatesPerSecond), startSecond));
|
||||
// We can do infos on the known existing objects.
|
||||
tasks.addAll(
|
||||
createTasks(createNumCopies(xmlContactInfo, contactInfosPerSecond), startSecond));
|
||||
tasks.addAll(createTasks(createNumCopies(xmlHostInfo, hostInfosPerSecond), startSecond));
|
||||
tasks.addAll(createTasks(createNumCopies(xmlDomainInfo, domainInfosPerSecond), startSecond));
|
||||
// The domain check template uses "example.TLD" which won't exist, and one existing domain.
|
||||
tasks.addAll(
|
||||
createTasks(createNumCopies(xmlDomainCheck, domainChecksPerSecond), startSecond));
|
||||
// Do successful creates on random names
|
||||
tasks.addAll(
|
||||
createTasks(
|
||||
createNumCopies(xmlContactCreateTmpl, successfulContactCreatesPerSecond)
|
||||
.stream()
|
||||
.map(randomNameReplacer("%contact%", MAX_CONTACT_LENGTH))
|
||||
.collect(toImmutableList()),
|
||||
startSecond));
|
||||
tasks.addAll(
|
||||
createTasks(
|
||||
createNumCopies(xmlHostCreateTmpl, successfulHostCreatesPerSecond)
|
||||
@@ -253,7 +201,6 @@ public class LoadTestAction implements Runnable {
|
||||
createNumCopies(xmlDomainCreateTmpl, successfulDomainCreatesPerSecond)
|
||||
.stream()
|
||||
.map(randomNameReplacer("%domain%", MAX_DOMAIN_LABEL_LENGTH))
|
||||
.map(listNameReplacer("%contact%", contactNames))
|
||||
.map(listNameReplacer("%host%", hostPrefixes))
|
||||
.collect(toImmutableList()),
|
||||
startSecond));
|
||||
@@ -272,9 +219,6 @@ public class LoadTestAction implements Runnable {
|
||||
|| failedDomainCreatesPerSecond > 0
|
||||
|| domainInfosPerSecond > 0
|
||||
|| domainChecksPerSecond > 0
|
||||
|| successfulContactCreatesPerSecond > 0
|
||||
|| failedContactCreatesPerSecond > 0
|
||||
|| contactInfosPerSecond > 0
|
||||
|| successfulHostCreatesPerSecond > 0
|
||||
|| failedHostCreatesPerSecond > 0
|
||||
|| hostInfosPerSecond > 0,
|
||||
@@ -282,8 +226,7 @@ public class LoadTestAction implements Runnable {
|
||||
logger.atInfo().log(
|
||||
"Running load test with the following params. registrarId: %s, delaySeconds: %d, "
|
||||
+ "runSeconds: %d, successful|failed domain creates/s: %d|%d, domain infos/s: %d, "
|
||||
+ "domain checks/s: %d, successful|failed contact creates/s: %d|%d, "
|
||||
+ "contact infos/s: %d, successful|failed host creates/s: %d|%d, host infos/s: %d.",
|
||||
+ "domain checks/s: %d, successful|failed host creates/s: %d|%d, host infos/s: %d.",
|
||||
registrarId,
|
||||
delaySeconds,
|
||||
runSeconds,
|
||||
@@ -291,9 +234,6 @@ public class LoadTestAction implements Runnable {
|
||||
failedDomainCreatesPerSecond,
|
||||
domainInfosPerSecond,
|
||||
domainChecksPerSecond,
|
||||
successfulContactCreatesPerSecond,
|
||||
failedContactCreatesPerSecond,
|
||||
contactInfosPerSecond,
|
||||
successfulHostCreatesPerSecond,
|
||||
failedHostCreatesPerSecond,
|
||||
hostInfosPerSecond);
|
||||
@@ -303,10 +243,10 @@ public class LoadTestAction implements Runnable {
|
||||
return readResourceUtf8(LoadTestAction.class, String.format("templates/%s.xml", name));
|
||||
}
|
||||
|
||||
private List<String> createNumCopies(String xml, int numCopies) {
|
||||
private ImmutableList<String> createNumCopies(String xml, int numCopies) {
|
||||
String[] xmls = new String[numCopies];
|
||||
Arrays.fill(xmls, xml);
|
||||
return asList(xmls);
|
||||
return ImmutableList.copyOf(xmls);
|
||||
}
|
||||
|
||||
private Function<String, String> listNameReplacer(final String toReplace, List<String> choices) {
|
||||
@@ -326,35 +266,27 @@ public class LoadTestAction implements Runnable {
|
||||
return name.toString();
|
||||
}
|
||||
|
||||
private List<Task> createTasks(List<String> xmls, DateTime start) {
|
||||
private ImmutableList<Task> createTasks(ImmutableList<String> xmls, DateTime start) {
|
||||
ImmutableList.Builder<Task> tasks = new ImmutableList.Builder<>();
|
||||
for (int i = 0; i < xmls.size(); i++) {
|
||||
// Space tasks evenly within across a second.
|
||||
Instant scheduleTime =
|
||||
Instant.ofEpochMilli(start.plusMillis((int) (1000.0 / xmls.size() * i)).getMillis());
|
||||
tasks.add(
|
||||
Task.newBuilder()
|
||||
.setAppEngineHttpRequest(
|
||||
cloudTasksUtils
|
||||
.createTask(
|
||||
EppToolAction.class,
|
||||
Action.Method.POST,
|
||||
ImmutableMultimap.of(
|
||||
"clientId",
|
||||
registrarId,
|
||||
"superuser",
|
||||
Boolean.FALSE.toString(),
|
||||
"dryRun",
|
||||
Boolean.FALSE.toString(),
|
||||
"xml",
|
||||
xmls.get(i)))
|
||||
.toBuilder()
|
||||
.getAppEngineHttpRequest()
|
||||
.toBuilder()
|
||||
// TODO: investigate if the following is necessary now that
|
||||
// LegacyAuthenticationMechanism is gone.
|
||||
.putHeaders(X_CSRF_TOKEN, xsrfToken)
|
||||
.build())
|
||||
cloudTasksUtils
|
||||
.createTask(
|
||||
EppToolAction.class,
|
||||
Action.Method.POST,
|
||||
ImmutableMultimap.of(
|
||||
"clientId",
|
||||
registrarId,
|
||||
"superuser",
|
||||
Boolean.FALSE.toString(),
|
||||
"dryRun",
|
||||
Boolean.FALSE.toString(),
|
||||
"xml",
|
||||
xmls.get(i)))
|
||||
.toBuilder()
|
||||
.setScheduleTime(
|
||||
Timestamp.newBuilder()
|
||||
.setSeconds(scheduleTime.getEpochSecond())
|
||||
@@ -365,7 +297,7 @@ public class LoadTestAction implements Runnable {
|
||||
return tasks.build();
|
||||
}
|
||||
|
||||
private void enqueue(List<Task> tasks) {
|
||||
private void enqueue(ImmutableList<Task> tasks) {
|
||||
List<List<Task>> chunks = partition(tasks, MAX_TASKS_PER_LOAD);
|
||||
// Farm out tasks to multiple queues to work around queue qps quotas.
|
||||
for (int i = 0; i < chunks.size(); i++) {
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<create>
|
||||
<contact:create
|
||||
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<contact:id>%contact%</contact:id>
|
||||
<contact:postalInfo type="int">
|
||||
<contact:name>John Doe</contact:name>
|
||||
<contact:org>Example Inc.</contact:org>
|
||||
<contact:addr>
|
||||
<contact:street>123 Example Dr.</contact:street>
|
||||
<contact:street>Suite 100</contact:street>
|
||||
<contact:city>Dulles</contact:city>
|
||||
<contact:sp>VA</contact:sp>
|
||||
<contact:pc>20166-6503</contact:pc>
|
||||
<contact:cc>US</contact:cc>
|
||||
</contact:addr>
|
||||
</contact:postalInfo>
|
||||
<contact:voice x="1234">+1.7035555555</contact:voice>
|
||||
<contact:fax>+1.7035555556</contact:fax>
|
||||
<contact:email>jdoe@example.com</contact:email>
|
||||
<contact:authInfo>
|
||||
<contact:pw>2fooBAR</contact:pw>
|
||||
</contact:authInfo>
|
||||
<contact:disclose flag="1">
|
||||
<contact:voice/>
|
||||
<contact:email/>
|
||||
</contact:disclose>
|
||||
</contact:create>
|
||||
</create>
|
||||
<clTRID>trid</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -1,14 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<info>
|
||||
<contact:info
|
||||
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
|
||||
<contact:id>%contact%</contact:id>
|
||||
<contact:authInfo>
|
||||
<contact:pw>2fooBAR</contact:pw>
|
||||
</contact:authInfo>
|
||||
</contact:info>
|
||||
</info>
|
||||
<clTRID>trid</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -8,9 +8,6 @@
|
||||
<domain:ns>
|
||||
<domain:hostObj>%host%.example.com</domain:hostObj>
|
||||
</domain:ns>
|
||||
<domain:registrant>%contact%</domain:registrant>
|
||||
<domain:contact type="admin">%contact%</domain:contact>
|
||||
<domain:contact type="tech">%contact%</domain:contact>
|
||||
<domain:authInfo>
|
||||
<domain:pw>2fooBAR</domain:pw>
|
||||
</domain:authInfo>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<host:create
|
||||
xmlns:host="urn:ietf:params:xml:ns:host-1.0">
|
||||
<host:name>%host%.example.com</host:name>
|
||||
<host:addr ip="v4">8.8.8.8</host:addr>
|
||||
</host:create>
|
||||
</create>
|
||||
<clTRID>trid</clTRID>
|
||||
|
||||
@@ -101,8 +101,15 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
|
||||
@Column(nullable = false)
|
||||
private String registrarId;
|
||||
|
||||
/** The POC that performed the action, or null if it was a superuser. */
|
||||
@Expose private String registrarPocId;
|
||||
/**
|
||||
* The email address of the user that performed the action, or null if it was a superuser.
|
||||
*
|
||||
* <p>Note: this is misnamed in the database due to historical reasons, where we used the
|
||||
* registrar POC ID as the email address rather than a separate specialized field.
|
||||
*/
|
||||
@Column(name = "registrarPocId")
|
||||
@Expose
|
||||
private String registryLockEmail;
|
||||
|
||||
/** When the lock is first requested. */
|
||||
@AttributeOverrides({
|
||||
@@ -161,8 +168,8 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
|
||||
return registrarId;
|
||||
}
|
||||
|
||||
public String getRegistrarPocId() {
|
||||
return registrarPocId;
|
||||
public String getRegistryLockEmail() {
|
||||
return registryLockEmail;
|
||||
}
|
||||
|
||||
public DateTime getLockRequestTime() {
|
||||
@@ -255,7 +262,7 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
|
||||
checkArgumentNotNull(getInstance().registrarId, "Registrar ID cannot be null");
|
||||
checkArgumentNotNull(getInstance().verificationCode, "Verification code cannot be null");
|
||||
checkArgument(
|
||||
getInstance().registrarPocId != null || getInstance().isSuperuser,
|
||||
getInstance().registryLockEmail != null || getInstance().isSuperuser,
|
||||
"Registrar POC ID must be provided if superuser is false");
|
||||
return super.build();
|
||||
}
|
||||
@@ -275,8 +282,8 @@ public final class RegistryLock extends UpdateAutoTimestampEntity implements Bui
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegistrarPocId(String registrarPocId) {
|
||||
getInstance().registrarPocId = registrarPocId;
|
||||
public Builder setRegistryLockEmail(String registryLockEmail) {
|
||||
getInstance().registryLockEmail = registryLockEmail;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -578,7 +578,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* address.
|
||||
*/
|
||||
public ImmutableSortedSet<RegistrarPoc> getContacts() {
|
||||
return getContactPocs(tm()).stream()
|
||||
return getPocs(tm()).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR));
|
||||
}
|
||||
@@ -590,8 +590,8 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* <p>This method queries the replica database. It is reserved for use cases that can tolerate
|
||||
* slightly stale data.
|
||||
*/
|
||||
public ImmutableSortedSet<RegistrarPoc> getContactsFromReplica() {
|
||||
return getContactPocs(replicaTm()).stream()
|
||||
public ImmutableSortedSet<RegistrarPoc> getPocsFromReplica() {
|
||||
return getPocs(replicaTm()).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR));
|
||||
}
|
||||
@@ -600,8 +600,8 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* Returns a list of {@link RegistrarPoc} objects of a given type for this registrar sorted by
|
||||
* their email address.
|
||||
*/
|
||||
public ImmutableSortedSet<RegistrarPoc> getContactsOfType(final RegistrarPoc.Type type) {
|
||||
return getContactPocs(tm()).stream()
|
||||
public ImmutableSortedSet<RegistrarPoc> getPocsOfType(final RegistrarPoc.Type type) {
|
||||
return getPocs(tm()).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter((@Nullable RegistrarPoc contact) -> contact.getTypes().contains(type))
|
||||
.collect(toImmutableSortedSet(CONTACT_EMAIL_COMPARATOR));
|
||||
@@ -615,7 +615,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
return getContacts().stream().filter(RegistrarPoc::getVisibleInDomainWhoisAsAbuse).findFirst();
|
||||
}
|
||||
|
||||
private ImmutableList<RegistrarPoc> getContactPocs(TransactionManager txnManager) {
|
||||
private ImmutableList<RegistrarPoc> getPocs(TransactionManager txnManager) {
|
||||
return txnManager.transact(() -> RegistrarPoc.loadForRegistrar(registrarId));
|
||||
}
|
||||
|
||||
|
||||
@@ -14,16 +14,11 @@
|
||||
|
||||
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;
|
||||
@@ -38,7 +33,6 @@ import google.registry.model.Jsonifiable;
|
||||
import google.registry.model.UnsafeSerializable;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.QueryComposer;
|
||||
import google.registry.util.PasswordUtils;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EnumType;
|
||||
@@ -47,9 +41,7 @@ import jakarta.persistence.Id;
|
||||
import jakarta.persistence.IdClass;
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A contact for a Registrar. Note, equality, hashCode and comparable have been overridden to only
|
||||
@@ -107,9 +99,6 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
|
||||
@Id @Expose 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;
|
||||
|
||||
@@ -147,22 +136,10 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
@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.
|
||||
*/
|
||||
/** Legacy field, around until we can remove the non-null constraint and the column from SQL. */
|
||||
@Column(nullable = false)
|
||||
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}
|
||||
@@ -197,10 +174,6 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
return emailAddress;
|
||||
}
|
||||
|
||||
public Optional<String> getRegistryLockEmailAddress() {
|
||||
return Optional.ofNullable(registryLockEmailAddress);
|
||||
}
|
||||
|
||||
public String getPhoneNumber() {
|
||||
return phoneNumber;
|
||||
}
|
||||
@@ -229,24 +202,6 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
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.
|
||||
*
|
||||
@@ -296,15 +251,12 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
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("id", getId())
|
||||
.build();
|
||||
}
|
||||
@@ -344,14 +296,6 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -365,11 +309,6 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
|
||||
getInstance().registryLockEmailAddress = registryLockEmailAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPhoneNumber(String phoneNumber) {
|
||||
getInstance().phoneNumber = phoneNumber;
|
||||
return this;
|
||||
@@ -409,28 +348,6 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
getInstance().visibleInDomainWhoisAsAbuse = visible;
|
||||
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 static ImmutableList<RegistrarPoc> loadForRegistrar(String registrarId) {
|
||||
|
||||
@@ -95,10 +95,4 @@ public class SignedMarkRevocationList extends ImmutableObject {
|
||||
public int size() {
|
||||
return revokes.size();
|
||||
}
|
||||
|
||||
/** Save this list to Cloud SQL. Returns {@code this}. */
|
||||
public SignedMarkRevocationList save() {
|
||||
SignedMarkRevocationListDao.save(this);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,11 +44,25 @@ public class SignedMarkRevocationListDao {
|
||||
return smdrl.orElseGet(() -> SignedMarkRevocationList.create(START_OF_TIME, ImmutableMap.of()));
|
||||
}
|
||||
|
||||
/** Save the given {@link SignedMarkRevocationList} */
|
||||
static void save(SignedMarkRevocationList signedMarkRevocationList) {
|
||||
tm().transact(() -> tm().insert(signedMarkRevocationList));
|
||||
/**
|
||||
* Persists a {@link SignedMarkRevocationList} instance and returns the persisted entity.
|
||||
*
|
||||
* <p>Note that the input parameter is untouched. Use the returned object if metadata fields like
|
||||
* {@code revisionId} are needed.
|
||||
*/
|
||||
public static SignedMarkRevocationList save(SignedMarkRevocationList signedMarkRevocationList) {
|
||||
var persisted =
|
||||
tm().transact(
|
||||
() -> {
|
||||
var entity =
|
||||
SignedMarkRevocationList.create(
|
||||
signedMarkRevocationList.getCreationTime(),
|
||||
ImmutableMap.copyOf(signedMarkRevocationList.revokes));
|
||||
tm().insert(entity);
|
||||
return entity;
|
||||
});
|
||||
logger.atInfo().log(
|
||||
"Inserted %,d signed mark revocations into Cloud SQL.",
|
||||
signedMarkRevocationList.revokes.size());
|
||||
"Inserted %,d signed mark revocations into Cloud SQL.", persisted.revokes.size());
|
||||
return persisted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,24 +133,30 @@ public final class PremiumListDao {
|
||||
}
|
||||
|
||||
/** Saves the given premium list (and its premium list entries) to Cloud SQL. */
|
||||
public static PremiumList save(PremiumList premiumList) {
|
||||
tm().transact(
|
||||
() -> {
|
||||
tm().insert(premiumList);
|
||||
tm().getEntityManager().flush(); // This populates the revisionId.
|
||||
long revisionId = premiumList.getRevisionId();
|
||||
public static PremiumList save(PremiumList premiumListToPersist) {
|
||||
PremiumList persisted =
|
||||
tm().transact(
|
||||
() -> {
|
||||
// Make a new copy in each attempt to insert. See javadoc of the insert method for
|
||||
// more information.
|
||||
PremiumList premiumList = premiumListToPersist.asBuilder().build();
|
||||
tm().insert(premiumList);
|
||||
tm().getEntityManager().flush(); // This populates the revisionId.
|
||||
long revisionId = premiumList.getRevisionId();
|
||||
|
||||
if (!isNullOrEmpty(premiumList.getLabelsToPrices())) {
|
||||
ImmutableSet.Builder<PremiumEntry> entries = new ImmutableSet.Builder<>();
|
||||
premiumList
|
||||
.getLabelsToPrices()
|
||||
.forEach(
|
||||
(key, value) -> entries.add(PremiumEntry.create(revisionId, value, key)));
|
||||
tm().insertAll(entries.build());
|
||||
}
|
||||
});
|
||||
premiumListCache.invalidate(premiumList.getName());
|
||||
return premiumList;
|
||||
if (!isNullOrEmpty(premiumList.getLabelsToPrices())) {
|
||||
ImmutableSet.Builder<PremiumEntry> entries = new ImmutableSet.Builder<>();
|
||||
premiumList
|
||||
.getLabelsToPrices()
|
||||
.forEach(
|
||||
(key, value) ->
|
||||
entries.add(PremiumEntry.create(revisionId, value, key)));
|
||||
tm().insertAll(entries.build());
|
||||
}
|
||||
return premiumList;
|
||||
});
|
||||
premiumListCache.invalidate(persisted.getName());
|
||||
return persisted;
|
||||
}
|
||||
|
||||
public static void delete(PremiumList premiumList) {
|
||||
|
||||
@@ -27,14 +27,26 @@ public class ReservedListDao {
|
||||
|
||||
private ReservedListDao() {}
|
||||
|
||||
/** Persist a new reserved list to Cloud SQL. */
|
||||
public static void save(ReservedList reservedList) {
|
||||
/**
|
||||
* Persists a new reserved list to Cloud SQL and returns the persisted entity.
|
||||
*
|
||||
* <p>Note that the input parameter is untouched. Use the returned object if metadata fields like
|
||||
* {@code revisionId} are needed.
|
||||
*/
|
||||
public static ReservedList save(ReservedList reservedList) {
|
||||
checkArgumentNotNull(reservedList, "Must specify reservedList");
|
||||
logger.atInfo().log("Saving reserved list %s to Cloud SQL.", reservedList.getName());
|
||||
tm().transact(() -> tm().insert(reservedList));
|
||||
var persisted =
|
||||
tm().transact(
|
||||
() -> {
|
||||
var entity = reservedList.asBuilder().build();
|
||||
tm().insert(entity);
|
||||
return entity;
|
||||
});
|
||||
logger.atInfo().log(
|
||||
"Saved reserved list %s with %d entries to Cloud SQL.",
|
||||
reservedList.getName(), reservedList.getReservedListEntries().size());
|
||||
return persisted;
|
||||
}
|
||||
|
||||
/** Deletes a reserved list from Cloud SQL. */
|
||||
|
||||
@@ -49,10 +49,23 @@ public class ClaimsListDao {
|
||||
return CacheUtils.newCacheBuilder(expiry).build(ignored -> ClaimsListDao.getUncached());
|
||||
}
|
||||
|
||||
/** Saves the given {@link ClaimsList} to Cloud SQL. */
|
||||
public static void save(ClaimsList claimsList) {
|
||||
tm().transact(() -> tm().insert(claimsList));
|
||||
CACHE.put(ClaimsListDao.class, claimsList);
|
||||
/**
|
||||
* Persists a {@link ClaimsList} instance and returns the persisted entity.
|
||||
*
|
||||
* <p>Note that the input parameter is untouched. Use the returned object if metadata fields like
|
||||
* {@code revisionId} are needed.
|
||||
*/
|
||||
public static ClaimsList save(ClaimsList claimsList) {
|
||||
var persisted =
|
||||
tm().transact(
|
||||
() -> {
|
||||
var entity =
|
||||
ClaimsList.create(claimsList.tmdbGenerationTime, claimsList.labelsToKeys);
|
||||
tm().insert(entity);
|
||||
return entity;
|
||||
});
|
||||
CACHE.put(ClaimsListDao.class, persisted);
|
||||
return persisted;
|
||||
}
|
||||
|
||||
/** Returns the most recent revision of the {@link ClaimsList} from the cache. */
|
||||
|
||||
@@ -23,6 +23,7 @@ import google.registry.batch.DeleteLoadTestDataAction;
|
||||
import google.registry.batch.DeleteProberDataAction;
|
||||
import google.registry.batch.ExpandBillingRecurrencesAction;
|
||||
import google.registry.batch.RelockDomainAction;
|
||||
import google.registry.batch.RemoveAllDomainContactsAction;
|
||||
import google.registry.batch.ResaveAllEppResourcesPipelineAction;
|
||||
import google.registry.batch.ResaveEntityAction;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction;
|
||||
@@ -270,6 +271,8 @@ interface RequestComponent {
|
||||
|
||||
ReadinessProbeActionFrontend readinessProbeActionFrontend();
|
||||
|
||||
RemoveAllDomainContactsAction removeAllDomainContactsAction();
|
||||
|
||||
RdapAutnumAction rdapAutnumAction();
|
||||
|
||||
RdapDomainAction rdapDomainAction();
|
||||
|
||||
@@ -23,6 +23,7 @@ import google.registry.batch.DeleteLoadTestDataAction;
|
||||
import google.registry.batch.DeleteProberDataAction;
|
||||
import google.registry.batch.ExpandBillingRecurrencesAction;
|
||||
import google.registry.batch.RelockDomainAction;
|
||||
import google.registry.batch.RemoveAllDomainContactsAction;
|
||||
import google.registry.batch.ResaveAllEppResourcesPipelineAction;
|
||||
import google.registry.batch.ResaveEntityAction;
|
||||
import google.registry.batch.SendExpiringCertificateNotificationEmailAction;
|
||||
@@ -153,6 +154,8 @@ public interface BackendRequestComponent {
|
||||
|
||||
RelockDomainAction relockDomainAction();
|
||||
|
||||
RemoveAllDomainContactsAction removeAllDomainContactsAction();
|
||||
|
||||
ResaveAllEppResourcesPipelineAction resaveAllEppResourcesPipelineAction();
|
||||
|
||||
ResaveEntityAction resaveEntityAction();
|
||||
|
||||
@@ -344,6 +344,18 @@ public class JpaTransactionManagerImpl implements JpaTransactionManager {
|
||||
return txnInfo.transactionTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an object into the database.
|
||||
*
|
||||
* <p>If {@code entity} has an auto-generated identity field (i.e., a field annotated with {@link
|
||||
* jakarta.persistence.GeneratedValue}), the caller must not assign a value to this field,
|
||||
* otherwise Hibernate would mistake the entity as detached and raise an error.
|
||||
*
|
||||
* <p>The practical implication of the above is that when inserting such an entity using a
|
||||
* retriable transaction , the entity should be instantiated inside the transaction body. A failed
|
||||
* attempt may still assign and ID to the entity, therefore reusing the same entity would cause
|
||||
* retries to fail.
|
||||
*/
|
||||
@Override
|
||||
public void insert(Object entity) {
|
||||
checkArgumentNotNull(entity, "entity must be specified");
|
||||
|
||||
@@ -333,7 +333,6 @@ final class RdapDataStructures {
|
||||
*/
|
||||
@RestrictJsonNames("status[]")
|
||||
enum RdapStatus implements Jsonable {
|
||||
|
||||
// Status values specified in RFC 9083 § 10.2.2.
|
||||
VALIDATED("validated"),
|
||||
RENEW_PROHIBITED("renew prohibited"),
|
||||
|
||||
@@ -57,6 +57,9 @@ public class RdapDomainAction extends RdapActionBase {
|
||||
InternetDomainName domainName;
|
||||
try {
|
||||
domainName = validateDomainName(pathSearchString);
|
||||
} catch (DomainFlowUtils.TldDoesNotExistException e) {
|
||||
// A special case where a valid domain name on a nonexistent TLD should return 404
|
||||
throw new NotFoundException(pathSearchString + " not found");
|
||||
} catch (EppException e) {
|
||||
throw new BadRequestException(
|
||||
String.format(
|
||||
|
||||
@@ -14,18 +14,13 @@
|
||||
|
||||
package google.registry.rdap;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.rdap.RdapUtils.getRegistrarByIanaIdentifier;
|
||||
import static google.registry.rdap.RdapUtils.getRegistrarByName;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.HEAD;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.re2j.Pattern;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
|
||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapEntity;
|
||||
@@ -37,8 +32,8 @@ import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* RDAP action for entity (contact and registrar) requests. the ICANN operational profile dictates
|
||||
* that the "handle" for registrars is to be the IANA registrar ID:
|
||||
* RDAP action for entity (i.e. registrar) requests. the ICANN operational profile dictates that the
|
||||
* "handle" for registrars is to be the IANA registrar ID:
|
||||
*
|
||||
* <p>2.4.1.Registry RDAP servers MUST support Registrar object lookup using an entity path request
|
||||
* for entities with the registrar role using the handle (as described in 3.1.5 of RFC9082) where
|
||||
@@ -52,8 +47,6 @@ import java.util.Optional;
|
||||
auth = Auth.AUTH_PUBLIC)
|
||||
public class RdapEntityAction extends RdapActionBase {
|
||||
|
||||
private static final Pattern ROID_PATTERN = Pattern.compile("[-_.a-zA-Z0-9]+");
|
||||
|
||||
@Inject public RdapEntityAction() {
|
||||
super("entity", EndpointType.ENTITY);
|
||||
}
|
||||
@@ -61,24 +54,6 @@ public class RdapEntityAction extends RdapActionBase {
|
||||
@Override
|
||||
public RdapEntity getJsonObjectForResource(
|
||||
String pathSearchString, boolean isHeadRequest) {
|
||||
// The query string is not used; the RDAP syntax is /rdap/entity/handle (the handle is the roid
|
||||
// for contacts and the client identifier/fn for registrars). Since RDAP's concept of an entity
|
||||
// includes both contacts and registrars, search for one first, then the other.
|
||||
|
||||
// RDAP Technical Implementation Guide 2.3.1 - MUST support contact entity lookup using the
|
||||
// handle
|
||||
if (ROID_PATTERN.matcher(pathSearchString).matches()) {
|
||||
VKey<Contact> contactVKey = VKey.create(Contact.class, pathSearchString);
|
||||
Optional<Contact> contact =
|
||||
replicaTm().transact(() -> replicaTm().loadByKeyIfPresent(contactVKey));
|
||||
// As per Andy Newton on the regext mailing list, contacts by themselves have no role, since
|
||||
// they are global, and might have different roles for different domains.
|
||||
if (contact.isPresent() && isAuthorized(contact.get())) {
|
||||
return rdapJsonFormatter.createRdapContactEntity(
|
||||
contact.get(), ImmutableSet.of(), OutputDataType.FULL);
|
||||
}
|
||||
}
|
||||
|
||||
// RDAP Technical Implementation Guide 2.4.1 - MUST support registrar entity lookup using the
|
||||
// IANA ID as handle
|
||||
Long ianaIdentifier = Longs.tryParse(pathSearchString);
|
||||
@@ -96,7 +71,7 @@ public class RdapEntityAction extends RdapActionBase {
|
||||
return rdapJsonFormatter.createRdapRegistrarEntity(registrar.get(), OutputDataType.FULL);
|
||||
}
|
||||
|
||||
// At this point, we have failed to find either a contact or a registrar.
|
||||
// At this point, we have failed to find a registrar.
|
||||
//
|
||||
// RFC7480 5.3 - if the server wishes to respond that it doesn't have data satisfying the
|
||||
// query, it MUST reply with 404 response code.
|
||||
|
||||
@@ -15,22 +15,16 @@
|
||||
package google.registry.rdap;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.rdap.RdapUtils.getRegistrarByIanaIdentifier;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.HEAD;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.common.primitives.Booleans;
|
||||
import com.google.common.primitives.Longs;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.CriteriaQueryBuilder;
|
||||
import google.registry.rdap.RdapAuthorization.Role;
|
||||
import google.registry.rdap.RdapJsonFormatter.OutputDataType;
|
||||
import google.registry.rdap.RdapMetrics.EndpointType;
|
||||
import google.registry.rdap.RdapMetrics.SearchType;
|
||||
@@ -49,27 +43,12 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* RDAP action for entity (contact and registrar) search requests.
|
||||
* RDAP action for entity (i.e. registrar) search requests.
|
||||
*
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in STD 95 and its RFCs.
|
||||
*
|
||||
* <p>The RDAP specification lumps contacts and registrars together and calls them "entities", which
|
||||
* is confusing for us, because "entity" means something else in SQL. But here, when we use the
|
||||
* term, it means either a contact or registrar. When searching for entities, we always start by
|
||||
* returning all matching contacts, and after that all matching registrars.
|
||||
*
|
||||
* <p>There are two ways to search for entities: by full name (for contacts, the search name, for
|
||||
* registrars, the registrar name) or by handle (for contacts, the ROID, for registrars, the IANA
|
||||
* number). The ICANN operational profile document specifies this meaning for handle searches.
|
||||
*
|
||||
* <p>Cursors are complicated by the fact that we are essentially doing two independent searches:
|
||||
* one for contacts, and one for registrars. To accommodate this, the cursor has a prefix indicating
|
||||
* the type of the last returned item. If the last item was a contact, we return c:{value}, where
|
||||
* the value is either the search name or the ROID. If the last item was a registrar, we return
|
||||
* r:{value}, where the value is either the registrar name or the IANA number. If we get a c:
|
||||
* cursor, we use it to weed out contacts, and fetch all registrars. If we get an r: cursor, we know
|
||||
* that we can skip the contact search altogether (because we returned a registrar, and all
|
||||
* registrars come after all contacts).
|
||||
* <p>There are two ways to search for entities: by full name (the registrar name) or by handle (the
|
||||
* IANA number). The ICANN operational profile document specifies this meaning for handle searches.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
@@ -85,31 +64,10 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
|
||||
@Inject @Parameter("fn") Optional<String> fnParam;
|
||||
@Inject @Parameter("handle") Optional<String> handleParam;
|
||||
@Inject @Parameter("subtype") Optional<String> subtypeParam;
|
||||
@Inject public RdapEntitySearchAction() {
|
||||
super("entity search", EndpointType.ENTITIES);
|
||||
}
|
||||
|
||||
private enum QueryType {
|
||||
FULL_NAME,
|
||||
HANDLE
|
||||
}
|
||||
|
||||
private enum Subtype {
|
||||
ALL,
|
||||
CONTACTS,
|
||||
REGISTRARS
|
||||
}
|
||||
|
||||
private enum CursorType {
|
||||
NONE,
|
||||
CONTACT,
|
||||
REGISTRAR
|
||||
}
|
||||
|
||||
private static final String CONTACT_CURSOR_PREFIX = "c:";
|
||||
private static final String REGISTRAR_CURSOR_PREFIX = "r:";
|
||||
|
||||
/** Parses the parameters and calls the appropriate search function. */
|
||||
@Override
|
||||
public EntitySearchResponse getSearchResponse(boolean isHeadRequest) {
|
||||
@@ -118,61 +76,23 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
throw new BadRequestException("You must specify either fn=XXXX or handle=YYYY");
|
||||
}
|
||||
|
||||
// Check the subtype.
|
||||
Subtype subtype;
|
||||
if (subtypeParam.isEmpty() || subtypeParam.get().equalsIgnoreCase("all")) {
|
||||
subtype = Subtype.ALL;
|
||||
} else if (subtypeParam.get().equalsIgnoreCase("contacts")) {
|
||||
subtype = Subtype.CONTACTS;
|
||||
} else if (subtypeParam.get().equalsIgnoreCase("registrars")) {
|
||||
subtype = Subtype.REGISTRARS;
|
||||
} else {
|
||||
throw new BadRequestException("Subtype parameter must specify contacts, registrars or all");
|
||||
}
|
||||
|
||||
CursorType cursorType;
|
||||
Optional<String> cursorQueryString;
|
||||
if (cursorString.isEmpty()) {
|
||||
cursorType = CursorType.NONE;
|
||||
cursorQueryString = Optional.empty();
|
||||
} else {
|
||||
if (cursorString.get().startsWith(CONTACT_CURSOR_PREFIX)) {
|
||||
cursorType = CursorType.CONTACT;
|
||||
cursorQueryString =
|
||||
Optional.of(cursorString.get().substring(CONTACT_CURSOR_PREFIX.length()));
|
||||
} else if (cursorString.get().startsWith(REGISTRAR_CURSOR_PREFIX)) {
|
||||
cursorType = CursorType.REGISTRAR;
|
||||
cursorQueryString =
|
||||
Optional.of(cursorString.get().substring(REGISTRAR_CURSOR_PREFIX.length()));
|
||||
} else {
|
||||
throw new BadRequestException(String.format("invalid cursor: %s", cursorTokenParam));
|
||||
}
|
||||
}
|
||||
|
||||
// Search by name.
|
||||
EntitySearchResponse results;
|
||||
if (fnParam.isPresent()) {
|
||||
metricInformationBuilder.setSearchType(SearchType.BY_FULL_NAME);
|
||||
// syntax: /rdap/entities?fn=Bobby%20Joe*
|
||||
// The name is the contact name or registrar name (not registrar contact name).
|
||||
// The name is the registrar name (not registrar contact name).
|
||||
results =
|
||||
searchByName(
|
||||
recordWildcardType(RdapSearchPattern.createFromUnicodeString(fnParam.get())),
|
||||
cursorType,
|
||||
cursorQueryString,
|
||||
subtype);
|
||||
|
||||
// Search by handle.
|
||||
recordWildcardType(RdapSearchPattern.createFromUnicodeString(fnParam.get())));
|
||||
} else {
|
||||
// Search by handle.
|
||||
metricInformationBuilder.setSearchType(SearchType.BY_HANDLE);
|
||||
// syntax: /rdap/entities?handle=12345-*
|
||||
// The handle is either the contact roid or the registrar clientId.
|
||||
// The handle is the registrar ID.
|
||||
results =
|
||||
searchByHandle(
|
||||
recordWildcardType(RdapSearchPattern.createFromUnicodeString(handleParam.get())),
|
||||
cursorType,
|
||||
cursorQueryString,
|
||||
subtype);
|
||||
recordWildcardType(RdapSearchPattern.createFromUnicodeString(handleParam.get())));
|
||||
}
|
||||
|
||||
// Build the result object and return it.
|
||||
@@ -194,91 +114,32 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
* <p>According to RFC 9082 section 6.1, punycode is only used for domain name labels, so we can
|
||||
* assume that entity names are regular unicode.
|
||||
*
|
||||
* <p>The includeDeleted flag is ignored when searching for contacts, because contact names are
|
||||
* set to null when the contact is deleted, so a deleted contact can never have a name.
|
||||
*
|
||||
* <p>Since we are restricting access to contact names, we don't want name searches to return
|
||||
* contacts whose names are not visible. That would allow unscrupulous users to query by name and
|
||||
* infer that all returned contacts contain that name string. So we check the authorization level
|
||||
* to determine what to do.
|
||||
*
|
||||
* @see <a
|
||||
* href="https://newgtlds.icann.org/sites/default/files/agreements/agreement-approved-09jan14-en.htm">1.6
|
||||
* of Section 4 of the Base Registry Agreement</a>
|
||||
*/
|
||||
private EntitySearchResponse searchByName(
|
||||
final RdapSearchPattern partialStringQuery,
|
||||
CursorType cursorType,
|
||||
Optional<String> cursorQueryString,
|
||||
Subtype subtype) {
|
||||
private EntitySearchResponse searchByName(final RdapSearchPattern partialStringQuery) {
|
||||
// Don't allow wildcard suffixes when searching for entities.
|
||||
if (partialStringQuery.getHasWildcard() && (partialStringQuery.getSuffix() != null)) {
|
||||
throw new UnprocessableEntityException(
|
||||
"Suffixes not allowed in wildcard entity name searches");
|
||||
}
|
||||
// For wildcards, make sure the initial string is long enough, except in the special case of
|
||||
// searching for all registrars, where we aren't worried about inefficient searches.
|
||||
if (partialStringQuery.getHasWildcard()
|
||||
&& (subtype != Subtype.REGISTRARS)
|
||||
&& (partialStringQuery.getInitialString().length()
|
||||
< RdapSearchPattern.MIN_INITIAL_STRING_LENGTH)) {
|
||||
throw new UnprocessableEntityException(
|
||||
"Initial search string required in wildcard entity name searches");
|
||||
}
|
||||
// Get the registrar matches. If we have a registrar cursor, weed out registrars up to and
|
||||
// including the one we ended with last time. We can skip registrars if subtype is CONTACTS.
|
||||
ImmutableList<Registrar> registrars;
|
||||
if (subtype == Subtype.CONTACTS) {
|
||||
registrars = ImmutableList.of();
|
||||
} else {
|
||||
registrars =
|
||||
Streams.stream(Registrar.loadAllCached())
|
||||
.sorted(
|
||||
Comparator.comparing(Registrar::getRegistrarName, String.CASE_INSENSITIVE_ORDER))
|
||||
.filter(
|
||||
registrar ->
|
||||
partialStringQuery.matches(registrar.getRegistrarName())
|
||||
&& ((cursorType != CursorType.REGISTRAR)
|
||||
|| (registrar.getRegistrarName().compareTo(cursorQueryString.get())
|
||||
> 0))
|
||||
&& shouldBeVisible(registrar))
|
||||
.limit(rdapResultSetMaxSize + 1)
|
||||
.collect(toImmutableList());
|
||||
}
|
||||
// Get the contact matches and return the results, fetching an additional contact to detect
|
||||
// truncation. Don't bother searching for contacts by name if the request would not be able to
|
||||
// see any names anyway. Also, if a registrar cursor is present, we have already moved past the
|
||||
// contacts, and don't need to fetch them this time. We can skip contacts if subtype is
|
||||
// REGISTRARS.
|
||||
RdapResultSet<Contact> resultSet;
|
||||
if (subtype == Subtype.REGISTRARS) {
|
||||
resultSet = RdapResultSet.create(ImmutableList.of());
|
||||
} else {
|
||||
if ((rdapAuthorization.role() == RdapAuthorization.Role.PUBLIC)
|
||||
|| (cursorType == CursorType.REGISTRAR)) {
|
||||
resultSet = RdapResultSet.create(ImmutableList.of());
|
||||
} else {
|
||||
resultSet =
|
||||
replicaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
CriteriaQueryBuilder<Contact> builder =
|
||||
queryItems(
|
||||
Contact.class,
|
||||
"searchName",
|
||||
partialStringQuery,
|
||||
cursorQueryString,
|
||||
DeletedItemHandling.EXCLUDE);
|
||||
if (!rdapAuthorization.role().equals(Role.ADMINISTRATOR)) {
|
||||
builder =
|
||||
builder.whereFieldIsIn(
|
||||
"currentSponsorRegistrarId", rdapAuthorization.registrarIds());
|
||||
}
|
||||
return getMatchingResources(builder, false, rdapResultSetMaxSize + 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
return makeSearchResults(resultSet, registrars, QueryType.FULL_NAME);
|
||||
// Get the registrar matches. If we have a cursor, weed out registrars up to and including the
|
||||
// one we ended with last time.
|
||||
ImmutableList<Registrar> registrars =
|
||||
Streams.stream(Registrar.loadAllCached())
|
||||
.sorted(
|
||||
Comparator.comparing(Registrar::getRegistrarName, String.CASE_INSENSITIVE_ORDER))
|
||||
.filter(
|
||||
registrar ->
|
||||
partialStringQuery.matches(registrar.getRegistrarName())
|
||||
&& (cursorString.isEmpty()
|
||||
|| (registrar.getRegistrarName().compareTo(cursorString.get()) > 0))
|
||||
&& shouldBeVisible(registrar))
|
||||
.limit(rdapResultSetMaxSize + 1)
|
||||
.collect(toImmutableList());
|
||||
|
||||
return makeSearchResults(registrars);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,101 +149,41 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
*
|
||||
* <p>We don't allow suffixes after a wildcard in entity searches. Suffixes are used in domain
|
||||
* searches to specify a TLD, and in nameserver searches to specify a locally managed domain name.
|
||||
* In both cases, the suffix can be turned into an additional query filter field. For contacts,
|
||||
* there is no equivalent string suffix that can be used as a query filter, so we disallow use.
|
||||
* In both cases, the suffix can be turned into an additional query filter field.
|
||||
*/
|
||||
private EntitySearchResponse searchByHandle(
|
||||
final RdapSearchPattern partialStringQuery,
|
||||
CursorType cursorType,
|
||||
Optional<String> cursorQueryString,
|
||||
Subtype subtype) {
|
||||
private EntitySearchResponse searchByHandle(final RdapSearchPattern partialStringQuery) {
|
||||
if (partialStringQuery.getSuffix() != null) {
|
||||
throw new UnprocessableEntityException("Suffixes not allowed in entity handle searches");
|
||||
}
|
||||
// Handle queries without a wildcard (and not including deleted) -- load by ID.
|
||||
if (!partialStringQuery.getHasWildcard() && !shouldIncludeDeleted()) {
|
||||
ImmutableList<Contact> contactList;
|
||||
if (subtype == Subtype.REGISTRARS) {
|
||||
contactList = ImmutableList.of();
|
||||
} else {
|
||||
Optional<Contact> contact =
|
||||
replicaTm()
|
||||
.transact(
|
||||
() ->
|
||||
replicaTm()
|
||||
.loadByKeyIfPresent(
|
||||
VKey.create(Contact.class, partialStringQuery.getInitialString())));
|
||||
contactList =
|
||||
(contact.isPresent() && shouldBeVisible(contact.get()))
|
||||
? ImmutableList.of(contact.get())
|
||||
: ImmutableList.of();
|
||||
}
|
||||
ImmutableList<Registrar> registrarList;
|
||||
if (subtype == Subtype.CONTACTS) {
|
||||
registrarList = ImmutableList.of();
|
||||
} else {
|
||||
registrarList = getMatchingRegistrars(partialStringQuery.getInitialString());
|
||||
}
|
||||
return makeSearchResults(
|
||||
contactList,
|
||||
IncompletenessWarningType.COMPLETE,
|
||||
contactList.size(),
|
||||
registrarList,
|
||||
QueryType.HANDLE);
|
||||
return makeSearchResults(getMatchingRegistrars(partialStringQuery.getInitialString()));
|
||||
}
|
||||
// Handle queries with a wildcard (or including deleted), but no suffix. Because the handle
|
||||
// for registrars is the IANA identifier number, don't allow wildcard searches for registrars,
|
||||
// by simply not searching for registrars if a wildcard is present (unless the request is for
|
||||
// all registrars, in which case we know what to do). Fetch an extra contact to detect result
|
||||
// set truncation.
|
||||
// all registrars, in which case we know what to do).
|
||||
ImmutableList<Registrar> registrars;
|
||||
if (partialStringQuery.getHasWildcard() && partialStringQuery.getInitialString().isEmpty()) {
|
||||
// Even though we are searching by IANA identifier, we should still sort by name, because
|
||||
// the IANA identifier can by missing, and sorting on that would screw up our cursors.
|
||||
registrars =
|
||||
Streams.stream(Registrar.loadAllCached())
|
||||
.sorted(
|
||||
Comparator.comparing(Registrar::getRegistrarName, String.CASE_INSENSITIVE_ORDER))
|
||||
.filter(
|
||||
registrar ->
|
||||
(cursorString.isEmpty()
|
||||
|| (registrar.getRegistrarName().compareTo(cursorString.get()) > 0))
|
||||
&& shouldBeVisible(registrar))
|
||||
.limit(rdapResultSetMaxSize + 1)
|
||||
.collect(toImmutableList());
|
||||
} else if (partialStringQuery.getHasWildcard()) {
|
||||
registrars = ImmutableList.of();
|
||||
} else {
|
||||
ImmutableList<Registrar> registrars;
|
||||
if ((subtype == Subtype.REGISTRARS)
|
||||
&& partialStringQuery.getHasWildcard()
|
||||
&& partialStringQuery.getInitialString().isEmpty()) {
|
||||
// Even though we are searching by IANA identifier, we should still sort by name, because
|
||||
// the IANA identifier can by missing, and sorting on that would screw up our cursors.
|
||||
registrars =
|
||||
Streams.stream(Registrar.loadAllCached())
|
||||
.sorted(
|
||||
Comparator.comparing(
|
||||
Registrar::getRegistrarName, String.CASE_INSENSITIVE_ORDER))
|
||||
.filter(
|
||||
registrar ->
|
||||
((cursorType != CursorType.REGISTRAR)
|
||||
|| (registrar.getRegistrarName().compareTo(cursorQueryString.get())
|
||||
> 0))
|
||||
&& shouldBeVisible(registrar))
|
||||
.limit(rdapResultSetMaxSize + 1)
|
||||
.collect(toImmutableList());
|
||||
} else if ((subtype == Subtype.CONTACTS) || partialStringQuery.getHasWildcard()) {
|
||||
registrars = ImmutableList.of();
|
||||
} else {
|
||||
registrars = getMatchingRegistrars(partialStringQuery.getInitialString());
|
||||
}
|
||||
// Get the contact matches and return the results, fetching an additional contact to detect
|
||||
// truncation. If we are including deleted entries, we must fetch more entries, in case some
|
||||
// get excluded due to permissioning. Any cursor present must be a contact cursor, because we
|
||||
// would never return a registrar for this search.
|
||||
int querySizeLimit = getStandardQuerySizeLimit();
|
||||
RdapResultSet<Contact> contactResultSet;
|
||||
if (subtype == Subtype.REGISTRARS) {
|
||||
contactResultSet = RdapResultSet.create(ImmutableList.of());
|
||||
} else {
|
||||
contactResultSet =
|
||||
replicaTm()
|
||||
.transact(
|
||||
() ->
|
||||
getMatchingResources(
|
||||
queryItemsByKey(
|
||||
Contact.class,
|
||||
partialStringQuery,
|
||||
cursorQueryString,
|
||||
getDeletedItemHandling()),
|
||||
shouldIncludeDeleted(),
|
||||
querySizeLimit));
|
||||
}
|
||||
return makeSearchResults(contactResultSet, registrars, QueryType.HANDLE);
|
||||
registrars = getMatchingRegistrars(partialStringQuery.getInitialString());
|
||||
}
|
||||
return makeSearchResults(registrars);
|
||||
}
|
||||
|
||||
/** Looks up registrars by handle (i.e. IANA identifier). */
|
||||
@@ -397,90 +198,25 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
: ImmutableList.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a JSON array of entity info maps based on the specified contacts and registrars.
|
||||
*
|
||||
* <p>This is a convenience wrapper for the four-argument makeSearchResults; it unpacks the
|
||||
* properties of the {@link RdapResultSet} structure and passes them as separate arguments.
|
||||
*/
|
||||
private EntitySearchResponse makeSearchResults(
|
||||
RdapResultSet<Contact> resultSet, List<Registrar> registrars, QueryType queryType) {
|
||||
return makeSearchResults(
|
||||
resultSet.resources(),
|
||||
resultSet.incompletenessWarningType(),
|
||||
resultSet.numResourcesRetrieved(),
|
||||
registrars,
|
||||
queryType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a JSON array of entity info maps based on the specified contacts and registrars.
|
||||
*
|
||||
* <p>The number of contacts retrieved is recorded for use by the metrics.
|
||||
*
|
||||
* @param contacts the list of contacts which can be returned
|
||||
* @param incompletenessWarningType MIGHT_BE_INCOMPLETE if the list of contacts might be
|
||||
* incomplete; this only matters if the total count of contacts and registrars combined is
|
||||
* less than a full result set's worth
|
||||
* @param numContactsRetrieved the number of contacts retrieved in the process of generating the
|
||||
* results
|
||||
* @param registrars the list of registrars which can be returned
|
||||
* @param queryType whether the query was by full name or by handle
|
||||
* @return an {@link RdapSearchResults} object
|
||||
*/
|
||||
private EntitySearchResponse makeSearchResults(
|
||||
List<Contact> contacts,
|
||||
IncompletenessWarningType incompletenessWarningType,
|
||||
int numContactsRetrieved,
|
||||
List<Registrar> registrars,
|
||||
QueryType queryType) {
|
||||
|
||||
metricInformationBuilder.setNumContactsRetrieved(numContactsRetrieved);
|
||||
|
||||
/** Builds a JSON array of entity info maps based on the specified registrars. */
|
||||
private EntitySearchResponse makeSearchResults(List<Registrar> registrars) {
|
||||
// Determine what output data type to use, depending on whether more than one entity will be
|
||||
// returned.
|
||||
OutputDataType outputDataType =
|
||||
(contacts.size() + registrars.size() > 1) ? OutputDataType.SUMMARY : OutputDataType.FULL;
|
||||
|
||||
// There can be more results than our max size, partially because we have two pools to draw from
|
||||
// (contacts and registrars), and partially because we try to fetch one more than the max size,
|
||||
// so we can tell whether to display the truncation notification.
|
||||
//
|
||||
// Each time we add a contact or registrar to the output data set, remember what the appropriate
|
||||
// cursor would be if it were the last item returned. When we stop adding items, the last cursor
|
||||
// value we remembered will be the right one to pass back.
|
||||
EntitySearchResponse.Builder builder =
|
||||
EntitySearchResponse.builder()
|
||||
.setIncompletenessWarningType(incompletenessWarningType);
|
||||
Optional<String> newCursor = Optional.empty();
|
||||
for (Contact contact : Iterables.limit(contacts, rdapResultSetMaxSize)) {
|
||||
// As per Andy Newton on the regext mailing list, contacts by themselves have no role, since
|
||||
// they are global, and might have different roles for different domains.
|
||||
registrars.size() > 1 ? OutputDataType.SUMMARY : OutputDataType.FULL;
|
||||
EntitySearchResponse.Builder builder = EntitySearchResponse.builder();
|
||||
Iterable<Registrar> limitedRegistrars = Iterables.limit(registrars, rdapResultSetMaxSize);
|
||||
for (Registrar registrar : limitedRegistrars) {
|
||||
builder
|
||||
.entitySearchResultsBuilder()
|
||||
.add(
|
||||
rdapJsonFormatter.createRdapContactEntity(
|
||||
contact, ImmutableSet.of(), outputDataType));
|
||||
newCursor =
|
||||
Optional.of(
|
||||
CONTACT_CURSOR_PREFIX
|
||||
+ ((queryType == QueryType.FULL_NAME)
|
||||
? contact.getSearchName()
|
||||
: contact.getRepoId()));
|
||||
.add(rdapJsonFormatter.createRdapRegistrarEntity(registrar, outputDataType));
|
||||
}
|
||||
if (rdapResultSetMaxSize > contacts.size()) {
|
||||
for (Registrar registrar :
|
||||
Iterables.limit(registrars, rdapResultSetMaxSize - contacts.size())) {
|
||||
builder
|
||||
.entitySearchResultsBuilder()
|
||||
.add(rdapJsonFormatter.createRdapRegistrarEntity(registrar, outputDataType));
|
||||
newCursor = Optional.of(REGISTRAR_CURSOR_PREFIX + registrar.getRegistrarName());
|
||||
}
|
||||
}
|
||||
if (rdapResultSetMaxSize < contacts.size() + registrars.size()) {
|
||||
builder.setNextPageUri(createNavigationUri(newCursor.get()));
|
||||
if (rdapResultSetMaxSize < registrars.size()) {
|
||||
builder.setNextPageUri(
|
||||
createNavigationUri(Iterables.getLast(limitedRegistrars).getRegistrarName()));
|
||||
builder.setIncompletenessWarningType(IncompletenessWarningType.TRUNCATED);
|
||||
return builder.build();
|
||||
} else {
|
||||
builder.setIncompletenessWarningType(IncompletenessWarningType.COMPLETE);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -117,44 +117,4 @@ public class RdapIcannStandardInformation {
|
||||
/** Possibly incomplete notice as a singleton list, for easy use. */
|
||||
static final ImmutableList<Notice> POSSIBLY_INCOMPLETE_NOTICES =
|
||||
ImmutableList.of(POSSIBLY_INCOMPLETE_RESULT_SET_NOTICE);
|
||||
|
||||
/**
|
||||
* Included when requester is not logged in as the owner of the contact being returned.
|
||||
*
|
||||
* <p>>Note: if we were keeping this around, we'd want/need to implement the <a
|
||||
* href="https://datatracker.ietf.org/doc/rfc9537/">official RDAP redaction spec</a> for contacts.
|
||||
* We are getting rid of contacts in 2025 though so this should be unnecessary.
|
||||
*/
|
||||
static final Remark CONTACT_PERSONAL_DATA_HIDDEN_DATA_REMARK =
|
||||
Remark.builder()
|
||||
.setTitle("REDACTED FOR PRIVACY")
|
||||
.setDescription(
|
||||
"Some of the data in this object has been removed.",
|
||||
"Contact personal data is visible only to the owning registrar.")
|
||||
.setType(Remark.Type.OBJECT_REDACTED_AUTHORIZATION)
|
||||
.addLink(
|
||||
Link.builder()
|
||||
.setRel("alternate")
|
||||
.setHref(
|
||||
"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication")
|
||||
.setType("text/html")
|
||||
.build())
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Included in ALL contact responses, even if the user is authorized.
|
||||
*
|
||||
* <p>>Note: if we were keeping this around, we'd want/need to implement the <a
|
||||
* href="https://datatracker.ietf.org/doc/rfc9537/">official RDAP redaction spec</a> for contacts.
|
||||
* We are getting rid of contacts in 2025 though so this should be unnecessary.
|
||||
*/
|
||||
static final Remark CONTACT_EMAIL_REDACTED_FOR_DOMAIN =
|
||||
Remark.builder()
|
||||
.setTitle("EMAIL REDACTED FOR PRIVACY")
|
||||
.setDescription(
|
||||
"Please query the RDDS service of the Registrar of Record identifies in this output"
|
||||
+ " for information on how to contact the Registrant of the queried domain"
|
||||
+ " name.")
|
||||
.setType(Remark.Type.OBJECT_REDACTED_AUTHORIZATION)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -18,17 +18,14 @@ import static com.google.common.base.Predicates.not;
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.ImmutableSetMultimap.toImmutableSetMultimap;
|
||||
import static google.registry.model.EppResourceUtils.isLinked;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
import static google.registry.util.CollectionUtils.union;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.collect.Streams;
|
||||
@@ -40,11 +37,6 @@ import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.CacheUtils;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.adapters.EnumToAttributeAdapter.EppEnum;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactPhoneNumber;
|
||||
import google.registry.model.contact.PostalInfo;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.DesignatedContact.Type;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.rgp.GracePeriodStatus;
|
||||
import google.registry.model.eppcommon.Address;
|
||||
@@ -55,19 +47,17 @@ import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.model.reporting.HistoryEntry;
|
||||
import google.registry.model.reporting.HistoryEntryDao;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.rdap.RdapDataStructures.Event;
|
||||
import google.registry.rdap.RdapDataStructures.EventAction;
|
||||
import google.registry.rdap.RdapDataStructures.Link;
|
||||
import google.registry.rdap.RdapDataStructures.Notice;
|
||||
import google.registry.rdap.RdapDataStructures.PublicId;
|
||||
import google.registry.rdap.RdapDataStructures.RdapStatus;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapContactEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapDomain;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapEntity.Role;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapNameserver;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapRegistrarEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.RdapRegistrarPocEntity;
|
||||
import google.registry.rdap.RdapObjectClasses.SecureDns;
|
||||
import google.registry.rdap.RdapObjectClasses.Vcard;
|
||||
import google.registry.rdap.RdapObjectClasses.VcardArray;
|
||||
@@ -134,8 +124,8 @@ public class RdapJsonFormatter {
|
||||
* What type of data to generate.
|
||||
*
|
||||
* <p>Summary data includes only information about the object itself, while full data includes
|
||||
* associated items (e.g. for domains, full data includes the hosts, contacts and history entries
|
||||
* connected with the domain).
|
||||
* associated items (e.g. for domains, full data includes the hosts and history entries connected
|
||||
* with the domain).
|
||||
*
|
||||
* <p>Summary data is appropriate for search queries which return many results, to avoid load on
|
||||
* the system. According to the ICANN operational profile, a remark must be attached to the
|
||||
@@ -260,10 +250,6 @@ public class RdapJsonFormatter {
|
||||
private static final Ordering<Host> HOST_RESOURCE_ORDERING =
|
||||
Ordering.natural().onResultOf(Host::getHostName);
|
||||
|
||||
/** Sets the ordering for designated contacts; order them in a fixed order by contact type. */
|
||||
private static final Ordering<DesignatedContact> DESIGNATED_CONTACT_ORDERING =
|
||||
Ordering.natural().onResultOf(DesignatedContact::getType);
|
||||
|
||||
/** Creates the TOS notice that is added to every reply. */
|
||||
Notice createTosNotice() {
|
||||
String linkValue = makeRdapServletRelativeUrl("help", RdapHelpAction.TOS_PATH);
|
||||
@@ -387,43 +373,6 @@ public class RdapJsonFormatter {
|
||||
.transact(
|
||||
() ->
|
||||
ImmutableSet.copyOf(replicaTm().loadByKeys(domain.getNameservers()).values()));
|
||||
// Load the registrant and other contacts and add them to the data.
|
||||
ImmutableSet<VKey<Contact>> contacts = domain.getReferencedContacts();
|
||||
ImmutableMap<VKey<? extends Contact>, Contact> loadedContacts =
|
||||
contacts.isEmpty()
|
||||
? ImmutableMap.of()
|
||||
: replicaTm().transact(() -> replicaTm().loadByKeysIfPresent(contacts));
|
||||
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts. 2.7.4 discusses redaction of
|
||||
// fields we don't want to show (as opposed to not having contacts at all) because of GDPR etc.
|
||||
//
|
||||
// The GDPR redaction is handled in createRdapContactEntity.
|
||||
|
||||
// Load all contacts that are present and group them by type (it is common for a single contact
|
||||
// entity to be used across multiple contact types on domain, e.g. registrant and admin).
|
||||
ImmutableSetMultimap<VKey<Contact>, Type> contactsToRoles =
|
||||
domain.getAllContacts().stream()
|
||||
.sorted(DESIGNATED_CONTACT_ORDERING)
|
||||
.collect(
|
||||
toImmutableSetMultimap(
|
||||
DesignatedContact::getContactKey, DesignatedContact::getType));
|
||||
|
||||
// Convert the contact entities to RDAP output contacts (this also converts the contact types
|
||||
// to RDAP roles).
|
||||
for (VKey<Contact> contactKey : contactsToRoles.keySet()) {
|
||||
Set<Role> roles =
|
||||
contactsToRoles.get(contactKey).stream()
|
||||
.map(RdapJsonFormatter::convertContactTypeToRdapRole)
|
||||
.collect(toImmutableSet());
|
||||
if (roles.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
builder
|
||||
.entitiesBuilder()
|
||||
.add(
|
||||
createRdapContactEntity(
|
||||
loadedContacts.get(contactKey), roles, OutputDataType.INTERNAL));
|
||||
}
|
||||
|
||||
// Add the nameservers to the data; the load was kicked off above for efficiency.
|
||||
// RDAP Response Profile 2.8: we MUST have the nameservers
|
||||
@@ -523,134 +472,6 @@ public class RdapJsonFormatter {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON object for a {@link Contact} and associated contact type.
|
||||
*
|
||||
* <p>If the contact isn't present (i.e. because of minimum registration data set), then always
|
||||
* show all of its fields as if they were redacted, and always deny RDAP authorization.
|
||||
*
|
||||
* @param contact the contact resource object from which the JSON object should be created
|
||||
* @param roles the roles of this contact
|
||||
* @param outputDataType whether to generate full or summary data
|
||||
*/
|
||||
RdapContactEntity createRdapContactEntity(
|
||||
Contact contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
|
||||
RdapContactEntity.Builder contactBuilder = RdapContactEntity.builder();
|
||||
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts
|
||||
boolean isAuthorized =
|
||||
rdapAuthorization.isAuthorizedForRegistrar(contact.getCurrentSponsorRegistrarId());
|
||||
|
||||
VcardArray.Builder vcardBuilder = VcardArray.builder();
|
||||
|
||||
if (isAuthorized) {
|
||||
fillRdapContactEntityWhenAuthorized(contactBuilder, vcardBuilder, contact, outputDataType);
|
||||
} else {
|
||||
// GTLD Registration Data Temp Spec 17may18, Appendix A, 2.3, 2.4 and RDAP Response Profile
|
||||
// 2.7.4.1, 2.7.4.2 - the following fields must be redacted:
|
||||
// for REGISTRANT:
|
||||
// handle (ROID), FN (name), TEL (telephone/fax and extension), street, city, postal code
|
||||
// for ADMIN, TECH:
|
||||
// handle (ROID), FN (name), TEL (telephone/fax and extension), Organization, street, city,
|
||||
// state/province, postal code, country
|
||||
//
|
||||
// Note that in theory we have to show the Organization and state/province and country for the
|
||||
// REGISTRANT. For now, we won't do that until we make sure it's really OK for GDPR
|
||||
//
|
||||
// RDAP Response Profile 2.7.4.3: if we redact values from the contact, we MUST include a
|
||||
// remark
|
||||
contactBuilder
|
||||
.remarksBuilder()
|
||||
.add(RdapIcannStandardInformation.CONTACT_PERSONAL_DATA_HIDDEN_DATA_REMARK);
|
||||
contactBuilder.setHandle("");
|
||||
// The VCard format requires a "fn" entry even if it is empty (redacted)
|
||||
vcardBuilder.add(Vcard.create("fn", "text", ""));
|
||||
}
|
||||
|
||||
contactBuilder.setVcardArray(vcardBuilder.build());
|
||||
contactBuilder.rolesBuilder().addAll(roles);
|
||||
|
||||
// RDAP Response Profile 2.7.5.1, 2.7.5.3:
|
||||
// email MUST be omitted, and we MUST have a Remark saying so
|
||||
contactBuilder
|
||||
.remarksBuilder()
|
||||
.add(RdapIcannStandardInformation.CONTACT_EMAIL_REDACTED_FOR_DOMAIN);
|
||||
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
// Rdap Response Profile 1.5 must have "last update of RDAP database" response. But this is
|
||||
// only for direct query responses and not for internal objects. I'm not sure why it's in that
|
||||
// section at all...
|
||||
contactBuilder.setLastUpdateOfRdapDatabaseEvent(
|
||||
Event.builder()
|
||||
.setEventAction(EventAction.LAST_UPDATE_OF_RDAP_DATABASE)
|
||||
.setEventDate(getRequestTime())
|
||||
.build());
|
||||
}
|
||||
return contactBuilder.build();
|
||||
}
|
||||
|
||||
private void fillRdapContactEntityWhenAuthorized(
|
||||
RdapContactEntity.Builder contactBuilder,
|
||||
VcardArray.Builder vcardBuilder,
|
||||
Contact contact,
|
||||
OutputDataType outputDataType) {
|
||||
// ROID needs to be redacted if we aren't authorized, so we can't have a self-link for
|
||||
// unauthorized users
|
||||
contactBuilder.linksBuilder().add(makeSelfLink("entity", contact.getRepoId()));
|
||||
// RDAP Response Profile 2.7.3 - we MUST provide a handle set with the ROID, subject to
|
||||
// redaction.
|
||||
contactBuilder.setHandle(contact.getRepoId());
|
||||
if (outputDataType.equals(OutputDataType.FULL)) {
|
||||
// RDAP Response Profile doesn't mention status for contacts, so we only show it if we're both
|
||||
// FULL and Authorized.
|
||||
contactBuilder
|
||||
.statusBuilder()
|
||||
.addAll(
|
||||
makeStatusValueList(
|
||||
isLinked(contact.createVKey(), getRequestTime())
|
||||
? union(contact.getStatusValues(), StatusValue.LINKED)
|
||||
: contact.getStatusValues(),
|
||||
false,
|
||||
contact.getDeletionTime().isBefore(getRequestTime())));
|
||||
// If we are outputting all data (not just summary data), also add events taken from the
|
||||
// history entries. This isn't strictly required.
|
||||
//
|
||||
// We also only add it for authorized users because millisecond times can fingerprint a user
|
||||
// just as much as the handle can.
|
||||
contactBuilder.eventsBuilder().addAll(makeOptionalEvents(contact));
|
||||
} else {
|
||||
// Only show the "summary data remark" if the user is authorized to see this data - because
|
||||
// unauthorized users don't have a self link meaning they can't navigate to the full data.
|
||||
contactBuilder.remarksBuilder().add(RdapIcannStandardInformation.SUMMARY_DATA_REMARK);
|
||||
}
|
||||
// Adding the VCard members when not redacted.
|
||||
//
|
||||
// RDAP Response Profile 2.7.3 - we MUST have FN, ADR, TEL, EMAIL.
|
||||
//
|
||||
// Note that 2.7.5 also says the EMAIL must be omitted, so we'll omit it
|
||||
PostalInfo postalInfo = contact.getInternationalizedPostalInfo();
|
||||
if (postalInfo == null) {
|
||||
postalInfo = contact.getLocalizedPostalInfo();
|
||||
}
|
||||
if (postalInfo != null) {
|
||||
if (postalInfo.getName() != null) {
|
||||
vcardBuilder.add(Vcard.create("fn", "text", postalInfo.getName()));
|
||||
}
|
||||
if (postalInfo.getOrg() != null) {
|
||||
vcardBuilder.add(Vcard.create("org", "text", postalInfo.getOrg()));
|
||||
}
|
||||
addVCardAddressEntry(vcardBuilder, postalInfo.getAddress());
|
||||
}
|
||||
ContactPhoneNumber voicePhoneNumber = contact.getVoiceNumber();
|
||||
if (voicePhoneNumber != null) {
|
||||
vcardBuilder.add(makePhoneEntry(PHONE_TYPE_VOICE, makePhoneString(voicePhoneNumber)));
|
||||
}
|
||||
ContactPhoneNumber faxPhoneNumber = contact.getFaxNumber();
|
||||
if (faxPhoneNumber != null) {
|
||||
vcardBuilder.add(makePhoneEntry(PHONE_TYPE_FAX, makePhoneString(faxPhoneNumber)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSON object for a {@link Registrar}.
|
||||
*
|
||||
@@ -740,38 +561,37 @@ public class RdapJsonFormatter {
|
||||
|
||||
builder.setVcardArray(vcardBuilder.build());
|
||||
|
||||
// Registrar contacts are a bit complicated.
|
||||
// Registrar POCs are a bit complicated.
|
||||
//
|
||||
// Rdap Response Profile 3.2, we SHOULD have at least ADMIN and TECH contacts. It says
|
||||
// Rdap Response Profile 3.2, we SHOULD have at least ADMIN and TECH POCs. It says
|
||||
// nothing about ABUSE at all.
|
||||
//
|
||||
// Rdap Response Profile 4.3 doesn't mention contacts at all, meaning probably we don't have to
|
||||
// have any contacts there. But the Registrar itself is Optional in that case, so we will just
|
||||
// Rdap Response Profile 4.3 doesn't mention POCs at all, meaning probably we don't have to
|
||||
// have any POCs there. But the Registrar itself is Optional in that case, so we will just
|
||||
// skip it completely.
|
||||
//
|
||||
// Rdap Response Profile 2.4.5 says the Registrar inside a Domain response MUST include the
|
||||
// ABUSE contact, but doesn't require any other contact.
|
||||
// ABUSE POC, but doesn't require any other POCs.
|
||||
//
|
||||
// Write the minimum, meaning only ABUSE for INTERNAL registrars, nothing for SUMMARY and
|
||||
// everything for FULL.
|
||||
if (outputDataType != OutputDataType.SUMMARY) {
|
||||
ImmutableList<RdapContactEntity> registrarContacts =
|
||||
registrar.getContactsFromReplica().stream()
|
||||
.map(RdapJsonFormatter::makeRdapJsonForRegistrarContact)
|
||||
ImmutableList<RdapRegistrarPocEntity> registrarPocs =
|
||||
registrar.getPocsFromReplica().stream()
|
||||
.map(RdapJsonFormatter::makeRdapJsonForRegistrarPoc)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.filter(
|
||||
contact ->
|
||||
poc ->
|
||||
outputDataType == OutputDataType.FULL
|
||||
|| contact.roles().contains(RdapEntity.Role.ABUSE))
|
||||
|| poc.roles().contains(RdapEntity.Role.ABUSE))
|
||||
.collect(toImmutableList());
|
||||
if (registrarContacts.stream()
|
||||
.noneMatch(contact -> contact.roles().contains(RdapEntity.Role.ABUSE))) {
|
||||
if (registrarPocs.stream().noneMatch(poc -> poc.roles().contains(RdapEntity.Role.ABUSE))) {
|
||||
logger.atWarning().log(
|
||||
"Registrar '%s' (IANA ID %s) is missing ABUSE contact.",
|
||||
"Registrar '%s' (IANA ID %s) is missing ABUSE POC.",
|
||||
registrar.getRegistrarId(), registrar.getIanaIdentifier());
|
||||
}
|
||||
builder.entitiesBuilder().addAll(registrarContacts);
|
||||
builder.entitiesBuilder().addAll(registrarPocs);
|
||||
}
|
||||
|
||||
// Rdap Response Profile 1.5, must have "last update of RDAP database" response. But this is
|
||||
@@ -789,7 +609,7 @@ public class RdapJsonFormatter {
|
||||
/**
|
||||
* Creates a JSON object for a {@link RegistrarPoc}.
|
||||
*
|
||||
* <p>Returns empty if this contact shouldn't be visible (doesn't have a role).
|
||||
* <p>Returns empty if this POC shouldn't be visible (doesn't have a role).
|
||||
*
|
||||
* <p>NOTE that registrar locations in the response require different roles and different VCard
|
||||
* members according to the spec. Currently, this function returns all the rolls and all the
|
||||
@@ -799,19 +619,18 @@ public class RdapJsonFormatter {
|
||||
* <p>Specifically:
|
||||
* <li>Registrar inside a Domain only requires the ABUSE role, and only the TEL and EMAIL members
|
||||
* (RDAP Response Profile 2.4.5)
|
||||
* <li>Registrar responses to direct query don't require any contact, but *should* have the TECH
|
||||
* and ADMIN roles, but require the FN, TEL and EMAIL members
|
||||
* <li>Registrar inside a Nameserver isn't required at all, and if given doesn't require any
|
||||
* contacts
|
||||
* <li>Registrar responses to direct query don't require any POCs, but *should* have the TECH and
|
||||
* ADMIN roles, but require the FN, TEL and EMAIL members
|
||||
* <li>Registrar inside a Nameserver isn't required at all, and if given doesn't require any POCs
|
||||
*
|
||||
* @param registrarPoc the registrar contact for which the JSON object should be created
|
||||
* @param registrarPoc the registrar POC for which the JSON object should be created
|
||||
*/
|
||||
static Optional<RdapContactEntity> makeRdapJsonForRegistrarContact(RegistrarPoc registrarPoc) {
|
||||
static Optional<RdapRegistrarPocEntity> makeRdapJsonForRegistrarPoc(RegistrarPoc registrarPoc) {
|
||||
ImmutableList<RdapEntity.Role> roles = makeRdapRoleList(registrarPoc);
|
||||
if (roles.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
RdapContactEntity.Builder builder = RdapContactEntity.builder();
|
||||
RdapRegistrarPocEntity.Builder builder = RdapRegistrarPocEntity.builder();
|
||||
builder.statusBuilder().addAll(STATUS_LIST_ACTIVE);
|
||||
builder.rolesBuilder().addAll(roles);
|
||||
// Create the vCard.
|
||||
@@ -838,20 +657,10 @@ public class RdapJsonFormatter {
|
||||
return Optional.of(builder.build());
|
||||
}
|
||||
|
||||
/** Converts a domain registry contact type into a role as defined by RFC 9083. */
|
||||
private static RdapEntity.Role convertContactTypeToRdapRole(DesignatedContact.Type contactType) {
|
||||
return switch (contactType) {
|
||||
case REGISTRANT -> RdapEntity.Role.REGISTRANT;
|
||||
case TECH -> RdapEntity.Role.TECH;
|
||||
case BILLING -> RdapEntity.Role.BILLING;
|
||||
case ADMIN -> RdapEntity.Role.ADMIN;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the list of RDAP roles for a registrar contact, using the visibleInWhoisAs* flags.
|
||||
* Creates the list of RDAP roles for a registrar POC, using the visibleInWhoisAs* flags.
|
||||
*
|
||||
* <p>Only contacts with a non-empty role list should be visible.
|
||||
* <p>Only POCs with a non-empty role list should be visible.
|
||||
*
|
||||
* <p>The RDAP response profile only mandates the "abuse" entity:
|
||||
*
|
||||
@@ -929,7 +738,7 @@ public class RdapJsonFormatter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the list of optional events to list in domain, nameserver, or contact replies.
|
||||
* Creates the list of optional events to list in domain or nameserver replies.
|
||||
*
|
||||
* <p>Only has entries for optional events that won't be shown in "SUMMARY" versions of these
|
||||
* objects. These are either stated as optional in the RDAP Response Profile, or not mentioned at
|
||||
@@ -1055,15 +864,6 @@ public class RdapJsonFormatter {
|
||||
return Vcard.create("tel", type, "uri", phoneNumber);
|
||||
}
|
||||
|
||||
/** Creates a phone string in URI format, as per the vCard spec. */
|
||||
private static String makePhoneString(ContactPhoneNumber phoneNumber) {
|
||||
String phoneString = String.format("tel:%s", phoneNumber.getPhoneNumber());
|
||||
if (phoneNumber.getExtension() != null) {
|
||||
phoneString = phoneString + ";ext=" + phoneNumber.getExtension();
|
||||
}
|
||||
return phoneString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string array of status values.
|
||||
*
|
||||
|
||||
@@ -133,16 +133,6 @@ public class RdapMetrics {
|
||||
LABEL_DESCRIPTORS_FOR_RETRIEVAL_COUNTS,
|
||||
FIBONACCI_FITTER);
|
||||
|
||||
@VisibleForTesting
|
||||
static final EventMetric numberOfContactsRetrieved =
|
||||
MetricRegistryImpl.getDefault()
|
||||
.newEventMetric(
|
||||
"/rdap/num_contacts_retrieved",
|
||||
"Number of contacts retrieved",
|
||||
"count",
|
||||
LABEL_DESCRIPTORS_FOR_RETRIEVAL_COUNTS,
|
||||
FIBONACCI_FITTER);
|
||||
|
||||
@Inject
|
||||
public RdapMetrics() {}
|
||||
|
||||
@@ -191,15 +181,6 @@ public class RdapMetrics {
|
||||
getLabelStringForPrefixLength(rdapMetricInformation.prefixLength()),
|
||||
rdapMetricInformation.includeDeleted() ? "YES" : "NO");
|
||||
}
|
||||
if (rdapMetricInformation.numContactsRetrieved().isPresent()) {
|
||||
numberOfContactsRetrieved.record(
|
||||
rdapMetricInformation.numContactsRetrieved().get(),
|
||||
rdapMetricInformation.endpointType().toString(),
|
||||
rdapMetricInformation.searchType().toString(),
|
||||
rdapMetricInformation.wildcardType().toString(),
|
||||
getLabelStringForPrefixLength(rdapMetricInformation.prefixLength()),
|
||||
rdapMetricInformation.includeDeleted() ? "YES" : "NO");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,10 +202,8 @@ public class RdapMetrics {
|
||||
* than were actually returned in the response; absent if a search was not performed.
|
||||
* @param numHostsRetrieved Number of hosts retrieved from the database; this might be more than
|
||||
* were actually returned in the response; absent if a search was not performed.
|
||||
* @param numContactsRetrieved Number of contacts retrieved from the database; this might be more
|
||||
* than were actually returned in the response; absent if a search was not performed.
|
||||
*/
|
||||
record RdapMetricInformation(
|
||||
public record RdapMetricInformation(
|
||||
EndpointType endpointType,
|
||||
SearchType searchType,
|
||||
WildcardType wildcardType,
|
||||
@@ -236,8 +215,7 @@ public class RdapMetrics {
|
||||
int statusCode,
|
||||
IncompletenessWarningType incompletenessWarningType,
|
||||
Optional<Long> numDomainsRetrieved,
|
||||
Optional<Long> numHostsRetrieved,
|
||||
Optional<Long> numContactsRetrieved) {
|
||||
Optional<Long> numHostsRetrieved) {
|
||||
|
||||
@AutoBuilder
|
||||
interface Builder {
|
||||
@@ -265,8 +243,6 @@ public class RdapMetrics {
|
||||
|
||||
Builder setNumHostsRetrieved(long numHostsRetrieved);
|
||||
|
||||
Builder setNumContactsRetrieved(long numContactRetrieved);
|
||||
|
||||
RdapMetricInformation build();
|
||||
}
|
||||
|
||||
|
||||
@@ -253,8 +253,8 @@ final class RdapObjectClasses {
|
||||
/**
|
||||
* The Entity Object Class defined in 5.1 of RFC 9083.
|
||||
*
|
||||
* <p>Entities are used both for Contacts and for Registrars. We will create different subobjects
|
||||
* for each one for type safety.
|
||||
* <p>Entities are used both for Registrar POCs and for Registrars. We will create different
|
||||
* subobjects for each one for type safety.
|
||||
*
|
||||
* <p>We're missing the "autnums" and "networks" fields
|
||||
*/
|
||||
@@ -309,8 +309,8 @@ final class RdapObjectClasses {
|
||||
/**
|
||||
* Registrar version of the Entity Object Class defined in 5.1 of RFC 9083.
|
||||
*
|
||||
* <p>Entities are used both for Contacts and for Registrars. We will create different subobjects
|
||||
* for each one for type safety.
|
||||
* <p>Entities are used both for Registrar POCs and for Registrars. We will create different
|
||||
* subobjects for each one for type safety.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract static class RdapRegistrarEntity extends RdapEntity {
|
||||
@@ -326,21 +326,21 @@ final class RdapObjectClasses {
|
||||
}
|
||||
|
||||
/**
|
||||
* Contact version of the Entity Object Class defined in 5.1 of RFC 9083.
|
||||
* RegistrarPoc version of the Entity Object Class defined in 5.1 of RFC 9083.
|
||||
*
|
||||
* <p>Entities are used both for Contacts and for Registrars. We will create different subobjects
|
||||
* for each one for type safety.
|
||||
* <p>Entities are used both for Registrar POCs and for Registrars. We will create different
|
||||
* subobjects for each one for type safety.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract static class RdapContactEntity extends RdapEntity {
|
||||
public abstract static class RdapRegistrarPocEntity extends RdapEntity {
|
||||
|
||||
static Builder builder() {
|
||||
return new AutoValue_RdapObjectClasses_RdapContactEntity.Builder();
|
||||
return new AutoValue_RdapObjectClasses_RdapRegistrarPocEntity.Builder();
|
||||
}
|
||||
|
||||
@AutoValue.Builder
|
||||
abstract static class Builder extends RdapEntity.Builder<Builder> {
|
||||
abstract RdapContactEntity build();
|
||||
abstract RdapRegistrarPocEntity build();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ abstract class RdapSearchResults {
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
abstract static class EntitySearchResponse extends BaseSearchResponse {
|
||||
public abstract static class EntitySearchResponse extends BaseSearchResponse {
|
||||
|
||||
@JsonableElement public abstract ImmutableList<RdapEntity> entitySearchResults();
|
||||
|
||||
|
||||
@@ -18,11 +18,12 @@ import static com.google.common.base.Throwables.getRootCause;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import com.google.cloud.storage.BlobId;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.io.ByteStreams;
|
||||
@@ -41,7 +42,6 @@ import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Copy all registrar detail reports in a given bucket's subdirectory from GCS to Drive. */
|
||||
@Action(
|
||||
@@ -98,7 +98,8 @@ public final class CopyDetailReportsAction implements Runnable {
|
||||
response.setPayload(String.format("Failure, encountered %s", e.getMessage()));
|
||||
return;
|
||||
}
|
||||
ImmutableMap.Builder<String, Throwable> copyErrorsBuilder = new ImmutableMap.Builder<>();
|
||||
ImmutableMultimap.Builder<String, Throwable> copyErrorsBuilder =
|
||||
new ImmutableMultimap.Builder<>();
|
||||
for (String detailReportName : detailReportObjectNames) {
|
||||
// The standard report format is "invoice_details_yyyy-MM_registrarId_tld.csv
|
||||
// TODO(larryruili): Determine a safer way of enforcing this.
|
||||
@@ -145,17 +146,18 @@ public final class CopyDetailReportsAction implements Runnable {
|
||||
response.setStatus(SC_OK);
|
||||
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
|
||||
StringBuilder payload = new StringBuilder().append("Copied detail reports.\n");
|
||||
ImmutableMap<String, Throwable> copyErrors = copyErrorsBuilder.build();
|
||||
ImmutableMultimap<String, Throwable> copyErrors = copyErrorsBuilder.build();
|
||||
if (!copyErrors.isEmpty()) {
|
||||
payload.append("The following errors were encountered:\n");
|
||||
payload.append(
|
||||
copyErrors.entrySet().stream()
|
||||
.map(
|
||||
entrySet ->
|
||||
String.format(
|
||||
"Registrar: %s\nError: %s\n",
|
||||
entrySet.getKey(), entrySet.getValue().getMessage()))
|
||||
.collect(Collectors.joining()));
|
||||
for (var registrarId : copyErrors.keySet()) {
|
||||
payload.append(
|
||||
String.format(
|
||||
"Registrar: %s\nError: %s\n",
|
||||
registrarId,
|
||||
copyErrors.get(registrarId).stream()
|
||||
.map(Throwable::getMessage)
|
||||
.collect(joining("\n\t"))));
|
||||
}
|
||||
}
|
||||
response.setPayload(payload.toString());
|
||||
emailUtils.sendAlertEmail(payload.toString());
|
||||
|
||||
@@ -135,7 +135,8 @@ public final class RequestModule {
|
||||
@Provides
|
||||
@RequestUrl
|
||||
static String provideRequestUrl(HttpServletRequest req) {
|
||||
return req.getRequestURL().toString();
|
||||
String url = req.getRequestURL().toString();
|
||||
return url.startsWith("https") ? url : url.replaceFirst("http", "https");
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -30,6 +30,7 @@ import google.registry.request.auth.AuthModule.IapOidc;
|
||||
import google.registry.request.auth.AuthModule.RegularOidc;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import google.registry.util.StopwatchLogger;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Optional;
|
||||
@@ -77,7 +78,9 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
logger.atWarning().log("Using AuthResult %s for testing.", authResultForTesting);
|
||||
return authResultForTesting;
|
||||
}
|
||||
final StopwatchLogger stopwatch = new StopwatchLogger();
|
||||
String rawIdToken = tokenExtractor.extract(request);
|
||||
stopwatch.tick("OidcTokenAuthenticationMechanism tokenExtractor extracted");
|
||||
if (rawIdToken == null) {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
@@ -99,7 +102,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
? "Raw token redacted in prod"
|
||||
: rawIdToken);
|
||||
}
|
||||
|
||||
stopwatch.tick("OidcTokenAuthenticationMechanism token verified");
|
||||
if (token == null) {
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
@@ -109,16 +112,23 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
logger.atInfo().log("No email address from the OIDC token:\n%s", token.getPayload());
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
|
||||
// Short-circuit the user lookup for known service accounts.
|
||||
// This check bypasses the database lookup for high-volume
|
||||
// traffic from trusted system accounts to reduce database load.
|
||||
|
||||
if (serviceAccountEmails.contains(email)) {
|
||||
return AuthResult.createApp(email);
|
||||
}
|
||||
logger.atInfo().log("No service account found for email address %s, loading the User", email);
|
||||
Optional<User> maybeUser =
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(VKey.create(User.class, email)));
|
||||
stopwatch.tick("OidcTokenAuthenticationMechanism maybeUser loaded");
|
||||
if (maybeUser.isPresent()) {
|
||||
return AuthResult.createUser(maybeUser.get());
|
||||
}
|
||||
logger.atInfo().log("No end user found for email address %s", email);
|
||||
if (serviceAccountEmails.stream().anyMatch(e -> e.equals(email))) {
|
||||
return AuthResult.createApp(email);
|
||||
}
|
||||
logger.atInfo().log("No service account found for email address %s", email);
|
||||
|
||||
logger.atWarning().log(
|
||||
"The email address %s is not tied to a principal with access to Nomulus", email);
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.keyring.api.KeyModule.Key;
|
||||
import google.registry.model.smd.SignedMarkRevocationList;
|
||||
import google.registry.model.smd.SignedMarkRevocationListDao;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.auth.Auth;
|
||||
@@ -57,7 +58,7 @@ public final class TmchSmdrlAction implements Runnable {
|
||||
} catch (GeneralSecurityException | IOException | PGPException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
smdrl.save();
|
||||
smdrl = SignedMarkRevocationListDao.save(smdrl);
|
||||
logger.atInfo().log(
|
||||
"Inserted %,d smd revocations into the database, created at %s.",
|
||||
smdrl.size(), smdrl.getCreationTime());
|
||||
|
||||
@@ -47,6 +47,11 @@ final class CreateCdnsTld extends ConfirmingCommand {
|
||||
)
|
||||
String name;
|
||||
|
||||
@Parameter(
|
||||
names = "--skip_sandbox_tld_check",
|
||||
description = "In Sandbox, skip the dns_name format check.")
|
||||
boolean skipSandboxTldCheck;
|
||||
|
||||
@Inject
|
||||
@Config("projectId")
|
||||
String projectId;
|
||||
@@ -61,10 +66,15 @@ final class CreateCdnsTld extends ConfirmingCommand {
|
||||
protected void init() {
|
||||
// Sandbox talks to production Cloud DNS. As a result, we can't configure any domains with a
|
||||
// suffix that might be used by customers on the same nameserver set. Limit the user to setting
|
||||
// up *.test TLDs.
|
||||
if (RegistryToolEnvironment.get() == RegistryToolEnvironment.SANDBOX
|
||||
&& !dnsName.endsWith(".test.")) {
|
||||
throw new IllegalArgumentException("Sandbox TLDs must be of the form \"*.test.\"");
|
||||
// up *.test TLDs unless the user declares that the name is approved.
|
||||
//
|
||||
// The name format check simply provides a user-friendly error message. If the user wrongly
|
||||
// declares name approval, the request to the Cloud DNS API will still fail.
|
||||
if (RegistryToolEnvironment.get() == RegistryToolEnvironment.SANDBOX) {
|
||||
if (!skipSandboxTldCheck && !dnsName.endsWith(".test.")) {
|
||||
throw new IllegalArgumentException(
|
||||
"Sandbox TLDs must be approved or in the form \"*.test.\"");
|
||||
}
|
||||
}
|
||||
|
||||
managedZone =
|
||||
|
||||
@@ -74,11 +74,12 @@ public final class DomainLockUtils {
|
||||
* <p>The lock will not be applied until {@link #verifyVerificationCode} is called.
|
||||
*/
|
||||
public RegistryLock saveNewRegistryLockRequest(
|
||||
String domainName, String registrarId, @Nullable String registrarPocId, boolean isAdmin) {
|
||||
String domainName, String registrarId, @Nullable String registryLockEmail, boolean isAdmin) {
|
||||
return tm().transact(
|
||||
() ->
|
||||
RegistryLockDao.save(
|
||||
createLockBuilder(domainName, registrarId, registrarPocId, isAdmin).build()));
|
||||
createLockBuilder(domainName, registrarId, registryLockEmail, isAdmin)
|
||||
.build()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,13 +130,13 @@ public final class DomainLockUtils {
|
||||
* the case of relocks, isAdmin is determined by the previous lock.
|
||||
*/
|
||||
public RegistryLock administrativelyApplyLock(
|
||||
String domainName, String registrarId, @Nullable String registrarPocId, boolean isAdmin) {
|
||||
String domainName, String registrarId, @Nullable String registryLockEmail, boolean isAdmin) {
|
||||
return tm().transact(
|
||||
() -> {
|
||||
DateTime now = tm().getTransactionTime();
|
||||
RegistryLock newLock =
|
||||
RegistryLockDao.save(
|
||||
createLockBuilder(domainName, registrarId, registrarPocId, isAdmin)
|
||||
createLockBuilder(domainName, registrarId, registryLockEmail, isAdmin)
|
||||
.setLockCompletionTime(now)
|
||||
.build());
|
||||
applyLockStatuses(newLock, now, isAdmin);
|
||||
@@ -235,7 +236,7 @@ public final class DomainLockUtils {
|
||||
}
|
||||
|
||||
private RegistryLock.Builder createLockBuilder(
|
||||
String domainName, String registrarId, @Nullable String registrarPocId, boolean isAdmin) {
|
||||
String domainName, String registrarId, @Nullable String registryLockEmail, boolean isAdmin) {
|
||||
DateTime now = tm().getTransactionTime();
|
||||
Domain domain = getDomain(domainName, registrarId, now);
|
||||
verifyDomainNotLocked(domain, isAdmin);
|
||||
@@ -255,7 +256,7 @@ public final class DomainLockUtils {
|
||||
.setDomainName(domainName)
|
||||
.setRepoId(domain.getRepoId())
|
||||
.setRegistrarId(registrarId)
|
||||
.setRegistrarPocId(registrarPocId)
|
||||
.setRegistryLockEmail(registryLockEmail)
|
||||
.isSuperuser(isAdmin);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.collect.Sets.difference;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.DEFAULT;
|
||||
@@ -46,6 +47,9 @@ import google.registry.model.domain.token.AllocationToken;
|
||||
import google.registry.model.domain.token.AllocationToken.RegistrationBehavior;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenStatus;
|
||||
import google.registry.model.domain.token.AllocationToken.TokenType;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tlds;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.tools.params.MoneyParameter;
|
||||
import google.registry.tools.params.TransitionListParameter.TokenStatusTransitions;
|
||||
@@ -291,10 +295,12 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
!ImmutableList.of("").equals(allowedClientIds),
|
||||
"Either omit --allowed_client_ids if all registrars are allowed, or include a"
|
||||
+ " comma-separated list");
|
||||
verifyAllRegistrarIdsExist(allowedClientIds);
|
||||
|
||||
checkArgument(
|
||||
!ImmutableList.of("").equals(allowedTlds),
|
||||
"Either omit --allowed_tlds if all TLDs are allowed, or include a comma-separated list");
|
||||
verifyAllTldsExist(allowedTlds);
|
||||
|
||||
if (ImmutableList.of("").equals(allowedEppActions)) {
|
||||
allowedEppActions = ImmutableList.of();
|
||||
@@ -326,6 +332,34 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyAllRegistrarIdsExist(@Nullable List<String> allowedClientIds) {
|
||||
// a null/empty list means that all registrars are allowed
|
||||
if (isNullOrEmpty(allowedClientIds)) {
|
||||
return;
|
||||
}
|
||||
ImmutableSet<String> allRegistrarIds =
|
||||
Registrar.loadAllKeysCached().stream()
|
||||
.map(VKey::getKey)
|
||||
.map(Object::toString)
|
||||
.collect(toImmutableSet());
|
||||
ImmutableList<String> badRegistrarIds =
|
||||
allowedClientIds.stream()
|
||||
.filter(id -> !allRegistrarIds.contains(id))
|
||||
.collect(toImmutableList());
|
||||
checkArgument(badRegistrarIds.isEmpty(), "Unknown registrar ID(s) %s", badRegistrarIds);
|
||||
}
|
||||
|
||||
static void verifyAllTldsExist(@Nullable List<String> allowedTlds) {
|
||||
// a null/empty list means that all TLDs are allowed
|
||||
if (isNullOrEmpty(allowedTlds)) {
|
||||
return;
|
||||
}
|
||||
ImmutableSet<String> allTlds = Tlds.getTldsOfType(Tld.TldType.REAL);
|
||||
ImmutableList<String> badTlds =
|
||||
allowedTlds.stream().filter(tld -> !allTlds.contains(tld)).collect(toImmutableList());
|
||||
checkArgument(badTlds.isEmpty(), "Unknown REAL TLD(s) %s", badTlds);
|
||||
}
|
||||
|
||||
private void verifyTokenStringsDoNotExist() {
|
||||
ImmutableSet<String> existingTokenStrings =
|
||||
getExistingTokenStrings(ImmutableSet.copyOf(tokenStrings));
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -52,11 +51,6 @@ class LoadTestCommand extends ConfirmingCommand implements CommandWithConnection
|
||||
description = "Number of domains to create per second.")
|
||||
int successfulDomainCreates = 1;
|
||||
|
||||
@Parameter(
|
||||
names = {"--successful_contact_creates"},
|
||||
description = "Number of contact records to create per second.")
|
||||
int successfulContactCreates = 1;
|
||||
|
||||
@Parameter(
|
||||
names = {"--host_infos"},
|
||||
description = "Number of successful host:info commands to send per second.")
|
||||
@@ -67,11 +61,6 @@ class LoadTestCommand extends ConfirmingCommand implements CommandWithConnection
|
||||
description = "Number of successful domain:info commands to send per second.")
|
||||
int domainInfos = 1;
|
||||
|
||||
@Parameter(
|
||||
names = {"--contact_infos"},
|
||||
description = "Number of successful contact:info commands to send per second.")
|
||||
int contactInfos = 1;
|
||||
|
||||
@Parameter(
|
||||
names = {"--run_seconds"},
|
||||
description = "Time to run the load test in seconds.")
|
||||
@@ -120,10 +109,8 @@ class LoadTestCommand extends ConfirmingCommand implements CommandWithConnection
|
||||
.put("clientId", clientId)
|
||||
.put("successfulHostCreates", successfulHostCreates)
|
||||
.put("successfulDomainCreates", successfulDomainCreates)
|
||||
.put("successfulContactCreates", successfulContactCreates)
|
||||
.put("hostInfos", hostInfos)
|
||||
.put("domainInfos", domainInfos)
|
||||
.put("contactInfos", contactInfos)
|
||||
.put("runSeconds", runSeconds)
|
||||
.build();
|
||||
|
||||
|
||||
@@ -85,12 +85,6 @@ final class RegistrarPocCommand extends MutatingCommand {
|
||||
+ " and will be used as the console login email, if --login_email is not specified.")
|
||||
String email;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--registry_lock_email",
|
||||
description = "Email address used for registry lock confirmation emails")
|
||||
String registryLockEmail;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--phone",
|
||||
@@ -132,15 +126,6 @@ final class RegistrarPocCommand extends MutatingCommand {
|
||||
arity = 1)
|
||||
private Boolean visibleInDomainWhoisAsAbuse;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--allowed_to_set_registry_lock_password",
|
||||
description =
|
||||
"Allow this contact to set their registry lock password in the console,"
|
||||
+ " enabling registry lock",
|
||||
arity = 1)
|
||||
private Boolean allowedToSetRegistryLockPassword;
|
||||
|
||||
@Parameter(
|
||||
names = {"-o", "--output"},
|
||||
description = "Output file when --mode=LIST",
|
||||
@@ -235,9 +220,6 @@ final class RegistrarPocCommand extends MutatingCommand {
|
||||
builder.setRegistrar(registrar);
|
||||
builder.setName(name);
|
||||
builder.setEmailAddress(email);
|
||||
if (!isNullOrEmpty(registryLockEmail)) {
|
||||
builder.setRegistryLockEmailAddress(registryLockEmail);
|
||||
}
|
||||
if (phone != null) {
|
||||
builder.setPhoneNumber(phone.orElse(null));
|
||||
}
|
||||
@@ -255,9 +237,6 @@ final class RegistrarPocCommand extends MutatingCommand {
|
||||
if (visibleInDomainWhoisAsAbuse != null) {
|
||||
builder.setVisibleInDomainWhoisAsAbuse(visibleInDomainWhoisAsAbuse);
|
||||
}
|
||||
if (allowedToSetRegistryLockPassword != null) {
|
||||
builder.setAllowedToSetRegistryLockPassword(allowedToSetRegistryLockPassword);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -269,9 +248,6 @@ final class RegistrarPocCommand extends MutatingCommand {
|
||||
if (!isNullOrEmpty(name)) {
|
||||
builder.setName(name);
|
||||
}
|
||||
if (!isNullOrEmpty(registryLockEmail)) {
|
||||
builder.setRegistryLockEmailAddress(registryLockEmail);
|
||||
}
|
||||
if (phone != null) {
|
||||
builder.setPhoneNumber(phone.orElse(null));
|
||||
}
|
||||
@@ -290,9 +266,6 @@ final class RegistrarPocCommand extends MutatingCommand {
|
||||
if (visibleInDomainWhoisAsAbuse != null) {
|
||||
builder.setVisibleInDomainWhoisAsAbuse(visibleInDomainWhoisAsAbuse);
|
||||
}
|
||||
if (allowedToSetRegistryLockPassword != null) {
|
||||
builder.setAllowedToSetRegistryLockPassword(allowedToSetRegistryLockPassword);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.re2j.Pattern;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.Action.GkeService;
|
||||
@@ -56,9 +55,6 @@ import org.json.simple.JSONValue;
|
||||
*/
|
||||
public class ServiceConnection {
|
||||
|
||||
/** Pattern to heuristically extract title tag contents in HTML responses. */
|
||||
protected static final Pattern HTML_TITLE_TAG_PATTERN = Pattern.compile("<title>(.*?)</title>");
|
||||
|
||||
private final Service service;
|
||||
private final boolean useCanary;
|
||||
private final HttpRequestFactory requestFactory;
|
||||
|
||||
@@ -157,6 +157,9 @@ final class UpdateAllocationTokensCommand extends UpdateOrDeleteAllocationTokens
|
||||
endToken = true;
|
||||
}
|
||||
|
||||
GenerateAllocationTokensCommand.verifyAllRegistrarIdsExist(allowedClientIds);
|
||||
GenerateAllocationTokensCommand.verifyAllTldsExist(allowedTlds);
|
||||
|
||||
tokensToSave =
|
||||
tm().transact(
|
||||
() ->
|
||||
|
||||
@@ -21,7 +21,6 @@ import static google.registry.util.DomainNameUtils.canonicalizeHostname;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InternetDomainName;
|
||||
@@ -29,7 +28,6 @@ import com.google.re2j.Pattern;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.ui.forms.FormException;
|
||||
import google.registry.ui.forms.FormField;
|
||||
import google.registry.ui.forms.FormFieldException;
|
||||
import google.registry.ui.forms.FormFields;
|
||||
@@ -38,7 +36,6 @@ import google.registry.util.X509Utils;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -192,14 +189,6 @@ public final class RegistrarFormFields {
|
||||
public static final FormField<String, String> CONTACT_FAX_NUMBER_FIELD =
|
||||
FormFields.PHONE_NUMBER.asBuilderNamed("faxNumber").build();
|
||||
|
||||
public static final FormField<Object, Boolean> CONTACT_ALLOWED_TO_SET_REGISTRY_LOCK_PASSWORD =
|
||||
FormField.named("allowedToSetRegistryLockPassword", Object.class)
|
||||
.transform(Boolean.class, b -> Boolean.valueOf(Objects.toString(b)))
|
||||
.build();
|
||||
|
||||
public static final FormField<String, String> CONTACT_REGISTRY_LOCK_PASSWORD_FIELD =
|
||||
FormFields.NAME.asBuilderNamed("registryLockPassword").build();
|
||||
|
||||
public static final FormField<String, Set<RegistrarPoc.Type>> CONTACT_TYPES =
|
||||
FormField.named("types")
|
||||
.uppercased()
|
||||
@@ -369,8 +358,6 @@ public final class RegistrarFormFields {
|
||||
private static void applyRegistrarContactArgs(RegistrarPoc.Builder builder, Map<String, ?> args) {
|
||||
builder.setName(CONTACT_NAME_FIELD.extractUntyped(args).orElse(null));
|
||||
builder.setEmailAddress(CONTACT_EMAIL_ADDRESS_FIELD.extractUntyped(args).orElse(null));
|
||||
builder.setRegistryLockEmailAddress(
|
||||
REGISTRY_LOCK_EMAIL_ADDRESS_FIELD.extractUntyped(args).orElse(null));
|
||||
builder.setVisibleInWhoisAsAdmin(
|
||||
CONTACT_VISIBLE_IN_WHOIS_AS_ADMIN_FIELD.extractUntyped(args).orElse(false));
|
||||
builder.setVisibleInWhoisAsTech(
|
||||
@@ -380,23 +367,5 @@ public final class RegistrarFormFields {
|
||||
builder.setPhoneNumber(CONTACT_PHONE_NUMBER_FIELD.extractUntyped(args).orElse(null));
|
||||
builder.setFaxNumber(CONTACT_FAX_NUMBER_FIELD.extractUntyped(args).orElse(null));
|
||||
builder.setTypes(CONTACT_TYPES.extractUntyped(args).orElse(ImmutableSet.of()));
|
||||
// The parser is inconsistent with whether it retrieves boolean values as strings or booleans.
|
||||
// As a result, use a potentially-redundant converter that can deal with both.
|
||||
builder.setAllowedToSetRegistryLockPassword(
|
||||
CONTACT_ALLOWED_TO_SET_REGISTRY_LOCK_PASSWORD.extractUntyped(args).orElse(false));
|
||||
|
||||
// Registry lock password does not need to be set every time
|
||||
CONTACT_REGISTRY_LOCK_PASSWORD_FIELD
|
||||
.extractUntyped(args)
|
||||
.ifPresent(
|
||||
password -> {
|
||||
if (!Strings.isNullOrEmpty(password)) {
|
||||
if (password.length() < 8) {
|
||||
throw new FormException(
|
||||
"Registry lock password must be at least 8 characters long");
|
||||
}
|
||||
builder.setRegistryLockPassword(password);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ public class PasswordResetVerifyAction extends ConsoleApiAction {
|
||||
// Temporary flag when testing email sending etc
|
||||
if (!user.getUserRoles().isAdmin()) {
|
||||
setFailedResponse("", HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
PasswordResetRequest request = tm().transact(() -> loadAndValidateResetRequest(user));
|
||||
ImmutableMap<String, ?> result =
|
||||
@@ -76,6 +77,7 @@ public class PasswordResetVerifyAction extends ConsoleApiAction {
|
||||
// Temporary flag when testing email sending etc
|
||||
if (!user.getUserRoles().isAdmin()) {
|
||||
setFailedResponse("", HttpServletResponse.SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
checkArgument(!Strings.isNullOrEmpty(newPassword.orElse(null)), "Password must be provided");
|
||||
tm().transact(
|
||||
|
||||
@@ -53,8 +53,10 @@ import java.util.Optional;
|
||||
public class RegistrarsAction extends ConsoleApiAction {
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
private static final int PASSCODE_LENGTH = 5;
|
||||
private static final ImmutableList<Registrar.Type> allowedRegistrarTypes =
|
||||
ImmutableList.of(Registrar.Type.REAL, Registrar.Type.OTE);
|
||||
private static final ImmutableSet<Registrar.Type> TYPES_ALLOWED_FOR_USERS =
|
||||
ImmutableSet.of(Registrar.Type.REAL, Registrar.Type.OTE);
|
||||
private static final ImmutableSet<Registrar.Type> TYPES_ALLOWED_FOR_ADMINS =
|
||||
ImmutableSet.of(Registrar.Type.INTERNAL, Registrar.Type.REAL, Registrar.Type.OTE);
|
||||
private static final String SQL_TEMPLATE =
|
||||
"""
|
||||
SELECT * FROM "Registrar"
|
||||
@@ -80,6 +82,8 @@ public class RegistrarsAction extends ConsoleApiAction {
|
||||
@Override
|
||||
protected void getHandler(User user) {
|
||||
if (user.getUserRoles().hasGlobalPermission(ConsolePermission.VIEW_REGISTRARS)) {
|
||||
ImmutableSet<Registrar.Type> allowedRegistrarTypes =
|
||||
user.getUserRoles().isAdmin() ? TYPES_ALLOWED_FOR_ADMINS : TYPES_ALLOWED_FOR_USERS;
|
||||
ImmutableList<Registrar> registrars =
|
||||
Streams.stream(Registrar.loadAll())
|
||||
.filter(r -> allowedRegistrarTypes.contains(r.getType()))
|
||||
@@ -94,6 +98,7 @@ public class RegistrarsAction extends ConsoleApiAction {
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(toImmutableSet());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Registrar> registrars =
|
||||
tm().transact(
|
||||
() ->
|
||||
|
||||
@@ -45,7 +45,6 @@ import google.registry.ui.server.console.ConsoleApiAction;
|
||||
import google.registry.ui.server.console.ConsoleApiParams;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
@@ -246,7 +245,6 @@ public class ContactAction extends ConsoleApiAction {
|
||||
throw new ContactRequirementException(
|
||||
"An abuse contact visible in domain WHOIS query must be designated");
|
||||
}
|
||||
checkContactRegistryLockRequirements(existingContacts, updatedContacts);
|
||||
}
|
||||
|
||||
private static void enforcePrimaryContactRestrictions(
|
||||
@@ -266,69 +264,6 @@ public class ContactAction extends ConsoleApiAction {
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkContactRegistryLockRequirements(
|
||||
ImmutableSet<RegistrarPoc> existingContacts, ImmutableSet<RegistrarPoc> updatedContacts) {
|
||||
// Any contact(s) with new passwords must be allowed to set them
|
||||
for (RegistrarPoc updatedContact : updatedContacts) {
|
||||
if (updatedContact.isRegistryLockAllowed()
|
||||
|| updatedContact.isAllowedToSetRegistryLockPassword()) {
|
||||
RegistrarPoc existingContact =
|
||||
existingContacts.stream()
|
||||
.filter(
|
||||
contact -> contact.getEmailAddress().equals(updatedContact.getEmailAddress()))
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new FormException(
|
||||
"Cannot set registry lock password directly on new contact"));
|
||||
// Can't modify registry lock email address
|
||||
if (!Objects.equals(
|
||||
updatedContact.getRegistryLockEmailAddress(),
|
||||
existingContact.getRegistryLockEmailAddress())) {
|
||||
throw new FormException("Cannot modify registryLockEmailAddress through the UI");
|
||||
}
|
||||
if (updatedContact.isRegistryLockAllowed()) {
|
||||
// the password must have been set before or the user was allowed to set it now
|
||||
if (!existingContact.isAllowedToSetRegistryLockPassword()
|
||||
&& !existingContact.isRegistryLockAllowed()) {
|
||||
throw new FormException("Registrar contact not allowed to set registry lock password");
|
||||
}
|
||||
}
|
||||
if (updatedContact.isAllowedToSetRegistryLockPassword()) {
|
||||
if (!existingContact.isAllowedToSetRegistryLockPassword()) {
|
||||
throw new FormException(
|
||||
"Cannot modify isAllowedToSetRegistryLockPassword through the UI");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Any previously-existing contacts with registry lock enabled cannot be deleted
|
||||
existingContacts.stream()
|
||||
.filter(RegistrarPoc::isRegistryLockAllowed)
|
||||
.forEach(
|
||||
contact -> {
|
||||
Optional<RegistrarPoc> updatedContactOptional =
|
||||
updatedContacts.stream()
|
||||
.filter(
|
||||
updatedContact ->
|
||||
updatedContact.getEmailAddress().equals(contact.getEmailAddress()))
|
||||
.findFirst();
|
||||
if (updatedContactOptional.isEmpty()) {
|
||||
throw new FormException(
|
||||
String.format(
|
||||
"Cannot delete the contact %s that has registry lock enabled",
|
||||
contact.getEmailAddress()));
|
||||
}
|
||||
if (!updatedContactOptional.get().isRegistryLockAllowed()) {
|
||||
throw new FormException(
|
||||
String.format(
|
||||
"Cannot remove the ability to use registry lock on the contact %s",
|
||||
contact.getEmailAddress()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the registrar contact whose phone number and email address is visible in domain WHOIS
|
||||
* query as abuse contact (if any).
|
||||
|
||||
@@ -120,7 +120,7 @@ public class SecurityAction extends ConsoleApiAction {
|
||||
}
|
||||
}
|
||||
} catch (InsecureCertificateException e) {
|
||||
setFailedResponse("Invalid certificate in parameter", SC_BAD_REQUEST);
|
||||
setFailedResponse(e.getMessage(), SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ final class DomainWhoisResponse extends WhoisResponseImpl {
|
||||
domain.getCurrentSponsorRegistrarId());
|
||||
Registrar registrar = registrarOptional.get();
|
||||
Optional<RegistrarPoc> abuseContact =
|
||||
registrar.getContactsFromReplica().stream()
|
||||
registrar.getPocsFromReplica().stream()
|
||||
.filter(RegistrarPoc::getVisibleInDomainWhoisAsAbuse)
|
||||
.findFirst();
|
||||
return WhoisResponseResults.create(
|
||||
|
||||
@@ -44,7 +44,7 @@ class RegistrarWhoisResponse extends WhoisResponseImpl {
|
||||
|
||||
@Override
|
||||
public WhoisResponseResults getResponse(boolean preferUnicode, String disclaimer) {
|
||||
Set<RegistrarPoc> contacts = registrar.getContactsFromReplica();
|
||||
Set<RegistrarPoc> contacts = registrar.getPocsFromReplica();
|
||||
String plaintext =
|
||||
new RegistrarEmitter()
|
||||
.emitField("Registrar", registrar.getRegistrarName())
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user