mirror of
https://github.com/google/nomulus
synced 2025-12-23 06:15:42 +00:00
Add Registry Lock password reset on front end (#2785)
This is only enabled for admins, for now at least. It sends an email to the registry lock email address to reset it.
This commit is contained in:
@@ -280,4 +280,15 @@ export class BackendService {
|
||||
`/console-api/registry-lock-verify?lockVerificationCode=${lockVerificationCode}`
|
||||
);
|
||||
}
|
||||
|
||||
requestRegistryLockPasswordReset(
|
||||
registrarId: string,
|
||||
registryLockEmail: string
|
||||
) {
|
||||
return this.http.post('/console-api/password-reset-request', {
|
||||
type: 'REGISTRY_LOCK',
|
||||
registrarId,
|
||||
registryLockEmail,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,15 @@
|
||||
roleToDescription(userDetails().role)
|
||||
}}</span>
|
||||
</mat-list-item>
|
||||
@if (userDetails().password) {
|
||||
@if (userDetails().registryLockEmailAddress) {
|
||||
<mat-divider></mat-divider>
|
||||
<mat-list-item role="listitem">
|
||||
<span class="console-app__list-key">Registry Lock email</span>
|
||||
<span class="console-app__list-value">{{
|
||||
userDetails().registryLockEmailAddress
|
||||
}}</span>
|
||||
</mat-list-item>
|
||||
} @if (userDetails().password) {
|
||||
<mat-divider></mat-divider>
|
||||
<mat-list-item role="listitem">
|
||||
<span class="console-app__list-key">Password</span>
|
||||
|
||||
@@ -35,5 +35,8 @@
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.console-app__list-key {
|
||||
width: 160px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,57 @@
|
||||
<form (ngSubmit)="saveEdit($event)" #form>
|
||||
<p *ngIf="isNew()">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label
|
||||
>User name prefix:
|
||||
<mat-icon
|
||||
matTooltip="Prefix will be combined with registrar ID to create a unique user name - {prefix}.{registrarId}@registry.google"
|
||||
>help_outline</mat-icon
|
||||
></mat-label
|
||||
>
|
||||
<input
|
||||
matInput
|
||||
minlength="3"
|
||||
maxlength="3"
|
||||
[required]="true"
|
||||
[(ngModel)]="user().emailAddress"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label
|
||||
>User Role:
|
||||
<mat-icon
|
||||
matTooltip="Viewer role doesn't allow making updates; Editor role allows updates, like Contacts delete or SSL certificate change"
|
||||
>help_outline</mat-icon
|
||||
></mat-label
|
||||
>
|
||||
<mat-select [(ngModel)]="user().role" name="userRole">
|
||||
<mat-option value="PRIMARY_CONTACT">Editor</mat-option>
|
||||
<mat-option value="ACCOUNT_MANAGER">Viewer</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<div class="console-app__user-edit">
|
||||
<form (ngSubmit)="saveEdit($event)" #form>
|
||||
<p *ngIf="isNew()">
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label
|
||||
>User name prefix:
|
||||
<mat-icon
|
||||
matTooltip="Prefix will be combined with registrar ID to create a unique user name - {prefix}.{registrarId}@registry.google"
|
||||
>help_outline</mat-icon
|
||||
></mat-label
|
||||
>
|
||||
<input
|
||||
matInput
|
||||
minlength="3"
|
||||
maxlength="3"
|
||||
[required]="true"
|
||||
[(ngModel)]="user().emailAddress"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label
|
||||
>User Role:
|
||||
<mat-icon
|
||||
matTooltip="Viewer role doesn't allow making updates; Editor role allows updates, like Contacts delete or SSL certificate change"
|
||||
>help_outline</mat-icon
|
||||
></mat-label
|
||||
>
|
||||
<mat-select [(ngModel)]="user().role" name="userRole">
|
||||
<mat-option value="PRIMARY_CONTACT">Editor</mat-option>
|
||||
<mat-option value="ACCOUNT_MANAGER">Viewer</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
aria-label="Save user"
|
||||
type="submit"
|
||||
aria-label="Save changes to the user"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
@if(userDataService.userData()?.isAdmin) {
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
aria-label="Save user"
|
||||
type="submit"
|
||||
aria-label="Save changes to the user"
|
||||
aria-label="Reset registry lock password"
|
||||
(click)="requestRegistryLockPasswordReset()"
|
||||
>
|
||||
Save
|
||||
Reset registry lock password
|
||||
</button>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2025 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
.console-app__user-edit {
|
||||
button {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,56 @@ import {
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Inject,
|
||||
input,
|
||||
Output,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { User } from './users.service';
|
||||
import { User, UsersService } from './users.service';
|
||||
import { UserDataService } from '../shared/services/userData.service';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { RegistrarService } from '../registrar/registrar.service';
|
||||
import {
|
||||
MAT_DIALOG_DATA,
|
||||
MatDialog,
|
||||
MatDialogRef,
|
||||
} from '@angular/material/dialog';
|
||||
import { filter, switchMap, take } from 'rxjs';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-reset-lock-password-dialog',
|
||||
template: `
|
||||
<h2 mat-dialog-title>Please confirm the password reset:</h2>
|
||||
<mat-dialog-content>
|
||||
This will send a registry lock password reset email to
|
||||
{{ data.registryLockEmailAddress }}.
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions>
|
||||
<button mat-button (click)="onCancel()">Cancel</button>
|
||||
<button mat-button color="warn" (click)="onSave()">Confirm</button>
|
||||
</mat-dialog-actions>
|
||||
`,
|
||||
imports: [CommonModule, MaterialModule],
|
||||
})
|
||||
export class ResetRegistryLockPasswordComponent {
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<ResetRegistryLockPasswordComponent>,
|
||||
@Inject(MAT_DIALOG_DATA)
|
||||
public data: { registryLockEmailAddress: string }
|
||||
) {}
|
||||
|
||||
onSave(): void {
|
||||
this.dialogRef.close(true);
|
||||
}
|
||||
|
||||
onCancel(): void {
|
||||
this.dialogRef.close(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-user-edit-form',
|
||||
@@ -39,12 +82,22 @@ export class UserEditFormComponent {
|
||||
{
|
||||
emailAddress: '',
|
||||
role: 'ACCOUNT_MANAGER',
|
||||
registryLockEmailAddress: '',
|
||||
},
|
||||
{ transform: (user: User) => structuredClone(user) }
|
||||
);
|
||||
|
||||
@Output() onEditComplete = new EventEmitter<User>();
|
||||
|
||||
constructor(
|
||||
protected userDataService: UserDataService,
|
||||
private backendService: BackendService,
|
||||
private resetRegistryLockPasswordDialog: MatDialog,
|
||||
private registrarService: RegistrarService,
|
||||
private usersService: UsersService,
|
||||
private _snackBar: MatSnackBar
|
||||
) {}
|
||||
|
||||
saveEdit(e: SubmitEvent) {
|
||||
e.preventDefault();
|
||||
if (this.form.nativeElement.checkValidity()) {
|
||||
@@ -53,4 +106,34 @@ export class UserEditFormComponent {
|
||||
this.form.nativeElement.reportValidity();
|
||||
}
|
||||
}
|
||||
|
||||
sendRegistryLockPasswordResetRequest() {
|
||||
return this.backendService.requestRegistryLockPasswordReset(
|
||||
this.registrarService.registrarId(),
|
||||
this.user().registryLockEmailAddress!
|
||||
);
|
||||
}
|
||||
|
||||
requestRegistryLockPasswordReset() {
|
||||
const dialogRef = this.resetRegistryLockPasswordDialog.open(
|
||||
ResetRegistryLockPasswordComponent,
|
||||
{
|
||||
data: {
|
||||
registryLockEmailAddress: this.user().registryLockEmailAddress,
|
||||
},
|
||||
}
|
||||
);
|
||||
dialogRef
|
||||
.afterClosed()
|
||||
.pipe(
|
||||
take(1),
|
||||
filter((result) => !!result)
|
||||
)
|
||||
.pipe(switchMap((_) => this.sendRegistryLockPasswordResetRequest()))
|
||||
.subscribe({
|
||||
next: (_) => this.usersService.currentlyOpenUserEmail.set(''),
|
||||
error: (err: HttpErrorResponse) =>
|
||||
this._snackBar.open(err.error || err.message),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface User {
|
||||
emailAddress: string;
|
||||
role: string;
|
||||
password?: string;
|
||||
registryLockEmailAddress?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
|
||||
@@ -62,7 +62,7 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
@Id @Expose String emailAddress;
|
||||
|
||||
/** Optional external email address to use for registry lock confirmation emails. */
|
||||
@Column String registryLockEmailAddress;
|
||||
@Column @Expose String registryLockEmailAddress;
|
||||
|
||||
/** Roles (which grant permissions) associated with this user. */
|
||||
@Expose
|
||||
@@ -250,51 +250,50 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<? extends User, ?> asBuilder() {
|
||||
return new Builder<>(clone(this));
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
/** Builder for constructing immutable {@link User} objects. */
|
||||
public static class Builder<T extends User, B extends Builder<T, B>>
|
||||
extends GenericBuilder<T, B> {
|
||||
public static class Builder extends Buildable.Builder<User> {
|
||||
|
||||
public Builder() {}
|
||||
|
||||
public Builder(T abstractUser) {
|
||||
super(abstractUser);
|
||||
public Builder(User user) {
|
||||
super(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T build() {
|
||||
public User build() {
|
||||
checkArgumentNotNull(getInstance().emailAddress, "Email address cannot be null");
|
||||
checkArgumentNotNull(getInstance().userRoles, "User roles cannot be null");
|
||||
return super.build();
|
||||
}
|
||||
|
||||
public B setEmailAddress(String emailAddress) {
|
||||
public Builder setEmailAddress(String emailAddress) {
|
||||
getInstance().emailAddress = checkValidEmail(emailAddress);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
|
||||
public Builder setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
|
||||
getInstance().registryLockEmailAddress =
|
||||
registryLockEmailAddress == null ? null : checkValidEmail(registryLockEmailAddress);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setUserRoles(UserRoles userRoles) {
|
||||
public Builder setUserRoles(UserRoles userRoles) {
|
||||
checkArgumentNotNull(userRoles, "User roles cannot be null");
|
||||
getInstance().userRoles = userRoles;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B removeRegistryLockPassword() {
|
||||
public Builder removeRegistryLockPassword() {
|
||||
getInstance().registryLockPasswordHash = null;
|
||||
getInstance().registryLockPasswordSalt = null;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistryLockPassword(String registryLockPassword) {
|
||||
public Builder setRegistryLockPassword(String registryLockPassword) {
|
||||
checkArgument(
|
||||
getInstance().hasAnyRegistryLockPermission(), "User has no registry lock permission");
|
||||
checkArgument(
|
||||
@@ -304,7 +303,7 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
byte[] salt = SALT_SUPPLIER.get();
|
||||
getInstance().registryLockPasswordSalt = base64().encode(salt);
|
||||
getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import static google.registry.util.CollectionUtils.forceEmptyToNull;
|
||||
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.model.Buildable.GenericBuilder;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.domain.Period;
|
||||
import google.registry.model.domain.fee.Fee;
|
||||
@@ -77,8 +77,7 @@ public class FeeCheckResponseExtensionItemCommandV12 extends ImmutableObject {
|
||||
}
|
||||
|
||||
/** Builder for {@link FeeCheckResponseExtensionItemCommandV12}. */
|
||||
public static class Builder
|
||||
extends GenericBuilder<FeeCheckResponseExtensionItemCommandV12, Builder> {
|
||||
public static class Builder extends Buildable.Builder<FeeCheckResponseExtensionItemCommandV12> {
|
||||
|
||||
public Builder setCommandName(CommandName commandName) {
|
||||
getInstance().commandName = Ascii.toLowerCase(commandName.name());
|
||||
|
||||
@@ -690,8 +690,8 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder<? extends Registrar, ?> asBuilder() {
|
||||
return new Builder<>(clone(this));
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -706,59 +706,58 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
}
|
||||
|
||||
/** A builder for constructing {@link Registrar}, since it is immutable. */
|
||||
public static class Builder<T extends Registrar, B extends Builder<T, B>>
|
||||
extends GenericBuilder<T, B> {
|
||||
public static class Builder extends Buildable.Builder<Registrar> {
|
||||
public Builder() {}
|
||||
|
||||
public Builder(T instance) {
|
||||
public Builder(Registrar instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
public B setRegistrarId(String registrarId) {
|
||||
public Builder setRegistrarId(String registrarId) {
|
||||
// Registrar id must be [3,16] chars long. See "clIDType" in the base EPP schema of RFC 5730.
|
||||
// (Need to validate this here as there's no matching EPP XSD for validation.)
|
||||
checkArgument(
|
||||
Range.closed(3, 16).contains(registrarId.length()),
|
||||
"Registrar ID must be 3-16 characters long.");
|
||||
getInstance().registrarId = registrarId;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setIanaIdentifier(@Nullable Long ianaIdentifier) {
|
||||
public Builder setIanaIdentifier(@Nullable Long ianaIdentifier) {
|
||||
checkArgument(
|
||||
ianaIdentifier == null || ianaIdentifier > 0, "IANA ID must be a positive number");
|
||||
getInstance().ianaIdentifier = ianaIdentifier;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setPoNumber(Optional<String> poNumber) {
|
||||
public Builder setPoNumber(Optional<String> poNumber) {
|
||||
getInstance().poNumber = poNumber.orElse(null);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setBillingAccountMap(@Nullable Map<CurrencyUnit, String> billingAccountMap) {
|
||||
public Builder setBillingAccountMap(@Nullable Map<CurrencyUnit, String> billingAccountMap) {
|
||||
getInstance().billingAccountMap = nullToEmptyImmutableCopy(billingAccountMap);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistrarName(String registrarName) {
|
||||
public Builder setRegistrarName(String registrarName) {
|
||||
getInstance().registrarName = registrarName;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setType(Type type) {
|
||||
public Builder setType(Type type) {
|
||||
getInstance().type = type;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setState(State state) {
|
||||
public Builder setState(State state) {
|
||||
getInstance().state = state;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setAllowedTlds(Set<String> allowedTlds) {
|
||||
public Builder setAllowedTlds(Set<String> allowedTlds) {
|
||||
getInstance().allowedTlds = ImmutableSortedSet.copyOf(assertTldsExist(allowedTlds));
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -771,7 +770,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* {@code .now()} when saving the Registry entity to make sure it's actually saved before trying
|
||||
* to set the allowed TLDs.
|
||||
*/
|
||||
public B setAllowedTldsUncached(Set<String> allowedTlds) {
|
||||
public Builder setAllowedTldsUncached(Set<String> allowedTlds) {
|
||||
ImmutableSet<VKey<Tld>> newTldKeys =
|
||||
Sets.difference(allowedTlds, getInstance().getAllowedTlds()).stream()
|
||||
.map(Tld::createVKey)
|
||||
@@ -780,10 +779,10 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
Sets.difference(newTldKeys, tm().loadByKeysIfPresent(newTldKeys).keySet());
|
||||
checkArgument(missingTldKeys.isEmpty(), "Trying to set nonexistent TLDs: %s", missingTldKeys);
|
||||
getInstance().allowedTlds = ImmutableSortedSet.copyOf(allowedTlds);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setClientCertificate(String clientCertificate, DateTime now) {
|
||||
public Builder setClientCertificate(String clientCertificate, DateTime now) {
|
||||
clientCertificate = emptyToNull(clientCertificate);
|
||||
String clientCertificateHash = calculateHash(clientCertificate);
|
||||
if (!Objects.equals(clientCertificate, getInstance().clientCertificate)
|
||||
@@ -792,23 +791,23 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
getInstance().clientCertificateHash = clientCertificateHash;
|
||||
getInstance().lastCertificateUpdateTime = now;
|
||||
}
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setLastExpiringCertNotificationSentDate(DateTime now) {
|
||||
public Builder setLastExpiringCertNotificationSentDate(DateTime now) {
|
||||
checkArgumentNotNull(now, "Registrar lastExpiringCertNotificationSentDate cannot be null");
|
||||
getInstance().lastExpiringCertNotificationSentDate = now;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setLastExpiringFailoverCertNotificationSentDate(DateTime now) {
|
||||
public Builder setLastExpiringFailoverCertNotificationSentDate(DateTime now) {
|
||||
checkArgumentNotNull(
|
||||
now, "Registrar lastExpiringFailoverCertNotificationSentDate cannot be null");
|
||||
getInstance().lastExpiringFailoverCertNotificationSentDate = now;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setFailoverClientCertificate(String clientCertificate, DateTime now) {
|
||||
public Builder setFailoverClientCertificate(String clientCertificate, DateTime now) {
|
||||
clientCertificate = emptyToNull(clientCertificate);
|
||||
String clientCertificateHash = calculateHash(clientCertificate);
|
||||
if (!Objects.equals(clientCertificate, getInstance().failoverClientCertificate)
|
||||
@@ -817,13 +816,13 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
getInstance().failoverClientCertificateHash = clientCertificateHash;
|
||||
getInstance().lastCertificateUpdateTime = now;
|
||||
}
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setLastPocVerificationDate(DateTime now) {
|
||||
public Builder setLastPocVerificationDate(DateTime now) {
|
||||
checkArgumentNotNull(now, "Registrar lastPocVerificationDate cannot be null");
|
||||
getInstance().lastPocVerificationDate = now;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
private static String calculateHash(String clientCertificate) {
|
||||
@@ -855,75 +854,75 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
Objects.equals(newInstance.ianaIdentifier, registrar.getIanaIdentifier()));
|
||||
}
|
||||
|
||||
public B setContactsRequireSyncing(boolean contactsRequireSyncing) {
|
||||
public Builder setContactsRequireSyncing(boolean contactsRequireSyncing) {
|
||||
getInstance().contactsRequireSyncing = contactsRequireSyncing;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setIpAddressAllowList(Iterable<CidrAddressBlock> ipAddressAllowList) {
|
||||
public Builder setIpAddressAllowList(Iterable<CidrAddressBlock> ipAddressAllowList) {
|
||||
getInstance().ipAddressAllowList = ImmutableList.copyOf(ipAddressAllowList);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setLocalizedAddress(RegistrarAddress localizedAddress) {
|
||||
public Builder setLocalizedAddress(RegistrarAddress localizedAddress) {
|
||||
getInstance().localizedAddress = localizedAddress;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setInternationalizedAddress(RegistrarAddress internationalizedAddress) {
|
||||
public Builder setInternationalizedAddress(RegistrarAddress internationalizedAddress) {
|
||||
getInstance().internationalizedAddress = internationalizedAddress;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setPhoneNumber(String phoneNumber) {
|
||||
public Builder setPhoneNumber(String phoneNumber) {
|
||||
getInstance().phoneNumber = (phoneNumber == null) ? null : checkValidPhoneNumber(phoneNumber);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setFaxNumber(String faxNumber) {
|
||||
public Builder setFaxNumber(String faxNumber) {
|
||||
getInstance().faxNumber = (faxNumber == null) ? null : checkValidPhoneNumber(faxNumber);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setEmailAddress(String emailAddress) {
|
||||
public Builder setEmailAddress(String emailAddress) {
|
||||
getInstance().emailAddress = checkValidEmail(emailAddress);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setWhoisServer(String whoisServer) {
|
||||
public Builder setWhoisServer(String whoisServer) {
|
||||
getInstance().whoisServer = whoisServer;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRdapBaseUrls(Set<String> rdapBaseUrls) {
|
||||
public Builder setRdapBaseUrls(Set<String> rdapBaseUrls) {
|
||||
getInstance().rdapBaseUrls = ImmutableSet.copyOf(rdapBaseUrls);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setBlockPremiumNames(boolean blockPremiumNames) {
|
||||
public Builder setBlockPremiumNames(boolean blockPremiumNames) {
|
||||
getInstance().blockPremiumNames = blockPremiumNames;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setUrl(String url) {
|
||||
public Builder setUrl(String url) {
|
||||
getInstance().url = url;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setIcannReferralEmail(String icannReferralEmail) {
|
||||
public Builder setIcannReferralEmail(String icannReferralEmail) {
|
||||
getInstance().icannReferralEmail = checkValidEmail(icannReferralEmail);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setDriveFolderId(@Nullable String driveFolderId) {
|
||||
public Builder setDriveFolderId(@Nullable String driveFolderId) {
|
||||
checkArgument(
|
||||
driveFolderId == null || !driveFolderId.contains("/"),
|
||||
"Drive folder ID must not be a full URL");
|
||||
getInstance().driveFolderId = driveFolderId;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setPassword(String password) {
|
||||
public Builder setPassword(String password) {
|
||||
// Passwords must be [6,16] chars long. See "pwType" in the base EPP schema of RFC 5730.
|
||||
checkArgument(
|
||||
Range.closed(6, 16).contains(nullToEmpty(password).length()),
|
||||
@@ -931,7 +930,7 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
byte[] salt = SALT_SUPPLIER.get();
|
||||
getInstance().salt = base64().encode(salt);
|
||||
getInstance().passwordHash = hashPassword(password, salt);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -939,18 +938,18 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
*
|
||||
* @throws IllegalArgumentException if provided passcode is not 5-digit numeric
|
||||
*/
|
||||
public B setPhonePasscode(String phonePasscode) {
|
||||
public Builder setPhonePasscode(String phonePasscode) {
|
||||
checkArgument(
|
||||
phonePasscode == null || PHONE_PASSCODE_PATTERN.matcher(phonePasscode).matches(),
|
||||
"Not a valid telephone passcode (must be 5 digits long): %s",
|
||||
phonePasscode);
|
||||
getInstance().phonePasscode = phonePasscode;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistryLockAllowed(boolean registryLockAllowed) {
|
||||
public Builder setRegistryLockAllowed(boolean registryLockAllowed) {
|
||||
getInstance().registryLockAllowed = registryLockAllowed;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -958,14 +957,14 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
* and breaks the verification that an object has not been updated since it was copied.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public B setLastUpdateTime(DateTime timestamp) {
|
||||
public Builder setLastUpdateTime(DateTime timestamp) {
|
||||
getInstance().setUpdateTimestamp(UpdateAutoTimestamp.create(timestamp));
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Build the registrar, nullifying empty fields. */
|
||||
@Override
|
||||
public T build() {
|
||||
public Registrar build() {
|
||||
checkArgumentNotNull(getInstance().type, "Registrar type cannot be null");
|
||||
checkArgumentNotNull(getInstance().registrarName, "Registrar name cannot be null");
|
||||
checkArgument(
|
||||
|
||||
@@ -31,7 +31,7 @@ import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedSet;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.model.Buildable.GenericBuilder;
|
||||
import google.registry.model.Buildable;
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.JsonMapBuilder;
|
||||
import google.registry.model.Jsonifiable;
|
||||
@@ -225,8 +225,8 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
return visibleInDomainWhoisAsAbuse;
|
||||
}
|
||||
|
||||
public Builder<? extends RegistrarPoc, ?> asBuilder() {
|
||||
return new Builder<>(clone(this));
|
||||
public Builder asBuilder() {
|
||||
return new Builder(clone(this));
|
||||
}
|
||||
|
||||
public boolean isAllowedToSetRegistryLockPassword() {
|
||||
@@ -332,17 +332,16 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
}
|
||||
|
||||
/** A builder for constructing a {@link RegistrarPoc}, since it is immutable. */
|
||||
public static class Builder<T extends RegistrarPoc, B extends Builder<T, B>>
|
||||
extends GenericBuilder<T, B> {
|
||||
public static class Builder extends Buildable.Builder<RegistrarPoc> {
|
||||
public Builder() {}
|
||||
|
||||
protected Builder(T instance) {
|
||||
protected Builder(RegistrarPoc instance) {
|
||||
super(instance);
|
||||
}
|
||||
|
||||
/** Build the registrar, nullifying empty fields. */
|
||||
@Override
|
||||
public T build() {
|
||||
public RegistrarPoc build() {
|
||||
checkNotNull(getInstance().registrarId, "Registrar ID cannot be null");
|
||||
checkValidEmail(getInstance().emailAddress);
|
||||
// Check allowedToSetRegistryLockPassword here because if we want to allow the user to set
|
||||
@@ -356,71 +355,71 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
return cloneEmptyToNull(super.build());
|
||||
}
|
||||
|
||||
public B setName(String name) {
|
||||
public Builder setName(String name) {
|
||||
getInstance().name = name;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setEmailAddress(String emailAddress) {
|
||||
public Builder setEmailAddress(String emailAddress) {
|
||||
getInstance().emailAddress = emailAddress;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
|
||||
public Builder setRegistryLockEmailAddress(@Nullable String registryLockEmailAddress) {
|
||||
getInstance().registryLockEmailAddress = registryLockEmailAddress;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setPhoneNumber(String phoneNumber) {
|
||||
public Builder setPhoneNumber(String phoneNumber) {
|
||||
getInstance().phoneNumber = phoneNumber;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistrarId(String registrarId) {
|
||||
public Builder setRegistrarId(String registrarId) {
|
||||
getInstance().registrarId = registrarId;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistrar(Registrar registrar) {
|
||||
public Builder setRegistrar(Registrar registrar) {
|
||||
getInstance().registrarId = registrar.getRegistrarId();
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setFaxNumber(String faxNumber) {
|
||||
public Builder setFaxNumber(String faxNumber) {
|
||||
getInstance().faxNumber = faxNumber;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setTypes(Iterable<Type> types) {
|
||||
public Builder setTypes(Iterable<Type> types) {
|
||||
getInstance().types = ImmutableSet.copyOf(types);
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setVisibleInWhoisAsAdmin(boolean visible) {
|
||||
public Builder setVisibleInWhoisAsAdmin(boolean visible) {
|
||||
getInstance().visibleInWhoisAsAdmin = visible;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setVisibleInWhoisAsTech(boolean visible) {
|
||||
public Builder setVisibleInWhoisAsTech(boolean visible) {
|
||||
getInstance().visibleInWhoisAsTech = visible;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setVisibleInDomainWhoisAsAbuse(boolean visible) {
|
||||
public Builder setVisibleInDomainWhoisAsAbuse(boolean visible) {
|
||||
getInstance().visibleInDomainWhoisAsAbuse = visible;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setAllowedToSetRegistryLockPassword(boolean allowedToSetRegistryLockPassword) {
|
||||
public Builder setAllowedToSetRegistryLockPassword(boolean allowedToSetRegistryLockPassword) {
|
||||
if (allowedToSetRegistryLockPassword) {
|
||||
getInstance().registryLockPasswordSalt = null;
|
||||
getInstance().registryLockPasswordHash = null;
|
||||
}
|
||||
getInstance().allowedToSetRegistryLockPassword = allowedToSetRegistryLockPassword;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
|
||||
public B setRegistryLockPassword(String registryLockPassword) {
|
||||
public Builder setRegistryLockPassword(String registryLockPassword) {
|
||||
checkArgument(
|
||||
getInstance().allowedToSetRegistryLockPassword,
|
||||
"Not allowed to set registry lock password for this contact");
|
||||
@@ -430,7 +429,7 @@ public class RegistrarPoc extends ImmutableObject implements Jsonifiable, Unsafe
|
||||
getInstance().registryLockPasswordSalt = base64().encode(salt);
|
||||
getInstance().registryLockPasswordHash = hashPassword(registryLockPassword, salt);
|
||||
getInstance().allowedToSetRegistryLockPassword = false;
|
||||
return thisCastToDerived();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
u ->
|
||||
new UserData(
|
||||
u.getEmailAddress(),
|
||||
u.getRegistryLockEmailAddress().orElse(null),
|
||||
u.getUserRoles().getRegistrarRoles().get(registrarId).toString(),
|
||||
null))
|
||||
.collect(Collectors.toList());
|
||||
@@ -237,7 +238,9 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
.setPayload(
|
||||
consoleApiParams
|
||||
.gson()
|
||||
.toJson(new UserData(newEmail, ACCOUNT_MANAGER.toString(), newUser.getPassword())));
|
||||
.toJson(
|
||||
new UserData(
|
||||
newEmail, null, ACCOUNT_MANAGER.toString(), newUser.getPassword())));
|
||||
finishAndPersistConsoleUpdateHistory(
|
||||
new ConsoleUpdateHistory.Builder()
|
||||
.setType(ConsoleUpdateHistory.Type.USER_CREATE)
|
||||
@@ -345,5 +348,8 @@ public class ConsoleUsersAction extends ConsoleApiAction {
|
||||
}
|
||||
|
||||
public record UserData(
|
||||
@Expose String emailAddress, @Expose String role, @Expose @Nullable String password) {}
|
||||
@Expose String emailAddress,
|
||||
@Expose String registryLockEmailAddress,
|
||||
@Expose String role,
|
||||
@Expose @Nullable String password) {}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ class ConsoleUsersActionTest extends ConsoleActionBaseTestCase {
|
||||
createAction(
|
||||
Optional.of(ConsoleApiParamsUtils.createFake(authResult)),
|
||||
Optional.of("POST"),
|
||||
Optional.of(new UserData("a@d", RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
Optional.of(new UserData("a@d", null, RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
when(directory.users()).thenReturn(users);
|
||||
when(users.insert(any(com.google.api.services.directory.model.User.class))).thenReturn(insert);
|
||||
@@ -170,7 +170,7 @@ class ConsoleUsersActionTest extends ConsoleActionBaseTestCase {
|
||||
createAction(
|
||||
Optional.of(ConsoleApiParamsUtils.createFake(authResult)),
|
||||
Optional.of("POST"),
|
||||
Optional.of(new UserData("lol", RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
Optional.of(new UserData("lol", null, RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
when(directory.users()).thenReturn(users);
|
||||
when(users.insert(any(com.google.api.services.directory.model.User.class))).thenReturn(insert);
|
||||
@@ -195,7 +195,8 @@ class ConsoleUsersActionTest extends ConsoleActionBaseTestCase {
|
||||
Optional.of(ConsoleApiParamsUtils.createFake(authResult)),
|
||||
Optional.of("DELETE"),
|
||||
Optional.of(
|
||||
new UserData("test3@test.com", RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
new UserData(
|
||||
"test3@test.com", null, RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
when(directory.users()).thenReturn(users);
|
||||
when(users.delete(any(String.class))).thenReturn(delete);
|
||||
action.run();
|
||||
@@ -213,7 +214,8 @@ class ConsoleUsersActionTest extends ConsoleActionBaseTestCase {
|
||||
Optional.of(ConsoleApiParamsUtils.createFake(authResult)),
|
||||
Optional.of("DELETE"),
|
||||
Optional.of(
|
||||
new UserData("email-1@email.com", RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
new UserData(
|
||||
"email-1@email.com", null, RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
when(directory.users()).thenReturn(users);
|
||||
when(users.delete(any(String.class))).thenReturn(delete);
|
||||
action.run();
|
||||
@@ -235,7 +237,8 @@ class ConsoleUsersActionTest extends ConsoleActionBaseTestCase {
|
||||
Optional.of(ConsoleApiParamsUtils.createFake(authResult)),
|
||||
Optional.of("DELETE"),
|
||||
Optional.of(
|
||||
new UserData("test2@test.com", RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
new UserData(
|
||||
"test2@test.com", null, RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
when(directory.users()).thenReturn(users);
|
||||
when(users.delete(any(String.class))).thenReturn(delete);
|
||||
@@ -274,7 +277,8 @@ class ConsoleUsersActionTest extends ConsoleActionBaseTestCase {
|
||||
Optional.of(ConsoleApiParamsUtils.createFake(authResult)),
|
||||
Optional.of("DELETE"),
|
||||
Optional.of(
|
||||
new UserData("test4@test.com", RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
new UserData(
|
||||
"test4@test.com", null, RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
when(directory.users()).thenReturn(users);
|
||||
@@ -318,7 +322,8 @@ class ConsoleUsersActionTest extends ConsoleActionBaseTestCase {
|
||||
Optional.of(ConsoleApiParamsUtils.createFake(authResult)),
|
||||
Optional.of("POST"),
|
||||
Optional.of(
|
||||
new UserData("test3@test.com", RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
new UserData(
|
||||
"test3@test.com", null, RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
when(directory.users()).thenReturn(users);
|
||||
when(users.insert(any(com.google.api.services.directory.model.User.class))).thenReturn(insert);
|
||||
@@ -348,7 +353,8 @@ class ConsoleUsersActionTest extends ConsoleActionBaseTestCase {
|
||||
Optional.of(ConsoleApiParamsUtils.createFake(authResult)),
|
||||
Optional.of("PUT"),
|
||||
Optional.of(
|
||||
new UserData("test2@test.com", RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
new UserData(
|
||||
"test2@test.com", null, RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
@@ -374,7 +380,8 @@ class ConsoleUsersActionTest extends ConsoleActionBaseTestCase {
|
||||
Optional.of(ConsoleApiParamsUtils.createFake(authResult)),
|
||||
Optional.of("PUT"),
|
||||
Optional.of(
|
||||
new UserData("test3@test.com", RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
new UserData(
|
||||
"test3@test.com", null, RegistrarRole.ACCOUNT_MANAGER.toString(), null)));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_FORBIDDEN);
|
||||
assertThat(response.getPayload())
|
||||
|
||||
Reference in New Issue
Block a user