mirror of
https://github.com/google/nomulus.git
synced 2025-07-23 19:20:44 +02:00
Add console registrars screen API support to /console-api/registrars endpoint (#2095)
This commit is contained in:
parent
68d35d2d95
commit
ec9a220bc3
13 changed files with 403 additions and 131 deletions
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2023 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.adapters;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import google.registry.model.adapters.CurrencyUnitAdapter.UnknownCurrencyException;
|
||||
import java.io.IOException;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
|
||||
public class CurrencyJsonAdapter extends TypeAdapter<CurrencyUnit> {
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, CurrencyUnit value) throws IOException {
|
||||
String currency = CurrencyUnitAdapter.convertFromCurrency(value);
|
||||
out.value(currency);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CurrencyUnit read(JsonReader in) throws IOException {
|
||||
String currency = in.nextString();
|
||||
try {
|
||||
return CurrencyUnitAdapter.convertFromString(currency);
|
||||
} catch (UnknownCurrencyException e) {
|
||||
throw new IOException("Unknown currency");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||
// Copyright 2023 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.
|
||||
|
@ -22,9 +22,7 @@ import org.joda.money.CurrencyUnit;
|
|||
/** Adapter to use Joda {@link CurrencyUnit} when marshalling strings. */
|
||||
public class CurrencyUnitAdapter extends XmlAdapter<String, CurrencyUnit> {
|
||||
|
||||
/** Parses a string into a {@link CurrencyUnit} object. */
|
||||
@Override
|
||||
public CurrencyUnit unmarshal(String currency) throws UnknownCurrencyException {
|
||||
public static CurrencyUnit convertFromString(String currency) throws UnknownCurrencyException {
|
||||
try {
|
||||
return CurrencyUnit.of(nullToEmpty(currency).trim());
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
@ -32,10 +30,20 @@ public class CurrencyUnitAdapter extends XmlAdapter<String, CurrencyUnit> {
|
|||
}
|
||||
}
|
||||
|
||||
public static String convertFromCurrency(CurrencyUnit currency) {
|
||||
return currency == null ? null : currency.toString();
|
||||
}
|
||||
|
||||
/** Parses a string into a {@link CurrencyUnit} object. */
|
||||
@Override
|
||||
public CurrencyUnit unmarshal(String currency) throws UnknownCurrencyException {
|
||||
return convertFromString(currency);
|
||||
}
|
||||
|
||||
/** Converts {@link CurrencyUnit} to a string. */
|
||||
@Override
|
||||
public String marshal(CurrencyUnit currency) {
|
||||
return currency == null ? null : currency.toString();
|
||||
return convertFromCurrency(currency);
|
||||
}
|
||||
|
||||
/** Exception to throw when failing to parse a currency. */
|
||||
|
|
|
@ -20,6 +20,7 @@ import static google.registry.util.CollectionUtils.nullToEmptyImmutableCopy;
|
|||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.JsonMapBuilder;
|
||||
|
@ -87,6 +88,7 @@ public class Address extends ImmutableObject implements Jsonifiable, UnsafeSeria
|
|||
*/
|
||||
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
|
||||
@Transient
|
||||
@Expose
|
||||
protected List<String> street;
|
||||
|
||||
@XmlTransient @IgnoredInDiffableMap protected String streetLine1;
|
||||
|
@ -96,18 +98,22 @@ public class Address extends ImmutableObject implements Jsonifiable, UnsafeSeria
|
|||
@XmlTransient @IgnoredInDiffableMap protected String streetLine3;
|
||||
|
||||
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
|
||||
@Expose
|
||||
protected String city;
|
||||
|
||||
@XmlElement(name = "sp")
|
||||
@XmlJavaTypeAdapter(NormalizedStringAdapter.class)
|
||||
@Expose
|
||||
protected String state;
|
||||
|
||||
@XmlElement(name = "pc")
|
||||
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
|
||||
@Expose
|
||||
protected String zip;
|
||||
|
||||
@XmlElement(name = "cc")
|
||||
@XmlJavaTypeAdapter(CollapsedStringAdapter.class)
|
||||
@Expose
|
||||
protected String countryCode;
|
||||
|
||||
public ImmutableList<String> getStreet() {
|
||||
|
|
|
@ -211,6 +211,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
|||
*/
|
||||
@Id
|
||||
@Column(nullable = false)
|
||||
@Expose
|
||||
String registrarId;
|
||||
|
||||
/**
|
||||
|
@ -224,6 +225,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
|||
* @see <a href="http://www.icann.org/registrar-reports/accredited-list.html">ICANN-Accredited
|
||||
* Registrars</a>
|
||||
*/
|
||||
@Expose
|
||||
@Column(nullable = false)
|
||||
String registrarName;
|
||||
|
||||
|
@ -237,7 +239,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
|||
State state;
|
||||
|
||||
/** The set of TLDs which this registrar is allowed to access. */
|
||||
Set<String> allowedTlds;
|
||||
@Expose Set<String> allowedTlds;
|
||||
|
||||
/** Host name of WHOIS server. */
|
||||
String whoisServer;
|
||||
|
@ -287,6 +289,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
|||
* unrestricted UTF-8.
|
||||
*/
|
||||
@Embedded
|
||||
@Expose
|
||||
@AttributeOverrides({
|
||||
@AttributeOverride(
|
||||
name = "streetLine1",
|
||||
|
@ -329,7 +332,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
|||
String faxNumber;
|
||||
|
||||
/** Email address. */
|
||||
String emailAddress;
|
||||
@Expose String emailAddress;
|
||||
|
||||
// External IDs.
|
||||
|
||||
|
@ -345,7 +348,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
|||
* @see <a href="http://www.iana.org/assignments/registrar-ids/registrar-ids.txt">Registrar
|
||||
* IDs</a>
|
||||
*/
|
||||
@Nullable Long ianaIdentifier;
|
||||
@Expose @Nullable Long ianaIdentifier;
|
||||
|
||||
/** Purchase Order number used for invoices in external billing system, if applicable. */
|
||||
@Nullable String poNumber;
|
||||
|
@ -358,7 +361,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
|||
* accessed by {@link #getBillingAccountMap}, a sorted map is returned to guarantee deterministic
|
||||
* behavior when serializing the map, for display purpose for instance.
|
||||
*/
|
||||
@Nullable Map<CurrencyUnit, String> billingAccountMap;
|
||||
@Expose @Nullable Map<CurrencyUnit, String> billingAccountMap;
|
||||
|
||||
/** URL of registrar's website. */
|
||||
String url;
|
||||
|
@ -369,10 +372,10 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
|||
* <p>This value is specified in the initial registrar contact. It can't be edited in the web GUI,
|
||||
* and it must be specified when the registrar account is created.
|
||||
*/
|
||||
String icannReferralEmail;
|
||||
@Expose String icannReferralEmail;
|
||||
|
||||
/** Id of the folder in drive used to publish information for this registrar. */
|
||||
String driveFolderId;
|
||||
@Expose String driveFolderId;
|
||||
|
||||
// Metadata.
|
||||
|
||||
|
@ -400,7 +403,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
|||
boolean contactsRequireSyncing = true;
|
||||
|
||||
/** Whether or not registry lock is allowed for this registrar. */
|
||||
boolean registryLockAllowed = false;
|
||||
@Expose boolean registryLockAllowed = false;
|
||||
|
||||
public String getRegistrarId() {
|
||||
return registrarId;
|
||||
|
|
|
@ -31,21 +31,28 @@ import com.google.common.io.ByteStreams;
|
|||
import com.google.common.io.CharStreams;
|
||||
import com.google.common.net.MediaType;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.protobuf.ByteString;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.model.adapters.CurrencyJsonAdapter;
|
||||
import google.registry.request.HttpException.BadRequestException;
|
||||
import google.registry.request.HttpException.UnsupportedMediaTypeException;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.lock.LockHandler;
|
||||
import google.registry.request.lock.LockHandlerImpl;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.CidrAddressBlock.CidrAddressBlockAdapter;
|
||||
import google.registry.util.DateTimeTypeAdapter;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import org.joda.money.CurrencyUnit;
|
||||
import org.joda.time.DateTime;
|
||||
import org.json.simple.JSONValue;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
|
@ -69,6 +76,18 @@ public final class RequestModule {
|
|||
this.authResult = authResult;
|
||||
}
|
||||
|
||||
@RequestScope
|
||||
@VisibleForTesting
|
||||
@Provides
|
||||
public static Gson provideGson() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(DateTime.class, new DateTimeTypeAdapter())
|
||||
.registerTypeAdapter(CidrAddressBlock.class, new CidrAddressBlockAdapter())
|
||||
.registerTypeAdapter(CurrencyUnit.class, new CurrencyJsonAdapter())
|
||||
.excludeFieldsWithoutExposeAnnotation()
|
||||
.create();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter(RequestParameters.PARAM_TLD)
|
||||
static String provideTld(HttpServletRequest req) {
|
||||
|
|
|
@ -14,7 +14,11 @@
|
|||
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
@ -23,46 +27,155 @@ import com.google.gson.Gson;
|
|||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.Registrar.State;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.Response;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.ui.server.registrar.JsonGetAction;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@Action(
|
||||
service = Action.Service.DEFAULT,
|
||||
path = RegistrarsAction.PATH,
|
||||
method = {GET},
|
||||
method = {GET, POST},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class RegistrarsAction implements JsonGetAction {
|
||||
private static final int PASSWORD_LENGTH = 16;
|
||||
private static final int PASSCODE_LENGTH = 5;
|
||||
static final String PATH = "/console-api/registrars";
|
||||
|
||||
private final AuthResult authResult;
|
||||
private final Response response;
|
||||
private final Gson gson;
|
||||
private final HttpServletRequest req;
|
||||
private Optional<Registrar> registrar;
|
||||
private StringGenerator passwordGenerator;
|
||||
private StringGenerator passcodeGenerator;
|
||||
|
||||
@Inject
|
||||
public RegistrarsAction(AuthResult authResult, Response response, Gson gson) {
|
||||
public RegistrarsAction(
|
||||
HttpServletRequest req,
|
||||
AuthResult authResult,
|
||||
Response response,
|
||||
Gson gson,
|
||||
@Parameter("registrar") Optional<Registrar> registrar,
|
||||
@Named("base58StringGenerator") StringGenerator passwordGenerator,
|
||||
@Named("digitOnlyStringGenerator") StringGenerator passcodeGenerator) {
|
||||
this.authResult = authResult;
|
||||
this.response = response;
|
||||
this.gson = gson;
|
||||
this.registrar = registrar;
|
||||
this.req = req;
|
||||
this.passcodeGenerator = passcodeGenerator;
|
||||
this.passwordGenerator = passwordGenerator;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
User user = authResult.userAuthInfo().get().consoleUser().get();
|
||||
if (req.getMethod().equals(GET.toString())) {
|
||||
getHandler(user);
|
||||
} else {
|
||||
postHandler(user);
|
||||
}
|
||||
}
|
||||
|
||||
private void getHandler(User user) {
|
||||
if (!user.getUserRoles().hasGlobalPermission(ConsolePermission.VIEW_REGISTRARS)) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
ImmutableList<String> registrarIds =
|
||||
ImmutableList<Registrar> registrars =
|
||||
Streams.stream(Registrar.loadAllCached())
|
||||
.filter(r -> r.getType() == Registrar.Type.REAL)
|
||||
.map(Registrar::getRegistrarId)
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
|
||||
response.setPayload(gson.toJson(registrarIds));
|
||||
response.setPayload(gson.toJson(registrars));
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
}
|
||||
|
||||
private void postHandler(User user) {
|
||||
if (!user.getUserRoles().isAdmin()) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!registrar.isPresent()) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
response.setPayload(gson.toJson("'registrar' parameter is not present"));
|
||||
return;
|
||||
}
|
||||
|
||||
Registrar registrarParam = registrar.get();
|
||||
String errorMsg = "Missing value for %s";
|
||||
try {
|
||||
checkArgument(!isNullOrEmpty(registrarParam.getRegistrarId()), errorMsg, "registrarId");
|
||||
checkArgument(!isNullOrEmpty(registrarParam.getRegistrarName()), errorMsg, "name");
|
||||
checkArgument(!registrarParam.getBillingAccountMap().isEmpty(), errorMsg, "billingAccount");
|
||||
checkArgument(registrarParam.getIanaIdentifier() != null, String.format(errorMsg, "ianaId"));
|
||||
checkArgument(
|
||||
!isNullOrEmpty(registrarParam.getIcannReferralEmail()), errorMsg, "referralEmail");
|
||||
checkArgument(!isNullOrEmpty(registrarParam.getDriveFolderId()), errorMsg, "driveId");
|
||||
checkArgument(!isNullOrEmpty(registrarParam.getEmailAddress()), errorMsg, "consoleUserEmail");
|
||||
checkArgument(
|
||||
registrarParam.getLocalizedAddress() != null
|
||||
&& !isNullOrEmpty(registrarParam.getLocalizedAddress().getState())
|
||||
&& !isNullOrEmpty(registrarParam.getLocalizedAddress().getCity())
|
||||
&& !isNullOrEmpty(registrarParam.getLocalizedAddress().getZip())
|
||||
&& !isNullOrEmpty(registrarParam.getLocalizedAddress().getCountryCode())
|
||||
&& !registrarParam.getLocalizedAddress().getStreet().isEmpty(),
|
||||
errorMsg,
|
||||
"address");
|
||||
|
||||
String password = passwordGenerator.createString(PASSWORD_LENGTH);
|
||||
String phonePasscode = passcodeGenerator.createString(PASSCODE_LENGTH);
|
||||
|
||||
Registrar registrar =
|
||||
new Registrar.Builder()
|
||||
.setRegistrarId(registrarParam.getRegistrarId())
|
||||
.setRegistrarName(registrarParam.getRegistrarName())
|
||||
.setBillingAccountMap(registrarParam.getBillingAccountMap())
|
||||
.setIanaIdentifier(Long.valueOf(registrarParam.getIanaIdentifier()))
|
||||
.setIcannReferralEmail(registrarParam.getIcannReferralEmail())
|
||||
.setEmailAddress(registrarParam.getIcannReferralEmail())
|
||||
.setDriveFolderId(registrarParam.getDriveFolderId())
|
||||
.setType(Registrar.Type.REAL)
|
||||
.setPassword(password)
|
||||
.setPhonePasscode(phonePasscode)
|
||||
.setState(State.PENDING)
|
||||
.setLocalizedAddress(registrarParam.getLocalizedAddress())
|
||||
.build();
|
||||
|
||||
RegistrarPoc contact =
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName(registrarParam.getEmailAddress())
|
||||
.setEmailAddress(registrarParam.getEmailAddress())
|
||||
.setLoginEmailAddress(registrarParam.getEmailAddress())
|
||||
.build();
|
||||
|
||||
tm().transact(
|
||||
() -> {
|
||||
checkArgument(
|
||||
!Registrar.loadByRegistrarId(registrar.getRegistrarId()).isPresent(),
|
||||
"Registrar with registrarId %s already exists",
|
||||
registrar.getRegistrarId());
|
||||
tm().putAll(registrar, contact);
|
||||
});
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
response.setPayload(gson.toJson(e.getMessage()));
|
||||
} catch (Throwable e) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_SERVER_ERROR);
|
||||
response.setPayload(gson.toJson(e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
package google.registry.ui.server.console.settings;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
|
||||
import avro.shaded.com.google.common.collect.ImmutableList;
|
||||
|
@ -36,28 +35,25 @@ import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAcce
|
|||
import google.registry.ui.server.registrar.JsonGetAction;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@Action(
|
||||
service = Action.Service.DEFAULT,
|
||||
path = SecurityAction.PATH,
|
||||
method = {GET, POST},
|
||||
method = {POST},
|
||||
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
|
||||
public class SecurityAction implements JsonGetAction {
|
||||
|
||||
static final String PATH = "/console-api/settings/security";
|
||||
private final HttpServletRequest req;
|
||||
private final AuthResult authResult;
|
||||
private final Response response;
|
||||
private final Gson gson;
|
||||
private final String registrarId;
|
||||
private AuthenticatedRegistrarAccessor registrarAccessor;
|
||||
private Optional<Registrar> registrar;
|
||||
private CertificateChecker certificateChecker;
|
||||
private final AuthenticatedRegistrarAccessor registrarAccessor;
|
||||
private final Optional<Registrar> registrar;
|
||||
private final CertificateChecker certificateChecker;
|
||||
|
||||
@Inject
|
||||
public SecurityAction(
|
||||
HttpServletRequest req,
|
||||
AuthResult authResult,
|
||||
Response response,
|
||||
Gson gson,
|
||||
|
@ -65,7 +61,6 @@ public class SecurityAction implements JsonGetAction {
|
|||
AuthenticatedRegistrarAccessor registrarAccessor,
|
||||
@Parameter("registrarId") String registrarId,
|
||||
@Parameter("registrar") Optional<Registrar> registrar) {
|
||||
this.req = req;
|
||||
this.authResult = authResult;
|
||||
this.response = response;
|
||||
this.gson = gson;
|
||||
|
@ -77,25 +72,6 @@ public class SecurityAction implements JsonGetAction {
|
|||
|
||||
@Override
|
||||
public void run() {
|
||||
if (req.getMethod().equals(GET.toString())) {
|
||||
getHandler();
|
||||
} else {
|
||||
postHandler();
|
||||
}
|
||||
}
|
||||
|
||||
private void getHandler() {
|
||||
try {
|
||||
Registrar registrar = registrarAccessor.getRegistrar(registrarId);
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
response.setPayload(gson.toJson(registrar));
|
||||
} catch (RegistrarAccessDeniedException e) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
|
||||
response.setPayload(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void postHandler() {
|
||||
User user = authResult.userAuthInfo().get().consoleUser().get();
|
||||
if (!user.getUserRoles().hasPermission(registrarId, ConsolePermission.EDIT_REGISTRAR_DETAILS)) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_FORBIDDEN);
|
||||
|
@ -153,17 +129,15 @@ public class SecurityAction implements JsonGetAction {
|
|||
registrarParameter
|
||||
.getClientCertificate()
|
||||
.ifPresent(
|
||||
newClientCert -> {
|
||||
updatedRegistrar.setClientCertificate(newClientCert, tm().getTransactionTime());
|
||||
});
|
||||
newClientCert ->
|
||||
updatedRegistrar.setClientCertificate(newClientCert, tm().getTransactionTime()));
|
||||
|
||||
registrarParameter
|
||||
.getFailoverClientCertificate()
|
||||
.ifPresent(
|
||||
failoverCert -> {
|
||||
updatedRegistrar.setFailoverClientCertificate(
|
||||
failoverCert, tm().getTransactionTime());
|
||||
});
|
||||
failoverCert ->
|
||||
updatedRegistrar.setFailoverClientCertificate(
|
||||
failoverCert, tm().getTransactionTime()));
|
||||
|
||||
tm().put(updatedRegistrar.build());
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
|
|
|
@ -25,12 +25,12 @@ import google.registry.model.console.RegistrarRole;
|
|||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.util.UtilsModule;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
@ -38,7 +38,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
|||
/** Tests for {@link google.registry.ui.server.console.ConsoleDomainGetAction}. */
|
||||
public class ConsoleDomainGetActionTest {
|
||||
|
||||
private static final Gson GSON = UtilsModule.provideGson();
|
||||
private static final Gson GSON = RequestModule.provideGson();
|
||||
private static final FakeResponse RESPONSE = new FakeResponse();
|
||||
|
||||
@RegisterExtension
|
||||
|
|
|
@ -15,11 +15,17 @@
|
|||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
|
@ -27,21 +33,69 @@ import google.registry.model.console.RegistrarRole;
|
|||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.util.UtilsModule;
|
||||
import google.registry.ui.server.registrar.RegistrarConsoleModule;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link google.registry.ui.server.console.RegistrarsAction}. */
|
||||
class RegistrarsActionTest {
|
||||
|
||||
private static final Gson GSON = UtilsModule.provideGson();
|
||||
private final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
private static final Gson GSON = RequestModule.provideGson();
|
||||
private FakeResponse response;
|
||||
|
||||
private StringGenerator passwordGenerator =
|
||||
new DeterministicStringGenerator("abcdefghijklmnopqrstuvwxyz");
|
||||
private StringGenerator passcodeGenerator = new DeterministicStringGenerator("314159265");
|
||||
|
||||
private ImmutableMap<String, String> userFriendlyKeysToRegistrarKeys =
|
||||
ImmutableMap.of(
|
||||
"registrarId", "registrarId",
|
||||
"registrarName", "name",
|
||||
"billingAccountMap", "billingAccount",
|
||||
"ianaIdentifier", "ianaId",
|
||||
"icannReferralEmail", "referralEmail",
|
||||
"driveFolderId", "driveId",
|
||||
"emailAddress", "consoleUserEmail",
|
||||
"localizedAddress", "address");
|
||||
|
||||
private ImmutableMap<String, String> registrarParamMap =
|
||||
ImmutableMap.of(
|
||||
"registrarId",
|
||||
"regIdTest",
|
||||
"registrarName",
|
||||
"name",
|
||||
"billingAccountMap",
|
||||
"{\"USD\": \"789\"}",
|
||||
"ianaIdentifier",
|
||||
"123",
|
||||
"icannReferralEmail",
|
||||
"cannReferralEmail@gmail.com",
|
||||
"driveFolderId",
|
||||
"testDriveId",
|
||||
"emailAddress",
|
||||
"testEmailAddress@gmail.com",
|
||||
"localizedAddress",
|
||||
"{ \"street\": [\"test street\"], \"city\": \"test city\", \"state\": \"test state\","
|
||||
+ " \"zip\": \"00700\", \"countryCode\": \"US\" }");
|
||||
|
||||
@RegisterExtension
|
||||
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
@ -53,6 +107,7 @@ class RegistrarsActionTest {
|
|||
persistResource(registrar);
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(
|
||||
|
@ -60,22 +115,98 @@ class RegistrarsActionTest {
|
|||
new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_LEAD).build()))));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
assertThat(response.getPayload()).isEqualTo("[\"NewRegistrar\",\"TheRegistrar\"]");
|
||||
String payload = response.getPayload();
|
||||
assertThat(
|
||||
ImmutableList.of("\"registrarId\":\"NewRegistrar\"", "\"registrarId\":\"TheRegistrar\"")
|
||||
.stream()
|
||||
.allMatch(s -> payload.contains(s)))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_getRegistrarIds() {
|
||||
void testSuccess_getRegistrars() {
|
||||
saveRegistrar("registrarId");
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(
|
||||
createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
String payload = response.getPayload();
|
||||
assertThat(
|
||||
ImmutableList.of(
|
||||
"\"registrarId\":\"NewRegistrar\"",
|
||||
"\"registrarId\":\"TheRegistrar\"",
|
||||
"\"registrarId\":\"registrarId\"")
|
||||
.stream()
|
||||
.allMatch(s -> payload.contains(s)))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createRegistrar() {
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(createUser(new UserRoles.Builder().setIsAdmin(true).build()))));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
Registrar r = loadRegistrar("regIdTest");
|
||||
assertThat(r).isNotNull();
|
||||
assertThat(
|
||||
loadAllOf(RegistrarPoc.class).stream()
|
||||
.filter(rPOC -> rPOC.getEmailAddress().equals("testEmailAddress@gmail.com"))
|
||||
.findAny()
|
||||
.isPresent())
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_createRegistrar_missingValue() {
|
||||
ImmutableMap<String, String> copy = ImmutableMap.copyOf(registrarParamMap);
|
||||
copy.keySet()
|
||||
.forEach(
|
||||
key -> {
|
||||
registrarParamMap =
|
||||
ImmutableMap.copyOf(
|
||||
copy.entrySet().stream()
|
||||
.filter(entry -> !entry.getKey().equals(key))
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(
|
||||
createUser(new UserRoles.Builder().setIsAdmin(true).build()))));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo(
|
||||
GSON.toJson(
|
||||
String.format(
|
||||
"Missing value for %s", userFriendlyKeysToRegistrarKeys.get(key))));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_createRegistrar_existingRegistrar() {
|
||||
saveRegistrar("regIdTest");
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(createUser(new UserRoles.Builder().setIsAdmin(true).build()))));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo("[\"NewRegistrar\",\"TheRegistrar\",\"registrarId\"]");
|
||||
.isEqualTo(GSON.toJson("Registrar with registrarId regIdTest already exists"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -83,6 +214,7 @@ class RegistrarsActionTest {
|
|||
saveRegistrar("registrarId");
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(
|
||||
|
@ -105,8 +237,46 @@ class RegistrarsActionTest {
|
|||
.build();
|
||||
}
|
||||
|
||||
private RegistrarsAction createAction(AuthResult authResult) {
|
||||
private RegistrarsAction createAction(Action.Method method, AuthResult authResult) {
|
||||
response = new FakeResponse();
|
||||
return new RegistrarsAction(authResult, response, GSON);
|
||||
when(request.getMethod()).thenReturn(method.toString());
|
||||
if (method.equals(Action.Method.GET)) {
|
||||
return new RegistrarsAction(
|
||||
request,
|
||||
authResult,
|
||||
response,
|
||||
GSON,
|
||||
Optional.ofNullable(null),
|
||||
passwordGenerator,
|
||||
passcodeGenerator);
|
||||
} else {
|
||||
try {
|
||||
doReturn(
|
||||
new BufferedReader(
|
||||
new StringReader("{\"registrar\":" + registrarParamMap.toString() + "}")))
|
||||
.when(request)
|
||||
.getReader();
|
||||
} catch (IOException e) {
|
||||
return new RegistrarsAction(
|
||||
request,
|
||||
authResult,
|
||||
response,
|
||||
GSON,
|
||||
Optional.ofNullable(null),
|
||||
passwordGenerator,
|
||||
passcodeGenerator);
|
||||
}
|
||||
Optional<Registrar> maybeRegistrar =
|
||||
RegistrarConsoleModule.provideRegistrar(
|
||||
GSON, RequestModule.provideJsonBody(request, GSON));
|
||||
return new RegistrarsAction(
|
||||
request,
|
||||
authResult,
|
||||
response,
|
||||
GSON,
|
||||
maybeRegistrar,
|
||||
passwordGenerator,
|
||||
passcodeGenerator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ import google.registry.request.auth.AuthSettings.AuthLevel;
|
|||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.ui.server.registrar.RegistrarConsoleModule;
|
||||
import google.registry.util.UtilsModule;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
@ -73,7 +72,7 @@ class ContactActionTest {
|
|||
private Registrar testRegistrar;
|
||||
private final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
private RegistrarPoc testRegistrarPoc;
|
||||
private static final Gson GSON = UtilsModule.provideGson();
|
||||
private static final Gson GSON = RequestModule.provideGson();
|
||||
private FakeResponse response;
|
||||
|
||||
@RegisterExtension
|
||||
|
|
|
@ -15,21 +15,17 @@
|
|||
package google.registry.ui.server.console.settings;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.CertificateSamples.SAMPLE_CERT;
|
||||
import static google.registry.testing.CertificateSamples.SAMPLE_CERT2;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.net.InetAddresses;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
|
@ -37,7 +33,6 @@ import google.registry.model.console.User;
|
|||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
|
@ -46,8 +41,6 @@ import google.registry.request.auth.UserAuthInfo;
|
|||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.ui.server.registrar.RegistrarConsoleModule;
|
||||
import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.UtilsModule;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
|
@ -66,7 +59,7 @@ class SecurityActionTest {
|
|||
"{\"registrarId\": \"registrarId\", \"clientCertificate\": \"%s\","
|
||||
+ " \"ipAddressAllowList\": [\"192.168.1.1/32\"]}",
|
||||
SAMPLE_CERT2);
|
||||
private static final Gson GSON = UtilsModule.provideGson();
|
||||
private static final Gson GSON = RequestModule.provideGson();
|
||||
private final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
private final FakeClock clock = new FakeClock();
|
||||
private Registrar testRegistrar;
|
||||
|
@ -94,39 +87,11 @@ class SecurityActionTest {
|
|||
testRegistrar = saveRegistrar("registrarId");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_getRegistrarInfo() throws IOException {
|
||||
persistResource(
|
||||
testRegistrar
|
||||
.asBuilder()
|
||||
.setClientCertificate(SAMPLE_CERT, clock.nowUtc())
|
||||
.setIpAddressAllowList(
|
||||
ImmutableSet.of(
|
||||
CidrAddressBlock.create(InetAddresses.forString("192.168.1.1"), 32),
|
||||
CidrAddressBlock.create(InetAddresses.forString("2001:db8::1"), 128)))
|
||||
.build());
|
||||
SecurityAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(
|
||||
createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))),
|
||||
testRegistrar.getRegistrarId());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
String payload = response.getPayload().replace("\\n", "").replace("\\u003d", "=");
|
||||
assertThat(payload).contains(SAMPLE_CERT.replace("\n", ""));
|
||||
assertThat(payload).contains("192.168.1.1/32");
|
||||
assertThat(payload).contains("2001:db8:0:0:0:0:0:1/128");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_postRegistrarInfo() throws IOException {
|
||||
clock.setTo(DateTime.parse("2020-11-01T00:00:00Z"));
|
||||
SecurityAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
UserAuthInfo.create(
|
||||
|
@ -149,20 +114,8 @@ class SecurityActionTest {
|
|||
.build();
|
||||
}
|
||||
|
||||
private SecurityAction createAction(
|
||||
Action.Method method, AuthResult authResult, String registrarId) throws IOException {
|
||||
when(request.getMethod()).thenReturn(method.toString());
|
||||
if (method.equals(Action.Method.GET)) {
|
||||
return new SecurityAction(
|
||||
request,
|
||||
authResult,
|
||||
response,
|
||||
GSON,
|
||||
certificateChecker,
|
||||
registrarAccessor,
|
||||
registrarId,
|
||||
Optional.empty());
|
||||
} else {
|
||||
private SecurityAction createAction(AuthResult authResult, String registrarId)
|
||||
throws IOException {
|
||||
doReturn(new BufferedReader(new StringReader("{\"registrar\":" + jsonRegistrar1 + "}")))
|
||||
.when(request)
|
||||
.getReader();
|
||||
|
@ -170,7 +123,6 @@ class SecurityActionTest {
|
|||
RegistrarConsoleModule.provideRegistrar(
|
||||
GSON, RequestModule.provideJsonBody(request, GSON));
|
||||
return new SecurityAction(
|
||||
request,
|
||||
authResult,
|
||||
response,
|
||||
GSON,
|
||||
|
@ -178,6 +130,6 @@ class SecurityActionTest {
|
|||
registrarAccessor,
|
||||
registrarId,
|
||||
maybeRegistrar);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/epp EppTlsAction POST n API APP PUBLIC
|
||||
/console-api/domain ConsoleDomainGetAction GET n API,LEGACY USER PUBLIC
|
||||
/console-api/registrars RegistrarsAction GET n API,LEGACY USER PUBLIC
|
||||
/console-api/registrars RegistrarsAction GET,POST n API,LEGACY USER PUBLIC
|
||||
/console-api/settings/contacts ContactAction GET,POST n API,LEGACY USER PUBLIC
|
||||
/console-api/settings/security SecurityAction GET,POST n API,LEGACY USER PUBLIC
|
||||
/console-api/settings/security SecurityAction POST n API,LEGACY USER PUBLIC
|
||||
/registrar ConsoleUiAction GET n API,LEGACY NONE PUBLIC
|
||||
/registrar-create ConsoleRegistrarCreatorAction POST,GET n API,LEGACY NONE PUBLIC
|
||||
/registrar-ote-setup ConsoleOteSetupAction POST,GET n API,LEGACY NONE PUBLIC
|
||||
|
|
|
@ -14,19 +14,15 @@
|
|||
|
||||
package google.registry.util;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import dagger.Binds;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import google.registry.util.CidrAddressBlock.CidrAddressBlockAdapter;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.ProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.joda.time.DateTime;
|
||||
|
||||
/** Dagger module to provide instances of various utils classes. */
|
||||
@Module
|
||||
|
@ -75,13 +71,4 @@ public abstract class UtilsModule {
|
|||
return new RandomStringGenerator(StringGenerator.Alphabets.DIGITS_ONLY, secureRandom);
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
public static Gson provideGson() {
|
||||
return new GsonBuilder()
|
||||
.registerTypeAdapter(DateTime.class, new DateTimeTypeAdapter())
|
||||
.registerTypeAdapter(CidrAddressBlock.class, new CidrAddressBlockAdapter())
|
||||
.excludeFieldsWithoutExposeAnnotation()
|
||||
.create();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue