1
0
mirror of https://github.com/google/nomulus synced 2026-07-19 06:22:33 +00:00

Compare commits

...

6 Commits

Author SHA1 Message Date
gbrodman 1765f4f0b4 Allow skip of emailing/uploading for activity reports (#2538)
This will help us if/when we need to run the report generation multiple
times, or for past dates and we don't want to send extra emails or
upload any extra reports to ICANN.
2024-08-26 20:25:31 +00:00
gbrodman e88c6e1550 Update activity/txn reporting to use new GAE log format (#2535)
Instead of having to parse the protoPayload.line from the request logs,
we just want to inspect the textPayload from the app logs (stored in a
separate table). This applies to the EPP metrics from the activity
reporting and the attempted-adds column for the transaction reporting.
2024-08-26 19:41:40 +00:00
Pavlo Tkach 1739c6d74f Update node.js to v22 (#2537) 2024-08-26 18:15:39 +00:00
Pavlo Tkach 66513a114e Add OT&E UI to the new console (#2536) 2024-08-23 20:53:45 +00:00
Pavlo Tkach 0e808a4c01 Add OT&E create and status to the new console (#2534) 2024-08-22 20:03:56 +00:00
Lai Jiang 4e013603be Make GKE networking work more properly (#2531) 2024-08-22 13:10:56 +00:00
96 changed files with 1728 additions and 504 deletions
+2 -1
View File
@@ -60,7 +60,7 @@ dependencyLocking {
node {
download = false
version = "20.10.0"
version = "22.7.0"
}
wrapper {
@@ -119,6 +119,7 @@ if (environment == '') {
rootProject.ext.environment = environment
rootProject.ext.gcpProject = gcpProject
rootProject.ext.baseDomain = baseDomains[environment]
rootProject.ext.prodOrSandboxEnv = environment in ['production', 'sandbox']
// Function to verify that the deployment parameters have been set.
+11 -1
View File
@@ -17,6 +17,9 @@ import { Route, RouterModule } from '@angular/router';
import { BillingInfoComponent } from './billingInfo/billingInfo.component';
import { DomainListComponent } from './domains/domainList.component';
import { HomeComponent } from './home/home.component';
import { RegistryLockVerifyComponent } from './lock/registryLockVerify.component';
import { NewOteComponent } from './ote/newOte.component';
import { OteStatusComponent } from './ote/oteStatus.component';
import { RegistrarDetailsComponent } from './registrar/registrarDetails.component';
import { RegistrarComponent } from './registrar/registrarsTable.component';
import { ResourcesComponent } from './resources/resources.component';
@@ -26,7 +29,6 @@ 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;
@@ -38,6 +40,14 @@ export const routes: RouteWithIcon[] = [
path: RegistryLockVerifyComponent.PATH,
component: RegistryLockVerifyComponent,
},
{
path: NewOteComponent.PATH,
component: NewOteComponent,
},
{
path: OteStatusComponent.PATH,
component: OteStatusComponent,
},
{ path: 'registrars', component: RegistrarComponent },
{
path: 'home',
+5 -1
View File
@@ -30,7 +30,10 @@ 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 { RegistryLockVerifyComponent } from './lock/registryLockVerify.component';
import { NavigationComponent } from './navigation/navigation.component';
import { NewOteComponent } from './ote/newOte.component';
import { OteStatusComponent } from './ote/oteStatus.component';
import NewRegistrarComponent from './registrar/newRegistrar.component';
import { RegistrarDetailsComponent } from './registrar/registrarDetails.component';
import { RegistrarSelectorComponent } from './registrar/registrarSelector.component';
@@ -54,7 +57,6 @@ 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: [
@@ -66,6 +68,8 @@ import { RegistryLockVerifyComponent } from './lock/registryLockVerify.component
HeaderComponent,
HomeComponent,
LocationBackDirective,
NewOteComponent,
OteStatusComponent,
UserLevelVisibility,
NavigationComponent,
NewRegistrarComponent,
@@ -0,0 +1,36 @@
<h1 class="mat-headline-4">Generate OT&E Accounts</h1>
<div class="console-app__new-ote">
@if (oteCreateResponseFormatted()) {
<h1>Generated Successfully</h1>
<mat-card appearance="outlined">
<mat-card-header>
<mat-card-title>Epp Credentials</mat-card-title>
<mat-card-subtitle
>Copy and paste this into an email to the registrars</mat-card-subtitle
>
</mat-card-header>
<mat-card-content>
<p>{{ oteCreateResponseFormatted() }}</p>
</mat-card-content>
</mat-card>
} @else {
<form (ngSubmit)="onSubmit()" [formGroup]="createOte">
<p>
<mat-form-field name="registrarId" appearance="outline">
<mat-label>Base Registrar Id: </mat-label>
<input matInput type="text" formControlName="registrarId" required />
</mat-form-field>
</p>
<p>
<mat-form-field name="registrarEmail" appearance="outline">
<mat-label>Contact Email: </mat-label>
<input matInput type="text" formControlName="registrarEmail" required />
<mat-hint
>Will be granted web-console access to the OTE registrars.</mat-hint
>
</mat-form-field>
</p>
<button mat-flat-button color="primary" type="submit">Save</button>
</form>
}
</div>
@@ -0,0 +1,7 @@
.console-app__new-ote {
max-width: 720px;
mat-card-content {
white-space: break-spaces;
padding: 20px;
}
}
@@ -0,0 +1,81 @@
// 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, signal } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { RegistrarService } from '../registrar/registrar.service';
export interface OteCreateResponse extends Map<string, string> {
password: string;
}
@Component({
selector: 'app-ote',
templateUrl: './newOte.component.html',
styleUrls: ['./newOte.component.scss'],
})
export class NewOteComponent {
public static PATH = 'new-ote';
oteCreateResponse = signal<OteCreateResponse | undefined>(undefined);
readonly oteCreateResponseFormatted = computed(() => {
const oteCreateResponse = this.oteCreateResponse();
if (oteCreateResponse) {
const { password } = oteCreateResponse;
return Object.entries(oteCreateResponse)
.filter((entry) => entry[0] !== 'password')
.map(
([login, tld]) =>
`Login: ${login}\t\tPassword: ${password}\t\tTLD: ${tld}`
)
.join('\n');
}
return undefined;
});
createOte = new FormGroup({
registrarId: new FormControl('', [Validators.required]),
registrarEmail: new FormControl('', [Validators.required]),
});
constructor(
protected registrarService: RegistrarService,
private _snackBar: MatSnackBar
) {}
onSubmit() {
if (this.createOte.valid) {
const { registrarId, registrarEmail } = this.createOte.value;
this.registrarService
.generateOte(
{
registrarId,
registrarEmail,
},
registrarId || ''
)
.subscribe({
next: (oteCreateResponse: OteCreateResponse) => {
this.oteCreateResponse.set(oteCreateResponse);
},
error: (err: HttpErrorResponse) => {
this._snackBar.open(err.error || err.message);
},
});
}
}
}
@@ -0,0 +1,31 @@
<app-selected-registrar-wrapper>
<h1 class="mat-headline-4">OT&E Status Check</h1>
@if(isOte()) {
<h1 *ngIf="oteStatusResponse().length">
Status:
<span>{{ oteStatusUnfinished().length ? "Unfinished" : "Completed" }}</span>
</h1>
<div class="console-app__ote-status">
@if(oteStatusCompleted().length) {
<div class="console-app__ote-status_completed">
<h1>Completed</h1>
<div *ngFor="let entry of oteStatusCompleted()">
<mat-icon>check_box</mat-icon>{{ entry.description }}
</div>
</div>
} @if(oteStatusUnfinished().length) {
<div class="console-app__ote-status_unfinished">
<h1>Unfinished</h1>
<div *ngFor="let entry of oteStatusUnfinished()">
<mat-icon>check_box_outline_blank</mat-icon>{{ entry.description }}
</div>
</div>
}
</div>
} @else {
<h1>
Registrar {{ registrarService.registrar()?.registrarId }} is not an OT&E
registrar
</h1>
}
</app-selected-registrar-wrapper>
@@ -0,0 +1,28 @@
.console-app__ote-status {
max-width: 730px;
display: flex;
flex-wrap: wrap;
&_completed,
&_unfinished {
border: 1px solid #ddd;
padding: 20px;
border-radius: 10px;
margin: 0 20px 30px 0;
div {
display: flex;
min-width: 300px;
align-items: flex-start;
max-width: 300px;
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 1px solid #ddd;
&:last-child {
border: none;
}
}
mat-icon {
min-width: 30px;
}
}
}
@@ -0,0 +1,62 @@
// 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, signal } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { RegistrarService } from '../registrar/registrar.service';
export interface OteStatusResponse {
description: string;
requirement: number;
timesPerformed: number;
completed: boolean;
}
@Component({
selector: 'app-ote-status',
templateUrl: './oteStatus.component.html',
styleUrls: ['./oteStatus.component.scss'],
})
export class OteStatusComponent {
public static PATH = 'ote-status';
oteStatusResponse = signal<OteStatusResponse[]>([]);
oteStatusCompleted = computed(() =>
this.oteStatusResponse().filter((v) => v.completed)
);
oteStatusUnfinished = computed(() =>
this.oteStatusResponse().filter((v) => !v.completed)
);
isOte = computed(
() => this.registrarService.registrar()?.type?.toLowerCase() === 'ote'
);
constructor(
protected registrarService: RegistrarService,
private _snackBar: MatSnackBar
) {
this.registrarService
.oteStatus(this.registrarService.registrarId())
.subscribe({
next: (oteStatusResponse: OteStatusResponse[]) => {
this.oteStatusResponse.set(oteStatusResponse);
},
error: (err: HttpErrorResponse) => {
this._snackBar.open(err.error || err.message);
},
});
}
}
@@ -17,6 +17,8 @@ import { Observable, switchMap, tap } from 'rxjs';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { OteCreateResponse } from '../ote/newOte.component';
import { OteStatusResponse } from '../ote/oteStatus.component';
import { BackendService } from '../shared/services/backend.service';
import {
GlobalLoader,
@@ -69,6 +71,7 @@ export interface Registrar
registrarId: string;
registrarName: string;
registryLockAllowed?: boolean;
type?: string;
}
@Injectable({
@@ -149,4 +152,17 @@ export class RegistrarService implements GlobalLoader {
loadingTimeout() {
this._snackBar.open('Timeout loading registrars');
}
generateOte(
oteForm: Object,
registrarId: string
): Observable<OteCreateResponse> {
return this.backend
.generateOte(oteForm, registrarId)
.pipe(tap((_) => this.loadRegistrars()));
}
oteStatus(registrarId: string): Observable<OteStatusResponse[]> {
return this.backend.getOteStatus(registrarId);
}
}
@@ -8,6 +8,14 @@
</button>
<div class="spacer"></div>
@if(!inEdit && !registrarNotFound) {
<button
mat-stroked-button
(click)="checkOteStatus()"
aria-label="Check OT&E account"
[elementId]="getElementIdForOteBlock()"
>
Check OT&E Status
</button>
<button
mat-flat-button
color="primary"
@@ -18,6 +18,8 @@ import { MatChipInputEvent } from '@angular/material/chips';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute, ParamMap, Router } from '@angular/router';
import { Subscription } from 'rxjs';
import { OteStatusComponent } from '../ote/oteStatus.component';
import { RESTRICTED_ELEMENTS } from '../shared/directives/userLevelVisiblity.directive';
import { Registrar, RegistrarService } from './registrar.service';
import { RegistrarComponent, columns } from './registrarsTable.component';
@@ -70,6 +72,14 @@ export class RegistrarDetailsComponent implements OnInit {
];
}
checkOteStatus() {
this.router.navigate([OteStatusComponent.PATH]);
}
getElementIdForOteBlock() {
return RESTRICTED_ELEMENTS.OTE;
}
removeTLD(tld: string) {
this.registrarInEdit.allowedTlds = this.registrarInEdit.allowedTlds?.filter(
(v) => v != tld
@@ -91,6 +101,6 @@ export class RegistrarDetailsComponent implements OnInit {
}
ngOnDestroy() {
this.subscription.unsubscribe();
this.subscription && this.subscription.unsubscribe();
}
}
@@ -4,7 +4,17 @@
<div class="console-app__registrars">
<div class="console-app__registrars-header">
<h1 class="mat-headline-4">Registrars</h1>
<div class="spacer"></div>
<button
mat-stroked-button
(click)="createOteAccount()"
aria-label="Generate OT&E accounts"
[elementId]="getElementIdForOteBlock()"
>
Create OT&E accounts
</button>
<button
class="console-app__registrars-new"
mat-flat-button
color="primary"
(click)="openNewRegistrar()"
@@ -10,6 +10,10 @@
min-width: $min-width !important;
}
&__registrars-new {
margin-left: 20px;
}
&__registrars-header {
display: flex;
justify-content: space-between;
@@ -17,6 +17,8 @@ import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Router } from '@angular/router';
import { NewOteComponent } from '../ote/newOte.component';
import { RESTRICTED_ELEMENTS } from '../shared/directives/userLevelVisiblity.directive';
import { Registrar, RegistrarService } from './registrar.service';
export const columns = [
@@ -103,6 +105,14 @@ export class RegistrarComponent {
this.dataSource.sort = this.sort;
}
createOteAccount() {
this.router.navigate([NewOteComponent.PATH]);
}
getElementIdForOteBlock() {
return RESTRICTED_ELEMENTS.OTE;
}
openDetails(registrarId: string) {
this.router.navigate(['registrars/', registrarId], {
queryParamsHandling: 'merge',
@@ -17,10 +17,11 @@ import { UserDataService } from '../services/userData.service';
export enum RESTRICTED_ELEMENTS {
REGISTRAR_ELEMENT,
OTE,
}
export const DISABLED_ELEMENTS_PER_ROLE = {
NONE: [RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT],
NONE: [RESTRICTED_ELEMENTS.REGISTRAR_ELEMENT, RESTRICTED_ELEMENTS.OTE],
};
@Directive({
@@ -19,6 +19,8 @@ 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 { OteCreateResponse } from 'src/app/ote/newOte.component';
import { OteStatusResponse } from 'src/app/ote/oteStatus.component';
import {
Registrar,
SecuritySettingsBackendModel,
@@ -198,6 +200,22 @@ export class BackendService {
.pipe(catchError((err) => this.errorCatcher<DomainLocksResult[]>(err)));
}
generateOte(
oteForm: Object,
registrarId: string
): Observable<OteCreateResponse> {
return this.http.post<OteCreateResponse>(
`/console-api/ote?registrarId=${registrarId}`,
oteForm
);
}
getOteStatus(registrarId: string) {
return this.http
.get<OteStatusResponse[]>(`/console-api/ote?registrarId=${registrarId}`)
.pipe(catchError((err) => this.errorCatcher<OteStatusResponse[]>(err)));
}
verifyRegistryLockRequest(
lockVerificationCode: string
): Observable<RegistryLockVerificationResponse> {
@@ -119,6 +119,18 @@ public final class RegistryConfig {
return config.gcpProject.projectIdNumber;
}
@Provides
@Config("backendServiceIds")
public static Map<String, Long> provideBackendServiceIds(RegistryConfigSettings config) {
return config.gcpProject.backendServiceIds;
}
@Provides
@Config("baseDomain")
public static String provideBaseDomain(RegistryConfigSettings config) {
return config.gcpProject.baseDomain;
}
@Provides
@Config("locationId")
public static String provideLocationId(RegistryConfigSettings config) {
@@ -1259,12 +1271,6 @@ public final class RegistryConfig {
return config.auth.oauthClientId;
}
@Provides
@Config("fallbackOauthClientId")
public static String provideFallbackOauthClientId(RegistryConfigSettings config) {
return config.auth.fallbackOauthClientId;
}
/**
* Provides the OAuth scopes required for accessing Google APIs using the default credential.
*/
@@ -56,13 +56,14 @@ public class RegistryConfigSettings {
public String bsaServiceUrl;
public String toolsServiceUrl;
public String pubapiServiceUrl;
public Map<String, Long> backendServiceIds;
public String baseDomain;
}
/** Configuration options for authenticating users. */
public static class Auth {
public List<String> allowedServiceAccountEmails;
public String oauthClientId;
public String fallbackOauthClientId;
}
/** Configuration options for accessing Google APIs. */
@@ -1,5 +1,5 @@
# This is the default configuration file for Nomulus. Do not make changes to it
# unless you are writing new features that requires you to. To customize an
# unless you are writing new features that require you to. To customize an
# individual deployment or environment, create a nomulus-config.yaml file in the
# WEB-INF/ directory overriding only the values you wish to change. You may need
# to override some of these values to configure and enable some services used in
@@ -24,6 +24,17 @@ gcpProject:
toolsServiceUrl: https://tools.example.com
pubapiServiceUrl: https://pubapi.example.com
# The backend service IDs created when setting up GKE routes. They will be included in the
# audience field in the JWT that IAP creates.
# See: https://cloud.google.com/iap/docs/signed-headers-howto#verifying_the_jwt_payload
backendServiceIds:
frontend: 12345
backend: 12345
pubapi: 12345
console: 12345
# The base domain name of the registry service. Services are reachable at [service].baseDomain.
baseDomain: registry.test
gSuite:
# Publicly accessible domain name of the running G Suite instance.
@@ -328,25 +339,21 @@ caching:
# Note: Only allowedServiceAccountEmails and oauthClientId should be configured.
# Other fields are related to OAuth-based authentication and will be removed.
auth:
# Service accounts (e.g. default service account, account used by Cloud
# Service accounts (e.g., default service account, account used by Cloud
# Scheduler) allowed to send authenticated requests.
allowedServiceAccountEmails:
- default-service-account-email@email.com
- cloud-scheduler-email@email.com
# OAuth 2.0 client ID that will be used as the audience in OIDC ID tokens sent
# from clients (e.g. proxy, nomulus tool, cloud tasks) for authentication. The
# from clients (e.g., proxy, nomulus tool, cloud tasks) for authentication. The
# same ID is the only one accepted by the regular OIDC or IAP authentication
# mechanisms. In most cases we should use the client ID created for IAP here,
# mechanisms. In most cases, we should use the client ID created for IAP here,
# as it allows requests bearing a token with this audience to be accepted by
# both IAP or regular OIDC. The clientId value in proxy config file should be
# the same as this one.
oauthClientId: iap-oauth-clientid
# Same as above, but serve as a fallback, so we can switch the client ID of
# the proxy without downtime.
fallbackOauthClientId: fallback-oauth-clientid
credentialOAuth:
# OAuth scopes required for accessing Google APIs using the default
# credential.
@@ -36,7 +36,7 @@ import javax.inject.Inject;
/** Action that manually triggers refresh of DNS information. */
@Action(
service = Action.Service.BACKEND,
path = "/_dr/dnsRefresh",
path = "/_dr/task/dnsRefresh",
automaticallyPrintOk = true,
auth = Auth.AUTH_ADMIN)
public final class RefreshDnsAction implements Runnable {
@@ -165,7 +165,7 @@
<!-- Manually refreshes DNS information. -->
<servlet-mapping>
<servlet-name>backend-servlet</servlet-name>
<url-pattern>/_dr/dnsRefresh</url-pattern>
<url-pattern>/_dr/task/dnsRefresh</url-pattern>
</servlet-mapping>
<!-- Fans out a cron task over an adjustable range of TLDs. -->
@@ -233,6 +233,7 @@ public class RegistrarBase extends UpdateAutoTimestampEntity implements Buildabl
String registrarName;
/** The type of this registrar. */
@Expose
@Column(nullable = false)
@Enumerated(EnumType.STRING)
Type type;
@@ -102,9 +102,11 @@ interface RegistryComponent {
class RegistryModule {
@Provides
static RequestHandler<RequestComponent> provideRequestHandler(
@Config("baseDomain") String baseDomain,
Provider<RequestComponent.Builder> componentProvider,
RequestAuthenticator requestAuthenticator) {
return RequestHandler.create(RequestComponent.class, componentProvider, requestAuthenticator);
return RequestHandler.create(
RequestComponent.class, baseDomain, componentProvider, requestAuthenticator);
}
}
}
@@ -113,6 +113,8 @@ import google.registry.ui.server.console.ConsoleDomainGetAction;
import google.registry.ui.server.console.ConsoleDomainListAction;
import google.registry.ui.server.console.ConsoleDumDownloadAction;
import google.registry.ui.server.console.ConsoleEppPasswordAction;
import google.registry.ui.server.console.ConsoleModule;
import google.registry.ui.server.console.ConsoleOteAction;
import google.registry.ui.server.console.ConsoleRegistryLockAction;
import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
@@ -121,15 +123,6 @@ import google.registry.ui.server.console.RegistrarsAction;
import google.registry.ui.server.console.settings.ContactAction;
import google.registry.ui.server.console.settings.SecurityAction;
import google.registry.ui.server.console.settings.WhoisRegistrarFieldsAction;
import google.registry.ui.server.registrar.ConsoleOteSetupAction;
import google.registry.ui.server.registrar.ConsoleRegistrarCreatorAction;
import google.registry.ui.server.registrar.ConsoleUiAction;
import google.registry.ui.server.registrar.OteStatusAction;
import google.registry.ui.server.registrar.RegistrarConsoleModule;
import google.registry.ui.server.registrar.RegistrarSettingsAction;
import google.registry.ui.server.registrar.RegistryLockGetAction;
import google.registry.ui.server.registrar.RegistryLockPostAction;
import google.registry.ui.server.registrar.RegistryLockVerifyAction;
import google.registry.whois.WhoisAction;
import google.registry.whois.WhoisHttpAction;
import google.registry.whois.WhoisModule;
@@ -142,6 +135,7 @@ import google.registry.whois.WhoisModule;
BillingModule.class,
CheckApiModule.class,
CloudDnsWriterModule.class,
ConsoleModule.class,
CronModule.class,
CustomLogicModule.class,
DnsCountQueryCoordinatorModule.class,
@@ -154,7 +148,6 @@ import google.registry.whois.WhoisModule;
LoadTestModule.class,
RdapModule.class,
RdeModule.class,
RegistrarConsoleModule.class,
ReportingModule.class,
RequestModule.class,
SheetModule.class,
@@ -186,16 +179,12 @@ interface RequestComponent {
ConsoleEppPasswordAction consoleEppPasswordAction();
ConsoleOteSetupAction consoleOteSetupAction();
ConsoleRegistrarCreatorAction consoleRegistrarCreatorAction();
ConsoleOteAction consoleOteAction();
ConsoleRegistryLockAction consoleRegistryLockAction();
ConsoleRegistryLockVerifyAction consoleRegistryLockVerifyAction();
ConsoleUiAction consoleUiAction();
ConsoleUpdateRegistrarAction consoleUpdateRegistrarAction();
ConsoleUserDataAction consoleUserDataAction();
@@ -254,8 +243,6 @@ interface RequestComponent {
NordnVerifyAction nordnVerifyAction();
OteStatusAction oteStatusAction();
PublishDnsUpdatesAction publishDnsUpdatesAction();
PublishInvoicesAction uploadInvoicesAction();
@@ -296,16 +283,8 @@ interface RequestComponent {
RefreshDnsOnHostRenameAction refreshDnsOnHostRenameAction();
RegistrarSettingsAction registrarSettingsAction();
RegistrarsAction registrarsAction();
RegistryLockGetAction registryLockGetAction();
RegistryLockPostAction registryLockPostAction();
RegistryLockVerifyAction registryLockVerifyAction();
RelockDomainAction relockDomainAction();
ResaveAllEppResourcesPipelineAction resaveAllEppResourcesPipelineAction();
@@ -29,6 +29,8 @@ import google.registry.ui.server.console.ConsoleDomainGetAction;
import google.registry.ui.server.console.ConsoleDomainListAction;
import google.registry.ui.server.console.ConsoleDumDownloadAction;
import google.registry.ui.server.console.ConsoleEppPasswordAction;
import google.registry.ui.server.console.ConsoleModule;
import google.registry.ui.server.console.ConsoleOteAction;
import google.registry.ui.server.console.ConsoleRegistryLockAction;
import google.registry.ui.server.console.ConsoleRegistryLockVerifyAction;
import google.registry.ui.server.console.ConsoleUpdateRegistrarAction;
@@ -41,7 +43,6 @@ import google.registry.ui.server.registrar.ConsoleOteSetupAction;
import google.registry.ui.server.registrar.ConsoleRegistrarCreatorAction;
import google.registry.ui.server.registrar.ConsoleUiAction;
import google.registry.ui.server.registrar.OteStatusAction;
import google.registry.ui.server.registrar.RegistrarConsoleModule;
import google.registry.ui.server.registrar.RegistrarSettingsAction;
import google.registry.ui.server.registrar.RegistryLockGetAction;
import google.registry.ui.server.registrar.RegistryLockPostAction;
@@ -54,7 +55,7 @@ import google.registry.ui.server.registrar.RegistryLockVerifyAction;
BatchModule.class,
DnsModule.class,
EppTlsModule.class,
RegistrarConsoleModule.class,
ConsoleModule.class,
RequestModule.class,
WhiteboxModule.class,
})
@@ -65,6 +66,8 @@ public interface FrontendRequestComponent {
ConsoleEppPasswordAction consoleEppPasswordAction();
ConsoleOteAction consoleOteAction();
ConsoleOteSetupAction consoleOteSetupAction();
ConsoleRegistrarCreatorAction consoleRegistrarCreatorAction();
@@ -82,7 +82,6 @@ public final class ActivityReportingQueryBuilder implements QueryBuilder {
// Convert reportingMonth into YYYYMMDD format for Bigquery table partition pattern-matching.
DateTimeFormatter logTableFormatter = DateTimeFormat.forPattern("yyyyMMdd");
// The monthly logs are a shared dependency for epp counts and whois metrics
String monthlyLogsQuery =
SqlTemplate.create(getQueryFromFile("monthly_logs.sql"))
.put("PROJECT_ID", projectId)
@@ -96,12 +95,10 @@ public final class ActivityReportingQueryBuilder implements QueryBuilder {
String eppQuery =
SqlTemplate.create(getQueryFromFile("epp_metrics.sql"))
.put("PROJECT_ID", projectId)
.put("ICANN_REPORTING_DATA_SET", icannReportingDataSet)
.put("MONTHLY_LOGS_TABLE", getTableName(MONTHLY_LOGS, yearMonth))
// All metadata logs for reporting come from google.registry.flows.FlowReporter.
.put(
"METADATA_LOG_PREFIX",
"google.registry.flows.FlowReporter recordToLogs: FLOW-LOG-SIGNATURE-METADATA")
.put("APPENGINE_LOGS_DATA_SET", "appengine_logs")
.put("APP_LOGS_TABLE", "_var_log_app_")
.put("FIRST_DAY_OF_MONTH", logTableFormatter.print(firstDayOfMonth))
.put("LAST_DAY_OF_MONTH", logTableFormatter.print(lastDayOfMonth))
.build();
queriesBuilder.put(getTableName(EPP_METRICS, yearMonth), eppQuery);
@@ -14,6 +14,7 @@
package google.registry.reporting.icann;
import static google.registry.request.RequestParameters.extractOptionalBooleanParameter;
import static google.registry.request.RequestParameters.extractOptionalParameter;
import static google.registry.request.RequestParameters.extractRequiredParameter;
import static google.registry.request.RequestParameters.extractSetOfEnumParameters;
@@ -42,6 +43,7 @@ public final class IcannReportingModule {
static final String PARAM_SUBDIR = "subdir";
static final String PARAM_REPORT_TYPES = "reportTypes";
static final String PARAM_SHOULD_UPLOAD = "shouldUpload";
static final String ICANN_REPORTING_DATA_SET = "icannReportingDataSet";
static final String MANIFEST_FILE_NAME = "MANIFEST.txt";
@@ -76,6 +78,12 @@ public final class IcannReportingModule {
return reportTypes.isEmpty() ? ImmutableSet.copyOf(ReportType.values()) : reportTypes;
}
@Provides
@Parameter(PARAM_SHOULD_UPLOAD)
static boolean provideShouldUpload(HttpServletRequest req) {
return extractOptionalBooleanParameter(req, PARAM_SHOULD_UPLOAD).orElse(true);
}
/**
* Constructs a BigqueryConnection with default settings.
*
@@ -16,6 +16,7 @@ package google.registry.reporting.icann;
import static com.google.common.base.Throwables.getRootCause;
import static google.registry.reporting.icann.IcannReportingModule.PARAM_REPORT_TYPES;
import static google.registry.reporting.icann.IcannReportingModule.PARAM_SHOULD_UPLOAD;
import static google.registry.reporting.icann.IcannReportingModule.PARAM_SUBDIR;
import static google.registry.request.Action.Method.POST;
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
@@ -30,6 +31,7 @@ import google.registry.batch.CloudTasksUtils;
import google.registry.bigquery.BigqueryJobFailureException;
import google.registry.config.RegistryConfig.Config;
import google.registry.groups.GmailClient;
import google.registry.reporting.ReportingModule;
import google.registry.reporting.icann.IcannReportingModule.ReportType;
import google.registry.request.Action;
import google.registry.request.Action.Service;
@@ -79,6 +81,15 @@ public final class IcannReportingStagingAction implements Runnable {
@Inject YearMonth yearMonth;
@Inject @Parameter(PARAM_SUBDIR) Optional<String> overrideSubdir;
@Inject @Parameter(PARAM_REPORT_TYPES) ImmutableSet<ReportType> reportTypes;
@Inject
@Parameter(ReportingModule.SEND_EMAIL)
boolean sendEmail;
@Inject
@Parameter(PARAM_SHOULD_UPLOAD)
boolean shouldUpload;
@Inject IcannReportingStager stager;
@Inject Retrier retrier;
@Inject Response response;
@@ -106,28 +117,35 @@ public final class IcannReportingStagingAction implements Runnable {
stager.createAndUploadManifest(subdir, manifestedFiles);
logger.atInfo().log("Completed staging %d report files.", manifestedFiles.size());
gmailClient.sendEmail(
EmailMessage.newBuilder()
.setSubject("ICANN Monthly report staging summary [SUCCESS]")
.setBody(
String.format(
"Completed staging the following %d ICANN reports:\n%s",
manifestedFiles.size(), Joiner.on('\n').join(manifestedFiles)))
.addRecipient(recipient)
.build());
if (sendEmail) {
gmailClient.sendEmail(
EmailMessage.newBuilder()
.setSubject("ICANN Monthly report staging summary [SUCCESS]")
.setBody(
String.format(
"Completed staging the following %d ICANN reports:\n%s",
manifestedFiles.size(), Joiner.on('\n').join(manifestedFiles)))
.addRecipient(recipient)
.build());
} else {
logger.atInfo().log("Would have sent staging report summary to %s", recipient);
}
response.setStatus(SC_OK);
response.setContentType(MediaType.PLAIN_TEXT_UTF_8);
response.setPayload("Completed staging action.");
logger.atInfo().log("Enqueueing report upload.");
cloudTasksUtils.enqueue(
CRON_QUEUE,
cloudTasksUtils.createPostTaskWithDelay(
IcannReportingUploadAction.PATH,
Service.BACKEND,
null,
Duration.standardMinutes(2)));
if (shouldUpload) {
logger.atInfo().log("Enqueueing report upload.");
cloudTasksUtils.enqueue(
CRON_QUEUE,
cloudTasksUtils.createPostTaskWithDelay(
IcannReportingUploadAction.PATH,
Service.BACKEND,
null,
Duration.standardMinutes(2)));
} else {
logger.atInfo().log("Would have enqueued report upload");
}
return null;
},
BigqueryJobFailureException.class);
@@ -113,13 +113,9 @@ public final class TransactionsReportingQueryBuilder implements QueryBuilder {
SqlTemplate.create(getQueryFromFile("cloud_sql_attempted_adds.sql"))
.put("PROJECT_ID", projectId)
.put("APPENGINE_LOGS_DATA_SET", "appengine_logs")
.put("REQUEST_TABLE", "appengine_googleapis_com_request_log_")
.put("APP_LOGS_TABLE", "_var_log_app_")
.put("FIRST_DAY_OF_MONTH", logTableFormatter.print(earliestReportTime))
.put("LAST_DAY_OF_MONTH", logTableFormatter.print(latestReportTime))
// All metadata logs for reporting come from google.registry.flows.FlowReporter.
.put(
"METADATA_LOG_PREFIX",
"google.registry.flows.FlowReporter recordToLogs: FLOW-LOG-SIGNATURE-METADATA")
.build();
queriesBuilder.put(getTableName(ATTEMPTED_ADDS, yearMonth), attemptedAddsQuery);
@@ -14,6 +14,8 @@
package google.registry.request;
import static com.google.common.base.Preconditions.checkState;
import google.registry.request.auth.Auth;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -36,7 +38,6 @@ public @interface Action {
BACKEND("backend"),
PUBAPI("pubapi");
private final String serviceId;
Service(String serviceId) {
@@ -49,9 +50,33 @@ public @interface Action {
}
}
enum GkeService {
// This designation means that it defers to the GAE service, so we don't have to annotate EVERY
// action during the GKE migration.
SAME_AS_GAE("same_as_gae"),
FRONTEND("frontend"),
BACKEND("backend"),
PUBAPI("pubapi"),
CONSOLE("console");
private final String serviceId;
GkeService(String serviceId) {
this.serviceId = serviceId;
}
public String getServiceId() {
checkState(this != SAME_AS_GAE, "Cannot get service Id for SAME_AS_GAE");
return serviceId;
}
}
/** Which App Engine service this action lives on. */
Service service();
/** Which GKE service this action lives on. */
GkeService gkeService() default GkeService.SAME_AS_GAE;
/** HTTP path to serve the action from. The path components must be percent-escaped. */
String path();
@@ -72,4 +97,22 @@ public @interface Action {
/** Authentication settings. */
Auth auth();
// TODO(jianglai): Use Action.gkeService() directly once we are off GAE.
class ServiceGetter {
public static GkeService get(Action action) {
GkeService service = action.gkeService();
if (service != GkeService.SAME_AS_GAE) {
return service;
}
Service gaeService = action.service();
return switch (gaeService) {
case DEFAULT -> GkeService.FRONTEND;
case BACKEND -> GkeService.BACKEND;
case TOOLS -> GkeService.BACKEND;
case BSA -> GkeService.BACKEND;
case PUBAPI -> GkeService.PUBAPI;
};
}
}
}
@@ -22,14 +22,17 @@ import static jakarta.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import com.google.common.flogger.FluentLogger;
import google.registry.request.Action.GkeService;
import google.registry.request.auth.AuthResult;
import google.registry.request.auth.RequestAuthenticator;
import google.registry.util.NonFinalForTesting;
import google.registry.util.RegistryEnvironment;
import google.registry.util.SystemClock;
import google.registry.util.TypeUtils.TypeInstantiator;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.inject.Provider;
@@ -69,6 +72,7 @@ public class RequestHandler<C> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private final Router router;
@Nullable private final String baseDomain;
private final Provider<? extends RequestComponentBuilder<C>> requestComponentBuilderProvider;
private final RequestAuthenticator requestAuthenticator;
private final SystemClock clock = new SystemClock();
@@ -91,22 +95,22 @@ public class RequestHandler<C> {
protected RequestHandler(
Provider<? extends RequestComponentBuilder<C>> requestComponentBuilderProvider,
RequestAuthenticator requestAuthenticator) {
this(null, requestComponentBuilderProvider, requestAuthenticator);
this(null, null, requestComponentBuilderProvider, requestAuthenticator);
}
/** Creates a new RequestHandler with an explicit component class for test purposes. */
public static <C> RequestHandler<C> create(
Class<C> component,
@Nullable String baseDomain,
Provider<? extends RequestComponentBuilder<C>> requestComponentBuilderProvider,
RequestAuthenticator requestAuthenticator) {
return new RequestHandler<>(
checkNotNull(component),
requestComponentBuilderProvider,
requestAuthenticator);
checkNotNull(component), baseDomain, requestComponentBuilderProvider, requestAuthenticator);
}
private RequestHandler(
@Nullable Class<C> component,
@Nullable String baseDomain,
Provider<? extends RequestComponentBuilder<C>> requestComponentBuilderProvider,
RequestAuthenticator requestAuthenticator) {
// If the component class isn't explicitly provided, infer it from the class's own typing.
@@ -114,6 +118,7 @@ public class RequestHandler<C> {
// preserved at runtime, so only expose that option via the protected constructor.
this.router = Router.create(
component != null ? component : new TypeInstantiator<C>(getClass()){}.getExactType());
this.baseDomain = baseDomain;
this.requestComponentBuilderProvider = checkNotNull(requestComponentBuilderProvider);
this.requestAuthenticator = checkNotNull(requestAuthenticator);
}
@@ -137,6 +142,17 @@ public class RequestHandler<C> {
rsp.sendError(SC_NOT_FOUND);
return;
}
if (RegistryEnvironment.isOnJetty()) {
GkeService service = Action.ServiceGetter.get(route.get().action());
String expectedDomain = String.format("%s.%s", service.getServiceId(), baseDomain);
String actualDomain = req.getServerName();
if (!Objects.equals(actualDomain, expectedDomain)) {
logger.atWarning().log(
"Actual domain %s does not match expected domain %s", actualDomain, expectedDomain);
rsp.sendError(SC_NOT_FOUND);
return;
}
}
if (!route.get().isMethodAllowed(method)) {
logger.atWarning().log("Method %s not allowed for: %s", method, path);
rsp.sendError(SC_METHOD_NOT_ALLOWED);
@@ -21,6 +21,7 @@ import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Streams;
import java.util.Comparator;
import java.util.Map;
/**
@@ -37,6 +38,7 @@ import java.util.Map;
* the content to be displayed. The columns are:
*
* <ol>
* <li>the GKE service this action lives on
* <li>the URL path which maps to this action (with a "(*)" after it if the prefix flag is set)
* <li>the simple name of the action class
* <li>the allowable HTTP methods
@@ -49,12 +51,13 @@ import java.util.Map;
*/
public class RouterDisplayHelper {
private static final String SERVICE = "service";
private static final String PATH = "path";
private static final String CLASS = "class";
private static final String METHODS = "methods";
private static final String MINIMUM_LEVEL = "minLevel";
private static final String FORMAT = "%%-%ds %%-%ds %%-%ds %%-2s %%-%ds %%s";
private static final String FORMAT = "%%-%ds %%-%ds %%-%ds %%-%ds %%-2s %%-%ds %%s";
/** Returns a string representation of the routing map in the specified component. */
public static String extractHumanReadableRoutesFromComponent(Class<?> componentClass) {
@@ -76,6 +79,7 @@ public class RouterDisplayHelper {
private static String getFormatString(Map<String, Integer> columnWidths) {
return String.format(
FORMAT,
columnWidths.get(SERVICE),
columnWidths.get(PATH),
columnWidths.get(CLASS),
columnWidths.get(METHODS),
@@ -84,18 +88,13 @@ public class RouterDisplayHelper {
private static String headerToString(String formatString) {
return String.format(
formatString,
"PATH",
"CLASS",
"METHODS",
"OK",
"MIN",
"USER_POLICY");
formatString, "SERVICE", "PATH", "CLASS", "METHODS", "OK", "MIN", "USER_POLICY");
}
private static String routeToString(Route route, String formatString) {
return String.format(
formatString,
Action.ServiceGetter.get(route.action()).name(),
route.action().isPrefix() ? (route.action().path() + "(*)") : route.action().path(),
route.actionClass().getSimpleName(),
Joiner.on(",").join(route.action().method()),
@@ -107,12 +106,17 @@ public class RouterDisplayHelper {
private static String formatRoutes(Iterable<Route> routes) {
// Use the column header length as a minimum.
int serviceWidth = 7;
int pathWidth = 4;
int classWidth = 5;
int methodsWidth = 7;
int minLevelWidth = 3;
for (Route route : routes) {
int len =
int len = Action.ServiceGetter.get(route.action()).name().length();
if (len > serviceWidth) {
serviceWidth = len;
}
len =
route.action().isPrefix()
? (route.action().path().length() + 3)
: route.action().path().length();
@@ -135,6 +139,7 @@ public class RouterDisplayHelper {
final String formatString =
getFormatString(
new ImmutableMap.Builder<String, Integer>()
.put(SERVICE, serviceWidth)
.put(PATH, pathWidth)
.put(CLASS, classWidth)
.put(METHODS, methodsWidth)
@@ -143,6 +148,9 @@ public class RouterDisplayHelper {
return headerToString(formatString)
+ String.format("%n")
+ Streams.stream(routes)
.sorted(
Comparator.comparing(
(Route route) -> Action.ServiceGetter.get(route.action()).ordinal()))
.map(route -> routeToString(route, formatString))
.collect(joining(String.format("%n")));
}
@@ -16,7 +16,6 @@ package google.registry.request.auth;
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
import com.google.auth.oauth2.TokenVerifier;
import com.google.common.collect.ImmutableList;
import dagger.Module;
import dagger.Provides;
@@ -24,6 +23,10 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.request.auth.OidcTokenAuthenticationMechanism.IapOidcAuthenticationMechanism;
import google.registry.request.auth.OidcTokenAuthenticationMechanism.RegularOidcAuthenticationMechanism;
import google.registry.request.auth.OidcTokenAuthenticationMechanism.TokenExtractor;
import google.registry.request.auth.OidcTokenAuthenticationMechanism.TokenVerifier;
import google.registry.util.RegistryEnvironment;
import java.util.Map;
import javax.annotation.Nullable;
import javax.inject.Qualifier;
import javax.inject.Singleton;
@@ -35,9 +38,10 @@ public class AuthModule {
// See https://cloud.google.com/iap/docs/signed-headers-howto#securing_iap_headers.
public static final String IAP_HEADER_NAME = "X-Goog-IAP-JWT-Assertion";
public static final String BEARER_PREFIX = "Bearer ";
// TODO: Change the IAP audience format once we are on GKE.
// TODO (jianglai): Only use GKE audience once we are fully migrated to GKE.
// See: https://cloud.google.com/iap/docs/signed-headers-howto#verifying_the_jwt_payload
private static final String IAP_AUDIENCE_FORMAT = "/projects/%d/apps/%s";
private static final String IAP_GAE_AUDIENCE_FORMAT = "/projects/%d/apps/%s";
private static final String IAP_GKE_AUDIENCE_FORMAT = "/projects/%d/global/backendServices/%d";
private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap";
private static final String REGULAR_ISSUER_URL = "https://accounts.google.com";
@@ -62,24 +66,35 @@ public class AuthModule {
@IapOidc
@Singleton
TokenVerifier provideIapTokenVerifier(
@Config("projectId") String projectId, @Config("projectIdNumber") long projectIdNumber) {
String audience = String.format(IAP_AUDIENCE_FORMAT, projectIdNumber, projectId);
return TokenVerifier.newBuilder().setAudience(audience).setIssuer(IAP_ISSUER_URL).build();
@Config("projectId") String projectId,
@Config("projectIdNumber") long projectIdNumber,
@Config("backendServiceIds") Map<String, Long> backendServiceIds) {
com.google.auth.oauth2.TokenVerifier.Builder tokenVerifierBuilder =
com.google.auth.oauth2.TokenVerifier.newBuilder().setIssuer(IAP_ISSUER_URL);
return (String service, String token) -> {
String audience;
if (RegistryEnvironment.isOnJetty()) {
long backendServiceId = backendServiceIds.get(service);
audience = String.format(IAP_GKE_AUDIENCE_FORMAT, projectIdNumber, backendServiceId);
} else {
audience = String.format(IAP_GAE_AUDIENCE_FORMAT, projectIdNumber, projectId);
}
return tokenVerifierBuilder.setAudience(audience).build().verify(token);
};
}
@Provides
@RegularOidc
@Singleton
TokenVerifier provideRegularTokenVerifier(@Config("oauthClientId") String clientId) {
return TokenVerifier.newBuilder().setAudience(clientId).setIssuer(REGULAR_ISSUER_URL).build();
}
@Provides
@RegularOidcFallback
@Singleton
TokenVerifier provideFallbackRegularTokenVerifier(
@Config("fallbackOauthClientId") String clientId) {
return TokenVerifier.newBuilder().setAudience(clientId).setIssuer(REGULAR_ISSUER_URL).build();
com.google.auth.oauth2.TokenVerifier tokenVerifier =
com.google.auth.oauth2.TokenVerifier.newBuilder()
.setAudience(clientId)
.setIssuer(REGULAR_ISSUER_URL)
.build();
return (@Nullable String service, String token) -> {
return tokenVerifier.verify(token);
};
}
@Provides
@@ -18,8 +18,9 @@ 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;
import com.google.auth.oauth2.TokenVerifier.VerificationException;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import google.registry.config.RegistryConfig.Config;
@@ -27,7 +28,6 @@ import google.registry.model.console.User;
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;
import google.registry.request.auth.AuthSettings.AuthLevel;
import google.registry.util.RegistryEnvironment;
import jakarta.servlet.http.HttpServletRequest;
@@ -51,27 +51,23 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
public static final FluentLogger logger = FluentLogger.forEnclosingClass();
// A workaround that allows "use" of the OIDC authenticator when running local testing, i.e.
// A workaround that allows "use" of the OIDC authenticator when running local testing, i.e.,
// the RegistryTestServer
private static AuthResult authResultForTesting = null;
protected final TokenVerifier tokenVerifier;
protected final Optional<TokenVerifier> fallbackTokenVerifier;
protected final TokenExtractor tokenExtractor;
protected final TokenVerifier tokenVerifier;
private final ImmutableSet<String> serviceAccountEmails;
protected OidcTokenAuthenticationMechanism(
ImmutableSet<String> serviceAccountEmails,
TokenVerifier tokenVerifier,
@Nullable TokenVerifier fallbackTokenVerifier,
TokenExtractor tokenExtractor) {
TokenExtractor tokenExtractor,
TokenVerifier tokenVerifier) {
this.serviceAccountEmails = serviceAccountEmails;
this.tokenVerifier = tokenVerifier;
this.fallbackTokenVerifier = Optional.ofNullable(fallbackTokenVerifier);
this.tokenExtractor = tokenExtractor;
this.tokenVerifier = tokenVerifier;
}
@Override
@@ -87,7 +83,12 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
}
JsonWebSignature token = null;
try {
token = tokenVerifier.verify(rawIdToken);
String service = null;
if (RegistryEnvironment.isOnJetty()) {
String hostname = request.getServerName();
service = Splitter.on('.').split(hostname).iterator().next();
}
token = tokenVerifier.verify(service, rawIdToken);
} catch (Exception e) {
logger.atInfo().withCause(e).log(
"Failed OIDC verification attempt:\n%s",
@@ -97,20 +98,7 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
}
if (token == null) {
if (fallbackTokenVerifier.isPresent()) {
try {
token = fallbackTokenVerifier.get().verify(rawIdToken);
} catch (Exception e) {
logger.atInfo().withCause(e).log(
"Failed OIDC fallback verification attempt:\n%s",
RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION)
? "Raw token redacted in prod"
: rawIdToken);
return AuthResult.NOT_AUTHENTICATED;
}
} else {
return AuthResult.NOT_AUTHENTICATED;
}
return AuthResult.NOT_AUTHENTICATED;
}
String email = (String) token.getPayload().get("email");
@@ -155,6 +143,12 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
String extract(HttpServletRequest request);
}
@FunctionalInterface
protected interface TokenVerifier {
@Nullable
JsonWebSignature verify(@Nullable String service, String rawToken) throws VerificationException;
}
/**
* A mechanism to authenticate HTTP requests that have gone through the GCP Identity-Aware Proxy.
*
@@ -171,9 +165,9 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
@Inject
protected IapOidcAuthenticationMechanism(
@Config("allowedServiceAccountEmails") ImmutableSet<String> serviceAccountEmails,
@IapOidc TokenVerifier tokenVerifier,
@IapOidc TokenExtractor tokenExtractor) {
super(serviceAccountEmails, tokenVerifier, null, tokenExtractor);
@IapOidc TokenExtractor tokenExtractor,
@IapOidc TokenVerifier tokenVerifier) {
super(serviceAccountEmails, tokenExtractor, tokenVerifier);
}
}
@@ -192,10 +186,9 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
@Inject
protected RegularOidcAuthenticationMechanism(
@Config("allowedServiceAccountEmails") ImmutableSet<String> serviceAccountEmails,
@RegularOidc TokenVerifier tokenVerifier,
@RegularOidcFallback TokenVerifier fallbackTokenVerifier,
@RegularOidc TokenExtractor tokenExtractor) {
super(serviceAccountEmails, tokenVerifier, fallbackTokenVerifier, tokenExtractor);
@RegularOidc TokenExtractor tokenExtractor,
@RegularOidc TokenVerifier tokenVerifier) {
super(serviceAccountEmails, tokenExtractor, tokenVerifier);
}
}
}
@@ -44,7 +44,6 @@ import google.registry.model.registrar.RegistrarPocBase;
import google.registry.request.Action.Service;
import google.registry.request.HttpException;
import google.registry.security.XsrfTokenManager;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.ui.server.registrar.ConsoleUiAction;
import google.registry.util.DiffUtils;
import google.registry.util.RegistryEnvironment;
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.ui.server.registrar;
package google.registry.ui.server.console;
import google.registry.request.Response;
import google.registry.request.auth.AuthResult;
@@ -24,15 +24,16 @@ import google.registry.model.console.ConsolePermission;
import google.registry.model.console.User;
import google.registry.model.domain.Domain;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.ui.server.registrar.ConsoleApiParams;
import java.util.Optional;
import javax.inject.Inject;
/** Returns a JSON representation of a domain to the registrar console. */
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = ConsoleDomainGetAction.PATH,
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
public class ConsoleDomainGetAction extends ConsoleApiAction {
@@ -27,9 +27,9 @@ import google.registry.model.CreateAutoTimestamp;
import google.registry.model.console.User;
import google.registry.model.domain.Domain;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.ui.server.registrar.ConsoleApiParams;
import java.util.List;
import java.util.Optional;
import javax.inject.Inject;
@@ -39,6 +39,7 @@ import org.joda.time.DateTime;
/** Returns a (paginated) list of domains for a particular registrar. */
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = ConsoleDomainListAction.PATH,
method = Action.Method.GET,
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
@@ -26,9 +26,9 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.model.console.ConsolePermission;
import google.registry.model.console.User;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.util.Clock;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -40,6 +40,7 @@ import org.joda.time.DateTime;
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = ConsoleDumDownloadAction.PATH,
method = {GET},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
@@ -30,17 +30,18 @@ import google.registry.flows.PasswordOnlyTransportCredentials;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.util.DiffUtils;
import java.util.Optional;
import javax.inject.Inject;
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = ConsoleEppPasswordAction.PATH,
method = {POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.ui.server.registrar;
package google.registry.ui.server.console;
import static google.registry.request.RequestParameters.extractBooleanParameter;
import static google.registry.request.RequestParameters.extractOptionalIntParameter;
@@ -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.ConsoleOteAction.OteCreateData;
import google.registry.ui.server.console.ConsoleRegistryLockAction.ConsoleRegistryLockPostInput;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Optional;
@@ -41,8 +42,8 @@ import org.joda.time.DateTime;
/** Dagger module for the Registrar Console parameters. */
@Module
public final class RegistrarConsoleModule {
static final String PARAM_CLIENT_ID = "clientId";
public final class ConsoleModule {
public static final String PARAM_CLIENT_ID = "clientId";
@Provides
@RequestScope
@@ -244,6 +245,13 @@ public final class RegistrarConsoleModule {
return payload.map(s -> gson.fromJson(s, EppPasswordData.class));
}
@Provides
@Parameter("oteCreateData")
public static Optional<OteCreateData> provideOteCreateData(
Gson gson, @OptionalJsonPayload Optional<JsonElement> payload) {
return payload.map(s -> gson.fromJson(s, OteCreateData.class));
}
@Provides
@Parameter("consoleRegistryLockPostInput")
public static Optional<ConsoleRegistryLockPostInput> provideRegistryLockPostInput(
@@ -0,0 +1,174 @@
// 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.
package google.registry.ui.server.console;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
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.util.RegistryEnvironment.PRODUCTION;
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 com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.google.gson.annotations.Expose;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.OteAccountBuilder;
import google.registry.model.OteStats;
import google.registry.model.OteStats.StatType;
import google.registry.model.console.ConsolePermission;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarBase;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.tools.IamClient;
import google.registry.util.RegistryEnvironment;
import google.registry.util.StringGenerator;
import java.util.Map;
import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Named;
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = ConsoleOteAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
public class ConsoleOteAction extends ConsoleApiAction {
static final String PATH = "/console-api/ote";
private static final int PASSWORD_LENGTH = 16;
private static final String COMPLETED_PARAM = "completed";
private static final String STAT_TYPE_DESCRIPTION_PARAM = "description";
private static final String STAT_TYPE_REQUIREMENT_PARAM = "requirement";
private static final String STAT_TYPE_TIMES_PERFORMED_PARAM = "timesPerformed";
private final Gson gson;
private final StringGenerator passwordGenerator;
private final Optional<OteCreateData> oteCreateData;
private final Optional<String> maybeGroupEmailAddress;
private final IamClient iamClient;
private final String registrarId;
@Inject
public ConsoleOteAction(
ConsoleApiParams consoleApiParams,
Gson gson,
IamClient iamClient,
@Parameter("registrarId") String registrarId, // Get request param
@Config("gSuiteConsoleUserGroupEmailAddress") Optional<String> maybeGroupEmailAddress,
@Named("base58StringGenerator") StringGenerator passwordGenerator,
@Parameter("oteCreateData") Optional<OteCreateData> oteCreateData) {
super(consoleApiParams);
this.gson = gson;
this.passwordGenerator = passwordGenerator;
this.oteCreateData = oteCreateData;
this.maybeGroupEmailAddress = maybeGroupEmailAddress;
this.iamClient = iamClient;
this.registrarId = registrarId;
}
@Override
protected void postHandler(User user) {
checkState(!RegistryEnvironment.get().equals(PRODUCTION), "Can't create OT&E in prod");
if (!user.getUserRoles().hasGlobalPermission(ConsolePermission.EDIT_REGISTRAR_DETAILS)) {
setFailedResponse("User doesn't have a permission to create OT&E accounts", SC_FORBIDDEN);
return;
}
boolean isBodyValid =
this.oteCreateData.isPresent()
&& !this.oteCreateData.get().registrarId.isEmpty()
&& !this.oteCreateData.get().registrarEmail.isEmpty();
checkArgument(isBodyValid, "OT&E create body is invalid");
String password = passwordGenerator.createString(PASSWORD_LENGTH);
OteAccountBuilder oteAccountBuilder =
OteAccountBuilder.forRegistrarId(this.oteCreateData.get().registrarId)
.addUser(this.oteCreateData.get().registrarEmail)
.setPassword(password);
ImmutableMap<String, String> registrarIdToTld = oteAccountBuilder.buildAndPersist();
oteAccountBuilder.grantIapPermission(maybeGroupEmailAddress, cloudTasksUtils, iamClient);
consoleApiParams.response().setStatus(SC_OK);
consoleApiParams
.response()
.setPayload(
gson.toJson(
ImmutableMap.builder().putAll(registrarIdToTld).put("password", password).build()));
}
@Override
protected void getHandler(User user) {
checkArgument(!Strings.isNullOrEmpty(registrarId), "Missing registrarId parameter");
if (!user.getUserRoles().hasGlobalPermission(ConsolePermission.EDIT_REGISTRAR_DETAILS)) {
setFailedResponse("User doesn't have a permission to check OT&E status", SC_BAD_REQUEST);
return;
}
String baseRegistrarId = OteAccountBuilder.getBaseRegistrarId(registrarId);
tm().transact(
() -> {
Optional<Registrar> registrar = Registrar.loadByRegistrarId(registrarId);
if (registrar.isEmpty()) {
setFailedResponse(
String.format("Registrar with ID %s is not present", registrarId),
SC_BAD_REQUEST);
return;
}
if (!RegistrarBase.Type.OTE.equals(registrar.get().getType())) {
setFailedResponse(
String.format("Registrar with ID %s is not an OT&E registrar", registrarId),
SC_BAD_REQUEST);
return;
}
OteStats oteStats = OteStats.getFromRegistrar(baseRegistrarId);
var stats =
StatType.REQUIRED_STAT_TYPES.stream()
.map(
statType ->
convertSingleRequirement(statType, oteStats.getCount(statType)))
.collect(toImmutableList());
consoleApiParams.response().setStatus(SC_OK);
consoleApiParams.response().setPayload(gson.toJson(stats));
});
}
private Map<String, Object> convertSingleRequirement(StatType statType, int count) {
int requirement = statType.getRequirement();
return ImmutableMap.of(
STAT_TYPE_DESCRIPTION_PARAM,
statType.getDescription(),
STAT_TYPE_REQUIREMENT_PARAM,
requirement,
STAT_TYPE_TIMES_PERFORMED_PARAM,
count,
COMPLETED_PARAM,
count >= requirement);
}
public record OteCreateData(@Expose String registrarId, @Expose String registrarEmail) {}
}
@@ -35,11 +35,11 @@ import google.registry.model.domain.RegistryLock;
import google.registry.model.registrar.Registrar;
import google.registry.model.tld.RegistryLockDao;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.tools.DomainLockUtils;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.util.EmailMessage;
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
@@ -56,6 +56,7 @@ import org.joda.time.Duration;
*/
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = ConsoleRegistryLockAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
@@ -22,16 +22,17 @@ import com.google.gson.annotations.Expose;
import google.registry.model.console.User;
import google.registry.model.domain.RegistryLock;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.tools.DomainLockUtils;
import google.registry.ui.server.registrar.ConsoleApiParams;
import jakarta.servlet.http.HttpServletResponse;
import javax.inject.Inject;
/** Handler for verifying registry lock requests, a form of 2FA. */
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = ConsoleRegistryLockVerifyAction.PATH,
method = {GET},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
@@ -26,10 +26,10 @@ import google.registry.model.console.ConsolePermission;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.util.DomainNameUtils;
import google.registry.util.RegistryEnvironment;
import java.util.Optional;
@@ -38,6 +38,7 @@ import javax.inject.Inject;
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = ConsoleUpdateRegistrarAction.PATH,
method = {POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
@@ -21,15 +21,16 @@ import com.google.common.collect.ImmutableMap;
import google.registry.config.RegistryConfig.Config;
import google.registry.model.console.User;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.auth.Auth;
import google.registry.security.XsrfTokenManager;
import google.registry.ui.server.registrar.ConsoleApiParams;
import jakarta.servlet.http.Cookie;
import javax.inject.Inject;
import org.json.JSONObject;
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = ConsoleUserDataAction.PATH,
method = {GET},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
@@ -34,9 +34,9 @@ import google.registry.model.registrar.RegistrarBase;
import google.registry.model.registrar.RegistrarBase.State;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
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;
@@ -46,6 +46,7 @@ import javax.inject.Named;
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = RegistrarsAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
@@ -31,11 +31,12 @@ import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarPoc;
import google.registry.persistence.transaction.QueryComposer.Comparator;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.ui.forms.FormException;
import google.registry.ui.server.console.ConsoleApiAction;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.ui.server.console.ConsoleApiParams;
import google.registry.ui.server.registrar.RegistrarSettingsAction;
import java.util.Collections;
import java.util.Optional;
@@ -43,6 +44,7 @@ import javax.inject.Inject;
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = ContactAction.PATH,
method = {GET, POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
@@ -28,17 +28,19 @@ import google.registry.model.console.ConsolePermission;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
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 google.registry.ui.server.console.ConsoleApiParams;
import java.util.Optional;
import javax.inject.Inject;
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = SecurityAction.PATH,
method = {POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
@@ -25,12 +25,13 @@ import google.registry.model.console.ConsolePermission;
import google.registry.model.console.User;
import google.registry.model.registrar.Registrar;
import google.registry.request.Action;
import google.registry.request.Action.GkeService;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
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 google.registry.ui.server.console.ConsoleApiParams;
import java.util.Objects;
import java.util.Optional;
import javax.inject.Inject;
@@ -43,6 +44,7 @@ import javax.inject.Inject;
*/
@Action(
service = Action.Service.DEFAULT,
gkeService = GkeService.CONSOLE,
path = WhoisRegistrarFieldsAction.PATH,
method = {POST},
auth = Auth.AUTH_PUBLIC_LOGGED_IN)
@@ -18,7 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.security.JsonResponseHelper.Status.SUCCESS;
import static google.registry.ui.server.registrar.RegistrarConsoleModule.PARAM_CLIENT_ID;
import static google.registry.ui.server.console.ConsoleModule.PARAM_CLIENT_ID;
import static jakarta.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static jakarta.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
@@ -41,29 +41,23 @@ FROM (
FROM (
-- Extract JSON metadata package from monthly logs
SELECT
REGEXP_EXTRACT(logMessages, r'FLOW-LOG-SIGNATURE-METADATA: (.*)\n?$')
REGEXP_EXTRACT(textPayload, r'FLOW-LOG-SIGNATURE-METADATA: (.*)\n?$')
AS json
FROM (
SELECT
protoPayload.resource AS requestPath,
ARRAY(
SELECT logMessage
FROM UNNEST(protoPayload.line)) AS logMessage
textPayload
FROM
`%PROJECT_ID%.%APPENGINE_LOGS_DATA_SET%.%REQUEST_TABLE%*`
`%PROJECT_ID%.%APPENGINE_LOGS_DATA_SET%.%APP_LOGS_TABLE%*`
WHERE _TABLE_SUFFIX
BETWEEN '%FIRST_DAY_OF_MONTH%'
AND '%LAST_DAY_OF_MONTH%')
JOIN UNNEST(logMessage) AS logMessages
-- Look for metadata logs from epp and registrar console requests
WHERE requestPath IN ('/_dr/epp', '/_dr/epptool', '/registrar-xhr')
AND STARTS_WITH(logMessages, "%METADATA_LOG_PREFIX%")
WHERE STARTS_WITH(textPayload, "FLOW-LOG-SIGNATURE-METADATA")
-- Look for domain creates
AND REGEXP_CONTAINS(
logMessages, r'"commandType":"create","resourceType":"domain"')
textPayload, r'"commandType":"create","resourceType":"domain"')
-- Filter prober data
AND NOT REGEXP_CONTAINS(
logMessages, r'"prober-[a-z]{2}-((any)|(canary))"') )
textPayload, r'"prober-[a-z]{2}-((any)|(canary))"') )
GROUP BY tld, clientId ) AS logs_table
JOIN
EXTERNAL_QUERY("projects/%PROJECT_ID%/locations/us/connections/%PROJECT_ID%-sql",
@@ -37,13 +37,12 @@ FROM (
FROM (
SELECT
-- Extract the logged JSON payload.
REGEXP_EXTRACT(logMessage, r'FLOW-LOG-SIGNATURE-METADATA: (.*)\n?$')
REGEXP_EXTRACT(textPayload, r'FLOW-LOG-SIGNATURE-METADATA: (.*)\n?$')
AS json
FROM `%PROJECT_ID%.%ICANN_REPORTING_DATA_SET%.%MONTHLY_LOGS_TABLE%` AS logs
JOIN
UNNEST(logs.logMessage) AS logMessage
FROM `%PROJECT_ID%.%APPENGINE_LOGS_DATA_SET%.%APP_LOGS_TABLE%*`
WHERE
STARTS_WITH(logMessage, "%METADATA_LOG_PREFIX%"))) AS regexes
STARTS_WITH(textPayload, "FLOW-LOG-SIGNATURE-METADATA")
AND _TABLE_SUFFIX BETWEEN '%FIRST_DAY_OF_MONTH%' AND '%LAST_DAY_OF_MONTH%')) AS regexes
JOIN
-- Unnest the JSON-parsed tlds.
UNNEST(regexes.tlds) AS tld
@@ -19,11 +19,6 @@
SELECT
protoPayload.resource AS requestPath,
ARRAY(
SELECT
logMessage
FROM
UNNEST(protoPayload.line)) AS logMessage
FROM
`%PROJECT_ID%.%APPENGINE_LOGS_DATA_SET%.%REQUEST_TABLE%*`
WHERE
@@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import google.registry.module.backend.BackendRequestComponent;
import google.registry.module.bsa.BsaRequestComponent;
import google.registry.module.frontend.FrontendRequestComponent;
@@ -26,8 +28,9 @@ import google.registry.module.pubapi.PubApiRequestComponent;
import google.registry.module.tools.ToolsRequestComponent;
import google.registry.testing.GoldenFileTestHelper;
import google.registry.testing.TestDataHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link RequestComponent}. */
@@ -40,6 +43,18 @@ public class RequestComponentTest {
PubApiRequestComponent.class, "pubapi",
BsaRequestComponent.class, "bsa");
// Paths that do not route to Jetty (all for the legacy console).
private static final ImmutableSet<String> ignoredPaths =
ImmutableSet.of(
"/registrar",
"/registrar-create",
"/registrar-ote-setup",
"/registrar-ote-status",
"/registrar-settings",
"/registry-lock-get",
"/registry-lock-post",
"/registry-lock-verify");
@Test
void testRoutingMap() {
GoldenFileTestHelper.assertThatRoutesFromComponent(RequestComponent.class)
@@ -49,32 +64,49 @@ public class RequestComponentTest {
@Test
void testGaeToJettyRoutingCoverage() {
List<Route> jettyRoutes = getRoutes(RequestComponent.class, "routing.txt");
List<Route> gaeRoutes = new ArrayList<>();
Set<Route> jettyRoutes = getRoutes(RequestComponent.class, "routing.txt");
Set<Route> gaeRoutes = new HashSet<>();
for (var component : GaeComponents.entrySet()) {
gaeRoutes.addAll(getRoutes(component.getKey(), component.getValue() + "_routing.txt"));
}
assertThat(jettyRoutes).containsExactlyElementsIn(gaeRoutes);
assertThat(Sets.difference(jettyRoutes, gaeRoutes)).isEmpty();
assertThat(
Sets.difference(gaeRoutes, jettyRoutes).stream()
.map(Route::path)
.collect(Collectors.toSet()))
.containsExactlyElementsIn(ignoredPaths);
}
private List<Route> getRoutes(Class<?> context, String filename) {
private Set<Route> getRoutes(Class<?> context, String filename) {
return TestDataHelper.loadFile(context, filename)
.trim()
.lines()
.skip(1) // Skip the headers
.map(Route::create)
.toList();
.collect(Collectors.toSet());
}
private record Route(
String path, String clazz, String methods, String ok, String min, String userPolicy) {
String service,
String path,
String clazz,
String methods,
String ok,
String min,
String userPolicy) {
private static final Splitter splitter = Splitter.on(' ').omitEmptyStrings().trimResults();
static Route create(String line) {
ImmutableList<String> parts = ImmutableList.copyOf(splitter.split(line));
assertThat(parts.size()).isEqualTo(6);
assertThat(parts.size()).isEqualTo(7);
return new Route(
parts.get(0), parts.get(1), parts.get(2), parts.get(3), parts.get(4), parts.get(5));
parts.get(0),
parts.get(1),
parts.get(2),
parts.get(3),
parts.get(4),
parts.get(5),
parts.get(6));
}
}
}
@@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import com.google.cloud.tasks.v2.HttpMethod;
@@ -60,6 +61,8 @@ class IcannReportingStagingActionTest {
action.yearMonth = yearMonth;
action.overrideSubdir = Optional.of(subdir);
action.reportTypes = ImmutableSet.of(ReportType.ACTIVITY, ReportType.TRANSACTIONS);
action.sendEmail = true;
action.shouldUpload = true;
action.response = response;
action.stager = stager;
action.retrier = new Retrier(new FakeSleeper(new FakeClock()), 3);
@@ -154,6 +157,33 @@ class IcannReportingStagingActionTest {
cloudTasksHelper.assertNoTasksEnqueued("retryable-cron-tasks");
}
@Test
void testSkipsEmail() throws Exception {
action.sendEmail = false;
action.run();
verify(stager).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
verify(stager).stageReports(yearMonth, subdir, ReportType.TRANSACTIONS);
verify(stager).createAndUploadManifest(subdir, ImmutableList.of("a", "b", "c", "d"));
verifyNoInteractions(action.gmailClient);
assertUploadTaskEnqueued();
}
@Test
void testSkipsUpload() throws Exception {
action.shouldUpload = false;
action.run();
verify(stager).stageReports(yearMonth, subdir, ReportType.ACTIVITY);
verify(stager).stageReports(yearMonth, subdir, ReportType.TRANSACTIONS);
verify(stager).createAndUploadManifest(subdir, ImmutableList.of("a", "b", "c", "d"));
verify(action.gmailClient)
.sendEmail(
EmailMessage.create(
"ICANN Monthly report staging summary [SUCCESS]",
"Completed staging the following 4 ICANN reports:\na\nb\nc\nd",
new InternetAddress("recipient@example.com")));
cloudTasksHelper.assertNoTasksEnqueued("retryable-cron-tasks");
}
@Test
void testEmptySubDir_returnsDefaultSubdir() {
action.overrideSubdir = Optional.empty();
@@ -204,6 +204,7 @@ public final class RequestHandlerTest {
handler =
RequestHandler.create(
Component.class,
"registry.test",
() ->
new Builder() {
@Override
@@ -26,8 +26,8 @@ import static org.mockito.Mockito.when;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.json.webtoken.JsonWebSignature;
import com.google.api.client.json.webtoken.JsonWebSignature.Header;
import com.google.auth.oauth2.TokenVerifier;
import com.google.auth.oauth2.TokenVerifier.VerificationException;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import dagger.Component;
import dagger.Module;
@@ -41,6 +41,7 @@ import google.registry.request.auth.AuthSettings.AuthLevel;
import google.registry.request.auth.OidcTokenAuthenticationMechanism.IapOidcAuthenticationMechanism;
import google.registry.request.auth.OidcTokenAuthenticationMechanism.RegularOidcAuthenticationMechanism;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Map;
import javax.inject.Singleton;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -59,13 +60,13 @@ public class OidcTokenAuthenticationMechanismTest {
private final Payload payload = new Payload();
private final JsonWebSignature jwt =
new JsonWebSignature(new Header(), payload, new byte[0], new byte[0]);
private final TokenVerifier tokenVerifier = mock(TokenVerifier.class);
private final HttpServletRequest request = mock(HttpServletRequest.class);
private User user;
private AuthResult authResult;
private OidcTokenAuthenticationMechanism authenticationMechanism =
new OidcTokenAuthenticationMechanism(serviceAccounts, tokenVerifier, null, e -> rawToken) {};
new OidcTokenAuthenticationMechanism(
serviceAccounts, request -> rawToken, (service, token) -> jwt) {};
@RegisterExtension
public final JpaTestExtensions.JpaUnitTestExtension jpaExtension =
@@ -73,7 +74,6 @@ public class OidcTokenAuthenticationMechanismTest {
@BeforeEach
void beforeEach() throws Exception {
when(tokenVerifier.verify(rawToken)).thenReturn(jwt);
payload.setEmail(email);
payload.setSubject(gaiaId);
user = createAdminUser(email);
@@ -93,28 +93,23 @@ public class OidcTokenAuthenticationMechanismTest {
@Test
void testAuthenticate_noTokenFromRequest() {
authenticationMechanism =
new OidcTokenAuthenticationMechanism(serviceAccounts, tokenVerifier, null, e -> null) {};
new OidcTokenAuthenticationMechanism(
serviceAccounts, e -> null, (service, token) -> jwt) {};
authResult = authenticationMechanism.authenticate(request);
assertThat(authResult).isEqualTo(AuthResult.NOT_AUTHENTICATED);
}
@Test
void testAuthenticate_invalidToken() throws Exception {
when(tokenVerifier.verify(rawToken)).thenThrow(new VerificationException("Bad token"));
authResult = authenticationMechanism.authenticate(request);
assertThat(authResult).isEqualTo(AuthResult.NOT_AUTHENTICATED);
}
@Test
void testAuthenticate_fallbackVerifier() throws Exception {
TokenVerifier fallbackVerifier = mock(TokenVerifier.class);
when(tokenVerifier.verify(rawToken)).thenThrow(new VerificationException("Bad token"));
when(fallbackVerifier.verify(rawToken)).thenReturn(jwt);
authenticationMechanism =
new OidcTokenAuthenticationMechanism(
serviceAccounts, tokenVerifier, fallbackVerifier, e -> rawToken) {};
serviceAccounts,
e -> null,
(service, token) -> {
throw new VerificationException("Bad token");
}) {};
authResult = authenticationMechanism.authenticate(request);
assertThat(authResult.isAuthenticated()).isEqualTo(true);
assertThat(authResult).isEqualTo(AuthResult.NOT_AUTHENTICATED);
}
@Test
@@ -233,9 +228,9 @@ public class OidcTokenAuthenticationMechanismTest {
@Provides
@Singleton
@Config("fallbackOauthClientId")
String provideFallbackOauthClientId() {
return "fallback-client-id";
@Config("backendServiceIds")
Map<String, Long> provideBackendServiceIds() {
return ImmutableMap.of();
}
}
}
@@ -23,7 +23,7 @@ import google.registry.model.console.User;
import google.registry.request.auth.AuthResult;
import google.registry.security.XsrfTokenManager;
import google.registry.ui.server.SendEmailUtils;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.ui.server.console.ConsoleApiParams;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import org.joda.time.DateTime;
@@ -33,7 +33,6 @@ import google.registry.request.auth.AuthResult;
import google.registry.testing.ConsoleApiParamsUtils;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeResponse;
import google.registry.ui.server.registrar.ConsoleApiParams;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -36,7 +36,6 @@ import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.tools.GsonUtils;
import google.registry.ui.server.console.ConsoleDomainListAction.DomainListResult;
import google.registry.ui.server.registrar.ConsoleApiParams;
import java.util.Optional;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
@@ -33,7 +33,6 @@ import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.tools.GsonUtils;
import google.registry.ui.server.registrar.ConsoleApiParams;
import java.io.IOException;
import org.joda.time.DateTime;
import org.junit.jupiter.api.BeforeEach;
@@ -44,8 +44,6 @@ import google.registry.testing.ConsoleApiParamsUtils;
import google.registry.testing.FakeResponse;
import google.registry.tools.GsonUtils;
import google.registry.ui.server.console.ConsoleEppPasswordAction.EppPasswordData;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.ui.server.registrar.RegistrarConsoleModule;
import google.registry.util.EmailMessage;
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
@@ -167,7 +165,7 @@ class ConsoleEppPasswordActionTest {
.when(consoleApiParams.request())
.getReader();
Optional<EppPasswordData> maybePasswordChangeRequest =
RegistrarConsoleModule.provideEppPasswordChangeRequest(
ConsoleModule.provideEppPasswordChangeRequest(
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON));
return new ConsoleEppPasswordAction(
@@ -0,0 +1,255 @@
// 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.
package google.registry.ui.server.console;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.model.OteAccountBuilderTest.verifyIapPermission;
import static google.registry.testing.DatabaseHelper.persistPremiumList;
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.joda.money.CurrencyUnit.USD;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import google.registry.model.OteStatsTestHelper;
import google.registry.model.console.GlobalRole;
import google.registry.model.console.User;
import google.registry.model.console.UserRoles;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.request.Action;
import google.registry.request.auth.AuthResult;
import google.registry.testing.CloudTasksHelper;
import google.registry.testing.ConsoleApiParamsUtils;
import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.FakeResponse;
import google.registry.tools.GsonUtils;
import google.registry.tools.IamClient;
import google.registry.ui.server.console.ConsoleOteAction.OteCreateData;
import google.registry.util.StringGenerator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.json.simple.JSONArray;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
class ConsoleOteActionTest {
@RegisterExtension
final JpaTestExtensions.JpaIntegrationTestExtension jpa =
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
private static final Gson GSON = GsonUtils.provideGson();
private final IamClient iamClient = mock(IamClient.class);
private final CloudTasksHelper cloudTasksHelper = new CloudTasksHelper();
private FakeResponse response;
private ConsoleApiParams consoleApiParams;
private StringGenerator passwordGenerator =
new DeterministicStringGenerator("abcdefghijklmnopqrstuvwxyz");
private User user =
new User.Builder()
.setEmailAddress("marla.singer@example.com")
.setUserRoles(new UserRoles())
.build();
@BeforeEach
void beforeEach() throws Exception {
persistPremiumList("default_sandbox_list", USD, "sandbox,USD 1000");
}
@Test
void testFailure_missingGlobalPermission() {
AuthResult authResult = AuthResult.createUser(user);
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
ConsoleOteAction action =
createAction(
Action.Method.POST,
authResult,
"testRegistrarId",
Optional.of("someRandomString"),
Optional.of(new OteCreateData("testRegistrarId", "tescontact@registry.example")));
action.run();
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_FORBIDDEN);
}
@Test
void testFailure_invalidParamsNoRegistrarId() {
user =
user.asBuilder()
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
.build();
AuthResult authResult = AuthResult.createUser(user);
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
ConsoleOteAction action =
createAction(
Action.Method.POST,
authResult,
"testRegistrarId",
Optional.of("someRandomString"),
Optional.of(new OteCreateData("", "test@email.com")));
action.run();
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
.isEqualTo("OT&E create body is invalid");
}
@Test
void testFailure_invalidParamsNoEmail() {
user =
user.asBuilder()
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
.build();
AuthResult authResult = AuthResult.createUser(user);
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
ConsoleOteAction action =
createAction(
Action.Method.POST,
authResult,
"testRegistrarId",
Optional.of("someRandomString"),
Optional.of(new OteCreateData("testRegistrarId", "")));
action.run();
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
.isEqualTo("OT&E create body is invalid");
}
@Test
void testSuccess_oteCreated() {
user =
user.asBuilder()
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
.build();
AuthResult authResult = AuthResult.createUser(user);
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
ConsoleOteAction action =
createAction(
Action.Method.POST,
authResult,
"theregistrar",
Optional.of("someRandomString@email.test"),
Optional.of(new OteCreateData("theregistrar", "contact@registry.example")));
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
action.run();
String response = ((FakeResponse) consoleApiParams.response()).getPayload();
var obsResponse = GSON.fromJson(response, Map.class);
assertThat(
ImmutableMap.of(
"theregistrar-1", "theregistrar-sunrise",
"theregistrar-3", "theregistrar-ga",
"theregistrar-4", "theregistrar-ga",
"theregistrar-5", "theregistrar-eap",
"password", "abcdefghijklmnop"))
.containsExactlyEntriesIn(obsResponse);
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
verifyIapPermission(
"contact@registry.example",
Optional.of("someRandomString@email.test"),
cloudTasksHelper,
iamClient);
}
@Test
void testFail_statusMissingParam() {
AuthResult authResult = AuthResult.createUser(user);
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
ConsoleOteAction action =
createAction(
Action.Method.GET,
authResult,
"",
Optional.of("someRandomString@email.test"),
Optional.of(new OteCreateData("theregistrar", "contact@registry.example")));
action.cloudTasksUtils = cloudTasksHelper.getTestCloudTasksUtils();
action.run();
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_BAD_REQUEST);
assertThat(((FakeResponse) consoleApiParams.response()).getPayload())
.isEqualTo("Missing registrarId parameter");
}
@Test
void testSuccess_finishedOte() throws Exception {
OteStatsTestHelper.setupCompleteOte("theregistrar");
user =
user.asBuilder()
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
.build();
AuthResult authResult = AuthResult.createUser(user);
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
ConsoleOteAction action =
createAction(
Action.Method.GET, authResult, "theregistrar-1", Optional.empty(), Optional.empty());
action.run();
List<Map<String, ?>> response =
GSON.fromJson(((FakeResponse) consoleApiParams.response()).getPayload(), JSONArray.class);
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
assertTrue(response.stream().allMatch(status -> Boolean.TRUE.equals(status.get("completed"))));
}
@Test
void testSuccess_unfinishedOte() throws Exception {
OteStatsTestHelper.setupIncompleteOte("theregistrar");
user =
user.asBuilder()
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
.build();
AuthResult authResult = AuthResult.createUser(user);
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
ConsoleOteAction action =
createAction(
Action.Method.GET, authResult, "theregistrar-1", Optional.empty(), Optional.empty());
action.run();
List<Map<String, ?>> response =
GSON.fromJson(((FakeResponse) consoleApiParams.response()).getPayload(), JSONArray.class);
assertThat(((FakeResponse) consoleApiParams.response()).getStatus()).isEqualTo(SC_OK);
assertThat(
response.stream()
.filter(status -> Boolean.FALSE.equals(status.get("completed")))
.map(status -> status.get("description"))
.collect(Collectors.toList()))
.containsExactlyElementsIn(
ImmutableList.of("domain creates idn", "domain restores", "host deletes"));
}
private ConsoleOteAction createAction(
Action.Method method,
AuthResult authResult,
String registrarId,
Optional<String> maybeGroupEmailAddress,
Optional<OteCreateData> oteCreateData) {
response = new FakeResponse();
consoleApiParams = ConsoleApiParamsUtils.createFake(authResult);
when(consoleApiParams.request().getMethod()).thenReturn(method.toString());
return new ConsoleOteAction(
consoleApiParams,
GSON,
iamClient,
registrarId,
maybeGroupEmailAddress,
passwordGenerator,
oteCreateData);
}
}
@@ -52,7 +52,6 @@ import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.tools.DomainLockUtils;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.util.EmailMessage;
import google.registry.util.StringGenerator;
import jakarta.mail.internet.InternetAddress;
@@ -40,7 +40,6 @@ import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.tools.DomainLockUtils;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.util.StringGenerator;
import jakarta.servlet.http.HttpServletResponse;
import org.joda.time.Duration;
@@ -42,8 +42,6 @@ import google.registry.testing.ConsoleApiParamsUtils;
import google.registry.testing.FakeResponse;
import google.registry.testing.SystemPropertyExtension;
import google.registry.tools.GsonUtils;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.ui.server.registrar.RegistrarConsoleModule;
import google.registry.util.EmailMessage;
import google.registry.util.RegistryEnvironment;
import jakarta.mail.internet.AddressException;
@@ -172,7 +170,7 @@ class ConsoleUpdateRegistrarActionTest {
.when(consoleApiParams.request())
.getReader();
Optional<Registrar> maybeRegistrarUpdateData =
RegistrarConsoleModule.provideRegistrar(
ConsoleModule.provideRegistrar(
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON));
return new ConsoleUpdateRegistrarAction(consoleApiParams, maybeRegistrarUpdateData);
}
@@ -28,7 +28,6 @@ import google.registry.request.auth.AuthResult;
import google.registry.testing.ConsoleApiParamsUtils;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeResponse;
import google.registry.ui.server.registrar.ConsoleApiParams;
import jakarta.servlet.http.Cookie;
import java.io.IOException;
import java.util.List;
@@ -41,8 +41,6 @@ import google.registry.request.auth.AuthResult;
import google.registry.testing.ConsoleApiParamsUtils;
import google.registry.testing.DeterministicStringGenerator;
import google.registry.testing.FakeResponse;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.ui.server.registrar.RegistrarConsoleModule;
import google.registry.util.StringGenerator;
import java.io.BufferedReader;
import java.io.IOException;
@@ -253,7 +251,7 @@ class RegistrarsActionTest {
passcodeGenerator);
}
Optional<Registrar> maybeRegistrar =
RegistrarConsoleModule.provideRegistrar(
ConsoleModule.provideRegistrar(
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON));
return new RegistrarsAction(
consoleApiParams, GSON, maybeRegistrar, passwordGenerator, passcodeGenerator);
@@ -45,8 +45,8 @@ import google.registry.request.RequestModule;
import google.registry.request.auth.AuthResult;
import google.registry.testing.ConsoleApiParamsUtils;
import google.registry.testing.FakeResponse;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.ui.server.registrar.RegistrarConsoleModule;
import google.registry.ui.server.console.ConsoleApiParams;
import google.registry.ui.server.console.ConsoleModule;
import google.registry.util.EmailMessage;
import jakarta.mail.internet.AddressException;
import jakarta.mail.internet.InternetAddress;
@@ -296,7 +296,7 @@ class ContactActionTest {
.when(consoleApiParams.request())
.getReader();
Optional<ImmutableSet<RegistrarPoc>> maybeContacts =
RegistrarConsoleModule.provideContacts(
ConsoleModule.provideContacts(
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON));
return new ContactAction(consoleApiParams, GSON, registrarId, maybeContacts);
}
@@ -38,8 +38,8 @@ import google.registry.testing.ConsoleApiParamsUtils;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.ui.server.registrar.RegistrarConsoleModule;
import google.registry.ui.server.console.ConsoleApiParams;
import google.registry.ui.server.console.ConsoleModule;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
@@ -108,7 +108,7 @@ class SecurityActionTest {
.when(consoleApiParams.request())
.getReader();
Optional<Registrar> maybeRegistrar =
RegistrarConsoleModule.provideRegistrar(
ConsoleModule.provideRegistrar(
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON));
return new SecurityAction(
consoleApiParams, certificateChecker, registrarAccessor, registrarId, maybeRegistrar);
@@ -40,8 +40,8 @@ import google.registry.testing.ConsoleApiParamsUtils;
import google.registry.testing.DatabaseHelper;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeResponse;
import google.registry.ui.server.registrar.ConsoleApiParams;
import google.registry.ui.server.registrar.RegistrarConsoleModule;
import google.registry.ui.server.console.ConsoleApiParams;
import google.registry.ui.server.console.ConsoleModule;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
@@ -169,7 +169,7 @@ public class WhoisRegistrarFieldsActionTest {
return new WhoisRegistrarFieldsAction(
consoleApiParams,
registrarAccessor,
RegistrarConsoleModule.provideRegistrar(
ConsoleModule.provideRegistrar(
GSON, RequestModule.provideJsonBody(consoleApiParams.request(), GSON)));
}
}
@@ -1,38 +1,38 @@
PATH CLASS METHODS OK MIN USER_POLICY
/_dr/cron/fanout TldFanoutAction GET y APP ADMIN
/_dr/dnsRefresh RefreshDnsAction GET y APP ADMIN
/_dr/task/brdaCopy BrdaCopyAction POST y APP ADMIN
/_dr/task/copyDetailReports CopyDetailReportsAction POST n APP ADMIN
/_dr/task/deleteExpiredDomains DeleteExpiredDomainsAction GET n APP ADMIN
/_dr/task/deleteLoadTestData DeleteLoadTestDataAction POST n APP ADMIN
/_dr/task/deleteProberData DeleteProberDataAction POST n APP ADMIN
/_dr/task/executeCannedScript CannedScriptExecutionAction POST,GET y APP ADMIN
/_dr/task/expandBillingRecurrences ExpandBillingRecurrencesAction GET n APP ADMIN
/_dr/task/exportDomainLists ExportDomainListsAction POST n APP ADMIN
/_dr/task/exportPremiumTerms ExportPremiumTermsAction POST n APP ADMIN
/_dr/task/exportReservedTerms ExportReservedTermsAction POST n APP ADMIN
/_dr/task/generateInvoices GenerateInvoicesAction POST n APP ADMIN
/_dr/task/generateSpec11 GenerateSpec11ReportAction POST n APP ADMIN
/_dr/task/icannReportingStaging IcannReportingStagingAction POST n APP ADMIN
/_dr/task/icannReportingUpload IcannReportingUploadAction POST n APP ADMIN
/_dr/task/nordnUpload NordnUploadAction POST y APP ADMIN
/_dr/task/nordnVerify NordnVerifyAction POST y APP ADMIN
/_dr/task/publishDnsUpdates PublishDnsUpdatesAction POST y APP ADMIN
/_dr/task/publishInvoices PublishInvoicesAction POST n APP ADMIN
/_dr/task/publishSpec11 PublishSpec11ReportAction POST n APP ADMIN
/_dr/task/rdeReport RdeReportAction POST n APP ADMIN
/_dr/task/rdeStaging RdeStagingAction GET,POST n APP ADMIN
/_dr/task/rdeUpload RdeUploadAction POST n APP ADMIN
/_dr/task/readDnsRefreshRequests ReadDnsRefreshRequestsAction POST y APP ADMIN
/_dr/task/refreshDnsOnHostRename RefreshDnsOnHostRenameAction POST n APP ADMIN
/_dr/task/relockDomain RelockDomainAction POST y APP ADMIN
/_dr/task/resaveAllEppResourcesPipeline ResaveAllEppResourcesPipelineAction GET n APP ADMIN
/_dr/task/resaveEntity ResaveEntityAction POST n APP ADMIN
/_dr/task/sendExpiringCertificateNotificationEmail SendExpiringCertificateNotificationEmailAction GET n APP ADMIN
/_dr/task/syncGroupMembers SyncGroupMembersAction POST n APP ADMIN
/_dr/task/syncRegistrarsSheet SyncRegistrarsSheetAction POST n APP ADMIN
/_dr/task/tmchCrl TmchCrlAction POST y APP ADMIN
/_dr/task/tmchDnl TmchDnlAction POST y APP ADMIN
/_dr/task/tmchSmdrl TmchSmdrlAction POST y APP ADMIN
/_dr/task/updateRegistrarRdapBaseUrls UpdateRegistrarRdapBaseUrlsAction GET y APP ADMIN
/_dr/task/wipeOutContactHistoryPii WipeOutContactHistoryPiiAction GET n APP ADMIN
SERVICE PATH CLASS METHODS OK MIN USER_POLICY
BACKEND /_dr/cron/fanout TldFanoutAction GET y APP ADMIN
BACKEND /_dr/task/brdaCopy BrdaCopyAction POST y APP ADMIN
BACKEND /_dr/task/copyDetailReports CopyDetailReportsAction POST n APP ADMIN
BACKEND /_dr/task/deleteExpiredDomains DeleteExpiredDomainsAction GET n APP ADMIN
BACKEND /_dr/task/deleteLoadTestData DeleteLoadTestDataAction POST n APP ADMIN
BACKEND /_dr/task/deleteProberData DeleteProberDataAction POST n APP ADMIN
BACKEND /_dr/task/dnsRefresh RefreshDnsAction GET y APP ADMIN
BACKEND /_dr/task/executeCannedScript CannedScriptExecutionAction POST,GET y APP ADMIN
BACKEND /_dr/task/expandBillingRecurrences ExpandBillingRecurrencesAction GET n APP ADMIN
BACKEND /_dr/task/exportDomainLists ExportDomainListsAction POST n APP ADMIN
BACKEND /_dr/task/exportPremiumTerms ExportPremiumTermsAction POST n APP ADMIN
BACKEND /_dr/task/exportReservedTerms ExportReservedTermsAction POST n APP ADMIN
BACKEND /_dr/task/generateInvoices GenerateInvoicesAction POST n APP ADMIN
BACKEND /_dr/task/generateSpec11 GenerateSpec11ReportAction POST n APP ADMIN
BACKEND /_dr/task/icannReportingStaging IcannReportingStagingAction POST n APP ADMIN
BACKEND /_dr/task/icannReportingUpload IcannReportingUploadAction POST n APP ADMIN
BACKEND /_dr/task/nordnUpload NordnUploadAction POST y APP ADMIN
BACKEND /_dr/task/nordnVerify NordnVerifyAction POST y APP ADMIN
BACKEND /_dr/task/publishDnsUpdates PublishDnsUpdatesAction POST y APP ADMIN
BACKEND /_dr/task/publishInvoices PublishInvoicesAction POST n APP ADMIN
BACKEND /_dr/task/publishSpec11 PublishSpec11ReportAction POST n APP ADMIN
BACKEND /_dr/task/rdeReport RdeReportAction POST n APP ADMIN
BACKEND /_dr/task/rdeStaging RdeStagingAction GET,POST n APP ADMIN
BACKEND /_dr/task/rdeUpload RdeUploadAction POST n APP ADMIN
BACKEND /_dr/task/readDnsRefreshRequests ReadDnsRefreshRequestsAction POST y APP ADMIN
BACKEND /_dr/task/refreshDnsOnHostRename RefreshDnsOnHostRenameAction POST n APP ADMIN
BACKEND /_dr/task/relockDomain RelockDomainAction POST y APP ADMIN
BACKEND /_dr/task/resaveAllEppResourcesPipeline ResaveAllEppResourcesPipelineAction GET n APP ADMIN
BACKEND /_dr/task/resaveEntity ResaveEntityAction POST n APP ADMIN
BACKEND /_dr/task/sendExpiringCertificateNotificationEmail SendExpiringCertificateNotificationEmailAction GET n APP ADMIN
BACKEND /_dr/task/syncGroupMembers SyncGroupMembersAction POST n APP ADMIN
BACKEND /_dr/task/syncRegistrarsSheet SyncRegistrarsSheetAction POST n APP ADMIN
BACKEND /_dr/task/tmchCrl TmchCrlAction POST y APP ADMIN
BACKEND /_dr/task/tmchDnl TmchDnlAction POST y APP ADMIN
BACKEND /_dr/task/tmchSmdrl TmchSmdrlAction POST y APP ADMIN
BACKEND /_dr/task/updateRegistrarRdapBaseUrls UpdateRegistrarRdapBaseUrlsAction GET y APP ADMIN
BACKEND /_dr/task/wipeOutContactHistoryPii WipeOutContactHistoryPiiAction GET n APP ADMIN
@@ -1,5 +1,5 @@
PATH CLASS METHODS OK MIN USER_POLICY
/_dr/task/bsaDownload BsaDownloadAction GET,POST n APP ADMIN
/_dr/task/bsaRefresh BsaRefreshAction GET,POST n APP ADMIN
/_dr/task/bsaValidate BsaValidateAction GET,POST n APP ADMIN
/_dr/task/uploadBsaUnavailableNames UploadBsaUnavailableDomainsAction GET,POST n APP ADMIN
SERVICE PATH CLASS METHODS OK MIN USER_POLICY
BACKEND /_dr/task/bsaDownload BsaDownloadAction GET,POST n APP ADMIN
BACKEND /_dr/task/bsaRefresh BsaRefreshAction GET,POST n APP ADMIN
BACKEND /_dr/task/bsaValidate BsaValidateAction GET,POST n APP ADMIN
BACKEND /_dr/task/uploadBsaUnavailableNames UploadBsaUnavailableDomainsAction GET,POST n APP ADMIN
@@ -1,22 +1,23 @@
PATH CLASS METHODS OK MIN USER_POLICY
/_dr/epp EppTlsAction POST n APP ADMIN
/console-api/domain ConsoleDomainGetAction GET n USER PUBLIC
/console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
/console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
/console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
/console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
/console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
/console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
/console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
/console-api/settings/contacts ContactAction GET,POST n USER PUBLIC
/console-api/settings/security SecurityAction POST n USER PUBLIC
/console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC
/console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
/registrar ConsoleUiAction GET n USER PUBLIC
/registrar-create ConsoleRegistrarCreatorAction POST,GET n USER PUBLIC
/registrar-ote-setup ConsoleOteSetupAction POST,GET n USER PUBLIC
/registrar-ote-status OteStatusAction POST n USER PUBLIC
/registrar-settings RegistrarSettingsAction POST n USER PUBLIC
/registry-lock-get RegistryLockGetAction GET n USER PUBLIC
/registry-lock-post RegistryLockPostAction POST n USER PUBLIC
/registry-lock-verify RegistryLockVerifyAction GET n USER PUBLIC
SERVICE PATH CLASS METHODS OK MIN USER_POLICY
FRONTEND /_dr/epp EppTlsAction POST n APP ADMIN
FRONTEND /registrar ConsoleUiAction GET n USER PUBLIC
FRONTEND /registrar-create ConsoleRegistrarCreatorAction POST,GET n USER PUBLIC
FRONTEND /registrar-ote-setup ConsoleOteSetupAction POST,GET n USER PUBLIC
FRONTEND /registrar-ote-status OteStatusAction POST n USER PUBLIC
FRONTEND /registrar-settings RegistrarSettingsAction POST n USER PUBLIC
FRONTEND /registry-lock-get RegistryLockGetAction GET n USER PUBLIC
FRONTEND /registry-lock-post RegistryLockPostAction POST n USER PUBLIC
FRONTEND /registry-lock-verify RegistryLockVerifyAction GET n USER PUBLIC
CONSOLE /console-api/domain ConsoleDomainGetAction GET n USER PUBLIC
CONSOLE /console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
CONSOLE /console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
CONSOLE /console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
CONSOLE /console-api/ote ConsoleOteAction GET,POST n USER PUBLIC
CONSOLE /console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
CONSOLE /console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
CONSOLE /console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
CONSOLE /console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
CONSOLE /console-api/settings/contacts ContactAction GET,POST n USER PUBLIC
CONSOLE /console-api/settings/security SecurityAction POST n USER PUBLIC
CONSOLE /console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC
CONSOLE /console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
@@ -1,13 +1,13 @@
PATH CLASS METHODS OK MIN USER_POLICY
/_dr/whois WhoisAction POST n APP ADMIN
/check CheckApiAction GET n NONE PUBLIC
/rdap/autnum/(*) RdapAutnumAction GET,HEAD n NONE PUBLIC
/rdap/domain/(*) RdapDomainAction GET,HEAD n NONE PUBLIC
/rdap/domains RdapDomainSearchAction GET,HEAD n NONE PUBLIC
/rdap/entities RdapEntitySearchAction GET,HEAD n NONE PUBLIC
/rdap/entity/(*) RdapEntityAction GET,HEAD n NONE PUBLIC
/rdap/help(*) RdapHelpAction GET,HEAD n NONE PUBLIC
/rdap/ip/(*) RdapIpAction GET,HEAD n NONE PUBLIC
/rdap/nameserver/(*) RdapNameserverAction GET,HEAD n NONE PUBLIC
/rdap/nameservers RdapNameserverSearchAction GET,HEAD n NONE PUBLIC
/whois/(*) WhoisHttpAction GET n NONE PUBLIC
SERVICE PATH CLASS METHODS OK MIN USER_POLICY
PUBAPI /_dr/whois WhoisAction POST n APP ADMIN
PUBAPI /check CheckApiAction GET n NONE PUBLIC
PUBAPI /rdap/autnum/(*) RdapAutnumAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/domain/(*) RdapDomainAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/domains RdapDomainSearchAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/entities RdapEntitySearchAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/entity/(*) RdapEntityAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/help(*) RdapHelpAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/ip/(*) RdapIpAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/nameserver/(*) RdapNameserverAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/nameservers RdapNameserverSearchAction GET,HEAD n NONE PUBLIC
PUBAPI /whois/(*) WhoisHttpAction GET n NONE PUBLIC
@@ -1,88 +1,81 @@
PATH CLASS METHODS OK MIN USER_POLICY
/_dr/admin/createGroups CreateGroupsAction POST n APP ADMIN
/_dr/admin/list/domains ListDomainsAction GET,POST n APP ADMIN
/_dr/admin/list/hosts ListHostsAction GET,POST n APP ADMIN
/_dr/admin/list/premiumLists ListPremiumListsAction GET,POST n APP ADMIN
/_dr/admin/list/registrars ListRegistrarsAction GET,POST n APP ADMIN
/_dr/admin/list/reservedLists ListReservedListsAction GET,POST n APP ADMIN
/_dr/admin/list/tlds ListTldsAction GET,POST n APP ADMIN
/_dr/admin/updateUserGroup UpdateUserGroupAction POST n APP ADMIN
/_dr/admin/verifyOte VerifyOteAction POST n APP ADMIN
/_dr/cron/fanout TldFanoutAction GET y APP ADMIN
/_dr/dnsRefresh RefreshDnsAction GET y APP ADMIN
/_dr/epp EppTlsAction POST n APP ADMIN
/_dr/epptool EppToolAction POST n APP ADMIN
/_dr/loadtest LoadTestAction POST y APP ADMIN
/_dr/task/brdaCopy BrdaCopyAction POST y APP ADMIN
/_dr/task/bsaDownload BsaDownloadAction GET,POST n APP ADMIN
/_dr/task/bsaRefresh BsaRefreshAction GET,POST n APP ADMIN
/_dr/task/bsaValidate BsaValidateAction GET,POST n APP ADMIN
/_dr/task/copyDetailReports CopyDetailReportsAction POST n APP ADMIN
/_dr/task/deleteExpiredDomains DeleteExpiredDomainsAction GET n APP ADMIN
/_dr/task/deleteLoadTestData DeleteLoadTestDataAction POST n APP ADMIN
/_dr/task/deleteProberData DeleteProberDataAction POST n APP ADMIN
/_dr/task/executeCannedScript CannedScriptExecutionAction POST,GET y APP ADMIN
/_dr/task/expandBillingRecurrences ExpandBillingRecurrencesAction GET n APP ADMIN
/_dr/task/exportDomainLists ExportDomainListsAction POST n APP ADMIN
/_dr/task/exportPremiumTerms ExportPremiumTermsAction POST n APP ADMIN
/_dr/task/exportReservedTerms ExportReservedTermsAction POST n APP ADMIN
/_dr/task/generateInvoices GenerateInvoicesAction POST n APP ADMIN
/_dr/task/generateSpec11 GenerateSpec11ReportAction POST n APP ADMIN
/_dr/task/generateZoneFiles GenerateZoneFilesAction POST n APP ADMIN
/_dr/task/icannReportingStaging IcannReportingStagingAction POST n APP ADMIN
/_dr/task/icannReportingUpload IcannReportingUploadAction POST n APP ADMIN
/_dr/task/nordnUpload NordnUploadAction POST y APP ADMIN
/_dr/task/nordnVerify NordnVerifyAction POST y APP ADMIN
/_dr/task/publishDnsUpdates PublishDnsUpdatesAction POST y APP ADMIN
/_dr/task/publishInvoices PublishInvoicesAction POST n APP ADMIN
/_dr/task/publishSpec11 PublishSpec11ReportAction POST n APP ADMIN
/_dr/task/rdeReport RdeReportAction POST n APP ADMIN
/_dr/task/rdeStaging RdeStagingAction GET,POST n APP ADMIN
/_dr/task/rdeUpload RdeUploadAction POST n APP ADMIN
/_dr/task/readDnsRefreshRequests ReadDnsRefreshRequestsAction POST y APP ADMIN
/_dr/task/refreshDnsForAllDomains RefreshDnsForAllDomainsAction GET n APP ADMIN
/_dr/task/refreshDnsOnHostRename RefreshDnsOnHostRenameAction POST n APP ADMIN
/_dr/task/relockDomain RelockDomainAction POST y APP ADMIN
/_dr/task/resaveAllEppResourcesPipeline ResaveAllEppResourcesPipelineAction GET n APP ADMIN
/_dr/task/resaveEntity ResaveEntityAction POST n APP ADMIN
/_dr/task/sendExpiringCertificateNotificationEmail SendExpiringCertificateNotificationEmailAction GET n APP ADMIN
/_dr/task/syncGroupMembers SyncGroupMembersAction POST n APP ADMIN
/_dr/task/syncRegistrarsSheet SyncRegistrarsSheetAction POST n APP ADMIN
/_dr/task/tmchCrl TmchCrlAction POST y APP ADMIN
/_dr/task/tmchDnl TmchDnlAction POST y APP ADMIN
/_dr/task/tmchSmdrl TmchSmdrlAction POST y APP ADMIN
/_dr/task/updateRegistrarRdapBaseUrls UpdateRegistrarRdapBaseUrlsAction GET y APP ADMIN
/_dr/task/uploadBsaUnavailableNames UploadBsaUnavailableDomainsAction GET,POST n APP ADMIN
/_dr/task/wipeOutContactHistoryPii WipeOutContactHistoryPiiAction GET n APP ADMIN
/_dr/whois WhoisAction POST n APP ADMIN
/check CheckApiAction GET n NONE PUBLIC
/console-api/domain ConsoleDomainGetAction GET n USER PUBLIC
/console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
/console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
/console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
/console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
/console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
/console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
/console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
/console-api/settings/contacts ContactAction GET,POST n USER PUBLIC
/console-api/settings/security SecurityAction POST n USER PUBLIC
/console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC
/console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
/rdap/autnum/(*) RdapAutnumAction GET,HEAD n NONE PUBLIC
/rdap/domain/(*) RdapDomainAction GET,HEAD n NONE PUBLIC
/rdap/domains RdapDomainSearchAction GET,HEAD n NONE PUBLIC
/rdap/entities RdapEntitySearchAction GET,HEAD n NONE PUBLIC
/rdap/entity/(*) RdapEntityAction GET,HEAD n NONE PUBLIC
/rdap/help(*) RdapHelpAction GET,HEAD n NONE PUBLIC
/rdap/ip/(*) RdapIpAction GET,HEAD n NONE PUBLIC
/rdap/nameserver/(*) RdapNameserverAction GET,HEAD n NONE PUBLIC
/rdap/nameservers RdapNameserverSearchAction GET,HEAD n NONE PUBLIC
/registrar ConsoleUiAction GET n USER PUBLIC
/registrar-create ConsoleRegistrarCreatorAction POST,GET n USER PUBLIC
/registrar-ote-setup ConsoleOteSetupAction POST,GET n USER PUBLIC
/registrar-ote-status OteStatusAction POST n USER PUBLIC
/registrar-settings RegistrarSettingsAction POST n USER PUBLIC
/registry-lock-get RegistryLockGetAction GET n USER PUBLIC
/registry-lock-post RegistryLockPostAction POST n USER PUBLIC
/registry-lock-verify RegistryLockVerifyAction GET n USER PUBLIC
/whois/(*) WhoisHttpAction GET n NONE PUBLIC
SERVICE PATH CLASS METHODS OK MIN USER_POLICY
FRONTEND /_dr/epp EppTlsAction POST n APP ADMIN
BACKEND /_dr/admin/createGroups CreateGroupsAction POST n APP ADMIN
BACKEND /_dr/admin/list/domains ListDomainsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/list/hosts ListHostsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/list/premiumLists ListPremiumListsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/list/registrars ListRegistrarsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/list/reservedLists ListReservedListsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/list/tlds ListTldsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/updateUserGroup UpdateUserGroupAction POST n APP ADMIN
BACKEND /_dr/admin/verifyOte VerifyOteAction POST n APP ADMIN
BACKEND /_dr/cron/fanout TldFanoutAction GET y APP ADMIN
BACKEND /_dr/epptool EppToolAction POST n APP ADMIN
BACKEND /_dr/loadtest LoadTestAction POST y APP ADMIN
BACKEND /_dr/task/brdaCopy BrdaCopyAction POST y APP ADMIN
BACKEND /_dr/task/bsaDownload BsaDownloadAction GET,POST n APP ADMIN
BACKEND /_dr/task/bsaRefresh BsaRefreshAction GET,POST n APP ADMIN
BACKEND /_dr/task/bsaValidate BsaValidateAction GET,POST n APP ADMIN
BACKEND /_dr/task/copyDetailReports CopyDetailReportsAction POST n APP ADMIN
BACKEND /_dr/task/deleteExpiredDomains DeleteExpiredDomainsAction GET n APP ADMIN
BACKEND /_dr/task/deleteLoadTestData DeleteLoadTestDataAction POST n APP ADMIN
BACKEND /_dr/task/deleteProberData DeleteProberDataAction POST n APP ADMIN
BACKEND /_dr/task/dnsRefresh RefreshDnsAction GET y APP ADMIN
BACKEND /_dr/task/executeCannedScript CannedScriptExecutionAction POST,GET y APP ADMIN
BACKEND /_dr/task/expandBillingRecurrences ExpandBillingRecurrencesAction GET n APP ADMIN
BACKEND /_dr/task/exportDomainLists ExportDomainListsAction POST n APP ADMIN
BACKEND /_dr/task/exportPremiumTerms ExportPremiumTermsAction POST n APP ADMIN
BACKEND /_dr/task/exportReservedTerms ExportReservedTermsAction POST n APP ADMIN
BACKEND /_dr/task/generateInvoices GenerateInvoicesAction POST n APP ADMIN
BACKEND /_dr/task/generateSpec11 GenerateSpec11ReportAction POST n APP ADMIN
BACKEND /_dr/task/generateZoneFiles GenerateZoneFilesAction POST n APP ADMIN
BACKEND /_dr/task/icannReportingStaging IcannReportingStagingAction POST n APP ADMIN
BACKEND /_dr/task/icannReportingUpload IcannReportingUploadAction POST n APP ADMIN
BACKEND /_dr/task/nordnUpload NordnUploadAction POST y APP ADMIN
BACKEND /_dr/task/nordnVerify NordnVerifyAction POST y APP ADMIN
BACKEND /_dr/task/publishDnsUpdates PublishDnsUpdatesAction POST y APP ADMIN
BACKEND /_dr/task/publishInvoices PublishInvoicesAction POST n APP ADMIN
BACKEND /_dr/task/publishSpec11 PublishSpec11ReportAction POST n APP ADMIN
BACKEND /_dr/task/rdeReport RdeReportAction POST n APP ADMIN
BACKEND /_dr/task/rdeStaging RdeStagingAction GET,POST n APP ADMIN
BACKEND /_dr/task/rdeUpload RdeUploadAction POST n APP ADMIN
BACKEND /_dr/task/readDnsRefreshRequests ReadDnsRefreshRequestsAction POST y APP ADMIN
BACKEND /_dr/task/refreshDnsForAllDomains RefreshDnsForAllDomainsAction GET n APP ADMIN
BACKEND /_dr/task/refreshDnsOnHostRename RefreshDnsOnHostRenameAction POST n APP ADMIN
BACKEND /_dr/task/relockDomain RelockDomainAction POST y APP ADMIN
BACKEND /_dr/task/resaveAllEppResourcesPipeline ResaveAllEppResourcesPipelineAction GET n APP ADMIN
BACKEND /_dr/task/resaveEntity ResaveEntityAction POST n APP ADMIN
BACKEND /_dr/task/sendExpiringCertificateNotificationEmail SendExpiringCertificateNotificationEmailAction GET n APP ADMIN
BACKEND /_dr/task/syncGroupMembers SyncGroupMembersAction POST n APP ADMIN
BACKEND /_dr/task/syncRegistrarsSheet SyncRegistrarsSheetAction POST n APP ADMIN
BACKEND /_dr/task/tmchCrl TmchCrlAction POST y APP ADMIN
BACKEND /_dr/task/tmchDnl TmchDnlAction POST y APP ADMIN
BACKEND /_dr/task/tmchSmdrl TmchSmdrlAction POST y APP ADMIN
BACKEND /_dr/task/updateRegistrarRdapBaseUrls UpdateRegistrarRdapBaseUrlsAction GET y APP ADMIN
BACKEND /_dr/task/uploadBsaUnavailableNames UploadBsaUnavailableDomainsAction GET,POST n APP ADMIN
BACKEND /_dr/task/wipeOutContactHistoryPii WipeOutContactHistoryPiiAction GET n APP ADMIN
PUBAPI /_dr/whois WhoisAction POST n APP ADMIN
PUBAPI /check CheckApiAction GET n NONE PUBLIC
PUBAPI /rdap/autnum/(*) RdapAutnumAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/domain/(*) RdapDomainAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/domains RdapDomainSearchAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/entities RdapEntitySearchAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/entity/(*) RdapEntityAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/help(*) RdapHelpAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/ip/(*) RdapIpAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/nameserver/(*) RdapNameserverAction GET,HEAD n NONE PUBLIC
PUBAPI /rdap/nameservers RdapNameserverSearchAction GET,HEAD n NONE PUBLIC
PUBAPI /whois/(*) WhoisHttpAction GET n NONE PUBLIC
CONSOLE /console-api/domain ConsoleDomainGetAction GET n USER PUBLIC
CONSOLE /console-api/domain-list ConsoleDomainListAction GET n USER PUBLIC
CONSOLE /console-api/dum-download ConsoleDumDownloadAction GET n USER PUBLIC
CONSOLE /console-api/eppPassword ConsoleEppPasswordAction POST n USER PUBLIC
CONSOLE /console-api/ote ConsoleOteAction GET,POST n USER PUBLIC
CONSOLE /console-api/registrar ConsoleUpdateRegistrarAction POST n USER PUBLIC
CONSOLE /console-api/registrars RegistrarsAction GET,POST n USER PUBLIC
CONSOLE /console-api/registry-lock ConsoleRegistryLockAction GET,POST n USER PUBLIC
CONSOLE /console-api/registry-lock-verify ConsoleRegistryLockVerifyAction GET n USER PUBLIC
CONSOLE /console-api/settings/contacts ContactAction GET,POST n USER PUBLIC
CONSOLE /console-api/settings/security SecurityAction POST n USER PUBLIC
CONSOLE /console-api/settings/whois-fields WhoisRegistrarFieldsAction POST n USER PUBLIC
CONSOLE /console-api/userdata ConsoleUserDataAction GET n USER PUBLIC
@@ -1,14 +1,14 @@
PATH CLASS METHODS OK MIN USER_POLICY
/_dr/admin/createGroups CreateGroupsAction POST n APP ADMIN
/_dr/admin/list/domains ListDomainsAction GET,POST n APP ADMIN
/_dr/admin/list/hosts ListHostsAction GET,POST n APP ADMIN
/_dr/admin/list/premiumLists ListPremiumListsAction GET,POST n APP ADMIN
/_dr/admin/list/registrars ListRegistrarsAction GET,POST n APP ADMIN
/_dr/admin/list/reservedLists ListReservedListsAction GET,POST n APP ADMIN
/_dr/admin/list/tlds ListTldsAction GET,POST n APP ADMIN
/_dr/admin/updateUserGroup UpdateUserGroupAction POST n APP ADMIN
/_dr/admin/verifyOte VerifyOteAction POST n APP ADMIN
/_dr/epptool EppToolAction POST n APP ADMIN
/_dr/loadtest LoadTestAction POST y APP ADMIN
/_dr/task/generateZoneFiles GenerateZoneFilesAction POST n APP ADMIN
/_dr/task/refreshDnsForAllDomains RefreshDnsForAllDomainsAction GET n APP ADMIN
SERVICE PATH CLASS METHODS OK MIN USER_POLICY
BACKEND /_dr/admin/createGroups CreateGroupsAction POST n APP ADMIN
BACKEND /_dr/admin/list/domains ListDomainsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/list/hosts ListHostsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/list/premiumLists ListPremiumListsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/list/registrars ListRegistrarsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/list/reservedLists ListReservedListsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/list/tlds ListTldsAction GET,POST n APP ADMIN
BACKEND /_dr/admin/updateUserGroup UpdateUserGroupAction POST n APP ADMIN
BACKEND /_dr/admin/verifyOte VerifyOteAction POST n APP ADMIN
BACKEND /_dr/epptool EppToolAction POST n APP ADMIN
BACKEND /_dr/loadtest LoadTestAction POST y APP ADMIN
BACKEND /_dr/task/generateZoneFiles GenerateZoneFilesAction POST n APP ADMIN
BACKEND /_dr/task/refreshDnsForAllDomains RefreshDnsForAllDomainsAction GET n APP ADMIN
@@ -41,29 +41,23 @@ FROM (
FROM (
-- Extract JSON metadata package from monthly logs
SELECT
REGEXP_EXTRACT(logMessages, r'FLOW-LOG-SIGNATURE-METADATA: (.*)\n?$')
REGEXP_EXTRACT(textPayload, r'FLOW-LOG-SIGNATURE-METADATA: (.*)\n?$')
AS json
FROM (
SELECT
protoPayload.resource AS requestPath,
ARRAY(
SELECT logMessage
FROM UNNEST(protoPayload.line)) AS logMessage
textPayload
FROM
`domain-registry-alpha.appengine_logs.appengine_googleapis_com_request_log_*`
`domain-registry-alpha.appengine_logs._var_log_app_*`
WHERE _TABLE_SUFFIX
BETWEEN '20170901'
AND '20170930')
JOIN UNNEST(logMessage) AS logMessages
-- Look for metadata logs from epp and registrar console requests
WHERE requestPath IN ('/_dr/epp', '/_dr/epptool', '/registrar-xhr')
AND STARTS_WITH(logMessages, "google.registry.flows.FlowReporter recordToLogs: FLOW-LOG-SIGNATURE-METADATA")
WHERE STARTS_WITH(textPayload, "FLOW-LOG-SIGNATURE-METADATA")
-- Look for domain creates
AND REGEXP_CONTAINS(
logMessages, r'"commandType":"create","resourceType":"domain"')
textPayload, r'"commandType":"create","resourceType":"domain"')
-- Filter prober data
AND NOT REGEXP_CONTAINS(
logMessages, r'"prober-[a-z]{2}-((any)|(canary))"') )
textPayload, r'"prober-[a-z]{2}-((any)|(canary))"') )
GROUP BY tld, clientId ) AS logs_table
JOIN
EXTERNAL_QUERY("projects/domain-registry-alpha/locations/us/connections/domain-registry-alpha-sql",
@@ -37,13 +37,12 @@ FROM (
FROM (
SELECT
-- Extract the logged JSON payload.
REGEXP_EXTRACT(logMessage, r'FLOW-LOG-SIGNATURE-METADATA: (.*)\n?$')
REGEXP_EXTRACT(textPayload, r'FLOW-LOG-SIGNATURE-METADATA: (.*)\n?$')
AS json
FROM `domain-registry-alpha.cloud_sql_icann_reporting.monthly_logs_201709` AS logs
JOIN
UNNEST(logs.logMessage) AS logMessage
FROM `domain-registry-alpha.appengine_logs._var_log_app_*`
WHERE
STARTS_WITH(logMessage, "google.registry.flows.FlowReporter recordToLogs: FLOW-LOG-SIGNATURE-METADATA"))) AS regexes
STARTS_WITH(textPayload, "FLOW-LOG-SIGNATURE-METADATA")
AND _TABLE_SUFFIX BETWEEN '20170901' AND '20170930')) AS regexes
JOIN
-- Unnest the JSON-parsed tlds.
UNNEST(regexes.tlds) AS tld
@@ -19,11 +19,6 @@
SELECT
protoPayload.resource AS requestPath,
ARRAY(
SELECT
logMessage
FROM
UNNEST(protoPayload.line)) AS logMessage
FROM
`domain-registry-alpha.appengine_logs.appengine_googleapis_com_request_log_*`
WHERE
+1 -1
View File
@@ -85,7 +85,7 @@ tasks.register('run', JavaExec) {
tasks.register('deployNomulus', Exec) {
dependsOn('pushNomulusImage', ':proxy:pushProxyImage')
configure verifyDeploymentConfig
commandLine './deploy-nomulus-for-env.sh', "${rootProject.environment}"
commandLine './deploy-nomulus-for-env.sh', "${rootProject.environment}", "${rootProject.baseDomain}"
}
project.build.dependsOn(tasks.named('buildNomulusImage'))
+17 -7
View File
@@ -17,12 +17,13 @@
# kills all running pods to force k8s to create new pods using the just-pushed
# manifest.
if [[ $# -ne 1 ]]; then
echo "Usage: $0 alpha|crash|qa"
if [[ $# -ne 2 ]]; then
echo "Usage: $0 alpha|crash|qa [base_domain]}"
exit 1
fi
environment=${1}
base_domain=${2}
project="domain-registry-"${environment}
current_context=$(kubectl config current-context)
while read line
@@ -31,16 +32,25 @@ do
echo "Updating cluster ${parts[0]} in location ${parts[1]}..."
gcloud container clusters get-credentials "${parts[0]}" \
--project "${project}" --location "${parts[1]}"
sed s/GCP_PROJECT/"${project}"/g "./kubernetes/nomulus-deployment.yaml" | \
sed s/ENVIRONMENT/"${environment}"/g | \
kubectl apply -f -
kubectl apply -f "./kubernetes/nomulus-service.yaml"
for service in frontend backend pubapi console
do
sed s/GCP_PROJECT/"${project}"/g "./kubernetes/nomulus-${service}.yaml" | \
sed s/ENVIRONMENT/"${environment}"/g | \
kubectl apply -f -
done
# Kills all running pods, new pods created will be pulling the new image.
kubectl delete pods --all
# The multi-cluster gateway is only deployed to one cluster (the one in the US).
if [[ "${parts[1]}" == us-* ]]
then
kubectl apply -f "./kubernetes/nomulus-gateway.yaml"
kubectl apply -f "./kubernetes/gateway/nomulus-gateway.yaml"
for service in frontend backend pubapi console
do
sed s/BASE_DOMAIN/"${base_domain}"/g "./kubernetes/gateway/nomulus-route-${service}.yaml" | \
kubectl apply -f -
sed s/SERVICE/"${service}"/g "./kubernetes/gateway/nomulus-iap-${environment}.yaml" | \
kubectl apply -f -
done
fi
done < <(gcloud container clusters list --project "${project}" | grep nomulus)
kubectl config use-context "$current_context"
@@ -0,0 +1,17 @@
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: nomulus
spec:
gatewayClassName: gke-l7-global-external-managed-mc
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
options:
networking.gke.io/pre-shared-certs: nomulus
allowedRoutes:
kinds:
- kind: HTTPRoute
@@ -0,0 +1,47 @@
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: backend
spec:
parentRefs:
- kind: Gateway
name: nomulus
hostnames:
- "backend.BASE_DOMAIN"
rules:
- matches:
- path:
type: PathPrefix
value: /_dr/task
- path:
type: PathPrefix
value: /_dr/cron
- path:
type: PathPrefix
value: /_dr/admin
- path:
type: PathPrefix
value: /_dr/epptool
- path:
type: PathPrefix
value: /loadtest
backendRefs:
- group: net.gke.io
kind: ServiceImport
name: backend
port: 80
---
apiVersion: networking.gke.io/v1
kind: HealthCheckPolicy
metadata:
name: backend
spec:
default:
config:
type: HTTP
httpHealthCheck:
requestPath: /healthz/
targetRef:
group: net.gke.io
kind: ServiceImport
name: backend
@@ -1,38 +1,31 @@
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
name: nomulus
spec:
gatewayClassName: gke-l7-global-external-managed-mc
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
kinds:
- kind: HTTPRoute
---
kind: HTTPRoute
apiVersion: gateway.networking.k8s.io/v1beta1
metadata:
name: nomulus
labels:
app: nomulus
name: console
spec:
parentRefs:
- kind: Gateway
name: nomulus
hostnames:
- "console.BASE_DOMAIN"
rules:
- backendRefs:
- matches:
- path:
type: PathPrefix
value: /console-api
- path:
type: PathPrefix
value: /console
backendRefs:
- group: net.gke.io
kind: ServiceImport
name: nomulus
name: console
port: 80
---
apiVersion: networking.gke.io/v1
kind: HealthCheckPolicy
metadata:
name: nomulus
name: console
spec:
default:
config:
@@ -42,5 +35,4 @@ spec:
targetRef:
group: net.gke.io
kind: ServiceImport
name: nomulus
name: console
@@ -0,0 +1,35 @@
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: frontend
spec:
parentRefs:
- kind: Gateway
name: nomulus
hostnames:
- "frontend.BASE_DOMAIN"
rules:
- matches:
- path:
type: PathPrefix
value: /_dr/epp
backendRefs:
- group: net.gke.io
kind: ServiceImport
name: frontend
port: 80
---
apiVersion: networking.gke.io/v1
kind: HealthCheckPolicy
metadata:
name: frontend
spec:
default:
config:
type: HTTP
httpHealthCheck:
requestPath: /healthz/
targetRef:
group: net.gke.io
kind: ServiceImport
name: frontend
@@ -0,0 +1,44 @@
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: pubapi
spec:
parentRefs:
- kind: Gateway
name: nomulus
hostnames:
- "pubapi.BASE_DOMAIN"
rules:
- matches:
- path:
type: PathPrefix
value: /_dr/whois
- path:
type: PathPrefix
value: /check
- path:
type: PathPrefix
value: /whois
- path:
type: PathPrefix
value: /rdap
backendRefs:
- group: net.gke.io
kind: ServiceImport
name: pubapi
port: 80
---
apiVersion: networking.gke.io/v1
kind: HealthCheckPolicy
metadata:
name: pubapi
spec:
default:
config:
type: HTTP
httpHealthCheck:
requestPath: /healthz/
targetRef:
group: net.gke.io
kind: ServiceImport
name: pubapi
+60
View File
@@ -0,0 +1,60 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
selector:
matchLabels:
service: backend
template:
metadata:
labels:
service: backend
spec:
serviceAccountName: nomulus
containers:
- name: backend
image: gcr.io/GCP_PROJECT/nomulus
ports:
- containerPort: 8080
name: http
resources:
requests:
cpu: "500m"
args: [ENVIRONMENT]
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: backend
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: backend
minReplicas: 1
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 100
---
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
selector:
service: backend
ports:
- port: 80
targetPort: http
name: http
---
apiVersion: net.gke.io/v1
kind: ServiceExport
metadata:
name: backend
+60
View File
@@ -0,0 +1,60 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: console
spec:
selector:
matchLabels:
service: console
template:
metadata:
labels:
service: console
spec:
serviceAccountName: nomulus
containers:
- name: console
image: gcr.io/GCP_PROJECT/nomulus
ports:
- containerPort: 8080
name: http
resources:
requests:
cpu: "500m"
args: [ENVIRONMENT]
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: console
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: console
minReplicas: 1
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 100
---
apiVersion: v1
kind: Service
metadata:
name: console
spec:
selector:
service: console
ports:
- port: 80
targetPort: http
name: http
---
apiVersion: net.gke.io/v1
kind: ServiceExport
metadata:
name: console
@@ -1,21 +1,19 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: nomulus
labels:
app: nomulus
name: frontend
spec:
selector:
matchLabels:
app: nomulus
service: frontend
template:
metadata:
labels:
app: nomulus
service: frontend
spec:
serviceAccountName: nomulus
containers:
- name: nomulus
- name: frontend
image: gcr.io/GCP_PROJECT/nomulus
ports:
- containerPort: 8080
@@ -50,14 +48,12 @@ spec:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nomulus
labels:
app: nomulus
name: frontend
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nomulus
name: frontend
minReplicas: 1
maxReplicas: 20
metrics:
@@ -67,4 +63,26 @@ spec:
target:
type: Utilization
averageUtilization: 100
---
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
selector:
service: frontend
ports:
- port: 80
targetPort: http
name: http
- port: 43
targetPort: whois
name: whois
- port: 700
targetPort: epp
name: epp
---
apiVersion: net.gke.io/v1
kind: ServiceExport
metadata:
name: frontend
+60
View File
@@ -0,0 +1,60 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: pubapi
spec:
selector:
matchLabels:
service: pubapi
template:
metadata:
labels:
service: pubapi
spec:
serviceAccountName: nomulus
containers:
- name: pubapi
image: gcr.io/GCP_PROJECT/nomulus
ports:
- containerPort: 8080
name: http
resources:
requests:
cpu: "500m"
args: [ENVIRONMENT]
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: pubapi
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: pubapi
minReplicas: 1
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 100
---
apiVersion: v1
kind: Service
metadata:
name: pubapi
spec:
selector:
service: pubapi
ports:
- port: 80
targetPort: http
name: http
---
apiVersion: net.gke.io/v1
kind: ServiceExport
metadata:
name: pubapi
-22
View File
@@ -1,22 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: nomulus
spec:
selector:
app: nomulus
ports:
- port: 80
targetPort: http
name: http
- port: 43
targetPort: whois
name: whois
- port: 700
targetPort: epp
name: epp
---
kind: ServiceExport
apiVersion: net.gke.io/v1
metadata:
name: nomulus
+6
View File
@@ -25,3 +25,9 @@ rootProject.ext.projects = ['production': 'your-production-project',
// The project to host your development/deployment infrastructure. It hosts
// things like release artifacts, CI/CD system, etc.
rootProject.ext.devProject = 'your-dev-project'
rootProject.ext.baseDomains = ['production' : 'registry-production.test',
'sandbox' : 'registry-sandbox.test',
'alpha' : 'registry-alpha.test',
'crash' : 'registry-crash.test',
'qa' : 'registry-qa.test']
+1 -1
View File
@@ -54,7 +54,7 @@ npm cache clean -f
npm install -g n
# Retrying because fails are possible for node.js intallation. See
# https://github.com/nodejs/build/issues/1993
for i in {1..5}; do n 20.10.0 && break || sleep 15; done
for i in {1..5}; do n 22.7.0 && break || sleep 15; done
# Install gp_dump
apt-get install postgresql-client-11 procps -y