mirror of
https://github.com/google/nomulus
synced 2026-01-16 10:43:06 +00:00
Compare commits
10 Commits
proxy-2025
...
test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a67b04f3a | ||
|
|
9f191e9392 | ||
|
|
39c2a79898 | ||
|
|
e2e9d4cfc7 | ||
|
|
2948dcc1be | ||
|
|
c5644d5c8b | ||
|
|
514d24ed67 | ||
|
|
c6868b771b | ||
|
|
f34aec8b56 | ||
|
|
b27b077638 |
@@ -280,4 +280,15 @@ export class BackendService {
|
||||
`/console-api/registry-lock-verify?lockVerificationCode=${lockVerificationCode}`
|
||||
);
|
||||
}
|
||||
|
||||
requestRegistryLockPasswordReset(
|
||||
registrarId: string,
|
||||
registryLockEmail: string
|
||||
) {
|
||||
return this.http.post('/console-api/password-reset-request', {
|
||||
type: 'REGISTRY_LOCK',
|
||||
registrarId,
|
||||
registryLockEmail,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,15 @@
|
||||
roleToDescription(userDetails().role)
|
||||
}}</span>
|
||||
</mat-list-item>
|
||||
@if (userDetails().password) {
|
||||
@if (userDetails().registryLockEmailAddress) {
|
||||
<mat-divider></mat-divider>
|
||||
<mat-list-item role="listitem">
|
||||
<span class="console-app__list-key">Registry Lock email</span>
|
||||
<span class="console-app__list-value">{{
|
||||
userDetails().registryLockEmailAddress
|
||||
}}</span>
|
||||
</mat-list-item>
|
||||
} @if (userDetails().password) {
|
||||
<mat-divider></mat-divider>
|
||||
<mat-list-item role="listitem">
|
||||
<span class="console-app__list-key">Password</span>
|
||||
|
||||
@@ -35,5 +35,8 @@
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.console-app__list-key {
|
||||
width: 160px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,57 @@
|
||||
<form (ngSubmit)="saveEdit($event)" #form>
|
||||
<p *ngIf="isNew()">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label
|
||||
>User name prefix:
|
||||
<mat-icon
|
||||
matTooltip="Prefix will be combined with registrar ID to create a unique user name - {prefix}.{registrarId}@registry.google"
|
||||
>help_outline</mat-icon
|
||||
></mat-label
|
||||
>
|
||||
<input
|
||||
matInput
|
||||
minlength="3"
|
||||
maxlength="3"
|
||||
[required]="true"
|
||||
[(ngModel)]="user().emailAddress"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label
|
||||
>User Role:
|
||||
<mat-icon
|
||||
matTooltip="Viewer role doesn't allow making updates; Editor role allows updates, like Contacts delete or SSL certificate change"
|
||||
>help_outline</mat-icon
|
||||
></mat-label
|
||||
>
|
||||
<mat-select [(ngModel)]="user().role" name="userRole">
|
||||
<mat-option value="PRIMARY_CONTACT">Editor</mat-option>
|
||||
<mat-option value="ACCOUNT_MANAGER">Viewer</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<div class="console-app__user-edit">
|
||||
<form (ngSubmit)="saveEdit($event)" #form>
|
||||
<p *ngIf="isNew()">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label
|
||||
>User name prefix:
|
||||
<mat-icon
|
||||
matTooltip="Prefix will be combined with registrar ID to create a unique user name - {prefix}.{registrarId}@registry.google"
|
||||
>help_outline</mat-icon
|
||||
></mat-label
|
||||
>
|
||||
<input
|
||||
matInput
|
||||
minlength="3"
|
||||
maxlength="3"
|
||||
[required]="true"
|
||||
[(ngModel)]="user().emailAddress"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label
|
||||
>User Role:
|
||||
<mat-icon
|
||||
matTooltip="Viewer role doesn't allow making updates; Editor role allows updates, like Contacts delete or SSL certificate change"
|
||||
>help_outline</mat-icon
|
||||
></mat-label
|
||||
>
|
||||
<mat-select [(ngModel)]="user().role" name="userRole">
|
||||
<mat-option value="PRIMARY_CONTACT">Editor</mat-option>
|
||||
<mat-option value="ACCOUNT_MANAGER">Viewer</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
aria-label="Save user"
|
||||
type="submit"
|
||||
aria-label="Save changes to the user"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
@if(userDataService.userData()?.isAdmin) {
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
aria-label="Save user"
|
||||
type="submit"
|
||||
aria-label="Save changes to the user"
|
||||
aria-label="Reset registry lock password"
|
||||
(click)="requestRegistryLockPasswordReset()"
|
||||
>
|
||||
Save
|
||||
Reset registry lock password
|
||||
</button>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
.console-app__user-edit {
|
||||
button {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,56 @@ import {
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Inject,
|
||||
input,
|
||||
Output,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { User } from './users.service';
|
||||
import { User, UsersService } from './users.service';
|
||||
import { UserDataService } from '../shared/services/userData.service';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { RegistrarService } from '../registrar/registrar.service';
|
||||
import {
|
||||
MAT_DIALOG_DATA,
|
||||
MatDialog,
|
||||
MatDialogRef,
|
||||
} from '@angular/material/dialog';
|
||||
import { filter, switchMap, take } from 'rxjs';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-reset-lock-password-dialog',
|
||||
template: `
|
||||
<h2 mat-dialog-title>Please confirm the password reset:</h2>
|
||||
<mat-dialog-content>
|
||||
This will send a registry lock password reset email to
|
||||
{{ data.registryLockEmailAddress }}.
|
||||
</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 ResetRegistryLockPasswordComponent {
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<ResetRegistryLockPasswordComponent>,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
public data: { registryLockEmailAddress: string }
|
||||
) {}
|
||||
|
||||
onSave(): void {
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
|
||||
onCancel(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-edit-form',
|
||||
@@ -39,12 +82,22 @@ export class UserEditFormComponent {
|
||||
{
|
||||
emailAddress: '',
|
||||
role: 'ACCOUNT_MANAGER',
|
||||
registryLockEmailAddress: '',
|
||||
},
|
||||
{ transform: (user: User) => structuredClone(user) }
|
||||
);
|
||||
|
||||
@Output() onEditComplete = new EventEmitter<User>();
|
||||
|
||||
constructor(
|
||||
protected userDataService: UserDataService,
|
||||
private backendService: BackendService,
|
||||
private resetRegistryLockPasswordDialog: MatDialog,
|
||||
private registrarService: RegistrarService,
|
||||
private usersService: UsersService,
|
||||
private _snackBar: MatSnackBar
|
||||
) {}
|
||||
|
||||
saveEdit(e: SubmitEvent) {
|
||||
e.preventDefault();
|
||||
if (this.form.nativeElement.checkValidity()) {
|
||||
@@ -53,4 +106,34 @@ export class UserEditFormComponent {
|
||||
this.form.nativeElement.reportValidity();
|
||||
}
|
||||
}
|
||||
|
||||
sendRegistryLockPasswordResetRequest() {
|
||||
return this.backendService.requestRegistryLockPasswordReset(
|
||||
this.registrarService.registrarId(),
|
||||
this.user().registryLockEmailAddress!
|
||||
);
|
||||
}
|
||||
|
||||
requestRegistryLockPasswordReset() {
|
||||
const dialogRef = this.resetRegistryLockPasswordDialog.open(
|
||||
ResetRegistryLockPasswordComponent,
|
||||
{
|
||||
data: {
|
||||
registryLockEmailAddress: this.user().registryLockEmailAddress,
|
||||
},
|
||||
}
|
||||
);
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
filter((result) => !!result)
|
||||
)
|
||||
.pipe(switchMap((_) => this.sendRegistryLockPasswordResetRequest()))
|
||||
.subscribe({
|
||||
next: (_) => this.usersService.currentlyOpenUserEmail.set(''),
|
||||
error: (err: HttpErrorResponse) =>
|
||||
this._snackBar.open(err.error || err.message),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface User {
|
||||
emailAddress: string;
|
||||
role: string;
|
||||
password?: string;
|
||||
registryLockEmailAddress?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.util.Optional
|
||||
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id "org.flywaydb.flyway" version "11.0.1"
|
||||
id "org.flywaydb.flyway" version "9.22.3"
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package google.registry.export;
|
||||
|
||||
import static com.google.common.base.Verify.verifyNotNull;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS;
|
||||
import static google.registry.model.tld.Tlds.getTldsOfType;
|
||||
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
|
||||
@@ -75,7 +76,8 @@ public class ExportDomainListsAction implements Runnable {
|
||||
ON d.repo_id = gp.domain_repo_id
|
||||
WHERE d.tld = :tld
|
||||
AND d.deletion_time > CAST(:now AS timestamptz)
|
||||
ORDER BY d.domain_name""";
|
||||
ORDER BY d.domain_name
|
||||
""";
|
||||
|
||||
// This may be a CSV, but it is uses a .txt file extension for back-compatibility
|
||||
static final String REGISTERED_DOMAINS_FILENAME_FORMAT = "registered_domains_%s.txt";
|
||||
@@ -93,10 +95,7 @@ public class ExportDomainListsAction implements Runnable {
|
||||
logger.atInfo().log("Exporting domain lists for TLDs %s.", realTlds);
|
||||
|
||||
boolean includeDeletionTimes =
|
||||
tm().transact(
|
||||
() ->
|
||||
FeatureFlag.isActiveNowOrElse(
|
||||
FeatureFlag.FeatureName.INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS, false));
|
||||
tm().transact(() -> FeatureFlag.isActiveNow(INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS));
|
||||
realTlds.forEach(
|
||||
tld -> {
|
||||
List<String> domainsList =
|
||||
|
||||
@@ -19,6 +19,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyResourceDoesNotExist
|
||||
import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo;
|
||||
import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy;
|
||||
import static google.registry.model.EppResourceUtils.createRepoId;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -29,8 +30,10 @@ import google.registry.flows.FlowModule.RegistrarId;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.MutatingFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException;
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactCommand.Create;
|
||||
import google.registry.model.contact.ContactHistory;
|
||||
@@ -47,6 +50,7 @@ import org.joda.time.DateTime;
|
||||
* An EPP flow that creates a new contact.
|
||||
*
|
||||
* @error {@link google.registry.flows.FlowUtils.NotLoggedInException}
|
||||
* @error {@link ContactsProhibitedException}
|
||||
* @error {@link ResourceAlreadyExistsForThisClientException}
|
||||
* @error {@link ResourceCreateContentionException}
|
||||
* @error {@link ContactFlowUtils.BadInternationalizedPostalInfoException}
|
||||
@@ -69,6 +73,9 @@ public final class ContactCreateFlow implements MutatingFlow {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
extensionManager.validate();
|
||||
if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) {
|
||||
throw new ContactsProhibitedException();
|
||||
}
|
||||
Create command = (Create) resourceCommand;
|
||||
DateTime now = tm().getTransactionTime();
|
||||
verifyResourceDoesNotExist(Contact.class, targetId, now, registrarId);
|
||||
|
||||
@@ -24,6 +24,7 @@ import static google.registry.flows.ResourceFlowUtils.verifyOptionalAuthInfo;
|
||||
import static google.registry.flows.ResourceFlowUtils.verifyResourceOwnership;
|
||||
import static google.registry.flows.contact.ContactFlowUtils.validateAsciiPostalInfo;
|
||||
import static google.registry.flows.contact.ContactFlowUtils.validateContactAgainstPolicy;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.CONTACT_UPDATE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
@@ -35,7 +36,9 @@ import google.registry.flows.FlowModule.Superuser;
|
||||
import google.registry.flows.FlowModule.TargetId;
|
||||
import google.registry.flows.MutatingFlow;
|
||||
import google.registry.flows.annotations.ReportingSpec;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactCommand.Update;
|
||||
import google.registry.model.contact.ContactCommand.Update.Change;
|
||||
@@ -55,6 +58,7 @@ import org.joda.time.DateTime;
|
||||
/**
|
||||
* An EPP flow that updates a contact.
|
||||
*
|
||||
* @error {@link ContactsProhibitedException}
|
||||
* @error {@link google.registry.flows.FlowUtils.NotLoggedInException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.AddRemoveSameValueException}
|
||||
* @error {@link google.registry.flows.ResourceFlowUtils.ResourceDoesNotExistException}
|
||||
@@ -92,6 +96,9 @@ public final class ContactUpdateFlow implements MutatingFlow {
|
||||
extensionManager.register(MetadataExtension.class);
|
||||
validateRegistrarIsLoggedIn(registrarId);
|
||||
extensionManager.validate();
|
||||
if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) {
|
||||
throw new ContactsProhibitedException();
|
||||
}
|
||||
Update command = (Update) resourceCommand;
|
||||
DateTime now = tm().getTransactionTime();
|
||||
Contact existingContact = loadAndVerifyExistence(Contact.class, targetId, now);
|
||||
|
||||
@@ -72,7 +72,9 @@ import google.registry.flows.custom.DomainCreateFlowCustomLogic;
|
||||
import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseParameters;
|
||||
import google.registry.flows.custom.DomainCreateFlowCustomLogic.BeforeResponseReturnData;
|
||||
import google.registry.flows.custom.EntityChanges;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException;
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
@@ -147,6 +149,7 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainCreateFlow.NoGeneralRegistrationsInCurrentPhaseException}
|
||||
* @error {@link DomainCreateFlow.NoTrademarkedRegistrationsBeforeSunriseException}
|
||||
* @error {@link BulkDomainRegisteredForTooManyYearsException}
|
||||
* @error {@link ContactsProhibitedException}
|
||||
* @error {@link DomainCreateFlow.SignedMarksOnlyDuringSunriseException}
|
||||
* @error {@link DomainFlowTmchUtils.NoMarksFoundMatchingDomainException}
|
||||
* @error {@link DomainFlowTmchUtils.FoundMarkNotYetValidException}
|
||||
@@ -194,6 +197,7 @@ import org.joda.time.Duration;
|
||||
* @error {@link DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverAllowListException}
|
||||
* @error {@link DomainFlowUtils.PremiumNameBlockedException}
|
||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||
* @error {@link RegistrantProhibitedException}
|
||||
* @error {@link DomainFlowUtils.RegistrarMustBeActiveForThisOperationException}
|
||||
* @error {@link DomainFlowUtils.TldDoesNotExistException}
|
||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||
@@ -244,7 +248,7 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
verifyResourceDoesNotExist(Domain.class, targetId, now, registrarId);
|
||||
// Validate that this is actually a legal domain name on a TLD that the registrar has access to.
|
||||
InternetDomainName domainName = validateDomainName(command.getDomainName());
|
||||
String domainLabel = domainName.parts().get(0);
|
||||
String domainLabel = domainName.parts().getFirst();
|
||||
Tld tld = Tld.get(domainName.parent().toString());
|
||||
validateCreateCommandContactsAndNameservers(command, tld, domainName);
|
||||
TldState tldState = tld.getTldState(now);
|
||||
|
||||
@@ -25,7 +25,7 @@ import static com.google.common.collect.Sets.intersection;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.bsa.persistence.BsaLabelUtils.isLabelBlocked;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.isActiveNow;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.domain.Domain.MAX_REGISTRATION_YEARS;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
|
||||
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
@@ -75,11 +75,13 @@ import google.registry.flows.EppException.ParameterValueSyntaxErrorException;
|
||||
import google.registry.flows.EppException.RequiredParameterMissingException;
|
||||
import google.registry.flows.EppException.StatusProhibitsOperationException;
|
||||
import google.registry.flows.EppException.UnimplementedOptionException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.model.EppResource;
|
||||
import google.registry.model.billing.BillingBase.Flag;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.DesignatedContact.Type;
|
||||
@@ -478,27 +480,37 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void validateRequiredContactsPresentIfRequiredForDataset(
|
||||
/**
|
||||
* Enforces the presence/absence of contact data depending on the minimum data set migration
|
||||
* schedule.
|
||||
*/
|
||||
static void validateContactDataPresence(
|
||||
Optional<VKey<Contact>> registrant, Set<DesignatedContact> contacts)
|
||||
throws RequiredParameterMissingException {
|
||||
// TODO(b/353347632): Change this flag check to a registry config check.
|
||||
if (isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) {
|
||||
// Contacts are not required once we have begun the migration to the minimum dataset
|
||||
return;
|
||||
}
|
||||
if (registrant.isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
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)) {
|
||||
if (registrant.isPresent()) {
|
||||
throw new RegistrantProhibitedException();
|
||||
}
|
||||
if (!contacts.isEmpty()) {
|
||||
throw new ContactsProhibitedException();
|
||||
}
|
||||
} else if (!FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) {
|
||||
if (registrant.isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
|
||||
Set<Type> roles = new HashSet<>();
|
||||
for (DesignatedContact contact : contacts) {
|
||||
roles.add(contact.getType());
|
||||
}
|
||||
if (!roles.contains(Type.ADMIN)) {
|
||||
throw new MissingAdminContactException();
|
||||
}
|
||||
if (!roles.contains(Type.TECH)) {
|
||||
throw new MissingTechnicalContactException();
|
||||
Set<Type> roles = new HashSet<>();
|
||||
for (DesignatedContact contact : contacts) {
|
||||
roles.add(contact.getType());
|
||||
}
|
||||
if (!roles.contains(Type.ADMIN)) {
|
||||
throw new MissingAdminContactException();
|
||||
}
|
||||
if (!roles.contains(Type.TECH)) {
|
||||
throw new MissingTechnicalContactException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1042,8 +1054,7 @@ public class DomainFlowUtils {
|
||||
String tldStr = tld.getTldStr();
|
||||
validateRegistrantAllowedOnTld(tldStr, command.getRegistrantContactId());
|
||||
validateNoDuplicateContacts(command.getContacts());
|
||||
validateRequiredContactsPresentIfRequiredForDataset(
|
||||
command.getRegistrant(), command.getContacts());
|
||||
validateContactDataPresence(command.getRegistrant(), command.getContacts());
|
||||
ImmutableSet<String> hostNames = command.getNameserverHostNames();
|
||||
validateNameserversCountForTld(tldStr, domainName, hostNames.size());
|
||||
validateNameserversAllowedOnTld(tldStr, hostNames);
|
||||
@@ -1367,6 +1378,13 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** Having a registrant is prohibited by registry policy. */
|
||||
static class RegistrantProhibitedException extends ParameterValuePolicyErrorException {
|
||||
public RegistrantProhibitedException() {
|
||||
super("Having a registrant is prohibited by registry policy");
|
||||
}
|
||||
}
|
||||
|
||||
/** Admin contact is required. */
|
||||
static class MissingAdminContactException extends RequiredParameterMissingException {
|
||||
public MissingAdminContactException() {
|
||||
|
||||
@@ -30,6 +30,7 @@ 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;
|
||||
@@ -37,11 +38,10 @@ 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.validateRequiredContactsPresentIfRequiredForDataset;
|
||||
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;
|
||||
import static google.registry.model.common.FeatureFlag.isActiveNow;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_UPDATE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
@@ -64,9 +64,11 @@ import google.registry.flows.custom.DomainUpdateFlowCustomLogic.BeforeSaveParame
|
||||
import google.registry.flows.custom.EntityChanges;
|
||||
import google.registry.flows.domain.DomainFlowUtils.MissingRegistrantException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverAllowListException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
@@ -130,6 +132,7 @@ import org.joda.time.DateTime;
|
||||
* @error {@link NameserversNotSpecifiedForTldWithNameserverAllowListException}
|
||||
* @error {@link DomainFlowUtils.NotAuthorizedForTldException}
|
||||
* @error {@link DomainFlowUtils.RegistrantNotAllowedException}
|
||||
* @error {@link RegistrantProhibitedException}
|
||||
* @error {@link DomainFlowUtils.SecDnsAllUsageException}
|
||||
* @error {@link DomainFlowUtils.TooManyDsRecordsException}
|
||||
* @error {@link DomainFlowUtils.TooManyNameserversException}
|
||||
@@ -304,11 +307,12 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
|
||||
private Optional<VKey<Contact>> determineUpdatedRegistrant(Change change, Domain domain)
|
||||
throws EppException {
|
||||
// During phase 1 of minimum dataset transition, allow registrant to be removed
|
||||
// During or after the minimum dataset transition, allow registrant to be removed.
|
||||
if (change.getRegistrantContactId().isPresent()
|
||||
&& change.getRegistrantContactId().get().isEmpty()) {
|
||||
// TODO(b/353347632): Change this flag check to a registry config check.
|
||||
if (isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) {
|
||||
if (FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
|| FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
throw new MissingRegistrantException();
|
||||
@@ -325,8 +329,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
* cause Domain update failure.
|
||||
*/
|
||||
private static void validateNewState(Domain newDomain) throws EppException {
|
||||
validateRequiredContactsPresentIfRequiredForDataset(
|
||||
newDomain.getRegistrant(), newDomain.getContacts());
|
||||
validateContactDataPresence(newDomain.getRegistrant(), newDomain.getContacts());
|
||||
validateDsData(newDomain.getDsData());
|
||||
validateNameserversCountForTld(
|
||||
newDomain.getTld(),
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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.flows.exceptions;
|
||||
|
||||
import google.registry.flows.EppException.ParameterValuePolicyErrorException;
|
||||
|
||||
/** Having contacts is prohibited by registry policy */
|
||||
public class ContactsProhibitedException extends ParameterValuePolicyErrorException {
|
||||
public ContactsProhibitedException() {
|
||||
super("Having contacts is prohibited by registry policy");
|
||||
}
|
||||
}
|
||||
@@ -62,11 +62,31 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
INACTIVE
|
||||
}
|
||||
|
||||
/** The names of the feature flags that can be individually set. */
|
||||
public enum FeatureName {
|
||||
TEST_FEATURE,
|
||||
MINIMUM_DATASET_CONTACTS_OPTIONAL,
|
||||
MINIMUM_DATASET_CONTACTS_PROHIBITED,
|
||||
INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS
|
||||
/** Feature flag name used for testing only. */
|
||||
TEST_FEATURE(FeatureStatus.INACTIVE),
|
||||
|
||||
/** If we're not requiring the presence of contact data on domain EPP commands. */
|
||||
MINIMUM_DATASET_CONTACTS_OPTIONAL(FeatureStatus.INACTIVE),
|
||||
|
||||
/** If we're not permitting the presence of contact data on any EPP commands. */
|
||||
MINIMUM_DATASET_CONTACTS_PROHIBITED(FeatureStatus.INACTIVE),
|
||||
|
||||
/**
|
||||
* If we're including the upcoming domain drop date in the exported list of registered domains.
|
||||
*/
|
||||
INCLUDE_PENDING_DELETE_DATE_FOR_DOMAINS(FeatureStatus.INACTIVE);
|
||||
|
||||
private final FeatureStatus defaultStatus;
|
||||
|
||||
FeatureName(FeatureStatus defaultStatus) {
|
||||
this.defaultStatus = defaultStatus;
|
||||
}
|
||||
|
||||
FeatureStatus getDefaultStatus() {
|
||||
return this.defaultStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/** The name of the flag/feature. */
|
||||
@@ -155,24 +175,24 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
return status.getValueAtTime(time);
|
||||
}
|
||||
|
||||
/** Returns if the flag is active, or the default value if the flag does not exist. */
|
||||
public static boolean isActiveNowOrElse(FeatureName featureName, boolean defaultValue) {
|
||||
tm().assertInTransaction();
|
||||
return CACHE
|
||||
.get(featureName)
|
||||
.map(flag -> flag.getStatus(tm().getTransactionTime()).equals(ACTIVE))
|
||||
.orElse(defaultValue);
|
||||
}
|
||||
|
||||
/** Returns if the FeatureFlag with the given FeatureName is active now. */
|
||||
/**
|
||||
* Returns whether the flag is active now, or else the flag's default value if it doesn't exist.
|
||||
*/
|
||||
public static boolean isActiveNow(FeatureName featureName) {
|
||||
tm().assertInTransaction();
|
||||
return isActiveAt(featureName, tm().getTransactionTime());
|
||||
}
|
||||
|
||||
/** Returns if the FeatureFlag with the given FeatureName is active at a given time. */
|
||||
/**
|
||||
* Returns whether the flag is active at the given time, or else the flag's default value if it
|
||||
* doesn't exist.
|
||||
*/
|
||||
public static boolean isActiveAt(FeatureName featureName, DateTime dateTime) {
|
||||
return FeatureFlag.get(featureName).getStatus(dateTime).equals(ACTIVE);
|
||||
tm().assertInTransaction();
|
||||
return CACHE
|
||||
.get(featureName)
|
||||
.map(flag -> flag.getStatus(dateTime).equals(ACTIVE))
|
||||
.orElse(featureName.getDefaultStatus().equals(ACTIVE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,6 +16,8 @@ package google.registry.model.console;
|
||||
|
||||
/** Permissions that users may have in the UI, either per-registrar or globally. */
|
||||
public enum ConsolePermission {
|
||||
AUDIT_ACTIVITY_BY_USER,
|
||||
AUDIT_ACTIVITY_BY_REGISTRAR,
|
||||
/** View basic information about a registrar. */
|
||||
VIEW_REGISTRAR_DETAILS,
|
||||
/** Edit basic information about a registrar. */
|
||||
|
||||
@@ -55,6 +55,8 @@ public class ConsoleRoleDefinitions {
|
||||
new ImmutableSet.Builder<ConsolePermission>()
|
||||
.addAll(SUPPORT_AGENT_PERMISSIONS)
|
||||
.add(
|
||||
ConsolePermission.AUDIT_ACTIVITY_BY_USER,
|
||||
ConsolePermission.AUDIT_ACTIVITY_BY_REGISTRAR,
|
||||
ConsolePermission.MANAGE_REGISTRARS,
|
||||
ConsolePermission.GET_REGISTRANT_EMAIL,
|
||||
ConsolePermission.SUSPEND_DOMAIN,
|
||||
@@ -111,6 +113,7 @@ public class ConsoleRoleDefinitions {
|
||||
new ImmutableSet.Builder<ConsolePermission>()
|
||||
.addAll(TECH_CONTACT_PERMISSIONS)
|
||||
.add(ConsolePermission.MANAGE_USERS)
|
||||
.add(ConsolePermission.AUDIT_ACTIVITY_BY_REGISTRAR)
|
||||
.build();
|
||||
|
||||
private ConsoleRoleDefinitions() {}
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.model.console;
|
||||
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.annotations.IdAllocation;
|
||||
@@ -45,6 +46,7 @@ public class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
|
||||
@Id @IdAllocation @Column Long revisionId;
|
||||
|
||||
@Column(nullable = false)
|
||||
@Expose
|
||||
DateTime modificationTime;
|
||||
|
||||
/** The HTTP method (e.g. POST, PUT) used to make this modification. */
|
||||
@@ -54,6 +56,7 @@ public class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
|
||||
/** The type of modification. */
|
||||
@Column(nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Expose
|
||||
Type type;
|
||||
|
||||
/** The URL of the action that was used to make the modification. */
|
||||
@@ -61,11 +64,12 @@ public class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
|
||||
String url;
|
||||
|
||||
/** An optional further description of the action. */
|
||||
String description;
|
||||
@Expose String description;
|
||||
|
||||
/** The user that performed the modification. */
|
||||
@JoinColumn(name = "actingUser", referencedColumnName = "emailAddress", nullable = false)
|
||||
@ManyToOne
|
||||
@Expose
|
||||
User actingUser;
|
||||
|
||||
public Long getRevisionId() {
|
||||
@@ -102,18 +106,24 @@ public class ConsoleUpdateHistory extends ImmutableObject implements Buildable {
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
DUM_DOWNLOAD,
|
||||
DOMAIN_DELETE,
|
||||
DOMAIN_SUSPEND,
|
||||
DOMAIN_UNSUSPEND,
|
||||
EPP_PASSWORD_UPDATE,
|
||||
REGISTRAR_CREATE,
|
||||
REGISTRAR_CONTACTS_UPDATE,
|
||||
REGISTRAR_SECURITY_UPDATE,
|
||||
REGISTRAR_UPDATE,
|
||||
REGISTRY_LOCK,
|
||||
REGISTRY_UNLOCK,
|
||||
USER_CREATE,
|
||||
USER_DELETE,
|
||||
USER_UPDATE
|
||||
USER_UPDATE,
|
||||
}
|
||||
|
||||
public static final String DESCRIPTION_SEPARATOR = "|";
|
||||
|
||||
public static class Builder extends Buildable.Builder<ConsoleUpdateHistory> {
|
||||
public Builder() {}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
@Id @Expose String emailAddress;
|
||||
|
||||
/** Optional external email address to use for registry lock confirmation emails. */
|
||||
@Column String registryLockEmailAddress;
|
||||
@Column @Expose String registryLockEmailAddress;
|
||||
|
||||
/** Roles (which grant permissions) associated with this user. */
|
||||
@Expose
|
||||
@@ -250,51 +250,50 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<? extends User, ?> asBuilder() {
|
||||
return new Builder<>(clone(this));
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
/** Builder for constructing immutable {@link User} objects. */
|
||||
public static class Builder<T extends User, B extends Builder<T, B>>
|
||||
extends GenericBuilder<T, B> {
|
||||
public static class Builder extends Buildable.Builder<User> {
|
||||
|
||||
public Builder() {}
|
||||
|
||||
public Builder(T abstractUser) {
|
||||
super(abstractUser);
|
||||
public Builder(User user) {
|
||||
super(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T build() {
|
||||
public User build() {
|
||||
checkArgumentNotNull(getInstance().emailAddress, "Email address cannot be null");
|
||||
checkArgumentNotNull(getInstance().userRoles, "User roles cannot be null");
|
||||
return super.build();
|
||||
}
|
||||
|
||||
public B setEmailAddress(String emailAddress) {
|
||||
public Builder setEmailAddress(String emailAddress) {
|
||||
getInstance().emailAddress = checkValidEmail(emailAddress);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
|
||||
public Builder setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
|
||||
getInstance().registryLockEmailAddress =
|
||||
registryLockEmailAddress == null ? null : checkValidEmail(registryLockEmailAddress);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setUserRoles(UserRoles userRoles) {
|
||||
public Builder setUserRoles(UserRoles userRoles) {
|
||||
checkArgumentNotNull(userRoles, "User roles cannot be null");
|
||||
getInstance().userRoles = userRoles;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B removeRegistryLockPassword() {
|
||||
public Builder removeRegistryLockPassword() {
|
||||
getInstance().registryLockPasswordHash = null;
|
||||
getInstance().registryLockPasswordSalt = null;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistryLockPassword(String registryLockPassword) {
|
||||
public Builder setRegistryLockPassword(String registryLockPassword) {
|
||||
checkArgument(
|
||||
getInstance().hasAnyRegistryLockPermission(), "User has no registry lock permission");
|
||||
checkArgument(
|
||||
@@ -304,7 +303,7 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
byte[] salt = SALT_SUPPLIER.get();
|
||||
getInstance().registryLockPasswordSalt = base64().encode(salt);
|
||||
getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import static google.registry.util.CollectionUtils.forceEmptyToNull;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.Buildable.GenericBuilder;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
@@ -77,8 +77,7 @@ public class FeeCheckResponseExtensionItemCommandV12 extends ImmutableObject {
|
||||
}
|
||||
|
||||
/** Builder for {@link FeeCheckResponseExtensionItemCommandV12}. */
|
||||
public static class Builder
|
||||
extends GenericBuilder<FeeCheckResponseExtensionItemCommandV12, Builder> {
|
||||
public static class Builder extends Buildable.Builder<FeeCheckResponseExtensionItemCommandV12> {
|
||||
|
||||
public Builder setCommandName(CommandName commandName) {
|
||||
getInstance().commandName = Ascii.toLowerCase(commandName.name());
|
||||
|
||||
@@ -690,8 +690,8 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<? extends Registrar, ?> asBuilder() {
|
||||
return new Builder<>(clone(this));
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -706,59 +706,58 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link Registrar}, since it is immutable. */
|
||||
public static class Builder<T extends Registrar, B extends Builder<T, B>>
|
||||
extends GenericBuilder<T, B> {
|
||||
public static class Builder extends Buildable.Builder<Registrar> {
|
||||
public Builder() {}
|
||||
|
||||
public Builder(T instance) {
|
||||
public Builder(Registrar instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
public B setRegistrarId(String registrarId) {
|
||||
public Builder setRegistrarId(String registrarId) {
|
||||
// Registrar id must be [3,16] chars long. See "clIDType" in the base EPP schema of RFC 5730.
|
||||
// (Need to validate this here as there's no matching EPP XSD for validation.)
|
||||
checkArgument(
|
||||
Range.closed(3, 16).contains(registrarId.length()),
|
||||
"Registrar ID must be 3-16 characters long.");
|
||||
getInstance().registrarId = registrarId;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setIanaIdentifier(@Nullable Long ianaIdentifier) {
|
||||
public Builder setIanaIdentifier(@Nullable Long ianaIdentifier) {
|
||||
checkArgument(
|
||||
ianaIdentifier == null || ianaIdentifier > 0, "IANA ID must be a positive number");
|
||||
getInstance().ianaIdentifier = ianaIdentifier;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setPoNumber(Optional<String> poNumber) {
|
||||
public Builder setPoNumber(Optional<String> poNumber) {
|
||||
getInstance().poNumber = poNumber.orElse(null);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setBillingAccountMap(@Nullable Map<CurrencyUnit, String> billingAccountMap) {
|
||||
public Builder setBillingAccountMap(@Nullable Map<CurrencyUnit, String> billingAccountMap) {
|
||||
getInstance().billingAccountMap = nullToEmptyImmutableCopy(billingAccountMap);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistrarName(String registrarName) {
|
||||
public Builder setRegistrarName(String registrarName) {
|
||||
getInstance().registrarName = registrarName;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setType(Type type) {
|
||||
public Builder setType(Type type) {
|
||||
getInstance().type = type;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setState(State state) {
|
||||
public Builder setState(State state) {
|
||||
getInstance().state = state;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setAllowedTlds(Set<String> allowedTlds) {
|
||||
public Builder setAllowedTlds(Set<String> allowedTlds) {
|
||||
getInstance().allowedTlds = ImmutableSortedSet.copyOf(assertTldsExist(allowedTlds));
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -771,7 +770,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* {@code .now()} when saving the Registry entity to make sure it's actually saved before trying
|
||||
* to set the allowed TLDs.
|
||||
*/
|
||||
public B setAllowedTldsUncached(Set<String> allowedTlds) {
|
||||
public Builder setAllowedTldsUncached(Set<String> allowedTlds) {
|
||||
ImmutableSet<VKey<Tld>> newTldKeys =
|
||||
Sets.difference(allowedTlds, getInstance().getAllowedTlds()).stream()
|
||||
.map(Tld::createVKey)
|
||||
@@ -780,10 +779,10 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
Sets.difference(newTldKeys, tm().loadByKeysIfPresent(newTldKeys).keySet());
|
||||
checkArgument(missingTldKeys.isEmpty(), "Trying to set nonexistent TLDs: %s", missingTldKeys);
|
||||
getInstance().allowedTlds = ImmutableSortedSet.copyOf(allowedTlds);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setClientCertificate(String clientCertificate, DateTime now) {
|
||||
public Builder setClientCertificate(String clientCertificate, DateTime now) {
|
||||
clientCertificate = emptyToNull(clientCertificate);
|
||||
String clientCertificateHash = calculateHash(clientCertificate);
|
||||
if (!Objects.equals(clientCertificate, getInstance().clientCertificate)
|
||||
@@ -792,23 +791,23 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
getInstance().clientCertificateHash = clientCertificateHash;
|
||||
getInstance().lastCertificateUpdateTime = now;
|
||||
}
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setLastExpiringCertNotificationSentDate(DateTime now) {
|
||||
public Builder setLastExpiringCertNotificationSentDate(DateTime now) {
|
||||
checkArgumentNotNull(now, "Registrar lastExpiringCertNotificationSentDate cannot be null");
|
||||
getInstance().lastExpiringCertNotificationSentDate = now;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setLastExpiringFailoverCertNotificationSentDate(DateTime now) {
|
||||
public Builder setLastExpiringFailoverCertNotificationSentDate(DateTime now) {
|
||||
checkArgumentNotNull(
|
||||
now, "Registrar lastExpiringFailoverCertNotificationSentDate cannot be null");
|
||||
getInstance().lastExpiringFailoverCertNotificationSentDate = now;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setFailoverClientCertificate(String clientCertificate, DateTime now) {
|
||||
public Builder setFailoverClientCertificate(String clientCertificate, DateTime now) {
|
||||
clientCertificate = emptyToNull(clientCertificate);
|
||||
String clientCertificateHash = calculateHash(clientCertificate);
|
||||
if (!Objects.equals(clientCertificate, getInstance().failoverClientCertificate)
|
||||
@@ -817,13 +816,13 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
getInstance().failoverClientCertificateHash = clientCertificateHash;
|
||||
getInstance().lastCertificateUpdateTime = now;
|
||||
}
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setLastPocVerificationDate(DateTime now) {
|
||||
public Builder setLastPocVerificationDate(DateTime now) {
|
||||
checkArgumentNotNull(now, "Registrar lastPocVerificationDate cannot be null");
|
||||
getInstance().lastPocVerificationDate = now;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
private static String calculateHash(String clientCertificate) {
|
||||
@@ -855,75 +854,75 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
Objects.equals(newInstance.ianaIdentifier, registrar.getIanaIdentifier()));
|
||||
}
|
||||
|
||||
public B setContactsRequireSyncing(boolean contactsRequireSyncing) {
|
||||
public Builder setContactsRequireSyncing(boolean contactsRequireSyncing) {
|
||||
getInstance().contactsRequireSyncing = contactsRequireSyncing;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setIpAddressAllowList(Iterable<CidrAddressBlock> ipAddressAllowList) {
|
||||
public Builder setIpAddressAllowList(Iterable<CidrAddressBlock> ipAddressAllowList) {
|
||||
getInstance().ipAddressAllowList = ImmutableList.copyOf(ipAddressAllowList);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setLocalizedAddress(RegistrarAddress localizedAddress) {
|
||||
public Builder setLocalizedAddress(RegistrarAddress localizedAddress) {
|
||||
getInstance().localizedAddress = localizedAddress;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setInternationalizedAddress(RegistrarAddress internationalizedAddress) {
|
||||
public Builder setInternationalizedAddress(RegistrarAddress internationalizedAddress) {
|
||||
getInstance().internationalizedAddress = internationalizedAddress;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setPhoneNumber(String phoneNumber) {
|
||||
public Builder setPhoneNumber(String phoneNumber) {
|
||||
getInstance().phoneNumber = (phoneNumber == null) ? null : checkValidPhoneNumber(phoneNumber);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setFaxNumber(String faxNumber) {
|
||||
public Builder setFaxNumber(String faxNumber) {
|
||||
getInstance().faxNumber = (faxNumber == null) ? null : checkValidPhoneNumber(faxNumber);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setEmailAddress(String emailAddress) {
|
||||
public Builder setEmailAddress(String emailAddress) {
|
||||
getInstance().emailAddress = checkValidEmail(emailAddress);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setWhoisServer(String whoisServer) {
|
||||
public Builder setWhoisServer(String whoisServer) {
|
||||
getInstance().whoisServer = whoisServer;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRdapBaseUrls(Set<String> rdapBaseUrls) {
|
||||
public Builder setRdapBaseUrls(Set<String> rdapBaseUrls) {
|
||||
getInstance().rdapBaseUrls = ImmutableSet.copyOf(rdapBaseUrls);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setBlockPremiumNames(boolean blockPremiumNames) {
|
||||
public Builder setBlockPremiumNames(boolean blockPremiumNames) {
|
||||
getInstance().blockPremiumNames = blockPremiumNames;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setUrl(String url) {
|
||||
public Builder setUrl(String url) {
|
||||
getInstance().url = url;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setIcannReferralEmail(String icannReferralEmail) {
|
||||
public Builder setIcannReferralEmail(String icannReferralEmail) {
|
||||
getInstance().icannReferralEmail = checkValidEmail(icannReferralEmail);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setDriveFolderId(@Nullable String driveFolderId) {
|
||||
public Builder setDriveFolderId(@Nullable String driveFolderId) {
|
||||
checkArgument(
|
||||
driveFolderId == null || !driveFolderId.contains("/"),
|
||||
"Drive folder ID must not be a full URL");
|
||||
getInstance().driveFolderId = driveFolderId;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setPassword(String password) {
|
||||
public Builder setPassword(String password) {
|
||||
// Passwords must be [6,16] chars long. See "pwType" in the base EPP schema of RFC 5730.
|
||||
checkArgument(
|
||||
Range.closed(6, 16).contains(nullToEmpty(password).length()),
|
||||
@@ -931,7 +930,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
byte[] salt = SALT_SUPPLIER.get();
|
||||
getInstance().salt = base64().encode(salt);
|
||||
getInstance().passwordHash = hashPassword(password, salt);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -939,18 +938,18 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
*
|
||||
* @throws IllegalArgumentException if provided passcode is not 5-digit numeric
|
||||
*/
|
||||
public B setPhonePasscode(String phonePasscode) {
|
||||
public Builder setPhonePasscode(String phonePasscode) {
|
||||
checkArgument(
|
||||
phonePasscode == null || PHONE_PASSCODE_PATTERN.matcher(phonePasscode).matches(),
|
||||
"Not a valid telephone passcode (must be 5 digits long): %s",
|
||||
phonePasscode);
|
||||
getInstance().phonePasscode = phonePasscode;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistryLockAllowed(boolean registryLockAllowed) {
|
||||
public Builder setRegistryLockAllowed(boolean registryLockAllowed) {
|
||||
getInstance().registryLockAllowed = registryLockAllowed;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -958,14 +957,14 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* and breaks the verification that an object has not been updated since it was copied.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public B setLastUpdateTime(DateTime timestamp) {
|
||||
public Builder setLastUpdateTime(DateTime timestamp) {
|
||||
getInstance().setUpdateTimestamp(UpdateAutoTimestamp.create(timestamp));
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Build the registrar, nullifying empty fields. */
|
||||
@Override
|
||||
public T build() {
|
||||
public Registrar build() {
|
||||
checkArgumentNotNull(getInstance().type, "Registrar type cannot be null");
|
||||
checkArgumentNotNull(getInstance().registrarName, "Registrar name cannot be null");
|
||||
checkArgument(
|
||||
|
||||
@@ -31,7 +31,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.Buildable.GenericBuilder;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.JsonMapBuilder;
|
||||
import google.registry.model.Jsonifiable;
|
||||
@@ -225,8 +225,8 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
return visibleInDomainWhoisAsAbuse;
|
||||
}
|
||||
|
||||
public Builder<? extends RegistrarPoc, ?> asBuilder() {
|
||||
return new Builder<>(clone(this));
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
public boolean isAllowedToSetRegistryLockPassword() {
|
||||
@@ -332,17 +332,16 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
}
|
||||
|
||||
/** A builder for constructing a {@link RegistrarPoc}, since it is immutable. */
|
||||
public static class Builder<T extends RegistrarPoc, B extends Builder<T, B>>
|
||||
extends GenericBuilder<T, B> {
|
||||
public static class Builder extends Buildable.Builder<RegistrarPoc> {
|
||||
public Builder() {}
|
||||
|
||||
protected Builder(T instance) {
|
||||
protected Builder(RegistrarPoc instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
/** Build the registrar, nullifying empty fields. */
|
||||
@Override
|
||||
public T build() {
|
||||
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
|
||||
@@ -356,71 +355,71 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
return cloneEmptyToNull(super.build());
|
||||
}
|
||||
|
||||
public B setName(String name) {
|
||||
public Builder setName(String name) {
|
||||
getInstance().name = name;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setEmailAddress(String emailAddress) {
|
||||
public Builder setEmailAddress(String emailAddress) {
|
||||
getInstance().emailAddress = emailAddress;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
|
||||
public Builder setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
|
||||
getInstance().registryLockEmailAddress = registryLockEmailAddress;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setPhoneNumber(String phoneNumber) {
|
||||
public Builder setPhoneNumber(String phoneNumber) {
|
||||
getInstance().phoneNumber = phoneNumber;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistrarId(String registrarId) {
|
||||
public Builder setRegistrarId(String registrarId) {
|
||||
getInstance().registrarId = registrarId;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistrar(Registrar registrar) {
|
||||
public Builder setRegistrar(Registrar registrar) {
|
||||
getInstance().registrarId = registrar.getRegistrarId();
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setFaxNumber(String faxNumber) {
|
||||
public Builder setFaxNumber(String faxNumber) {
|
||||
getInstance().faxNumber = faxNumber;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setTypes(Iterable<Type> types) {
|
||||
public Builder setTypes(Iterable<Type> types) {
|
||||
getInstance().types = ImmutableSet.copyOf(types);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setVisibleInWhoisAsAdmin(boolean visible) {
|
||||
public Builder setVisibleInWhoisAsAdmin(boolean visible) {
|
||||
getInstance().visibleInWhoisAsAdmin = visible;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setVisibleInWhoisAsTech(boolean visible) {
|
||||
public Builder setVisibleInWhoisAsTech(boolean visible) {
|
||||
getInstance().visibleInWhoisAsTech = visible;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setVisibleInDomainWhoisAsAbuse(boolean visible) {
|
||||
public Builder setVisibleInDomainWhoisAsAbuse(boolean visible) {
|
||||
getInstance().visibleInDomainWhoisAsAbuse = visible;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setAllowedToSetRegistryLockPassword(boolean allowedToSetRegistryLockPassword) {
|
||||
public Builder setAllowedToSetRegistryLockPassword(boolean allowedToSetRegistryLockPassword) {
|
||||
if (allowedToSetRegistryLockPassword) {
|
||||
getInstance().registryLockPasswordSalt = null;
|
||||
getInstance().registryLockPasswordHash = null;
|
||||
}
|
||||
getInstance().allowedToSetRegistryLockPassword = allowedToSetRegistryLockPassword;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistryLockPassword(String registryLockPassword) {
|
||||
public Builder setRegistryLockPassword(String registryLockPassword) {
|
||||
checkArgument(
|
||||
getInstance().allowedToSetRegistryLockPassword,
|
||||
"Not allowed to set registry lock password for this contact");
|
||||
@@ -430,7 +429,7 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
getInstance().registryLockPasswordSalt = base64().encode(salt);
|
||||
getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt);
|
||||
getInstance().allowedToSetRegistryLockPassword = false;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,6 +115,7 @@ import google.registry.ui.server.console.ConsoleDomainGetAction;
|
||||
import google.registry.ui.server.console.ConsoleDomainListAction;
|
||||
import google.registry.ui.server.console.ConsoleDumDownloadAction;
|
||||
import google.registry.ui.server.console.ConsoleEppPasswordAction;
|
||||
import google.registry.ui.server.console.ConsoleHistoryDataAction;
|
||||
import google.registry.ui.server.console.ConsoleModule;
|
||||
import google.registry.ui.server.console.ConsoleOteAction;
|
||||
import google.registry.ui.server.console.ConsoleRegistryLockAction;
|
||||
@@ -122,6 +123,8 @@ import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
|
||||
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
|
||||
import google.registry.ui.server.console.ConsoleUserDataAction;
|
||||
import google.registry.ui.server.console.ConsoleUsersAction;
|
||||
import google.registry.ui.server.console.PasswordResetRequestAction;
|
||||
import google.registry.ui.server.console.PasswordResetVerifyAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
import google.registry.ui.server.console.domains.ConsoleBulkDomainAction;
|
||||
import google.registry.ui.server.console.settings.ContactAction;
|
||||
@@ -183,6 +186,8 @@ interface RequestComponent {
|
||||
|
||||
ConsoleEppPasswordAction consoleEppPasswordAction();
|
||||
|
||||
ConsoleHistoryDataAction consoleHistoryDataAction();
|
||||
|
||||
ConsoleOteAction consoleOteAction();
|
||||
|
||||
ConsoleRegistryLockAction consoleRegistryLockAction();
|
||||
@@ -249,6 +254,10 @@ interface RequestComponent {
|
||||
|
||||
NordnVerifyAction nordnVerifyAction();
|
||||
|
||||
PasswordResetRequestAction passwordResetRequestAction();
|
||||
|
||||
PasswordResetVerifyAction passwordResetVerifyAction();
|
||||
|
||||
PublishDnsUpdatesAction publishDnsUpdatesAction();
|
||||
|
||||
PublishInvoicesAction uploadInvoicesAction();
|
||||
@@ -281,6 +290,8 @@ interface RequestComponent {
|
||||
|
||||
RdapNameserverSearchAction rdapNameserverSearchAction();
|
||||
|
||||
RdapRegistrarFieldsAction rdapRegistrarFieldsAction();
|
||||
|
||||
RdeReportAction rdeReportAction();
|
||||
|
||||
RdeReporter rdeReporter();
|
||||
@@ -332,9 +343,7 @@ interface RequestComponent {
|
||||
WhoisAction whoisAction();
|
||||
|
||||
WhoisHttpAction whoisHttpAction();
|
||||
|
||||
RdapRegistrarFieldsAction rdapRegistrarFieldsAction();
|
||||
|
||||
|
||||
WipeOutContactHistoryPiiAction wipeOutContactHistoryPiiAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
|
||||
@@ -38,6 +38,8 @@ import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
|
||||
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
|
||||
import google.registry.ui.server.console.ConsoleUserDataAction;
|
||||
import google.registry.ui.server.console.ConsoleUsersAction;
|
||||
import google.registry.ui.server.console.PasswordResetRequestAction;
|
||||
import google.registry.ui.server.console.PasswordResetVerifyAction;
|
||||
import google.registry.ui.server.console.RegistrarsAction;
|
||||
import google.registry.ui.server.console.domains.ConsoleBulkDomainAction;
|
||||
import google.registry.ui.server.console.settings.ContactAction;
|
||||
@@ -84,6 +86,12 @@ public interface FrontendRequestComponent {
|
||||
|
||||
FlowComponent.Builder flowComponentBuilder();
|
||||
|
||||
PasswordResetRequestAction passwordResetRequestAction();
|
||||
|
||||
PasswordResetVerifyAction passwordResetVerifyAction();
|
||||
|
||||
RdapRegistrarFieldsAction rdapRegistrarFieldsAction();
|
||||
|
||||
ReadinessProbeActionFrontend readinessProbeActionFrontend();
|
||||
|
||||
ReadinessProbeConsoleAction readinessProbeConsoleAction();
|
||||
@@ -92,8 +100,6 @@ public interface FrontendRequestComponent {
|
||||
|
||||
SecurityAction securityAction();
|
||||
|
||||
RdapRegistrarFieldsAction rdapRegistrarFieldsAction();
|
||||
|
||||
@Subcomponent.Builder
|
||||
abstract class Builder implements RequestComponentBuilder<FrontendRequestComponent> {
|
||||
@Override public abstract Builder requestModule(RequestModule requestModule);
|
||||
|
||||
@@ -55,7 +55,7 @@ import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/**
|
||||
* Base RDAP (new WHOIS) action for all requests.
|
||||
* Base RDAP action for all requests.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
@@ -138,7 +138,7 @@ public abstract class RdapActionBase implements Runnable {
|
||||
// RFC7480 4.2 - servers receiving an RDAP request return an entity with a Content-Type header
|
||||
// containing the RDAP-specific JSON media type.
|
||||
response.setContentType(RESPONSE_MEDIA_TYPE);
|
||||
// RDAP Technical Implementation Guide 1.13 - when responding to RDAP valid requests, we MUST
|
||||
// RDAP Technical Implementation Guide 1.14 - when responding to RDAP valid requests, we MUST
|
||||
// include the Access-Control-Allow-Origin, which MUST be "*" unless otherwise specified.
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN, "*");
|
||||
try {
|
||||
|
||||
@@ -26,7 +26,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for RDAP autonomous system number requests.
|
||||
* RDAP action for RDAP autonomous system number requests.
|
||||
*
|
||||
* <p>This feature is not implemented because it's only necessary for <i>address</i> registries like
|
||||
* ARIN, not domain registries.
|
||||
|
||||
@@ -41,14 +41,13 @@ final class RdapDataStructures {
|
||||
// Conformance to RFC 9083
|
||||
jsonArray.add("rdap_level_0");
|
||||
|
||||
// Conformance to the RDAP Response Profile V2.1
|
||||
// Conformance to the RDAP Response Profile V2.2 (February 2024)
|
||||
// (see section 1.2)
|
||||
jsonArray.add("icann_rdap_response_profile_1");
|
||||
|
||||
// Conformance to the RDAP Technical Implementation Guide V2.2 (February 2024)
|
||||
// (see section 1.3)
|
||||
jsonArray.add("icann_rdap_response_profile_0");
|
||||
|
||||
// Conformance to the RDAP Technical Implementation Guide V2.1
|
||||
// (see section 1.14)
|
||||
jsonArray.add("icann_rdap_technical_implementation_guide_0");
|
||||
|
||||
jsonArray.add("icann_rdap_technical_implementation_guide_1");
|
||||
return jsonArray;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/** RDAP (new WHOIS) action for domain requests. */
|
||||
/** RDAP action for domain requests. */
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/domain/",
|
||||
|
||||
@@ -51,6 +51,7 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.NonFinalForTesting;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.Query;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Comparator;
|
||||
@@ -60,17 +61,15 @@ import java.util.stream.Stream;
|
||||
import org.hibernate.Hibernate;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for domain search requests.
|
||||
* RDAP action for domain search requests.
|
||||
*
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in STD 95 and its RFCs.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9083">RFC 9083: JSON Responses for the Registration
|
||||
* Data Access Protocol (RDAP)</a>
|
||||
*/
|
||||
// TODO: This isn't required by the RDAP Technical Implementation Guide, and hence should be
|
||||
// deleted, at least until it's actually required.
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/domains",
|
||||
@@ -442,7 +441,7 @@ public class RdapDomainSearchAction extends RdapSearchActionBase {
|
||||
replicaTm()
|
||||
.transact(
|
||||
() -> {
|
||||
jakarta.persistence.Query query =
|
||||
Query query =
|
||||
replicaTm()
|
||||
.getEntityManager()
|
||||
.createNativeQuery(queryBuilder.toString())
|
||||
|
||||
@@ -37,14 +37,12 @@ import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for entity (contact and registrar) requests. the ICANN operational
|
||||
* profile dictates that the "handle" for registrars is to be the IANA registrar ID:
|
||||
* RDAP action for entity (contact and registrar) requests. the ICANN operational profile dictates
|
||||
* that the "handle" for registrars is to be the IANA registrar ID:
|
||||
*
|
||||
* <p>2.8.3. Registries MUST support lookup for entities with the registrar role within other
|
||||
* objects using the handle (as described in 3.1.5 of RFC 9082). The handle of the entity with the
|
||||
* registrar role MUST be equal to IANA Registrar ID. The entity with the registrar role in the RDAP
|
||||
* response MUST contain a publicIDs member to identify the IANA Registrar ID from the IANA’s
|
||||
* Registrar ID registry. The type value of the publicID object MUST be equal to IANA Registrar ID.
|
||||
* <p>2.4.1.Registry RDAP servers MUST support Registrar object lookup using an entity path request
|
||||
* for entities with the registrar role using the handle (as described in 3.1.5 of RFC9082) where
|
||||
* the handle of the entity with the registrar role is be [sic] equal to the IANA Registrar ID.
|
||||
*/
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
@@ -104,7 +102,7 @@ public class RdapEntityAction extends RdapActionBase {
|
||||
// query, it MUST reply with 404 response code.
|
||||
//
|
||||
// Note we don't do RFC7480 5.3 - returning a different code if we wish to say "this info
|
||||
// exists but we don't want to show it to you", because we DON'T wish to say that.
|
||||
// exists, but we don't want to show it to you", because we DON'T wish to say that.
|
||||
throw new NotFoundException(pathSearchString + " not found");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for entity (contact and registrar) search requests.
|
||||
* RDAP action for entity (contact and registrar) search requests.
|
||||
*
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in STD 95 and its RFCs.
|
||||
*
|
||||
* <p>The RDAP specification lumps contacts and registrars together and calls them "entities", which
|
||||
* is confusing for us, because "entity" means something else in SQL. But here, when we use the
|
||||
@@ -76,8 +76,6 @@ import java.util.Optional;
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9083">RFC 9083: JSON Responses for the Registration
|
||||
* Data Access Protocol (RDAP)</a>
|
||||
*/
|
||||
// TODO: This isn't required by the RDAP Technical Implementation Guide, and hence should be
|
||||
// deleted, at least until it's actually required.
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/entities",
|
||||
|
||||
@@ -28,7 +28,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/** RDAP (new WHOIS) action for help requests. */
|
||||
/** RDAP action for help requests. */
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = RdapHelpAction.PATH,
|
||||
|
||||
@@ -22,26 +22,18 @@ import google.registry.rdap.RdapDataStructures.Remark;
|
||||
/**
|
||||
* This file contains boilerplate required by the ICANN RDAP Profile.
|
||||
*
|
||||
* @see <a href="https://www.icann.org/resources/pages/rdap-operational-profile-2016-07-26-en">RDAP
|
||||
* Operational Profile for gTLD Registries and Registrars</a>
|
||||
* @see <a
|
||||
* href="https://itp.cdn.icann.org/en/files/registry-operators/rdap-response-profile-21feb24-en.pdf">
|
||||
* RDAP Response Profile</a>
|
||||
*/
|
||||
public class RdapIcannStandardInformation {
|
||||
|
||||
/** Required by ICANN RDAP Profile section 1.4.10. */
|
||||
private static final Notice CONFORMANCE_NOTICE =
|
||||
Notice.builder()
|
||||
.setDescription(
|
||||
"This response conforms to the RDAP Operational Profile for gTLD Registries and"
|
||||
+ " Registrars version 1.0")
|
||||
.build();
|
||||
|
||||
/** Required by ICANN RDAP Profile section 1.5.18. */
|
||||
/** Required by RDAP Response Profile section 2.6.3. */
|
||||
private static final Notice DOMAIN_STATUS_CODES_NOTICE =
|
||||
Notice.builder()
|
||||
.setTitle("Status Codes")
|
||||
.setDescription(
|
||||
"For more information on domain status codes, please visit"
|
||||
+ " https://icann.org/epp")
|
||||
"For more information on domain status codes, please visit https://icann.org/epp")
|
||||
.addLink(
|
||||
Link.builder()
|
||||
.setRel("glossary")
|
||||
@@ -50,7 +42,7 @@ public class RdapIcannStandardInformation {
|
||||
.build())
|
||||
.build();
|
||||
|
||||
/** Required by ICANN RDAP Response Profile section 2.11. */
|
||||
/** Required by RDAP Response Profile section 2.10. */
|
||||
private static final Notice INACCURACY_COMPLAINT_FORM_NOTICE =
|
||||
Notice.builder()
|
||||
.setTitle("RDDS Inaccuracy Complaint Form")
|
||||
@@ -79,28 +71,16 @@ public class RdapIcannStandardInformation {
|
||||
/** Boilerplate notices required by domain responses. */
|
||||
static final ImmutableList<Notice> DOMAIN_BOILERPLATE_NOTICES =
|
||||
ImmutableList.of(
|
||||
CONFORMANCE_NOTICE,
|
||||
// RDAP Response Profile 2.6.3
|
||||
DOMAIN_STATUS_CODES_NOTICE,
|
||||
// RDAP Response Profile 2.11
|
||||
// RDAP Response Profile 2.10
|
||||
INACCURACY_COMPLAINT_FORM_NOTICE);
|
||||
|
||||
/** Boilerplate notice for when a domain is blocked by BSA. */
|
||||
static final ImmutableList<Notice> DOMAIN_BLOCKED_BY_BSA_BOILERPLATE_NOTICES =
|
||||
ImmutableList.of(DOMAIN_BLOCKED_BY_BSA_NOTICE);
|
||||
|
||||
/** Boilerplate remarks required by nameserver and entity responses. */
|
||||
static final ImmutableList<Notice> NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES =
|
||||
ImmutableList.of(CONFORMANCE_NOTICE);
|
||||
|
||||
/**
|
||||
* Required by ICANN RDAP Profile section 1.4.9, as corrected by Gustavo Lozano of ICANN.
|
||||
*
|
||||
* <p>Also mentioned in the RDAP Technical Implementation Guide 3.6.
|
||||
*
|
||||
* @see <a href="http://mm.icann.org/pipermail/gtld-tech/2016-October/000822.html">Questions about
|
||||
* the ICANN RDAP Profile</a>
|
||||
*/
|
||||
/** Required by the RDAP Technical Implementation Guide 3.6. */
|
||||
static final Remark SUMMARY_DATA_REMARK =
|
||||
Remark.builder()
|
||||
.setTitle("Incomplete Data")
|
||||
@@ -109,14 +89,7 @@ public class RdapIcannStandardInformation {
|
||||
.setType(Remark.Type.OBJECT_TRUNCATED_UNEXPLAINABLE)
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Required by ICANN RDAP Profile section 1.4.8, as corrected by Gustavo Lozano of ICANN.
|
||||
*
|
||||
* <p>Also mentioned in the RDAP Technical Implementation Guide 3.5.
|
||||
*
|
||||
* @see <a href="http://mm.icann.org/pipermail/gtld-tech/2016-October/000822.html">Questions about
|
||||
* the ICANN RDAP Profile</a>
|
||||
*/
|
||||
/** Required by the RDAP Technical Implementation Guide 3.5. */
|
||||
static final Notice TRUNCATED_RESULT_SET_NOTICE =
|
||||
Notice.builder()
|
||||
.setTitle("Search Policy")
|
||||
@@ -148,7 +121,9 @@ public class RdapIcannStandardInformation {
|
||||
/**
|
||||
* Included when requester is not logged in as the owner of the contact being returned.
|
||||
*
|
||||
* <p>Format required by ICANN RDAP Response Profile 15feb19 section 2.7.4.3.
|
||||
* <p>>Note: if we were keeping this around, we'd want/need to implement the <a
|
||||
* href="https://datatracker.ietf.org/doc/rfc9537/">official RDAP redaction spec</a> for contacts.
|
||||
* We are getting rid of contacts in 2025 though so this should be unnecessary.
|
||||
*/
|
||||
static final Remark CONTACT_PERSONAL_DATA_HIDDEN_DATA_REMARK =
|
||||
Remark.builder()
|
||||
@@ -169,10 +144,9 @@ public class RdapIcannStandardInformation {
|
||||
/**
|
||||
* Included in ALL contact responses, even if the user is authorized.
|
||||
*
|
||||
* <p>Format required by ICANN RDAP Response Profile 15feb19 section 2.7.5.3.
|
||||
*
|
||||
* <p>NOTE that unlike other redacted fields, there's no allowance to give the email to authorized
|
||||
* users or allow for registrar consent.
|
||||
* <p>>Note: if we were keeping this around, we'd want/need to implement the <a
|
||||
* href="https://datatracker.ietf.org/doc/rfc9537/">official RDAP redaction spec</a> for contacts.
|
||||
* We are getting rid of contacts in 2025 though so this should be unnecessary.
|
||||
*/
|
||||
static final Remark CONTACT_EMAIL_REDACTED_FOR_DOMAIN =
|
||||
Remark.builder()
|
||||
|
||||
@@ -26,7 +26,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for RDAP IP address requests.
|
||||
* RDAP action for RDAP IP address requests.
|
||||
*
|
||||
* <p>This feature is not implemented because it's only necessary for <i>address</i> registries like
|
||||
* ARIN, not domain registries.
|
||||
|
||||
@@ -221,7 +221,7 @@ public class RdapJsonFormatter {
|
||||
* Map of EPP event values to the RDAP equivalents.
|
||||
*
|
||||
* <p>Only has entries for optional events, either stated as optional in the RDAP Response Profile
|
||||
* 15feb19, or not mentioned at all but thought to be useful anyway.
|
||||
* section 2.3.2, or not mentioned at all but thought to be useful anyway.
|
||||
*
|
||||
* <p>Any required event should be added elsewhere, preferably without using HistoryEntries (so
|
||||
* that we don't need to load HistoryEntries for "summary" responses).
|
||||
@@ -292,8 +292,8 @@ public class RdapJsonFormatter {
|
||||
* Creates a JSON object for a {@link Domain}.
|
||||
*
|
||||
* <p>NOTE that domain searches aren't in the spec yet - they're in the RFC 9082 that describes
|
||||
* the query format, but they aren't in the RDAP Technical Implementation Guide 15feb19, meaning
|
||||
* we don't have to implement them yet and the RDAP Response Profile doesn't apply to them.
|
||||
* the query format, but they aren't in the RDAP Technical Implementation Guide, meaning we don't
|
||||
* have to implement them yet and the RDAP Response Profile doesn't apply to them.
|
||||
*
|
||||
* <p>We're implementing domain searches anyway, BUT we won't have the response for searches
|
||||
* conform to the RDAP Response Profile.
|
||||
@@ -307,9 +307,9 @@ public class RdapJsonFormatter {
|
||||
if (outputDataType != OutputDataType.FULL) {
|
||||
builder.remarksBuilder().add(RdapIcannStandardInformation.SUMMARY_DATA_REMARK);
|
||||
}
|
||||
// RDAP Response Profile 15feb19 section 2.1 discusses the domain name.
|
||||
// RDAP Response Profile section 2.1 discusses the domain name.
|
||||
builder.setLdhName(domain.getDomainName());
|
||||
// RDAP Response Profile 15feb19 section 2.2:
|
||||
// RDAP Response Profile section 2.2:
|
||||
// The domain handle MUST be the ROID
|
||||
builder.setHandle(domain.getRepoId());
|
||||
// If this is a summary (search result) - we'll return now. Since there's no requirement for
|
||||
@@ -317,9 +317,9 @@ public class RdapJsonFormatter {
|
||||
if (outputDataType == OutputDataType.SUMMARY) {
|
||||
return builder.build();
|
||||
}
|
||||
// RDAP Response Profile 15feb19 section 2.3.1:
|
||||
// RDAP Response Profile section 2.3.1:
|
||||
// The domain object in the RDAP response MUST contain the following events:
|
||||
// [registration, expiration, last update of RDAP database]
|
||||
// [registration, expiration]
|
||||
builder
|
||||
.eventsBuilder()
|
||||
.add(
|
||||
@@ -333,14 +333,18 @@ public class RdapJsonFormatter {
|
||||
.setEventAction(EventAction.EXPIRATION)
|
||||
.setEventDate(domain.getRegistrationExpirationTime())
|
||||
.build(),
|
||||
// RDAP response profile section 1.5:
|
||||
// The topmost object in the RDAP response MUST contain an event of "eventAction" type
|
||||
// "last update of RDAP database" with a value equal to the timestamp when the RDAP
|
||||
// database was last updated
|
||||
Event.builder()
|
||||
.setEventAction(EventAction.LAST_UPDATE_OF_RDAP_DATABASE)
|
||||
.setEventDate(getRequestTime())
|
||||
.build());
|
||||
// RDAP Response Profile 15feb19 section 2.3.2 discusses optional events. We add some of those
|
||||
// RDAP Response Profile section 2.3.2 discusses optional events. We add some of those
|
||||
// here. We also add a few others we find interesting.
|
||||
builder.eventsBuilder().addAll(makeOptionalEvents(domain));
|
||||
// RDAP Response Profile 15feb19 section 2.4.1:
|
||||
// RDAP Response Profile section 2.4.1:
|
||||
// The domain object in the RDAP response MUST contain an entity with the Registrar role.
|
||||
//
|
||||
// See {@link createRdapRegistrarEntity} for details of section 2.4 conformance
|
||||
@@ -378,8 +382,6 @@ public class RdapJsonFormatter {
|
||||
// RDAP Response Profile 2.6.3, must have a notice about statuses. That is in {@link
|
||||
// RdapIcannStandardInformation#domainBoilerplateNotices}
|
||||
|
||||
// Kick off the database loads of the nameservers that we will need, so it can load
|
||||
// asynchronously while we load and process the contacts.
|
||||
ImmutableSet<Host> loadedHosts =
|
||||
replicaTm()
|
||||
.transact(
|
||||
@@ -424,12 +426,12 @@ public class RdapJsonFormatter {
|
||||
}
|
||||
|
||||
// Add the nameservers to the data; the load was kicked off above for efficiency.
|
||||
// RDAP Response Profile 2.9: we MUST have the nameservers
|
||||
// RDAP Response Profile 2.8: we MUST have the nameservers
|
||||
for (Host host : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
|
||||
builder.nameserversBuilder().add(createRdapNameserver(host, OutputDataType.INTERNAL));
|
||||
}
|
||||
|
||||
// RDAP Response Profile 2.10 - MUST contain a secureDns member including at least a
|
||||
// RDAP Response Profile 2.9 - MUST contain a secureDns member including at least a
|
||||
// delegationSigned element. Other elements (e.g. dsData) MUST be included if the domain name is
|
||||
// signed and the elements are stored in the Registry
|
||||
//
|
||||
@@ -454,13 +456,13 @@ public class RdapJsonFormatter {
|
||||
builder.remarksBuilder().add(RdapIcannStandardInformation.SUMMARY_DATA_REMARK);
|
||||
}
|
||||
|
||||
// We need the ldhName: RDAP Response Profile 2.9.1, 4.1
|
||||
// We need the ldhName: RDAP Response Profile 2.8.1, 4.1
|
||||
builder.setLdhName(host.getHostName());
|
||||
// Handle is optional, but if given it MUST be the ROID.
|
||||
// We will set it always as it's important as a "self link"
|
||||
builder.setHandle(host.getRepoId());
|
||||
|
||||
// Status is optional for internal Nameservers - RDAP Response Profile 2.9.2
|
||||
// Status is optional for internal Nameservers - RDAP Response Profile 2.8.2
|
||||
// It isn't mentioned at all anywhere else. So we can just not put it at all?
|
||||
//
|
||||
// To be safe, we'll put it on the "FULL" version anyway
|
||||
@@ -492,7 +494,7 @@ public class RdapJsonFormatter {
|
||||
|
||||
// For query responses - we MUST have all the ip addresses: RDAP Response Profile 4.2.
|
||||
//
|
||||
// However, it is optional for internal responses: RDAP Response Profile 2.9.2
|
||||
// However, it is optional for internal responses: RDAP Response Profile 2.8.2
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
for (InetAddress inetAddress : host.getInetAddresses()) {
|
||||
if (inetAddress instanceof Inet4Address) {
|
||||
@@ -510,7 +512,7 @@ public class RdapJsonFormatter {
|
||||
builder.entitiesBuilder().add(createRdapRegistrarEntity(registrar, OutputDataType.INTERNAL));
|
||||
}
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
// Rdap Response Profile 4.4, must have "last update of RDAP database" response. But this is
|
||||
// Rdap Response Profile 1.5, must have "last update of RDAP database" response. But this is
|
||||
// only for direct query responses and not for internal objects.
|
||||
builder.setLastUpdateOfRdapDatabaseEvent(
|
||||
Event.builder()
|
||||
@@ -535,10 +537,7 @@ public class RdapJsonFormatter {
|
||||
Contact contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
|
||||
RdapContactEntity.Builder contactBuilder = RdapContactEntity.builder();
|
||||
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts. 2.7.4 discusses censoring of
|
||||
// fields we don't want to show (as opposed to not having contacts at all) because of GDPR etc.
|
||||
//
|
||||
// 2.8 allows for unredacted output for authorized people.
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts
|
||||
boolean isAuthorized =
|
||||
rdapAuthorization.isAuthorizedForRegistrar(contact.getCurrentSponsorRegistrarId());
|
||||
|
||||
@@ -578,7 +577,7 @@ public class RdapJsonFormatter {
|
||||
.add(RdapIcannStandardInformation.CONTACT_EMAIL_REDACTED_FOR_DOMAIN);
|
||||
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
// Rdap Response Profile 2.7.6 must have "last update of RDAP database" response. But this is
|
||||
// Rdap Response Profile 1.5 must have "last update of RDAP database" response. But this is
|
||||
// only for direct query responses and not for internal objects. I'm not sure why it's in that
|
||||
// section at all...
|
||||
contactBuilder.setLastUpdateOfRdapDatabaseEvent(
|
||||
@@ -656,8 +655,8 @@ public class RdapJsonFormatter {
|
||||
* Creates a JSON object for a {@link Registrar}.
|
||||
*
|
||||
* <p>This object can be INTERNAL to the Domain and Nameserver responses, with requirements
|
||||
* discussed in the RDAP Response Profile 15feb19 sections 2.4 (internal to Domain) and 4.3
|
||||
* (internal to Namesever)
|
||||
* discussed in the RDAP Response Profile sections 2.4 (internal to Domain) and 4.3 (internal to
|
||||
* Namesever)
|
||||
*
|
||||
* @param registrar the registrar object from which the RDAP response
|
||||
* @param outputDataType whether to generate FULL, SUMMARY, or INTERNAL data.
|
||||
@@ -721,6 +720,15 @@ public class RdapJsonFormatter {
|
||||
builder.linksBuilder().add(makeSelfLink("entity", ianaIdentifier.toString()));
|
||||
}
|
||||
|
||||
// RDAP Response Profile 2.4.6: must have a links entry pointing to the registrar URL, with a
|
||||
// rel:about and a value containing the registrar RDAP base URL (if present)
|
||||
if (registrar.getUrl() != null) {
|
||||
Link.Builder registrarLinkBuilder =
|
||||
Link.builder().setHref(registrar.getUrl()).setRel("about").setType("text/html");
|
||||
registrar.getRdapBaseUrls().stream().findFirst().ifPresent(registrarLinkBuilder::setValue);
|
||||
builder.linksBuilder().add(registrarLinkBuilder.build());
|
||||
}
|
||||
|
||||
// There's no mention of the registrar STATUS in the RDAP Response Profile, so we'll only add it
|
||||
// for FULL response
|
||||
// We could probably not add it at all, but it could be useful for us internally
|
||||
@@ -746,7 +754,6 @@ public class RdapJsonFormatter {
|
||||
//
|
||||
// Write the minimum, meaning only ABUSE for INTERNAL registrars, nothing for SUMMARY and
|
||||
// everything for FULL.
|
||||
//
|
||||
if (outputDataType != OutputDataType.SUMMARY) {
|
||||
ImmutableList<RdapContactEntity> registrarContacts =
|
||||
registrar.getContactsFromReplica().stream()
|
||||
@@ -767,7 +774,7 @@ public class RdapJsonFormatter {
|
||||
builder.entitiesBuilder().addAll(registrarContacts);
|
||||
}
|
||||
|
||||
// Rdap Response Profile 3.3, must have "last update of RDAP database" response. But this is
|
||||
// Rdap Response Profile 1.5, must have "last update of RDAP database" response. But this is
|
||||
// only for direct query responses and not for internal objects.
|
||||
if (outputDataType != OutputDataType.INTERNAL) {
|
||||
builder.setLastUpdateOfRdapDatabaseEvent(
|
||||
@@ -925,8 +932,8 @@ public class RdapJsonFormatter {
|
||||
* Creates the list of optional events to list in domain, nameserver, or contact replies.
|
||||
*
|
||||
* <p>Only has entries for optional events that won't be shown in "SUMMARY" versions of these
|
||||
* objects. These are either stated as optional in the RDAP Response Profile 15feb19, or not
|
||||
* mentioned at all but thought to be useful anyway.
|
||||
* objects. These are either stated as optional in the RDAP Response Profile, or not mentioned at
|
||||
* all but thought to be useful anyway.
|
||||
*
|
||||
* <p>Any required event should be added elsewhere, preferably without using HistoryEntries (so
|
||||
* that we don't need to load HistoryEntries for "summary" responses).
|
||||
@@ -965,7 +972,7 @@ public class RdapJsonFormatter {
|
||||
lastChangeTime = modificationTime;
|
||||
}
|
||||
}
|
||||
// RDAP Response Profile 15feb19 section 2.3.2.2:
|
||||
// RDAP Response Profile section 2.3.2.2:
|
||||
// The event of eventAction type last changed MUST be omitted if the domain name has not been
|
||||
// updated since it was created
|
||||
if (lastChangeTime.isAfter(creationTime)) {
|
||||
@@ -982,7 +989,7 @@ public class RdapJsonFormatter {
|
||||
/**
|
||||
* Creates a vCard address entry: array of strings specifying the components of the address.
|
||||
*
|
||||
* <p>Rdap Response Profile 3.1.1: MUST contain the following fields: Street, City, Country Rdap
|
||||
* <p>RDAP Response Profile 3.1.1: MUST contain the following fields: Street, City, Country Rdap
|
||||
* Response Profile 3.1.2: optional fields: State/Province, Postal Code, Fax Number
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc7095">RFC 7095: jCard: The JSON Format for
|
||||
|
||||
@@ -33,7 +33,7 @@ import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
|
||||
/** RDAP (new WHOIS) action for nameserver requests. */
|
||||
/** RDAP action for nameserver requests. */
|
||||
@Action(
|
||||
service = GaeService.PUBAPI,
|
||||
path = "/rdap/nameserver/",
|
||||
@@ -48,7 +48,7 @@ public class RdapNameserverAction extends RdapActionBase {
|
||||
|
||||
@Override
|
||||
public RdapNameserver getJsonObjectForResource(String pathSearchString, boolean isHeadRequest) {
|
||||
// RDAP Technical Implementation Guide 2.2.1 - we must support A-label (Punycode) and U-label
|
||||
// RDAP Technical Implementation Guide 2.1.1 - we must support A-label (Punycode) and U-label
|
||||
// (Unicode) formats. canonicalizeName will transform Unicode to Punycode so we support both.
|
||||
pathSearchString = canonicalizeName(pathSearchString);
|
||||
// The RDAP syntax is /rdap/nameserver/ns1.mydomain.com.
|
||||
|
||||
@@ -47,9 +47,9 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* RDAP (new WHOIS) action for nameserver search requests.
|
||||
* RDAP action for nameserver search requests.
|
||||
*
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
|
||||
* <p>All commands and responses conform to the RDAP spec as defined in STD 95 and its RFCs.
|
||||
*
|
||||
* @see <a href="http://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
|
||||
@@ -45,11 +45,7 @@ import java.util.Optional;
|
||||
/** Object Classes defined in RFC 9083 section 5. */
|
||||
final class RdapObjectClasses {
|
||||
|
||||
/**
|
||||
* Temporary implementation of VCards.
|
||||
*
|
||||
* <p>Will create a better implementation soon.
|
||||
*/
|
||||
/** Rough implementation of VCards. */
|
||||
@RestrictJsonNames({})
|
||||
@AutoValue
|
||||
public abstract static class Vcard implements Jsonable {
|
||||
@@ -140,8 +136,8 @@ final class RdapObjectClasses {
|
||||
public enum BoilerplateType {
|
||||
DOMAIN(RdapIcannStandardInformation.DOMAIN_BOILERPLATE_NOTICES),
|
||||
DOMAIN_BLOCKED_BY_BSA(RdapIcannStandardInformation.DOMAIN_BLOCKED_BY_BSA_BOILERPLATE_NOTICES),
|
||||
NAMESERVER(RdapIcannStandardInformation.NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES),
|
||||
ENTITY(RdapIcannStandardInformation.NAMESERVER_AND_ENTITY_BOILERPLATE_NOTICES),
|
||||
NAMESERVER(ImmutableList.of()),
|
||||
ENTITY(ImmutableList.of()),
|
||||
OTHER(ImmutableList.of());
|
||||
|
||||
@SuppressWarnings("ImmutableEnumChecker") // immutable lists are, in fact, immutable
|
||||
@@ -173,8 +169,8 @@ final class RdapObjectClasses {
|
||||
* The Top Level JSON reply, Adds the required top-level boilerplate to a ReplyPayloadBase.
|
||||
*
|
||||
* <p>RFC 9083 specifies that the top-level object should include an entry indicating the
|
||||
* conformance level. ICANN RDAP spec for 15feb19 mandates several additional entries, in sections
|
||||
* 2.6.3, 2.11 of the Response Profile and 3.3, 3.5, of the Technical Implementation Guide.
|
||||
* conformance level. The RDAP spec mandates several additional entries, in sections 2.6.3, 2.10
|
||||
* of the Response Profile and 3.3, 3.5, of the Technical Implementation Guide.
|
||||
*/
|
||||
@AutoValue
|
||||
@RestrictJsonNames({})
|
||||
@@ -353,7 +349,7 @@ final class RdapObjectClasses {
|
||||
*
|
||||
* <p>Takes care of the name and unicode field.
|
||||
*
|
||||
* <p>See RDAP Response Profile 15feb19 sections 2.1 and 4.1.
|
||||
* <p>See RDAP Response Profile sections 2.1 and 4.1.
|
||||
*
|
||||
* <p>Note the ldhName field is only required for non-IDN names or IDN names when the query was an
|
||||
* A-label. It is optional for IDN names when the query was a U-label. Because we don't want to
|
||||
@@ -471,7 +467,7 @@ final class RdapObjectClasses {
|
||||
}
|
||||
|
||||
/**
|
||||
* an integer representing the signature lifetime in seconds to be used when creating the RRSIG
|
||||
* An integer representing the signature lifetime in seconds to be used when creating the RRSIG
|
||||
* DS record in the parent zone [RFC5910].
|
||||
*
|
||||
* <p>Note that although it isn't given as optional in RFC 9083, in RFC5910 it's mentioned as
|
||||
|
||||
@@ -44,7 +44,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Base RDAP (new WHOIS) action for domain, nameserver and entity search requests.
|
||||
* Base RDAP action for domain, nameserver and entity search requests.
|
||||
*
|
||||
* @see <a href="https://tools.ietf.org/html/rfc9082">RFC 9082: Registration Data Access Protocol
|
||||
* (RDAP) Query Format</a>
|
||||
@@ -155,7 +155,6 @@ public abstract class RdapSearchActionBase extends RdapActionBase {
|
||||
*/
|
||||
<T extends EppResource> RdapResultSet<T> getMatchingResources(
|
||||
CriteriaQueryBuilder<T> builder, boolean checkForVisibility, int querySizeLimit) {
|
||||
replicaTm().assertInTransaction();
|
||||
Optional<String> desiredRegistrar = getDesiredRegistrar();
|
||||
if (desiredRegistrar.isPresent()) {
|
||||
builder =
|
||||
|
||||
@@ -39,8 +39,8 @@ public final class RdapSearchPattern {
|
||||
/**
|
||||
* Pattern for allowed LDH searches.
|
||||
*
|
||||
* <p>Based on RFC 9082 4.1. Must contains only alphanumeric plus dots and hyphens. A single
|
||||
* whildcard asterix is allowed - but if exists must be the last character of a domain name label
|
||||
* <p>Based on RFC 9082 4.1. Must contain only alphanumeric plus dots and hyphens. A single
|
||||
* wildcard asterix is allowed - but if exists must be the last character of a domain name label
|
||||
* (so exam* and exam*.com are allowed, but exam*le.com isn't allowd)
|
||||
*
|
||||
* <p>The prefix is in group(1), and the suffix without the dot (if it exists) is in group(4). If
|
||||
@@ -123,7 +123,7 @@ public final class RdapSearchPattern {
|
||||
* Creates a SearchPattern using the provided domain search pattern in LDH format.
|
||||
*
|
||||
* <p>The domain search pattern can have a single wildcard asterix that can match 0 or more
|
||||
* charecters. If such an asterix exists - it must be at the end of a domain label.
|
||||
* characters. If such an asterix exists - it must be at the end of a domain label.
|
||||
*
|
||||
* @param searchQuery the string containing the partial match pattern
|
||||
* @throws UnprocessableEntityException if {@code pattern} does not meet the requirements of RFC
|
||||
@@ -150,7 +150,7 @@ public final class RdapSearchPattern {
|
||||
* Creates a SearchPattern using the provided domain search pattern in LDH or Unicode format.
|
||||
*
|
||||
* <p>The domain search pattern can have a single wildcard asterix that can match 0 or more
|
||||
* charecters. If such an asterix exists - it must be at the end of a domain label.
|
||||
* characters. If such an asterix exists - it must be at the end of a domain label.
|
||||
*
|
||||
* <p>In theory, according to RFC 9082 4.1 - we should make some checks about partial matching in
|
||||
* unicode queries. We don't, but we might want to just disable partial matches for unicode inputs
|
||||
|
||||
@@ -37,7 +37,7 @@ public final class RdapUtils {
|
||||
*
|
||||
* <p>Used for RDAP Technical Implementation Guide 2.4.2 - search of registrar by the fn element.
|
||||
*
|
||||
* <p>For convenience, we use case insensitive search.
|
||||
* <p>For convenience, we use case-insensitive search.
|
||||
*/
|
||||
static Optional<Registrar> getRegistrarByName(String registrarName) {
|
||||
return Streams.stream(Registrar.loadAllCached())
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.PersistenceModule;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.HttpException.InternalServerErrorException;
|
||||
@@ -36,7 +37,7 @@ import jakarta.inject.Inject;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
import java.security.GeneralSecurityException;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
@@ -72,7 +73,7 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
public void run() {
|
||||
try {
|
||||
ImmutableMap<String, String> ianaIdsToUrls = getIanaIdsToUrls();
|
||||
tm().transact(() -> processAllRegistrars(ianaIdsToUrls));
|
||||
processAllRegistrars(ianaIdsToUrls);
|
||||
} catch (Exception e) {
|
||||
throw new InternalServerErrorException("Error when retrieving RDAP base URL CSV file", e);
|
||||
}
|
||||
@@ -80,7 +81,14 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
|
||||
private static void processAllRegistrars(ImmutableMap<String, String> ianaIdsToUrls) {
|
||||
int nonUpdatedRegistrars = 0;
|
||||
for (Registrar registrar : Registrar.loadAll()) {
|
||||
// Split into multiple transactions to avoid load-save-reload conflicts. Re-building a registrar
|
||||
// requires a full (cached) load of all registrars to avoid IANA ID conflicts, so if multiple
|
||||
// registrars are modified in the same transaction, the second build call will fail.
|
||||
Iterable<Registrar> registrars =
|
||||
tm().transact(
|
||||
PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ,
|
||||
Registrar::loadAll);
|
||||
for (Registrar registrar : registrars) {
|
||||
// Only update REAL registrars
|
||||
if (registrar.getType() != Registrar.Type.REAL) {
|
||||
continue;
|
||||
@@ -100,7 +108,12 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
"Updating RDAP base URLs for registrar %s from %s to %s",
|
||||
registrar.getRegistrarId(), registrar.getRdapBaseUrls(), baseUrls);
|
||||
}
|
||||
tm().put(registrar.asBuilder().setRdapBaseUrls(baseUrls).build());
|
||||
tm().transact(
|
||||
() -> {
|
||||
// Reload inside a transaction to avoid race conditions
|
||||
Registrar reloadedRegistrar = tm().loadByEntity(registrar);
|
||||
tm().put(reloadedRegistrar.asBuilder().setRdapBaseUrls(baseUrls).build());
|
||||
});
|
||||
}
|
||||
}
|
||||
logger.atInfo().log("No change in RDAP base URLs for %d registrars", nonUpdatedRegistrars);
|
||||
@@ -108,9 +121,9 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
|
||||
private ImmutableMap<String, String> getIanaIdsToUrls()
|
||||
throws IOException, GeneralSecurityException {
|
||||
CSVParser csv;
|
||||
HttpURLConnection connection = urlConnectionService.createConnection(new URL(RDAP_IDS_URL));
|
||||
// Explictly set the accepted encoding, as we know Brotli causes us problems when talking to
|
||||
HttpURLConnection connection =
|
||||
urlConnectionService.createConnection(URI.create(RDAP_IDS_URL).toURL());
|
||||
// Explicitly set the accepted encoding, as we know Brotli causes us problems when talking to
|
||||
// ICANN.
|
||||
connection.setRequestProperty(ACCEPT_ENCODING, "gzip");
|
||||
String csvString;
|
||||
@@ -128,11 +141,11 @@ public final class UpdateRegistrarRdapBaseUrlsAction implements Runnable {
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
csv =
|
||||
CSVParser csv =
|
||||
CSVFormat.Builder.create(CSVFormat.DEFAULT)
|
||||
.setHeader()
|
||||
.setSkipHeaderRecord(true)
|
||||
.build()
|
||||
.get()
|
||||
.parse(new StringReader(csvString));
|
||||
ImmutableMap.Builder<String, String> result = new ImmutableMap.Builder<>();
|
||||
for (CSVRecord record : csv) {
|
||||
|
||||
@@ -16,6 +16,8 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.pricing.PricingEngineProxy.getPricesForDomainName;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
@@ -62,8 +64,8 @@ final class CreateDomainCommand extends CreateOrUpdateDomainCommand {
|
||||
protected void initMutatingEppToolCommand() {
|
||||
tm().transact(
|
||||
() -> {
|
||||
if (!FeatureFlag.isActiveNowOrElse(
|
||||
FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL, false)) {
|
||||
if (!FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
&& !FeatureFlag.isActiveNow(MINIMUM_DATASET_CONTACTS_PROHIBITED)) {
|
||||
checkArgumentNotNull(registrant, "Registrant must be specified");
|
||||
checkArgument(!admins.isEmpty(), "At least one admin must be specified");
|
||||
checkArgument(!techs.isEmpty(), "At least one tech must be specified");
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
@@ -24,6 +23,7 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
@@ -34,7 +34,6 @@ import google.registry.util.Clock;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVPrinter;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -100,27 +99,40 @@ public class ConsoleDumDownloadAction extends ConsoleApiAction {
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
return;
|
||||
}
|
||||
tm().transact(
|
||||
() -> {
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.DUM_DOWNLOAD)
|
||||
.setDescription(registrarId));
|
||||
});
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
private void writeCsv(CSVPrinter printer) throws IOException {
|
||||
String sql = SQL_TEMPLATE.replaceAll(":now", clock.nowUtc().toString());
|
||||
|
||||
// We deliberately don't want to use ImmutableList.copyOf because underlying list may contain
|
||||
// large amount of records and that will degrade performance.
|
||||
List<String> queryResult =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(sql)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.setHint("org.hibernate.fetchSize", 1000)
|
||||
.getResultList());
|
||||
|
||||
ImmutableList<String[]> formattedRecords =
|
||||
queryResult.stream().map(r -> r.split(",")).collect(toImmutableList());
|
||||
printer.printRecord(
|
||||
ImmutableList.of("Domain Name", "Creation Time", "Expiration Time", "Domain Statuses"));
|
||||
printer.printRecords(formattedRecords);
|
||||
|
||||
tm().transact(
|
||||
() -> {
|
||||
try (var resultStream =
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(sql, String.class)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.setHint("org.hibernate.fetchSize", 1000)
|
||||
.getResultStream()) {
|
||||
|
||||
resultStream.forEach(
|
||||
row -> {
|
||||
try {
|
||||
printer.printRecord((Object[]) ((String) row).split(","));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Action.GaeService;
|
||||
import google.registry.request.Action.GkeService;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
gkeService = GkeService.CONSOLE,
|
||||
path = ConsoleHistoryDataAction.PATH,
|
||||
method = {GET},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class ConsoleHistoryDataAction extends ConsoleApiAction {
|
||||
|
||||
private static final String SQL_USER_HISTORY =
|
||||
"""
|
||||
SELECT * FROM "ConsoleUpdateHistory"
|
||||
WHERE acting_user = :actingUser
|
||||
""";
|
||||
|
||||
private static final String SQL_REGISTRAR_HISTORY =
|
||||
"""
|
||||
SELECT *
|
||||
FROM "ConsoleUpdateHistory"
|
||||
WHERE SPLIT_PART(description, '|', 1) = :registrarId;
|
||||
""";
|
||||
|
||||
public static final String PATH = "/console-api/history";
|
||||
|
||||
private final String registrarId;
|
||||
private final Optional<String> consoleUserEmail;
|
||||
|
||||
@Inject
|
||||
public ConsoleHistoryDataAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Parameter("registrarId") String registrarId,
|
||||
@Parameter("consoleUserEmail") Optional<String> consoleUserEmail) {
|
||||
super(consoleApiParams);
|
||||
this.registrarId = registrarId;
|
||||
this.consoleUserEmail = consoleUserEmail;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getHandler(User user) {
|
||||
if (this.consoleUserEmail.isPresent()) {
|
||||
this.historyByUser(user, this.consoleUserEmail.get());
|
||||
return;
|
||||
}
|
||||
|
||||
this.historyByRegistrarId(user, this.registrarId);
|
||||
}
|
||||
|
||||
private void historyByUser(User user, String consoleUserEmail) {
|
||||
if (!user.getUserRoles().hasGlobalPermission(ConsolePermission.AUDIT_ACTIVITY_BY_USER)) {
|
||||
setFailedResponse(
|
||||
"User doesn't have a permission to check audit activity by user", SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
List<ConsoleUpdateHistory> queryResult =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(SQL_USER_HISTORY, ConsoleUpdateHistory.class)
|
||||
.setParameter("actingUser", consoleUserEmail)
|
||||
.setHint("org.hibernate.fetchSize", 1000)
|
||||
.getResultList());
|
||||
|
||||
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(queryResult));
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
|
||||
private void historyByRegistrarId(User user, String registrarId) {
|
||||
checkArgument(!Strings.isNullOrEmpty(registrarId), "Empty registrarId param");
|
||||
checkPermission(user, registrarId, ConsolePermission.AUDIT_ACTIVITY_BY_REGISTRAR);
|
||||
List<ConsoleUpdateHistory> queryResult =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(SQL_REGISTRAR_HISTORY, ConsoleUpdateHistory.class)
|
||||
.setParameter("registrarId", registrarId)
|
||||
.setHint("org.hibernate.fetchSize", 1000)
|
||||
.getResultList());
|
||||
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(queryResult));
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import google.registry.ui.server.console.ConsoleEppPasswordAction.EppPasswordDat
|
||||
import google.registry.ui.server.console.ConsoleOteAction.OteCreateData;
|
||||
import google.registry.ui.server.console.ConsoleRegistryLockAction.ConsoleRegistryLockPostInput;
|
||||
import google.registry.ui.server.console.ConsoleUsersAction.UserData;
|
||||
import google.registry.ui.server.console.PasswordResetRequestAction.PasswordResetRequestData;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -246,6 +247,12 @@ public final class ConsoleModule {
|
||||
return extractRequiredParameter(req, "bulkDomainAction");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("resetRequestVerificationCode")
|
||||
public static String provideResetRequestVerificationCode(HttpServletRequest req) {
|
||||
return extractRequiredParameter(req, "resetRequestVerificationCode");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("eppPasswordChangeRequest")
|
||||
public static Optional<EppPasswordData> provideEppPasswordChangeRequest(
|
||||
@@ -273,4 +280,21 @@ public final class ConsoleModule {
|
||||
Gson gson, @OptionalJsonPayload Optional<JsonElement> payload) {
|
||||
return payload.map(e -> gson.fromJson(e, ConsoleRegistryLockPostInput.class));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("passwordResetRequestData")
|
||||
public static PasswordResetRequestData providePasswordResetRequestData(
|
||||
Gson gson, @OptionalJsonPayload Optional<JsonElement> payload) {
|
||||
return payload
|
||||
.map(e -> gson.fromJson(e, PasswordResetRequestData.class))
|
||||
.orElseThrow(
|
||||
() -> new IllegalArgumentException("Must provide password request reset data"));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("newPassword")
|
||||
public static Optional<String> provideNewPassword(
|
||||
Gson gson, @OptionalJsonPayload Optional<JsonElement> payload) {
|
||||
return payload.map(e -> gson.fromJson(e, String.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,12 @@
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
import google.registry.request.Action;
|
||||
@@ -64,6 +66,21 @@ public class ConsoleRegistryLockVerifyAction extends ConsoleApiAction {
|
||||
RegistryLockVerificationResponse lockResponse =
|
||||
new RegistryLockVerificationResponse(
|
||||
Ascii.toLowerCase(action.toString()), lock.getDomainName(), lock.getRegistrarId());
|
||||
tm().transact(
|
||||
() -> {
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(
|
||||
action == RegistryLockAction.LOCKED
|
||||
? ConsoleUpdateHistory.Type.REGISTRY_LOCK
|
||||
: ConsoleUpdateHistory.Type.REGISTRY_UNLOCK)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s",
|
||||
lock.getRegistrarId(),
|
||||
ConsoleUpdateHistory.DESCRIPTION_SEPARATOR,
|
||||
lockResponse)));
|
||||
});
|
||||
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(lockResponse));
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
@@ -118,6 +119,7 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
u ->
|
||||
new UserData(
|
||||
u.getEmailAddress(),
|
||||
u.getRegistryLockEmailAddress().orElse(null),
|
||||
u.getUserRoles().getRegistrarRoles().get(registrarId).toString(),
|
||||
null))
|
||||
.collect(Collectors.toList());
|
||||
@@ -177,6 +179,12 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
tm().delete(key);
|
||||
User.revokeIapPermission(email, maybeGroupEmailAddress, cloudTasksUtils, null, iamClient);
|
||||
sendConfirmationEmail(registrarId, email, "Deleted user");
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.USER_DELETE)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s", registrarId, ConsoleUpdateHistory.DESCRIPTION_SEPARATOR, email)));
|
||||
}
|
||||
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
@@ -230,7 +238,15 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
.setPayload(
|
||||
consoleApiParams
|
||||
.gson()
|
||||
.toJson(new UserData(newEmail, ACCOUNT_MANAGER.toString(), newUser.getPassword())));
|
||||
.toJson(
|
||||
new UserData(
|
||||
newEmail, null, ACCOUNT_MANAGER.toString(), newUser.getPassword())));
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.USER_CREATE)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s", registrarId, ConsoleUpdateHistory.DESCRIPTION_SEPARATOR, newEmail)));
|
||||
}
|
||||
|
||||
private void runUpdateInTransaction() {
|
||||
@@ -245,6 +261,15 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
|
||||
sendConfirmationEmail(registrarId, this.userData.get().emailAddress, "Updated user");
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.USER_UPDATE)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s",
|
||||
registrarId,
|
||||
ConsoleUpdateHistory.DESCRIPTION_SEPARATOR,
|
||||
this.userData.get().emailAddress)));
|
||||
}
|
||||
|
||||
private boolean isModifyingRequestValid() {
|
||||
@@ -323,5 +348,8 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
}
|
||||
|
||||
public record UserData(
|
||||
@Expose String emailAddress, @Expose String role, @Expose @Nullable String password) {}
|
||||
@Expose String emailAddress,
|
||||
@Expose String registryLockEmailAddress,
|
||||
@Expose String role,
|
||||
@Expose @Nullable String password) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.PasswordResetRequest;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.persistence.transaction.QueryComposer;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.util.EmailMessage;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Action(
|
||||
service = Action.GaeService.DEFAULT,
|
||||
gkeService = Action.GkeService.CONSOLE,
|
||||
path = PasswordResetRequestAction.PATH,
|
||||
method = Action.Method.POST,
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class PasswordResetRequestAction extends ConsoleApiAction {
|
||||
|
||||
static final String PATH = "/console-api/password-reset-request";
|
||||
static final String VERIFICATION_EMAIL_TEMPLATE =
|
||||
"""
|
||||
Please click the link below to perform the requested password reset. Note: this\
|
||||
code will expire in one hour.
|
||||
|
||||
%s\
|
||||
""";
|
||||
|
||||
private final PasswordResetRequestData passwordResetRequestData;
|
||||
|
||||
@Inject
|
||||
public PasswordResetRequestAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Parameter("passwordResetRequestData") PasswordResetRequestData passwordResetRequestData) {
|
||||
super(consoleApiParams);
|
||||
this.passwordResetRequestData = passwordResetRequestData;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postHandler(User user) {
|
||||
// Temporary flag when testing email sending etc
|
||||
if (!user.getUserRoles().isAdmin()) {
|
||||
setFailedResponse("", HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
tm().transact(() -> performRequest(user));
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
private void performRequest(User user) {
|
||||
checkArgument(passwordResetRequestData.type != null, "Type cannot be null");
|
||||
checkArgument(passwordResetRequestData.registrarId != null, "Registrar ID cannot be null");
|
||||
PasswordResetRequest.Type type = passwordResetRequestData.type;
|
||||
String registrarId = passwordResetRequestData.registrarId;
|
||||
|
||||
ConsolePermission requiredPermission;
|
||||
String destinationEmail;
|
||||
String emailSubject;
|
||||
switch (type) {
|
||||
case EPP:
|
||||
requiredPermission = ConsolePermission.EDIT_REGISTRAR_DETAILS;
|
||||
destinationEmail = getAdminPocEmail(registrarId);
|
||||
emailSubject = "EPP password reset request";
|
||||
break;
|
||||
case REGISTRY_LOCK:
|
||||
checkArgument(
|
||||
passwordResetRequestData.registryLockEmail != null,
|
||||
"Must provide registry lock email to reset");
|
||||
requiredPermission = ConsolePermission.MANAGE_USERS;
|
||||
destinationEmail = passwordResetRequestData.registryLockEmail;
|
||||
checkUserExistsWithRegistryLockEmail(destinationEmail);
|
||||
emailSubject = "Registry lock password reset request";
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown type " + type);
|
||||
}
|
||||
|
||||
checkPermission(user, registrarId, requiredPermission);
|
||||
|
||||
InternetAddress destinationAddress;
|
||||
try {
|
||||
destinationAddress = new InternetAddress(destinationEmail);
|
||||
} catch (AddressException e) {
|
||||
// Shouldn't happen
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
PasswordResetRequest resetRequest =
|
||||
new PasswordResetRequest.Builder()
|
||||
.setRequester(user.getEmailAddress())
|
||||
.setRegistrarId(registrarId)
|
||||
.setType(type)
|
||||
.setDestinationEmail(destinationEmail)
|
||||
.build();
|
||||
tm().put(resetRequest);
|
||||
String verificationUrl =
|
||||
String.format(
|
||||
"https://%s/console/#/password-reset-verify?resetRequestVerificationCode=%s",
|
||||
consoleApiParams.request().getServerName(), resetRequest.getVerificationCode());
|
||||
String body = String.format(VERIFICATION_EMAIL_TEMPLATE, verificationUrl);
|
||||
consoleApiParams
|
||||
.sendEmailUtils()
|
||||
.gmailClient
|
||||
.sendEmail(EmailMessage.create(emailSubject, body, destinationAddress));
|
||||
}
|
||||
|
||||
static User checkUserExistsWithRegistryLockEmail(String destinationEmail) {
|
||||
return tm().createQueryComposer(User.class)
|
||||
.where("registryLockEmailAddress", QueryComposer.Comparator.EQ, destinationEmail)
|
||||
.first()
|
||||
.orElseThrow(
|
||||
() -> new IllegalArgumentException("Unknown user with lock email " + destinationEmail));
|
||||
}
|
||||
|
||||
private String getAdminPocEmail(String registrarId) {
|
||||
return RegistrarPoc.loadForRegistrar(registrarId).stream()
|
||||
.filter(poc -> poc.getTypes().contains(RegistrarPoc.Type.ADMIN))
|
||||
.map(RegistrarPoc::getEmailAddress)
|
||||
.findAny()
|
||||
.orElseThrow(() -> new IllegalStateException("No admin contacts found for " + registrarId));
|
||||
}
|
||||
|
||||
public record PasswordResetRequestData(
|
||||
@Expose PasswordResetRequest.Type type,
|
||||
@Expose String registrarId,
|
||||
@Expose @Nullable String registryLockEmail) {}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.ui.server.console.PasswordResetRequestAction.checkUserExistsWithRegistryLockEmail;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.PasswordResetRequest;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
@Action(
|
||||
service = Action.GaeService.DEFAULT,
|
||||
gkeService = Action.GkeService.CONSOLE,
|
||||
path = PasswordResetVerifyAction.PATH,
|
||||
method = {GET, POST},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class PasswordResetVerifyAction extends ConsoleApiAction {
|
||||
|
||||
static final String PATH = "/console-api/password-reset-verify";
|
||||
|
||||
private final String verificationCode;
|
||||
private final Optional<String> newPassword;
|
||||
|
||||
@Inject
|
||||
public PasswordResetVerifyAction(
|
||||
ConsoleApiParams consoleApiParams,
|
||||
@Parameter("resetRequestVerificationCode") String verificationCode,
|
||||
@Parameter("newPassword") Optional<String> newPassword) {
|
||||
super(consoleApiParams);
|
||||
this.verificationCode = verificationCode;
|
||||
this.newPassword = newPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getHandler(User user) {
|
||||
// Temporary flag when testing email sending etc
|
||||
if (!user.getUserRoles().isAdmin()) {
|
||||
setFailedResponse("", HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
PasswordResetRequest request = tm().transact(() -> loadAndValidateResetRequest(user));
|
||||
ImmutableMap<String, ?> result =
|
||||
ImmutableMap.of("type", request.getType(), "registrarId", request.getRegistrarId());
|
||||
consoleApiParams.response().setPayload(consoleApiParams.gson().toJson(result));
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postHandler(User user) {
|
||||
// Temporary flag when testing email sending etc
|
||||
if (!user.getUserRoles().isAdmin()) {
|
||||
setFailedResponse("", HttpServletResponse.SC_FORBIDDEN);
|
||||
}
|
||||
checkArgument(!Strings.isNullOrEmpty(newPassword.orElse(null)), "Password must be provided");
|
||||
tm().transact(
|
||||
() -> {
|
||||
PasswordResetRequest request = loadAndValidateResetRequest(user);
|
||||
switch (request.getType()) {
|
||||
case EPP -> handleEppPasswordReset(request);
|
||||
case REGISTRY_LOCK -> handleRegistryLockPasswordReset(request);
|
||||
}
|
||||
tm().put(request.asBuilder().setFulfillmentTime(tm().getTransactionTime()).build());
|
||||
});
|
||||
consoleApiParams.response().setStatus(HttpServletResponse.SC_OK);
|
||||
}
|
||||
|
||||
private void handleEppPasswordReset(PasswordResetRequest request) {
|
||||
Registrar registrar = Registrar.loadByRegistrarId(request.getRegistrarId()).get();
|
||||
tm().put(registrar.asBuilder().setPassword(newPassword.get()).build());
|
||||
}
|
||||
|
||||
private void handleRegistryLockPasswordReset(PasswordResetRequest request) {
|
||||
User affectedUser = checkUserExistsWithRegistryLockEmail(request.getDestinationEmail());
|
||||
tm().put(
|
||||
affectedUser
|
||||
.asBuilder()
|
||||
.removeRegistryLockPassword()
|
||||
.setRegistryLockPassword(newPassword.get())
|
||||
.build());
|
||||
}
|
||||
|
||||
private PasswordResetRequest loadAndValidateResetRequest(User user) {
|
||||
PasswordResetRequest request =
|
||||
tm().loadByKeyIfPresent(VKey.create(PasswordResetRequest.class, verificationCode))
|
||||
.orElseThrow(this::createVerificationCodeException);
|
||||
ConsolePermission requiredVerifyPermission =
|
||||
switch (request.getType()) {
|
||||
case EPP -> ConsolePermission.MANAGE_USERS;
|
||||
case REGISTRY_LOCK -> ConsolePermission.REGISTRY_LOCK;
|
||||
};
|
||||
checkPermission(user, request.getRegistrarId(), requiredVerifyPermission);
|
||||
if (request
|
||||
.getRequestTime()
|
||||
.plus(Duration.standardHours(1))
|
||||
.isBefore(tm().getTransactionTime())) {
|
||||
throw createVerificationCodeException();
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
private IllegalArgumentException createVerificationCodeException() {
|
||||
return new IllegalArgumentException(
|
||||
"Unknown, invalid, or expired verification code " + verificationCode);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.ConsoleUpdateHistory;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
@@ -84,6 +85,7 @@ public class ContactAction extends ConsoleApiAction {
|
||||
protected void deleteHandler(User user) {
|
||||
updateContacts(
|
||||
user,
|
||||
"Deleted " + contact.get().getEmailAddress(),
|
||||
(registrar, oldContacts) ->
|
||||
oldContacts.stream()
|
||||
.filter(
|
||||
@@ -96,6 +98,7 @@ public class ContactAction extends ConsoleApiAction {
|
||||
protected void postHandler(User user) {
|
||||
updateContacts(
|
||||
user,
|
||||
"Created " + contact.get().getEmailAddress(),
|
||||
(registrar, oldContacts) -> {
|
||||
RegistrarPoc newContact = contact.get();
|
||||
return ImmutableSet.<RegistrarPoc>builder()
|
||||
@@ -121,6 +124,7 @@ public class ContactAction extends ConsoleApiAction {
|
||||
protected void putHandler(User user) {
|
||||
updateContacts(
|
||||
user,
|
||||
"Updated " + contact.get().getEmailAddress(),
|
||||
(registrar, oldContacts) -> {
|
||||
RegistrarPoc updatedContact = contact.get();
|
||||
return oldContacts.stream()
|
||||
@@ -146,6 +150,7 @@ public class ContactAction extends ConsoleApiAction {
|
||||
|
||||
private void updateContacts(
|
||||
User user,
|
||||
String historyDescription,
|
||||
BiFunction<Registrar, ImmutableSet<RegistrarPoc>, ImmutableSet<RegistrarPoc>>
|
||||
contactsUpdater) {
|
||||
checkPermission(user, registrarId, ConsolePermission.EDIT_REGISTRAR_DETAILS);
|
||||
@@ -176,6 +181,15 @@ public class ContactAction extends ConsoleApiAction {
|
||||
tm().put(updatedRegistrar);
|
||||
sendExternalUpdatesIfNecessary(
|
||||
EmailInfo.create(registrar, updatedRegistrar, oldContacts, newContacts));
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.REGISTRAR_CONTACTS_UPDATE)
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s",
|
||||
registrarId,
|
||||
ConsoleUpdateHistory.DESCRIPTION_SEPARATOR,
|
||||
historyDescription)));
|
||||
});
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import google.registry.ui.server.console.ConsoleApiAction;
|
||||
import google.registry.ui.server.console.ConsoleApiParams;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
/**
|
||||
* Console action for editing fields on a registrar that are visible in WHOIS/RDAP.
|
||||
@@ -82,19 +83,37 @@ public class RdapRegistrarFieldsAction extends ConsoleApiAction {
|
||||
return;
|
||||
}
|
||||
|
||||
Registrar newRegistrar =
|
||||
savedRegistrar
|
||||
.asBuilder()
|
||||
.setLocalizedAddress(providedRegistrar.getLocalizedAddress())
|
||||
.setPhoneNumber(providedRegistrar.getPhoneNumber())
|
||||
.setFaxNumber(providedRegistrar.getFaxNumber())
|
||||
.setEmailAddress(providedRegistrar.getEmailAddress())
|
||||
.build();
|
||||
StringJoiner updates = new StringJoiner(",");
|
||||
|
||||
var newRegistrarBuilder = savedRegistrar.asBuilder();
|
||||
|
||||
if (!providedRegistrar.getLocalizedAddress().equals(savedRegistrar.getLocalizedAddress())) {
|
||||
newRegistrarBuilder.setLocalizedAddress(providedRegistrar.getLocalizedAddress());
|
||||
updates.add("ADDRESS");
|
||||
}
|
||||
if (!providedRegistrar.getPhoneNumber().equals(savedRegistrar.getPhoneNumber())) {
|
||||
newRegistrarBuilder.setPhoneNumber(providedRegistrar.getPhoneNumber());
|
||||
updates.add("PHONE");
|
||||
}
|
||||
if (!providedRegistrar.getFaxNumber().equals(savedRegistrar.getPhoneNumber())) {
|
||||
newRegistrarBuilder.setFaxNumber(providedRegistrar.getFaxNumber());
|
||||
updates.add("FAX");
|
||||
}
|
||||
if (!providedRegistrar.getEmailAddress().equals(savedRegistrar.getEmailAddress())) {
|
||||
newRegistrarBuilder.setEmailAddress(providedRegistrar.getEmailAddress());
|
||||
updates.add("EMAIL");
|
||||
}
|
||||
var newRegistrar = newRegistrarBuilder.build();
|
||||
tm().put(newRegistrar);
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.REGISTRAR_UPDATE)
|
||||
.setDescription(newRegistrar.getRegistrarId()));
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s",
|
||||
newRegistrar.getRegistrarId(),
|
||||
ConsoleUpdateHistory.DESCRIPTION_SEPARATOR,
|
||||
updates)));
|
||||
sendExternalUpdatesIfNecessary(
|
||||
EmailInfo.create(
|
||||
savedRegistrar,
|
||||
|
||||
@@ -39,6 +39,7 @@ import google.registry.ui.server.console.ConsoleApiAction;
|
||||
import google.registry.ui.server.console.ConsoleApiParams;
|
||||
import jakarta.inject.Inject;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
@Action(
|
||||
service = GaeService.DEFAULT,
|
||||
@@ -86,10 +87,15 @@ public class SecurityAction extends ConsoleApiAction {
|
||||
|
||||
private void setResponse(Registrar savedRegistrar) {
|
||||
Registrar registrarParameter = registrar.get();
|
||||
Registrar.Builder updatedRegistrarBuilder =
|
||||
savedRegistrar
|
||||
.asBuilder()
|
||||
.setIpAddressAllowList(registrarParameter.getIpAddressAllowList());
|
||||
Registrar.Builder updatedRegistrarBuilder = savedRegistrar.asBuilder();
|
||||
StringJoiner updates = new StringJoiner(",");
|
||||
|
||||
if (!savedRegistrar
|
||||
.getIpAddressAllowList()
|
||||
.equals(registrarParameter.getIpAddressAllowList())) {
|
||||
updatedRegistrarBuilder.setIpAddressAllowList(registrarParameter.getIpAddressAllowList());
|
||||
updates.add("IP_CHANGE");
|
||||
}
|
||||
|
||||
try {
|
||||
if (!savedRegistrar
|
||||
@@ -99,6 +105,7 @@ public class SecurityAction extends ConsoleApiAction {
|
||||
String newClientCert = registrarParameter.getClientCertificate().get();
|
||||
certificateChecker.validateCertificate(newClientCert);
|
||||
updatedRegistrarBuilder.setClientCertificate(newClientCert, tm().getTransactionTime());
|
||||
updates.add("PRIMARY_SSL_CERT_CHANGE");
|
||||
}
|
||||
}
|
||||
if (!savedRegistrar
|
||||
@@ -109,6 +116,7 @@ public class SecurityAction extends ConsoleApiAction {
|
||||
certificateChecker.validateCertificate(newFailoverCert);
|
||||
updatedRegistrarBuilder.setFailoverClientCertificate(
|
||||
newFailoverCert, tm().getTransactionTime());
|
||||
updates.add("FAILOVER_SSL_CERT_CHANGE");
|
||||
}
|
||||
}
|
||||
} catch (InsecureCertificateException e) {
|
||||
@@ -121,7 +129,9 @@ public class SecurityAction extends ConsoleApiAction {
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.REGISTRAR_SECURITY_UPDATE)
|
||||
.setDescription(registrarId));
|
||||
.setDescription(
|
||||
String.format(
|
||||
"%s%s%s", registrarId, ConsoleUpdateHistory.DESCRIPTION_SEPARATOR, updates)));
|
||||
|
||||
sendExternalUpdatesIfNecessary(
|
||||
EmailInfo.create(savedRegistrar, updatedRegistrar, ImmutableSet.of(), ImmutableSet.of()));
|
||||
|
||||
@@ -27,7 +27,6 @@ import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistDeletedDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistDomainAsDeleted;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResource;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -287,7 +286,7 @@ class DeleteProberDataActionTest {
|
||||
private static Set<ImmutableObject> persistDomainAndDescendants(String fqdn) {
|
||||
Domain domain = persistDeletedDomain(fqdn, DELETION_TIME);
|
||||
DomainHistory historyEntry =
|
||||
persistSimpleResource(
|
||||
persistResource(
|
||||
new DomainHistory.Builder()
|
||||
.setDomain(domain)
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
@@ -295,7 +294,7 @@ class DeleteProberDataActionTest {
|
||||
.setModificationTime(DELETION_TIME.minusYears(3))
|
||||
.build());
|
||||
BillingEvent billingEvent =
|
||||
persistSimpleResource(
|
||||
persistResource(
|
||||
new BillingEvent.Builder()
|
||||
.setDomainHistory(historyEntry)
|
||||
.setBillingTime(DELETION_TIME.plusYears(1))
|
||||
@@ -307,7 +306,7 @@ class DeleteProberDataActionTest {
|
||||
.setTargetId(fqdn)
|
||||
.build());
|
||||
PollMessage.OneTime pollMessage =
|
||||
persistSimpleResource(
|
||||
persistResource(
|
||||
new PollMessage.OneTime.Builder()
|
||||
.setHistoryEntry(historyEntry)
|
||||
.setEventTime(DELETION_TIME)
|
||||
@@ -315,7 +314,7 @@ class DeleteProberDataActionTest {
|
||||
.setMsg("Domain registered")
|
||||
.build());
|
||||
GracePeriod gracePeriod =
|
||||
persistSimpleResource(
|
||||
persistResource(
|
||||
GracePeriod.create(
|
||||
ADD,
|
||||
domain.getRepoId(),
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.JpaTransactionManagerExtension.makeRegistrar1;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
|
||||
import static google.registry.testing.DatabaseHelper.persistResources;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.apache.http.HttpStatus.SC_OK;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -62,7 +62,8 @@ class SendExpiringCertificateNotificationEmailActionTest {
|
||||
Kind update your account using the following steps:
|
||||
1. Navigate to support and login using your %4$s@registry.example credentials.
|
||||
2. Click Settings -> Privacy on the top left corner.
|
||||
3. Click Edit and enter certificate string. 3. Click SaveRegards,Example Registry""";
|
||||
3. Click Edit and enter certificate string. 3. Click SaveRegards,Example Registry\
|
||||
""";
|
||||
|
||||
private static final String EXPIRATION_WARNING_EMAIL_SUBJECT_TEXT = "Expiration Warning Email";
|
||||
|
||||
@@ -223,7 +224,7 @@ class SendExpiringCertificateNotificationEmailActionTest {
|
||||
.setVisibleInWhoisAsAdmin(true)
|
||||
.setVisibleInWhoisAsTech(false)
|
||||
.build());
|
||||
persistSimpleResources(contacts);
|
||||
persistResources(contacts);
|
||||
RuntimeException thrown =
|
||||
assertThrows(
|
||||
RuntimeException.class,
|
||||
@@ -548,7 +549,7 @@ class SendExpiringCertificateNotificationEmailActionTest {
|
||||
.setTypes(ImmutableSet.of(RegistrarPoc.Type.ADMIN))
|
||||
.setVisibleInWhoisAsTech(true)
|
||||
.build());
|
||||
persistSimpleResources(contacts);
|
||||
persistResources(contacts);
|
||||
assertThat(action.getEmailAddresses(registrar, Type.TECH))
|
||||
.containsExactly(
|
||||
new InternetAddress("will@example-registrar.tld"),
|
||||
@@ -700,7 +701,7 @@ class SendExpiringCertificateNotificationEmailActionTest {
|
||||
/** Returns persisted sample contacts with a customized contact email type. */
|
||||
private static ImmutableList<RegistrarPoc> persistSampleContacts(
|
||||
Registrar registrar, RegistrarPoc.Type emailType) {
|
||||
return persistSimpleResources(
|
||||
return persistResources(
|
||||
ImmutableList.of(
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
package google.registry.beam.common;
|
||||
|
||||
import static google.registry.persistence.transaction.JpaTransactionManagerExtension.makeRegistrar1;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.newContact;
|
||||
import static google.registry.testing.DatabaseHelper.newTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistResources;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
@@ -75,7 +76,7 @@ public class RegistryJpaReadTest {
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
Registrar ofyRegistrar = JpaIntegrationTestExtension.makeRegistrar2();
|
||||
insertInDb(ofyRegistrar);
|
||||
persistResource(ofyRegistrar);
|
||||
|
||||
ImmutableList.Builder<Contact> builder = new ImmutableList.Builder<>();
|
||||
|
||||
@@ -84,7 +85,7 @@ public class RegistryJpaReadTest {
|
||||
builder.add(contact);
|
||||
}
|
||||
contacts = builder.build();
|
||||
insertInDb(contacts);
|
||||
persistResources(contacts);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -211,6 +212,6 @@ public class RegistryJpaReadTest {
|
||||
null,
|
||||
100L))
|
||||
.build();
|
||||
insertInDb(registry, registrar, contact, domain);
|
||||
persistResources(registry, registrar, contact, domain);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import static google.registry.rde.RdeResourceType.DOMAIN;
|
||||
import static google.registry.rde.RdeResourceType.HOST;
|
||||
import static google.registry.rde.RdeResourceType.REGISTRAR;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.insertSimpleResources;
|
||||
import static google.registry.testing.DatabaseHelper.newDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
@@ -38,6 +37,7 @@ import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistEppResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistResources;
|
||||
import static google.registry.util.ResourceUtils.readResourceUtf8;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
@@ -223,7 +223,7 @@ public class RdePipelineTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
insertSimpleResources(ImmutableList.of(makeRegistrar1(), makeRegistrar2()));
|
||||
persistResources(ImmutableList.of(makeRegistrar1(), makeRegistrar2()));
|
||||
|
||||
// Two real registrars have been created by loadInitialData(), named "New Registrar" and "The
|
||||
// Registrar". Create one included registrar (external_monitoring) and two excluded ones.
|
||||
@@ -403,7 +403,8 @@ public class RdePipelineTest {
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
</rdeDomain:domain>\
|
||||
""");
|
||||
}
|
||||
if (kv.getKey().mode().equals(FULL)) {
|
||||
// Contact fragments for hello.soy.
|
||||
@@ -441,7 +442,8 @@ public class RdePipelineTest {
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
</rdeDomain:domain>\
|
||||
""");
|
||||
} else {
|
||||
// Contact fragments for cat.fun.
|
||||
assertThat(
|
||||
@@ -484,7 +486,8 @@ public class RdePipelineTest {
|
||||
<rdeDomain:crRr>TheRegistrar</rdeDomain:crRr>
|
||||
<rdeDomain:crDate>1970-01-01T00:00:00Z</rdeDomain:crDate>
|
||||
<rdeDomain:exDate>294247-01-10T04:00:54Z</rdeDomain:exDate>
|
||||
</rdeDomain:domain>""");
|
||||
</rdeDomain:domain>\
|
||||
""");
|
||||
}
|
||||
});
|
||||
return null;
|
||||
|
||||
@@ -23,7 +23,7 @@ import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKey;
|
||||
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
|
||||
import static google.registry.testing.DatabaseHelper.persistResources;
|
||||
import static org.joda.money.CurrencyUnit.JPY;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
@@ -179,7 +179,7 @@ public class SyncRegistrarsSheetTest {
|
||||
.build());
|
||||
// Use registrar key for contacts' parent.
|
||||
DateTime registrarCreationTime = persistResource(registrar).getCreationTime();
|
||||
persistSimpleResources(contacts);
|
||||
persistResources(contacts);
|
||||
|
||||
clock.advanceBy(standardMinutes(1));
|
||||
newSyncRegistrarsSheet().run("foobar");
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
package google.registry.flows.contact;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.testing.ContactSubject.assertAboutContacts;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.newContact;
|
||||
@@ -22,15 +25,19 @@ import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistDeletedContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.FlowUtils.NotLoggedInException;
|
||||
import google.registry.flows.ResourceFlowTestCase;
|
||||
import google.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException;
|
||||
import google.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException;
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -89,6 +96,18 @@ class ContactCreateFlowTest extends ResourceFlowTestCase<ContactCreateFlow, Cont
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_cannotCreateContacts() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
EppException thrown = assertThrows(ContactsProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_resourceContention() throws Exception {
|
||||
String targetId = getUniqueIdFromCommand();
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
package google.registry.flows.contact;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.testing.ContactSubject.assertAboutContacts;
|
||||
import static google.registry.testing.DatabaseHelper.assertNoBillingEvents;
|
||||
import static google.registry.testing.DatabaseHelper.newContact;
|
||||
@@ -22,10 +25,12 @@ import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistDeletedContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.EppExceptionSubject.assertAboutEppExceptions;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.FlowUtils.NotLoggedInException;
|
||||
import google.registry.flows.ResourceFlowTestCase;
|
||||
@@ -35,8 +40,10 @@ import google.registry.flows.ResourceFlowUtils.ResourceNotOwnedException;
|
||||
import google.registry.flows.ResourceFlowUtils.StatusNotClientSettableException;
|
||||
import google.registry.flows.contact.ContactFlowUtils.BadInternationalizedPostalInfoException;
|
||||
import google.registry.flows.contact.ContactFlowUtils.DeclineContactDisclosureFieldDisallowedPolicyException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactAddress;
|
||||
import google.registry.model.contact.PostalInfo;
|
||||
@@ -86,6 +93,18 @@ class ContactUpdateFlowTest extends ResourceFlowTestCase<ContactUpdateFlow, Cont
|
||||
doSuccessfulTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_cannotUpdateContacts() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
EppException thrown = assertThrows(ContactsProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_updatingInternationalizedPostalInfoDeletesLocalized() throws Exception {
|
||||
Contact contact =
|
||||
|
||||
@@ -25,6 +25,7 @@ import static google.registry.model.billing.BillingBase.Flag.SUNRISE;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS;
|
||||
@@ -133,6 +134,7 @@ import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTl
|
||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.PremiumNameBlockedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrarMustBeActiveForThisOperationException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TldDoesNotExistException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
|
||||
@@ -145,6 +147,7 @@ import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTok
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AllocationTokenNotValidForRegistrarException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.AlreadyRedeemedAllocationTokenException;
|
||||
import google.registry.flows.domain.token.AllocationTokenFlowUtils.NonexistentAllocationTokenException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.OnlyToolCanPassMetadataException;
|
||||
import google.registry.flows.exceptions.ResourceAlreadyExistsForThisClientException;
|
||||
import google.registry.flows.exceptions.ResourceCreateContentionException;
|
||||
@@ -247,12 +250,6 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
"badcrash,NAME_COLLISION"),
|
||||
persistReservedList("global-list", "resdom,FULLY_BLOCKED"))
|
||||
.build());
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE))
|
||||
.build());
|
||||
persistClaimsList(ImmutableMap.of("example-one", CLAIMS_KEY, "test-validate", CLAIMS_KEY));
|
||||
}
|
||||
|
||||
@@ -2104,8 +2101,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingRegistrant() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -2115,6 +2112,20 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_noRegistrantButSomeOtherContactTypes() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_missing_registrant.xml");
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(ContactsProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_missingAdmin() {
|
||||
setEppInput("domain_create_missing_admin.xml");
|
||||
@@ -2126,8 +2137,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingAdmin() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -2137,6 +2148,20 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_registrantAndOtherContactsSent() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_missing_admin.xml");
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(RegistrantProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_missingTech() {
|
||||
setEppInput("domain_create_missing_tech.xml");
|
||||
@@ -2148,8 +2173,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingTech() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -2170,8 +2195,8 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingNonRegistrantContacts() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -2181,6 +2206,34 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_registrantNotPermitted() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_missing_non_registrant_contacts.xml");
|
||||
persistContactsAndHosts();
|
||||
EppException thrown = assertThrows(RegistrantProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase2_noContactsWhatsoever() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_no_contacts.xml");
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badIdn() {
|
||||
createTld("xn--q9jyb4c");
|
||||
|
||||
@@ -150,53 +150,50 @@ class DomainRenewFlowTest extends ResourceFlowTestCase<DomainRenewFlow, Domain>
|
||||
StatusValue... statusValues)
|
||||
throws Exception {
|
||||
Domain domain = DatabaseHelper.newDomain(getUniqueIdFromCommand());
|
||||
tm().transact(
|
||||
() -> {
|
||||
try {
|
||||
DomainHistory historyEntryDomainCreate =
|
||||
new DomainHistory.Builder()
|
||||
.setDomain(domain)
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setRegistrarId(domain.getCreationRegistrarId())
|
||||
.build();
|
||||
BillingRecurrence autorenewEvent =
|
||||
new BillingRecurrence.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setTargetId(getUniqueIdFromCommand())
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setEventTime(expirationTime)
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setDomainHistory(historyEntryDomainCreate)
|
||||
.setRenewalPriceBehavior(renewalPriceBehavior)
|
||||
.setRenewalPrice(renewalPrice)
|
||||
.build();
|
||||
PollMessage.Autorenew autorenewPollMessage =
|
||||
new PollMessage.Autorenew.Builder()
|
||||
.setTargetId(getUniqueIdFromCommand())
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setEventTime(expirationTime)
|
||||
.setAutorenewEndTime(END_OF_TIME)
|
||||
.setMsg("Domain was auto-renewed.")
|
||||
.setHistoryEntry(historyEntryDomainCreate)
|
||||
.build();
|
||||
Domain newDomain =
|
||||
domain
|
||||
.asBuilder()
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.setStatusValues(ImmutableSet.copyOf(statusValues))
|
||||
.setAutorenewBillingEvent(autorenewEvent.createVKey())
|
||||
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
|
||||
.build();
|
||||
persistResources(
|
||||
ImmutableSet.of(
|
||||
historyEntryDomainCreate, autorenewEvent,
|
||||
autorenewPollMessage, newDomain));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error persisting domain", e);
|
||||
}
|
||||
});
|
||||
try {
|
||||
DomainHistory historyEntryDomainCreate =
|
||||
new DomainHistory.Builder()
|
||||
.setDomain(domain)
|
||||
.setType(HistoryEntry.Type.DOMAIN_CREATE)
|
||||
.setModificationTime(clock.nowUtc())
|
||||
.setRegistrarId(domain.getCreationRegistrarId())
|
||||
.build();
|
||||
BillingRecurrence autorenewEvent =
|
||||
new BillingRecurrence.Builder()
|
||||
.setReason(Reason.RENEW)
|
||||
.setFlags(ImmutableSet.of(Flag.AUTO_RENEW))
|
||||
.setTargetId(getUniqueIdFromCommand())
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setEventTime(expirationTime)
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setDomainHistory(historyEntryDomainCreate)
|
||||
.setRenewalPriceBehavior(renewalPriceBehavior)
|
||||
.setRenewalPrice(renewalPrice)
|
||||
.build();
|
||||
PollMessage.Autorenew autorenewPollMessage =
|
||||
new PollMessage.Autorenew.Builder()
|
||||
.setTargetId(getUniqueIdFromCommand())
|
||||
.setRegistrarId("TheRegistrar")
|
||||
.setEventTime(expirationTime)
|
||||
.setAutorenewEndTime(END_OF_TIME)
|
||||
.setMsg("Domain was auto-renewed.")
|
||||
.setHistoryEntry(historyEntryDomainCreate)
|
||||
.build();
|
||||
Domain newDomain =
|
||||
domain
|
||||
.asBuilder()
|
||||
.setRegistrationExpirationTime(expirationTime)
|
||||
.setStatusValues(ImmutableSet.copyOf(statusValues))
|
||||
.setAutorenewBillingEvent(autorenewEvent.createVKey())
|
||||
.setAutorenewPollMessage(autorenewPollMessage.createVKey())
|
||||
.build();
|
||||
persistResources(
|
||||
ImmutableSet.of(
|
||||
historyEntryDomainCreate, autorenewEvent,
|
||||
autorenewPollMessage, newDomain));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Error persisting domain", e);
|
||||
}
|
||||
clock.advanceOneMilli();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_PROHIBITED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_DELETE_PROHIBITED;
|
||||
@@ -89,10 +90,12 @@ import google.registry.flows.domain.DomainFlowUtils.NameserversNotAllowedForTldE
|
||||
import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTldWithNameserverAllowListException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.NotAuthorizedForTldException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantNotAllowedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.RegistrantProhibitedException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.SecDnsAllUsageException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TooManyDsRecordsException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.TooManyNameserversException;
|
||||
import google.registry.flows.domain.DomainFlowUtils.UrgentAttributeNotSupportedException;
|
||||
import google.registry.flows.exceptions.ContactsProhibitedException;
|
||||
import google.registry.flows.exceptions.OnlyToolCanPassMetadataException;
|
||||
import google.registry.flows.exceptions.ResourceHasClientUpdateProhibitedException;
|
||||
import google.registry.flows.exceptions.ResourceStatusProhibitsOperationException;
|
||||
@@ -146,12 +149,6 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
createTld("tld");
|
||||
// Note that "domain_update.xml" tests adding and removing the same contact type.
|
||||
setEppInput("domain_update.xml");
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE))
|
||||
.build());
|
||||
}
|
||||
|
||||
private void persistReferencedEntities() {
|
||||
@@ -336,8 +333,8 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_emptyRegistrant() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -348,6 +345,24 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertThat(reloadResourceByForeignKey().getRegistrant()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_nonRegistrantContactsStillExist() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_update_empty_registrant.xml");
|
||||
persistReferencedEntities();
|
||||
persistDomain();
|
||||
// Fails because after the update the domain would still have some contacts on it even though
|
||||
// the registrant has been removed.
|
||||
ContactsProhibitedException thrown =
|
||||
assertThrows(ContactsProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
private void modifyDomainToHave13Nameservers() throws Exception {
|
||||
ImmutableSet.Builder<VKey<Host>> nameservers = new ImmutableSet.Builder<>();
|
||||
for (int i = 1; i < 15; i++) {
|
||||
@@ -1540,8 +1555,8 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_removeAdmin() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -1558,6 +1573,29 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_minimumDatasetPhase2_registrantStillExists() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_update_remove_admin.xml");
|
||||
persistReferencedEntities();
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
|
||||
.build());
|
||||
RegistrantProhibitedException thrown =
|
||||
assertThrows(RegistrantProhibitedException.class, this::runFlow);
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_removeTech() throws Exception {
|
||||
setEppInput("domain_update_remove_tech.xml");
|
||||
@@ -1577,8 +1615,8 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_removeTech() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
@@ -1595,6 +1633,30 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase2_removeAllContacts() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_PROHIBITED)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_update_remove_all_contacts.xml");
|
||||
persistReferencedEntities();
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
|
||||
.build());
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
Domain updatedDomain = reloadResourceByForeignKey();
|
||||
assertThat(updatedDomain.getRegistrant()).isEmpty();
|
||||
assertThat(updatedDomain.getContacts()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_addPendingDeleteContact() throws Exception {
|
||||
persistReferencedEntities();
|
||||
|
||||
@@ -24,7 +24,7 @@ import static google.registry.testing.CertificateSamples.SAMPLE_CERT_HASH;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKeyIfPresent;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -275,7 +275,7 @@ public final class OteAccountBuilderTest {
|
||||
|
||||
@Test
|
||||
void testCreateOteEntities_registrarExists_failsWhenNotReplaceExisting() {
|
||||
persistSimpleResource(makeRegistrar1().asBuilder().setRegistrarId("myclientid-1").build());
|
||||
persistResource(makeRegistrar1().asBuilder().setRegistrarId("myclientid-1").build());
|
||||
|
||||
OteAccountBuilder oteSetupHelper = OteAccountBuilder.forRegistrarId("myclientid");
|
||||
|
||||
@@ -301,7 +301,7 @@ public final class OteAccountBuilderTest {
|
||||
|
||||
@Test
|
||||
void testCreateOteEntities_entitiesExist_succeedsWhenReplaceExisting() {
|
||||
persistSimpleResource(makeRegistrar1().asBuilder().setRegistrarId("myclientid-1").build());
|
||||
persistResource(makeRegistrar1().asBuilder().setRegistrarId("myclientid-1").build());
|
||||
// we intentionally create the -ga TLD with the wrong state, to make sure it's overwritten.
|
||||
createTld("myclientid-ga", START_DATE_SUNRISE);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.model.common;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -41,7 +41,7 @@ public class DnsRefreshRequestTest extends EntityTestCase {
|
||||
void testPersistence() {
|
||||
assertThat(request.getLastProcessTime()).isEqualTo(START_OF_TIME);
|
||||
fakeClock.advanceOneMilli();
|
||||
insertInDb(request);
|
||||
tm().transact(() -> tm().insert(request));
|
||||
fakeClock.advanceOneMilli();
|
||||
ImmutableList<DnsRefreshRequest> requests = loadAllOf(DnsRefreshRequest.class);
|
||||
assertThat(requests.size()).isEqualTo(1);
|
||||
|
||||
@@ -56,6 +56,7 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
persistResource(featureFlag);
|
||||
FeatureFlag flagFromDb = loadByEntity(featureFlag);
|
||||
assertThat(featureFlag).isEqualTo(flagFromDb);
|
||||
assertThat(featureFlag.getFeatureName()).isEqualTo(TEST_FEATURE);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -217,23 +218,12 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
.build());
|
||||
tm().transact(
|
||||
() -> {
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, false)).isFalse();
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, true)).isFalse();
|
||||
assertThat(FeatureFlag.isActiveNow(TEST_FEATURE)).isFalse();
|
||||
});
|
||||
fakeClock.advanceBy(Duration.standardDays(365));
|
||||
tm().transact(
|
||||
() -> {
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, false)).isTrue();
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, true)).isTrue();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_default_doesNotExist() {
|
||||
tm().transact(
|
||||
() -> {
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, false)).isFalse();
|
||||
assertThat(FeatureFlag.isActiveNowOrElse(TEST_FEATURE, true)).isTrue();
|
||||
assertThat(FeatureFlag.isActiveNow(TEST_FEATURE)).isTrue();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableO
|
||||
import static google.registry.testing.ContactSubject.assertAboutContacts;
|
||||
import static google.registry.testing.DatabaseHelper.cloneAndSetAutoTimestamps;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.SqlHelper.assertThrowForeignKeyViolation;
|
||||
@@ -132,7 +131,7 @@ public class ContactTest extends EntityTestCase {
|
||||
void testCloudSqlPersistence_failWhenViolateForeignKeyConstraint() {
|
||||
assertThrowForeignKeyViolation(
|
||||
() ->
|
||||
insertInDb(
|
||||
persistResource(
|
||||
originalContact
|
||||
.asBuilder()
|
||||
.setRepoId("2-FOOBAR")
|
||||
|
||||
@@ -20,11 +20,10 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKey;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.updateInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResources;
|
||||
import static google.registry.testing.SqlHelper.assertThrowForeignKeyViolation;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
@@ -169,13 +168,13 @@ public class DomainSqlTest {
|
||||
@Test
|
||||
void testHostForeignKeyConstraints() {
|
||||
// Persist the domain without the associated host object.
|
||||
assertThrowForeignKeyViolation(() -> insertInDb(contact, contact2, domain));
|
||||
assertThrowForeignKeyViolation(() -> persistResources(contact, contact2, domain));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testContactForeignKeyConstraints() {
|
||||
// Persist the domain without the associated contact objects.
|
||||
assertThrowForeignKeyViolation(() -> insertInDb(domain, host));
|
||||
assertThrowForeignKeyViolation(() -> persistResources(domain, host));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -358,7 +357,7 @@ public class DomainSqlTest {
|
||||
@Test
|
||||
void testSerializable() {
|
||||
createTld("com");
|
||||
insertInDb(contact, contact2, domain, host);
|
||||
persistResources(contact, contact2, domain, host);
|
||||
Domain persisted = tm().transact(() -> tm().loadByEntity(domain));
|
||||
assertThat(SerializeUtils.serializeDeserialize(persisted)).isEqualTo(persisted);
|
||||
}
|
||||
@@ -366,9 +365,9 @@ public class DomainSqlTest {
|
||||
@Test
|
||||
void testUpdates() {
|
||||
createTld("com");
|
||||
insertInDb(contact, contact2, domain, host);
|
||||
persistResources(contact, contact2, domain, host);
|
||||
domain = domain.asBuilder().setNameservers(ImmutableSet.of()).build();
|
||||
updateInDb(domain);
|
||||
persistResource(domain);
|
||||
assertAboutImmutableObjects()
|
||||
.that(loadByEntity(domain))
|
||||
.isEqualExceptFields(domain, "updateTimestamp", "creationTime");
|
||||
@@ -385,7 +384,7 @@ public class DomainSqlTest {
|
||||
|
||||
private void persistDomain() {
|
||||
createTld("com");
|
||||
insertInDb(contact, contact2, domain, host);
|
||||
persistResources(contact, contact2, domain, host);
|
||||
}
|
||||
|
||||
private <T> VKey<T> createKey(Class<T> clazz, String key) {
|
||||
@@ -414,24 +413,19 @@ public class DomainSqlTest {
|
||||
Domain persisted = loadByKey(domain.createVKey());
|
||||
DateTime originalUpdateTime = persisted.getUpdateTimestamp().getTimestamp();
|
||||
fakeClock.advanceOneMilli();
|
||||
DateTime transactionTime =
|
||||
tm().transact(
|
||||
() -> {
|
||||
Host host2 =
|
||||
new Host.Builder()
|
||||
.setRepoId("host2")
|
||||
.setHostName("ns2.example.com")
|
||||
.setCreationRegistrarId("registrar1")
|
||||
.setPersistedCurrentSponsorRegistrarId("registrar2")
|
||||
.build();
|
||||
insertInDb(host2);
|
||||
Host host2 =
|
||||
new Host.Builder()
|
||||
.setRepoId("host2")
|
||||
.setHostName("ns2.example.com")
|
||||
.setCreationRegistrarId("registrar1")
|
||||
.setPersistedCurrentSponsorRegistrarId("registrar2")
|
||||
.build();
|
||||
persistResource(host2);
|
||||
domain = persisted.asBuilder().addNameserver(host2.createVKey()).build();
|
||||
updateInDb(domain);
|
||||
return tm().getTransactionTime();
|
||||
});
|
||||
persistResource(domain);
|
||||
domain = loadByKey(domain.createVKey());
|
||||
assertThat(domain.getUpdateTimestamp().getTimestamp()).isEqualTo(transactionTime);
|
||||
assertThat(domain.getUpdateTimestamp().getTimestamp()).isNotEqualTo(originalUpdateTime);
|
||||
assertThat(domain.getUpdateTimestamp().getTimestamp())
|
||||
.isEqualTo(originalUpdateTime.plusMillis(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -440,20 +434,14 @@ public class DomainSqlTest {
|
||||
Domain persisted = loadByKey(domain.createVKey());
|
||||
DateTime originalUpdateTime = persisted.getUpdateTimestamp().getTimestamp();
|
||||
fakeClock.advanceOneMilli();
|
||||
DateTime transactionTime =
|
||||
tm().transact(
|
||||
() -> {
|
||||
domain =
|
||||
persisted
|
||||
.asBuilder()
|
||||
.setDsData(
|
||||
ImmutableSet.of(DomainDsData.create(1, 2, 3, new byte[] {0, 1, 2})))
|
||||
.build();
|
||||
updateInDb(domain);
|
||||
return tm().getTransactionTime();
|
||||
});
|
||||
domain =
|
||||
persisted
|
||||
.asBuilder()
|
||||
.setDsData(ImmutableSet.of(DomainDsData.create(1, 2, 3, new byte[] {0, 1, 2})))
|
||||
.build();
|
||||
persistResource(domain);
|
||||
domain = loadByKey(domain.createVKey());
|
||||
assertThat(domain.getUpdateTimestamp().getTimestamp()).isEqualTo(transactionTime);
|
||||
assertThat(domain.getUpdateTimestamp().getTimestamp()).isNotEqualTo(originalUpdateTime);
|
||||
assertThat(domain.getUpdateTimestamp().getTimestamp())
|
||||
.isEqualTo(originalUpdateTime.plusMillis(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.testing.DatabaseHelper.cloneAndSetAutoTimestamps;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.newHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveDomain;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistResources;
|
||||
import static google.registry.testing.DomainSubject.assertAboutDomains;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
@@ -162,7 +162,7 @@ public class DomainTest {
|
||||
.setRecurrenceEndTime(END_OF_TIME)
|
||||
.setDomainHistory(historyEntry)
|
||||
.build();
|
||||
insertInDb(historyEntry, billingEventBill, billingRecurrence);
|
||||
persistResources(historyEntry, billingEventBill, billingRecurrence);
|
||||
recurrenceBillKey = billingRecurrence.createVKey();
|
||||
VKey<PollMessage.Autorenew> autorenewPollKey = VKey.create(PollMessage.Autorenew.class, 3L);
|
||||
VKey<PollMessage.OneTime> onetimePollKey = VKey.create(PollMessage.OneTime.class, 1L);
|
||||
|
||||
@@ -16,8 +16,8 @@ package google.registry.model.eppcommon;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -50,25 +50,25 @@ class AddressTest {
|
||||
|
||||
private static final String ENTITY_XML =
|
||||
"""
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<testEntity>
|
||||
<address>
|
||||
<street>123 W 14th St</street>
|
||||
<street>8th Fl</street>
|
||||
<street>Rm 8</street>
|
||||
<city>New York</city>
|
||||
<sp>NY</sp>
|
||||
<pc>10011</pc>
|
||||
<cc>US</cc>
|
||||
</address>
|
||||
</testEntity>
|
||||
""";
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<testEntity>
|
||||
<address>
|
||||
<street>123 W 14th St</street>
|
||||
<street>8th Fl</street>
|
||||
<street>Rm 8</street>
|
||||
<city>New York</city>
|
||||
<sp>NY</sp>
|
||||
<pc>10011</pc>
|
||||
<cc>US</cc>
|
||||
</address>
|
||||
</testEntity>
|
||||
""";
|
||||
|
||||
private TestAddress address = createAddress("123 W 14th St", "8th Fl", "Rm 8");
|
||||
private TestEntity entity = new TestEntity(1L, address);
|
||||
|
||||
private static TestEntity saveAndLoad(TestEntity entity) {
|
||||
insertInDb(entity);
|
||||
persistResource(entity);
|
||||
return loadByEntity(entity);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ package google.registry.model.history;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.newContactWithRoid;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -45,10 +45,10 @@ public class ContactHistoryTest extends EntityTestCase {
|
||||
@Test
|
||||
void testPersistence() {
|
||||
Contact contact = newContactWithRoid("contactId", "contact1");
|
||||
insertInDb(contact);
|
||||
persistResource(contact);
|
||||
Contact contactFromDb = loadByEntity(contact);
|
||||
ContactHistory contactHistory = createContactHistory(contactFromDb);
|
||||
insertInDb(contactHistory);
|
||||
persistResource(contactHistory);
|
||||
tm().transact(
|
||||
() -> {
|
||||
ContactHistory fromDatabase = tm().loadByKey(contactHistory.createVKey());
|
||||
@@ -60,10 +60,10 @@ public class ContactHistoryTest extends EntityTestCase {
|
||||
@Test
|
||||
void testSerializable() {
|
||||
Contact contact = newContactWithRoid("contactId", "contact1");
|
||||
insertInDb(contact);
|
||||
persistResource(contact);
|
||||
Contact contactFromDb = loadByEntity(contact);
|
||||
ContactHistory contactHistory = createContactHistory(contactFromDb);
|
||||
insertInDb(contactHistory);
|
||||
persistResource(contactHistory);
|
||||
ContactHistory fromDatabase = tm().transact(() -> tm().loadByKey(contactHistory.createVKey()));
|
||||
assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
|
||||
}
|
||||
|
||||
@@ -18,10 +18,10 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.newContactWithRoid;
|
||||
import static google.registry.testing.DatabaseHelper.newDomain;
|
||||
import static google.registry.testing.DatabaseHelper.newHostWithRoid;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
@@ -60,7 +60,7 @@ public class DomainHistoryTest extends EntityTestCase {
|
||||
void testPersistence() {
|
||||
Domain domain = addGracePeriodForSql(createDomainWithContactsAndHosts());
|
||||
DomainHistory domainHistory = createDomainHistory(domain);
|
||||
insertInDb(domainHistory);
|
||||
persistResource(domainHistory);
|
||||
|
||||
tm().transact(
|
||||
() -> {
|
||||
@@ -74,7 +74,7 @@ public class DomainHistoryTest extends EntityTestCase {
|
||||
void testSerializable() {
|
||||
Domain domain = addGracePeriodForSql(createDomainWithContactsAndHosts());
|
||||
DomainHistory domainHistory = createDomainHistory(domain);
|
||||
insertInDb(domainHistory);
|
||||
persistResource(domainHistory);
|
||||
DomainHistory fromDatabase = tm().transact(() -> tm().loadByKey(domainHistory.createVKey()));
|
||||
assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
|
||||
}
|
||||
@@ -96,7 +96,7 @@ public class DomainHistoryTest extends EntityTestCase {
|
||||
.setNameservers(host.createVKey())
|
||||
.setDsData(ImmutableSet.of(DomainDsData.create(1, 2, 3, new byte[] {0, 1, 2})))
|
||||
.build();
|
||||
insertInDb(domain);
|
||||
persistResource(domain);
|
||||
return domain;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ package google.registry.model.history;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.newHostWithRoid;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import google.registry.model.EntityTestCase;
|
||||
@@ -41,10 +41,10 @@ public class HostHistoryTest extends EntityTestCase {
|
||||
@Test
|
||||
void testPersistence() {
|
||||
Host host = newHostWithRoid("ns1.example.com", "host1");
|
||||
insertInDb(host);
|
||||
persistResource(host);
|
||||
Host hostFromDb = loadByEntity(host);
|
||||
HostHistory hostHistory = createHostHistory(hostFromDb);
|
||||
insertInDb(hostHistory);
|
||||
persistResource(hostHistory);
|
||||
tm().transact(
|
||||
() -> {
|
||||
HostHistory fromDatabase = tm().loadByKey(hostHistory.createVKey());
|
||||
@@ -56,10 +56,10 @@ public class HostHistoryTest extends EntityTestCase {
|
||||
@Test
|
||||
void testSerializable() {
|
||||
Host host = newHostWithRoid("ns1.example.com", "host1");
|
||||
insertInDb(host);
|
||||
persistResource(host);
|
||||
Host hostFromDb = loadByEntity(host);
|
||||
HostHistory hostHistory = createHostHistory(hostFromDb);
|
||||
insertInDb(hostHistory);
|
||||
persistResource(hostHistory);
|
||||
HostHistory fromDatabase = tm().transact(() -> tm().loadByKey(hostHistory.createVKey()));
|
||||
assertThat(SerializeUtils.serializeDeserialize(fromDatabase)).isEqualTo(fromDatabase);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.model.poll;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKey;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
@@ -90,12 +89,12 @@ public class PollMessageTest extends EntityTestCase {
|
||||
|
||||
@Test
|
||||
void testCloudSqlSupportForPolymorphicVKey() {
|
||||
insertInDb(oneTime);
|
||||
persistResource(oneTime);
|
||||
PollMessage persistedOneTime = loadByKey(VKey.create(PollMessage.class, oneTime.getId()));
|
||||
assertThat(persistedOneTime).isInstanceOf(PollMessage.OneTime.class);
|
||||
assertThat(persistedOneTime).isEqualTo(oneTime);
|
||||
|
||||
insertInDb(autoRenew);
|
||||
persistResource(autoRenew);
|
||||
PollMessage persistedAutoRenew = loadByKey(VKey.create(PollMessage.class, autoRenew.getId()));
|
||||
assertThat(persistedAutoRenew).isInstanceOf(PollMessage.Autorenew.class);
|
||||
assertThat(persistedAutoRenew).isEqualTo(autoRenew);
|
||||
|
||||
@@ -25,8 +25,7 @@ import static google.registry.testing.DatabaseHelper.cloneAndSetAutoTimestamps;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.newTld;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResource;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResources;
|
||||
import static google.registry.testing.DatabaseHelper.persistResources;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.JPY;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
@@ -132,7 +131,7 @@ class RegistrarTest extends EntityTestCase {
|
||||
.setFaxNumber("+1.2125551213")
|
||||
.setTypes(ImmutableSet.of(RegistrarPoc.Type.ABUSE, RegistrarPoc.Type.ADMIN))
|
||||
.build();
|
||||
persistSimpleResources(
|
||||
persistResources(
|
||||
ImmutableList.of(
|
||||
abuseAdminContact,
|
||||
new RegistrarPoc.Builder()
|
||||
@@ -301,7 +300,7 @@ class RegistrarTest extends EntityTestCase {
|
||||
|
||||
@Test
|
||||
void testSuccess_emptyContactTypesAllowed() {
|
||||
persistSimpleResource(
|
||||
persistResource(
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName("John Abussy")
|
||||
@@ -318,7 +317,7 @@ class RegistrarTest extends EntityTestCase {
|
||||
@Test
|
||||
void testSuccess_getContactsByType() {
|
||||
RegistrarPoc newTechContact =
|
||||
persistSimpleResource(
|
||||
persistResource(
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName("Jake Tech")
|
||||
@@ -330,7 +329,7 @@ class RegistrarTest extends EntityTestCase {
|
||||
.setTypes(ImmutableSet.of(RegistrarPoc.Type.TECH))
|
||||
.build());
|
||||
RegistrarPoc newTechAbuseContact =
|
||||
persistSimpleResource(
|
||||
persistResource(
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName("Jim Tech-Abuse")
|
||||
|
||||
@@ -17,9 +17,10 @@ package google.registry.model.reporting;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.model.reporting.Spec11ThreatMatch.ThreatType.MALWARE;
|
||||
import static google.registry.model.reporting.Spec11ThreatMatch.ThreatType.UNWANTED_SOFTWARE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResources;
|
||||
import static google.registry.testing.SqlHelper.assertThrowForeignKeyViolation;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -105,7 +106,7 @@ public final class Spec11ThreatMatchTest extends EntityTestCase {
|
||||
void testPersistence() {
|
||||
createTld("tld");
|
||||
saveRegistrar(REGISTRAR_ID);
|
||||
insertInDb(registrantContact, domain, host, threat);
|
||||
tm().transact(() -> tm().insertAll(registrantContact, domain, host, threat));
|
||||
assertAboutImmutableObjects().that(loadByEntity(threat)).isEqualExceptFields(threat, "id");
|
||||
}
|
||||
|
||||
@@ -113,12 +114,12 @@ public final class Spec11ThreatMatchTest extends EntityTestCase {
|
||||
@Disabled("We can't rely on foreign keys until we've migrated to SQL")
|
||||
void testThreatForeignKeyConstraints() {
|
||||
// Persist the threat without the associated registrar.
|
||||
assertThrowForeignKeyViolation(() -> insertInDb(host, registrantContact, domain, threat));
|
||||
assertThrowForeignKeyViolation(() -> persistResources(host, registrantContact, domain, threat));
|
||||
|
||||
saveRegistrar(REGISTRAR_ID);
|
||||
|
||||
// Persist the threat without the associated domain.
|
||||
assertThrowForeignKeyViolation(() -> insertInDb(registrantContact, host, threat));
|
||||
assertThrowForeignKeyViolation(() -> persistResources(registrantContact, host, threat));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -29,6 +29,7 @@ import google.registry.testing.TestDataHelper;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Unit tests for {@link RequestComponent}. */
|
||||
@@ -49,6 +50,7 @@ public class RequestComponentTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled("To be removed with GAE components")
|
||||
void testGaeToJettyRoutingCoverage() {
|
||||
Set<Route> jettyRoutes = getRoutes(RequestComponent.class, "routing.txt");
|
||||
Set<Route> gaeRoutes = new HashSet<>();
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.ImmutableObject;
|
||||
@@ -51,7 +51,7 @@ class EntityCallbacksListenerTest {
|
||||
@Test
|
||||
void verifyAllCallbacks_executedExpectedTimes() {
|
||||
TestEntity testPersist = new TestEntity();
|
||||
insertInDb(testPersist);
|
||||
tm().transact(() -> tm().insert(testPersist));
|
||||
checkAll(testPersist, 1, 0, 0, 0);
|
||||
|
||||
TestEntity testUpdate = new TestEntity();
|
||||
@@ -99,7 +99,7 @@ class EntityCallbacksListenerTest {
|
||||
|
||||
@Test
|
||||
void verifyCallbacksNotCalledOnCommit() {
|
||||
insertInDb(new TestEntity());
|
||||
persistResource(new TestEntity());
|
||||
|
||||
TestEntity testLoad = tm().transact(() -> tm().loadByKey(VKey.create(TestEntity.class, "id")));
|
||||
assertThat(testLoad.entityPreUpdate).isEqualTo(0);
|
||||
|
||||
@@ -19,7 +19,7 @@ import static google.registry.model.domain.token.AllocationToken.TokenStatus.END
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.NOT_STARTED;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenStatus.VALID;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
@@ -59,7 +59,7 @@ public class AllocationTokenStatusTransitionUserTypeTest {
|
||||
TimedTransitionProperty.fromValueMap(values);
|
||||
AllocationTokenStatusTransitionConverterTestEntity testEntity =
|
||||
new AllocationTokenStatusTransitionConverterTestEntity(timedTransitionProperty);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
AllocationTokenStatusTransitionConverterTestEntity persisted =
|
||||
tm().transact(
|
||||
() ->
|
||||
|
||||
@@ -18,7 +18,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.SINGLE_USE;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.UNLIMITED_USE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.ImmutableObject;
|
||||
@@ -51,7 +51,7 @@ public class AllocationTokenVkeyListUserTypeTest {
|
||||
List<VKey<AllocationToken>> tokens = ImmutableList.of(token1.createVKey(), token2.createVKey());
|
||||
TestAllocationTokenVKeyList testAllocationTokenVKeyList =
|
||||
new TestAllocationTokenVKeyList(tokens);
|
||||
insertInDb(testAllocationTokenVKeyList);
|
||||
persistResource(testAllocationTokenVKeyList);
|
||||
TestAllocationTokenVKeyList persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestAllocationTokenVKeyList.class, "id"));
|
||||
assertThat(persisted.tokenList).isEqualTo(tokens);
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
|
||||
@@ -52,7 +52,7 @@ public class BillingCostTransitionUserTypeTest {
|
||||
TimedTransitionProperty<Money> timedTransitionProperty =
|
||||
TimedTransitionProperty.fromValueMap(values);
|
||||
TestEntity testEntity = new TestEntity(timedTransitionProperty);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.timedTransitionProperty.toValueMap())
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
import static com.google.common.hash.Funnels.stringFunnel;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -41,7 +41,7 @@ class BloomFilterConverterTest {
|
||||
BloomFilter<String> bloomFilter = BloomFilter.create(stringFunnel(US_ASCII), 3);
|
||||
ImmutableSet.of("foo", "bar", "baz").forEach(bloomFilter::put);
|
||||
TestEntity entity = new TestEntity(bloomFilter);
|
||||
insertInDb(entity);
|
||||
persistResource(entity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.bloomFilter).isEqualTo(bloomFilter);
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.ImmutableObject;
|
||||
@@ -47,7 +47,7 @@ public class CidrBlockListUserTypeTest {
|
||||
CidrAddressBlock.create("8000::/1"),
|
||||
CidrAddressBlock.create("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"));
|
||||
TestEntity testEntity = new TestEntity(addresses);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.addresses).isEqualTo(addresses);
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.ImmutableObject;
|
||||
@@ -44,7 +44,7 @@ public class CurrencyToStringMapUserTypeTest {
|
||||
CurrencyUnit.of("USD"), "accountId1",
|
||||
CurrencyUnit.of("CNY"), "accountId2");
|
||||
TestEntity testEntity = new TestEntity(currencyToBilling);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.currencyToBilling).containsExactlyEntriesIn(currencyToBilling);
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
@@ -39,7 +39,7 @@ public class CurrencyUnitConverterTest {
|
||||
@Test
|
||||
void roundTripConversion() {
|
||||
TestEntity entity = new TestEntity(CurrencyUnit.EUR);
|
||||
insertInDb(entity);
|
||||
persistResource(entity);
|
||||
assertThat(
|
||||
tm().transact(
|
||||
() ->
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static java.time.ZoneOffset.UTC;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
@@ -75,7 +75,7 @@ public class DateTimeConverterTest {
|
||||
void converter_generatesTimestampWithNormalizedZone() {
|
||||
DateTime dt = parseDateTime("2019-09-01T01:01:01Z");
|
||||
TestEntity entity = new TestEntity("normalized_utc_time", dt);
|
||||
insertInDb(entity);
|
||||
persistResource(entity);
|
||||
TestEntity retrievedEntity =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "normalized_utc_time"));
|
||||
assertThat(retrievedEntity.dt.toString()).isEqualTo("2019-09-01T01:01:01.000Z");
|
||||
@@ -86,7 +86,7 @@ public class DateTimeConverterTest {
|
||||
DateTime dt = parseDateTime("2019-09-01T01:01:01-05:00");
|
||||
TestEntity entity = new TestEntity("new_york_time", dt);
|
||||
|
||||
insertInDb(entity);
|
||||
persistResource(entity);
|
||||
TestEntity retrievedEntity =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "new_york_time"));
|
||||
assertThat(retrievedEntity.dt.toString()).isEqualTo("2019-09-01T06:01:01.000Z");
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
@@ -39,7 +39,7 @@ public class DurationUserTypeTest {
|
||||
@Test
|
||||
void testNulls() {
|
||||
DurationTestEntity entity = new DurationTestEntity(null);
|
||||
insertInDb(entity);
|
||||
persistResource(entity);
|
||||
DurationTestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(DurationTestEntity.class, "id"));
|
||||
assertThat(persisted.duration).isNull();
|
||||
@@ -90,7 +90,7 @@ public class DurationUserTypeTest {
|
||||
|
||||
private void assertPersistedEntityHasSameDuration(Duration duration) {
|
||||
DurationTestEntity entity = new DurationTestEntity(duration);
|
||||
insertInDb(entity);
|
||||
persistResource(entity);
|
||||
DurationTestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(DurationTestEntity.class, "id"));
|
||||
assertThat(persisted.duration.getMillis()).isEqualTo(duration.getMillis());
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.net.InetAddresses;
|
||||
@@ -64,7 +64,7 @@ public class InetAddressSetUserTypeTest {
|
||||
|
||||
private void verifySaveAndLoad(@Nullable Set<InetAddress> inetAddresses) {
|
||||
InetAddressSetTestEntity testEntity = new InetAddressSetTestEntity(inetAddresses);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
InetAddressSetTestEntity persisted =
|
||||
tm().transact(() -> tm().loadByKey(VKey.create(InetAddressSetTestEntity.class, "id")));
|
||||
assertThat(persisted.addresses).isEqualTo(inetAddresses);
|
||||
|
||||
@@ -15,7 +15,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -57,7 +57,7 @@ public class JodaMoneyTypeTest {
|
||||
Money money = Money.of(CurrencyUnit.USD, 100.12);
|
||||
assertThat(money.getAmount().scale()).isEqualTo(2);
|
||||
TestEntity entity = new TestEntity(money);
|
||||
insertInDb(entity);
|
||||
persistResource(entity);
|
||||
List<?> result =
|
||||
tm().transact(
|
||||
() ->
|
||||
@@ -81,7 +81,7 @@ public class JodaMoneyTypeTest {
|
||||
Money money = Money.ofMajor(CurrencyUnit.JPY, 100);
|
||||
assertThat(money.getAmount().scale()).isEqualTo(0); // JPY's amount has scale at 0.
|
||||
TestEntity entity = new TestEntity(money);
|
||||
insertInDb(entity);
|
||||
persistResource(entity);
|
||||
List<?> result =
|
||||
tm().transact(
|
||||
() ->
|
||||
@@ -121,7 +121,7 @@ public class JodaMoneyTypeTest {
|
||||
"dos", Money.ofMajor(CurrencyUnit.JPY, 2000),
|
||||
"tres", Money.of(CurrencyUnit.GBP, 20));
|
||||
ComplexTestEntity entity = new ComplexTestEntity(moneyMap, myMoney, yourMoney);
|
||||
insertInDb(entity);
|
||||
persistResource(entity);
|
||||
List<?> result =
|
||||
tm().transact(
|
||||
() ->
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -54,7 +54,7 @@ public class LocalDateConverterTest {
|
||||
|
||||
private LocalDateConverterTestEntity persistAndLoadTestEntity(LocalDate date) {
|
||||
LocalDateConverterTestEntity entity = new LocalDateConverterTestEntity(date);
|
||||
insertInDb(entity);
|
||||
persistResource(entity);
|
||||
return tm().transact(
|
||||
() -> tm().loadByKey(VKey.create(LocalDateConverterTestEntity.class, "id")));
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class RegistrarToRoleMapUserTypeTest {
|
||||
"FooRegistrar",
|
||||
RegistrarRole.TECH_CONTACT);
|
||||
TestEntity entity = new TestEntity(map);
|
||||
DatabaseHelper.insertInDb(entity);
|
||||
DatabaseHelper.persistResource(entity);
|
||||
TestEntity persisted = Iterables.getOnlyElement(DatabaseHelper.loadAllOf(TestEntity.class));
|
||||
assertThat(persisted.map).isEqualTo(map);
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ package google.registry.persistence.converter;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -45,7 +45,7 @@ public class StringCollectionUserTypeTest {
|
||||
List<ListElement> value =
|
||||
ImmutableList.of(new ListElement("app"), new ListElement("dev"), new ListElement("com"));
|
||||
TestEntity testEntity = new TestEntity(value);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.value).containsExactlyElementsIn(value);
|
||||
@@ -56,7 +56,7 @@ public class StringCollectionUserTypeTest {
|
||||
List<ListElement> value =
|
||||
ImmutableList.of(new ListElement("app"), new ListElement("dev"), new ListElement("com"));
|
||||
TestEntity testEntity = new TestEntity(value);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
persisted.value = ImmutableList.of(new ListElement("app"), new ListElement("org"));
|
||||
@@ -68,7 +68,7 @@ public class StringCollectionUserTypeTest {
|
||||
@Test
|
||||
void testNullValue_writesAndReadsNullSuccessfully() {
|
||||
TestEntity testEntity = new TestEntity(null);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.value).isNull();
|
||||
@@ -77,7 +77,7 @@ public class StringCollectionUserTypeTest {
|
||||
@Test
|
||||
void testEmptyCollection_writesAndReadsEmptyCollectionSuccessfully() {
|
||||
TestEntity testEntity = new TestEntity(ImmutableList.of());
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.value).isEmpty();
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -41,7 +41,7 @@ public class StringListConversionTest {
|
||||
void roundTripConversion_returnsSameStringList() {
|
||||
List<String> tlds = ImmutableList.of("app", "dev", "how");
|
||||
TestEntity testEntity = new TestEntity(tlds);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.tlds).containsExactly("app", "dev", "how");
|
||||
@@ -51,7 +51,7 @@ public class StringListConversionTest {
|
||||
void testMerge_succeeds() {
|
||||
List<String> tlds = ImmutableList.of("app", "dev", "how");
|
||||
TestEntity testEntity = new TestEntity(tlds);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
persisted.tlds = ImmutableList.of("com", "gov");
|
||||
@@ -63,7 +63,7 @@ public class StringListConversionTest {
|
||||
@Test
|
||||
void testNullValue_writesAndReadsNullSuccessfully() {
|
||||
TestEntity testEntity = new TestEntity(null);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.tlds).isNull();
|
||||
@@ -72,7 +72,7 @@ public class StringListConversionTest {
|
||||
@Test
|
||||
void testEmptyCollection_writesAndReadsEmptyCollectionSuccessfully() {
|
||||
TestEntity testEntity = new TestEntity(ImmutableList.of());
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.tlds).isEmpty();
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@@ -47,7 +47,7 @@ public class StringMapUserTypeTest {
|
||||
@Test
|
||||
void roundTripConversion_returnsSameMap() {
|
||||
TestEntity testEntity = new TestEntity(MAP);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.map).containsExactlyEntriesIn(MAP);
|
||||
@@ -56,7 +56,7 @@ public class StringMapUserTypeTest {
|
||||
@Test
|
||||
void testUpdateColumn_succeeds() {
|
||||
TestEntity testEntity = new TestEntity(MAP);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.map).containsExactlyEntriesIn(MAP);
|
||||
@@ -69,7 +69,7 @@ public class StringMapUserTypeTest {
|
||||
@Test
|
||||
void testNullValue_writesAndReadsNullSuccessfully() {
|
||||
TestEntity testEntity = new TestEntity(null);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.map).isNull();
|
||||
@@ -78,7 +78,7 @@ public class StringMapUserTypeTest {
|
||||
@Test
|
||||
void testEmptyMap_writesAndReadsEmptyCollectionSuccessfully() {
|
||||
TestEntity testEntity = new TestEntity(ImmutableMap.of());
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.map).isEmpty();
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.model.ImmutableObject;
|
||||
@@ -39,7 +39,7 @@ public class StringSetConversionTest {
|
||||
void roundTripConversion_returnsSameStringList() {
|
||||
Set<String> tlds = ImmutableSet.of("app", "dev", "how");
|
||||
TestEntity testEntity = new TestEntity(tlds);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.tlds).containsExactly("app", "dev", "how");
|
||||
@@ -48,7 +48,7 @@ public class StringSetConversionTest {
|
||||
@Test
|
||||
void testNullValue_writesAndReadsNullSuccessfully() {
|
||||
TestEntity testEntity = new TestEntity(null);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.tlds).isNull();
|
||||
@@ -57,7 +57,7 @@ public class StringSetConversionTest {
|
||||
@Test
|
||||
void testEmptyCollection_writesAndReadsEmptyCollectionSuccessfully() {
|
||||
TestEntity testEntity = new TestEntity(ImmutableSet.of());
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.tlds).isEmpty();
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.registrar.Registrar.State;
|
||||
@@ -39,7 +39,7 @@ public class StringValueEnumeratedTest {
|
||||
@Test
|
||||
void roundTripConversion_returnsSameEnum() {
|
||||
TestEntity testEntity = new TestEntity(State.ACTIVE);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.state).isEqualTo(State.ACTIVE);
|
||||
@@ -48,7 +48,7 @@ public class StringValueEnumeratedTest {
|
||||
@Test
|
||||
void testNativeQuery_succeeds() {
|
||||
TestEntity testEntity = new TestEntity(State.DISABLED);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
|
||||
assertThat(
|
||||
tm().transact(
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
@@ -55,7 +55,7 @@ class TimedTransitionBaseUserTypeTest {
|
||||
@Test
|
||||
void roundTripConversion_returnsSameTimedTransitionProperty() {
|
||||
TestEntity testEntity = new TestEntity(TIMED_TRANSITION_PROPERTY);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.property.toValueMap())
|
||||
@@ -65,7 +65,7 @@ class TimedTransitionBaseUserTypeTest {
|
||||
@Test
|
||||
void testUpdateColumn_succeeds() {
|
||||
TestEntity testEntity = new TestEntity(TIMED_TRANSITION_PROPERTY);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.property.toValueMap())
|
||||
@@ -80,7 +80,7 @@ class TimedTransitionBaseUserTypeTest {
|
||||
@Test
|
||||
void testNullValue_writesAndReadsNullSuccessfully() {
|
||||
TestEntity testEntity = new TestEntity(null);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.property).isNull();
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
@@ -55,7 +55,7 @@ class TldStateTransitionUserTypeTest {
|
||||
TimedTransitionProperty<TldState> timedTransitionProperty =
|
||||
TimedTransitionProperty.fromValueMap(values);
|
||||
TestEntity testEntity = new TestEntity(timedTransitionProperty);
|
||||
insertInDb(testEntity);
|
||||
persistResource(testEntity);
|
||||
TestEntity persisted =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, "id"));
|
||||
assertThat(persisted.timedTransitionProperty.toValueMap())
|
||||
|
||||
@@ -16,7 +16,7 @@ package google.registry.persistence.converter;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.persistResources;
|
||||
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.persistence.VKey;
|
||||
@@ -50,7 +50,7 @@ public class VKeyConverterTest {
|
||||
TestLongEntity longEntity = new TestLongEntity(300L);
|
||||
VKey<TestLongEntity> longKey = VKey.create(TestLongEntity.class, 300L);
|
||||
TestEntity original = new TestEntity(1984L, stringKey, longKey);
|
||||
insertInDb(stringEntity, longEntity, original);
|
||||
persistResources(stringEntity, longEntity, original);
|
||||
|
||||
TestEntity retrieved =
|
||||
tm().transact(() -> tm().getEntityManager().find(TestEntity.class, 1984L));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user