mirror of
https://github.com/google/nomulus
synced 2026-07-09 17:46:52 +00:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2d1260c01b | |||
| 06da6a2cc6 | |||
| 858a22f82e | |||
| 3c126ddfd4 | |||
| 2b98e6f177 | |||
| 20036b6a74 | |||
| 396cbd6bd3 | |||
| 71ea16ff69 | |||
| 45331be166 | |||
| beb7c14adb | |||
| d33571dde3 | |||
| 53a7d1b66c | |||
| fa721e82ff | |||
| d4faa77ee4 | |||
| 96d3d88c2f | |||
| 213e06f02e | |||
| d5445dd049 | |||
| af5adcb0ba | |||
| ca238a8578 | |||
| 1a8f133d54 | |||
| 233ee09efe | |||
| 35ff768176 | |||
| c4e5bc913e | |||
| 0241937dee | |||
| 68b46735cd | |||
| bfeaf4a23e |
@@ -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',
|
||||
|
||||
@@ -27,6 +27,7 @@ import { provideHttpClient } from '@angular/common/http';
|
||||
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
|
||||
import { BillingInfoComponent } from './billingInfo/billingInfo.component';
|
||||
import { DomainListComponent } from './domains/domainList.component';
|
||||
import { RegistryLockComponent } from './domains/registryLock.component';
|
||||
import { HeaderComponent } from './header/header.component';
|
||||
import { HomeComponent } from './home/home.component';
|
||||
import { NavigationComponent } from './navigation/navigation.component';
|
||||
@@ -46,12 +47,14 @@ import WhoisEditComponent from './settings/whois/whoisEdit.component';
|
||||
import { NotificationsComponent } from './shared/components/notifications/notifications.component';
|
||||
import { SelectedRegistrarWrapper } from './shared/components/selectedRegistrarWrapper/selectedRegistrarWrapper.component';
|
||||
import { LocationBackDirective } from './shared/directives/locationBack.directive';
|
||||
import { UserLevelVisibility } from './shared/directives/userLevelVisiblity.directive';
|
||||
import { BreakPointObserverService } from './shared/services/breakPoint.service';
|
||||
import { GlobalLoaderService } from './shared/services/globalLoader.service';
|
||||
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: [
|
||||
@@ -63,12 +66,15 @@ import { TldsComponent } from './tlds/tlds.component';
|
||||
HeaderComponent,
|
||||
HomeComponent,
|
||||
LocationBackDirective,
|
||||
UserLevelVisibility,
|
||||
NavigationComponent,
|
||||
NewRegistrarComponent,
|
||||
NotificationsComponent,
|
||||
RegistrarComponent,
|
||||
RegistrarDetailsComponent,
|
||||
RegistryLockComponent,
|
||||
RegistrarSelectorComponent,
|
||||
RegistryLockVerifyComponent,
|
||||
ResourcesComponent,
|
||||
SecurityComponent,
|
||||
SecurityEditComponent,
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
<div class="console-app-domains">
|
||||
<h1 class="mat-headline-4">Domains</h1>
|
||||
|
||||
<div
|
||||
class="console-app-domains__actions-wrapper"
|
||||
[hidden]="!domainListService.activeActionComponent"
|
||||
>
|
||||
<ng-container
|
||||
v-if="domainListService.activeActionComponent"
|
||||
*ngComponentOutlet="domainListService.activeActionComponent"
|
||||
>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
@if (!isLoading && totalResults == 0) {
|
||||
<div class="console-app__empty-domains">
|
||||
<h1>
|
||||
@@ -12,14 +23,25 @@
|
||||
<h1>No domains found</h1>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="console-app__domains-table-parent">
|
||||
<mat-menu #actions="matMenu">
|
||||
<ng-template matMenuContent let-domainName="domainName">
|
||||
<button mat-menu-item (click)="openRegistryLock(domainName)">
|
||||
<mat-icon>key</mat-icon>
|
||||
<span>Registry Lock</span>
|
||||
</button>
|
||||
</ng-template>
|
||||
</mat-menu>
|
||||
<div
|
||||
class="console-app__domains-table-parent"
|
||||
[hidden]="domainListService.activeActionComponent"
|
||||
>
|
||||
<div class="console-app__scrollable-wrapper">
|
||||
<div class="console-app__scrollable">
|
||||
@if (isLoading) {
|
||||
<div class="console-app__domains-spinner">
|
||||
<mat-spinner />
|
||||
</div>
|
||||
} @else {
|
||||
}
|
||||
<a
|
||||
mat-stroked-button
|
||||
color="primary"
|
||||
@@ -39,11 +61,10 @@
|
||||
matInput
|
||||
[(ngModel)]="searchTerm"
|
||||
(ngModelChange)="sendInput()"
|
||||
[disabled]="isLoading"
|
||||
#input
|
||||
/>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
<mat-table
|
||||
[dataSource]="dataSource"
|
||||
class="mat-elevation-z0"
|
||||
@@ -79,6 +100,29 @@
|
||||
}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="registryLock">
|
||||
<mat-header-cell *matHeaderCellDef
|
||||
>Registry-Locked</mat-header-cell
|
||||
>
|
||||
<mat-cell *matCellDef="let element">{{
|
||||
isDomainLocked(element.domainName)
|
||||
}}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="actions">
|
||||
<mat-header-cell *matHeaderCellDef>Actions</mat-header-cell>
|
||||
<mat-cell *matCellDef="let element">
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="actions"
|
||||
[matMenuTriggerData]="{ domainName: element.domainName }"
|
||||
aria-label="Domain actions"
|
||||
>
|
||||
<mat-icon>more_horiz</mat-icon>
|
||||
</button>
|
||||
</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<mat-header-row
|
||||
*matHeaderRowDef="displayedColumns"
|
||||
></mat-header-row>
|
||||
@@ -86,7 +130,7 @@
|
||||
|
||||
<!-- Row shown when there is no matching data. -->
|
||||
<mat-row *matNoDataRow>
|
||||
<mat-cell colspan="4">No domains found</mat-cell>
|
||||
<mat-cell colspan="6">No domains found</mat-cell>
|
||||
</mat-row>
|
||||
</mat-table>
|
||||
<mat-paginator
|
||||
|
||||
@@ -30,6 +30,12 @@
|
||||
|
||||
&__domains-table {
|
||||
min-width: $min-width !important;
|
||||
.mat-column-actions {
|
||||
max-width: 100px;
|
||||
}
|
||||
.mat-column-registryLock {
|
||||
max-width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
&__domains-spinner {
|
||||
@@ -40,6 +46,7 @@
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.mat-mdc-paginator {
|
||||
|
||||
@@ -19,14 +19,14 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Subject, debounceTime } from 'rxjs';
|
||||
import { RegistrarService } from '../registrar/registrar.service';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
import { Domain, DomainListService } from './domainList.service';
|
||||
import { RegistryLockComponent } from './registryLock.component';
|
||||
import { RegistryLockService } from './registryLock.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-domain-list',
|
||||
templateUrl: './domainList.component.html',
|
||||
styleUrls: ['./domainList.component.scss'],
|
||||
providers: [DomainListService],
|
||||
})
|
||||
export class DomainListComponent {
|
||||
public static PATH = 'domain-list';
|
||||
@@ -37,6 +37,8 @@ export class DomainListComponent {
|
||||
'creationTime',
|
||||
'registrationExpirationTime',
|
||||
'statuses',
|
||||
'registryLock',
|
||||
'actions',
|
||||
];
|
||||
|
||||
dataSource: MatTableDataSource<Domain> = new MatTableDataSource();
|
||||
@@ -52,15 +54,16 @@ export class DomainListComponent {
|
||||
@ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;
|
||||
|
||||
constructor(
|
||||
private backendService: BackendService,
|
||||
private domainListService: DomainListService,
|
||||
protected domainListService: DomainListService,
|
||||
protected registrarService: RegistrarService,
|
||||
protected registryLockService: RegistryLockService,
|
||||
private _snackBar: MatSnackBar
|
||||
) {
|
||||
effect(() => {
|
||||
this.pageNumber = 0;
|
||||
this.totalResults = 0;
|
||||
if (this.registrarService.registrarId()) {
|
||||
this.loadLocks();
|
||||
this.reloadData();
|
||||
}
|
||||
});
|
||||
@@ -80,6 +83,25 @@ export class DomainListComponent {
|
||||
this.searchTermSubject.complete();
|
||||
}
|
||||
|
||||
openRegistryLock(domainName: string) {
|
||||
this.domainListService.selectedDomain = domainName;
|
||||
this.domainListService.activeActionComponent = RegistryLockComponent;
|
||||
}
|
||||
|
||||
loadLocks() {
|
||||
this.registryLockService.retrieveLocks().subscribe({
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.message);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
isDomainLocked(domainName: string) {
|
||||
return this.registryLockService.domainsLocks.some(
|
||||
(d) => d.domainName === domainName
|
||||
);
|
||||
}
|
||||
|
||||
reloadData() {
|
||||
this.isLoading = true;
|
||||
this.domainListService
|
||||
@@ -95,7 +117,7 @@ export class DomainListComponent {
|
||||
this.isLoading = false;
|
||||
},
|
||||
next: (domainListResult) => {
|
||||
this.dataSource.data = (domainListResult || {}).domains;
|
||||
this.dataSource.data = this.domainListService.domainsList;
|
||||
this.totalResults = (domainListResult || {}).totalResults || 0;
|
||||
this.isLoading = false;
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, Type } from '@angular/core';
|
||||
import { tap } from 'rxjs';
|
||||
import { RegistrarService } from '../registrar/registrar.service';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
@@ -35,9 +35,14 @@ export interface DomainListResult {
|
||||
totalResults: number;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class DomainListService {
|
||||
checkpointTime?: string;
|
||||
selectedDomain?: string;
|
||||
public activeActionComponent: Type<any> | null = null;
|
||||
public domainsList: Domain[] = [];
|
||||
|
||||
constructor(
|
||||
private backendService: BackendService,
|
||||
@@ -62,6 +67,7 @@ export class DomainListService {
|
||||
.pipe(
|
||||
tap((domainListResult: DomainListResult) => {
|
||||
this.checkpointTime = domainListResult?.checkpointTime;
|
||||
this.domainsList = domainListResult?.domains;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
<div class="console-app__registry-lock">
|
||||
<p>
|
||||
<button
|
||||
mat-icon-button
|
||||
aria-label="Back to domains list"
|
||||
(click)="goBack()"
|
||||
>
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
</p>
|
||||
|
||||
@if(!registrarService.registrar()?.registryLockAllowed) {
|
||||
<h1>
|
||||
Sorry, your registrar hasn't enrolled in registry lock yet. To do so, please
|
||||
contact {{ userDataService.userData()?.supportEmail }}.
|
||||
</h1>
|
||||
} @else if (isLocked()) {
|
||||
<h1>Unlock the domain {{ domainListService.selectedDomain }}</h1>
|
||||
<form (ngSubmit)="save(false)" [formGroup]="unlockDomain">
|
||||
<p>
|
||||
<mat-label for="password">Password: </mat-label>
|
||||
<mat-form-field name="password" appearance="outline">
|
||||
<input matInput type="text" formControlName="password" required />
|
||||
</mat-form-field>
|
||||
</p>
|
||||
<p>
|
||||
<mat-label for="relockTime"
|
||||
>Automatically re-lock the domain after:</mat-label
|
||||
>
|
||||
<mat-radio-group
|
||||
name="relockTime"
|
||||
formControlName="relockTime"
|
||||
aria-label="Automatically relock option"
|
||||
>
|
||||
@for (option of relockOptions; track option.name) {
|
||||
<mat-radio-button [value]="option.duration">{{
|
||||
option.name
|
||||
}}</mat-radio-button>
|
||||
}
|
||||
</mat-radio-group>
|
||||
</p>
|
||||
|
||||
<div class="console-app__registry-lock-notification">
|
||||
<mat-icon>priority_high</mat-icon>Confirmation email will be sent to your
|
||||
email address to confirm the unlock
|
||||
</div>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
type="submit"
|
||||
[disabled]="!unlockDomain.valid"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
} @else {
|
||||
<h1>Lock the domain {{ domainListService.selectedDomain }}</h1>
|
||||
<form (ngSubmit)="save(true)" [formGroup]="lockDomain">
|
||||
<p>
|
||||
<mat-label for="password">Password: </mat-label>
|
||||
<mat-form-field name="password" appearance="outline">
|
||||
<input matInput type="text" formControlName="password" required />
|
||||
</mat-form-field>
|
||||
</p>
|
||||
|
||||
<div class="console-app__registry-lock-notification">
|
||||
<mat-icon>priority_high</mat-icon>The lock will not take effect until you
|
||||
click the confirmation link that will be emailed to you. When it takes
|
||||
effect, you will be billed the standard server status change billing cost.
|
||||
</div>
|
||||
<button
|
||||
mat-flat-button
|
||||
color="primary"
|
||||
type="submit"
|
||||
[disabled]="!lockDomain.valid"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
.console-app {
|
||||
&__registry-lock {
|
||||
mat-label {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
}
|
||||
&__registry-lock-notification {
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
background-color: var(--light-highlight);
|
||||
margin-bottom: 20px;
|
||||
width: max-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// 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 { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Component, computed } from '@angular/core';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { RegistrarService } from '../registrar/registrar.service';
|
||||
import { UserDataService } from '../shared/services/userData.service';
|
||||
import { DomainListService } from './domainList.service';
|
||||
import { RegistryLockService } from './registryLock.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-registry-lock',
|
||||
templateUrl: './registryLock.component.html',
|
||||
styleUrls: ['./registryLock.component.scss'],
|
||||
})
|
||||
export class RegistryLockComponent {
|
||||
readonly isLocked = computed(() =>
|
||||
this.registryLockService.domainsLocks.some(
|
||||
(dl) => dl.domainName === this.domainListService.selectedDomain
|
||||
)
|
||||
);
|
||||
|
||||
relockOptions = [
|
||||
{ name: '1 hour', duration: 3600000 },
|
||||
{ name: '6 hours', duration: 21600000 },
|
||||
{ name: '24 hours', duration: 86400000 },
|
||||
{ name: 'Never', duration: undefined },
|
||||
];
|
||||
|
||||
lockDomain = new FormGroup({
|
||||
password: new FormControl(''),
|
||||
});
|
||||
|
||||
unlockDomain = new FormGroup({
|
||||
password: new FormControl(''),
|
||||
relockTime: new FormControl(undefined),
|
||||
});
|
||||
|
||||
constructor(
|
||||
protected registrarService: RegistrarService,
|
||||
protected domainListService: DomainListService,
|
||||
protected registryLockService: RegistryLockService,
|
||||
protected userDataService: UserDataService,
|
||||
private _snackBar: MatSnackBar
|
||||
) {}
|
||||
|
||||
goBack() {
|
||||
this.domainListService.selectedDomain = undefined;
|
||||
this.domainListService.activeActionComponent = null;
|
||||
}
|
||||
|
||||
save(isLock: boolean) {
|
||||
let request;
|
||||
if (!isLock) {
|
||||
request = this.registryLockService.registryLockDomain(
|
||||
this.domainListService.selectedDomain || '',
|
||||
this.unlockDomain.value.password || '',
|
||||
this.unlockDomain.value.relockTime || undefined,
|
||||
isLock
|
||||
);
|
||||
} else {
|
||||
request = this.registryLockService.registryLockDomain(
|
||||
this.domainListService.selectedDomain || '',
|
||||
this.lockDomain.value.password || '',
|
||||
undefined,
|
||||
isLock
|
||||
);
|
||||
}
|
||||
|
||||
request.subscribe({
|
||||
complete: () => {
|
||||
this.goBack();
|
||||
},
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.error);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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 { tap } from 'rxjs';
|
||||
import { RegistrarService } from '../registrar/registrar.service';
|
||||
import { BackendService } from '../shared/services/backend.service';
|
||||
|
||||
export interface DomainLocksResult {
|
||||
domainName: string;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RegistryLockService {
|
||||
public domainsLocks: DomainLocksResult[] = [];
|
||||
|
||||
constructor(
|
||||
private backendService: BackendService,
|
||||
private registrarService: RegistrarService
|
||||
) {}
|
||||
|
||||
retrieveLocks() {
|
||||
return this.backendService
|
||||
.getLocks(this.registrarService.registrarId())
|
||||
.pipe(
|
||||
tap((domainLocksResult) => {
|
||||
this.domainsLocks = domainLocksResult;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
registryLockDomain(
|
||||
domainName: string,
|
||||
password: string,
|
||||
relockDurationMillis: number | undefined,
|
||||
isLock: boolean
|
||||
) {
|
||||
return this.backendService.registryLockDomain(
|
||||
domainName,
|
||||
password,
|
||||
relockDurationMillis,
|
||||
this.registrarService.registrarId(),
|
||||
isLock
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,10 @@
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
|
||||
<mat-card appearance="outlined">
|
||||
<mat-card
|
||||
appearance="outlined"
|
||||
[elementId]="getElementIdForRegistrarsBlock()"
|
||||
>
|
||||
<mat-card-content>
|
||||
<h3>
|
||||
<mat-icon class="secondary-text">account_circle</mat-icon>
|
||||
|
||||
@@ -18,6 +18,7 @@ import { DomainListComponent } from '../domains/domainList.component';
|
||||
import { RegistrarComponent } from '../registrar/registrarsTable.component';
|
||||
import SecurityComponent from '../settings/security/security.component';
|
||||
import { SettingsComponent } from '../settings/settings.component';
|
||||
import { RESTRICTED_ELEMENTS } from '../shared/directives/userLevelVisiblity.directive';
|
||||
import { BreakPointObserverService } from '../shared/services/breakPoint.service';
|
||||
|
||||
@Component({
|
||||
@@ -30,6 +31,9 @@ export class HomeComponent {
|
||||
protected breakPointObserverService: BreakPointObserverService,
|
||||
private router: Router
|
||||
) {}
|
||||
getElementIdForRegistrarsBlock() {
|
||||
return RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT;
|
||||
}
|
||||
viewRegistrars() {
|
||||
this.router.navigate([RegistrarComponent.PATH], {
|
||||
queryParamsHandling: 'merge',
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
@if (isLoading) {
|
||||
<div class="console-app__registry-lock-verify-spinner">
|
||||
<mat-spinner />
|
||||
</div>
|
||||
} @else if (domainName) {
|
||||
<h1 class="mat-headline-4">Success!</h1>
|
||||
<div class="console-app__registry-lock-content">
|
||||
<div class="console-app__registry-lock-subhead">
|
||||
The domain {{ domainName }} has been successfully {{ action }}ed.
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
class="text-l"
|
||||
routerLink="{{ DOMAIN_LIST_COMPONENT_PATH }}"
|
||||
[queryParams]="{ registrarId: this.registrarService.registrarId() }"
|
||||
>Return to the list of domains</a
|
||||
>
|
||||
</div>
|
||||
} @else {
|
||||
<h1 class="mat-headline-4">Failure</h1>
|
||||
<div class="console-app__registry-lock-content">
|
||||
<div class="console-app__registry-lock-subhead">
|
||||
An error occurred: {{ errorMessage }}.<br /><br />Please double-check the
|
||||
verification code and try again.
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
.console-app__registry-lock {
|
||||
&-content {
|
||||
margin-top: 30px;
|
||||
}
|
||||
&-subhead {
|
||||
font-size: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
matTreeNodeToggle
|
||||
(click)="onClick(node)"
|
||||
[class.active]="router.url.includes(node.path)"
|
||||
[elementId]="getElementId(node)"
|
||||
>
|
||||
<mat-icon class="console-app__nav-icon" *ngIf="node.iconName">
|
||||
{{ node.iconName }}
|
||||
|
||||
@@ -18,6 +18,7 @@ import { MatTreeNestedDataSource } from '@angular/material/tree';
|
||||
import { NavigationEnd, Router } from '@angular/router';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { RouteWithIcon, routes } from '../app-routing.module';
|
||||
import { RESTRICTED_ELEMENTS } from '../shared/directives/userLevelVisiblity.directive';
|
||||
|
||||
interface NavMenuNode extends RouteWithIcon {
|
||||
parentRoute?: RouteWithIcon;
|
||||
@@ -37,6 +38,7 @@ export class NavigationComponent {
|
||||
treeControl = new NestedTreeControl<RouteWithIcon>((node) => node.children);
|
||||
dataSource = new MatTreeNestedDataSource<RouteWithIcon>();
|
||||
private subscription!: Subscription;
|
||||
|
||||
hasChild = (_: number, node: RouteWithIcon) =>
|
||||
!!node.children && node.children.length > 0;
|
||||
|
||||
@@ -56,6 +58,12 @@ export class NavigationComponent {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
getElementId(node: RouteWithIcon) {
|
||||
return node.path === 'registrars'
|
||||
? RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT
|
||||
: null;
|
||||
}
|
||||
|
||||
syncExpandedNavigationWithRoute(url: string) {
|
||||
const maybeComponentWithChildren = this.dataSource.data.find((menuNode) => {
|
||||
return (
|
||||
|
||||
@@ -142,7 +142,7 @@ JPY=billing-id-for-yen"
|
||||
<mat-label>State/Region: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
[required]="false"
|
||||
[required]="true"
|
||||
[(ngModel)]="newRegistrar.localizedAddress.state"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="console-app__resources-subhead">Technical resources</div>
|
||||
<a
|
||||
class="text-l"
|
||||
href="{{ userDataService.userData.technicalDocsUrl }}"
|
||||
href="{{ userDataService.userData()?.technicalDocsUrl }}"
|
||||
target="_blank"
|
||||
>View onboarding FAQs, TLD information, and technical documentation on
|
||||
Google Drive</a
|
||||
|
||||
@@ -132,6 +132,18 @@
|
||||
/>
|
||||
</mat-form-field>
|
||||
|
||||
@if((userDataService.userData()?.globalRole || 'NONE') !== "NONE") {
|
||||
<mat-form-field appearance="outline">
|
||||
<mat-label>ICANN Referral Email: </mat-label>
|
||||
<input
|
||||
matInput
|
||||
type="text"
|
||||
[(ngModel)]="registrarInEdit.icannReferralEmail"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</mat-form-field>
|
||||
}
|
||||
|
||||
<button mat-flat-button color="primary" type="submit">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
Registrar,
|
||||
RegistrarService,
|
||||
} from 'src/app/registrar/registrar.service';
|
||||
import { UserDataService } from 'src/app/shared/services/userData.service';
|
||||
import { WhoisService } from './whois.service';
|
||||
|
||||
@Component({
|
||||
@@ -30,6 +31,7 @@ export default class WhoisEditComponent {
|
||||
registrarInEdit: Registrar | undefined;
|
||||
|
||||
constructor(
|
||||
public userDataService: UserDataService,
|
||||
public whoisService: WhoisService,
|
||||
public registrarService: RegistrarService,
|
||||
private _snackBar: MatSnackBar
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 { Directive, ElementRef, Input, effect } from '@angular/core';
|
||||
import { UserDataService } from '../services/userData.service';
|
||||
|
||||
export enum RESTRICTED_ELEMENTS {
|
||||
REGISTRAR_ELEMENT,
|
||||
}
|
||||
|
||||
export const DISABLED_ELEMENTS_PER_ROLE = {
|
||||
NONE: [RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT],
|
||||
};
|
||||
|
||||
@Directive({
|
||||
selector: '[elementId]',
|
||||
})
|
||||
export class UserLevelVisibility {
|
||||
@Input() elementId!: RESTRICTED_ELEMENTS | null;
|
||||
|
||||
constructor(
|
||||
private userDataService: UserDataService,
|
||||
private el: ElementRef
|
||||
) {
|
||||
effect(this.processElement.bind(this));
|
||||
}
|
||||
|
||||
processElement() {
|
||||
const globalRole = this.userDataService?.userData()?.globalRole || 'NONE';
|
||||
if (this.elementId === null) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
// @ts-ignore
|
||||
(DISABLED_ELEMENTS_PER_ROLE[globalRole] || []).includes(this.elementId)
|
||||
) {
|
||||
this.el.nativeElement.style.display = 'none';
|
||||
} else {
|
||||
this.el.nativeElement.style.display = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ import { Injectable } from '@angular/core';
|
||||
import { Observable, catchError, of, throwError } from 'rxjs';
|
||||
|
||||
import { DomainListResult } from 'src/app/domains/domainList.service';
|
||||
import { DomainLocksResult } from 'src/app/domains/registryLock.service';
|
||||
import { RegistryLockVerificationResponse } from 'src/app/lock/registryLockVerify.service';
|
||||
import {
|
||||
Registrar,
|
||||
SecuritySettingsBackendModel,
|
||||
@@ -169,4 +171,38 @@ export class BackendService {
|
||||
whoisRegistrarFields
|
||||
);
|
||||
}
|
||||
|
||||
registryLockDomain(
|
||||
domainName: string,
|
||||
password: string | undefined,
|
||||
relockDurationMillis: number | undefined,
|
||||
registrarId: string,
|
||||
isLock: boolean
|
||||
) {
|
||||
return this.http.post(
|
||||
`/console-api/registry-lock?registrarId=${registrarId}`,
|
||||
{
|
||||
domainName,
|
||||
password,
|
||||
isLock,
|
||||
relockDurationMillis,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getLocks(registrarId: string): Observable<DomainLocksResult[]> {
|
||||
return this.http
|
||||
.get<DomainLocksResult[]>(
|
||||
`/console-api/registry-lock?registrarId=${registrarId}`
|
||||
)
|
||||
.pipe(catchError((err) => this.errorCatcher<DomainLocksResult[]>(err)));
|
||||
}
|
||||
|
||||
verifyRegistryLockRequest(
|
||||
lockVerificationCode: string
|
||||
): Observable<RegistryLockVerificationResponse> {
|
||||
return this.http.get<RegistryLockVerificationResponse>(
|
||||
`/console-api/registry-lock-verify?lockVerificationCode=${lockVerificationCode}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { Observable, tap } from 'rxjs';
|
||||
import { BackendService } from './backend.service';
|
||||
@@ -33,7 +33,7 @@ export interface UserData {
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UserDataService implements GlobalLoader {
|
||||
public userData!: UserData;
|
||||
userData = signal<UserData | undefined>(undefined);
|
||||
constructor(
|
||||
private backend: BackendService,
|
||||
protected globalLoader: GlobalLoaderService,
|
||||
@@ -48,7 +48,7 @@ export class UserDataService implements GlobalLoader {
|
||||
getUserData(): Observable<UserData> {
|
||||
return this.backend.getUserData().pipe(
|
||||
tap((userData: UserData) => {
|
||||
this.userData = userData;
|
||||
this.userData.set(userData);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
For general purpose questions once you are integrated with our registry
|
||||
system. If the issue is urgent, please put "Urgent" in the email title.
|
||||
</p>
|
||||
<a class="text-l" href="mailto:{{ userDataService.userData.supportEmail }}">{{
|
||||
userDataService.userData.supportEmail
|
||||
}}</a>
|
||||
<a
|
||||
class="text-l"
|
||||
href="mailto:{{ userDataService.userData()?.supportEmail }}"
|
||||
>{{ userDataService.userData()?.supportEmail }}</a
|
||||
>
|
||||
<p class="secondary-text">
|
||||
Note: You may receive occasional service announcements via
|
||||
registrar-announcement@google.com. You will not be able to reply to
|
||||
@@ -29,13 +31,13 @@
|
||||
<p class="text-l">For general support inquiries 24/7:</p>
|
||||
<a
|
||||
class="text-l"
|
||||
href="tel:{{ userDataService.userData.supportPhoneNumber }}"
|
||||
>{{ userDataService.userData.supportPhoneNumber }}</a
|
||||
href="tel:{{ userDataService.userData()?.supportPhoneNumber }}"
|
||||
>{{ userDataService.userData()?.supportPhoneNumber }}</a
|
||||
>
|
||||
@if (userDataService.userData.passcode) {
|
||||
@if (userDataService.userData()?.passcode) {
|
||||
<p class="text-l">Your telephone passcode:</p>
|
||||
<p class="text-l console-app__support-passcode">
|
||||
{{ userDataService.userData.passcode }}
|
||||
{{ userDataService.userData()?.passcode }}
|
||||
</p>
|
||||
<p class="secondary-text">
|
||||
Note: Please be ready with your account name and telephone passcode when
|
||||
|
||||
@@ -77,6 +77,7 @@ public class CloudTasksUtils implements Serializable {
|
||||
@Config("projectId") String projectId,
|
||||
@Config("locationId") String locationId,
|
||||
@Config("oauthClientId") String oauthClientId,
|
||||
// Note that this has to be a service account, due to limitations of the Cloud Tasks API.
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credential,
|
||||
SerializableCloudTasksClient client) {
|
||||
this.retrier = retrier;
|
||||
|
||||
@@ -64,7 +64,7 @@ import org.joda.time.Duration;
|
||||
auth = Auth.AUTH_ADMIN)
|
||||
public class DeleteProberDataAction implements Runnable {
|
||||
|
||||
// TODO(b/323026070): Add email alert on failure of this action
|
||||
// TODO(b/346390641): Add email alert on failure of this action
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
|
||||
<command>
|
||||
<delete>
|
||||
<domain:delete
|
||||
xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
|
||||
<domain:name>%DOMAIN%</domain:name>
|
||||
</domain:delete>
|
||||
</delete>
|
||||
<extension>
|
||||
<metadata:metadata xmlns:metadata="urn:google:params:xml:ns:metadata-1.0">
|
||||
<metadata:reason>Non-renewing domain has reached expiration date.</metadata:reason>
|
||||
<metadata:requestedByRegistrar>false</metadata:requestedByRegistrar>
|
||||
</metadata:metadata>
|
||||
</extension>
|
||||
<clTRID>ABC-12345</clTRID>
|
||||
</command>
|
||||
</epp>
|
||||
@@ -266,7 +266,6 @@ public final class DomainCreateFlow implements MutatingFlow {
|
||||
validateLaunchCreateNotice(launchCreate.get().getNotice(), domainLabel, isSuperuser, now);
|
||||
}
|
||||
boolean isSunriseCreate = hasSignedMarks && (tldState == START_DATE_SUNRISE);
|
||||
// TODO(sarahbot@): Add check for valid EPP actions on the token
|
||||
Optional<AllocationToken> allocationToken =
|
||||
allocationTokenFlowUtils.verifyAllocationTokenCreateIfPresent(
|
||||
command,
|
||||
|
||||
@@ -24,6 +24,8 @@ import static com.google.common.collect.Sets.difference;
|
||||
import static com.google.common.collect.Sets.intersection;
|
||||
import static com.google.common.collect.Sets.union;
|
||||
import static google.registry.bsa.persistence.BsaLabelUtils.isLabelBlocked;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.isActiveNow;
|
||||
import static google.registry.model.domain.Domain.MAX_REGISTRATION_YEARS;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.REGISTER_BSA;
|
||||
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
@@ -481,10 +483,14 @@ public class DomainFlowUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void validateRequiredContactsPresent(
|
||||
static void validateRequiredContactsPresentIfRequiredForDataset(
|
||||
Optional<VKey<Contact>> registrant, Set<DesignatedContact> contacts)
|
||||
throws RequiredParameterMissingException {
|
||||
// TODO: Check minimum reg data set migration schedule here and don't throw when any are empty.
|
||||
// TODO(b/353347632): Change this flag check to a registry config check.
|
||||
if (isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) {
|
||||
// Contacts are not required once we have begun the migration to the minimum dataset
|
||||
return;
|
||||
}
|
||||
if (registrant.isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
@@ -1041,7 +1047,8 @@ public class DomainFlowUtils {
|
||||
String tldStr = tld.getTldStr();
|
||||
validateRegistrantAllowedOnTld(tldStr, command.getRegistrantContactId());
|
||||
validateNoDuplicateContacts(command.getContacts());
|
||||
validateRequiredContactsPresent(command.getRegistrant(), command.getContacts());
|
||||
validateRequiredContactsPresentIfRequiredForDataset(
|
||||
command.getRegistrant(), command.getContacts());
|
||||
ImmutableSet<String> hostNames = command.getNameserverHostNames();
|
||||
validateNameserversCountForTld(tldStr, domainName, hostNames.size());
|
||||
validateNameserversAllowedOnTld(tldStr, hostNames);
|
||||
|
||||
@@ -38,9 +38,11 @@ import static google.registry.flows.domain.DomainFlowUtils.validateNameserversAl
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNameserversCountForTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateNoDuplicateContacts;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateRegistrantAllowedOnTld;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresent;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.validateRequiredContactsPresentIfRequiredForDataset;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyClientUpdateNotProhibited;
|
||||
import static google.registry.flows.domain.DomainFlowUtils.verifyNotInPendingDelete;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.isActiveNow;
|
||||
import static google.registry.model.reporting.HistoryEntry.Type.DOMAIN_UPDATE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
@@ -66,6 +68,7 @@ import google.registry.flows.domain.DomainFlowUtils.NameserversNotSpecifiedForTl
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainCommand.Update;
|
||||
@@ -87,6 +90,7 @@ import google.registry.model.poll.PendingActionNotificationResponse.DomainPendin
|
||||
import google.registry.model.poll.PollMessage;
|
||||
import google.registry.model.reporting.IcannReportingTypes.ActivityReportField;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
@@ -248,7 +252,6 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
checkSameValuesNotAddedAndRemoved(add.getContacts(), remove.getContacts());
|
||||
checkSameValuesNotAddedAndRemoved(add.getStatusValues(), remove.getStatusValues());
|
||||
Change change = command.getInnerChange();
|
||||
validateRegistrantIsntBeingRemoved(change);
|
||||
Optional<SecDnsUpdateExtension> secDnsUpdate =
|
||||
eppInput.getSingleExtension(SecDnsUpdateExtension.class);
|
||||
|
||||
@@ -278,7 +281,7 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
.removeStatusValues(remove.getStatusValues())
|
||||
.removeContacts(remove.getContacts())
|
||||
.addContacts(add.getContacts())
|
||||
.setRegistrant(change.getRegistrant().or(domain::getRegistrant))
|
||||
.setRegistrant(determineUpdatedRegistrant(change, domain))
|
||||
.setAuthInfo(firstNonNull(change.getAuthInfo(), domain.getAuthInfo()));
|
||||
|
||||
if (!add.getNameservers().isEmpty()) {
|
||||
@@ -300,13 +303,19 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
return domainBuilder.build();
|
||||
}
|
||||
|
||||
private static void validateRegistrantIsntBeingRemoved(Change change) throws EppException {
|
||||
// TODO(mcilwain): Make this check the minimum registration data set migration schedule
|
||||
// and not require presence of a registrant in later stages.
|
||||
private Optional<VKey<Contact>> determineUpdatedRegistrant(Change change, Domain domain)
|
||||
throws EppException {
|
||||
// During phase 1 of minimum dataset transition, allow registrant to be removed
|
||||
if (change.getRegistrantContactId().isPresent()
|
||||
&& change.getRegistrantContactId().get().isEmpty()) {
|
||||
throw new MissingRegistrantException();
|
||||
// TODO(b/353347632): Change this flag check to a registry config check.
|
||||
if (isActiveNow(MINIMUM_DATASET_CONTACTS_OPTIONAL)) {
|
||||
return Optional.empty();
|
||||
} else {
|
||||
throw new MissingRegistrantException();
|
||||
}
|
||||
}
|
||||
return change.getRegistrant().or(domain::getRegistrant);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,7 +326,8 @@ public final class DomainUpdateFlow implements MutatingFlow {
|
||||
* cause Domain update failure.
|
||||
*/
|
||||
private static void validateNewState(Domain newDomain) throws EppException {
|
||||
validateRequiredContactsPresent(newDomain.getRegistrant(), newDomain.getContacts());
|
||||
validateRequiredContactsPresentIfRequiredForDataset(
|
||||
newDomain.getRegistrant(), newDomain.getContacts());
|
||||
validateDsData(newDomain.getDsData());
|
||||
validateNameserversCountForTld(
|
||||
newDomain.getTld(),
|
||||
|
||||
@@ -32,7 +32,6 @@ import com.google.common.collect.Streams;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.pricing.StaticPremiumListPricingEngine;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
@@ -48,9 +47,7 @@ import google.registry.util.CidrAddressBlock;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -271,29 +268,21 @@ public final class OteAccountBuilder {
|
||||
Optional<String> groupEmailAddress, CloudTasksUtils cloudTasksUtils, IamClient iamClient) {
|
||||
for (User user : users) {
|
||||
User.grantIapPermission(
|
||||
user.getEmailAddress(), groupEmailAddress, cloudTasksUtils, iamClient);
|
||||
user.getEmailAddress(), groupEmailAddress, cloudTasksUtils, null, iamClient);
|
||||
}
|
||||
}
|
||||
|
||||
/** Saves all the OT&E entities we created. */
|
||||
private void saveAllEntities() {
|
||||
ImmutableList<Tld> registries = ImmutableList.of(sunriseTld, gaTld, eapTld);
|
||||
Map<String, User> existingUsers = new HashMap<>();
|
||||
|
||||
users.forEach(
|
||||
user ->
|
||||
UserDao.loadUser(user.getEmailAddress())
|
||||
.ifPresent(
|
||||
existingUser ->
|
||||
existingUsers.put(existingUser.getEmailAddress(), existingUser)));
|
||||
|
||||
if (!replaceExisting) {
|
||||
checkState(existingUsers.isEmpty(), "Found existing users: %s", existingUsers);
|
||||
}
|
||||
|
||||
tm().transact(
|
||||
() -> {
|
||||
if (!replaceExisting) {
|
||||
ImmutableMap<String, User> existingUsers =
|
||||
tm().loadByEntitiesIfPresent(users).stream()
|
||||
.collect(toImmutableMap(User::getEmailAddress, u -> u));
|
||||
checkState(existingUsers.isEmpty(), "Found existing users: %s", existingUsers);
|
||||
|
||||
ImmutableList<VKey<? extends ImmutableObject>> keys =
|
||||
Streams.concat(
|
||||
registries.stream().map(tld -> Tld.createVKey(tld.getTldStr())),
|
||||
@@ -317,16 +306,7 @@ public final class OteAccountBuilder {
|
||||
tm().putAll(registrars);
|
||||
});
|
||||
|
||||
for (User user : users) {
|
||||
String email = user.getEmailAddress();
|
||||
if (existingUsers.containsKey(email)) {
|
||||
// Note that other roles for the existing user are reset. We do this instead of simply
|
||||
// saving the new user is that UserDao does not allow us to save the new user with the same
|
||||
// email as the existing user.
|
||||
user = existingUsers.get(email).asBuilder().setUserRoles(user.getUserRoles()).build();
|
||||
}
|
||||
UserDao.saveUser(user);
|
||||
}
|
||||
tm().transact(() -> tm().putAll(ImmutableList.copyOf(users)));
|
||||
}
|
||||
|
||||
private Registrar addAllowedTld(Registrar registrar) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.ImmutableMap.toImmutableMap;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.config.RegistryConfig.getSingletonCacheRefreshDuration;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
@@ -62,7 +63,8 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
public enum FeatureName {
|
||||
TEST_FEATURE,
|
||||
MINIMUM_DATASET_CONTACTS_OPTIONAL,
|
||||
MINIMUM_DATASET_CONTACTS_PROHIBITED
|
||||
MINIMUM_DATASET_CONTACTS_PROHIBITED,
|
||||
NEW_CONSOLE
|
||||
}
|
||||
|
||||
/** The name of the flag/feature. */
|
||||
@@ -150,6 +152,17 @@ public class FeatureFlag extends ImmutableObject implements Buildable {
|
||||
return status.getValueAtTime(time);
|
||||
}
|
||||
|
||||
/** Returns if the FeatureFlag with the given FeatureName is active now. */
|
||||
public static boolean isActiveNow(FeatureName featureName) {
|
||||
tm().assertInTransaction();
|
||||
return isActiveAt(featureName, tm().getTransactionTime());
|
||||
}
|
||||
|
||||
/** Returns if the FeatureFlag with the given FeatureName is active at a given time. */
|
||||
public static boolean isActiveAt(FeatureName featureName, DateTime dateTime) {
|
||||
return FeatureFlag.get(featureName).getStatus(dateTime).equals(ACTIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FeatureFlag.Builder asBuilder() {
|
||||
return new FeatureFlag.Builder(clone(this));
|
||||
|
||||
@@ -14,33 +14,36 @@
|
||||
|
||||
package google.registry.model.console;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.tools.server.UpdateUserGroupAction.GROUP_UPDATE_QUEUE;
|
||||
|
||||
import com.google.cloud.tasks.v2.Task;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.Action.Service;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.tools.ServiceConnection;
|
||||
import google.registry.tools.server.UpdateUserGroupAction;
|
||||
import google.registry.tools.server.UpdateUserGroupAction.Mode;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/** A console user, either a registry employee or a registrar partner. */
|
||||
@Embeddable
|
||||
@Entity
|
||||
@Table(indexes = {@Index(columnList = "emailAddress", name = "user_email_address_idx")})
|
||||
@Table
|
||||
public class User extends UserBase {
|
||||
|
||||
public static final String IAP_SECURED_WEB_APP_USER_ROLE = "roles/iap.httpsResourceAccessor";
|
||||
@@ -55,18 +58,30 @@ public class User extends UserBase {
|
||||
public static void grantIapPermission(
|
||||
String emailAddress,
|
||||
Optional<String> groupEmailAddress,
|
||||
CloudTasksUtils cloudTasksUtils,
|
||||
@Nullable CloudTasksUtils cloudTasksUtils,
|
||||
@Nullable ServiceConnection connection,
|
||||
IamClient iamClient) {
|
||||
if (RegistryEnvironment.isInTestServer()) {
|
||||
return;
|
||||
}
|
||||
checkArgument(
|
||||
cloudTasksUtils != null || connection != null,
|
||||
"At least one of cloudTasksUtils or connection must be set");
|
||||
checkArgument(
|
||||
cloudTasksUtils == null || connection == null,
|
||||
"Only one of cloudTasksUtils or connection can be set");
|
||||
if (groupEmailAddress.isEmpty()) {
|
||||
logger.atInfo().log("Granting IAP role to user %s", emailAddress);
|
||||
iamClient.addBinding(emailAddress, IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
} else {
|
||||
logger.atInfo().log("Adding %s to group %s", emailAddress, groupEmailAddress.get());
|
||||
modifyGroupMembershipAsync(
|
||||
emailAddress, groupEmailAddress.get(), cloudTasksUtils, UpdateUserGroupAction.Mode.ADD);
|
||||
if (cloudTasksUtils != null) {
|
||||
modifyGroupMembershipAsync(
|
||||
emailAddress, groupEmailAddress.get(), cloudTasksUtils, UpdateUserGroupAction.Mode.ADD);
|
||||
} else {
|
||||
modifyGroupMembershipSync(
|
||||
emailAddress, groupEmailAddress.get(), connection, UpdateUserGroupAction.Mode.ADD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,18 +94,29 @@ public class User extends UserBase {
|
||||
public static void revokeIapPermission(
|
||||
String emailAddress,
|
||||
Optional<String> groupEmailAddress,
|
||||
CloudTasksUtils cloudTasksUtils,
|
||||
@Nullable CloudTasksUtils cloudTasksUtils,
|
||||
@Nullable ServiceConnection connection,
|
||||
IamClient iamClient) {
|
||||
if (RegistryEnvironment.isInTestServer()) {
|
||||
return;
|
||||
}
|
||||
checkArgument(
|
||||
cloudTasksUtils != null || connection != null,
|
||||
"At least one of cloudTasksUtils or connection must be set");
|
||||
checkArgument(
|
||||
cloudTasksUtils == null || connection == null,
|
||||
"Only one of cloudTasksUtils or connection can be set");
|
||||
if (groupEmailAddress.isEmpty()) {
|
||||
logger.atInfo().log("Removing IAP role from user %s", emailAddress);
|
||||
iamClient.removeBinding(emailAddress, IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
} else {
|
||||
logger.atInfo().log("Removing %s from group %s", emailAddress, groupEmailAddress.get());
|
||||
modifyGroupMembershipAsync(
|
||||
emailAddress, groupEmailAddress.get(), cloudTasksUtils, Mode.REMOVE);
|
||||
if (cloudTasksUtils != null) {
|
||||
modifyGroupMembershipAsync(
|
||||
emailAddress, groupEmailAddress.get(), cloudTasksUtils, Mode.REMOVE);
|
||||
} else {
|
||||
modifyGroupMembershipSync(emailAddress, groupEmailAddress.get(), connection, Mode.REMOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,12 +139,30 @@ public class User extends UserBase {
|
||||
cloudTasksUtils.enqueue(GROUP_UPDATE_QUEUE, task);
|
||||
}
|
||||
|
||||
@Override
|
||||
private static void modifyGroupMembershipSync(
|
||||
String userEmailAddress, String groupEmailAddress, ServiceConnection connection, Mode mode) {
|
||||
try {
|
||||
connection.sendPostRequest(
|
||||
UpdateUserGroupAction.PATH,
|
||||
ImmutableMap.of(
|
||||
"userEmailAddress",
|
||||
userEmailAddress,
|
||||
"groupEmailAddress",
|
||||
groupEmailAddress,
|
||||
"groupUpdateMode",
|
||||
mode.name()),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot send request to server", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Override
|
||||
@Access(AccessType.PROPERTY)
|
||||
public Long getId() {
|
||||
return super.getId();
|
||||
public String getEmailAddress() {
|
||||
return super.getEmailAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,7 +172,7 @@ public class User extends UserBase {
|
||||
|
||||
@Override
|
||||
public VKey<User> createVKey() {
|
||||
return VKey.create(User.class, getId());
|
||||
return VKey.create(User.class, getEmailAddress());
|
||||
}
|
||||
|
||||
/** Builder for constructing immutable {@link User} objects. */
|
||||
|
||||
@@ -49,12 +49,8 @@ public class UserBase extends UpdateAutoTimestampEntity implements Buildable {
|
||||
|
||||
private static final long serialVersionUID = 6936728603828566721L;
|
||||
|
||||
/** Autogenerated unique ID of this user. */
|
||||
@Transient private Long id;
|
||||
|
||||
/** Email address of the user in question. */
|
||||
@Column(nullable = false)
|
||||
String emailAddress;
|
||||
@Transient String emailAddress;
|
||||
|
||||
/** Optional external email address to use for registry lock confirmation emails. */
|
||||
@Column String registryLockEmailAddress;
|
||||
@@ -72,22 +68,17 @@ public class UserBase extends UpdateAutoTimestampEntity implements Buildable {
|
||||
/** Randomly generated hash salt. */
|
||||
String registryLockPasswordSalt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user ID.
|
||||
* Sets the user email address.
|
||||
*
|
||||
* <p>This should only be used for restoring the user id of an object being loaded in a PostLoad
|
||||
* method (effectively, when it is still under construction by Hibernate). In all other cases, the
|
||||
* object should be regarded as immutable and changes should go through a Builder.
|
||||
* <p>This should only be used for restoring an object being loaded in a PostLoad method
|
||||
* (effectively, when it is still under construction by Hibernate). In all other cases, the object
|
||||
* should be regarded as immutable and changes should go through a Builder.
|
||||
*
|
||||
* <p>In addition to this special case use, this method must exist to satisfy Hibernate.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
void setId(Long id) {
|
||||
this.id = id;
|
||||
void setEmailAddress(String emailAddress) {
|
||||
this.emailAddress = emailAddress;
|
||||
}
|
||||
|
||||
public String getEmailAddress() {
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.console;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/** Data access object for {@link User} objects to simplify saving and retrieval. */
|
||||
public class UserDao {
|
||||
|
||||
/** Retrieves the one user with this email address if it exists. */
|
||||
public static Optional<User> loadUser(String emailAddress) {
|
||||
return tm().transact(
|
||||
() ->
|
||||
tm().query("FROM User WHERE emailAddress = :emailAddress", User.class)
|
||||
.setParameter("emailAddress", emailAddress)
|
||||
.getResultStream()
|
||||
.findFirst());
|
||||
}
|
||||
|
||||
/** Saves the given user, checking that no existing user already exists with this email. */
|
||||
public static void saveUser(User user) {
|
||||
tm().transact(
|
||||
() -> {
|
||||
// Check for an existing user (the unique constraint protects us, but this gives a
|
||||
// nicer exception)
|
||||
Optional<User> maybeSavedUser = loadUser(user.getEmailAddress());
|
||||
if (maybeSavedUser.isPresent()) {
|
||||
User savedUser = maybeSavedUser.get();
|
||||
checkArgument(
|
||||
savedUser.getId().equals(user.getId()),
|
||||
String.format(
|
||||
"Attempted save of User with email address %s and ID %s, user with that"
|
||||
+ " email already exists with ID %s",
|
||||
user.getEmailAddress(), user.getId(), savedUser.getId()));
|
||||
}
|
||||
tm().put(user);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -38,9 +38,8 @@ public class UserUpdateHistory extends ConsoleUpdateHistory {
|
||||
|
||||
UserBase user;
|
||||
|
||||
// This field exists so that it's populated in the SQL table
|
||||
@Column(nullable = false, name = "userId")
|
||||
Long id;
|
||||
@Column(nullable = false, name = "emailAddress")
|
||||
String emailAddress;
|
||||
|
||||
public UserBase getUser() {
|
||||
return user;
|
||||
@@ -48,7 +47,7 @@ public class UserUpdateHistory extends ConsoleUpdateHistory {
|
||||
|
||||
@PostLoad
|
||||
void postLoad() {
|
||||
user.setId(id);
|
||||
user.setEmailAddress(emailAddress);
|
||||
}
|
||||
|
||||
/** Creates a {@link VKey} instance for this entity. */
|
||||
@@ -79,7 +78,7 @@ public class UserUpdateHistory extends ConsoleUpdateHistory {
|
||||
|
||||
public Builder setUser(User user) {
|
||||
getInstance().user = user;
|
||||
getInstance().id = user.getId();
|
||||
getInstance().emailAddress = user.getEmailAddress();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
/** Token saved on a TLD to use if no other token is passed from the client */
|
||||
DEFAULT_PROMO(/* isOneTimeUse= */ false),
|
||||
/** This is the old name for what is now BULK_PRICING. */
|
||||
// TODO(sarahbot@): Remove this type once all tokens of this type have been scrubbed from the
|
||||
// TODO(b/261763205): Remove this type once all tokens of this type have been scrubbed from the
|
||||
// database
|
||||
@Deprecated
|
||||
PACKAGE(/* isOneTimeUse= */ false),
|
||||
|
||||
@@ -25,8 +25,6 @@ import javax.persistence.AccessType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.Table;
|
||||
|
||||
/**
|
||||
* A contact for a Registrar. Note, equality, hashCode and comparable have been overridden to only
|
||||
@@ -37,7 +35,6 @@ import javax.persistence.Table;
|
||||
* set to true.
|
||||
*/
|
||||
@Entity
|
||||
@Table(indexes = @Index(columnList = "loginEmailAddress", name = "registrarpoc_login_email_idx"))
|
||||
@IdClass(RegistrarPocId.class)
|
||||
@Access(AccessType.FIELD)
|
||||
public class RegistrarPoc extends RegistrarPocBase {
|
||||
|
||||
@@ -98,12 +98,7 @@ public class RegistrarPocBase extends ImmutableObject implements Jsonifiable, Un
|
||||
/** The name of the contact. */
|
||||
@Expose String name;
|
||||
|
||||
/**
|
||||
* The contact email address of the contact.
|
||||
*
|
||||
* <p>This is different from the login email which is assgined to the regstrar and cannot be
|
||||
* changed.
|
||||
*/
|
||||
/** The contact email address of the contact. */
|
||||
@Expose @Transient String emailAddress;
|
||||
|
||||
@Expose @Transient public String registrarId;
|
||||
@@ -123,9 +118,6 @@ public class RegistrarPocBase extends ImmutableObject implements Jsonifiable, Un
|
||||
*/
|
||||
@Expose Set<Type> types;
|
||||
|
||||
/** A GAIA email address that was assigned to the registrar for console login purpose. */
|
||||
String loginEmailAddress;
|
||||
|
||||
/**
|
||||
* Whether this contact is publicly visible in WHOIS registrar query results as an Admin contact.
|
||||
*/
|
||||
@@ -219,10 +211,6 @@ public class RegistrarPocBase extends ImmutableObject implements Jsonifiable, Un
|
||||
return visibleInDomainWhoisAsAbuse;
|
||||
}
|
||||
|
||||
public String getLoginEmailAddress() {
|
||||
return loginEmailAddress;
|
||||
}
|
||||
|
||||
public Builder<? extends RegistrarPocBase, ?> asBuilder() {
|
||||
return new Builder<>(clone(this));
|
||||
}
|
||||
@@ -286,13 +274,6 @@ public class RegistrarPocBase extends ImmutableObject implements Jsonifiable, Un
|
||||
+ "Registrar Abuse contact info: ")
|
||||
.append(getVisibleInDomainWhoisAsAbuse() ? "Yes" : "No")
|
||||
.append('\n');
|
||||
result
|
||||
.append("Registrar-Console access: ")
|
||||
.append(getLoginEmailAddress() != null ? "Yes" : "No")
|
||||
.append('\n');
|
||||
if (getLoginEmailAddress() != null) {
|
||||
result.append("Login Email Address: ").append(getLoginEmailAddress()).append('\n');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@@ -310,7 +291,6 @@ public class RegistrarPocBase extends ImmutableObject implements Jsonifiable, Un
|
||||
.put("visibleInDomainWhoisAsAbuse", visibleInDomainWhoisAsAbuse)
|
||||
.put("allowedToSetRegistryLockPassword", allowedToSetRegistryLockPassword)
|
||||
.put("registryLockAllowed", isRegistryLockAllowed())
|
||||
.put("loginEmailAddress", loginEmailAddress)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -411,11 +391,6 @@ public class RegistrarPocBase extends ImmutableObject implements Jsonifiable, Un
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setLoginEmailAddress(String loginEmailAddress) {
|
||||
getInstance().loginEmailAddress = loginEmailAddress;
|
||||
return thisCastToDerived();
|
||||
}
|
||||
|
||||
public B setAllowedToSetRegistryLockPassword(boolean allowedToSetRegistryLockPassword) {
|
||||
if (allowedToSetRegistryLockPassword) {
|
||||
getInstance().registryLockPasswordSalt = null;
|
||||
|
||||
@@ -76,7 +76,7 @@ public class RdapEntityAction extends RdapActionBase {
|
||||
// they are global, and might have different roles for different domains.
|
||||
if (contact.isPresent() && isAuthorized(contact.get())) {
|
||||
return rdapJsonFormatter.createRdapContactEntity(
|
||||
contact, ImmutableSet.of(), OutputDataType.FULL);
|
||||
contact.get(), ImmutableSet.of(), OutputDataType.FULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -461,7 +461,7 @@ public class RdapEntitySearchAction extends RdapSearchActionBase {
|
||||
.entitySearchResultsBuilder()
|
||||
.add(
|
||||
rdapJsonFormatter.createRdapContactEntity(
|
||||
Optional.of(contact), ImmutableSet.of(), outputDataType));
|
||||
contact, ImmutableSet.of(), outputDataType));
|
||||
newCursor =
|
||||
Optional.of(
|
||||
CONTACT_CURSOR_PREFIX
|
||||
|
||||
@@ -75,7 +75,6 @@ import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -251,16 +250,6 @@ public class RdapJsonFormatter {
|
||||
private static final Ordering<DesignatedContact> DESIGNATED_CONTACT_ORDERING =
|
||||
Ordering.natural().onResultOf(DesignatedContact::getType);
|
||||
|
||||
/**
|
||||
* The list of RDAP contact roles that are required to be present on each domain.
|
||||
*
|
||||
* <p>Per RDAP Response Profile 2.7.3, A domain MUST have the REGISTRANT, ADMIN, TECH roles and
|
||||
* MAY have others. We also have the BILLING role in our system but it isn't required and is only
|
||||
* listed if actually present.
|
||||
*/
|
||||
private static final ImmutableSet<Role> REQUIRED_CONTACT_ROLES =
|
||||
ImmutableSet.of(Role.REGISTRANT, Role.ADMIN, Role.TECH);
|
||||
|
||||
/** Creates the TOS notice that is added to every reply. */
|
||||
Notice createTosNotice() {
|
||||
String linkValue = makeRdapServletRelativeUrl("help", RdapHelpAction.TOS_PATH);
|
||||
@@ -403,7 +392,6 @@ public class RdapJsonFormatter {
|
||||
|
||||
// Convert the contact entities to RDAP output contacts (this also converts the contact types
|
||||
// to RDAP roles).
|
||||
Set<RdapContactEntity> rdapContacts = new LinkedHashSet<>();
|
||||
for (VKey<Contact> contactKey : contactsToRoles.keySet()) {
|
||||
Set<Role> roles =
|
||||
contactsToRoles.get(contactKey).stream()
|
||||
@@ -412,22 +400,13 @@ public class RdapJsonFormatter {
|
||||
if (roles.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
rdapContacts.add(
|
||||
createRdapContactEntity(
|
||||
Optional.ofNullable(loadedContacts.get(contactKey)), roles, OutputDataType.INTERNAL));
|
||||
builder
|
||||
.entitiesBuilder()
|
||||
.add(
|
||||
createRdapContactEntity(
|
||||
loadedContacts.get(contactKey), roles, OutputDataType.INTERNAL));
|
||||
}
|
||||
|
||||
// Loop through all required contact roles and fill in placeholder REDACTED info for any
|
||||
// required ones that are missing, i.e. because of minimum registration data set.
|
||||
for (Role role : REQUIRED_CONTACT_ROLES) {
|
||||
if (rdapContacts.stream().noneMatch(c -> c.roles().contains(role))) {
|
||||
rdapContacts.add(
|
||||
createRdapContactEntity(
|
||||
Optional.empty(), ImmutableSet.of(role), OutputDataType.INTERNAL));
|
||||
}
|
||||
}
|
||||
builder.entitiesBuilder().addAll(rdapContacts);
|
||||
|
||||
// Add the nameservers to the data; the load was kicked off above for efficiency.
|
||||
// RDAP Response Profile 2.9: we MUST have the nameservers
|
||||
for (Host host : HOST_RESOURCE_ORDERING.immutableSortedCopy(loadedHosts)) {
|
||||
@@ -537,28 +516,20 @@ public class RdapJsonFormatter {
|
||||
* @param outputDataType whether to generate full or summary data
|
||||
*/
|
||||
RdapContactEntity createRdapContactEntity(
|
||||
Optional<Contact> contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
|
||||
Contact contact, Iterable<RdapEntity.Role> roles, OutputDataType outputDataType) {
|
||||
RdapContactEntity.Builder contactBuilder = RdapContactEntity.builder();
|
||||
|
||||
// RDAP Response Profile 2.7.1, 2.7.3 - we MUST have the contacts. 2.7.4 discusses censoring of
|
||||
// fields we don't want to show (as opposed to not having contacts at all) because of GDPR etc.
|
||||
//
|
||||
// 2.8 allows for unredacted output for authorized people.
|
||||
// TODO(mcilwain): Once the RDAP profile is fully updated for minimum registration data set,
|
||||
// we will want to not include non-existent contacts at all, rather than
|
||||
// pretending they exist and just showing REDACTED info. This is especially
|
||||
// important for authorized flows, where you wouldn't expect to see redaction
|
||||
// (although no one actually has access to authorized flows yet).
|
||||
boolean isAuthorized =
|
||||
contact.isPresent()
|
||||
&& rdapAuthorization.isAuthorizedForRegistrar(
|
||||
contact.get().getCurrentSponsorRegistrarId());
|
||||
rdapAuthorization.isAuthorizedForRegistrar(contact.getCurrentSponsorRegistrarId());
|
||||
|
||||
VcardArray.Builder vcardBuilder = VcardArray.builder();
|
||||
|
||||
if (isAuthorized) {
|
||||
fillRdapContactEntityWhenAuthorized(
|
||||
contactBuilder, vcardBuilder, contact.get(), outputDataType);
|
||||
fillRdapContactEntityWhenAuthorized(contactBuilder, vcardBuilder, contact, outputDataType);
|
||||
} else {
|
||||
// GTLD Registration Data Temp Spec 17may18, Appendix A, 2.3, 2.4 and RDAP Response Profile
|
||||
// 2.7.4.1, 2.7.4.2 - the following fields must be redacted:
|
||||
|
||||
+4
-2
@@ -15,6 +15,7 @@
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
@@ -23,7 +24,7 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.request.auth.AuthModule.IapOidc;
|
||||
import google.registry.request.auth.AuthModule.RegularOidc;
|
||||
import google.registry.request.auth.AuthModule.RegularOidcFallback;
|
||||
@@ -117,7 +118,8 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
logger.atWarning().log("No email address from the OIDC token:\n%s", token.getPayload());
|
||||
return AuthResult.NOT_AUTHENTICATED;
|
||||
}
|
||||
Optional<User> maybeUser = UserDao.loadUser(email);
|
||||
Optional<User> maybeUser =
|
||||
tm().transact(() -> tm().loadByKeyIfPresent(VKey.create(User.class, email)));
|
||||
if (maybeUser.isPresent()) {
|
||||
return AuthResult.createUser(maybeUser.get());
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.tools.params.KeyValueMapParameter.StringToRegistrarRoleMap;
|
||||
import java.util.Optional;
|
||||
@@ -95,8 +94,6 @@ public abstract class CreateOrUpdateUserCommand extends ConfirmingCommand {
|
||||
builder.setRegistryLockEmailAddress(registryLockEmailAddress);
|
||||
}
|
||||
}
|
||||
|
||||
User newUser = builder.build();
|
||||
UserDao.saveUser(newUser);
|
||||
tm().put(builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,24 +16,24 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.model.console.User.grantIapPermission;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Command to create a new User. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a user account")
|
||||
public class CreateUserCommand extends CreateOrUpdateUserCommand {
|
||||
public class CreateUserCommand extends CreateOrUpdateUserCommand implements CommandWithConnection {
|
||||
|
||||
private ServiceConnection connection;
|
||||
|
||||
@Inject IamClient iamClient;
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject
|
||||
@Config("gSuiteConsoleUserGroupEmailAddress")
|
||||
Optional<String> maybeGroupEmailAddress;
|
||||
@@ -41,14 +41,20 @@ public class CreateUserCommand extends CreateOrUpdateUserCommand {
|
||||
@Nullable
|
||||
@Override
|
||||
User getExistingUser(String email) {
|
||||
checkArgument(UserDao.loadUser(email).isEmpty(), "A user with email %s already exists", email);
|
||||
checkArgument(
|
||||
!tm().exists(VKey.create(User.class, email)), "A user with email %s already exists", email);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String execute() throws Exception {
|
||||
String ret = super.execute();
|
||||
grantIapPermission(email, maybeGroupEmailAddress, cloudTasksUtils, iamClient);
|
||||
grantIapPermission(email, maybeGroupEmailAddress, null, connection, iamClient);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConnection(ServiceConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,28 +14,27 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.api.client.util.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentNotNull;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Deletes a {@link User}. */
|
||||
@Parameters(separators = " =", commandDescription = "Delete a user account")
|
||||
public class DeleteUserCommand extends ConfirmingCommand {
|
||||
public class DeleteUserCommand extends ConfirmingCommand implements CommandWithConnection {
|
||||
|
||||
private ServiceConnection connection;
|
||||
|
||||
@Inject IamClient iamClient;
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject
|
||||
@Config("gSuiteConsoleUserGroupEmailAddress")
|
||||
Optional<String> maybeGroupEmailAddress;
|
||||
@@ -47,7 +46,9 @@ public class DeleteUserCommand extends ConfirmingCommand {
|
||||
@Override
|
||||
protected String prompt() {
|
||||
checkArgumentNotNull(email, "Email must be provided");
|
||||
checkArgumentPresent(UserDao.loadUser(email), "Email does not correspond to a valid user");
|
||||
checkArgument(
|
||||
tm().transact(() -> tm().exists(VKey.create(User.class, email))),
|
||||
"Email does not correspond to a valid user");
|
||||
return String.format("Delete user with email %s?", email);
|
||||
}
|
||||
|
||||
@@ -55,11 +56,16 @@ public class DeleteUserCommand extends ConfirmingCommand {
|
||||
protected String execute() throws Exception {
|
||||
tm().transact(
|
||||
() -> {
|
||||
Optional<User> optionalUser = UserDao.loadUser(email);
|
||||
checkArgumentPresent(optionalUser, "Email no longer corresponds to a valid user");
|
||||
tm().delete(optionalUser.get());
|
||||
VKey<User> key = VKey.create(User.class, email);
|
||||
checkArgument(tm().exists(key), "Email no longer corresponds to a valid user");
|
||||
tm().delete(key);
|
||||
});
|
||||
User.revokeIapPermission(email, maybeGroupEmailAddress, cloudTasksUtils, iamClient);
|
||||
User.revokeIapPermission(email, maybeGroupEmailAddress, null, connection, iamClient);
|
||||
return String.format("Deleted user with email %s", email);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConnection(ServiceConnection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -293,7 +293,7 @@ class GenerateAllocationTokensCommand implements Command {
|
||||
// tokens should only be scheduled to end with a brief time period before the status
|
||||
// transition occurs so that no new domains are registered using that token between when the
|
||||
// status is scheduled and when the transition occurs.
|
||||
// TODO(@sarahbot): Create a cleaner way to handle ending bulk pricing packages once we
|
||||
// TODO(b/261763205): Create a cleaner way to handle ending bulk pricing packages once we
|
||||
// actually have customers using them
|
||||
boolean hasEnding =
|
||||
tokenStatusTransitions.containsValue(TokenStatus.ENDED)
|
||||
|
||||
@@ -14,10 +14,12 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
|
||||
import com.beust.jcommander.Parameter;
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.persistence.VKey;
|
||||
import java.util.List;
|
||||
|
||||
/** Command to display one or more users. */
|
||||
@@ -29,11 +31,14 @@ public class GetUserCommand implements Command {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
for (String emailAddress : mainParameters) {
|
||||
System.out.println(
|
||||
UserDao.loadUser(emailAddress)
|
||||
.map(User::toString)
|
||||
.orElse(String.format("No user with email address %s", emailAddress)));
|
||||
}
|
||||
tm().transact(
|
||||
() -> {
|
||||
for (String emailAddress : mainParameters) {
|
||||
System.out.println(
|
||||
tm().loadByKeyIfPresent(VKey.create(User.class, emailAddress))
|
||||
.map(User::toString)
|
||||
.orElse(String.format("No user with email address %s", emailAddress)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -87,12 +86,6 @@ final class RegistrarPocCommand extends MutatingCommand {
|
||||
+ " and will be used as the console login email, if --login_email is not specified.")
|
||||
String email;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--login_email",
|
||||
description = "Console login email address. If not specified, --email will be used.")
|
||||
String loginEmail;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--registry_lock_email",
|
||||
@@ -115,13 +108,6 @@ final class RegistrarPocCommand extends MutatingCommand {
|
||||
validateWith = OptionalPhoneNumberParameter.class)
|
||||
private Optional<String> fax;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--allow_console_access",
|
||||
description = "Enable or disable access to the registrar console for this contact.",
|
||||
arity = 1)
|
||||
Boolean allowConsoleAccess;
|
||||
|
||||
@Nullable
|
||||
@Parameter(
|
||||
names = "--visible_in_whois_as_admin",
|
||||
@@ -173,7 +159,7 @@ final class RegistrarPocCommand extends MutatingCommand {
|
||||
protected void init() throws Exception {
|
||||
checkArgument(mainParameters.size() == 1,
|
||||
"Must specify exactly one client identifier: %s", ImmutableList.copyOf(mainParameters));
|
||||
String clientId = mainParameters.get(0);
|
||||
String clientId = mainParameters.getFirst();
|
||||
Registrar registrar =
|
||||
checkArgumentPresent(
|
||||
Registrar.loadByRegistrarId(clientId), "Registrar %s not found", clientId);
|
||||
@@ -261,9 +247,6 @@ final class RegistrarPocCommand extends MutatingCommand {
|
||||
}
|
||||
builder.setTypes(nullToEmpty(contactTypes));
|
||||
|
||||
if (Objects.equals(allowConsoleAccess, Boolean.TRUE)) {
|
||||
builder.setLoginEmailAddress(loginEmail == null ? email : loginEmail);
|
||||
}
|
||||
if (visibleInWhoisAsAdmin != null) {
|
||||
builder.setVisibleInWhoisAsAdmin(visibleInWhoisAsAdmin);
|
||||
}
|
||||
@@ -308,13 +291,6 @@ final class RegistrarPocCommand extends MutatingCommand {
|
||||
if (visibleInDomainWhoisAsAbuse != null) {
|
||||
builder.setVisibleInDomainWhoisAsAbuse(visibleInDomainWhoisAsAbuse);
|
||||
}
|
||||
if (allowConsoleAccess != null) {
|
||||
if (allowConsoleAccess.equals(Boolean.TRUE)) {
|
||||
builder.setLoginEmailAddress(loginEmail == null ? email : loginEmail);
|
||||
} else {
|
||||
builder.setLoginEmailAddress(null);
|
||||
}
|
||||
}
|
||||
if (allowedToSetRegistryLockPassword != null) {
|
||||
builder.setAllowedToSetRegistryLockPassword(allowedToSetRegistryLockPassword);
|
||||
}
|
||||
|
||||
@@ -14,20 +14,20 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.util.PreconditionsUtils.checkArgumentPresent;
|
||||
|
||||
import com.beust.jcommander.Parameters;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import javax.annotation.Nullable;
|
||||
import google.registry.persistence.VKey;
|
||||
|
||||
/** Updates a user, assuming that the user in question already exists. */
|
||||
@Parameters(separators = " =", commandDescription = "Update a user account")
|
||||
public class UpdateUserCommand extends CreateOrUpdateUserCommand {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
User getExistingUser(String email) {
|
||||
return checkArgumentPresent(UserDao.loadUser(email), "User %s not found", email);
|
||||
return checkArgumentPresent(
|
||||
tm().loadByKeyIfPresent(VKey.create(User.class, email)), "User %s not found", email);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,9 +193,6 @@ public final class RegistrarFormFields {
|
||||
public static final FormField<String, String> CONTACT_FAX_NUMBER_FIELD =
|
||||
FormFields.PHONE_NUMBER.asBuilderNamed("faxNumber").build();
|
||||
|
||||
public static final FormField<String, String> CONTACT_LOGIN_EMAIL_ADDRESS_FIELD =
|
||||
FormFields.NAME.asBuilderNamed("loginEmailAddress").build();
|
||||
|
||||
public static final FormField<Object, Boolean> CONTACT_ALLOWED_TO_SET_REGISTRY_LOCK_PASSWORD =
|
||||
FormField.named("allowedToSetRegistryLockPassword", Object.class)
|
||||
.transform(Boolean.class, b -> Boolean.valueOf(Objects.toString(b)))
|
||||
@@ -384,8 +381,6 @@ public final class RegistrarFormFields {
|
||||
builder.setPhoneNumber(CONTACT_PHONE_NUMBER_FIELD.extractUntyped(args).orElse(null));
|
||||
builder.setFaxNumber(CONTACT_FAX_NUMBER_FIELD.extractUntyped(args).orElse(null));
|
||||
builder.setTypes(CONTACT_TYPES.extractUntyped(args).orElse(ImmutableSet.of()));
|
||||
builder.setLoginEmailAddress(
|
||||
CONTACT_LOGIN_EMAIL_ADDRESS_FIELD.extractUntyped(args).orElse(null));
|
||||
// The parser is inconsistent with whether it retrieves boolean values as strings or booleans.
|
||||
// As a result, use a potentially-redundant converter that can deal with both.
|
||||
builder.setAllowedToSetRegistryLockPassword(
|
||||
|
||||
@@ -16,6 +16,9 @@ package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.NEW_CONSOLE;
|
||||
import static google.registry.model.common.FeatureFlag.isActiveNow;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
@@ -29,10 +32,12 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.export.sheet.SyncRegistrarsSheetAction;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
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.model.registrar.RegistrarPocBase;
|
||||
@@ -62,6 +67,10 @@ public abstract class ConsoleApiAction implements Runnable {
|
||||
|
||||
@Inject CloudTasksUtils cloudTasksUtils;
|
||||
|
||||
@Inject
|
||||
@RegistryConfig.Config("registryAdminClientId")
|
||||
String registryAdminClientId;
|
||||
|
||||
public ConsoleApiAction(ConsoleApiParams consoleApiParams) {
|
||||
this.consoleApiParams = consoleApiParams;
|
||||
}
|
||||
@@ -77,8 +86,15 @@ public abstract class ConsoleApiAction implements Runnable {
|
||||
|
||||
// This allows us to enable console to a selected cohort of users with release
|
||||
// We can ignore it in tests
|
||||
if (RegistryEnvironment.get() != RegistryEnvironment.UNITTEST
|
||||
&& GlobalRole.NONE.equals(user.getUserRoles().getGlobalRole())) {
|
||||
UserRoles userRoles = user.getUserRoles();
|
||||
boolean hasGlobalOrTestingRole =
|
||||
!GlobalRole.NONE.equals(userRoles.getGlobalRole())
|
||||
|| userRoles.hasPermission(
|
||||
registryAdminClientId, ConsolePermission.VIEW_REGISTRAR_DETAILS);
|
||||
|
||||
if (!hasGlobalOrTestingRole
|
||||
&& RegistryEnvironment.get() != RegistryEnvironment.UNITTEST
|
||||
&& tm().transact(() -> !isActiveNow(NEW_CONSOLE))) {
|
||||
try {
|
||||
consoleApiParams.response().sendRedirect(ConsoleUiAction.PATH);
|
||||
return;
|
||||
@@ -161,7 +177,7 @@ public abstract class ConsoleApiAction implements Runnable {
|
||||
|
||||
Map<String, Object> 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<String, Object> result = new LinkedHashMap<>(registrarDiffMap);
|
||||
|
||||
@@ -115,6 +115,7 @@ public class ConsoleDomainListAction extends ConsoleApiAction {
|
||||
.setFirstResult(numResultsToSkip)
|
||||
.setMaxResults(resultsPerPage)
|
||||
.getResultList();
|
||||
|
||||
consoleApiParams
|
||||
.response()
|
||||
.setPayload(gson.toJson(new DomainListResult(domains, checkpoint, actualTotalResults)));
|
||||
|
||||
+18
-10
@@ -19,16 +19,13 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
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 static google.registry.request.RequestParameters.extractBooleanParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalLongParameter;
|
||||
import static google.registry.request.RequestParameters.extractOptionalParameter;
|
||||
import static google.registry.request.RequestParameters.extractRequiredParameter;
|
||||
import static google.registry.ui.server.registrar.RegistryLockPostAction.VERIFICATION_EMAIL_TEMPLATE;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import google.registry.flows.EppException;
|
||||
import google.registry.flows.domain.DomainFlowUtils;
|
||||
import google.registry.groups.GmailClient;
|
||||
@@ -46,8 +43,8 @@ import google.registry.ui.server.registrar.ConsoleApiParams;
|
||||
import google.registry.util.EmailMessage;
|
||||
import jakarta.mail.internet.AddressException;
|
||||
import jakarta.mail.internet.InternetAddress;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import org.joda.time.Duration;
|
||||
|
||||
@@ -69,6 +66,7 @@ public class ConsoleRegistryLockAction extends ConsoleApiAction {
|
||||
private final DomainLockUtils domainLockUtils;
|
||||
private final GmailClient gmailClient;
|
||||
private final Gson gson;
|
||||
private final Optional<ConsoleRegistryLockPostInput> optionalPostInput;
|
||||
private final String registrarId;
|
||||
|
||||
@Inject
|
||||
@@ -77,11 +75,14 @@ public class ConsoleRegistryLockAction extends ConsoleApiAction {
|
||||
DomainLockUtils domainLockUtils,
|
||||
GmailClient gmailClient,
|
||||
Gson gson,
|
||||
@Parameter("consoleRegistryLockPostInput")
|
||||
Optional<ConsoleRegistryLockPostInput> optionalPostInput,
|
||||
@Parameter("registrarId") String registrarId) {
|
||||
super(consoleApiParams);
|
||||
this.domainLockUtils = domainLockUtils;
|
||||
this.gmailClient = gmailClient;
|
||||
this.gson = gson;
|
||||
this.optionalPostInput = optionalPostInput;
|
||||
this.registrarId = registrarId;
|
||||
}
|
||||
|
||||
@@ -94,7 +95,6 @@ public class ConsoleRegistryLockAction extends ConsoleApiAction {
|
||||
|
||||
@Override
|
||||
protected void postHandler(User user) {
|
||||
HttpServletRequest req = consoleApiParams.request();
|
||||
Response response = consoleApiParams.response();
|
||||
// User must have the proper permission on the registrar
|
||||
checkPermission(user, registrarId, ConsolePermission.REGISTRY_LOCK);
|
||||
@@ -107,10 +107,12 @@ public class ConsoleRegistryLockAction extends ConsoleApiAction {
|
||||
registrarId);
|
||||
|
||||
// Retrieve and validate the necessary params
|
||||
String domainName = extractRequiredParameter(req, "domainName");
|
||||
boolean isLock = extractBooleanParameter(req, "isLock");
|
||||
Optional<String> maybePassword = extractOptionalParameter(req, "password");
|
||||
Optional<Long> relockDurationMillis = extractOptionalLongParameter(req, "relockDurationMillis");
|
||||
ConsoleRegistryLockPostInput postInput =
|
||||
optionalPostInput.orElseThrow(() -> new IllegalArgumentException("No POST input provided"));
|
||||
String domainName = postInput.domainName();
|
||||
boolean isLock = postInput.isLock();
|
||||
Optional<String> maybePassword = Optional.ofNullable(postInput.password());
|
||||
Optional<Long> relockDurationMillis = Optional.ofNullable(postInput.relockDurationMillis());
|
||||
|
||||
try {
|
||||
DomainFlowUtils.validateDomainName(domainName);
|
||||
@@ -176,4 +178,10 @@ public class ConsoleRegistryLockAction extends ConsoleApiAction {
|
||||
.filter(lock -> !lock.isLockRequestExpired(tm().getTransactionTime()))
|
||||
.collect(toImmutableList()));
|
||||
}
|
||||
|
||||
public record ConsoleRegistryLockPostInput(
|
||||
@Expose String domainName,
|
||||
@Expose boolean isLock,
|
||||
@Expose @Nullable String password,
|
||||
@Expose @Nullable Long relockDurationMillis) {}
|
||||
}
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ public class ConsoleRegistryLockVerifyAction extends ConsoleApiAction {
|
||||
RegistryLock lock =
|
||||
domainLockUtils.verifyVerificationCode(lockVerificationCode, user.getUserRoles().isAdmin());
|
||||
RegistryLockAction action =
|
||||
lock.getLockCompletionTime().isPresent()
|
||||
lock.getUnlockCompletionTime().isPresent()
|
||||
? RegistryLockAction.UNLOCKED
|
||||
: RegistryLockAction.LOCKED;
|
||||
RegistryLockVerificationResponse lockResponse =
|
||||
|
||||
@@ -16,6 +16,7 @@ package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static com.google.common.collect.ImmutableSet.toImmutableSet;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
@@ -23,6 +24,7 @@ import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.ConsolePermission;
|
||||
@@ -36,6 +38,8 @@ import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
import google.registry.ui.server.registrar.ConsoleApiParams;
|
||||
import google.registry.util.StringGenerator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
@@ -50,6 +54,11 @@ public class RegistrarsAction extends ConsoleApiAction {
|
||||
private static final int PASSCODE_LENGTH = 5;
|
||||
private static final ImmutableList<RegistrarBase.Type> allowedRegistrarTypes =
|
||||
ImmutableList.of(Registrar.Type.REAL, RegistrarBase.Type.OTE);
|
||||
private static final String SQL_TEMPLATE =
|
||||
"""
|
||||
SELECT * FROM "Registrar"
|
||||
WHERE registrar_id in :registrarIds
|
||||
""";
|
||||
static final String PATH = "/console-api/registrars";
|
||||
private final Gson gson;
|
||||
private final Optional<Registrar> registrar;
|
||||
@@ -72,18 +81,34 @@ public class RegistrarsAction extends ConsoleApiAction {
|
||||
|
||||
@Override
|
||||
protected void getHandler(User user) {
|
||||
if (!user.getUserRoles().hasGlobalPermission(ConsolePermission.VIEW_REGISTRARS)) {
|
||||
if (user.getUserRoles().hasGlobalPermission(ConsolePermission.VIEW_REGISTRARS)) {
|
||||
ImmutableList<Registrar> registrars =
|
||||
Streams.stream(Registrar.loadAll())
|
||||
.filter(r -> allowedRegistrarTypes.contains(r.getType()))
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
consoleApiParams.response().setPayload(gson.toJson(registrars));
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
} else if (user.getUserRoles().getRegistrarRoles().values().stream()
|
||||
.anyMatch(role -> role.hasPermission(ConsolePermission.VIEW_REGISTRAR_DETAILS))) {
|
||||
ImmutableSet<String> accessibleRegistrarIds =
|
||||
user.getUserRoles().getRegistrarRoles().entrySet().stream()
|
||||
.filter(e -> e.getValue().hasPermission(ConsolePermission.VIEW_REGISTRAR_DETAILS))
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(toImmutableSet());
|
||||
|
||||
List<Registrar> registrars =
|
||||
tm().transact(
|
||||
() ->
|
||||
tm().getEntityManager()
|
||||
.createNativeQuery(SQL_TEMPLATE, Registrar.class)
|
||||
.setParameter("registrarIds", accessibleRegistrarIds)
|
||||
.getResultList());
|
||||
|
||||
consoleApiParams.response().setPayload(gson.toJson(registrars));
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
} else {
|
||||
consoleApiParams.response().setStatus(SC_FORBIDDEN);
|
||||
return;
|
||||
}
|
||||
|
||||
ImmutableList<Registrar> registrars =
|
||||
Streams.stream(Registrar.loadAll())
|
||||
.filter(r -> allowedRegistrarTypes.contains(r.getType()))
|
||||
.collect(ImmutableList.toImmutableList());
|
||||
|
||||
consoleApiParams.response().setPayload(gson.toJson(registrars));
|
||||
consoleApiParams.response().setStatus(SC_OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,7 +164,6 @@ public class RegistrarsAction extends ConsoleApiAction {
|
||||
.setRegistrar(registrar)
|
||||
.setName(registrarParam.getEmailAddress())
|
||||
.setEmailAddress(registrarParam.getEmailAddress())
|
||||
.setLoginEmailAddress(registrarParam.getEmailAddress())
|
||||
.build();
|
||||
|
||||
tm().transact(
|
||||
@@ -151,4 +175,5 @@ public class RegistrarsAction extends ConsoleApiAction {
|
||||
tm().putAll(registrar, contact);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+15
-2
@@ -17,6 +17,7 @@ package google.registry.ui.server.console.settings;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
|
||||
@@ -30,6 +31,7 @@ import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
|
||||
import google.registry.ui.server.console.ConsoleApiAction;
|
||||
import google.registry.ui.server.registrar.ConsoleApiParams;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -65,10 +67,10 @@ public class WhoisRegistrarFieldsAction extends ConsoleApiAction {
|
||||
checkArgument(registrar.isPresent(), "'registrar' parameter is not present");
|
||||
checkPermission(
|
||||
user, registrar.get().getRegistrarId(), ConsolePermission.EDIT_REGISTRAR_DETAILS);
|
||||
tm().transact(() -> loadAndModifyRegistrar(registrar.get()));
|
||||
tm().transact(() -> loadAndModifyRegistrar(registrar.get(), user));
|
||||
}
|
||||
|
||||
private void loadAndModifyRegistrar(Registrar providedRegistrar) {
|
||||
private void loadAndModifyRegistrar(Registrar providedRegistrar, User user) {
|
||||
Registrar savedRegistrar;
|
||||
try {
|
||||
// reload to make sure the object has all the correct fields
|
||||
@@ -78,6 +80,15 @@ public class WhoisRegistrarFieldsAction extends ConsoleApiAction {
|
||||
return;
|
||||
}
|
||||
|
||||
// icannReferralEmail can't be updated by partners, only by global users with
|
||||
// EDIT_REGISTRAR_DETAILS permission
|
||||
if (!Objects.equals(
|
||||
savedRegistrar.getIcannReferralEmail(), providedRegistrar.getIcannReferralEmail())
|
||||
&& !user.getUserRoles().hasGlobalPermission(ConsolePermission.EDIT_REGISTRAR_DETAILS)) {
|
||||
setFailedResponse(
|
||||
"Icann Referral Email update is not permitted for this user", SC_BAD_REQUEST);
|
||||
}
|
||||
|
||||
Registrar newRegistrar =
|
||||
savedRegistrar
|
||||
.asBuilder()
|
||||
@@ -86,6 +97,8 @@ public class WhoisRegistrarFieldsAction extends ConsoleApiAction {
|
||||
.setLocalizedAddress(providedRegistrar.getLocalizedAddress())
|
||||
.setPhoneNumber(providedRegistrar.getPhoneNumber())
|
||||
.setFaxNumber(providedRegistrar.getFaxNumber())
|
||||
.setIcannReferralEmail(providedRegistrar.getIcannReferralEmail())
|
||||
.setEmailAddress(providedRegistrar.getEmailAddress())
|
||||
.build();
|
||||
tm().put(newRegistrar);
|
||||
sendExternalUpdatesIfNecessary(
|
||||
|
||||
+2
-3
@@ -30,7 +30,6 @@ import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarAddress;
|
||||
@@ -260,10 +259,10 @@ public final class ConsoleRegistrarCreatorAction extends HtmlAction {
|
||||
"Registrar with client ID %s already exists",
|
||||
registrar.getRegistrarId());
|
||||
tm().put(registrar);
|
||||
tm().put(user);
|
||||
});
|
||||
UserDao.saveUser(user);
|
||||
User.grantIapPermission(
|
||||
user.getEmailAddress(), maybeGroupEmailAddress, cloudTasksUtils, iamClient);
|
||||
user.getEmailAddress(), maybeGroupEmailAddress, cloudTasksUtils, null, iamClient);
|
||||
data.put("password", password);
|
||||
data.put("passcode", phonePasscode);
|
||||
|
||||
|
||||
@@ -14,10 +14,13 @@
|
||||
|
||||
package google.registry.ui.server.registrar;
|
||||
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.auth.AuthenticatedRegistrarAccessor.Role.ADMIN;
|
||||
import static google.registry.request.auth.AuthenticatedRegistrarAccessor.Role.OWNER;
|
||||
import static google.registry.ui.server.SoyTemplateUtils.CSS_RENAMING_MAP_SUPPLIER;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_PERMANENT_REDIRECT;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
@@ -26,6 +29,8 @@ import com.google.common.flogger.FluentLogger;
|
||||
import com.google.template.soy.data.SoyMapData;
|
||||
import com.google.template.soy.tofu.SoyTofu;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.Parameter;
|
||||
import google.registry.request.auth.Auth;
|
||||
@@ -35,8 +40,10 @@ import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role;
|
||||
import google.registry.ui.server.SoyTemplateUtils;
|
||||
import google.registry.ui.soy.registrar.ConsoleSoyInfo;
|
||||
import google.registry.util.RegistryEnvironment;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** Action that serves Registrar Console single HTML page (SPA). */
|
||||
@@ -100,6 +107,7 @@ public final class ConsoleUiAction extends HtmlAction {
|
||||
soyMapData.put("announcementsEmail", announcementsEmail);
|
||||
soyMapData.put("supportPhoneNumber", supportPhoneNumber);
|
||||
soyMapData.put("technicalDocsUrl", technicalDocsUrl);
|
||||
|
||||
if (!enabled) {
|
||||
response.setStatus(SC_SERVICE_UNAVAILABLE);
|
||||
response.setPayload(
|
||||
@@ -111,9 +119,35 @@ public final class ConsoleUiAction extends HtmlAction {
|
||||
.render());
|
||||
return;
|
||||
}
|
||||
|
||||
// Set permanent redirect to the new console for tech support
|
||||
if (isNullOrEmpty(req.getParameter("redirect"))
|
||||
&& Stream.of(GlobalRole.SUPPORT_LEAD, GlobalRole.SUPPORT_AGENT)
|
||||
.anyMatch(
|
||||
globalRole ->
|
||||
globalRole.equals(authResult.user().get().getUserRoles().getGlobalRole()))) {
|
||||
response.setStatus(SC_PERMANENT_REDIRECT);
|
||||
try {
|
||||
response.sendRedirect("/console");
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
ImmutableSetMultimap<String, Role> roleMap = registrarAccessor.getAllRegistrarIdsWithRoles();
|
||||
soyMapData.put("allClientIds", roleMap.keySet());
|
||||
soyMapData.put("environment", RegistryEnvironment.get().toString());
|
||||
boolean newConsole =
|
||||
tm().transact(
|
||||
() ->
|
||||
FeatureFlag.getUncached(FeatureFlag.FeatureName.NEW_CONSOLE)
|
||||
.map(
|
||||
flag ->
|
||||
flag.getStatus(tm().getTransactionTime())
|
||||
.equals(FeatureFlag.FeatureStatus.ACTIVE))
|
||||
.orElse(false));
|
||||
soyMapData.put("includeDeprecationWarning", newConsole);
|
||||
// We set the initial value to the value that will show if guessClientId throws.
|
||||
String clientId = "<null>";
|
||||
try {
|
||||
|
||||
@@ -34,6 +34,7 @@ import google.registry.request.auth.AuthResult;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.ui.server.SendEmailUtils;
|
||||
import google.registry.ui.server.console.ConsoleEppPasswordAction.EppPasswordData;
|
||||
import google.registry.ui.server.console.ConsoleRegistryLockAction.ConsoleRegistryLockPostInput;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.Optional;
|
||||
import org.joda.time.DateTime;
|
||||
@@ -242,4 +243,11 @@ public final class RegistrarConsoleModule {
|
||||
Gson gson, @OptionalJsonPayload Optional<JsonElement> payload) {
|
||||
return payload.map(s -> gson.fromJson(s, EppPasswordData.class));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Parameter("consoleRegistryLockPostInput")
|
||||
public static Optional<ConsoleRegistryLockPostInput> provideRegistryLockPostInput(
|
||||
Gson gson, @OptionalJsonPayload Optional<JsonElement> payload) {
|
||||
return payload.map(e -> gson.fromJson(e, ConsoleRegistryLockPostInput.class));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ li.kd-menulistitem {
|
||||
top: 136px;
|
||||
left: 173px;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
width: calc(100% - 173px);
|
||||
margin: 0;
|
||||
padding: 25px 0 1em 0;
|
||||
overflow-y: scroll !important;
|
||||
@@ -296,3 +296,31 @@ li.kd-menulistitem {
|
||||
.reg-select {
|
||||
margin-left: 23px;
|
||||
}
|
||||
|
||||
div#reg-deprecation-warning {
|
||||
position: fixed;
|
||||
top: 64px;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
background-color: #ba2a1a;
|
||||
font-size: 0.6svw;
|
||||
}
|
||||
|
||||
div#reg-deprecation-warning h1 {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
div#reg-deprecation-warning a {
|
||||
color: #add8e6;
|
||||
}
|
||||
|
||||
div#reg-accessing-as-role {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1600px) {
|
||||
div#reg-deprecation-warning {
|
||||
font-size: 0.85vw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
{@param technicalDocsUrl: string}
|
||||
{@param environment: string}
|
||||
{@param analyticsConfig: [googleAnalyticsId: string|null]}
|
||||
{@param includeDeprecationWarning: bool}
|
||||
|
||||
{call registry.soy.console.header}
|
||||
{param app: 'registrar' /}
|
||||
@@ -45,7 +46,7 @@
|
||||
{call registry.soy.console.googlebar data="all" /}
|
||||
<div id="reg-app" lang="en-US">
|
||||
<div id="reg-appbar" class="{css('kd-appbar')}">
|
||||
<div class="{css('kd-description')}">
|
||||
<div id="reg-accessing-as-role" class="{css('kd-description')}">
|
||||
Accessing <span class="{css('kd-value')}">{$clientId}</span> as{sp}
|
||||
{if $isOwner}<span class="{css('kd-value')}">Owner</span>{/if}
|
||||
{if $isAdmin}<span class="{css('kd-value')}">Admin</span>{/if}
|
||||
@@ -53,6 +54,13 @@
|
||||
{sp}(Switch registrar: {call .clientIdSelect_ data="all" /})
|
||||
{/if}
|
||||
</div>
|
||||
{if $includeDeprecationWarning}
|
||||
<div id="reg-deprecation-warning">
|
||||
<h1>Note: this console is deprecated and will be deactivated in October 2024.
|
||||
Please use the <a href="/console">new console</a> instead.
|
||||
</h1>
|
||||
</div>
|
||||
{/if}
|
||||
<div id="reg-app-buttons" class="{css('kd-buttonbar')} {css('left')}"></div>
|
||||
</div>
|
||||
{call .navbar_ data="all" /}
|
||||
|
||||
@@ -172,7 +172,6 @@ public class SyncRegistrarsSheetTest {
|
||||
// distinction to make sure we're not relying on it. Sigh.
|
||||
.setVisibleInWhoisAsAdmin(false)
|
||||
.setVisibleInWhoisAsTech(true)
|
||||
.setLoginEmailAddress("john.doe@example.tld")
|
||||
.build(),
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
@@ -191,7 +190,7 @@ public class SyncRegistrarsSheetTest {
|
||||
ImmutableList<ImmutableMap<String, String>> rows = getOnlyElement(rowsCaptor.getAllValues());
|
||||
assertThat(rows).hasSize(2);
|
||||
|
||||
ImmutableMap<String, String> row = rows.get(0);
|
||||
ImmutableMap<String, String> row = rows.getFirst();
|
||||
assertThat(row).containsEntry("registrarId", "aaaregistrar");
|
||||
assertThat(row).containsEntry("registrarName", "AAA Registrar Inc.");
|
||||
assertThat(row).containsEntry("state", "SUSPENDED");
|
||||
@@ -208,7 +207,6 @@ public class SyncRegistrarsSheetTest {
|
||||
Visible in registrar WHOIS query as Technical contact: No
|
||||
Phone number and email visible in domain WHOIS query as Registrar Abuse contact\
|
||||
info: No
|
||||
Registrar-Console access: No
|
||||
|
||||
John Doe
|
||||
john.doe@example.tld
|
||||
@@ -219,8 +217,6 @@ public class SyncRegistrarsSheetTest {
|
||||
Visible in registrar WHOIS query as Technical contact: Yes
|
||||
Phone number and email visible in domain WHOIS query as Registrar Abuse contact\
|
||||
info: No
|
||||
Registrar-Console access: Yes
|
||||
Login Email Address: john.doe@example.tld
|
||||
""");
|
||||
assertThat(row)
|
||||
.containsEntry(
|
||||
@@ -233,7 +229,6 @@ public class SyncRegistrarsSheetTest {
|
||||
Visible in registrar WHOIS query as Technical contact: No
|
||||
Phone number and email visible in domain WHOIS query as Registrar Abuse contact\
|
||||
info: No
|
||||
Registrar-Console access: No
|
||||
""");
|
||||
assertThat(row).containsEntry("marketingContacts", "");
|
||||
assertThat(row).containsEntry("abuseContacts", "");
|
||||
@@ -251,7 +246,6 @@ public class SyncRegistrarsSheetTest {
|
||||
Visible in registrar WHOIS query as Technical contact: No
|
||||
Phone number and email visible in domain WHOIS query as Registrar Abuse contact\
|
||||
info: No
|
||||
Registrar-Console access: No
|
||||
""");
|
||||
assertThat(row).containsEntry("contactsMarkedAsWhoisAdmin", "");
|
||||
assertThat(row)
|
||||
@@ -267,8 +261,6 @@ public class SyncRegistrarsSheetTest {
|
||||
Visible in registrar WHOIS query as Technical contact: Yes
|
||||
Phone number and email visible in domain WHOIS query as Registrar Abuse contact\
|
||||
info: No
|
||||
Registrar-Console access: Yes
|
||||
Login Email Address: john.doe@example.tld
|
||||
""");
|
||||
assertThat(row).containsEntry("emailAddress", "nowhere@example.org");
|
||||
assertThat(row).containsEntry(
|
||||
|
||||
@@ -16,6 +16,8 @@ package google.registry.flows;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_AND_CLOSE;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS_WITH_ACTION_PENDING;
|
||||
@@ -40,6 +42,7 @@ import com.google.re2j.Matcher;
|
||||
import com.google.re2j.Pattern;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.reporting.HistoryEntry.Type;
|
||||
@@ -73,6 +76,12 @@ class EppLifecycleDomainTest extends EppTestCase {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE))
|
||||
.build());
|
||||
createTlds("example", "tld");
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,19 @@ package google.registry.flows;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.model.eppoutput.Result.Code.SUCCESS;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.EppMetricSubject.assertThat;
|
||||
import static google.registry.testing.HostSubject.assertAboutHosts;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.host.Host;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
@@ -88,6 +94,12 @@ class EppLifecycleHostTest extends EppTestCase {
|
||||
|
||||
@Test
|
||||
void testRenamingHostToExistingHost_fails() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE))
|
||||
.build());
|
||||
createTld("example");
|
||||
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
|
||||
// Create the fakesite domain.
|
||||
@@ -138,6 +150,12 @@ class EppLifecycleHostTest extends EppTestCase {
|
||||
|
||||
@Test
|
||||
void testSuccess_multipartTldsWithSharedSuffixes() throws Exception {
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE))
|
||||
.build());
|
||||
createTlds("bar.foo.tld", "foo.tld", "tld");
|
||||
|
||||
assertThatLoginSucceeds("NewRegistrar", "foo-BAR2");
|
||||
|
||||
@@ -17,18 +17,24 @@ package google.registry.flows;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadAtPointInTime;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveContact;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.joda.time.Duration.standardDays;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import google.registry.flows.EppTestComponent.FakesAndMocksModule;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.monitoring.whitebox.EppMetric;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
@@ -54,6 +60,12 @@ class EppPointInTimeTest {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE))
|
||||
.build());
|
||||
createTld("tld");
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ import static google.registry.model.billing.BillingBase.Flag.SUNRISE;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.DEFAULT;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.NONPREMIUM;
|
||||
import static google.registry.model.billing.BillingBase.RenewalPriceBehavior.SPECIFIED;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.model.domain.fee.Fee.FEE_EXTENSION_URIS;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.BULK_PRICING;
|
||||
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
|
||||
@@ -156,6 +159,7 @@ import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingBase.RenewalPriceBehavior;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.domain.GracePeriod;
|
||||
@@ -224,7 +228,7 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
|
||||
DomainCreateFlowTest() {
|
||||
setEppInput("domain_create.xml", ImmutableMap.of("DOMAIN", "example.tld"));
|
||||
clock.setTo(DateTime.parse("1999-04-03T22:00:00.0Z").minus(Duration.millis(1)));
|
||||
clock.setTo(DateTime.parse("1999-04-03T22:00:00.0Z").minus(Duration.millis(2)));
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@@ -250,6 +254,12 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
"badcrash,NAME_COLLISION"),
|
||||
persistReservedList("global-list", "resdom,FULLY_BLOCKED"))
|
||||
.build());
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE))
|
||||
.build());
|
||||
persistClaimsList(ImmutableMap.of("example-one", CLAIMS_KEY, "test-validate", CLAIMS_KEY));
|
||||
}
|
||||
|
||||
@@ -2153,6 +2163,20 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingRegistrant() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_missing_registrant.xml");
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_missingAdmin() {
|
||||
setEppInput("domain_create_missing_admin.xml");
|
||||
@@ -2161,6 +2185,20 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingAdmin() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_missing_admin.xml");
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_missingTech() {
|
||||
setEppInput("domain_create_missing_tech.xml");
|
||||
@@ -2169,6 +2207,20 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingTech() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_missing_tech.xml");
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_missingNonRegistrantContacts() {
|
||||
setEppInput("domain_create_missing_non_registrant_contacts.xml");
|
||||
@@ -2177,6 +2229,20 @@ class DomainCreateFlowTest extends ResourceFlowTestCase<DomainCreateFlow, Domain
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_missingNonRegistrantContacts() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_create_missing_non_registrant_contacts.xml");
|
||||
persistContactsAndHosts();
|
||||
runFlowAssertResponse(
|
||||
loadFile("domain_create_response.xml", ImmutableMap.of("DOMAIN", "example.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badIdn() {
|
||||
createTld("xn--q9jyb4c");
|
||||
|
||||
@@ -19,6 +19,9 @@ import static com.google.common.collect.Sets.union;
|
||||
import static com.google.common.io.BaseEncoding.base16;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_DELETE_PROHIBITED;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_HOLD;
|
||||
import static google.registry.model.eppcommon.StatusValue.CLIENT_RENEW_PROHIBITED;
|
||||
@@ -96,6 +99,7 @@ import google.registry.flows.exceptions.ResourceStatusProhibitsOperationExceptio
|
||||
import google.registry.model.ImmutableObject;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.domain.DesignatedContact;
|
||||
import google.registry.model.domain.DesignatedContact.Type;
|
||||
@@ -140,6 +144,12 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
createTld("tld");
|
||||
// Note that "domain_update.xml" tests adding and removing the same contact type.
|
||||
setEppInput("domain_update.xml");
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE))
|
||||
.build());
|
||||
}
|
||||
|
||||
private void persistReferencedEntities() {
|
||||
@@ -289,6 +299,21 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_emptyRegistrant() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_update_empty_registrant.xml");
|
||||
persistReferencedEntities();
|
||||
persistDomain();
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
assertThat(reloadResourceByForeignKey().getRegistrant()).isEmpty();
|
||||
}
|
||||
|
||||
private void modifyDomainToHave13Nameservers() throws Exception {
|
||||
ImmutableSet.Builder<VKey<Host>> nameservers = new ImmutableSet.Builder<>();
|
||||
for (int i = 1; i < 15; i++) {
|
||||
@@ -1478,6 +1503,27 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_removeAdmin() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_update_remove_admin.xml");
|
||||
persistReferencedEntities();
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
|
||||
.build());
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_removeTech() throws Exception {
|
||||
setEppInput("domain_update_remove_tech.xml");
|
||||
@@ -1494,6 +1540,27 @@ class DomainUpdateFlowTest extends ResourceFlowTestCase<DomainUpdateFlow, Domain
|
||||
assertAboutEppExceptions().that(thrown).marshalsToXml();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_minimumDatasetPhase1_removeTech() throws Exception {
|
||||
persistResource(
|
||||
FeatureFlag.get(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.asBuilder()
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.of(START_OF_TIME, INACTIVE, clock.nowUtc().minusDays(5), ACTIVE))
|
||||
.build());
|
||||
setEppInput("domain_update_remove_tech.xml");
|
||||
persistReferencedEntities();
|
||||
persistResource(
|
||||
DatabaseHelper.newDomain(getUniqueIdFromCommand())
|
||||
.asBuilder()
|
||||
.setContacts(
|
||||
ImmutableSet.of(
|
||||
DesignatedContact.create(Type.ADMIN, sh8013Contact.createVKey()),
|
||||
DesignatedContact.create(Type.TECH, sh8013Contact.createVKey())))
|
||||
.build());
|
||||
runFlowAssertResponse(loadFile("generic_success_response.xml"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_addPendingDeleteContact() throws Exception {
|
||||
persistReferencedEntities();
|
||||
|
||||
@@ -22,6 +22,7 @@ import static google.registry.persistence.transaction.JpaTransactionManagerExten
|
||||
import static google.registry.testing.CertificateSamples.SAMPLE_CERT;
|
||||
import static google.registry.testing.CertificateSamples.SAMPLE_CERT_HASH;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadByKeyIfPresent;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static google.registry.testing.DatabaseHelper.persistSimpleResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
@@ -36,10 +37,10 @@ import com.google.common.collect.ImmutableList;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.tld.Tld;
|
||||
import google.registry.model.tld.Tld.TldState;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
@@ -106,7 +107,7 @@ public final class OteAccountBuilderTest {
|
||||
}
|
||||
|
||||
public static void verifyUser(String registrarId, String email) {
|
||||
Optional<User> maybeUser = UserDao.loadUser(email);
|
||||
Optional<User> maybeUser = loadByKeyIfPresent(VKey.create(User.class, email));
|
||||
assertThat(maybeUser).isPresent();
|
||||
assertThat(maybeUser.get().getUserRoles().getRegistrarRoles().get(registrarId))
|
||||
.isEqualTo(RegistrarRole.ACCOUNT_MANAGER);
|
||||
|
||||
@@ -20,6 +20,7 @@ import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATAS
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.TEST_FEATURE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.ACTIVE;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
@@ -185,4 +186,21 @@ public class FeatureFlagTest extends EntityTestCase {
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Must provide transition entry for the start of time (Unix Epoch)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_isActiveNow() {
|
||||
fakeClock.setTo(DateTime.parse("2010-10-17TZ"));
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(TEST_FEATURE)
|
||||
.setStatusMap(
|
||||
ImmutableSortedMap.<DateTime, FeatureStatus>naturalOrder()
|
||||
.put(START_OF_TIME, INACTIVE)
|
||||
.put(fakeClock.nowUtc().plusWeeks(8), ACTIVE)
|
||||
.build())
|
||||
.build());
|
||||
assertThat(tm().transact(() -> FeatureFlag.isActiveNow(TEST_FEATURE))).isFalse();
|
||||
fakeClock.setTo(DateTime.parse("2011-10-17TZ"));
|
||||
assertThat(tm().transact(() -> FeatureFlag.isActiveNow(TEST_FEATURE))).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
// Copyright 2022 The Nomulus Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package google.registry.model.console;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.ImmutableObjectSubject.assertAboutImmutableObjects;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import google.registry.model.EntityTestCase;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for {@link UserDao}. */
|
||||
public class UserDaoTest extends EntityTestCase {
|
||||
|
||||
@Test
|
||||
void testSuccess_saveAndRetrieve() {
|
||||
User user1 =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
|
||||
.build();
|
||||
User user2 =
|
||||
new User.Builder()
|
||||
.setEmailAddress("foo@bar.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
|
||||
.build();
|
||||
UserDao.saveUser(user1);
|
||||
UserDao.saveUser(user2);
|
||||
assertAboutImmutableObjects()
|
||||
.that(user1)
|
||||
.isEqualExceptFields(UserDao.loadUser("email@email.com").get(), "id", "updateTimestamp");
|
||||
assertAboutImmutableObjects()
|
||||
.that(user2)
|
||||
.isEqualExceptFields(UserDao.loadUser("foo@bar.com").get(), "id", "updateTimestamp");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_absentUser() {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
|
||||
.build();
|
||||
UserDao.saveUser(user);
|
||||
User fromDb = UserDao.loadUser("email@email.com").get();
|
||||
// nonexistent one should never exist
|
||||
assertThat(UserDao.loadUser("nonexistent@email.com")).isEmpty();
|
||||
// now try deleting the one that does exist
|
||||
tm().transact(() -> tm().delete(fromDb));
|
||||
assertThat(UserDao.loadUser("email@email.com")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_sameEmail() {
|
||||
User user1 =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
|
||||
.build();
|
||||
User user2 =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
|
||||
.build();
|
||||
UserDao.saveUser(user1);
|
||||
assertThrows(IllegalArgumentException.class, () -> UserDao.saveUser(user2));
|
||||
assertAboutImmutableObjects()
|
||||
.that(user1)
|
||||
.isEqualExceptFields(UserDao.loadUser("email@email.com").get(), "id", "updateTimestamp");
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,20 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.batch.CloudTasksUtils;
|
||||
import google.registry.model.EntityTestCase;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.tools.IamClient;
|
||||
import google.registry.tools.ServiceConnection;
|
||||
import google.registry.tools.server.UpdateUserGroupAction;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -57,6 +62,34 @@ public class UserTest extends EntityTestCase {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_asyncAndSyncModeConflict() {
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> User.grantIapPermission("email@example.com", Optional.empty(), null, null, null));
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
User.grantIapPermission(
|
||||
"email@example.com",
|
||||
Optional.empty(),
|
||||
mock(CloudTasksUtils.class),
|
||||
mock(ServiceConnection.class),
|
||||
null));
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> User.revokeIapPermission("email@example.com", Optional.empty(), null, null, null));
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
User.revokeIapPermission(
|
||||
"email@example.com",
|
||||
Optional.empty(),
|
||||
mock(CloudTasksUtils.class),
|
||||
mock(ServiceConnection.class),
|
||||
null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_badInputs() {
|
||||
User.Builder builder = new User.Builder();
|
||||
@@ -116,19 +149,20 @@ public class UserTest extends EntityTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGrantIapPermission() {
|
||||
void testGrantIapPermissionAsync() {
|
||||
CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
IamClient iamClient = mock(IamClient.class);
|
||||
CloudTasksUtils cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
|
||||
// Individual permission.
|
||||
User.grantIapPermission("email@example.com", Optional.empty(), cloudTasksUtils, iamClient);
|
||||
User.grantIapPermission(
|
||||
"email@example.com", Optional.empty(), cloudTasksUtils, null, iamClient);
|
||||
cloudTasksHelper.assertNoTasksEnqueued();
|
||||
verify(iamClient).addBinding("email@example.com", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
|
||||
// Group membership.
|
||||
User.grantIapPermission(
|
||||
"email@example.com", Optional.of("console@example.com"), cloudTasksUtils, iamClient);
|
||||
"email@example.com", Optional.of("console@example.com"), cloudTasksUtils, null, iamClient);
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"console-user-group-update",
|
||||
new TaskMatcher()
|
||||
@@ -142,19 +176,49 @@ public class UserTest extends EntityTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRevokeIapPermission() {
|
||||
void testGrantIapPermissionSync() throws Exception {
|
||||
ServiceConnection connection = mock(ServiceConnection.class);
|
||||
IamClient iamClient = mock(IamClient.class);
|
||||
|
||||
// Individual permission.
|
||||
User.grantIapPermission("email@example.com", Optional.empty(), null, connection, iamClient);
|
||||
verifyNoInteractions(connection);
|
||||
verify(iamClient).addBinding("email@example.com", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
|
||||
// Group membership.
|
||||
User.grantIapPermission(
|
||||
"email@example.com", Optional.of("console@example.com"), null, connection, iamClient);
|
||||
verify(connection)
|
||||
.sendPostRequest(
|
||||
UpdateUserGroupAction.PATH,
|
||||
ImmutableMap.of(
|
||||
"userEmailAddress",
|
||||
"email@example.com",
|
||||
"groupEmailAddress",
|
||||
"console@example.com",
|
||||
"groupUpdateMode",
|
||||
"ADD"),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
verifyNoMoreInteractions(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRevokeIapPermissionAsync() {
|
||||
CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
IamClient iamClient = mock(IamClient.class);
|
||||
CloudTasksUtils cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
|
||||
// Individual permission.
|
||||
User.revokeIapPermission("email@example.com", Optional.empty(), cloudTasksUtils, iamClient);
|
||||
User.revokeIapPermission(
|
||||
"email@example.com", Optional.empty(), cloudTasksUtils, null, iamClient);
|
||||
cloudTasksHelper.assertNoTasksEnqueued();
|
||||
verify(iamClient).removeBinding("email@example.com", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
|
||||
// Group membership.
|
||||
User.revokeIapPermission(
|
||||
"email@example.com", Optional.of("console@example.com"), cloudTasksUtils, iamClient);
|
||||
"email@example.com", Optional.of("console@example.com"), cloudTasksUtils, null, iamClient);
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"console-user-group-update",
|
||||
new TaskMatcher()
|
||||
@@ -166,4 +230,33 @@ public class UserTest extends EntityTestCase {
|
||||
.param("groupUpdateMode", "REMOVE"));
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRevokeIapPermissionSync() throws Exception {
|
||||
ServiceConnection connection = mock(ServiceConnection.class);
|
||||
IamClient iamClient = mock(IamClient.class);
|
||||
|
||||
// Individual permission.
|
||||
User.revokeIapPermission("email@example.com", Optional.empty(), null, connection, iamClient);
|
||||
verifyNoInteractions(connection);
|
||||
verify(iamClient).removeBinding("email@example.com", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
|
||||
// Group membership.
|
||||
User.revokeIapPermission(
|
||||
"email@example.com", Optional.of("console@example.com"), null, connection, iamClient);
|
||||
verify(connection)
|
||||
.sendPostRequest(
|
||||
UpdateUserGroupAction.PATH,
|
||||
ImmutableMap.of(
|
||||
"userEmailAddress",
|
||||
"email@example.com",
|
||||
"groupEmailAddress",
|
||||
"console@example.com",
|
||||
"groupUpdateMode",
|
||||
"REMOVE"),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
verifyNoMoreInteractions(connection);
|
||||
}
|
||||
}
|
||||
|
||||
-2
@@ -426,7 +426,6 @@ public abstract class JpaTransactionManagerExtension
|
||||
.setEmailAddress("johndoe@theregistrar.com")
|
||||
.setPhoneNumber("+1.1234567890")
|
||||
.setTypes(ImmutableSet.of(RegistrarPocBase.Type.ADMIN))
|
||||
.setLoginEmailAddress("johndoe@theregistrar.com")
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -438,7 +437,6 @@ public abstract class JpaTransactionManagerExtension
|
||||
.setRegistryLockEmailAddress("Marla.Singer.RegistryLock@crr.com")
|
||||
.setPhoneNumber("+1.2128675309")
|
||||
.setTypes(ImmutableSet.of(RegistrarPocBase.Type.TECH))
|
||||
.setLoginEmailAddress("Marla.Singer@crr.com")
|
||||
.setAllowedToSetRegistryLockPassword(true)
|
||||
.setRegistryLockPassword("hi")
|
||||
.build();
|
||||
|
||||
@@ -276,27 +276,23 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_notLoggedIn_noContacts() {
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_with_remark.json");
|
||||
void testValidDomain_notLoggedIn_redactsAllContactInfo() {
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_redacted_contacts_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_notLoggedIn_contactsShowRedacted_evenWhenRegistrantDoesntExist() {
|
||||
// Even though the registrant is empty on this domain, it still shows a full set of REDACTED
|
||||
// fields through RDAP.
|
||||
void testValidDomain_notLoggedIn_showsNoRegistrant_whenRegistrantDoesntExist() {
|
||||
persistResource(
|
||||
loadByForeignKey(Domain.class, "cat.lol", clock.nowUtc())
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setRegistrant(Optional.empty())
|
||||
.build());
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_with_remark.json");
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_registrant_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_notLoggedIn_contactsShowRedacted_whenNoContactsExist() {
|
||||
// Even though the domain has no contacts, it still shows a full set of REDACTED fields through
|
||||
// RDAP.
|
||||
void testValidDomain_notLoggedIn_containsNoContactEntities_whenNoContactsExist() {
|
||||
persistResource(
|
||||
loadByForeignKey(Domain.class, "cat.lol", clock.nowUtc())
|
||||
.get()
|
||||
@@ -308,24 +304,38 @@ class RdapDomainActionTest extends RdapActionBaseTestCase<RdapDomainAction> {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_loggedInAsOtherRegistrar_noContacts() {
|
||||
void testValidDomain_loggedIn_containsNoContactEntities_whenNoContactsExist() {
|
||||
login("evilregistrar");
|
||||
persistResource(
|
||||
loadByForeignKey(Domain.class, "cat.lol", clock.nowUtc())
|
||||
.get()
|
||||
.asBuilder()
|
||||
.setRegistrant(Optional.empty())
|
||||
.setContacts(ImmutableSet.of())
|
||||
.build());
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_exist_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidDomain_loggedInAsOtherRegistrar_redactsAllContactInfo() {
|
||||
login("idnregistrar");
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_no_contacts_with_remark.json");
|
||||
assertProperResponseForCatLol("cat.lol", "rdap_domain_redacted_contacts_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpperCase_ignored() {
|
||||
assertProperResponseForCatLol("CaT.lOl", "rdap_domain_no_contacts_with_remark.json");
|
||||
assertProperResponseForCatLol("CaT.lOl", "rdap_domain_redacted_contacts_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTrailingDot_ignored() {
|
||||
assertProperResponseForCatLol("cat.lol.", "rdap_domain_no_contacts_with_remark.json");
|
||||
assertProperResponseForCatLol("cat.lol.", "rdap_domain_redacted_contacts_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testQueryParameter_ignored() {
|
||||
assertProperResponseForCatLol("cat.lol?key=value", "rdap_domain_no_contacts_with_remark.json");
|
||||
assertProperResponseForCatLol(
|
||||
"cat.lol?key=value", "rdap_domain_redacted_contacts_with_remark.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -750,7 +750,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
|
||||
void testDomainMatch_found_loggedInAsOtherRegistrar() {
|
||||
login("otherregistrar");
|
||||
runSuccessfulTestWithCatLol(
|
||||
RequestType.NAME, "cat.lol", "rdap_domain_no_contacts_with_remark.json");
|
||||
RequestType.NAME, "cat.lol", "rdap_domain_redacted_contacts_with_remark.json");
|
||||
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(1L));
|
||||
}
|
||||
|
||||
@@ -782,7 +782,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
|
||||
.addRegistrar("St. John Chrysostom")
|
||||
.addNameserver("ns1.cat.lol", "8-ROID")
|
||||
.addNameserver("ns2.external.tld", "1F-ROID")
|
||||
.load("rdap_domain_no_contacts_with_remark.json"));
|
||||
.load("rdap_domain_redacted_contacts_with_remark.json"));
|
||||
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(1L));
|
||||
}
|
||||
|
||||
@@ -826,7 +826,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
|
||||
.addRegistrar("1.test")
|
||||
.addNameserver("ns1.cat.1.test", "35-ROID")
|
||||
.addNameserver("ns2.cat.2.test", "37-ROID")
|
||||
.load("rdap_domain_no_contacts_with_remark.json"));
|
||||
.load("rdap_domain_redacted_contacts_with_remark.json"));
|
||||
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(1L));
|
||||
}
|
||||
|
||||
@@ -840,7 +840,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
|
||||
.addRegistrar("1.test")
|
||||
.addNameserver("ns1.cat.1.test", "35-ROID")
|
||||
.addNameserver("ns2.cat.2.test", "37-ROID")
|
||||
.load("rdap_domain_no_contacts_with_remark.json"));
|
||||
.load("rdap_domain_redacted_contacts_with_remark.json"));
|
||||
verifyMetrics(SearchType.BY_DOMAIN_NAME, Optional.of(1L));
|
||||
}
|
||||
|
||||
@@ -1378,7 +1378,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
|
||||
.addRegistrar("1.test")
|
||||
.addNameserver("ns1.cat.1.test", "35-ROID")
|
||||
.addNameserver("ns2.cat.2.test", "37-ROID")
|
||||
.load("rdap_domain_no_contacts_with_remark.json"));
|
||||
.load("rdap_domain_redacted_contacts_with_remark.json"));
|
||||
verifyMetrics(SearchType.BY_NAMESERVER_NAME, 1, 1);
|
||||
}
|
||||
|
||||
@@ -1392,7 +1392,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
|
||||
.addRegistrar("1.test")
|
||||
.addNameserver("ns1.cat.1.test", "35-ROID")
|
||||
.addNameserver("ns2.cat.2.test", "37-ROID")
|
||||
.load("rdap_domain_no_contacts_with_remark.json"));
|
||||
.load("rdap_domain_redacted_contacts_with_remark.json"));
|
||||
verifyMetrics(SearchType.BY_NAMESERVER_NAME, 1, 1);
|
||||
}
|
||||
|
||||
@@ -1675,7 +1675,7 @@ class RdapDomainSearchActionTest extends RdapSearchActionTestCase<RdapDomainSear
|
||||
runSuccessfulTestWithCatLol(
|
||||
RequestType.NS_IP,
|
||||
"bad:f00d:cafe:0:0:0:15:beef",
|
||||
"rdap_domain_no_contacts_with_remark.json");
|
||||
"rdap_domain_redacted_contacts_with_remark.json");
|
||||
verifyMetrics(SearchType.BY_NAMESERVER_ADDRESS, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,6 @@ import google.registry.rdap.RdapObjectClasses.ReplyPayloadBase;
|
||||
import google.registry.rdap.RdapObjectClasses.TopLevelReplyObject;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FullFieldsTestEntityHelper;
|
||||
import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@@ -373,7 +372,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactRegistrant),
|
||||
contactRegistrant,
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -386,7 +385,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactRegistrant),
|
||||
contactRegistrant,
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.SUMMARY)
|
||||
.toJson())
|
||||
@@ -400,7 +399,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactRegistrant),
|
||||
contactRegistrant,
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -420,7 +419,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactRegistrant),
|
||||
contactRegistrant,
|
||||
ImmutableSet.of(RdapEntity.Role.REGISTRANT),
|
||||
OutputDataType.FULL)
|
||||
.toJson())
|
||||
@@ -433,9 +432,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactAdmin),
|
||||
ImmutableSet.of(RdapEntity.Role.ADMIN),
|
||||
OutputDataType.FULL)
|
||||
contactAdmin, ImmutableSet.of(RdapEntity.Role.ADMIN), OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_admincontact.json"));
|
||||
}
|
||||
@@ -446,9 +443,7 @@ class RdapJsonFormatterTest {
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactTech),
|
||||
ImmutableSet.of(RdapEntity.Role.TECH),
|
||||
OutputDataType.FULL)
|
||||
contactTech, ImmutableSet.of(RdapEntity.Role.TECH), OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_techcontact.json"));
|
||||
}
|
||||
@@ -458,8 +453,7 @@ class RdapJsonFormatterTest {
|
||||
assertAboutJson()
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactTech), ImmutableSet.of(), OutputDataType.FULL)
|
||||
.createRdapContactEntity(contactTech, ImmutableSet.of(), OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_rolelesscontact.json"));
|
||||
}
|
||||
@@ -469,8 +463,7 @@ class RdapJsonFormatterTest {
|
||||
assertAboutJson()
|
||||
.that(
|
||||
rdapJsonFormatter
|
||||
.createRdapContactEntity(
|
||||
Optional.of(contactNotLinked), ImmutableSet.of(), OutputDataType.FULL)
|
||||
.createRdapContactEntity(contactNotLinked, ImmutableSet.of(), OutputDataType.FULL)
|
||||
.toJson())
|
||||
.isEqualTo(loadJson("rdapjson_unlinkedcontact.json"));
|
||||
}
|
||||
|
||||
@@ -73,7 +73,6 @@ import google.registry.model.billing.BillingRecurrence;
|
||||
import google.registry.model.common.DnsRefreshRequest;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.contact.Contact;
|
||||
import google.registry.model.contact.ContactAuthInfo;
|
||||
@@ -1014,20 +1013,33 @@ public final class DatabaseHelper {
|
||||
return tm().transact(() -> tm().loadByEntity(resource));
|
||||
}
|
||||
|
||||
public static User loadExistingUser(String emailAddress) {
|
||||
return loadByKey(VKey.create(User.class, emailAddress));
|
||||
}
|
||||
|
||||
/** Persists an admin {@link User} with the given email address if it doesn't already exist. */
|
||||
public static User createAdminUser(String emailAddress) {
|
||||
Optional<User> existingUser = UserDao.loadUser(emailAddress);
|
||||
if (existingUser.isPresent()) {
|
||||
return existingUser.get();
|
||||
}
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress(emailAddress)
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).setIsAdmin(true).build())
|
||||
.build();
|
||||
tm().transact(() -> tm().put(user));
|
||||
return UserDao.loadUser(emailAddress).get();
|
||||
// Reload the user to pick up the update time
|
||||
return loadByEntity(
|
||||
tm().transact(
|
||||
() -> {
|
||||
VKey<User> key = VKey.create(User.class, emailAddress);
|
||||
Optional<User> existingUser = tm().loadByKeyIfPresent(key);
|
||||
if (existingUser.isPresent()) {
|
||||
return existingUser.get();
|
||||
}
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress(emailAddress)
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setGlobalRole(GlobalRole.FTE)
|
||||
.setIsAdmin(true)
|
||||
.build())
|
||||
.build();
|
||||
tm().put(user);
|
||||
return user;
|
||||
}));
|
||||
}
|
||||
|
||||
/** Returns all the history entries that are parented off the given EppResource. */
|
||||
|
||||
@@ -16,22 +16,21 @@ package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.console.User.IAP_SECURED_WEB_APP_USER_ROLE;
|
||||
import static google.registry.testing.DatabaseHelper.loadExistingUser;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.tools.server.UpdateUserGroupAction;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -40,13 +39,13 @@ import org.junit.jupiter.api.Test;
|
||||
public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
|
||||
private final IamClient iamClient = mock(IamClient.class);
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
private final ServiceConnection connection = mock(ServiceConnection.class);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.iamClient = iamClient;
|
||||
command.maybeGroupEmailAddress = Optional.empty();
|
||||
command.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
command.setConnection(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -59,7 +58,7 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
assertThat(onlyUser.getUserRoles().getRegistrarRoles()).isEmpty();
|
||||
verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
verifyNoInteractions(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -71,16 +70,20 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
assertThat(onlyUser.getUserRoles().isAdmin()).isFalse();
|
||||
assertThat(onlyUser.getUserRoles().getGlobalRole()).isEqualTo(GlobalRole.NONE);
|
||||
assertThat(onlyUser.getUserRoles().getRegistrarRoles()).isEmpty();
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"console-user-group-update",
|
||||
new TaskMatcher()
|
||||
.method(HttpMethod.POST)
|
||||
.service("TOOLS")
|
||||
.path("/_dr/admin/updateUserGroup")
|
||||
.param("userEmailAddress", "user@example.test")
|
||||
.param("groupEmailAddress", "group@example.test")
|
||||
.param("groupUpdateMode", "ADD"));
|
||||
verify(connection)
|
||||
.sendPostRequest(
|
||||
UpdateUserGroupAction.PATH,
|
||||
ImmutableMap.of(
|
||||
"userEmailAddress",
|
||||
"user@example.test",
|
||||
"groupEmailAddress",
|
||||
"group@example.test",
|
||||
"groupUpdateMode",
|
||||
"ADD"),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
verifyNoInteractions(iamClient);
|
||||
verifyNoMoreInteractions(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,27 +93,27 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
"user@example.test",
|
||||
"--registry_lock_email_address",
|
||||
"registrylockemail@otherexample.test");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getRegistryLockEmailAddress())
|
||||
assertThat(loadExistingUser("user@example.test").getRegistryLockEmailAddress())
|
||||
.hasValue("registrylockemail@otherexample.test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_admin() throws Exception {
|
||||
runCommandForced("--email", "user@example.test", "--admin", "true");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isTrue();
|
||||
assertThat(loadExistingUser("user@example.test").getUserRoles().isAdmin()).isTrue();
|
||||
verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
verifyNoInteractions(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_globalRole() throws Exception {
|
||||
runCommandForced("--email", "user@example.test", "--global_role", "FTE");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getGlobalRole())
|
||||
assertThat(loadExistingUser("user@example.test").getUserRoles().getGlobalRole())
|
||||
.isEqualTo(GlobalRole.FTE);
|
||||
verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
verifyNoInteractions(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -120,7 +123,7 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
"user@example.test",
|
||||
"--registrar_roles",
|
||||
"TheRegistrar=ACCOUNT_MANAGER,NewRegistrar=PRIMARY_CONTACT");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
|
||||
assertThat(loadExistingUser("user@example.test").getUserRoles().getRegistrarRoles())
|
||||
.isEqualTo(
|
||||
ImmutableMap.of(
|
||||
"TheRegistrar",
|
||||
@@ -129,7 +132,7 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
RegistrarRole.PRIMARY_CONTACT));
|
||||
verify(iamClient).addBinding("user@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
verifyNoInteractions(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -144,7 +147,7 @@ public class CreateUserCommandTest extends CommandTestCase<CreateUserCommand> {
|
||||
.hasMessageThat()
|
||||
.isEqualTo("A user with email user@example.test already exists");
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
verifyNoInteractions(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -22,11 +22,12 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import com.google.cloud.tasks.v2.HttpMethod;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
import google.registry.testing.CloudTasksHelper.TaskMatcher;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.net.MediaType;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.persistence.VKey;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.tools.server.UpdateUserGroupAction;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -35,43 +36,49 @@ import org.junit.jupiter.api.Test;
|
||||
public class DeleteUserCommandTest extends CommandTestCase<DeleteUserCommand> {
|
||||
|
||||
private final IamClient iamClient = mock(IamClient.class);
|
||||
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
|
||||
private final ServiceConnection connection = mock(ServiceConnection.class);
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
command.iamClient = iamClient;
|
||||
command.maybeGroupEmailAddress = Optional.empty();
|
||||
command.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
|
||||
command.setConnection(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_deletesUser() throws Exception {
|
||||
DatabaseHelper.createAdminUser("email@example.test");
|
||||
assertThat(UserDao.loadUser("email@example.test")).isPresent();
|
||||
VKey<User> key = VKey.create(User.class, "email@example.test");
|
||||
assertThat(DatabaseHelper.loadByKeyIfPresent(key)).isPresent();
|
||||
runCommandForced("--email", "email@example.test");
|
||||
assertThat(UserDao.loadUser("email@example.test")).isEmpty();
|
||||
assertThat(DatabaseHelper.loadByKeyIfPresent(key)).isEmpty();
|
||||
verify(iamClient).removeBinding("email@example.test", IAP_SECURED_WEB_APP_USER_ROLE);
|
||||
verifyNoMoreInteractions(iamClient);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
verifyNoInteractions(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_deletesUser_removeFromGroup() throws Exception {
|
||||
command.maybeGroupEmailAddress = Optional.of("group@example.test");
|
||||
DatabaseHelper.createAdminUser("email@example.test");
|
||||
assertThat(UserDao.loadUser("email@example.test")).isPresent();
|
||||
VKey<User> key = VKey.create(User.class, "email@example.test");
|
||||
assertThat(DatabaseHelper.loadByKeyIfPresent(key)).isPresent();
|
||||
runCommandForced("--email", "email@example.test");
|
||||
assertThat(UserDao.loadUser("email@example.test")).isEmpty();
|
||||
cloudTasksHelper.assertTasksEnqueued(
|
||||
"console-user-group-update",
|
||||
new TaskMatcher()
|
||||
.method(HttpMethod.POST)
|
||||
.service("TOOLS")
|
||||
.path("/_dr/admin/updateUserGroup")
|
||||
.param("userEmailAddress", "email@example.test")
|
||||
.param("groupEmailAddress", "group@example.test")
|
||||
.param("groupUpdateMode", "REMOVE"));
|
||||
assertThat(DatabaseHelper.loadByKeyIfPresent(key)).isEmpty();
|
||||
verify(connection)
|
||||
.sendPostRequest(
|
||||
UpdateUserGroupAction.PATH,
|
||||
ImmutableMap.of(
|
||||
"userEmailAddress",
|
||||
"email@example.test",
|
||||
"groupEmailAddress",
|
||||
"group@example.test",
|
||||
"groupUpdateMode",
|
||||
"REMOVE"),
|
||||
MediaType.PLAIN_TEXT_UTF_8,
|
||||
new byte[0]);
|
||||
verifyNoInteractions(iamClient);
|
||||
verifyNoMoreInteractions(connection);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -83,6 +90,6 @@ public class DeleteUserCommandTest extends CommandTestCase<DeleteUserCommand> {
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Email does not correspond to a valid user");
|
||||
verifyNoInteractions(iamClient);
|
||||
cloudTasksHelper.assertNoTasksEnqueued("console-user-group-update");
|
||||
verifyNoInteractions(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,22 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static google.registry.model.EppResourceUtils.loadByForeignKey;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureName.MINIMUM_DATASET_CONTACTS_OPTIONAL;
|
||||
import static google.registry.model.common.FeatureFlag.FeatureStatus.INACTIVE;
|
||||
import static google.registry.testing.DatabaseHelper.assertBillingEventsForResource;
|
||||
import static google.registry.testing.DatabaseHelper.createTlds;
|
||||
import static google.registry.testing.DatabaseHelper.getOnlyHistoryEntryOfType;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.flows.EppTestCase;
|
||||
import google.registry.model.billing.BillingBase.Reason;
|
||||
import google.registry.model.billing.BillingEvent;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.DomainHistory;
|
||||
import google.registry.model.reporting.HistoryEntry.Type;
|
||||
@@ -53,6 +59,12 @@ class EppLifecycleToolsTest extends EppTestCase {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
persistResource(
|
||||
new FeatureFlag()
|
||||
.asBuilder()
|
||||
.setFeatureName(MINIMUM_DATASET_CONTACTS_OPTIONAL)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, INACTIVE))
|
||||
.build());
|
||||
createTlds("example", "tld");
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,8 @@ import com.google.common.io.Resources;
|
||||
import google.registry.persistence.NomulusPostgreSql;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
@@ -33,26 +34,31 @@ import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
@Testcontainers
|
||||
class GenerateSqlSchemaCommandTest extends CommandTestCase<GenerateSqlSchemaCommand> {
|
||||
|
||||
private String containerHostName;
|
||||
private int containerPort;
|
||||
private static String containerHostName;
|
||||
private static int containerPort;
|
||||
|
||||
@Container
|
||||
private static PostgreSQLContainer postgres =
|
||||
new PostgreSQLContainer(NomulusPostgreSql.getDockerTag())
|
||||
private static final PostgreSQLContainer<?> postgres =
|
||||
new PostgreSQLContainer<>(NomulusPostgreSql.getDockerTag())
|
||||
.withDatabaseName("postgres")
|
||||
.withUsername("postgres")
|
||||
.withPassword("domain-registry");
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
containerHostName = postgres.getContainerIpAddress();
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
containerHostName = postgres.getHost();
|
||||
containerPort = postgres.getMappedPort(GenerateSqlSchemaCommand.POSTGRESQL_PORT);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void afterAll() {
|
||||
postgres.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSchemaGeneration() throws Exception {
|
||||
runCommand(
|
||||
"--out_file=" + tmpDir.resolve("schema.sql").toString(),
|
||||
"--out_file=" + tmpDir.resolve("schema.sql"),
|
||||
"--db_host=" + containerHostName,
|
||||
"--db_port=" + containerPort);
|
||||
|
||||
@@ -69,7 +75,7 @@ class GenerateSqlSchemaCommandTest extends CommandTestCase<GenerateSqlSchemaComm
|
||||
@Test
|
||||
void testIncompatibleFlags() throws Exception {
|
||||
runCommand(
|
||||
"--out_file=" + tmpDir.resolve("schema.sql").toString(),
|
||||
"--out_file=" + tmpDir.resolve("schema.sql"),
|
||||
"--db_host=" + containerHostName,
|
||||
"--db_port=" + containerPort,
|
||||
"--start_postgresql");
|
||||
@@ -79,14 +85,14 @@ class GenerateSqlSchemaCommandTest extends CommandTestCase<GenerateSqlSchemaComm
|
||||
@Test
|
||||
void testDockerPostgresql() throws Exception {
|
||||
Path schemaFile = tmpDir.resolve("schema.sql");
|
||||
runCommand("--start_postgresql", "--out_file=" + schemaFile.toString());
|
||||
runCommand("--start_postgresql", "--out_file=" + schemaFile);
|
||||
assertThat(schemaFile.toFile().exists()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void validateGeneratedSchemaIsSameAsSchemaInFile() throws Exception {
|
||||
Path schemaFile = tmpDir.resolve("schema.sql");
|
||||
runCommand("--start_postgresql", "--out_file=" + schemaFile.toString());
|
||||
runCommand("--start_postgresql", "--out_file=" + schemaFile);
|
||||
assertThat(schemaFile.toFile().toURI().toURL())
|
||||
.hasSameContentAs(Resources.getResource("sql/schema/db-schema.sql.generated"));
|
||||
}
|
||||
|
||||
@@ -14,12 +14,13 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -28,7 +29,7 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
UserDao.saveUser(
|
||||
DatabaseHelper.putInDb(
|
||||
new User.Builder()
|
||||
.setEmailAddress("johndoe@theregistrar.com")
|
||||
.setUserRoles(
|
||||
@@ -36,8 +37,7 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of("TheRegistrar", RegistrarRole.PRIMARY_CONTACT))
|
||||
.build())
|
||||
.build());
|
||||
UserDao.saveUser(
|
||||
.build(),
|
||||
new User.Builder()
|
||||
.setEmailAddress("fte@google.com")
|
||||
.setUserRoles(
|
||||
@@ -52,7 +52,6 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
|
||||
"""
|
||||
User: {
|
||||
emailAddress=fte@google.com
|
||||
id=2
|
||||
registryLockEmailAddress=null
|
||||
registryLockPasswordHash=null
|
||||
registryLockPasswordSalt=null
|
||||
@@ -75,7 +74,6 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
|
||||
"""
|
||||
User: {
|
||||
emailAddress=johndoe@theregistrar.com
|
||||
id=1
|
||||
registryLockEmailAddress=null
|
||||
registryLockPasswordHash=null
|
||||
registryLockPasswordSalt=null
|
||||
@@ -90,7 +88,6 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
|
||||
}
|
||||
User: {
|
||||
emailAddress=fte@google.com
|
||||
id=2
|
||||
registryLockEmailAddress=null
|
||||
registryLockPasswordHash=null
|
||||
registryLockPasswordSalt=null
|
||||
@@ -113,7 +110,6 @@ public class GetUserCommandTest extends CommandTestCase<GetUserCommand> {
|
||||
"""
|
||||
User: {
|
||||
emailAddress=johndoe@theregistrar.com
|
||||
id=1
|
||||
registryLockEmailAddress=null
|
||||
registryLockPasswordHash=null
|
||||
registryLockPasswordSalt=null
|
||||
|
||||
@@ -68,8 +68,7 @@ class RegistrarPocCommandTest extends CommandTestCase<RegistrarPocCommand> {
|
||||
"Visible in registrar WHOIS query as Admin contact: Yes",
|
||||
"Visible in registrar WHOIS query as Technical contact: No",
|
||||
"Phone number and email visible in domain WHOIS query as "
|
||||
+ "Registrar Abuse contact info: No",
|
||||
"Registrar-Console access: No");
|
||||
+ "Registrar Abuse contact info: No");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,69 +115,6 @@ class RegistrarPocCommandTest extends CommandTestCase<RegistrarPocCommand> {
|
||||
.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate_enableConsoleAccess() throws Exception {
|
||||
Registrar registrar = loadRegistrar("NewRegistrar");
|
||||
persistSimpleResource(
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName("Jane Doe")
|
||||
.setEmailAddress("jane.doe@example.com")
|
||||
.build());
|
||||
runCommandForced(
|
||||
"--mode=UPDATE",
|
||||
"--email=jane.doe@example.com",
|
||||
"--allow_console_access=true",
|
||||
"NewRegistrar");
|
||||
RegistrarPoc registrarPoc =
|
||||
loadRegistrar("NewRegistrar").getContacts().stream()
|
||||
.filter(rc -> "jane.doe@example.com".equals(rc.getEmailAddress()))
|
||||
.findFirst()
|
||||
.get();
|
||||
assertThat(registrarPoc.getLoginEmailAddress()).isEqualTo("jane.doe@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate_enableConsoleAccess_specifyLoginEmail() throws Exception {
|
||||
Registrar registrar = loadRegistrar("NewRegistrar");
|
||||
persistSimpleResource(
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName("Jane Doe")
|
||||
.setEmailAddress("jane.doe@example.com")
|
||||
.build());
|
||||
runCommandForced(
|
||||
"--mode=UPDATE",
|
||||
"--email=jane.doe@example.com",
|
||||
"--login_email=jim.doe@example.com",
|
||||
"--allow_console_access=true",
|
||||
"NewRegistrar");
|
||||
RegistrarPoc registrarPoc =
|
||||
loadRegistrar("NewRegistrar").getContacts().stream()
|
||||
.filter(rc -> "jane.doe@example.com".equals(rc.getEmailAddress()))
|
||||
.findFirst()
|
||||
.get();
|
||||
assertThat(registrarPoc.getLoginEmailAddress()).isEqualTo("jim.doe@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate_disableConsoleAccess() throws Exception {
|
||||
Registrar registrar = loadRegistrar("NewRegistrar");
|
||||
persistSimpleResource(
|
||||
new RegistrarPoc.Builder()
|
||||
.setRegistrar(registrar)
|
||||
.setName("Judith Doe")
|
||||
.setEmailAddress("judith.doe@example.com")
|
||||
.build());
|
||||
runCommandForced(
|
||||
"--mode=UPDATE",
|
||||
"--email=judith.doe@example.com",
|
||||
"--allow_console_access=false",
|
||||
"NewRegistrar");
|
||||
RegistrarPoc registrarPoc = loadRegistrar("NewRegistrar").getContacts().asList().get(1);
|
||||
assertThat(registrarPoc.getLoginEmailAddress()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUpdate_unsetOtherWhoisAbuseFlags() throws Exception {
|
||||
Registrar registrar = loadRegistrar("NewRegistrar");
|
||||
@@ -334,7 +270,6 @@ class RegistrarPocCommandTest extends CommandTestCase<RegistrarPocCommand> {
|
||||
.setVisibleInWhoisAsTech(false)
|
||||
.setVisibleInDomainWhoisAsAbuse(true)
|
||||
.build());
|
||||
assertThat(registrarPoc.getLoginEmailAddress()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -346,7 +281,7 @@ class RegistrarPocCommandTest extends CommandTestCase<RegistrarPocCommand> {
|
||||
|
||||
@Test
|
||||
void testDelete_failsOnDomainWhoisAbuseContact() {
|
||||
RegistrarPoc registrarPoc = loadRegistrar("NewRegistrar").getContacts().asList().get(0);
|
||||
RegistrarPoc registrarPoc = loadRegistrar("NewRegistrar").getContacts().asList().getFirst();
|
||||
putInDb(registrarPoc.asBuilder().setVisibleInDomainWhoisAsAbuse(true).build());
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
@@ -358,33 +293,6 @@ class RegistrarPocCommandTest extends CommandTestCase<RegistrarPocCommand> {
|
||||
assertThat(loadRegistrar("NewRegistrar").getContacts()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreate_withConsoleAccessEnabled() throws Exception {
|
||||
runCommandForced(
|
||||
"--mode=CREATE",
|
||||
"--name=Jim Doe",
|
||||
"--email=jim.doe@example.com",
|
||||
"--allow_console_access=true",
|
||||
"--contact_type=ADMIN,ABUSE",
|
||||
"NewRegistrar");
|
||||
RegistrarPoc registrarPoc = loadRegistrar("NewRegistrar").getContacts().asList().get(1);
|
||||
assertThat(registrarPoc.getEmailAddress()).isEqualTo("jim.doe@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreate_withConsoleAccessEnabled_specifyLoginEmail() throws Exception {
|
||||
runCommandForced(
|
||||
"--mode=CREATE",
|
||||
"--name=Jim Doe",
|
||||
"--email=jim.doe@example.com",
|
||||
"--login_email=jane.doe@example.com",
|
||||
"--allow_console_access=true",
|
||||
"--contact_type=ADMIN,ABUSE",
|
||||
"NewRegistrar");
|
||||
RegistrarPoc registrarPoc = loadRegistrar("NewRegistrar").getContacts().asList().get(1);
|
||||
assertThat(registrarPoc.getLoginEmailAddress()).isEqualTo("jane.doe@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreate_withNoContactTypes() throws Exception {
|
||||
runCommandForced(
|
||||
|
||||
@@ -22,9 +22,11 @@ import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
|
||||
import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
|
||||
import static google.registry.testing.CertificateSamples.SAMPLE_CERT_HASH;
|
||||
import static google.registry.testing.DatabaseHelper.createTld;
|
||||
import static google.registry.testing.DatabaseHelper.loadExistingUser;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistPremiumList;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.DatabaseHelper.putInDb;
|
||||
import static org.joda.money.CurrencyUnit.USD;
|
||||
import static org.joda.time.DateTimeZone.UTC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
@@ -39,7 +41,6 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.tld.Tld;
|
||||
@@ -447,12 +448,11 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
|
||||
@Test
|
||||
void testFailure_userExists() {
|
||||
User user =
|
||||
putInDb(
|
||||
new User.Builder()
|
||||
.setEmailAddress("contact@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
UserDao.saveUser(user);
|
||||
.build());
|
||||
IllegalStateException thrown =
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
@@ -494,12 +494,11 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
|
||||
@Test
|
||||
void testSuccess_userExists_replaceExisting() throws Exception {
|
||||
User user =
|
||||
putInDb(
|
||||
new User.Builder()
|
||||
.setEmailAddress("contact@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
UserDao.saveUser(user);
|
||||
.build());
|
||||
|
||||
runCommandForced(
|
||||
"--overwrite",
|
||||
@@ -520,7 +519,7 @@ class SetupOteCommandTest extends CommandTestCase<SetupOteCommand> {
|
||||
verifyUser("blobio-5", "contact@email.com");
|
||||
|
||||
// verify that the role is completely replaced, e.g., the global role is gone.
|
||||
assertThat(UserDao.loadUser("contact@email.com").get().getUserRoles().getGlobalRole())
|
||||
assertThat(loadExistingUser("contact@email.com").getUserRoles().getGlobalRole())
|
||||
.isEqualTo(GlobalRole.NONE);
|
||||
|
||||
verifyIapPermission("contact@email.com");
|
||||
|
||||
@@ -15,13 +15,14 @@
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.testing.DatabaseHelper.loadExistingUser;
|
||||
import static google.registry.testing.DatabaseHelper.putInDb;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserDao;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -31,7 +32,7 @@ public class UpdateUserCommandTest extends CommandTestCase<UpdateUserCommand> {
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() throws Exception {
|
||||
UserDao.saveUser(
|
||||
putInDb(
|
||||
new User.Builder()
|
||||
.setEmailAddress("user@example.test")
|
||||
.setUserRoles(new UserRoles.Builder().build())
|
||||
@@ -45,41 +46,39 @@ public class UpdateUserCommandTest extends CommandTestCase<UpdateUserCommand> {
|
||||
"user@example.test",
|
||||
"--registry_lock_email_address",
|
||||
"registrylockemail@otherexample.test");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getRegistryLockEmailAddress())
|
||||
assertThat(loadExistingUser("user@example.test").getRegistryLockEmailAddress())
|
||||
.hasValue("registrylockemail@otherexample.test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_removeRegistryLockEmail() throws Exception {
|
||||
UserDao.saveUser(
|
||||
UserDao.loadUser("user@example.test")
|
||||
.get()
|
||||
putInDb(
|
||||
loadExistingUser("user@example.test")
|
||||
.asBuilder()
|
||||
.setRegistryLockEmailAddress("registrylock@otherexample.test")
|
||||
.build());
|
||||
runCommandForced("--email", "user@example.test", "--registry_lock_email_address", "");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getRegistryLockEmailAddress()).isEmpty();
|
||||
assertThat(loadExistingUser("user@example.test").getRegistryLockEmailAddress()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_admin() throws Exception {
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isFalse();
|
||||
assertThat(loadExistingUser("user@example.test").getUserRoles().isAdmin()).isFalse();
|
||||
runCommandForced("--email", "user@example.test", "--admin", "true");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isTrue();
|
||||
assertThat(loadExistingUser("user@example.test").getUserRoles().isAdmin()).isTrue();
|
||||
runCommandForced("--email", "user@example.test", "--admin", "false");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().isAdmin()).isFalse();
|
||||
assertThat(loadExistingUser("user@example.test").getUserRoles().isAdmin()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_registrarRoles() throws Exception {
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
|
||||
.isEmpty();
|
||||
assertThat(loadExistingUser("user@example.test").getUserRoles().getRegistrarRoles()).isEmpty();
|
||||
runCommandForced(
|
||||
"--email",
|
||||
"user@example.test",
|
||||
"--registrar_roles",
|
||||
"TheRegistrar=ACCOUNT_MANAGER,NewRegistrar=PRIMARY_CONTACT");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
|
||||
assertThat(loadExistingUser("user@example.test").getUserRoles().getRegistrarRoles())
|
||||
.isEqualTo(
|
||||
ImmutableMap.of(
|
||||
"TheRegistrar",
|
||||
@@ -87,16 +86,15 @@ public class UpdateUserCommandTest extends CommandTestCase<UpdateUserCommand> {
|
||||
"NewRegistrar",
|
||||
RegistrarRole.PRIMARY_CONTACT));
|
||||
runCommandForced("--email", "user@example.test", "--registrar_roles", "");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getRegistrarRoles())
|
||||
.isEmpty();
|
||||
assertThat(loadExistingUser("user@example.test").getUserRoles().getRegistrarRoles()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_globalRole() throws Exception {
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getGlobalRole())
|
||||
assertThat(loadExistingUser("user@example.test").getUserRoles().getGlobalRole())
|
||||
.isEqualTo(GlobalRole.NONE);
|
||||
runCommandForced("--email", "user@example.test", "--global_role", "FTE");
|
||||
assertThat(UserDao.loadUser("user@example.test").get().getUserRoles().getGlobalRole())
|
||||
assertThat(loadExistingUser("user@example.test").getUserRoles().getGlobalRole())
|
||||
.isEqualTo(GlobalRole.FTE);
|
||||
}
|
||||
|
||||
|
||||
+16
-23
@@ -24,6 +24,7 @@ import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.SqlHelper.getMostRecentRegistryLockByRepoId;
|
||||
import static google.registry.testing.SqlHelper.saveRegistryLock;
|
||||
import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STATUSES;
|
||||
import static google.registry.ui.server.console.ConsoleRegistryLockAction.ConsoleRegistryLockPostInput;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
@@ -309,10 +310,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
persistResource(defaultDomain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
action =
|
||||
createPostAction(
|
||||
"example.test",
|
||||
false,
|
||||
"registryLockPassword",
|
||||
Optional.of(Duration.standardDays(1).getMillis()));
|
||||
"example.test", false, "registryLockPassword", Duration.standardDays(1).getMillis());
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
verifyEmail();
|
||||
@@ -348,7 +346,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).setIsAdmin(true).build())
|
||||
.build();
|
||||
action = createPostAction("example.test", true, "", Optional.empty());
|
||||
action = createPostAction("example.test", true, "", null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_OK);
|
||||
verifyEmail();
|
||||
@@ -414,8 +412,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
.setCreationRegistrarId("NewRegistrar")
|
||||
.setPersistedCurrentSponsorRegistrarId("NewRegistrar")
|
||||
.build());
|
||||
action =
|
||||
createPostAction("otherregistrar.test", true, "registryLockPassword", Optional.empty());
|
||||
action = createPostAction("otherregistrar.test", true, "registryLockPassword", null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_BAD_REQUEST);
|
||||
assertThat(response.getPayload())
|
||||
@@ -444,7 +441,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
|
||||
@Test
|
||||
void testPost_failure_badPassword() throws Exception {
|
||||
action = createPostAction("example.test", true, "badPassword", Optional.empty());
|
||||
action = createPostAction("example.test", true, "badPassword", null);
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(SC_UNAUTHORIZED);
|
||||
}
|
||||
@@ -483,29 +480,25 @@ public class ConsoleRegistryLockActionTest {
|
||||
}
|
||||
|
||||
private ConsoleRegistryLockAction createDefaultPostAction(boolean isLock) {
|
||||
return createPostAction("example.test", isLock, "registryLockPassword", Optional.empty());
|
||||
return createPostAction("example.test", isLock, "registryLockPassword", null);
|
||||
}
|
||||
|
||||
private ConsoleRegistryLockAction createPostAction(
|
||||
String domainName, boolean isLock, String password, Optional<Long> relockDurationMillis) {
|
||||
String domainName, boolean isLock, String password, Long relockDurationMillis) {
|
||||
ConsoleApiParams params = createParams();
|
||||
when(params.request().getParameter("domainName")).thenReturn(domainName);
|
||||
when(params.request().getParameterMap())
|
||||
.thenReturn(ImmutableMap.of("isLock", new String[] {String.valueOf(isLock)}));
|
||||
when(params.request().getParameter("isLock")).thenReturn(String.valueOf(isLock));
|
||||
when(params.request().getParameter("password")).thenReturn(password);
|
||||
relockDurationMillis.ifPresent(
|
||||
duration ->
|
||||
when(params.request().getParameter("relockDurationMillis"))
|
||||
.thenReturn(String.valueOf(duration)));
|
||||
return createGenericAction(params, "POST");
|
||||
ConsoleRegistryLockPostInput postInput =
|
||||
new ConsoleRegistryLockPostInput(domainName, isLock, password, relockDurationMillis);
|
||||
return createGenericAction(params, "POST", Optional.of(postInput));
|
||||
}
|
||||
|
||||
private ConsoleRegistryLockAction createGetAction() throws IOException {
|
||||
return createGenericAction(createParams(), "GET");
|
||||
return createGenericAction(createParams(), "GET", Optional.empty());
|
||||
}
|
||||
|
||||
private ConsoleRegistryLockAction createGenericAction(ConsoleApiParams params, String method) {
|
||||
private ConsoleRegistryLockAction createGenericAction(
|
||||
ConsoleApiParams params,
|
||||
String method,
|
||||
Optional<ConsoleRegistryLockPostInput> optionalPostInput) {
|
||||
when(params.request().getMethod()).thenReturn(method);
|
||||
when(params.request().getServerName()).thenReturn("registrarconsole.tld");
|
||||
when(params.request().getParameter("registrarId")).thenReturn("TheRegistrar");
|
||||
@@ -516,7 +509,7 @@ public class ConsoleRegistryLockActionTest {
|
||||
new CloudTasksHelper(fakeClock).getTestCloudTasksUtils());
|
||||
response = (FakeResponse) params.response();
|
||||
return new ConsoleRegistryLockAction(
|
||||
params, domainLockUtils, gmailClient, GSON, "TheRegistrar");
|
||||
params, domainLockUtils, gmailClient, GSON, optionalPostInput, "TheRegistrar");
|
||||
}
|
||||
|
||||
private ConsoleApiParams createParams() {
|
||||
|
||||
+3
-3
@@ -90,7 +90,7 @@ public class ConsoleRegistryLockVerifyActionTest {
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo(
|
||||
"{\"action\":\"unlocked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
|
||||
"{\"action\":\"locked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
|
||||
assertThat(loadByEntity(defaultDomain).getStatusValues())
|
||||
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
@@ -123,7 +123,7 @@ public class ConsoleRegistryLockVerifyActionTest {
|
||||
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
|
||||
assertThat(response.getPayload())
|
||||
.isEqualTo(
|
||||
"{\"action\":\"unlocked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
|
||||
"{\"action\":\"locked\",\"domainName\":\"example.test\",\"registrarId\":\"TheRegistrar\"}");
|
||||
assertThat(loadByEntity(defaultDomain).getStatusValues())
|
||||
.containsAtLeastElementsIn(REGISTRY_LOCK_STATUSES);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import static google.registry.testing.DatabaseHelper.persistNewRegistrar;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static google.registry.testing.SqlHelper.saveRegistrar;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -148,6 +147,28 @@ class RegistrarsActionTest {
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_getOnlyAllowedRegistrars() {
|
||||
saveRegistrar("registrarId");
|
||||
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
AuthResult.createUser(
|
||||
createUser(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of("registrarId", RegistrarRole.ACCOUNT_MANAGER))
|
||||
.build())));
|
||||
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
|
||||
String payload = ((FakeResponse) consoleApiParams.response()).getPayload();
|
||||
Registrar[] registrars = GSON.fromJson(payload, Registrar[].class);
|
||||
assertThat(registrars).hasLength(1);
|
||||
assertThat(registrars[0].getRegistrarId()).isEqualTo("registrarId");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_createRegistrar() {
|
||||
RegistrarsAction action =
|
||||
@@ -205,23 +226,6 @@ class RegistrarsActionTest {
|
||||
.isEqualTo("Registrar with registrarId regIdTest already exists");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_getRegistrarIds() {
|
||||
saveRegistrar("registrarId");
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
AuthResult.createUser(
|
||||
createUser(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
ImmutableMap.of(
|
||||
"registrarId", RegistrarRole.ACCOUNT_MANAGER_WITH_REGISTRY_LOCK))
|
||||
.build())));
|
||||
action.run();
|
||||
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
private User createUser(UserRoles userRoles) {
|
||||
return new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
|
||||
+7
-8
@@ -17,7 +17,6 @@ package google.registry.ui.server.console.settings;
|
||||
import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.model.registrar.RegistrarPocBase.Type.ADMIN;
|
||||
import static google.registry.model.registrar.RegistrarPocBase.Type.WHOIS;
|
||||
import static google.registry.testing.DatabaseHelper.createAdminUser;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static google.registry.testing.DatabaseHelper.loadAllOf;
|
||||
@@ -67,7 +66,7 @@ class ContactActionTest {
|
||||
+ "\"emailAddress\":\"test.registrar1@example.com\","
|
||||
+ "\"registrarId\":\"registrarId\","
|
||||
+ "\"phoneNumber\":\"+1.9999999999\",\"faxNumber\":\"+1.9999999991\","
|
||||
+ "\"types\":[\"WHOIS\",\"ADMIN\"],\"visibleInWhoisAsAdmin\":true,"
|
||||
+ "\"types\":[\"ADMIN\"],\"visibleInWhoisAsAdmin\":true,"
|
||||
+ "\"visibleInWhoisAsTech\":false,\"visibleInDomainWhoisAsAbuse\":false}";
|
||||
|
||||
private static String jsonRegistrar2 =
|
||||
@@ -75,7 +74,7 @@ class ContactActionTest {
|
||||
+ "\"emailAddress\":\"test.registrar2@example.com\","
|
||||
+ "\"registrarId\":\"registrarId\","
|
||||
+ "\"phoneNumber\":\"+1.1234567890\",\"faxNumber\":\"+1.1234567891\","
|
||||
+ "\"types\":[\"WHOIS\",\"ADMIN\"],\"visibleInWhoisAsAdmin\":true,"
|
||||
+ "\"types\":[\"ADMIN\"],\"visibleInWhoisAsAdmin\":true,"
|
||||
+ "\"visibleInWhoisAsTech\":false,\"visibleInDomainWhoisAsAbuse\":false}";
|
||||
|
||||
private Registrar testRegistrar;
|
||||
@@ -97,7 +96,7 @@ class ContactActionTest {
|
||||
.setEmailAddress("test.registrar1@example.com")
|
||||
.setPhoneNumber("+1.9999999999")
|
||||
.setFaxNumber("+1.9999999991")
|
||||
.setTypes(ImmutableSet.of(WHOIS, ADMIN))
|
||||
.setTypes(ImmutableSet.of(ADMIN))
|
||||
.setVisibleInWhoisAsAdmin(true)
|
||||
.setVisibleInWhoisAsTech(false)
|
||||
.setVisibleInDomainWhoisAsAbuse(false)
|
||||
@@ -218,22 +217,22 @@ class ContactActionTest {
|
||||
+ " {name=Test Registrar 1,"
|
||||
+ " emailAddress=test.registrar1@example.com, registrarId=registrarId,"
|
||||
+ " registryLockEmailAddress=null, phoneNumber=+1.9999999999,"
|
||||
+ " faxNumber=+1.9999999991, types=[ADMIN, WHOIS], loginEmailAddress=null,"
|
||||
+ " faxNumber=+1.9999999991, types=[ADMIN],"
|
||||
+ " visibleInWhoisAsAdmin=true, visibleInWhoisAsTech=false,"
|
||||
+ " visibleInDomainWhoisAsAbuse=false,"
|
||||
+ " allowedToSetRegistryLockPassword=false}\n"
|
||||
+ " REMOVED:\n"
|
||||
+ " {name=Test Registrar 1, emailAddress=incorrect@email.com,"
|
||||
+ " registrarId=registrarId, registryLockEmailAddress=null,"
|
||||
+ " phoneNumber=+1.9999999999, faxNumber=+1.9999999991, types=[WHOIS,"
|
||||
+ " ADMIN], loginEmailAddress=null, visibleInWhoisAsAdmin=true,"
|
||||
+ " phoneNumber=+1.9999999999, faxNumber=+1.9999999991, types=[ADMIN],"
|
||||
+ " visibleInWhoisAsAdmin=true,"
|
||||
+ " visibleInWhoisAsTech=false, visibleInDomainWhoisAsAbuse=false,"
|
||||
+ " allowedToSetRegistryLockPassword=false}\n"
|
||||
+ " FINAL CONTENTS:\n"
|
||||
+ " {name=Test Registrar 1,"
|
||||
+ " emailAddress=test.registrar1@example.com, registrarId=registrarId,"
|
||||
+ " registryLockEmailAddress=null, phoneNumber=+1.9999999999,"
|
||||
+ " faxNumber=+1.9999999991, types=[ADMIN, WHOIS], loginEmailAddress=null,"
|
||||
+ " faxNumber=+1.9999999991, types=[ADMIN],"
|
||||
+ " visibleInWhoisAsAdmin=true, visibleInWhoisAsTech=false,"
|
||||
+ " visibleInDomainWhoisAsAbuse=false,"
|
||||
+ " allowedToSetRegistryLockPassword=false}\n")
|
||||
|
||||
+2
@@ -104,6 +104,8 @@ public class WhoisRegistrarFieldsActionTest {
|
||||
ImmutableMap.of(
|
||||
"whoisServer",
|
||||
"whois.nic.google",
|
||||
"icannReferralEmail",
|
||||
"lol@sloth.test",
|
||||
"phoneNumber",
|
||||
"+1.4155552671",
|
||||
"faxNumber",
|
||||
|
||||
@@ -29,6 +29,8 @@ import static google.registry.tools.LockOrUnlockDomainCommand.REGISTRY_LOCK_STAT
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSortedMap;
|
||||
import google.registry.model.common.FeatureFlag;
|
||||
import google.registry.model.console.RegistrarRole;
|
||||
import google.registry.model.domain.Domain;
|
||||
import google.registry.model.domain.RegistryLock;
|
||||
@@ -110,15 +112,6 @@ class RegistrarConsoleScreenshotTest extends WebDriverTestCase {
|
||||
driver.diffPage("page");
|
||||
}
|
||||
|
||||
/** Admins shouldn't have the "add" button */
|
||||
@RetryingTest(3)
|
||||
void settingsContact_asAdmin() throws Throwable {
|
||||
server.setIsAdmin(true);
|
||||
driver.get(server.getUrl("/registrar?clientId=NewRegistrar#contact-settings"));
|
||||
driver.waitForDisplayedElement(By.tagName("h1"));
|
||||
driver.diffPage("page");
|
||||
}
|
||||
|
||||
@RetryingTest(3)
|
||||
void settingsContactItem() throws Throwable {
|
||||
driver.get(server.getUrl("/registrar#contact-settings/johndoe@theregistrar.com"));
|
||||
@@ -518,6 +511,18 @@ class RegistrarConsoleScreenshotTest extends WebDriverTestCase {
|
||||
driver.diffPage("page");
|
||||
}
|
||||
|
||||
@RetryingTest(3)
|
||||
void deprecationWarning_active() throws Throwable {
|
||||
persistResource(
|
||||
new FeatureFlag.Builder()
|
||||
.setFeatureName(FeatureFlag.FeatureName.NEW_CONSOLE)
|
||||
.setStatusMap(ImmutableSortedMap.of(START_OF_TIME, FeatureFlag.FeatureStatus.ACTIVE))
|
||||
.build());
|
||||
driver.get(server.getUrl("/registrar"));
|
||||
driver.waitForDisplayedElement(By.tagName("h1"));
|
||||
driver.diffPage("page");
|
||||
}
|
||||
|
||||
private static void createDomainAndSaveLock() {
|
||||
createTld("tld");
|
||||
Domain domain = persistActiveDomain("example.tld");
|
||||
|
||||
-111
@@ -147,117 +147,6 @@
|
||||
"type": "object truncated due to unexplainable reasons"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"objectClassName":"entity",
|
||||
"handle":"",
|
||||
"remarks":[
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description":[
|
||||
"Some of the data in this object has been removed.",
|
||||
"Contact personal data is visible only to the owning registrar."
|
||||
],
|
||||
"links":[
|
||||
{
|
||||
"href":"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
|
||||
"rel":"alternate",
|
||||
"type":"text/html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title":"EMAIL REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description":[
|
||||
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
|
||||
]
|
||||
}
|
||||
],
|
||||
"roles":["registrant"],
|
||||
"vcardArray":[
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"objectClassName": "entity",
|
||||
"handle": "",
|
||||
"roles":["administrative"],
|
||||
"remarks": [
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description": [
|
||||
"Some of the data in this object has been removed.",
|
||||
"Contact personal data is visible only to the owning registrar."
|
||||
],
|
||||
"links":[
|
||||
{
|
||||
"href":"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
|
||||
"rel":"alternate",
|
||||
"type":"text/html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title":"EMAIL REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description": [
|
||||
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
|
||||
]
|
||||
}
|
||||
],
|
||||
"vcardArray":[
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"objectClassName":"entity",
|
||||
"handle":"",
|
||||
"remarks":[
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description":[
|
||||
"Some of the data in this object has been removed.",
|
||||
"Contact personal data is visible only to the owning registrar."
|
||||
],
|
||||
"links":[
|
||||
{
|
||||
"href":"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
|
||||
"rel":"alternate",
|
||||
"type":"text/html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":[
|
||||
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
|
||||
],
|
||||
"title":"EMAIL REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization"
|
||||
}
|
||||
],
|
||||
"roles": ["technical"],
|
||||
"vcardArray": [
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+225
@@ -0,0 +1,225 @@
|
||||
{
|
||||
"rdapConformance": [
|
||||
"rdap_level_0",
|
||||
"icann_rdap_response_profile_0",
|
||||
"icann_rdap_technical_implementation_guide_0"
|
||||
],
|
||||
"objectClassName": "domain",
|
||||
"handle": "%DOMAIN_HANDLE_1%",
|
||||
"ldhName": "%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"status": [
|
||||
"client delete prohibited",
|
||||
"client renew prohibited",
|
||||
"client transfer prohibited",
|
||||
"server update prohibited"
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"href": "https://example.tld/rdap/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "self"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related"
|
||||
},
|
||||
{
|
||||
"href": "https://rdap.example.com/withoutSlash/domain/%DOMAIN_PUNYCODE_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "related"
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
{
|
||||
"eventAction": "registration",
|
||||
"eventActor": "TheRegistrar",
|
||||
"eventDate": "1997-01-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"eventAction": "expiration",
|
||||
"eventDate": "2110-10-08T00:44:59.000Z"
|
||||
},
|
||||
{
|
||||
"eventAction": "last update of RDAP database",
|
||||
"eventDate": "2000-01-01T00:00:00.000Z"
|
||||
},
|
||||
{
|
||||
"eventAction": "last changed",
|
||||
"eventDate": "2009-05-29T20:13:00.000Z"
|
||||
}
|
||||
],
|
||||
"nameservers": [
|
||||
{
|
||||
"objectClassName": "nameserver",
|
||||
"handle": "%NAMESERVER_HANDLE_1%",
|
||||
"ldhName": "%NAMESERVER_NAME_1%",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://example.tld/rdap/nameserver/%NAMESERVER_NAME_1%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"remarks": [
|
||||
{
|
||||
"title": "Incomplete Data",
|
||||
"type": "object truncated due to unexplainable reasons",
|
||||
"description": ["Summary data only. For complete data, send a specific query for the object."]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"objectClassName": "nameserver",
|
||||
"handle": "%NAMESERVER_HANDLE_2%",
|
||||
"ldhName": "%NAMESERVER_NAME_2%",
|
||||
"links": [
|
||||
{
|
||||
"href": "https://example.tld/rdap/nameserver/%NAMESERVER_NAME_2%",
|
||||
"type": "application/rdap+json",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"remarks": [
|
||||
{
|
||||
"title": "Incomplete Data",
|
||||
"type": "object truncated due to unexplainable reasons",
|
||||
"description": ["Summary data only. For complete data, send a specific query for the object."]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"secureDNS" : {
|
||||
"delegationSigned": true,
|
||||
"zoneSigned":true,
|
||||
"dsData":[
|
||||
{"algorithm":2,"digest":"DEADFACE","digestType":3,"keyTag":1}
|
||||
]
|
||||
},
|
||||
"entities": [
|
||||
{
|
||||
"objectClassName" : "entity",
|
||||
"handle" : "1",
|
||||
"roles" : ["registrar"],
|
||||
"links" : [
|
||||
{
|
||||
"rel" : "self",
|
||||
"href" : "https://example.tld/rdap/entity/1",
|
||||
"type" : "application/rdap+json"
|
||||
}
|
||||
],
|
||||
"publicIds" : [
|
||||
{
|
||||
"type" : "IANA Registrar ID",
|
||||
"identifier" : "1"
|
||||
}
|
||||
],
|
||||
"vcardArray" : [
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", "%REGISTRAR_FULL_NAME_1%"]
|
||||
]
|
||||
],
|
||||
"entities" : [
|
||||
{
|
||||
"objectClassName":"entity",
|
||||
"roles":["abuse"],
|
||||
"status":["active"],
|
||||
"vcardArray": [
|
||||
"vcard",
|
||||
[
|
||||
["version",{},"text","4.0"],
|
||||
["fn",{},"text","Jake Doe"],
|
||||
["tel",{"type":["voice"]},"uri","tel:+1.2125551216"],
|
||||
["tel",{"type":["fax"]},"uri","tel:+1.2125551216"],
|
||||
["email",{},"text","jakedoe@example.com"]
|
||||
]
|
||||
]
|
||||
}
|
||||
],
|
||||
"remarks": [
|
||||
{
|
||||
"title": "Incomplete Data",
|
||||
"description": [
|
||||
"Summary data only. For complete data, send a specific query for the object."
|
||||
],
|
||||
"type": "object truncated due to unexplainable reasons"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"objectClassName": "entity",
|
||||
"handle": "",
|
||||
"roles":["administrative"],
|
||||
"remarks": [
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description": [
|
||||
"Some of the data in this object has been removed.",
|
||||
"Contact personal data is visible only to the owning registrar."
|
||||
],
|
||||
"links":[
|
||||
{
|
||||
"href":"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
|
||||
"rel":"alternate",
|
||||
"type":"text/html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title":"EMAIL REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description": [
|
||||
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
|
||||
]
|
||||
}
|
||||
],
|
||||
"vcardArray":[
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"objectClassName":"entity",
|
||||
"handle":"",
|
||||
"remarks":[
|
||||
{
|
||||
"title":"REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization",
|
||||
"description":[
|
||||
"Some of the data in this object has been removed.",
|
||||
"Contact personal data is visible only to the owning registrar."
|
||||
],
|
||||
"links":[
|
||||
{
|
||||
"href":"https://github.com/google/nomulus/blob/master/docs/rdap.md#authentication",
|
||||
"rel":"alternate",
|
||||
"type":"text/html"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"description":[
|
||||
"Please query the RDDS service of the Registrar of Record identifies in this output for information on how to contact the Registrant of the queried domain name."
|
||||
],
|
||||
"title":"EMAIL REDACTED FOR PRIVACY",
|
||||
"type":"object redacted due to authorization"
|
||||
}
|
||||
],
|
||||
"roles": ["technical"],
|
||||
"vcardArray": [
|
||||
"vcard",
|
||||
[
|
||||
["version", {}, "text", "4.0"],
|
||||
["fn", {}, "text", ""]
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+4
-4
@@ -11,9 +11,9 @@ emailAddress: the.registrar@example.com -> thase@the.registrar
|
||||
url: http://my.fake.url -> http://my.new.url
|
||||
contacts:
|
||||
ADDED:
|
||||
{name=Extra Terrestrial, emailAddress=etphonehome@example.com, registrarId=TheRegistrar, registryLockEmailAddress=null, phoneNumber=+1.2345678901, faxNumber=null, types=[ADMIN, BILLING, TECH, WHOIS], loginEmailAddress=null, visibleInWhoisAsAdmin=true, visibleInWhoisAsTech=false, visibleInDomainWhoisAsAbuse=false, allowedToSetRegistryLockPassword=false}
|
||||
{name=Extra Terrestrial, emailAddress=etphonehome@example.com, registrarId=TheRegistrar, registryLockEmailAddress=null, phoneNumber=+1.2345678901, faxNumber=null, types=[ADMIN, BILLING, TECH, WHOIS], visibleInWhoisAsAdmin=true, visibleInWhoisAsTech=false, visibleInDomainWhoisAsAbuse=false, allowedToSetRegistryLockPassword=false}
|
||||
REMOVED:
|
||||
{name=John Doe, emailAddress=johndoe@theregistrar.com, registrarId=TheRegistrar, registryLockEmailAddress=null, phoneNumber=+1.1234567890, faxNumber=null, types=[ADMIN], loginEmailAddress=johndoe@theregistrar.com, visibleInWhoisAsAdmin=false, visibleInWhoisAsTech=false, visibleInDomainWhoisAsAbuse=false, allowedToSetRegistryLockPassword=false}
|
||||
{name=John Doe, emailAddress=johndoe@theregistrar.com, registrarId=TheRegistrar, registryLockEmailAddress=null, phoneNumber=+1.1234567890, faxNumber=null, types=[ADMIN], visibleInWhoisAsAdmin=false, visibleInWhoisAsTech=false, visibleInDomainWhoisAsAbuse=false, allowedToSetRegistryLockPassword=false}
|
||||
FINAL CONTENTS:
|
||||
{name=Extra Terrestrial, emailAddress=etphonehome@example.com, registrarId=TheRegistrar, registryLockEmailAddress=null, phoneNumber=+1.2345678901, faxNumber=null, types=[ADMIN, BILLING, TECH, WHOIS], loginEmailAddress=null, visibleInWhoisAsAdmin=true, visibleInWhoisAsTech=false, visibleInDomainWhoisAsAbuse=false, allowedToSetRegistryLockPassword=false},
|
||||
{name=Marla Singer, emailAddress=Marla.Singer@crr.com, registrarId=TheRegistrar, registryLockEmailAddress=Marla.Singer.RegistryLock@crr.com, phoneNumber=+1.2128675309, faxNumber=null, types=[TECH], loginEmailAddress=Marla.Singer@crr.com, visibleInWhoisAsAdmin=false, visibleInWhoisAsTech=false, visibleInDomainWhoisAsAbuse=false, allowedToSetRegistryLockPassword=false}
|
||||
{name=Extra Terrestrial, emailAddress=etphonehome@example.com, registrarId=TheRegistrar, registryLockEmailAddress=null, phoneNumber=+1.2345678901, faxNumber=null, types=[ADMIN, BILLING, TECH, WHOIS], visibleInWhoisAsAdmin=true, visibleInWhoisAsTech=false, visibleInDomainWhoisAsAbuse=false, allowedToSetRegistryLockPassword=false},
|
||||
{name=Marla Singer, emailAddress=Marla.Singer@crr.com, registrarId=TheRegistrar, registryLockEmailAddress=Marla.Singer.RegistryLock@crr.com, phoneNumber=+1.2128675309, faxNumber=null, types=[TECH], visibleInWhoisAsAdmin=false, visibleInWhoisAsTech=false, visibleInDomainWhoisAsAbuse=false, allowedToSetRegistryLockPassword=false}
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user