diff --git a/console-webapp/src/app/app-routing.module.ts b/console-webapp/src/app/app-routing.module.ts index 3b0265666..d14bc2cd5 100644 --- a/console-webapp/src/app/app-routing.module.ts +++ b/console-webapp/src/app/app-routing.module.ts @@ -26,6 +26,7 @@ import { SettingsComponent } from './settings/settings.component'; import UsersComponent from './settings/users/users.component'; import WhoisComponent from './settings/whois/whois.component'; import { SupportComponent } from './support/support.component'; +import { RegistryLockVerifyComponent } from './lock/registryLockVerify.component'; export interface RouteWithIcon extends Route { iconName?: string; @@ -33,6 +34,10 @@ export interface RouteWithIcon extends Route { export const routes: RouteWithIcon[] = [ { path: '', redirectTo: '/home', pathMatch: 'full' }, + { + path: RegistryLockVerifyComponent.PATH, + component: RegistryLockVerifyComponent, + }, { path: 'registrars', component: RegistrarComponent }, { path: 'home', diff --git a/console-webapp/src/app/app.module.ts b/console-webapp/src/app/app.module.ts index 50776868b..40ddeeee7 100644 --- a/console-webapp/src/app/app.module.ts +++ b/console-webapp/src/app/app.module.ts @@ -53,6 +53,7 @@ import { UserDataService } from './shared/services/userData.service'; import { SnackBarModule } from './snackbar.module'; import { SupportComponent } from './support/support.component'; import { TldsComponent } from './tlds/tlds.component'; +import { RegistryLockVerifyComponent } from './lock/registryLockVerify.component'; @NgModule({ declarations: [ @@ -71,6 +72,7 @@ import { TldsComponent } from './tlds/tlds.component'; RegistrarComponent, RegistrarDetailsComponent, RegistrarSelectorComponent, + RegistryLockVerifyComponent, ResourcesComponent, SecurityComponent, SecurityEditComponent, diff --git a/console-webapp/src/app/lock/registryLockVerify.component.html b/console-webapp/src/app/lock/registryLockVerify.component.html new file mode 100644 index 000000000..dcad70b0e --- /dev/null +++ b/console-webapp/src/app/lock/registryLockVerify.component.html @@ -0,0 +1,28 @@ +@if (isLoading) { +
+ +
+} @else if (domainName) { +

Success!

+
+
+ The domain {{ domainName }} has been successfully {{ action }}ed. +
+
+
+ Return to the list of domains +
+} @else { +

Failure

+
+
+ An error occurred: {{ errorMessage }}.

Please double-check the + verification code and try again. +
+
+} diff --git a/console-webapp/src/app/lock/registryLockVerify.component.scss b/console-webapp/src/app/lock/registryLockVerify.component.scss new file mode 100644 index 000000000..064823060 --- /dev/null +++ b/console-webapp/src/app/lock/registryLockVerify.component.scss @@ -0,0 +1,9 @@ +.console-app__registry-lock { + &-content { + margin-top: 30px; + } + &-subhead { + font-size: 20px; + margin-bottom: 20px; + } +} diff --git a/console-webapp/src/app/lock/registryLockVerify.component.ts b/console-webapp/src/app/lock/registryLockVerify.component.ts new file mode 100644 index 000000000..192383897 --- /dev/null +++ b/console-webapp/src/app/lock/registryLockVerify.component.ts @@ -0,0 +1,65 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component } from '@angular/core'; +import { RegistrarService } from '../registrar/registrar.service'; +import { ActivatedRoute, ParamMap } from '@angular/router'; +import { RegistryLockVerifyService } from './registryLockVerify.service'; +import { HttpErrorResponse } from '@angular/common/http'; +import { take } from 'rxjs'; +import { DomainListComponent } from '../domains/domainList.component'; + +@Component({ + selector: 'app-registry-lock-verify', + templateUrl: './registryLockVerify.component.html', + styleUrls: ['./registryLockVerify.component.scss'], + providers: [RegistryLockVerifyService], +}) +export class RegistryLockVerifyComponent { + public static PATH = 'registry-lock-verify'; + + readonly DOMAIN_LIST_COMPONENT_PATH = `/${DomainListComponent.PATH}`; + + isLoading = true; + domainName?: string; + action?: string; + errorMessage?: string; + + constructor( + protected registrarService: RegistrarService, + protected registryLockVerifyService: RegistryLockVerifyService, + private route: ActivatedRoute + ) {} + + ngOnInit() { + this.route.queryParamMap.pipe(take(1)).subscribe((params: ParamMap) => { + this.registryLockVerifyService + .verifyRequest(params.get('lockVerificationCode') || '') + .subscribe({ + error: (err: HttpErrorResponse) => { + this.isLoading = false; + this.errorMessage = err.error; + }, + next: (verificationResponse) => { + this.domainName = verificationResponse.domainName; + this.action = verificationResponse.action; + this.registrarService.registrarId.set( + verificationResponse.registrarId + ); + this.isLoading = false; + }, + }); + }); + } +} diff --git a/console-webapp/src/app/lock/registryLockVerify.service.ts b/console-webapp/src/app/lock/registryLockVerify.service.ts new file mode 100644 index 000000000..3b34258de --- /dev/null +++ b/console-webapp/src/app/lock/registryLockVerify.service.ts @@ -0,0 +1,31 @@ +// Copyright 2024 The Nomulus Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable } from '@angular/core'; +import { BackendService } from '../shared/services/backend.service'; + +export interface RegistryLockVerificationResponse { + action: string; + domainName: string; + registrarId: string; +} + +@Injectable() +export class RegistryLockVerifyService { + constructor(private backendService: BackendService) {} + + verifyRequest(lockVerificationCode: string) { + return this.backendService.verifyRegistryLockRequest(lockVerificationCode); + } +} diff --git a/console-webapp/src/app/shared/services/backend.service.ts b/console-webapp/src/app/shared/services/backend.service.ts index 698cbbb04..26bbeb895 100644 --- a/console-webapp/src/app/shared/services/backend.service.ts +++ b/console-webapp/src/app/shared/services/backend.service.ts @@ -25,6 +25,7 @@ import { import { Contact } from '../../settings/contact/contact.service'; import { EppPasswordBackendModel } from '../../settings/security/security.service'; import { UserData } from './userData.service'; +import { RegistryLockVerificationResponse } from 'src/app/lock/registryLockVerify.service'; @Injectable() export class BackendService { @@ -169,4 +170,12 @@ export class BackendService { whoisRegistrarFields ); } + + verifyRegistryLockRequest( + lockVerificationCode: string + ): Observable { + return this.http.get( + `/console-api/registry-lock-verify?lockVerificationCode=${lockVerificationCode}` + ); + } } diff --git a/core/src/main/java/google/registry/tools/DomainLockUtils.java b/core/src/main/java/google/registry/tools/DomainLockUtils.java index 6c34ac94c..76ddff8db 100644 --- a/core/src/main/java/google/registry/tools/DomainLockUtils.java +++ b/core/src/main/java/google/registry/tools/DomainLockUtils.java @@ -343,7 +343,7 @@ public final class DomainLockUtils { .orElseThrow( () -> new IllegalArgumentException( - String.format("Invalid verification code %s", verificationCode))); + String.format("Invalid verification code \"%s\"", verificationCode))); } private void applyLockStatuses(RegistryLock lock, DateTime lockTime, boolean isAdmin) { diff --git a/core/src/main/java/google/registry/ui/server/console/ConsoleApiAction.java b/core/src/main/java/google/registry/ui/server/console/ConsoleApiAction.java index d4d3a3bc6..53d2725d6 100644 --- a/core/src/main/java/google/registry/ui/server/console/ConsoleApiAction.java +++ b/core/src/main/java/google/registry/ui/server/console/ConsoleApiAction.java @@ -161,7 +161,7 @@ public abstract class ConsoleApiAction implements Runnable { Map registrarDiffMap = registrar.toDiffableFieldMap(); Stream.of("passwordHash", "salt") // fields to remove from final diff - .forEach(fieldToBeRemoved -> registrarDiffMap.remove(fieldToBeRemoved)); + .forEach(registrarDiffMap::remove); // Use LinkedHashMap here to preserve ordering; null values mean we can't use ImmutableMap. LinkedHashMap result = new LinkedHashMap<>(registrarDiffMap); diff --git a/core/src/test/java/google/registry/ui/server/console/ConsoleRegistryLockVerifyActionTest.java b/core/src/test/java/google/registry/ui/server/console/ConsoleRegistryLockVerifyActionTest.java index 8ff207bb7..803083731 100644 --- a/core/src/test/java/google/registry/ui/server/console/ConsoleRegistryLockVerifyActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/ConsoleRegistryLockVerifyActionTest.java @@ -156,7 +156,7 @@ public class ConsoleRegistryLockVerifyActionTest { action.run(); assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST); assertThat(response.getPayload()) - .isEqualTo("Invalid verification code 123456789ABCDEFGHJKLMNPQRSTUUUUU"); + .isEqualTo("Invalid verification code \"123456789ABCDEFGHJKLMNPQRSTUUUUU\""); assertThat(loadByEntity(defaultDomain).getStatusValues()).containsExactly(StatusValue.INACTIVE); } diff --git a/core/src/test/resources/google/registry/webdriver/goldens/chrome-linux/RegistrarConsoleScreenshotTest_registryLockVerify_unknownLock_page.png b/core/src/test/resources/google/registry/webdriver/goldens/chrome-linux/RegistrarConsoleScreenshotTest_registryLockVerify_unknownLock_page.png index 765127351..725c4249c 100644 Binary files a/core/src/test/resources/google/registry/webdriver/goldens/chrome-linux/RegistrarConsoleScreenshotTest_registryLockVerify_unknownLock_page.png and b/core/src/test/resources/google/registry/webdriver/goldens/chrome-linux/RegistrarConsoleScreenshotTest_registryLockVerify_unknownLock_page.png differ