mirror of
https://github.com/google/nomulus.git
synced 2025-06-21 03:40:47 +02:00
Allow setting the registry lock password in the UI (#241)
* Allow setting the lock password in the UI * Add more screenshot tests * Responses to CR and more screenshot tests * Formatting * Simplify lambda
This commit is contained in:
parent
0b2c65c59d
commit
45b960db1d
15 changed files with 266 additions and 38 deletions
|
@ -619,6 +619,7 @@ public class Registrar extends ImmutableObject implements Buildable, Jsonifiable
|
|||
.putListOfStrings("allowedTlds", getAllowedTlds())
|
||||
.putListOfStrings("ipAddressWhitelist", ipAddressWhitelist)
|
||||
.putListOfJsonObjects("contacts", getContacts())
|
||||
.put("registryLockAllowed", registryLockAllowed)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -314,6 +314,7 @@ public class RegistrarContact extends ImmutableObject implements Jsonifiable {
|
|||
.put("visibleInWhoisAsTech", visibleInWhoisAsTech)
|
||||
.put("visibleInDomainWhoisAsAbuse", visibleInDomainWhoisAsAbuse)
|
||||
.put("allowedToSetRegistryLockPassword", allowedToSetRegistryLockPassword)
|
||||
.put("registryLockAllowed", isRegistryLockAllowed())
|
||||
.put("gaeUserId", gaeUserId)
|
||||
.build();
|
||||
}
|
||||
|
|
|
@ -202,8 +202,13 @@ public final class RegistrarFormFields {
|
|||
.build();
|
||||
|
||||
public static final FormField<String, String> CONTACT_GAE_USER_ID_FIELD =
|
||||
FormFields.NAME.asBuilderNamed("gaeUserId")
|
||||
.build();
|
||||
FormFields.NAME.asBuilderNamed("gaeUserId").build();
|
||||
|
||||
public static final FormField<Boolean, Boolean> CONTACT_ALLOWED_TO_SET_REGISTRY_LOCK_PASSWORD =
|
||||
FormField.named("allowedToSetRegistryLockPassword", Boolean.class).build();
|
||||
|
||||
public static final FormField<String, String> CONTACT_REGISTRY_LOCK_PASSWORD_FIELD =
|
||||
FormFields.NAME.asBuilderNamed("registryLockPassword").build();
|
||||
|
||||
public static final FormField<String, Set<RegistrarContact.Type>> CONTACT_TYPES =
|
||||
FormField.named("types")
|
||||
|
@ -360,6 +365,12 @@ public final class RegistrarFormFields {
|
|||
CONTACT_FAX_NUMBER_FIELD.extractUntyped(args).ifPresent(builder::setFaxNumber);
|
||||
CONTACT_TYPES.extractUntyped(args).ifPresent(builder::setTypes);
|
||||
CONTACT_GAE_USER_ID_FIELD.extractUntyped(args).ifPresent(builder::setGaeUserId);
|
||||
CONTACT_ALLOWED_TO_SET_REGISTRY_LOCK_PASSWORD
|
||||
.extractUntyped(args)
|
||||
.ifPresent(builder::setAllowedToSetRegistryLockPassword);
|
||||
CONTACT_REGISTRY_LOCK_PASSWORD_FIELD
|
||||
.extractUntyped(args)
|
||||
.ifPresent(builder::setRegistryLockPassword);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -438,6 +438,25 @@ public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonA
|
|||
throw new ContactRequirementException(
|
||||
"An abuse contact visible in domain WHOIS query must be designated");
|
||||
}
|
||||
|
||||
// Any contact(s) with new passwords must be allowed to set them
|
||||
for (RegistrarContact updatedContact : updatedContacts) {
|
||||
if (updatedContact.isRegistryLockAllowed()
|
||||
|| updatedContact.isAllowedToSetRegistryLockPassword()) {
|
||||
RegistrarContact existingContact =
|
||||
existingContacts.stream()
|
||||
.filter(
|
||||
contact -> contact.getEmailAddress().equals(updatedContact.getEmailAddress()))
|
||||
.findFirst()
|
||||
.orElseThrow(
|
||||
() ->
|
||||
new FormException(
|
||||
"Not allowed to set registry lock password directly on new contact"));
|
||||
if (!existingContact.isAllowedToSetRegistryLockPassword()) {
|
||||
throw new FormException("Registrar contact not allowed to set registry lock password");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -117,7 +117,8 @@ registry.json.Response.prototype.results;
|
|||
* localizedAddress: registry.json.RegistrarAddress,
|
||||
* whoisServer: (string?|undefined),
|
||||
* referralUrl: (string?|undefined),
|
||||
* contacts: !Array.<registry.json.RegistrarContact>
|
||||
* contacts: !Array.<registry.json.RegistrarContact>,
|
||||
* registryLockAllowed: boolean
|
||||
* }}
|
||||
*/
|
||||
registry.json.Registrar;
|
||||
|
@ -144,7 +145,9 @@ registry.json.RegistrarAddress;
|
|||
* visibleInDomainWhoisAsAbuse: boolean,
|
||||
* phoneNumber: (string?|undefined),
|
||||
* faxNumber: (string?|undefined),
|
||||
* types: (string?|undefined)
|
||||
* types: (string?|undefined),
|
||||
* allowedToSetRegistryLockPassword: boolean,
|
||||
* registryLockAllowed: boolean
|
||||
* }}
|
||||
*/
|
||||
registry.json.RegistrarContact;
|
||||
|
|
|
@ -102,7 +102,8 @@ registry.registrar.ContactSettings.prototype.renderItem = function(rspObj) {
|
|||
item: targetContact,
|
||||
namePrefix: 'contacts[' + targetContactNdx + '].',
|
||||
actualTypesLookup: actualTypesLookup,
|
||||
readonly: (rspObj.readonly || false)
|
||||
readonly: (rspObj.readonly || false),
|
||||
registryLockAllowedForRegistrar: rspObj.registryLockAllowed
|
||||
});
|
||||
this.setupAppbar();
|
||||
} else {
|
||||
|
|
|
@ -53,6 +53,8 @@
|
|||
{@param? description: ?} /** Input field description. */
|
||||
{@param? placeholder: ?} /** Placeholder text. */
|
||||
{@param? readonly: ?}
|
||||
{@param? disabled: ?}
|
||||
{@param? isPassword: ?}
|
||||
<tr class="{css('kd-settings-pane-section')}">
|
||||
<td>
|
||||
<label for="{if isNonnull($namePrefix)}{$namePrefix + $name}{else}{$name}{/if}"
|
||||
|
@ -82,6 +84,12 @@
|
|||
{/if}
|
||||
{if $readonly}
|
||||
readonly
|
||||
{/if}
|
||||
{if $disabled}
|
||||
disabled
|
||||
{/if}
|
||||
{if $isPassword}
|
||||
type="password"
|
||||
{/if}>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -105,6 +105,7 @@
|
|||
{@param item: legacy_object_map<string, ?>}
|
||||
{@param actualTypesLookup: legacy_object_map<string, bool>}
|
||||
{@param readonly: bool}
|
||||
{@param? registryLockAllowedForRegistrar: bool}
|
||||
<form name="item" class="{css('item')} {css('registrar')}">
|
||||
<h1>Contact Details</h1>
|
||||
{call .contactInfo data="all"}
|
||||
|
@ -112,6 +113,7 @@
|
|||
{param item: $item /}
|
||||
{param actualTypesLookup: $actualTypesLookup /}
|
||||
{param readonly: $readonly /}
|
||||
{param registryLockAllowedForRegistrar: $registryLockAllowedForRegistrar /}
|
||||
{/call}
|
||||
</form>
|
||||
{/template}
|
||||
|
@ -123,6 +125,7 @@
|
|||
{@param item: legacy_object_map<string, ?>}
|
||||
{@param actualTypesLookup: legacy_object_map<string, bool>}
|
||||
{@param? readonly: bool}
|
||||
{@param? registryLockAllowedForRegistrar: bool}
|
||||
{let $possibleTypesLookup: [
|
||||
['admin', 'Primary', 'Primary contact for general issues.'],
|
||||
['billing', 'Billing', 'Contact for financial communications & invoices.'],
|
||||
|
@ -165,6 +168,7 @@
|
|||
{param namePrefix: $namePrefix /}
|
||||
{param possibleTypesLookup: $possibleTypesLookup /}
|
||||
{param actualTypesLookup: $actualTypesLookup /}
|
||||
{param registryLockAllowedForRegistrar: $registryLockAllowedForRegistrar /}
|
||||
{/call}
|
||||
{/if}
|
||||
</table>
|
||||
|
@ -222,6 +226,7 @@
|
|||
{@param namePrefix: string}
|
||||
{@param possibleTypesLookup: list<list<string>>}
|
||||
{@param actualTypesLookup: legacy_object_map<string, bool>}
|
||||
{@param? registryLockAllowedForRegistrar: bool}
|
||||
<tr class="{css('kd-settings-pane-section')}">
|
||||
<td>
|
||||
<label class="{css('setting-label')}">Contact type</label>
|
||||
|
@ -236,7 +241,29 @@
|
|||
{param actualTypesLookup: $actualTypesLookup /}
|
||||
{/call}
|
||||
</tr>
|
||||
<tr><td colspan="2"><hr></tr>
|
||||
{if $registryLockAllowedForRegistrar}
|
||||
{let $placeholder: $item['registryLockAllowed'] ?
|
||||
// If registry lock is allowed, there's already a password
|
||||
'Contact support to reset password' :
|
||||
// Otherwise, if not allowed, support must enable it
|
||||
not $item['allowedToSetRegistryLockPassword'] ?
|
||||
'Contact support to enable registry lock' : '' /}
|
||||
{call registry.soy.forms.inputFieldRow data="all"}
|
||||
{param label: 'Registry lock password:' /}
|
||||
{param name: 'registryLockPassword' /}
|
||||
{param disabled: not $item['allowedToSetRegistryLockPassword'] /}
|
||||
{param isPassword: true /}
|
||||
{param placeholder: $placeholder /}
|
||||
{/call}
|
||||
{/if}
|
||||
{if isNonnull($item['allowedToSetRegistryLockPassword'])}
|
||||
<input type="hidden" name="allowedToSetRegistryLockPassword"
|
||||
value="{$item['allowedToSetRegistryLockPassword']}">
|
||||
{/if}
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<hr>
|
||||
</tr>
|
||||
{call .whoisVisibleRadios_}
|
||||
{param description: 'Show in Registrar WHOIS record as Admin contact' /}
|
||||
{param fieldName: $namePrefix + 'visibleInWhoisAsAdmin' /}
|
||||
|
@ -250,12 +277,10 @@
|
|||
{call .whoisVisibleRadios_}
|
||||
{param description:
|
||||
'Show Phone and Email in Domain WHOIS Record as Registrar Abuse Contact' +
|
||||
' (Per CL&D Requirements)'
|
||||
/}
|
||||
' (Per CL&D Requirements)' /}
|
||||
{param note:
|
||||
'*Can only apply to one contact. Selecting Yes for this contact will' +
|
||||
' force this setting for all other contacts to be No.'
|
||||
/}
|
||||
' force this setting for all other contacts to be No.' /}
|
||||
{param fieldName: $namePrefix + 'visibleInDomainWhoisAsAbuse' /}
|
||||
{param visible: $item['visibleInDomainWhoisAsAbuse'] == true /}
|
||||
{/call}
|
||||
|
|
|
@ -21,6 +21,7 @@ import static google.registry.testing.DatastoreHelper.persistSimpleResource;
|
|||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarContact;
|
||||
import google.registry.model.registrar.RegistrarContact.Type;
|
||||
|
@ -184,4 +185,97 @@ public class ContactSettingsTest extends RegistrarSettingsActionTestCase {
|
|||
"message", "The abuse contact visible in domain WHOIS query must have a phone number");
|
||||
assertMetric(CLIENT_ID, "update", "[OWNER]", "ERROR: ContactRequirementException");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccess_setRegistryLockPassword() {
|
||||
techContact =
|
||||
persistResource(techContact.asBuilder().setAllowedToSetRegistryLockPassword(true).build());
|
||||
Map<String, Object> contactMap = techContact.toJsonMap();
|
||||
contactMap.put("registryLockPassword", "hi");
|
||||
Map<String, Object> reqJson = loadRegistrar(CLIENT_ID).toJsonMap();
|
||||
reqJson.put(
|
||||
"contacts",
|
||||
ImmutableList.of(AppEngineRule.makeRegistrarContact2().toJsonMap(), contactMap));
|
||||
Map<String, Object> response =
|
||||
action.handleJsonRequest(ImmutableMap.of("op", "update", "id", CLIENT_ID, "args", reqJson));
|
||||
assertThat(response).containsAtLeastEntriesIn(ImmutableMap.of("status", "SUCCESS"));
|
||||
techContact = Iterables.getOnlyElement(loadRegistrar(CLIENT_ID).getContactsOfType(Type.TECH));
|
||||
assertThat(techContact.verifyRegistryLockPassword("hi")).isTrue();
|
||||
assertMetric(CLIENT_ID, "update", "[OWNER]", "SUCCESS");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPost_failure_setRegistryLockPassword_newContact() {
|
||||
Map<String, Object> reqJson = loadRegistrar(CLIENT_ID).toJsonMap();
|
||||
reqJson.put(
|
||||
"contacts",
|
||||
ImmutableList.of(
|
||||
AppEngineRule.makeRegistrarContact2()
|
||||
.asBuilder()
|
||||
.setEmailAddress("someotheremail@example.com")
|
||||
.setAllowedToSetRegistryLockPassword(true)
|
||||
.build()
|
||||
.toJsonMap(),
|
||||
techContact.toJsonMap()));
|
||||
|
||||
Map<String, Object> response =
|
||||
action.handleJsonRequest(ImmutableMap.of("op", "update", "id", CLIENT_ID, "args", reqJson));
|
||||
assertThat(response)
|
||||
.containsExactly(
|
||||
"status",
|
||||
"ERROR",
|
||||
"results",
|
||||
ImmutableList.of(),
|
||||
"message",
|
||||
"Not allowed to set registry lock password directly on new contact");
|
||||
assertMetric(CLIENT_ID, "update", "[OWNER]", "ERROR: FormException");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPost_failure_setRegistryLockPassword_notAllowed() {
|
||||
// "allowedToSetRegistryLockPassword" must be set through the back end first
|
||||
// before we can set a password through the UI
|
||||
Map<String, Object> contactMap =
|
||||
techContact.asBuilder().setAllowedToSetRegistryLockPassword(true).build().toJsonMap();
|
||||
contactMap.put("registryLockPassword", "hi");
|
||||
Map<String, Object> reqJson = loadRegistrar(CLIENT_ID).toJsonMap();
|
||||
reqJson.put(
|
||||
"contacts",
|
||||
ImmutableList.of(AppEngineRule.makeRegistrarContact2().toJsonMap(), contactMap));
|
||||
|
||||
Map<String, Object> response =
|
||||
action.handleJsonRequest(ImmutableMap.of("op", "update", "id", CLIENT_ID, "args", reqJson));
|
||||
assertThat(response)
|
||||
.containsExactly(
|
||||
"status",
|
||||
"ERROR",
|
||||
"results",
|
||||
ImmutableList.of(),
|
||||
"message",
|
||||
"Registrar contact not allowed to set registry lock password");
|
||||
assertMetric(CLIENT_ID, "update", "[OWNER]", "ERROR: FormException");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPost_failure_setRegistryLockAllowed() {
|
||||
// One cannot set the "isAllowedToSetRegistryLockPassword" field through the UI
|
||||
Map<String, Object> reqJson = loadRegistrar(CLIENT_ID).toJsonMap();
|
||||
reqJson.put(
|
||||
"contacts",
|
||||
ImmutableList.of(
|
||||
AppEngineRule.makeRegistrarContact2().toJsonMap(),
|
||||
techContact.asBuilder().setAllowedToSetRegistryLockPassword(true).build().toJsonMap()));
|
||||
|
||||
Map<String, Object> response =
|
||||
action.handleJsonRequest(ImmutableMap.of("op", "update", "id", CLIENT_ID, "args", reqJson));
|
||||
assertThat(response)
|
||||
.containsExactly(
|
||||
"status",
|
||||
"ERROR",
|
||||
"results",
|
||||
ImmutableList.of(),
|
||||
"message",
|
||||
"Registrar contact not allowed to set registry lock password");
|
||||
assertMetric(CLIENT_ID, "update", "[OWNER]", "ERROR: FormException");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ package google.registry.webdriver;
|
|||
|
||||
import static google.registry.server.Fixture.BASIC;
|
||||
import static google.registry.server.Route.route;
|
||||
import static google.registry.testing.AppEngineRule.makeRegistrar2;
|
||||
import static google.registry.testing.AppEngineRule.makeRegistrarContact2;
|
||||
import static google.registry.testing.DatastoreHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatastoreHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
@ -130,6 +132,69 @@ public class RegistrarConsoleScreenshotTest extends WebDriverTestCase {
|
|||
driver.diffPage("page");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingsContactEdit_setRegistryLockPassword() throws Throwable {
|
||||
server.runInAppEngineEnvironment(
|
||||
() -> {
|
||||
persistResource(
|
||||
makeRegistrarContact2()
|
||||
.asBuilder()
|
||||
.setAllowedToSetRegistryLockPassword(true)
|
||||
.build());
|
||||
persistResource(makeRegistrar2().asBuilder().setRegistryLockAllowed(true).build());
|
||||
return null;
|
||||
});
|
||||
driver.manage().window().setSize(new Dimension(1050, 2000));
|
||||
driver.get(server.getUrl("/registrar#contact-settings/johndoe@theregistrar.com"));
|
||||
Thread.sleep(1000);
|
||||
driver.waitForElement(By.tagName("h1"));
|
||||
driver.waitForElement(By.id("reg-app-btn-edit")).click();
|
||||
driver.diffPage("page");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingsContactEdit_setRegistryLockPassword_alreadySet() throws Throwable {
|
||||
server.runInAppEngineEnvironment(
|
||||
() -> {
|
||||
persistResource(
|
||||
makeRegistrarContact2()
|
||||
.asBuilder()
|
||||
.setAllowedToSetRegistryLockPassword(true)
|
||||
.setRegistryLockPassword("hi")
|
||||
.build());
|
||||
persistResource(makeRegistrar2().asBuilder().setRegistryLockAllowed(true).build());
|
||||
return null;
|
||||
});
|
||||
driver.manage().window().setSize(new Dimension(1050, 2000));
|
||||
driver.get(server.getUrl("/registrar#contact-settings/johndoe@theregistrar.com"));
|
||||
Thread.sleep(1000);
|
||||
driver.waitForElement(By.tagName("h1"));
|
||||
driver.waitForElement(By.id("reg-app-btn-edit")).click();
|
||||
driver.diffPage("page");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingsContactEdit_setRegistryLockPassword_notAllowedForContact() throws Throwable {
|
||||
server.runInAppEngineEnvironment(
|
||||
() -> persistResource(makeRegistrar2().asBuilder().setRegistryLockAllowed(true).build()));
|
||||
driver.manage().window().setSize(new Dimension(1050, 2000));
|
||||
driver.get(server.getUrl("/registrar#contact-settings/johndoe@theregistrar.com"));
|
||||
Thread.sleep(1000);
|
||||
driver.waitForElement(By.tagName("h1"));
|
||||
driver.waitForElement(By.id("reg-app-btn-edit")).click();
|
||||
driver.diffPage("page");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingsContactAdd() throws Throwable {
|
||||
driver.manage().window().setSize(new Dimension(1050, 2000));
|
||||
driver.get(server.getUrl("/registrar#contact-settings"));
|
||||
Thread.sleep(1000);
|
||||
driver.waitForElement(By.tagName("h1"));
|
||||
driver.waitForElement(By.id("reg-app-btn-add")).click();
|
||||
driver.diffPage("page");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void settingsAdmin_whenAdmin() throws Throwable {
|
||||
server.setIsAdmin(true);
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
Binary file not shown.
Before Width: | Height: | Size: 89 KiB After Width: | Height: | Size: 89 KiB |
Binary file not shown.
After Width: | Height: | Size: 93 KiB |
Binary file not shown.
After Width: | Height: | Size: 93 KiB |
Binary file not shown.
After Width: | Height: | Size: 91 KiB |
Loading…
Add table
Add a link
Reference in a new issue