1
0
mirror of https://github.com/google/nomulus synced 2026-07-11 10:32:21 +00:00

Compare commits

...

11 Commits

Author SHA1 Message Date
gbrodman fd7820759d Use token's renewalPrice if renewalBehavior is SPECIFIED (#2502)
Previous PRs and token changes (see b/332928676) have made it so that
SPECIFIED renewalPriceBehavior tokens must have a renewal price. As
such, we can now use that renewalPrice when creating domains with
SPECIFIED tokens.
2024-08-15 19:06:32 +00:00
sarahcaseybot 69359bb1e6 Add QPS and incomplete connections metrics to load test client (#2487)
* Add QPS and incomplete connections metrics to load test client

* Add a failed request count

* Add todos

* Reuse contact

* Add bugs to todos

* small fix

* Clarify QPS
2024-08-14 18:14:17 +00:00
gbrodman 35b602a76e Remove User ID field from SQL (#2523)
This will fail tests until the corresponding PR in Java is deployed.
2024-08-14 17:51:15 +00:00
dependabot[bot] 82002d1f75 Bump axios in /console-webapp in the npm_and_yarn group (#2532)
Bumps the npm_and_yarn group in /console-webapp with 1 update: [axios](https://github.com/axios/axios).


Updates `axios` from 1.7.2 to 1.7.4
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.7.2...v1.7.4)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-14 15:44:14 +00:00
Lai Jiang 2fd9b062df Make Nomulus work on GKE with external load balancer (#2527)
This will create a multi-cluster external load balancer exposing HTTP
traffic to nomulus running in clusters in the fleet.
2024-08-14 14:32:39 +00:00
Ben McIlwain ec3804e87e Make domain update flow handle null auth data (#2530)
It's valid for the auth data to be null (although it only happens 10 times
across our entire registry), so the domain update flow should not fail out with
a NullPointerException when the existing state of the data is null and the
update isn't adding that data either.

BUG=http://b/359264787
2024-08-13 18:19:44 +00:00
Pavlo Tkach d0d28cc7e6 Fix console contact delete button not working (#2528) 2024-08-09 16:42:39 +00:00
Pavlo Tkach 2d1260c01b Allow updating icannReferralEmail through the new console ui (#2525) 2024-08-07 16:28:08 +00:00
Pavlo Tkach 06da6a2cc6 Make ContactActionTest deterministic for stop fail under new Hibernate (#2524) 2024-08-07 13:37:13 +00:00
Lai Jiang 858a22f82e Delete a duplicate resource file (#2522)
It already exists under the resources folder.
2024-08-06 18:42:29 +00:00
gbrodman 3c126ddfd4 Remove ID field from User in Java classes and remove UserDao (#2517)
This is the first step in the field removal (second will be removing the
column from SQL once this is deployed).

There's no point in using a UserDao versus just doing the standard
loading-from-DB that we do everywhere else. No need to special-case it.
2024-08-05 20:36:17 +00:00
51 changed files with 2911 additions and 2966 deletions
+3 -3
View File
@@ -6451,9 +6451,9 @@
}
},
"node_modules/axios": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
"integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
"integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
"dev": true,
"dependencies": {
"follow-redirects": "^1.15.6",
@@ -18,7 +18,11 @@
<mat-icon>edit</mat-icon>
Edit
</button>
<button mat-icon-button aria-label="Delete Contact">
<button
mat-icon-button
aria-label="Delete Contact"
(click)="deleteContact()"
>
<mat-icon>delete</mat-icon>
</button>
}
@@ -50,6 +50,9 @@ export class ContactDetailsComponent {
error: (err: HttpErrorResponse) => {
this._snackBar.open(err.error);
},
complete: () => {
this.goBack();
},
});
}
}
@@ -132,6 +132,18 @@
/>
</mat-form-field>
@if((userDataService.userData()?.globalRole || 'NONE') !== "NONE") {
<mat-form-field appearance="outline">
<mat-label>ICANN Referral Email: </mat-label>
<input
matInput
type="text"
[(ngModel)]="registrarInEdit.icannReferralEmail"
[ngModelOptions]="{ standalone: true }"
/>
</mat-form-field>
}
<button mat-flat-button color="primary" type="submit">Save</button>
</form>
</div>
@@ -19,6 +19,7 @@ import {
Registrar,
RegistrarService,
} from 'src/app/registrar/registrar.service';
import { UserDataService } from 'src/app/shared/services/userData.service';
import { WhoisService } from './whois.service';
@Component({
@@ -30,6 +31,7 @@ export default class WhoisEditComponent {
registrarInEdit: Registrar | undefined;
constructor(
public userDataService: UserDataService,
public whoisService: WhoisService,
public registrarService: RegistrarService,
private _snackBar: MatSnackBar
@@ -1,17 +0,0 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<delete>
<domain:delete
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>%DOMAIN%</domain:name>
</domain:delete>
</delete>
<extension>
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
<metadata:reason>Non-renewing domain has reached expiration date.</metadata:reason>
<metadata:requestedByRegistrar>false</metadata:requestedByRegistrar>
</metadata:metadata>
</extension>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
@@ -365,7 +365,7 @@ public final class DomainCreateFlow implements MutatingFlow {
createAutorenewBillingEvent(
domainHistoryId,
registrationExpirationTime,
getRenewalPriceInfo(isAnchorTenant, allocationToken, feesAndCredits));
getRenewalPriceInfo(isAnchorTenant, allocationToken));
PollMessage.Autorenew autorenewPollMessage =
createAutorenewPollMessage(domainHistoryId, registrationExpirationTime);
ImmutableSet.Builder<ImmutableObject> entitiesToSave = new ImmutableSet.Builder<>();
@@ -688,9 +688,7 @@ public final class DomainCreateFlow implements MutatingFlow {
* AllocationToken} is 'SPECIFIED'.
*/
static RenewalPriceInfo getRenewalPriceInfo(
boolean isAnchorTenant,
Optional<AllocationToken> allocationToken,
FeesAndCredits feesAndCredits) {
boolean isAnchorTenant, Optional<AllocationToken> allocationToken) {
if (isAnchorTenant) {
allocationToken.ifPresent(
token ->
@@ -701,7 +699,7 @@ public final class DomainCreateFlow implements MutatingFlow {
} else if (allocationToken.isPresent()
&& allocationToken.get().getRenewalPriceBehavior() == RenewalPriceBehavior.SPECIFIED) {
return RenewalPriceInfo.create(
RenewalPriceBehavior.SPECIFIED, feesAndCredits.getCreateCost());
RenewalPriceBehavior.SPECIFIED, allocationToken.get().getRenewalPrice().get());
} else {
return RenewalPriceInfo.create(RenewalPriceBehavior.DEFAULT, null);
}
@@ -14,7 +14,6 @@
package google.registry.flows.domain;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
import static com.google.common.collect.Sets.symmetricDifference;
@@ -282,7 +281,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
.removeContacts(remove.getContacts())
.addContacts(add.getContacts())
.setRegistrant(determineUpdatedRegistrant(change, domain))
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
.setAuthInfo(Optional.ofNullable(change.getAuthInfo()).orElse(domain.getAuthInfo()));
if (!add.getNameservers().isEmpty()) {
domainBuilder.addNameservers(add.getNameservers().stream().collect(toImmutableSet()));
@@ -32,7 +32,6 @@ import com.google.common.collect.Streams;
import google.registry.batch.CloudTasksUtils;
import google.registry.model.console.RegistrarRole;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.model.console.UserRoles;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.registrar.Registrar;
@@ -48,9 +47,7 @@ import google.registry.util.CidrAddressBlock;
import google.registry.util.RegistryEnvironment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Pattern;
@@ -278,22 +275,14 @@ public final class OteAccountBuilder {
/** Saves all the OT&amp;E entities we created. */
private void saveAllEntities() {
ImmutableList<Tld> registries = ImmutableList.of(sunriseTld, gaTld, eapTld);
Map<String, User> existingUsers = new HashMap<>();
users.forEach(
user ->
UserDao.loadUser(user.getEmailAddress())
.ifPresent(
existingUser ->
existingUsers.put(existingUser.getEmailAddress(), existingUser)));
if (!replaceExisting) {
checkState(existingUsers.isEmpty(), "Found existing users: %s", existingUsers);
}
tm().transact(
() -> {
if (!replaceExisting) {
ImmutableMap<String, User> existingUsers =
tm().loadByEntitiesIfPresent(users).stream()
.collect(toImmutableMap(User::getEmailAddress, u -> u));
checkState(existingUsers.isEmpty(), "Found existing users: %s", existingUsers);
ImmutableList<VKey<? extends ImmutableObject>> keys =
Streams.concat(
registries.stream().map(tld -> Tld.createVKey(tld.getTldStr())),
@@ -317,16 +306,7 @@ public final class OteAccountBuilder {
tm().putAll(registrars);
});
for (User user : users) {
String email = user.getEmailAddress();
if (existingUsers.containsKey(email)) {
// Note that other roles for the existing user are reset. We do this instead of simply
// saving the new user is that UserDao does not allow us to save the new user with the same
// email as the existing user.
user = existingUsers.get(email).asBuilder().setUserRoles(user.getUserRoles()).build();
}
UserDao.saveUser(user);
}
tm().transact(() -> tm().putAll(ImmutableList.copyOf(users)));
}
private Registrar addAllowedTld(Registrar registrar) {
@@ -15,11 +15,9 @@
package google.registry.model.console;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.tools.server.UpdateUserGroupAction.GROUP_UPDATE_QUEUE;
import com.google.cloud.tasks.v2.Task;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.flogger.FluentLogger;
@@ -34,7 +32,6 @@ import google.registry.tools.server.UpdateUserGroupAction.Mode;
import google.registry.util.RegistryEnvironment;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
import javax.annotation.Nullable;
import javax.persistence.Access;
import javax.persistence.AccessType;
@@ -52,8 +49,6 @@ public class User extends UserBase {
public static final String IAP_SECURED_WEB_APP_USER_ROLE = "roles/iap.httpsResourceAccessor";
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@VisibleForTesting public static final AtomicLong ID_GENERATOR_FOR_TESTING = new AtomicLong();
/**
* Grants the user permission to pass IAP.
*
@@ -163,12 +158,6 @@ public class User extends UserBase {
}
}
@Override
@Access(AccessType.PROPERTY)
public Long getId() {
return super.getId();
}
@Id
@Override
@Access(AccessType.PROPERTY)
@@ -183,7 +172,7 @@ public class User extends UserBase {
@Override
public VKey<User> createVKey() {
return VKey.create(User.class, getId());
return VKey.create(User.class, getEmailAddress());
}
/** Builder for constructing immutable {@link User} objects. */
@@ -194,20 +183,5 @@ public class User extends UserBase {
public Builder(User user) {
super(user);
}
@Override
public User build() {
// Sets the ID temporarily until we can get rid of the non-null constraint (and the field)
if (getInstance().getId() == null || getInstance().getId().equals(0L)) {
// In tests, we cannot guarantee that the database is fully set up -- so don't use it to
// generate a new long
if (RegistryEnvironment.get() == RegistryEnvironment.UNITTEST) {
getInstance().setId(ID_GENERATOR_FOR_TESTING.getAndIncrement());
} else {
getInstance().setId(tm().reTransact(tm()::allocateId));
}
}
return super.build();
}
}
}
@@ -49,9 +49,6 @@ public class UserBase extends UpdateAutoTimestampEntity implements Buildable {
private static final long serialVersionUID = 6936728603828566721L;
/** Autogenerated unique ID of this user. */
@Transient private Long id;
/** Email address of the user in question. */
@Transient String emailAddress;
@@ -71,24 +68,15 @@ public class UserBase extends UpdateAutoTimestampEntity implements Buildable {
/** Randomly generated hash salt. */
String registryLockPasswordSalt;
public Long getId() {
return id;
}
/**
* Sets the user ID.
* Sets the user email address.
*
* <p>This should only be used for restoring the user id of an object being loaded in a PostLoad
* method (effectively, when it is still under construction by Hibernate). In all other cases, the
* object should be regarded as immutable and changes should go through a Builder.
* <p>This should only be used for restoring an object being loaded in a PostLoad method
* (effectively, when it is still under construction by Hibernate). In all other cases, the object
* should be regarded as immutable and changes should go through a Builder.
*
* <p>In addition to this special case use, this method must exist to satisfy Hibernate.
*/
@SuppressWarnings("unused")
void setId(Long id) {
this.id = id;
}
void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
@@ -1,38 +0,0 @@
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.console;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import java.util.Optional;
/** Data access object for {@link User} objects to simplify saving and retrieval. */
public class UserDao {
/** Retrieves the one user with this email address if it exists. */
public static Optional<User> loadUser(String emailAddress) {
return tm().transact(
() ->
tm().query("FROM User WHERE emailAddress = :emailAddress", User.class)
.setParameter("emailAddress", emailAddress)
.getResultStream()
.findFirst());
}
/** Saves the given user, updating it if it already exists. */
public static void saveUser(User user) {
tm().transact(() -> tm().put(user));
}
}
@@ -38,10 +38,6 @@ public class UserUpdateHistory extends ConsoleUpdateHistory {
UserBase user;
// This field exists so that it's populated in the SQL table
@Column(nullable = false, name = "userId")
Long id;
@Column(nullable = false, name = "emailAddress")
String emailAddress;
@@ -51,7 +47,6 @@ public class UserUpdateHistory extends ConsoleUpdateHistory {
@PostLoad
void postLoad() {
user.setId(id);
user.setEmailAddress(emailAddress);
}
@@ -83,7 +78,6 @@ public class UserUpdateHistory extends ConsoleUpdateHistory {
public Builder setUser(User user) {
getInstance().user = user;
getInstance().id = user.getId();
getInstance().emailAddress = user.getEmailAddress();
return this;
}
@@ -144,6 +144,7 @@ public class DomainBase extends EppResource
@AttributeOverride(name = "pw.value", column = @Column(name = "auth_info_value")),
@AttributeOverride(name = "pw.repoId", column = @Column(name = "auth_info_repo_id")),
})
@Nullable
DomainAuthInfo authInfo;
/** Data used to construct DS records for this domain. */
@@ -620,6 +621,7 @@ public class DomainBase extends EppResource
return getAllContacts(true);
}
@Nullable
public DomainAuthInfo getAuthInfo() {
return authInfo;
}
@@ -16,6 +16,7 @@ package google.registry.model.eppcommon;
import google.registry.model.ImmutableObject;
import google.registry.model.UnsafeSerializable;
import javax.annotation.Nullable;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.MappedSuperclass;
@@ -49,10 +50,12 @@ public abstract class AuthInfo extends ImmutableObject implements UnsafeSerializ
public static class PasswordAuth extends ImmutableObject implements UnsafeSerializable {
@XmlValue
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
@Nullable
String value;
@XmlAttribute(name = "roid")
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
@Nullable
String repoId;
public String getValue() {
@@ -63,14 +66,14 @@ public abstract class AuthInfo extends ImmutableObject implements UnsafeSerializ
return repoId;
}
public static PasswordAuth create(String value, String repoId) {
public static PasswordAuth create(@Nullable String value, @Nullable String repoId) {
PasswordAuth instance = new PasswordAuth();
instance.value = value;
instance.repoId = repoId;
return instance;
}
public static PasswordAuth create(String value) {
public static PasswordAuth create(@Nullable String value) {
return create(value, null);
}
}
@@ -15,6 +15,7 @@
package google.registry.request.auth;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.api.client.json.webtoken.JsonWebSignature;
import com.google.auth.oauth2.TokenVerifier;
@@ -23,7 +24,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.persistence.VKey;
import google.registry.request.auth.AuthModule.IapOidc;
import google.registry.request.auth.AuthModule.RegularOidc;
import google.registry.request.auth.AuthModule.RegularOidcFallback;
@@ -117,7 +118,8 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
logger.atWarning().log("No email address from the OIDC token:\n%s", token.getPayload());
return AuthResult.NOT_AUTHENTICATED;
}
Optional<User> maybeUser = UserDao.loadUser(email);
Optional<User> maybeUser =
tm().transact(() -> tm().loadByKeyIfPresent(VKey.create(User.class, email)));
if (maybeUser.isPresent()) {
return AuthResult.createUser(maybeUser.get());
}
@@ -22,7 +22,6 @@ import com.google.common.collect.ImmutableMap;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.RegistrarRole;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.model.console.UserRoles;
import google.registry.tools.params.KeyValueMapParameter.StringToRegistrarRoleMap;
import java.util.Optional;
@@ -95,8 +94,6 @@ public abstract class CreateOrUpdateUserCommand extends ConfirmingCommand {
builder.setRegistryLockEmailAddress(registryLockEmailAddress);
}
}
User newUser = builder.build();
UserDao.saveUser(newUser);
tm().put(builder.build());
}
}
@@ -16,11 +16,12 @@ package google.registry.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.model.console.User.grantIapPermission;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.beust.jcommander.Parameters;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.persistence.VKey;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.inject.Inject;
@@ -40,7 +41,8 @@ public class CreateUserCommand extends CreateOrUpdateUserCommand implements Comm
@Nullable
@Override
User getExistingUser(String email) {
checkArgument(UserDao.loadUser(email).isEmpty(), "A user with email %s already exists", email);
checkArgument(
!tm().exists(VKey.create(User.class, email)), "A user with email %s already exists", email);
return null;
}
@@ -14,15 +14,15 @@
package google.registry.tools;
import static com.google.api.client.util.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.persistence.VKey;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.inject.Inject;
@@ -46,7 +46,9 @@ public class DeleteUserCommand extends ConfirmingCommand implements CommandWithC
@Override
protected String prompt() {
checkArgumentNotNull(email, "Email must be provided");
checkArgumentPresent(UserDao.loadUser(email), "Email does not correspond to a valid user");
checkArgument(
tm().transact(() -> tm().exists(VKey.create(User.class, email))),
"Email does not correspond to a valid user");
return String.format("Delete user with email %s?", email);
}
@@ -54,9 +56,9 @@ public class DeleteUserCommand extends ConfirmingCommand implements CommandWithC
protected String execute() throws Exception {
tm().transact(
() -> {
Optional<User> optionalUser = UserDao.loadUser(email);
checkArgumentPresent(optionalUser, "Email no longer corresponds to a valid user");
tm().delete(optionalUser.get());
VKey<User> key = VKey.create(User.class, email);
checkArgument(tm().exists(key), "Email no longer corresponds to a valid user");
tm().delete(key);
});
User.revokeIapPermission(email, maybeGroupEmailAddress, null, connection, iamClient);
return String.format("Deleted user with email %s", email);
@@ -14,10 +14,12 @@
package google.registry.tools;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.persistence.VKey;
import java.util.List;
/** Command to display one or more users. */
@@ -29,11 +31,14 @@ public class GetUserCommand implements Command {
@Override
public void run() throws Exception {
for (String emailAddress : mainParameters) {
System.out.println(
UserDao.loadUser(emailAddress)
.map(User::toString)
.orElse(String.format("No user with email address %s", emailAddress)));
}
tm().transact(
() -> {
for (String emailAddress : mainParameters) {
System.out.println(
tm().loadByKeyIfPresent(VKey.create(User.class, emailAddress))
.map(User::toString)
.orElse(String.format("No user with email address %s", emailAddress)));
}
});
}
}
@@ -14,20 +14,20 @@
package google.registry.tools;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
import com.beust.jcommander.Parameters;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import javax.annotation.Nullable;
import google.registry.persistence.VKey;
/** Updates a user, assuming that the user in question already exists. */
@Parameters(separators = " =", commandDescription = "Update a user account")
public class UpdateUserCommand extends CreateOrUpdateUserCommand {
@Nullable
@Override
User getExistingUser(String email) {
return checkArgumentPresent(UserDao.loadUser(email), "User %s not found", email);
return checkArgumentPresent(
tm().loadByKeyIfPresent(VKey.create(User.class, email)), "User %s not found", email);
}
}
@@ -17,6 +17,7 @@ package google.registry.ui.server.console.settings;
import static com.google.common.base.Preconditions.checkArgument;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.request.Action.Method.POST;
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
@@ -30,6 +31,7 @@ import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
import google.registry.ui.server.console.ConsoleApiAction;
import google.registry.ui.server.registrar.ConsoleApiParams;
import java.util.Objects;
import java.util.Optional;
import javax.inject.Inject;
@@ -65,10 +67,10 @@ public class WhoisRegistrarFieldsAction extends ConsoleApiAction {
checkArgument(registrar.isPresent(), "'registrar' parameter is not present");
checkPermission(
user, registrar.get().getRegistrarId(), ConsolePermission.EDIT_REGISTRAR_DETAILS);
tm().transact(() -> loadAndModifyRegistrar(registrar.get()));
tm().transact(() -> loadAndModifyRegistrar(registrar.get(), user));
}
private void loadAndModifyRegistrar(Registrar providedRegistrar) {
private void loadAndModifyRegistrar(Registrar providedRegistrar, User user) {
Registrar savedRegistrar;
try {
// reload to make sure the object has all the correct fields
@@ -78,6 +80,15 @@ public class WhoisRegistrarFieldsAction extends ConsoleApiAction {
return;
}
// icannReferralEmail can't be updated by partners, only by global users with
// EDIT_REGISTRAR_DETAILS permission
if (!Objects.equals(
savedRegistrar.getIcannReferralEmail(), providedRegistrar.getIcannReferralEmail())
&& !user.getUserRoles().hasGlobalPermission(ConsolePermission.EDIT_REGISTRAR_DETAILS)) {
setFailedResponse(
"Icann Referral Email update is not permitted for this user", SC_BAD_REQUEST);
}
Registrar newRegistrar =
savedRegistrar
.asBuilder()
@@ -86,6 +97,8 @@ public class WhoisRegistrarFieldsAction extends ConsoleApiAction {
.setLocalizedAddress(providedRegistrar.getLocalizedAddress())
.setPhoneNumber(providedRegistrar.getPhoneNumber())
.setFaxNumber(providedRegistrar.getFaxNumber())
.setIcannReferralEmail(providedRegistrar.getIcannReferralEmail())
.setEmailAddress(providedRegistrar.getEmailAddress())
.build();
tm().put(newRegistrar);
sendExternalUpdatesIfNecessary(
@@ -30,7 +30,6 @@ import google.registry.batch.CloudTasksUtils;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.console.RegistrarRole;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.model.console.UserRoles;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarAddress;
@@ -260,8 +259,8 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
"Registrar with client ID %s already exists",
registrar.getRegistrarId());
tm().put(registrar);
tm().put(user);
});
UserDao.saveUser(user);
User.grantIapPermission(
user.getEmailAddress(), maybeGroupEmailAddress, cloudTasksUtils, null, iamClient);
data.put("password", password);
@@ -344,8 +344,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.and()
.hasPeriodYears(2);
RenewalPriceInfo renewalPriceInfo =
DomainCreateFlow.getRenewalPriceInfo(
isAnchorTenant, Optional.ofNullable(allocationToken), feesAndCredits);
DomainCreateFlow.getRenewalPriceInfo(isAnchorTenant, Optional.ofNullable(allocationToken));
// There should be one bill for the create and one for the recurrence autorenew event.
BillingEvent createBillingEvent =
new BillingEvent.Builder()
@@ -3189,53 +3188,25 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
@Test
void testGetRenewalPriceInfo_isAnchorTenantWithoutToken_returnsNonPremiumAndNullPrice() {
assertThat(
DomainCreateFlow.getRenewalPriceInfo(
true,
Optional.empty(),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(0), FeeType.CREATE, false))
.build()))
assertThat(DomainCreateFlow.getRenewalPriceInfo(true, Optional.empty()))
.isEqualTo(RenewalPriceInfo.create(NONPREMIUM, null));
}
@Test
void testGetRenewalPriceInfo_isAnchorTenantWithDefaultToken_returnsNonPremiumAndNullPrice() {
assertThat(
DomainCreateFlow.getRenewalPriceInfo(
true,
Optional.of(allocationToken),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(0), FeeType.CREATE, false))
.build()))
assertThat(DomainCreateFlow.getRenewalPriceInfo(true, Optional.of(allocationToken)))
.isEqualTo(RenewalPriceInfo.create(NONPREMIUM, null));
}
@Test
void testGetRenewalPriceInfo_isNotAnchorTenantWithDefaultToken_returnsDefaultAndNullPrice() {
assertThat(
DomainCreateFlow.getRenewalPriceInfo(
false,
Optional.of(allocationToken),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(100), FeeType.CREATE, false))
.build()))
assertThat(DomainCreateFlow.getRenewalPriceInfo(false, Optional.of(allocationToken)))
.isEqualTo(RenewalPriceInfo.create(DEFAULT, null));
}
@Test
void testGetRenewalPriceInfo_isNotAnchorTenantWithoutToken_returnsDefaultAndNullPrice() {
assertThat(
DomainCreateFlow.getRenewalPriceInfo(
false,
Optional.empty(),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(100), FeeType.CREATE, false))
.build()))
assertThat(DomainCreateFlow.getRenewalPriceInfo(false, Optional.empty()))
.isEqualTo(RenewalPriceInfo.create(DEFAULT, null));
}
@@ -3248,17 +3219,10 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setRenewalPriceBehavior(SPECIFIED)
.setRenewalPrice(Money.of(USD, 0))
.setRenewalPrice(Money.of(USD, 5))
.build());
assertThat(
DomainCreateFlow.getRenewalPriceInfo(
false,
Optional.of(token),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(100), FeeType.CREATE, false))
.build()))
.isEqualTo(RenewalPriceInfo.create(SPECIFIED, Money.of(USD, 100)));
assertThat(DomainCreateFlow.getRenewalPriceInfo(false, Optional.of(token)))
.isEqualTo(RenewalPriceInfo.create(SPECIFIED, Money.of(USD, 5)));
}
@Test
@@ -3276,11 +3240,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setTokenType(SINGLE_USE)
.setRenewalPriceBehavior(SPECIFIED)
.setRenewalPrice(Money.of(USD, 0))
.build())),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(0), FeeType.CREATE, true))
.build()));
.build()))));
assertThat(thrown)
.hasMessageThat()
.isEqualTo("Renewal price behavior cannot be SPECIFIED for anchor tenant");
@@ -3300,11 +3260,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
.setToken("abc123")
.setTokenType(SINGLE_USE)
.setRenewalPriceBehavior(RenewalPriceBehavior.valueOf("INVALID"))
.build())),
new FeesAndCredits.Builder()
.setCurrency(USD)
.addFeeOrCredit(Fee.create(BigDecimal.valueOf(0), FeeType.CREATE, true))
.build()));
.build()))));
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
@@ -104,8 +104,10 @@ import google.registry.model.contact.Contact;
import google.registry.model.domain.DesignatedContact;
import google.registry.model.domain.DesignatedContact.Type;
import google.registry.model.domain.Domain;
import google.registry.model.domain.DomainAuthInfo;
import google.registry.model.domain.DomainHistory;
import google.registry.model.domain.secdns.DomainDsData;
import google.registry.model.eppcommon.AuthInfo.PasswordAuth;
import google.registry.model.eppcommon.Trid;
import google.registry.model.host.Host;
import google.registry.model.poll.PendingActionNotificationResponse.DomainPendingActionNotificationResponse;
@@ -264,6 +266,38 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
doSuccessfulTest("generic_success_response_no_cltrid.xml");
}
@Test
void testSuccess_noExistingAuthData() throws Exception {
setEppInput("domain_update_no_auth_change.xml");
persistReferencedEntities();
persistDomain();
persistResource(
reloadResourceByForeignKey()
.asBuilder()
.setAuthInfo(DomainAuthInfo.create(PasswordAuth.create(null)))
.build());
assertMutatingFlow(true);
runFlowAssertResponse(loadFile("generic_success_response.xml"));
Domain domain = reloadResourceByForeignKey();
// Check that the domain was updated. These values came from the xml.
assertAboutDomains()
.that(domain)
.hasStatusValue(CLIENT_HOLD)
.and()
.hasAuthInfoPwd(null)
.and()
.hasOneHistoryEntryEachOfTypes(DOMAIN_CREATE, DOMAIN_UPDATE)
.and()
.hasLastEppUpdateTime(clock.nowUtc())
.and()
.hasLastEppUpdateRegistrarId("TheRegistrar")
.and()
.hasNoAutorenewEndTime();
assertNoBillingEvents();
assertDomainDnsRequests("example.tld");
assertLastHistoryContainsResource(reloadResourceByForeignKey());
}
@Test
void testSuccess_cachingDisabled() throws Exception {
boolean origIsCachingEnabled = RegistryConfig.isEppResourceCachingEnabled();
@@ -22,6 +22,7 @@ import static google.registry.persistence.transaction.JpaTransactionManagerExten
import static google.registry.testing.CertificateSamples.SAMPLE_CERT;
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.util.DateTimeUtils.START_OF_TIME;
@@ -36,10 +37,10 @@ import com.google.common.collect.ImmutableList;
import google.registry.batch.CloudTasksUtils;
import google.registry.model.console.RegistrarRole;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.model.registrar.Registrar;
import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldState;
import google.registry.persistence.VKey;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
import google.registry.testing.CloudTasksHelper;
@@ -106,7 +107,7 @@ public final class OteAccountBuilderTest {
}
public static void verifyUser(String registrarId, String email) {
Optional<User> maybeUser = UserDao.loadUser(email);
Optional<User> maybeUser = loadByKeyIfPresent(VKey.create(User.class, email));
assertThat(maybeUser).isPresent();
assertThat(maybeUser.get().getUserRoles().getRegistrarRoles().get(registrarId))
.isEqualTo(RegistrarRole.ACCOUNT_MANAGER);
@@ -1,83 +0,0 @@
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.model.console;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import google.registry.model.EntityTestCase;
import org.junit.jupiter.api.Test;
/** Tests for {@link UserDao}. */
public class UserDaoTest extends EntityTestCase {
@Test
void testSuccess_saveAndRetrieve() {
User user1 =
new User.Builder()
.setEmailAddress("email@email.com")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
.build();
User user2 =
new User.Builder()
.setEmailAddress("foo@bar.com")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
.build();
UserDao.saveUser(user1);
UserDao.saveUser(user2);
assertAboutImmutableObjects()
.that(user1)
.isEqualExceptFields(UserDao.loadUser("email@email.com").get(), "id", "updateTimestamp");
assertAboutImmutableObjects()
.that(user2)
.isEqualExceptFields(UserDao.loadUser("foo@bar.com").get(), "id", "updateTimestamp");
}
@Test
void testSuccess_absentUser() {
User user =
new User.Builder()
.setEmailAddress("email@email.com")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
.build();
UserDao.saveUser(user);
User fromDb = UserDao.loadUser("email@email.com").get();
// nonexistent one should never exist
assertThat(UserDao.loadUser("nonexistent@email.com")).isEmpty();
// now try deleting the one that does exist
tm().transact(() -> tm().delete(fromDb));
assertThat(UserDao.loadUser("email@email.com")).isEmpty();
}
@Test
void testSuccess_updateUser_sameEmail() {
User user1 =
new User.Builder()
.setEmailAddress("email@email.com")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
.build();
User user2 =
new User.Builder()
.setEmailAddress("email@email.com")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
.build();
UserDao.saveUser(user1);
UserDao.saveUser(user2);
assertAboutImmutableObjects()
.that(user2)
.isEqualExceptFields(UserDao.loadUser("email@email.com").get(), "updateTimestamp");
}
}
@@ -73,7 +73,6 @@ import google.registry.model.billing.BillingRecurrence;
import google.registry.model.common.DnsRefreshRequest;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.model.console.UserRoles;
import google.registry.model.contact.Contact;
import google.registry.model.contact.ContactAuthInfo;
@@ -1014,20 +1013,33 @@ public final class DatabaseHelper {
return tm().transact(() -> tm().loadByEntity(resource));
}
public static User loadExistingUser(String emailAddress) {
return loadByKey(VKey.create(User.class, emailAddress));
}
/** Persists an admin {@link User} with the given email address if it doesn't already exist. */
public static User createAdminUser(String emailAddress) {
Optional<User> existingUser = UserDao.loadUser(emailAddress);
if (existingUser.isPresent()) {
return existingUser.get();
}
User user =
new User.Builder()
.setEmailAddress(emailAddress)
.setUserRoles(
new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).setIsAdmin(true).build())
.build();
tm().transact(() -> tm().put(user));
return UserDao.loadUser(emailAddress).get();
// Reload the user to pick up the update time
return loadByEntity(
tm().transact(
() -> {
VKey<User> key = VKey.create(User.class, emailAddress);
Optional<User> existingUser = tm().loadByKeyIfPresent(key);
if (existingUser.isPresent()) {
return existingUser.get();
}
User user =
new User.Builder()
.setEmailAddress(emailAddress)
.setUserRoles(
new UserRoles.Builder()
.setGlobalRole(GlobalRole.FTE)
.setIsAdmin(true)
.build())
.build();
tm().put(user);
return user;
}));
}
/** Returns all the history entries that are parented off the given EppResource. */
@@ -29,6 +29,7 @@ import google.registry.model.eppcommon.AuthInfo;
import google.registry.testing.TruthChainer.And;
import google.registry.tmch.LordnTaskUtils.LordnPhase;
import java.util.Set;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
/** Truth subject for asserting things about {@link Domain} instances. */
@@ -65,7 +66,7 @@ public final class DomainSubject extends AbstractEppResourceSubject<Domain, Doma
return hasValue(lordnPhase, actual.getLordnPhase(), "lordnPhase");
}
public And<DomainSubject> hasAuthInfoPwd(String pw) {
public And<DomainSubject> hasAuthInfoPwd(@Nullable String pw) {
AuthInfo authInfo = actual.getAuthInfo();
return hasValue(pw, authInfo == null ? null : authInfo.getPw().getValue(), "has auth info pw");
}
@@ -16,6 +16,7 @@ package google.registry.tools;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.console.User.IAP_SECURED_WEB_APP_USER_ROLE;
import static google.registry.testing.DatabaseHelper.loadExistingUser;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -28,7 +29,6 @@ import com.google.common.net.MediaType;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.RegistrarRole;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.testing.DatabaseHelper;
import google.registry.tools.server.UpdateUserGroupAction;
import java.util.Optional;
@@ -93,14 +93,14 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
"user@example.test",
"--registry_lock_email_address",
"registrylockemail@otherexample.test");
assertThat(UserDao.loadUser("user@example.test").get().getRegistryLockEmailAddress())
assertThat(loadExistingUser("user@example.test").getRegistryLockEmailAddress())
.hasValue("registrylockemail@otherexample.test");
}
@Test
void testSuccess_admin() throws Exception {
runCommandForced("--email", "user@example.test", "--admin", "true");
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isTrue();
assertThat(loadExistingUser("user@example.test").getUserRoles().isAdmin()).isTrue();
verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
verifyNoMoreInteractions(iamClient);
verifyNoInteractions(connection);
@@ -109,7 +109,7 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
@Test
void testSuccess_globalRole() throws Exception {
runCommandForced("--email", "user@example.test", "--global_role", "FTE");
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getGlobalRole())
assertThat(loadExistingUser("user@example.test").getUserRoles().getGlobalRole())
.isEqualTo(GlobalRole.FTE);
verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
verifyNoMoreInteractions(iamClient);
@@ -123,7 +123,7 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
"user@example.test",
"--registrar_roles",
"TheRegistrar=ACCOUNT_MANAGER,NewRegistrar=PRIMARY_CONTACT");
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
assertThat(loadExistingUser("user@example.test").getUserRoles().getRegistrarRoles())
.isEqualTo(
ImmutableMap.of(
"TheRegistrar",
@@ -24,7 +24,8 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import com.google.common.collect.ImmutableMap;
import com.google.common.net.MediaType;
import google.registry.model.console.UserDao;
import google.registry.model.console.User;
import google.registry.persistence.VKey;
import google.registry.testing.DatabaseHelper;
import google.registry.tools.server.UpdateUserGroupAction;
import java.util.Optional;
@@ -47,9 +48,10 @@ public class DeleteUserCommandTest extends CommandTestCase<DeleteUserCommand> {
@Test
void testSuccess_deletesUser() throws Exception {
DatabaseHelper.createAdminUser("email@example.test");
assertThat(UserDao.loadUser("email@example.test")).isPresent();
VKey<User> key = VKey.create(User.class, "email@example.test");
assertThat(DatabaseHelper.loadByKeyIfPresent(key)).isPresent();
runCommandForced("--email", "email@example.test");
assertThat(UserDao.loadUser("email@example.test")).isEmpty();
assertThat(DatabaseHelper.loadByKeyIfPresent(key)).isEmpty();
verify(iamClient).removeBinding("email@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
verifyNoMoreInteractions(iamClient);
verifyNoInteractions(connection);
@@ -59,9 +61,10 @@ public class DeleteUserCommandTest extends CommandTestCase<DeleteUserCommand> {
void testSuccess_deletesUser_removeFromGroup() throws Exception {
command.maybeGroupEmailAddress = Optional.of("group@example.test");
DatabaseHelper.createAdminUser("email@example.test");
assertThat(UserDao.loadUser("email@example.test")).isPresent();
VKey<User> key = VKey.create(User.class, "email@example.test");
assertThat(DatabaseHelper.loadByKeyIfPresent(key)).isPresent();
runCommandForced("--email", "email@example.test");
assertThat(UserDao.loadUser("email@example.test")).isEmpty();
assertThat(DatabaseHelper.loadByKeyIfPresent(key)).isEmpty();
verify(connection)
.sendPostRequest(
UpdateUserGroupAction.PATH,
@@ -14,12 +14,13 @@
package google.registry.tools;
import com.google.common.collect.ImmutableMap;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.RegistrarRole;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.model.console.UserRoles;
import google.registry.testing.DatabaseHelper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -28,8 +29,7 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
@BeforeEach
void beforeEach() {
User.ID_GENERATOR_FOR_TESTING.set(0L);
UserDao.saveUser(
DatabaseHelper.putInDb(
new User.Builder()
.setEmailAddress("johndoe@theregistrar.com")
.setUserRoles(
@@ -37,8 +37,7 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
.setRegistrarRoles(
ImmutableMap.of("TheRegistrar", RegistrarRole.PRIMARY_CONTACT))
.build())
.build());
UserDao.saveUser(
.build(),
new User.Builder()
.setEmailAddress("fte@google.com")
.setUserRoles(
@@ -53,7 +52,6 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
"""
User: {
emailAddress=fte@google.com
id=1
registryLockEmailAddress=null
registryLockPasswordHash=null
registryLockPasswordSalt=null
@@ -76,7 +74,6 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
"""
User: {
emailAddress=johndoe@theregistrar.com
id=0
registryLockEmailAddress=null
registryLockPasswordHash=null
registryLockPasswordSalt=null
@@ -91,7 +88,6 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
}
User: {
emailAddress=fte@google.com
id=1
registryLockEmailAddress=null
registryLockPasswordHash=null
registryLockPasswordSalt=null
@@ -114,7 +110,6 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
"""
User: {
emailAddress=johndoe@theregistrar.com
id=0
registryLockEmailAddress=null
registryLockPasswordHash=null
registryLockPasswordSalt=null
@@ -22,9 +22,11 @@ import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
import static google.registry.testing.CertificateSamples.SAMPLE_CERT_HASH;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.loadExistingUser;
import static google.registry.testing.DatabaseHelper.loadRegistrar;
import static google.registry.testing.DatabaseHelper.persistPremiumList;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.testing.DatabaseHelper.putInDb;
import static org.joda.money.CurrencyUnit.USD;
import static org.joda.time.DateTimeZone.UTC;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -39,7 +41,6 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.model.console.UserRoles;
import google.registry.model.registrar.Registrar;
import google.registry.model.tld.Tld;
@@ -447,12 +448,11 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
@Test
void testFailure_userExists() {
User user =
putInDb(
new User.Builder()
.setEmailAddress("contact@email.com")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
.build();
UserDao.saveUser(user);
.build());
IllegalStateException thrown =
assertThrows(
IllegalStateException.class,
@@ -494,12 +494,11 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
@Test
void testSuccess_userExists_replaceExisting() throws Exception {
User user =
putInDb(
new User.Builder()
.setEmailAddress("contact@email.com")
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
.build();
UserDao.saveUser(user);
.build());
runCommandForced(
"--overwrite",
@@ -520,7 +519,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
verifyUser("blobio-5", "contact@email.com");
// verify that the role is completely replaced, e.g., the global role is gone.
assertThat(UserDao.loadUser("contact@email.com").get().getUserRoles().getGlobalRole())
assertThat(loadExistingUser("contact@email.com").getUserRoles().getGlobalRole())
.isEqualTo(GlobalRole.NONE);
verifyIapPermission("contact@email.com");
@@ -15,13 +15,14 @@
package google.registry.tools;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.testing.DatabaseHelper.loadExistingUser;
import static google.registry.testing.DatabaseHelper.putInDb;
import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableMap;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.RegistrarRole;
import google.registry.model.console.User;
import google.registry.model.console.UserDao;
import google.registry.model.console.UserRoles;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -31,7 +32,7 @@ public class UpdateUserCommandTest extends CommandTestCase<UpdateUserCommand> {
@BeforeEach
void beforeEach() throws Exception {
UserDao.saveUser(
putInDb(
new User.Builder()
.setEmailAddress("user@example.test")
.setUserRoles(new UserRoles.Builder().build())
@@ -45,41 +46,39 @@ public class UpdateUserCommandTest extends CommandTestCase<UpdateUserCommand> {
"user@example.test",
"--registry_lock_email_address",
"registrylockemail@otherexample.test");
assertThat(UserDao.loadUser("user@example.test").get().getRegistryLockEmailAddress())
assertThat(loadExistingUser("user@example.test").getRegistryLockEmailAddress())
.hasValue("registrylockemail@otherexample.test");
}
@Test
void testSuccess_removeRegistryLockEmail() throws Exception {
UserDao.saveUser(
UserDao.loadUser("user@example.test")
.get()
putInDb(
loadExistingUser("user@example.test")
.asBuilder()
.setRegistryLockEmailAddress("registrylock@otherexample.test")
.build());
runCommandForced("--email", "user@example.test", "--registry_lock_email_address", "");
assertThat(UserDao.loadUser("user@example.test").get().getRegistryLockEmailAddress()).isEmpty();
assertThat(loadExistingUser("user@example.test").getRegistryLockEmailAddress()).isEmpty();
}
@Test
void testSuccess_admin() throws Exception {
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isFalse();
assertThat(loadExistingUser("user@example.test").getUserRoles().isAdmin()).isFalse();
runCommandForced("--email", "user@example.test", "--admin", "true");
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isTrue();
assertThat(loadExistingUser("user@example.test").getUserRoles().isAdmin()).isTrue();
runCommandForced("--email", "user@example.test", "--admin", "false");
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isFalse();
assertThat(loadExistingUser("user@example.test").getUserRoles().isAdmin()).isFalse();
}
@Test
void testSuccess_registrarRoles() throws Exception {
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
.isEmpty();
assertThat(loadExistingUser("user@example.test").getUserRoles().getRegistrarRoles()).isEmpty();
runCommandForced(
"--email",
"user@example.test",
"--registrar_roles",
"TheRegistrar=ACCOUNT_MANAGER,NewRegistrar=PRIMARY_CONTACT");
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
assertThat(loadExistingUser("user@example.test").getUserRoles().getRegistrarRoles())
.isEqualTo(
ImmutableMap.of(
"TheRegistrar",
@@ -87,16 +86,15 @@ public class UpdateUserCommandTest extends CommandTestCase<UpdateUserCommand> {
"NewRegistrar",
RegistrarRole.PRIMARY_CONTACT));
runCommandForced("--email", "user@example.test", "--registrar_roles", "");
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
.isEmpty();
assertThat(loadExistingUser("user@example.test").getUserRoles().getRegistrarRoles()).isEmpty();
}
@Test
void testSuccess_globalRole() throws Exception {
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getGlobalRole())
assertThat(loadExistingUser("user@example.test").getUserRoles().getGlobalRole())
.isEqualTo(GlobalRole.NONE);
runCommandForced("--email", "user@example.test", "--global_role", "FTE");
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getGlobalRole())
assertThat(loadExistingUser("user@example.test").getUserRoles().getGlobalRole())
.isEqualTo(GlobalRole.FTE);
}
@@ -17,7 +17,6 @@ package google.registry.ui.server.console.settings;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.registrar.RegistrarPocBase.Type.ADMIN;
import static google.registry.model.registrar.RegistrarPocBase.Type.WHOIS;
import static google.registry.testing.DatabaseHelper.createAdminUser;
import static google.registry.testing.DatabaseHelper.insertInDb;
import static google.registry.testing.DatabaseHelper.loadAllOf;
@@ -67,7 +66,7 @@ class ContactActionTest {
+ "\"emailAddress\":\"test.registrar1@example.com\","
+ "\"registrarId\":\"registrarId\","
+ "\"phoneNumber\":\"+1.9999999999\",\"faxNumber\":\"+1.9999999991\","
+ "\"types\":[\"WHOIS\",\"ADMIN\"],\"visibleInWhoisAsAdmin\":true,"
+ "\"types\":[\"ADMIN\"],\"visibleInWhoisAsAdmin\":true,"
+ "\"visibleInWhoisAsTech\":false,\"visibleInDomainWhoisAsAbuse\":false}";
private static String jsonRegistrar2 =
@@ -75,7 +74,7 @@ class ContactActionTest {
+ "\"emailAddress\":\"test.registrar2@example.com\","
+ "\"registrarId\":\"registrarId\","
+ "\"phoneNumber\":\"+1.1234567890\",\"faxNumber\":\"+1.1234567891\","
+ "\"types\":[\"WHOIS\",\"ADMIN\"],\"visibleInWhoisAsAdmin\":true,"
+ "\"types\":[\"ADMIN\"],\"visibleInWhoisAsAdmin\":true,"
+ "\"visibleInWhoisAsTech\":false,\"visibleInDomainWhoisAsAbuse\":false}";
private Registrar testRegistrar;
@@ -97,7 +96,7 @@ class ContactActionTest {
.setEmailAddress("test.registrar1@example.com")
.setPhoneNumber("+1.9999999999")
.setFaxNumber("+1.9999999991")
.setTypes(ImmutableSet.of(WHOIS, ADMIN))
.setTypes(ImmutableSet.of(ADMIN))
.setVisibleInWhoisAsAdmin(true)
.setVisibleInWhoisAsTech(false)
.setVisibleInDomainWhoisAsAbuse(false)
@@ -218,22 +217,22 @@ class ContactActionTest {
+ " {name=Test Registrar 1,"
+ " emailAddress=test.registrar1@example.com, registrarId=registrarId,"
+ " registryLockEmailAddress=null, phoneNumber=+1.9999999999,"
+ " faxNumber=+1.9999999991, types=[ADMIN, WHOIS],"
+ " faxNumber=+1.9999999991, types=[ADMIN],"
+ " visibleInWhoisAsAdmin=true, visibleInWhoisAsTech=false,"
+ " visibleInDomainWhoisAsAbuse=false,"
+ " allowedToSetRegistryLockPassword=false}\n"
+ " REMOVED:\n"
+ " {name=Test Registrar 1, emailAddress=incorrect@email.com,"
+ " registrarId=registrarId, registryLockEmailAddress=null,"
+ " phoneNumber=+1.9999999999, faxNumber=+1.9999999991, types=[WHOIS,"
+ " ADMIN], visibleInWhoisAsAdmin=true,"
+ " phoneNumber=+1.9999999999, faxNumber=+1.9999999991, types=[ADMIN],"
+ " visibleInWhoisAsAdmin=true,"
+ " visibleInWhoisAsTech=false, visibleInDomainWhoisAsAbuse=false,"
+ " allowedToSetRegistryLockPassword=false}\n"
+ " FINAL CONTENTS:\n"
+ " {name=Test Registrar 1,"
+ " emailAddress=test.registrar1@example.com, registrarId=registrarId,"
+ " registryLockEmailAddress=null, phoneNumber=+1.9999999999,"
+ " faxNumber=+1.9999999991, types=[ADMIN, WHOIS],"
+ " faxNumber=+1.9999999991, types=[ADMIN],"
+ " visibleInWhoisAsAdmin=true, visibleInWhoisAsTech=false,"
+ " visibleInDomainWhoisAsAbuse=false,"
+ " allowedToSetRegistryLockPassword=false}\n")
@@ -104,6 +104,8 @@ public class WhoisRegistrarFieldsActionTest {
ImmutableMap.of(
"whoisServer",
"whois.nic.google",
"icannReferralEmail",
"lol@sloth.test",
"phoneNumber",
"+1.4155552671",
"faxNumber",
@@ -0,0 +1,26 @@
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<update>
<domain:update
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.tld</domain:name>
<domain:add>
<domain:ns>
<domain:hostObj>ns2.example.foo</domain:hostObj>
</domain:ns>
<domain:contact type="tech">mak21</domain:contact>
<domain:status s="clientHold"
lang="en">Payment overdue.</domain:status>
</domain:add>
<domain:rem>
<domain:ns>
<domain:hostObj>ns1.example.foo</domain:hostObj>
</domain:ns>
<domain:contact type="tech">sh8013</domain:contact>
<domain:status s="clientUpdateProhibited"/>
</domain:rem>
</domain:update>
</update>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
@@ -261,11 +261,11 @@ td.section {
</tr>
<tr>
<td class="property_name">generated on</td>
<td class="property_value">2024-07-31 14:38:11</td>
<td class="property_value">2024-08-14 16:47:59</td>
</tr>
<tr>
<td class="property_name">last flyway file</td>
<td id="lastFlywayFile" class="property_value">V176__drop_login_email_address_column_from_registrar_poc.sql</td>
<td id="lastFlywayFile" class="property_value">V178__drop_user_id_history.sql</td>
</tr>
</tbody>
</table>
@@ -280,7 +280,7 @@ td.section {
<text text-anchor="start" x="3795" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated by</text>
<text text-anchor="start" x="3878" y="-29.8" font-family="Helvetica,sans-Serif" font-size="14.00">SchemaCrawler 16.21.4</text>
<text text-anchor="start" x="3794" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">generated on</text>
<text text-anchor="start" x="3878" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2024-07-31 14:38:11</text>
<text text-anchor="start" x="3878" y="-10.8" font-family="Helvetica,sans-Serif" font-size="14.00">2024-08-14 16:47:59</text>
<polygon fill="none" stroke="#888888" points="3791,-4 3791,-44 4027,-44 4027,-4 3791,-4" /> <!-- allocationtoken_a08ccbef -->
<g id="node1" class="node">
<title>
File diff suppressed because it is too large Load Diff
+2
View File
@@ -174,3 +174,5 @@ V173__create_feature_flag_table.sql
V174__user_pkey.sql
V175__user_update_history_id.sql
V176__drop_login_email_address_column_from_registrar_poc.sql
V177__drop_user_id.sql
V178__drop_user_id_history.sql
@@ -0,0 +1,15 @@
-- Copyright 2024 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
ALTER TABLE "User" DROP COLUMN id;
@@ -0,0 +1,15 @@
-- Copyright 2024 The Nomulus Authors. All Rights Reserved.
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
ALTER TABLE "UserUpdateHistory" DROP COLUMN user_id;
@@ -893,7 +893,6 @@
global_role text not null,
is_admin boolean not null,
registrar_roles hstore,
id int8,
primary key (email_address)
);
@@ -905,7 +904,6 @@
history_type text not null,
history_url text not null,
email_address text not null,
user_id int8 not null,
registry_lock_email_address text,
registry_lock_password_hash text,
registry_lock_password_salt text,
@@ -1316,7 +1316,6 @@ CREATE TABLE public."TmchCrl" (
--
CREATE TABLE public."User" (
id bigint,
email_address text NOT NULL,
registry_lock_password_hash text,
registry_lock_password_salt text,
@@ -1339,7 +1338,6 @@ CREATE TABLE public."UserUpdateHistory" (
history_request_body text,
history_type text NOT NULL,
history_url text NOT NULL,
user_id bigint,
email_address text NOT NULL,
registry_lock_password_hash text,
registry_lock_password_salt text,
@@ -1352,25 +1350,6 @@ CREATE TABLE public."UserUpdateHistory" (
);
--
-- Name: User_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
CREATE SEQUENCE public."User_id_seq"
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
--
-- Name: User_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--
ALTER SEQUENCE public."User_id_seq" OWNED BY public."User".id;
--
-- Name: project_wide_unique_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
@@ -1460,13 +1439,6 @@ ALTER TABLE ONLY public."SignedMarkRevocationList" ALTER COLUMN revision_id SET
ALTER TABLE ONLY public."Spec11ThreatMatch" ALTER COLUMN id SET DEFAULT nextval('public."SafeBrowsingThreat_id_seq"'::regclass);
--
-- Name: User id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY public."User" ALTER COLUMN id SET DEFAULT nextval('public."User_id_seq"'::regclass);
--
-- Name: AllocationToken AllocationToken_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
+8 -4
View File
@@ -31,12 +31,16 @@ do
echo "Updating cluster ${parts[0]} in location ${parts[1]}..."
gcloud container clusters get-credentials "${parts[0]}" \
--project "${project}" --location "${parts[1]}"
sed s/GCP_PROJECT/${project}/g "./kubernetes/nomulus-deployment.yaml" | \
sed s/ENVIRONMENT/${environment}/g | \
sed s/GCP_PROJECT/"${project}"/g "./kubernetes/nomulus-deployment.yaml" | \
sed s/ENVIRONMENT/"${environment}"/g | \
kubectl apply -f -
kubectl apply -f "./kubernetes/nomulus-service.yaml"
#kubectl apply -f "./kubernetes/nomulus-gateway.yaml"
# Kills all running pods, new pods created will be pulling the new image.
kubectl delete pods --all
done < <(gcloud container clusters list --project ${project} | grep nomulus)
# The multi-cluster gateway is only deployed to one cluster (the one in the US).
if [[ "${parts[1]}" == us-* ]]
then
kubectl apply -f "./kubernetes/nomulus-gateway.yaml"
fi
done < <(gcloud container clusters list --project "${project}" | grep nomulus)
kubectl config use-context "$current_context"
+15
View File
@@ -28,4 +28,19 @@ spec:
kind: ServiceImport
name: nomulus
port: 80
---
apiVersion: networking.gke.io/v1
kind: HealthCheckPolicy
metadata:
name: nomulus
spec:
default:
config:
type: HTTP
httpHealthCheck:
requestPath: /healthz/
targetRef:
group: net.gke.io
kind: ServiceImport
name: nomulus
+5 -5
View File
@@ -15,8 +15,8 @@ spec:
- port: 700
targetPort: epp
name: epp
#---
#kind: ServiceExport
#apiVersion: net.gke.io/v1
#metadata:
# name: nomulus
---
kind: ServiceExport
apiVersion: net.gke.io/v1
metadata:
name: nomulus
@@ -0,0 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<title>Health Check</title>
<body>Health check successful.</body>
</html>
@@ -77,6 +77,8 @@ import org.joda.time.DateTime;
@SuppressWarnings("FutureReturnValueIgnored")
public class EppClient implements Runnable {
// TODO(b/354014191): Add loadtest handling for other EPP flows
static {
Security.addProvider(new BouncyCastleProvider());
}
@@ -164,8 +166,9 @@ public class EppClient implements Runnable {
@Parameter(
names = {"--tld"},
description = "TLD to create domains on.")
private String tld = "test";
description = "TLD to create domains on.",
required = true)
private String tld = "loadtest";
@Parameter(
names = {"--force_terminate", "-ft"},
@@ -193,14 +196,17 @@ public class EppClient implements Runnable {
ImmutableList.Builder<String> templatesList = ImmutableList.builder();
ImmutableList.Builder<String> inputList = ImmutableList.builder();
templatesList.add(readStringFromFile(LOGIN_FILE));
String randomContactString = generateRandomString(5);
if (domainCreates > 0) {
templatesList.add(
readStringFromFile(CONTACT_CREATE_FILE)
.replace("@@RANDOM_CONTACT@@", randomContactString));
}
for (int i = 0; i < domainCreates; i++) {
String randomString = generateRandomString(5);
templatesList.add(
readStringFromFile(CONTACT_CREATE_FILE)
.replace("@@REPEAT_NUMBER@@", String.valueOf(i))
.replace("@@RANDOM@@", randomString));
templatesList.add(
readStringFromFile(DOMAIN_CREATE_FILE)
.replace("@@RANDOM_CONTACT@@", randomContactString)
.replace("@@REPEAT_NUMBER@@", String.valueOf(i))
.replace("@@RANDOM@@", randomString));
}
@@ -329,9 +335,14 @@ public class EppClient implements Runnable {
List<ChannelFuture> channelFutures = new ArrayList<>();
// Three requests: hello (from the proxy), login and logout plus additional configured EPP
// requests.
int requestPerConnection = 3 + (domainCreates * 2);
// Three requests: hello (from the proxy), login and logout
int requestsPerConnection = 3;
// If testing domain creates, add the number of domain creates per connection plus the one
// contact created for each connection
if (domainCreates > 0) {
requestsPerConnection += domainCreates + 1;
}
for (int i = 0; i < connections; i++) {
bootstrap.attr(CHANNEL_NUMBER, i);
@@ -347,6 +358,11 @@ public class EppClient implements Runnable {
}
LinkedHashSet<Integer> killedConnections = new LinkedHashSet<>();
LinkedHashSet<Integer> incompleteConnections = new LinkedHashSet<>();
List<Long> requestDurations = new ArrayList<>();
ZonedDateTime startTime = null;
ZonedDateTime endTime = null;
int failedRequests = 0;
// Wait for all channels to close.
for (ChannelFuture channelFuture : channelFutures) {
@@ -365,10 +381,47 @@ public class EppClient implements Runnable {
}
}
for (ChannelFuture channelFuture : channelFutures) {
Channel channel = channelFuture.channel();
int channelNumber = channel.attr(CHANNEL_NUMBER).get();
int responsesReceived = channel.attr(RESPONSE_RECEIVED).get().size();
for (int i = 0; i < responsesReceived; i++) {
requestDurations.add(
Duration.between(
channel.attr(REQUEST_SENT).get().get(i),
channel.attr(RESPONSE_RECEIVED).get().get(i))
.toMillis());
}
if (responsesReceived != requestsPerConnection) {
incompleteConnections.add(channelNumber);
failedRequests += (requestsPerConnection - channel.attr(REQUEST_SENT).get().size());
}
if (responsesReceived > 0) {
startTime = updateStartTime(channelFutures, channelNumber, startTime);
endTime = updateEndTime(channelFutures, channelNumber, endTime);
}
}
if (!incompleteConnections.isEmpty()) {
System.out.printf("%d incomplete connections: ", incompleteConnections.size());
for (int channelNumber : incompleteConnections) {
System.out.printf("Channel %d not finished\n", channelNumber);
}
System.out.print("\n");
}
System.out.println();
System.out.println("====== SUMMARY ======");
System.out.printf("Number of connections: %d\n", connections);
System.out.printf("Number of requests per connection: %d\n", requestPerConnection);
System.out.printf("Number of requests per connection: %d\n", requestsPerConnection);
if (startTime != null && endTime != null) {
System.out.printf(
"Average Response QPS: %.2f\n",
(double) requestDurations.size()
* 1000.0
/ Duration.between(startTime, endTime).toMillis());
}
System.out.printf("Number of incomplete connections: %d\n", incompleteConnections.size());
if (!killedConnections.isEmpty()) {
System.out.printf("Force killed connections (%d): ", killedConnections.size());
for (int channelNumber : killedConnections) {
@@ -376,6 +429,7 @@ public class EppClient implements Runnable {
}
System.out.print("\n");
}
System.out.printf("Number of Failed Requests: %d\n", failedRequests);
eventLoopGroup.shutdownGracefully();
@@ -388,4 +442,25 @@ public class EppClient implements Runnable {
throw new RuntimeException(e);
}
}
private ZonedDateTime updateStartTime(
List<ChannelFuture> channelFutures, int channelNumber, ZonedDateTime startTime) {
ZonedDateTime channelStartTime =
channelFutures.get(channelNumber).channel().attr(REQUEST_SENT).get().getFirst();
if (startTime == null || startTime.isAfter(channelStartTime)) {
return channelStartTime;
}
return startTime;
}
private ZonedDateTime updateEndTime(
List<ChannelFuture> channelFutures, int channelNumber, ZonedDateTime endTime) {
ZonedDateTime channelEndTime =
channelFutures.get(channelNumber).channel().attr(RESPONSE_RECEIVED).get().getLast();
if (endTime == null || endTime.isBefore(channelEndTime)) {
return channelEndTime;
}
return endTime;
}
}
@@ -4,7 +4,7 @@
<create>
<contact:create
xmlns:contact="urn:ietf:params:xml:ns:contact-1.0">
<contact:id>@@RANDOM@@-@@CHANNEL_NUMBER@@-@@REPEAT_NUMBER@@</contact:id>
<contact:id>@@RANDOM_CONTACT@@-@@CHANNEL_NUMBER@@</contact:id>
<contact:postalInfo type="int">
<contact:name>John Doe</contact:name>
<contact:org>Example, Inc.</contact:org>
@@ -9,9 +9,9 @@
<domain:ns>
<domain:hostObj>ns1.domain.com</domain:hostObj>
</domain:ns>
<domain:registrant>@@RANDOM@@-@@CHANNEL_NUMBER@@-@@REPEAT_NUMBER@@</domain:registrant>
<domain:contact type="admin">@@RANDOM@@-@@CHANNEL_NUMBER@@-@@REPEAT_NUMBER@@</domain:contact>
<domain:contact type="tech">@@RANDOM@@-@@CHANNEL_NUMBER@@-@@REPEAT_NUMBER@@</domain:contact>
<domain:registrant>@@RANDOM_CONTACT@@-@@CHANNEL_NUMBER@@</domain:registrant>
<domain:contact type="admin">@@RANDOM_CONTACT@@-@@CHANNEL_NUMBER@@</domain:contact>
<domain:contact type="tech">@@RANDOM_CONTACT@@-@@CHANNEL_NUMBER@@</domain:contact>
<domain:authInfo>
<domain:pw>somepassword</domain:pw>
</domain:authInfo>