1
0
mirror of https://github.com/google/nomulus synced 2026-01-17 11:13:03 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Harshita Sharma
2a67b04f3a testing 2025-07-29 20:42:42 +00:00
30 changed files with 107 additions and 522 deletions

View File

@@ -77,17 +77,4 @@
Save
</button>
</form>
@if(userDataService.userData()?.isAdmin) {
<div class="settings-security__reset-password-field">
<h2>Need to reset your EPP password?</h2>
<button
mat-flat-button
color="primary"
aria-label="Reset EPP password via email"
(click)="requestEppPasswordReset()"
>
Reset EPP password via email
</button>
</div>
}
</div>

View File

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

View File

@@ -24,43 +24,11 @@ import {
import { MatSnackBar } from '@angular/material/snack-bar';
import { RegistrarService } from 'src/app/registrar/registrar.service';
import { SecurityService } from './security.service';
import { UserDataService } from 'src/app/shared/services/userData.service';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { CommonModule } from '@angular/common';
import { MaterialModule } from 'src/app/material.module';
import { filter, switchMap, take } from 'rxjs';
import { BackendService } from 'src/app/shared/services/backend.service';
type errorCode = 'required' | 'maxlength' | 'minlength' | 'passwordsDontMatch';
type errorFriendlyText = { [type in errorCode]: String };
@Component({
selector: 'app-reset-epp-password-dialog',
template: `
<h2 mat-dialog-title>Please confirm the password reset:</h2>
<mat-dialog-content>
This will send an EPP password reset email to the admin POC.
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button (click)="onCancel()">Cancel</button>
<button mat-button color="warn" (click)="onSave()">Confirm</button>
</mat-dialog-actions>
`,
imports: [CommonModule, MaterialModule],
})
export class ResetEppPasswordComponent {
constructor(public dialogRef: MatDialogRef<ResetEppPasswordComponent>) {}
onSave(): void {
this.dialogRef.close(true);
}
onCancel(): void {
this.dialogRef.close(false);
}
}
@Component({
selector: 'app-epp-password-edit',
templateUrl: './eppPasswordEdit.component.html',
@@ -80,12 +48,9 @@ export default class EppPasswordEditComponent {
};
constructor(
public registrarService: RegistrarService,
public securityService: SecurityService,
protected userDataService: UserDataService,
private backendService: BackendService,
private resetPasswordDialog: MatDialog,
private _snackBar: MatSnackBar
private _snackBar: MatSnackBar,
public registrarService: RegistrarService
) {}
hasError(controlName: string) {
@@ -155,26 +120,4 @@ export default class EppPasswordEditComponent {
goBack() {
this.securityService.isEditingPassword = false;
}
sendEppPasswordResetRequest() {
return this.backendService.requestEppPasswordReset(
this.registrarService.registrarId()
);
}
requestEppPasswordReset() {
const dialogRef = this.resetPasswordDialog.open(ResetEppPasswordComponent);
dialogRef
.afterClosed()
.pipe(
take(1),
filter((result) => !!result)
)
.pipe(switchMap((_) => this.sendEppPasswordResetRequest()))
.subscribe({
next: (_) => this.goBack(),
error: (err: HttpErrorResponse) =>
this._snackBar.open(err.error || err.message),
});
}
}

View File

@@ -291,11 +291,4 @@ export class BackendService {
registryLockEmail,
});
}
requestEppPasswordReset(registrarId: string) {
return this.http.post('/console-api/password-reset-request', {
type: 'EPP',
registrarId,
});
}
}

View File

@@ -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'
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,7 +23,7 @@ buildscript {
}
plugins {
id "org.flywaydb.flyway" version "11.0.1"
id "org.flywaydb.flyway" version "9.22.3"
id 'maven-publish'
}

View File

@@ -1,4 +1,4 @@
# This file defines properties used by the gradle build. It must be kept in
f# This file defines properties used by the gradle build. It must be kept in
# sync with config/nom_build.py.
#
# To regenerate, run ./nom_build --generate-gradle-properties