mirror of
https://github.com/google/nomulus
synced 2026-07-19 14:32:44 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2facedd60f | |||
| b1ec81f054 | |||
| 779da518df | |||
| 4f53ae0e89 | |||
| da04caeea2 | |||
| a63916b08e | |||
| 36bd508bf9 | |||
| bbdbfe85ed | |||
| 2a7e9a266a | |||
| bd0d8af7b3 | |||
| 2da8ea0185 | |||
| 7a84844000 | |||
| 1580555d30 | |||
| 4fb8a1b50b | |||
| e07f25000d | |||
| cc1777af0c | |||
| 87e54c001f |
@@ -37,7 +37,7 @@
|
||||
background-color: transparent;
|
||||
}
|
||||
.active {
|
||||
background: #eae1e1;
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
}
|
||||
&__content-wrapper {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { AfterViewInit, Component, ViewChild } from '@angular/core';
|
||||
import { RegistrarService } from './registrar/registrar.service';
|
||||
import { UserDataService } from './shared/services/userData.service';
|
||||
import { GlobalLoaderService } from './shared/services/globalLoader.service';
|
||||
@@ -24,7 +24,7 @@ import { MatSidenav } from '@angular/material/sidenav';
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
})
|
||||
export class AppComponent {
|
||||
export class AppComponent implements AfterViewInit {
|
||||
renderRouter: boolean = true;
|
||||
|
||||
@ViewChild('sidenav')
|
||||
|
||||
@@ -48,6 +48,7 @@ import { DomainsWidgetComponent } from './home/widgets/domains-widget.component'
|
||||
import { SettingsWidgetComponent } from './home/widgets/settings-widget.component';
|
||||
import { UserDataService } from './shared/services/userData.service';
|
||||
import WhoisComponent from './settings/whois/whois.component';
|
||||
import { SnackBarModule } from './snackbar.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -79,6 +80,7 @@ import WhoisComponent from './settings/whois/whois.component';
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
MaterialModule,
|
||||
SnackBarModule,
|
||||
],
|
||||
providers: [
|
||||
BackendService,
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
<p>
|
||||
<p class="console-app__header">
|
||||
<mat-toolbar color="primary">
|
||||
<button mat-icon-button aria-label="Open menu" (click)="toggleNavPane()">
|
||||
<mat-icon>menu</mat-icon>
|
||||
</button>
|
||||
<span>
|
||||
<a
|
||||
[routerLink]="'/home'"
|
||||
routerLinkActive="active"
|
||||
class="console-app__logo"
|
||||
>
|
||||
Google Registry
|
||||
</a>
|
||||
</span>
|
||||
<a
|
||||
[routerLink]="'/home'"
|
||||
routerLinkActive="active"
|
||||
class="console-app__logo"
|
||||
>
|
||||
Google Registry
|
||||
</a>
|
||||
<span class="spacer"></span>
|
||||
<app-registrar-selector />
|
||||
<button mat-icon-button aria-label="Open FAQ">
|
||||
<mat-icon>question_mark</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button aria-label="Open user info">
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="menu"
|
||||
#menuTrigger
|
||||
aria-label="Open user info"
|
||||
>
|
||||
<mat-icon>person</mat-icon>
|
||||
</button>
|
||||
<mat-menu #menu="matMenu">
|
||||
<button mat-menu-item (click)="logOut()">Log out</button>
|
||||
</mat-menu>
|
||||
</mat-toolbar>
|
||||
</p>
|
||||
|
||||
@@ -17,6 +17,21 @@
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
&__header {
|
||||
@media (max-width: 599px) {
|
||||
.mat-toolbar {
|
||||
padding: 0;
|
||||
}
|
||||
.console-app__logo {
|
||||
font-size: 16px;
|
||||
}
|
||||
button {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
width: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.spacer {
|
||||
flex: 1;
|
||||
|
||||
@@ -28,4 +28,8 @@ export class HeaderComponent {
|
||||
this.isNavOpen = !this.isNavOpen;
|
||||
this.toggleNavOpen.emit(this.isNavOpen);
|
||||
}
|
||||
|
||||
logOut() {
|
||||
window.open('/console?gcp-iap-mode=CLEAR_LOGIN_COOKIE', '_self');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,4 +29,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@media (max-width: 510px) {
|
||||
.console-app__widget-wrapper__wide {
|
||||
grid-column: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,21 +5,23 @@
|
||||
<mat-icon class="console-app__widget-icon">call</mat-icon>
|
||||
<h1 class="console-app__widget-title">Contact Support</h1>
|
||||
<h4 class="secondary-text text-center">
|
||||
View Google Registry support email and phone information
|
||||
Let us know if you have any questions
|
||||
</h4>
|
||||
</div>
|
||||
<div class="console-app__widget_right">
|
||||
<button mat-button color="primary" class="console-app__widget-link">
|
||||
Give us a Call
|
||||
</button>
|
||||
<div class="console-app__widget-section-header">Give us a Call</div>
|
||||
<p class="secondary-text">
|
||||
Call Google Registry support at <b>+1 (404) 978 8419</b>
|
||||
Call {{ userDataService.userData?.productName }} support at
|
||||
<a href="tel:{{ userDataService.userData?.supportPhoneNumber }}">{{
|
||||
userDataService.userData?.supportPhoneNumber
|
||||
}}</a>
|
||||
</p>
|
||||
<button mat-button color="primary" class="console-app__widget-link">
|
||||
Send us an Email
|
||||
</button>
|
||||
<div class="console-app__widget-section-header">Send us an Email</div>
|
||||
<p class="secondary-text">
|
||||
Email Google Registry at <b>support@google.com</b>
|
||||
Email {{ userDataService.userData?.productName }} at
|
||||
<a href="mailto:{{ userDataService.userData?.supportEmail }}">{{
|
||||
userDataService.userData?.supportEmail
|
||||
}}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { UserDataService } from 'src/app/shared/services/userData.service';
|
||||
|
||||
@Component({
|
||||
selector: '[app-contact-widget]',
|
||||
templateUrl: './contact-widget.component.html',
|
||||
})
|
||||
export class ContactWidgetComponent {
|
||||
constructor() {}
|
||||
constructor(public userDataService: UserDataService) {}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
white-space: nowrap;
|
||||
|
||||
&-icon {
|
||||
transform: scale(3);
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
<div class="console-app__registrar">
|
||||
<div>
|
||||
<button
|
||||
mat-button
|
||||
[routerLink]="'/settings/registrars'"
|
||||
routerLinkActive="active"
|
||||
*ngIf="isMobile; else desktop"
|
||||
>
|
||||
{{ registrarService.activeRegistrarId || "Select registrar" }}
|
||||
<mat-icon>open_in_new</mat-icon>
|
||||
</button>
|
||||
<ng-template #desktop>
|
||||
<mat-form-field class="mat-form-field-density-5" appearance="fill">
|
||||
<mat-label>Registrar</mat-label>
|
||||
<mat-select
|
||||
@@ -14,5 +23,5 @@
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
@@ -12,14 +12,35 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { RegistrarService } from './registrar.service';
|
||||
import { BreakpointObserver } from '@angular/cdk/layout';
|
||||
import { distinctUntilChanged } from 'rxjs';
|
||||
|
||||
const MOBILE_LAYOUT_BREAKPOINT = '(max-width: 599px)';
|
||||
|
||||
@Component({
|
||||
selector: 'app-registrar-selector',
|
||||
templateUrl: './registrar-selector.component.html',
|
||||
styleUrls: ['./registrar-selector.component.scss'],
|
||||
})
|
||||
export class RegistrarSelectorComponent {
|
||||
constructor(protected registrarService: RegistrarService) {}
|
||||
export class RegistrarSelectorComponent implements OnInit {
|
||||
protected isMobile: boolean = false;
|
||||
|
||||
readonly breakpoint$ = this.breakpointObserver
|
||||
.observe([MOBILE_LAYOUT_BREAKPOINT])
|
||||
.pipe(distinctUntilChanged());
|
||||
|
||||
constructor(
|
||||
protected registrarService: RegistrarService,
|
||||
protected breakpointObserver: BreakpointObserver
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.breakpoint$.subscribe(() => this.breakpointChanged());
|
||||
}
|
||||
|
||||
private breakpointChanged() {
|
||||
this.isMobile = this.breakpointObserver.isMatched(MOBILE_LAYOUT_BREAKPOINT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,8 +89,6 @@ export class RegistrarService implements GlobalLoader {
|
||||
}
|
||||
|
||||
loadingTimeout() {
|
||||
this._snackBar.open('Timeout loading registrars', undefined, {
|
||||
duration: 1500,
|
||||
});
|
||||
this._snackBar.open('Timeout loading registrars');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
<div class="console-app__registrars">
|
||||
<table
|
||||
mat-table
|
||||
[dataSource]="registrarService.registrars"
|
||||
<mat-table
|
||||
[dataSource]="dataSource"
|
||||
class="mat-elevation-z8"
|
||||
class="console-app__registrars-table"
|
||||
matSort
|
||||
>
|
||||
<ng-container
|
||||
*ngFor="let column of columns"
|
||||
[matColumnDef]="column.columnDef"
|
||||
>
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
{{ column.header }}
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let row" [innerHTML]="column.cell(row)"></td>
|
||||
<mat-header-cell *matHeaderCellDef> {{ column.header }} </mat-header-cell>
|
||||
<mat-cell *matCellDef="let row" [innerHTML]="column.cell(row)"></mat-cell>
|
||||
</ng-container>
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
||||
</mat-table>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns"></tr>
|
||||
</table>
|
||||
<mat-paginator
|
||||
class="mat-elevation-z8"
|
||||
[pageSizeOptions]="[5, 10, 20]"
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
.console-app {
|
||||
$min-width: 756px;
|
||||
|
||||
&__registrars {
|
||||
margin-top: 1.5rem;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
&__registrars-table {
|
||||
min-width: $min-width !important;
|
||||
}
|
||||
|
||||
.mat-mdc-paginator {
|
||||
min-width: $min-width !important;
|
||||
}
|
||||
|
||||
.mat-column {
|
||||
&-driveId {
|
||||
min-width: 200px;
|
||||
word-break: break-all;
|
||||
}
|
||||
&-registryLockAllowed {
|
||||
max-width: 80px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,16 +12,21 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, ViewChild, ViewEncapsulation } from '@angular/core';
|
||||
import { Registrar, RegistrarService } from './registrar.service';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
|
||||
@Component({
|
||||
selector: 'app-registrar',
|
||||
templateUrl: './registrarsTable.component.html',
|
||||
styleUrls: ['./registrarsTable.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class RegistrarComponent {
|
||||
public static PATH = 'registrars';
|
||||
dataSource: MatTableDataSource<Registrar>;
|
||||
columns = [
|
||||
{
|
||||
columnDef: 'registrarId',
|
||||
@@ -72,5 +77,18 @@ export class RegistrarComponent {
|
||||
},
|
||||
];
|
||||
displayedColumns = this.columns.map((c) => c.columnDef);
|
||||
constructor(protected registrarService: RegistrarService) {}
|
||||
|
||||
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
||||
@ViewChild(MatSort) sort!: MatSort;
|
||||
|
||||
constructor(protected registrarService: RegistrarService) {
|
||||
this.dataSource = new MatTableDataSource<Registrar>(
|
||||
registrarService.registrars
|
||||
);
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.dataSource.sort = this.sort;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<h3 mat-dialog-title>Contact details</h3>
|
||||
<div mat-dialog-content>
|
||||
<form (ngSubmit)="saveAndClose($event)">
|
||||
<div>
|
||||
<p>
|
||||
<mat-form-field class="contact-details__input">
|
||||
<mat-label>Name: </mat-label>
|
||||
<input
|
||||
@@ -11,9 +11,9 @@
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
<mat-form-field class="contact-details__input">
|
||||
<mat-label>Primary account email: </mat-label>
|
||||
<input
|
||||
@@ -25,9 +25,9 @@
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
<mat-form-field class="contact-details__input">
|
||||
<mat-label>Phone: </mat-label>
|
||||
<input
|
||||
@@ -36,9 +36,9 @@
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<p>
|
||||
<mat-form-field class="contact-details__input">
|
||||
<mat-label>Fax: </mat-label>
|
||||
<input
|
||||
@@ -47,7 +47,7 @@
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<div class="contact-details__group">
|
||||
<label>Contact type:</label>
|
||||
|
||||
@@ -129,9 +129,7 @@ export class ContactDetailsDialogComponent {
|
||||
operationObservable.subscribe({
|
||||
complete: this.onCloseCallback.bind(this),
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.error, undefined, {
|
||||
duration: 1500,
|
||||
});
|
||||
this._snackBar.open(err.error);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -175,9 +173,7 @@ export default class ContactComponent {
|
||||
if (confirm(`Please confirm contact ${contact.name} delete`)) {
|
||||
this.contactService.deleteContact(contact).subscribe({
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.error, undefined, {
|
||||
duration: 1500,
|
||||
});
|
||||
this._snackBar.open(err.error);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -64,9 +64,7 @@ export default class SecurityComponent {
|
||||
this.resetDataSource();
|
||||
},
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.error, undefined, {
|
||||
duration: 1500,
|
||||
});
|
||||
this._snackBar.open(err.error);
|
||||
},
|
||||
});
|
||||
this.cancel();
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
.console-settings {
|
||||
.mdc-tab {
|
||||
&.active-link {
|
||||
border-bottom: 2px solid #673ab7;
|
||||
border-bottom: 2px solid var(--primary);
|
||||
.mdc-tab__text-label {
|
||||
color: #673ab7;
|
||||
color: var(--primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,14 +24,14 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 5px;
|
||||
min-width: 450px;
|
||||
min-width: 400px;
|
||||
width: 50%;
|
||||
max-width: 50%;
|
||||
}
|
||||
&__section-description {
|
||||
display: inline-block;
|
||||
margin-block-start: 1em;
|
||||
min-width: 150px;
|
||||
width: 160px;
|
||||
}
|
||||
&__section-form {
|
||||
display: inline-block;
|
||||
|
||||
@@ -61,9 +61,8 @@ export default class WhoisComponent {
|
||||
this.resetDataSource();
|
||||
},
|
||||
error: (err: HttpErrorResponse) => {
|
||||
this._snackBar.open(err.error, undefined, {
|
||||
duration: 1500,
|
||||
});
|
||||
this._snackBar.open(err.error);
|
||||
this.loading = false;
|
||||
},
|
||||
});
|
||||
this.cancel();
|
||||
|
||||
@@ -95,7 +95,7 @@ export class BackendService {
|
||||
|
||||
getUserData(): Observable<UserData> {
|
||||
return this.http
|
||||
.get<UserData>(`/console-api/userdata`)
|
||||
.get<UserData>('/console-api/userdata')
|
||||
.pipe(catchError((err) => this.errorCatcher<UserData>(err)));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,11 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { GlobalLoader, GlobalLoaderService } from './globalLoader.service';
|
||||
|
||||
export interface UserData {
|
||||
isAdmin: boolean;
|
||||
globalRole: string;
|
||||
isAdmin: boolean;
|
||||
productName: string;
|
||||
supportEmail: string;
|
||||
supportPhoneNumber: string;
|
||||
technicalDocsUrl: string;
|
||||
}
|
||||
|
||||
@@ -49,8 +52,6 @@ export class UserDataService implements GlobalLoader {
|
||||
}
|
||||
|
||||
loadingTimeout() {
|
||||
this._snackBar.open('Timeout loading user data', undefined, {
|
||||
duration: 1500,
|
||||
});
|
||||
this._snackBar.open('Timeout loading user data');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2023 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 { NgModule } from '@angular/core';
|
||||
import { MAT_SNACK_BAR_DEFAULT_OPTIONS } from '@angular/material/snack-bar';
|
||||
|
||||
/** Provides a default set of options for the snack bar. */
|
||||
@NgModule({
|
||||
providers: [
|
||||
{ provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, useValue: { duration: 5000 } },
|
||||
],
|
||||
})
|
||||
export class SnackBarModule {}
|
||||
@@ -44,13 +44,19 @@ body {
|
||||
&-link {
|
||||
padding: 0 !important;
|
||||
text-align: left;
|
||||
height: 20px !important;
|
||||
min-width: auto !important;
|
||||
height: min-content !important;
|
||||
}
|
||||
&-section-header {
|
||||
font-weight: 500;
|
||||
color: var(--primary) !important;
|
||||
}
|
||||
&-title {
|
||||
color: var(--primary) !important;
|
||||
text-align: center;
|
||||
}
|
||||
&-icon {
|
||||
color: var(--text);
|
||||
font-size: 5rem;
|
||||
line-height: 5rem;
|
||||
height: 5rem !important;
|
||||
|
||||
@@ -17,20 +17,9 @@ $theme-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400);
|
||||
// The warn palette is optional (defaults to red).
|
||||
$theme-warn: mat.define-palette(mat.$red-palette);
|
||||
|
||||
// Create the theme object. A theme consists of configurations for individual
|
||||
// theming systems such as "color" or "typography".
|
||||
$theme: mat.define-light-theme(
|
||||
(
|
||||
color: (
|
||||
primary: $theme-primary,
|
||||
accent: $theme-accent,
|
||||
warn: $theme-warn,
|
||||
),
|
||||
density: 0,
|
||||
)
|
||||
);
|
||||
|
||||
/** Application specific section **/
|
||||
/**
|
||||
** Application specific section - Global styles and mixins
|
||||
**/
|
||||
|
||||
@mixin form-field-density($density) {
|
||||
$field-typography: mat.define-typography-config(
|
||||
@@ -46,19 +35,6 @@ $theme: mat.define-light-theme(
|
||||
@include form-field-density(-5);
|
||||
}
|
||||
|
||||
$foreground: map.merge($theme, mat.$light-theme-foreground-palette);
|
||||
|
||||
// Access and define a class with secondary color exposed
|
||||
.secondary-text {
|
||||
color: map.get($foreground, "secondary-text");
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary: #{mat.get-color-from-palette($theme-primary, 500)};
|
||||
--secondary: #{map.get($foreground, "secondary-text")};
|
||||
}
|
||||
|
||||
@include mat.all-component-themes($theme);
|
||||
@import "@angular/material/theming";
|
||||
|
||||
// Define application specific typography settings, font-family, etc
|
||||
@@ -67,3 +43,61 @@ $typography-configuration: mat-typography-config(
|
||||
);
|
||||
|
||||
@include angular-material-typography($typography-configuration);
|
||||
|
||||
/**
|
||||
** Light theme
|
||||
**/
|
||||
$light-theme: mat.define-light-theme(
|
||||
(
|
||||
color: (
|
||||
primary: $theme-primary,
|
||||
accent: $theme-accent,
|
||||
warn: $theme-warn,
|
||||
),
|
||||
density: 0,
|
||||
)
|
||||
);
|
||||
|
||||
// Access and define a class with secondary color exposed
|
||||
.secondary-text {
|
||||
color: map.get(mat.$light-theme-foreground-palette, "secondary-text");
|
||||
}
|
||||
|
||||
:root {
|
||||
--text: #{map.get(mat.$light-theme-foreground-palette, "base")};
|
||||
--primary: #{mat.get-color-from-palette($theme-primary, 500)};
|
||||
--secondary: #{map.get(mat.$light-theme-foreground-palette, "secondary-text")};
|
||||
}
|
||||
|
||||
@include mat.all-component-themes($light-theme);
|
||||
|
||||
/**
|
||||
** Dark theme
|
||||
**/
|
||||
$dark-theme: mat.define-dark-theme(
|
||||
(
|
||||
color: (
|
||||
primary: mat.define-palette(mat.$pink-palette),
|
||||
accent: mat.define-palette(mat.$blue-grey-palette),
|
||||
),
|
||||
density: 0,
|
||||
)
|
||||
);
|
||||
|
||||
@mixin _apply-dark-mode-colors() {
|
||||
@include mat.all-component-colors($dark-theme);
|
||||
|
||||
.secondary-text {
|
||||
color: map.get(mat.$dark-theme-foreground-palette, "secondary-text");
|
||||
}
|
||||
|
||||
:root {
|
||||
--text: #{map.get(mat.$dark-theme-foreground-palette, "base")};
|
||||
--primary: #{mat.get-color-from-palette(mat.$pink-palette, 500)};
|
||||
--secondary: #{map.get(mat.$dark-theme-background-palette, "secondary-text")};
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@include _apply-dark-mode-colors();
|
||||
}
|
||||
|
||||
@@ -879,6 +879,17 @@ public final class RegistryConfig {
|
||||
return Optional.ofNullable(config.misc.sheetExportId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the desired delay between outgoing emails when sending in bulk.
|
||||
*
|
||||
* <p>Gmail apparently has unpublished limits on peak throughput over short period.
|
||||
*/
|
||||
@Provides
|
||||
@Config("emailThrottleDuration")
|
||||
public static Duration provideEmailThrottleSeconds(RegistryConfigSettings config) {
|
||||
return Duration.standardSeconds(config.misc.emailThrottleSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the email address we send various alert e-mails to.
|
||||
*
|
||||
@@ -1163,44 +1174,6 @@ public final class RegistryConfig {
|
||||
return CONFIG_SETTINGS.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the OAuth scopes that authentication logic should detect on access tokens.
|
||||
*
|
||||
* <p>This list should be a superset of the required OAuth scope set provided below. Note that
|
||||
* ideally, this setting would not be required and all scopes on an access token would be
|
||||
* detected automatically, but that is not the case due to the way {@code OAuthService} works.
|
||||
*
|
||||
* <p>This is an independent setting from the required OAuth scopes (below) to support use cases
|
||||
* where certain actions require some additional scope (e.g. access to a user's Google Drive)
|
||||
* but that scope shouldn't be required for authentication alone; in that case the Drive scope
|
||||
* would be specified only for this setting, allowing that action to check for its presence.
|
||||
*/
|
||||
@Provides
|
||||
@Config("availableOauthScopes")
|
||||
public static ImmutableSet<String> provideAvailableOauthScopes(RegistryConfigSettings config) {
|
||||
return ImmutableSet.copyOf(config.auth.availableOauthScopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the OAuth scopes that are required for authenticating successfully.
|
||||
*
|
||||
* <p>This set contains the scopes which must be present to authenticate a user. It should be a
|
||||
* subset of the scopes we request from the OAuth interface, provided above.
|
||||
*
|
||||
* <p>If we feel the need, we could define additional fixed scopes, similar to the Java remote
|
||||
* API, which requires at least one of:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code https://www.googleapis.com/auth/appengine.apis}
|
||||
* <li>{@code https://www.googleapis.com/auth/cloud-platform}
|
||||
* </ul>
|
||||
*/
|
||||
@Provides
|
||||
@Config("requiredOauthScopes")
|
||||
public static ImmutableSet<String> provideRequiredOauthScopes(RegistryConfigSettings config) {
|
||||
return ImmutableSet.copyOf(config.auth.requiredOauthScopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides service account email addresses allowed to authenticate with the app at {@link
|
||||
* google.registry.request.auth.AuthSettings.AuthLevel#APP} level.
|
||||
@@ -1212,13 +1185,6 @@ public final class RegistryConfig {
|
||||
return ImmutableSet.copyOf(config.auth.allowedServiceAccountEmails);
|
||||
}
|
||||
|
||||
/** Provides the allowed OAuth client IDs (could be multibinding). */
|
||||
@Provides
|
||||
@Config("allowedOauthClientIds")
|
||||
public static ImmutableSet<String> provideAllowedOauthClientIds(RegistryConfigSettings config) {
|
||||
return ImmutableSet.copyOf(config.auth.allowedOauthClientIds);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Config("oauthClientId")
|
||||
public static String provideOauthClientId(RegistryConfigSettings config) {
|
||||
|
||||
@@ -58,9 +58,6 @@ public class RegistryConfigSettings {
|
||||
|
||||
/** Configuration options for authenticating users. */
|
||||
public static class Auth {
|
||||
public List<String> availableOauthScopes;
|
||||
public List<String> requiredOauthScopes;
|
||||
public List<String> allowedOauthClientIds;
|
||||
public List<String> allowedServiceAccountEmails;
|
||||
public String oauthClientId;
|
||||
}
|
||||
@@ -208,6 +205,7 @@ public class RegistryConfigSettings {
|
||||
public static class Misc {
|
||||
public String sheetExportId;
|
||||
public boolean isEmailSendingEnabled;
|
||||
public int emailThrottleSeconds;
|
||||
public String alertRecipientEmailAddress;
|
||||
// TODO(b/279671974): remove below field after migration
|
||||
public String newAlertRecipientEmailAddress;
|
||||
|
||||
@@ -304,24 +304,6 @@ caching:
|
||||
# Note: Only allowedServiceAccountEmails and oauthClientId should be configured.
|
||||
# Other fields are related to OAuth-based authentication and will be removed.
|
||||
auth:
|
||||
# Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth.
|
||||
# OAuth scopes to detect on access tokens. Superset of requiredOauthScopes.
|
||||
availableOauthScopes:
|
||||
- https://www.googleapis.com/auth/userinfo.email
|
||||
|
||||
# Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth.
|
||||
# OAuth scopes required for authenticating. Subset of availableOauthScopes.
|
||||
requiredOauthScopes:
|
||||
- https://www.googleapis.com/auth/userinfo.email
|
||||
|
||||
# Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth.
|
||||
# OAuth client IDs that are allowed to authenticate and communicate with
|
||||
# backend services, e.g. nomulus tool, EPP proxy, etc. The value in
|
||||
# registryTool.clientId field should be included in this list. Client IDs are
|
||||
# typically of the format
|
||||
# numbers-alphanumerics.apps.googleusercontent.com
|
||||
allowedOauthClientIds: []
|
||||
|
||||
# Service accounts (e.g. default service account, account used by Cloud
|
||||
# Scheduler) allowed to send authenticated requests.
|
||||
allowedServiceAccountEmails:
|
||||
@@ -443,6 +425,9 @@ misc:
|
||||
# Whether emails may be sent. For Prod and Sandbox this should be true.
|
||||
isEmailSendingEnabled: false
|
||||
|
||||
# Delay between bulk messages to avoid triggering Gmail fraud checks
|
||||
emailThrottleSeconds: 30
|
||||
|
||||
# Address we send alert summary emails to.
|
||||
alertRecipientEmailAddress: email@example.com
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
# This is a sample production config (to be deployed in the WEB-INF directory).
|
||||
# This is the same as what Google Registry runs in production, except with
|
||||
# placeholders for Google-specific settings.
|
||||
|
||||
gcpProject:
|
||||
projectId: placeholder
|
||||
# Set to true if running against local servers (localhost)
|
||||
isLocal: false
|
||||
# The "<service>-dot-" prefix is used on the project ID in this URL in order
|
||||
# to get around an issue with double-wildcard SSL certs.
|
||||
defaultServiceUrl: https://domain-registry-placeholder.appspot.com
|
||||
backendServiceUrl: https://backend-dot-domain-registry-placeholder.appspot.com
|
||||
toolsServiceUrl: https://tools-dot-domain-registry-placeholder.appspot.com
|
||||
pubapiServiceUrl: https://pubapi-dot-domain-registry-placeholder.appspot.com
|
||||
|
||||
gSuite:
|
||||
domainName: placeholder
|
||||
outgoingEmailDisplayName: placeholder
|
||||
outgoingEmailAddress: placeholder
|
||||
adminAccountEmailAddress: placeholder
|
||||
supportGroupEmailAddress: placeholder
|
||||
|
||||
registryPolicy:
|
||||
contactAndHostRoidSuffix: placeholder
|
||||
productName: placeholder
|
||||
greetingServerId: placeholder
|
||||
registrarChangesNotificationEmailAddresses:
|
||||
- placeholder
|
||||
- placeholder
|
||||
defaultRegistrarWhoisServer: placeholder
|
||||
tmchCaMode: PRODUCTION
|
||||
tmchCrlUrl: http://crl.icann.org/tmch.crl
|
||||
tmchMarksDbUrl: https://ry.marksdb.org
|
||||
checkApiServletClientId: placeholder
|
||||
registryAdminClientId: placeholder
|
||||
whoisDisclaimer: |
|
||||
multi-line
|
||||
placeholder
|
||||
|
||||
icannReporting:
|
||||
icannTransactionsReportingUploadUrl: https://ry-api.icann.org/report/registrar-transactions
|
||||
icannActivityReportingUploadUrl: https://ry-api.icann.org/report/registry-functions-activity
|
||||
|
||||
oAuth:
|
||||
allowedOauthClientIds:
|
||||
- placeholder.apps.googleusercontent.com
|
||||
- placeholder-for-proxy
|
||||
|
||||
rde:
|
||||
reportUrlPrefix: https://ry-api.icann.org/report/registry-escrow-report
|
||||
uploadUrl: sftp://placeholder@sftpipm2.ironmountain.com/Outbox
|
||||
sshIdentityEmailAddress: placeholder
|
||||
|
||||
registrarConsole:
|
||||
logoFilename: placeholder
|
||||
supportPhoneNumber: placeholder
|
||||
supportEmailAddress: placeholder
|
||||
announcementsEmailAddress: placeholder
|
||||
integrationEmailAddress: placeholder
|
||||
technicalDocsUrl: https://drive.google.com/drive/folders/placeholder
|
||||
|
||||
misc:
|
||||
sheetExportId: placeholder
|
||||
|
||||
cloudDns:
|
||||
rootUrl: null
|
||||
servicePath: null
|
||||
|
||||
keyring:
|
||||
activeKeyring: KMS
|
||||
kms:
|
||||
projectId: placeholder
|
||||
|
||||
registryTool:
|
||||
clientId: placeholder.apps.googleusercontent.com
|
||||
clientSecret: placeholder
|
||||
-9
@@ -59,13 +59,4 @@
|
||||
</description>
|
||||
<schedule>7 3 * * *</schedule>
|
||||
</task>
|
||||
|
||||
<task>
|
||||
<url><![CDATA[/_dr/task/wipeOutCloudSql]]></url>
|
||||
<name>wipeOutCloudSql</name>
|
||||
<description>
|
||||
This job runs an action that deletes all data in Cloud SQL.
|
||||
</description>
|
||||
<schedule>7 3 * * 6</schedule>
|
||||
</task>
|
||||
</entries>
|
||||
|
||||
@@ -29,7 +29,7 @@ import javax.servlet.http.HttpSession;
|
||||
service = Action.Service.DEFAULT,
|
||||
path = "/_dr/epp",
|
||||
method = Method.POST,
|
||||
auth = Auth.AUTH_API_PUBLIC)
|
||||
auth = Auth.AUTH_API_ADMIN)
|
||||
public class EppTlsAction implements Runnable {
|
||||
|
||||
@Inject @Payload byte[] inputXmlBytes;
|
||||
|
||||
@@ -58,13 +58,13 @@ public class EntityYamlUtils {
|
||||
SimpleModule module = new SimpleModule();
|
||||
module.addSerializer(Money.class, new MoneySerializer());
|
||||
module.addDeserializer(Money.class, new MoneyDeserializer());
|
||||
module.addSerializer(Duration.class, new DurationSerializer());
|
||||
ObjectMapper mapper =
|
||||
JsonMapper.builder(new YAMLFactory().disable(Feature.WRITE_DOC_START_MARKER))
|
||||
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
|
||||
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
|
||||
.build()
|
||||
.registerModule(module);
|
||||
mapper.findAndRegisterModules();
|
||||
.build();
|
||||
mapper.findAndRegisterModules().registerModule(module);
|
||||
return mapper;
|
||||
}
|
||||
|
||||
@@ -201,6 +201,24 @@ public class EntityYamlUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/** A custom JSON serializer for a {@link Duration} object. */
|
||||
public static class DurationSerializer extends StdSerializer<Duration> {
|
||||
|
||||
public DurationSerializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public DurationSerializer(Class<Duration> t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Duration value, JsonGenerator gen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
gen.writeString(value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/** A custom JSON serializer for an Optional of a {@link Duration} object. */
|
||||
public static class OptionalDurationSerializer extends StdSerializer<Optional<Duration>> {
|
||||
|
||||
@@ -216,7 +234,7 @@ public class EntityYamlUtils {
|
||||
public void serialize(Optional<Duration> value, JsonGenerator gen, SerializerProvider provider)
|
||||
throws IOException {
|
||||
if (value.isPresent()) {
|
||||
gen.writeNumber(value.get().getMillis());
|
||||
gen.writeString(value.get().toString());
|
||||
} else {
|
||||
gen.writeNull();
|
||||
}
|
||||
|
||||
@@ -35,21 +35,16 @@ import javax.persistence.Table;
|
||||
|
||||
/** A console user, either a registry employee or a registrar partner. */
|
||||
@Entity
|
||||
@Table(
|
||||
indexes = {
|
||||
@Index(columnList = "gaiaId", name = "user_gaia_id_idx"),
|
||||
@Index(columnList = "emailAddress", name = "user_email_address_idx")
|
||||
})
|
||||
@Table(indexes = {@Index(columnList = "emailAddress", name = "user_email_address_idx")})
|
||||
public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
|
||||
private static final long serialVersionUID = 6936728603828566721L;
|
||||
|
||||
/** Autogenerated unique ID of this user. */
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** GAIA ID associated with the user in question. */
|
||||
private String gaiaId;
|
||||
|
||||
/** Email address of the user in question. */
|
||||
@Column(nullable = false)
|
||||
private String emailAddress;
|
||||
@@ -71,10 +66,6 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getGaiaId() {
|
||||
return gaiaId;
|
||||
}
|
||||
|
||||
public String getEmailAddress() {
|
||||
return emailAddress;
|
||||
}
|
||||
@@ -139,12 +130,6 @@ public class User extends UpdateAutoTimestampEntity implements Buildable {
|
||||
return super.build();
|
||||
}
|
||||
|
||||
public Builder setGaiaId(String gaiaId) {
|
||||
checkArgument(!isNullOrEmpty(gaiaId), "Gaia ID cannot be null or empty");
|
||||
getInstance().gaiaId = gaiaId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setEmailAddress(String emailAddress) {
|
||||
getInstance().emailAddress = checkValidEmail(emailAddress);
|
||||
return this;
|
||||
|
||||
@@ -305,14 +305,14 @@ public class AllocationToken extends UpdateAutoTimestampEntity implements Builda
|
||||
new CacheLoader<VKey<AllocationToken>, Optional<AllocationToken>>() {
|
||||
@Override
|
||||
public Optional<AllocationToken> load(VKey<AllocationToken> key) {
|
||||
return tm().transact(() -> tm().loadByKeyIfPresent(key));
|
||||
return tm().reTransact(() -> tm().loadByKeyIfPresent(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<VKey<AllocationToken>, Optional<AllocationToken>> loadAll(
|
||||
Iterable<? extends VKey<AllocationToken>> keys) {
|
||||
ImmutableSet<VKey<AllocationToken>> keySet = ImmutableSet.copyOf(keys);
|
||||
return tm().transact(
|
||||
return tm().reTransact(
|
||||
() ->
|
||||
keySet.stream()
|
||||
.collect(
|
||||
|
||||
@@ -200,7 +200,11 @@ public class Registrar extends UpdateAutoTimestampEntity implements Buildable, J
|
||||
|
||||
/** A caching {@link Supplier} of a registrarId to {@link Registrar} map. */
|
||||
private static final Supplier<ImmutableMap<String, Registrar>> CACHE_BY_REGISTRAR_ID =
|
||||
memoizeWithShortExpiration(() -> Maps.uniqueIndex(loadAll(), Registrar::getRegistrarId));
|
||||
memoizeWithShortExpiration(
|
||||
() ->
|
||||
Maps.uniqueIndex(
|
||||
tm().reTransact(() -> tm().loadAllOf(Registrar.class)),
|
||||
Registrar::getRegistrarId));
|
||||
|
||||
/**
|
||||
* Unique registrar client id. Must conform to "clIDType" as defined in RFC5730.
|
||||
|
||||
@@ -28,7 +28,7 @@ public class SignedMarkRevocationListDao {
|
||||
/** Loads the {@link SignedMarkRevocationList}. */
|
||||
static SignedMarkRevocationList load() {
|
||||
Optional<SignedMarkRevocationList> smdrl =
|
||||
tm().transact(
|
||||
tm().reTransact(
|
||||
() -> {
|
||||
Long revisionId =
|
||||
tm().query("SELECT MAX(revisionId) FROM SignedMarkRevocationList", Long.class)
|
||||
|
||||
@@ -233,7 +233,8 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
new CacheLoader<String, Tld>() {
|
||||
@Override
|
||||
public Tld load(final String tld) {
|
||||
return tm().transact(() -> tm().loadByKeyIfPresent(createVKey(tld))).orElse(null);
|
||||
return tm().reTransact(() -> tm().loadByKeyIfPresent(createVKey(tld)))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -241,7 +242,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
|
||||
ImmutableMap<String, VKey<Tld>> keysMap =
|
||||
toMap(ImmutableSet.copyOf(tlds), Tld::createVKey);
|
||||
Map<VKey<? extends Tld>, Tld> entities =
|
||||
tm().transact(() -> tm().loadByKeysIfPresent(keysMap.values()));
|
||||
tm().reTransact(() -> tm().loadByKeysIfPresent(keysMap.values()));
|
||||
return Maps.transformEntries(keysMap, (k, v) -> entities.getOrDefault(v, null));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ public final class Tlds {
|
||||
private static Supplier<ImmutableMap<String, TldType>> createFreshCache() {
|
||||
return memoizeWithShortExpiration(
|
||||
() ->
|
||||
tm().transact(
|
||||
tm().reTransact(
|
||||
() -> {
|
||||
EntityManager entityManager = tm().getEntityManager();
|
||||
Stream<?> resultStream =
|
||||
|
||||
@@ -53,7 +53,7 @@ public class BillingEmailUtils {
|
||||
GmailClient gmailClient,
|
||||
YearMonth yearMonth,
|
||||
@Config("gSuiteOutgoingEmailAddress") InternetAddress outgoingEmailAddress,
|
||||
@Config("alertRecipientEmailAddress") InternetAddress alertRecipientAddress,
|
||||
@Config("newAlertRecipientEmailAddress") InternetAddress alertRecipientAddress,
|
||||
@Config("invoiceEmailRecipients") ImmutableList<InternetAddress> invoiceEmailRecipients,
|
||||
@Config("invoiceReplyToEmailAddress") Optional<InternetAddress> replyToEmailAddress,
|
||||
@Config("billingBucket") String billingBucket,
|
||||
|
||||
@@ -37,11 +37,13 @@ import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.Sleeper;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import org.joda.time.Duration;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
/** Provides e-mail functionality for Spec11 tasks, such as sending Spec11 reports to registrars. */
|
||||
@@ -57,6 +59,8 @@ public class Spec11EmailUtils {
|
||||
.build()
|
||||
.compileToTofu();
|
||||
private final GmailClient gmailClient;
|
||||
private final Sleeper sleeper;
|
||||
private final Duration emailThrottleDuration;
|
||||
private final InternetAddress outgoingEmailAddress;
|
||||
private final ImmutableList<InternetAddress> spec11BccEmailAddresses;
|
||||
private final InternetAddress alertRecipientAddress;
|
||||
@@ -66,12 +70,16 @@ public class Spec11EmailUtils {
|
||||
@Inject
|
||||
Spec11EmailUtils(
|
||||
GmailClient gmailClient,
|
||||
Sleeper sleeper,
|
||||
@Config("emailThrottleDuration") Duration emailThrottleDuration,
|
||||
@Config("newAlertRecipientEmailAddress") InternetAddress alertRecipientAddress,
|
||||
@Config("spec11OutgoingEmailAddress") InternetAddress spec11OutgoingEmailAddress,
|
||||
@Config("spec11BccEmailAddresses") ImmutableList<InternetAddress> spec11BccEmailAddresses,
|
||||
@Config("spec11WebResources") ImmutableList<String> spec11WebResources,
|
||||
@Config("registryName") String registryName) {
|
||||
this.gmailClient = gmailClient;
|
||||
this.sleeper = sleeper;
|
||||
this.emailThrottleDuration = emailThrottleDuration;
|
||||
this.outgoingEmailAddress = spec11OutgoingEmailAddress;
|
||||
this.spec11BccEmailAddresses = spec11BccEmailAddresses;
|
||||
this.alertRecipientAddress = alertRecipientAddress;
|
||||
@@ -94,6 +102,13 @@ public class Spec11EmailUtils {
|
||||
for (RegistrarThreatMatches registrarThreatMatches : registrarThreatMatchesSet) {
|
||||
RegistrarThreatMatches filteredMatches = filterOutNonPublishedMatches(registrarThreatMatches);
|
||||
if (!filteredMatches.threatMatches().isEmpty()) {
|
||||
if (numRegistrarsEmailed > 0) {
|
||||
try {
|
||||
sleeper.sleep(emailThrottleDuration);
|
||||
} catch (InterruptedException ie) {
|
||||
throw new RuntimeException(ie);
|
||||
}
|
||||
}
|
||||
try {
|
||||
// Handle exceptions individually per registrar so that one failed email doesn't prevent
|
||||
// the rest from being sent.
|
||||
@@ -156,7 +171,7 @@ public class Spec11EmailUtils {
|
||||
gmailClient.sendEmail(
|
||||
EmailMessage.newBuilder()
|
||||
.setSubject(subject)
|
||||
.setBody(getContent(date, soyTemplateInfo, registrarThreatMatches))
|
||||
.setBody(getEmailBody(date, soyTemplateInfo, registrarThreatMatches))
|
||||
.setContentType(MediaType.HTML_UTF_8)
|
||||
.setFrom(outgoingEmailAddress)
|
||||
.addRecipient(getEmailAddressForRegistrar(registrarThreatMatches.clientId()))
|
||||
@@ -164,7 +179,7 @@ public class Spec11EmailUtils {
|
||||
.build());
|
||||
}
|
||||
|
||||
private String getContent(
|
||||
private String getEmailBody(
|
||||
LocalDate date,
|
||||
SoyTemplateInfo soyTemplateInfo,
|
||||
RegistrarThreatMatches registrarThreatMatches) {
|
||||
@@ -175,7 +190,7 @@ public class Spec11EmailUtils {
|
||||
.map(
|
||||
threatMatch ->
|
||||
ImmutableMap.of(
|
||||
"domainName", threatMatch.domainName(),
|
||||
"domainName", toEmailSafeString(threatMatch.domainName()),
|
||||
"threatType", threatMatch.threatType()))
|
||||
.collect(toImmutableList());
|
||||
|
||||
@@ -190,6 +205,12 @@ public class Spec11EmailUtils {
|
||||
return renderer.render();
|
||||
}
|
||||
|
||||
// Mutates a known bad domain to pass spam checks by Email sender and clients, as suggested by
|
||||
// the Gmail abuse-detection team.
|
||||
private String toEmailSafeString(String knownUnsafeDomain) {
|
||||
return knownUnsafeDomain.replace(".", "[.]");
|
||||
}
|
||||
|
||||
/** Sends an e-mail indicating the state of the spec11 pipeline, with a given subject and body. */
|
||||
void sendAlertEmail(String subject, String body) {
|
||||
try {
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.request;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_METHOD_NOT_ALLOWED;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
|
||||
|
||||
@@ -162,6 +163,10 @@ public class RequestHandler<C> {
|
||||
} catch (HttpException e) {
|
||||
e.send(rsp);
|
||||
success = false;
|
||||
} catch (Exception e) {
|
||||
rsp.setStatus(SC_INTERNAL_SERVER_ERROR);
|
||||
rsp.getWriter().write("Internal server error, please try again later");
|
||||
logger.atSevere().withCause(e).log("Encountered internal server error");
|
||||
} finally {
|
||||
requestMetrics.record(
|
||||
new Duration(startTime, clock.nowUtc()),
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
package google.registry.request.auth;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import google.registry.flows.EppTlsAction;
|
||||
import google.registry.flows.TlsCredentials;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthSettings.AuthMethod;
|
||||
import google.registry.request.auth.AuthSettings.UserPolicy;
|
||||
@@ -48,30 +46,18 @@ public enum Auth {
|
||||
* Allows anyone to access, as long as they are logged in.
|
||||
*
|
||||
* <p>This is used by legacy registrar console programmatic endpoints (those that extend {@link
|
||||
* JsonGetAction}, which are accessed via XHR requests sent from a logged-in user when performing
|
||||
* JsonGetAction}), which are accessed via XHR requests sent from a logged-in user when performing
|
||||
* actions on the console.
|
||||
*/
|
||||
AUTH_PUBLIC_LOGGED_IN(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.USER, UserPolicy.PUBLIC),
|
||||
|
||||
/**
|
||||
* Allows any client to access, as long as they are logged in via API-based authentication
|
||||
* mechanisms.
|
||||
* Allows only the app itself (via service accounts) or admins to access.
|
||||
*
|
||||
* <p>This is used by the proxy to access Nomulus endpoints. The proxy service account does NOT
|
||||
* have admin privileges. For EPP, we handle client authentication within {@link EppTlsAction},
|
||||
* using {@link TlsCredentials}. For WHOIS, anyone connecting to the proxy can access.
|
||||
*
|
||||
* <p>Note that the proxy service account DOES need to be allow-listed in the {@code
|
||||
* auth.allowedServiceAccountEmails} field in the config YAML file in order for OIDC-based
|
||||
* authentication to pass.
|
||||
*/
|
||||
AUTH_API_PUBLIC(ImmutableList.of(AuthMethod.API), AuthLevel.APP, UserPolicy.PUBLIC),
|
||||
|
||||
/**
|
||||
* Allows only admins to access.
|
||||
*
|
||||
* <p>This applies to the majority of the endpoints.
|
||||
* <p>This applies to the majority of the endpoints. For APP level authentication to work, the
|
||||
* associated service account needs to be allowlisted in the {@code
|
||||
* auth.allowedServiceAccountEmails} field in the config YAML file.
|
||||
*/
|
||||
AUTH_API_ADMIN(ImmutableList.of(AuthMethod.API), AuthLevel.APP, UserPolicy.ADMIN);
|
||||
|
||||
|
||||
@@ -16,8 +16,6 @@ package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
|
||||
import com.google.appengine.api.oauth.OAuthService;
|
||||
import com.google.appengine.api.oauth.OAuthServiceFactory;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import dagger.Module;
|
||||
@@ -36,9 +34,6 @@ public class AuthModule {
|
||||
// IAP-signed JWT will be in this header.
|
||||
// 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";
|
||||
// GAE will put the content in header "proxy-authorization" in this header when it routes the
|
||||
// request to the app.
|
||||
public static final String PROXY_HEADER_NAME = "X-Google-Proxy-Authorization";
|
||||
public static final String BEARER_PREFIX = "Bearer ";
|
||||
// TODO: Change the IAP audience format once we are on GKE.
|
||||
// See: https://cloud.google.com/iap/docs/signed-headers-howto#verifying_the_jwt_payload
|
||||
@@ -46,16 +41,12 @@ public class AuthModule {
|
||||
private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap";
|
||||
private static final String REGULAR_ISSUER_URL = "https://accounts.google.com";
|
||||
|
||||
/** Provides the custom authentication mechanisms (including OAuth and OIDC). */
|
||||
/** Provides the custom authentication mechanisms. */
|
||||
@Provides
|
||||
ImmutableList<AuthenticationMechanism> provideApiAuthenticationMechanisms(
|
||||
OAuthAuthenticationMechanism oauthAuthenticationMechanism,
|
||||
IapOidcAuthenticationMechanism iapOidcAuthenticationMechanism,
|
||||
RegularOidcAuthenticationMechanism regularOidcAuthenticationMechanism) {
|
||||
return ImmutableList.of(
|
||||
oauthAuthenticationMechanism,
|
||||
iapOidcAuthenticationMechanism,
|
||||
regularOidcAuthenticationMechanism);
|
||||
return ImmutableList.of(iapOidcAuthenticationMechanism, regularOidcAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Qualifier
|
||||
@@ -64,12 +55,6 @@ public class AuthModule {
|
||||
@Qualifier
|
||||
@interface RegularOidc {}
|
||||
|
||||
/** Provides the OAuthService instance. */
|
||||
@Provides
|
||||
OAuthService provideOauthService() {
|
||||
return OAuthServiceFactory.getOAuthService();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@IapOidc
|
||||
@Singleton
|
||||
@@ -98,11 +83,7 @@ public class AuthModule {
|
||||
@Singleton
|
||||
TokenExtractor provideRegularTokenExtractor() {
|
||||
return request -> {
|
||||
// TODO: only check the Authorizaiton header after the migration to OIDC is complete.
|
||||
String rawToken = request.getHeader(PROXY_HEADER_NAME);
|
||||
if (rawToken == null) {
|
||||
rawToken = request.getHeader(AUTHORIZATION);
|
||||
}
|
||||
String rawToken = request.getHeader(AUTHORIZATION);
|
||||
if (rawToken != null && rawToken.startsWith(BEARER_PREFIX)) {
|
||||
return rawToken.substring(BEARER_PREFIX.length());
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.APP;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.USER;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
@@ -22,8 +24,8 @@ import java.util.Optional;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Results of authentication for a given HTTP request, as emitted by an
|
||||
* {@link AuthenticationMechanism}.
|
||||
* Results of authentication for a given HTTP request, as emitted by an {@link
|
||||
* AuthenticationMechanism}.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class AuthResult {
|
||||
@@ -33,6 +35,10 @@ public abstract class AuthResult {
|
||||
/** Information about the authenticated user, if there is one. */
|
||||
public abstract Optional<UserAuthInfo> userAuthInfo();
|
||||
|
||||
/** Service account email of the authenticated app, if there is one. */
|
||||
@SuppressWarnings("unused") // The service account will be logged upon successful login.
|
||||
public abstract Optional<String> appServiceAccount();
|
||||
|
||||
public boolean isAuthenticated() {
|
||||
return authLevel() != AuthLevel.NONE;
|
||||
}
|
||||
@@ -47,15 +53,27 @@ public abstract class AuthResult {
|
||||
.orElse("<logged-out user>");
|
||||
}
|
||||
|
||||
public static AuthResult create(AuthLevel authLevel) {
|
||||
return new AutoValue_AuthResult(authLevel, Optional.empty());
|
||||
public static AuthResult createApp(String email) {
|
||||
return create(APP, null, email);
|
||||
}
|
||||
|
||||
public static AuthResult create(AuthLevel authLevel, @Nullable UserAuthInfo userAuthInfo) {
|
||||
if (authLevel == AuthLevel.USER) {
|
||||
checkNotNull(userAuthInfo);
|
||||
}
|
||||
return new AutoValue_AuthResult(authLevel, Optional.ofNullable(userAuthInfo));
|
||||
public static AuthResult createUser(UserAuthInfo userAuthInfo) {
|
||||
return create(USER, userAuthInfo, null);
|
||||
}
|
||||
|
||||
private static AuthResult create(
|
||||
AuthLevel authLevel, @Nullable UserAuthInfo userAuthInfo, @Nullable String email) {
|
||||
checkArgument(
|
||||
userAuthInfo == null || email == null,
|
||||
"User auth info and service account email cannot be specificed at the same time");
|
||||
checkArgument(
|
||||
authLevel != USER || userAuthInfo != null,
|
||||
"User auth info must be specified for auth level USER");
|
||||
checkArgument(
|
||||
authLevel != APP || email != null,
|
||||
"Service account email must be specified for auth level APP");
|
||||
return new AutoValue_AuthResult(
|
||||
authLevel, Optional.ofNullable(userAuthInfo), Optional.ofNullable(email));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,5 +85,5 @@ public abstract class AuthResult {
|
||||
* returns NOT_AUTHENTICATED in this case, as opposed to absent() if authentication failed and was
|
||||
* required. So as a return from an authorization check, this can be treated as a success.
|
||||
*/
|
||||
public static final AuthResult NOT_AUTHENTICATED = create(AuthLevel.NONE);
|
||||
public static final AuthResult NOT_AUTHENTICATED = create(AuthLevel.NONE, null, null);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package google.registry.request.auth;
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.annotations.Immutable;
|
||||
import google.registry.model.console.UserRoles;
|
||||
|
||||
/**
|
||||
* Parameters used to configure the authenticator.
|
||||
@@ -42,7 +43,10 @@ public abstract class AuthSettings {
|
||||
/** Available methods for authentication. */
|
||||
public enum AuthMethod {
|
||||
|
||||
/** Authentication methods suitable for API-style access, such as OAuth 2. */
|
||||
/**
|
||||
* Authentication methods suitable for API-style access, such as {@link
|
||||
* OidcTokenAuthenticationMechanism}.
|
||||
*/
|
||||
API,
|
||||
|
||||
/** Legacy authentication using cookie-based App Engine Users API. Must come last if present. */
|
||||
@@ -68,10 +72,11 @@ public abstract class AuthSettings {
|
||||
/**
|
||||
* Authentication required, but user not required.
|
||||
*
|
||||
* <p>In Auth: Authentication is required, but app-internal authentication (which isn't
|
||||
* associated with a specific user) is permitted.
|
||||
* <p>In Auth: authentication is required, but App-internal authentication (which isn't
|
||||
* associated with a specific user, but a service account) is permitted. Examples include
|
||||
* requests from Cloud Tasks, Cloud Scheduler, and the proxy.
|
||||
*
|
||||
* <p>In AuthResult: App-internal authentication was successful.
|
||||
* <p>In AuthResult: App-internal authentication (via service accounts) was successful.
|
||||
*/
|
||||
APP,
|
||||
|
||||
@@ -93,10 +98,14 @@ public abstract class AuthSettings {
|
||||
PUBLIC,
|
||||
|
||||
/**
|
||||
* If there is a user, it must be an admin, as determined by isUserAdmin().
|
||||
* If there is a user, it must be an admin, as determined by {@link UserAuthInfo#isUserAdmin()}.
|
||||
*
|
||||
* <p>Note that, according to App Engine, anybody with access to the app in the GCP Console,
|
||||
* <p>Note that, if the user returned is an App Engine {@link
|
||||
* com.google.appengine.api.users.User} , anybody with access to the app in the GCP Console,
|
||||
* including editors and viewers, is an admin.
|
||||
*
|
||||
* <p>On the other hand, if the user is a {@link google.registry.model.console.User}, the admin
|
||||
* role is explicitly defined in that object via the {@link UserRoles#isAdmin()} method.
|
||||
*/
|
||||
ADMIN
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
/**
|
||||
* A particular way to authenticate an HTTP request, returning an {@link AuthResult}.
|
||||
*
|
||||
* <p>For instance, a request could be authenticated using OAuth, via special request headers, etc.
|
||||
* <p>For instance, a request could be authenticated using OIDC, via special request headers, etc.
|
||||
*/
|
||||
public interface AuthenticationMechanism {
|
||||
|
||||
|
||||
@@ -16,8 +16,7 @@ package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.base.Strings.emptyToNull;
|
||||
import static com.google.common.base.Strings.nullToEmpty;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.USER;
|
||||
import static google.registry.request.auth.AuthResult.NOT_AUTHENTICATED;
|
||||
import static google.registry.security.XsrfTokenManager.P_CSRF_TOKEN;
|
||||
import static google.registry.security.XsrfTokenManager.X_CSRF_TOKEN;
|
||||
|
||||
@@ -49,15 +48,14 @@ public class LegacyAuthenticationMechanism implements AuthenticationMechanism {
|
||||
@Override
|
||||
public AuthResult authenticate(HttpServletRequest request) {
|
||||
if (!userService.isUserLoggedIn()) {
|
||||
return AuthResult.create(NONE);
|
||||
return NOT_AUTHENTICATED;
|
||||
}
|
||||
|
||||
if (!SAFE_METHODS.contains(request.getMethod()) && !validateXsrf(request)) {
|
||||
return AuthResult.create(NONE);
|
||||
return NOT_AUTHENTICATED;
|
||||
}
|
||||
|
||||
return AuthResult.create(
|
||||
USER,
|
||||
return AuthResult.createUser(
|
||||
UserAuthInfo.create(userService.getCurrentUser(), userService.isUserAdmin()));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
// Copyright 2017 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.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static google.registry.request.auth.AuthModule.BEARER_PREFIX;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.USER;
|
||||
|
||||
import com.google.appengine.api.oauth.OAuthRequestException;
|
||||
import com.google.appengine.api.oauth.OAuthService;
|
||||
import com.google.appengine.api.oauth.OAuthServiceFailureException;
|
||||
import com.google.appengine.api.users.User;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.flogger.FluentLogger;
|
||||
import google.registry.config.RegistryConfig.Config;
|
||||
import google.registry.config.RegistryEnvironment;
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* OAuth authentication mechanism, using the OAuthService interface.
|
||||
*
|
||||
* <p>Only OAuth version 2 is supported.
|
||||
*/
|
||||
public class OAuthAuthenticationMechanism implements AuthenticationMechanism {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
private final OAuthService oauthService;
|
||||
|
||||
/** The available OAuth scopes for which {@link OAuthService} should check. */
|
||||
private final ImmutableSet<String> availableOauthScopes;
|
||||
|
||||
/** The OAuth scopes which must all be present for authentication to succeed. */
|
||||
private final ImmutableSet<String> requiredOauthScopes;
|
||||
|
||||
private final ImmutableSet<String> allowedOauthClientIds;
|
||||
|
||||
@Inject
|
||||
public OAuthAuthenticationMechanism(
|
||||
OAuthService oauthService,
|
||||
@Config("availableOauthScopes") ImmutableSet<String> availableOauthScopes,
|
||||
@Config("requiredOauthScopes") ImmutableSet<String> requiredOauthScopes,
|
||||
@Config("allowedOauthClientIds") ImmutableSet<String> allowedOauthClientIds) {
|
||||
this.oauthService = oauthService;
|
||||
this.availableOauthScopes = availableOauthScopes;
|
||||
this.requiredOauthScopes = requiredOauthScopes;
|
||||
this.allowedOauthClientIds = allowedOauthClientIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResult authenticate(HttpServletRequest request) {
|
||||
|
||||
// Make sure that there is an Authorization header in Bearer form. OAuthService also accepts
|
||||
// tokens in the request body and URL string, but we should not use those, since they are more
|
||||
// likely to be logged than the Authorization header. Checking to make sure there's a token also
|
||||
// avoids unnecessary RPCs, since OAuthService itself does not check whether the header is
|
||||
// present. In theory, there could be more than one Authorization header, but we only check the
|
||||
// first one, because there's not a legitimate use case for having more than one, and
|
||||
// OAuthService itself only looks at the first one anyway.
|
||||
String header = request.getHeader(AUTHORIZATION);
|
||||
if ((header == null) || !header.startsWith(BEARER_PREFIX)) {
|
||||
if (header != null) {
|
||||
logger.atInfo().log("Invalid authorization header.");
|
||||
}
|
||||
return AuthResult.create(NONE);
|
||||
}
|
||||
// Assume that, if a bearer token is found, it's what OAuthService will use to attempt
|
||||
// authentication. This is not technically guaranteed by the contract of OAuthService; see
|
||||
// OAuthTokenInfo for more information.
|
||||
String rawAccessToken =
|
||||
RegistryEnvironment.get() == RegistryEnvironment.PRODUCTION
|
||||
? "Raw token redacted in prod"
|
||||
: header.substring(BEARER_PREFIX.length());
|
||||
|
||||
// Get the OAuth information. The various oauthService method calls use a single cached
|
||||
// authentication result, so we can call them one by one.
|
||||
User currentUser;
|
||||
boolean isUserAdmin;
|
||||
String oauthClientId;
|
||||
ImmutableSet<String> authorizedScopes;
|
||||
try {
|
||||
String[] availableOauthScopeArray = availableOauthScopes.toArray(new String[0]);
|
||||
currentUser = oauthService.getCurrentUser(availableOauthScopeArray);
|
||||
isUserAdmin = oauthService.isUserAdmin(availableOauthScopeArray);
|
||||
logger.atInfo().log(
|
||||
"Current user: %s (%s).", currentUser, isUserAdmin ? "admin" : "not admin");
|
||||
oauthClientId = oauthService.getClientId(availableOauthScopeArray);
|
||||
logger.atInfo().log("OAuth client ID: %s", oauthClientId);
|
||||
authorizedScopes =
|
||||
ImmutableSet.copyOf(oauthService.getAuthorizedScopes(availableOauthScopeArray));
|
||||
logger.atInfo().log("Authorized scope(s): %s", authorizedScopes);
|
||||
} catch (OAuthRequestException | OAuthServiceFailureException e) {
|
||||
logger.atInfo().withCause(e).log("Unable to get OAuth information.");
|
||||
return AuthResult.create(NONE);
|
||||
}
|
||||
if ((currentUser == null) || (oauthClientId == null) || (authorizedScopes == null)) {
|
||||
return AuthResult.create(NONE);
|
||||
}
|
||||
|
||||
// Make sure that the client ID matches, to avoid a confused deputy attack; see:
|
||||
// http://stackoverflow.com/a/17439317/1179226
|
||||
if (!allowedOauthClientIds.contains(oauthClientId)) {
|
||||
logger.atInfo().log("OAuth client ID is not allowed.");
|
||||
return AuthResult.create(NONE);
|
||||
}
|
||||
|
||||
// Make sure that all required scopes are present.
|
||||
if (!authorizedScopes.containsAll(requiredOauthScopes)) {
|
||||
logger.atInfo().log("Missing required scope(s).");
|
||||
return AuthResult.create(NONE);
|
||||
}
|
||||
|
||||
// Create the {@link AuthResult}, including the OAuth token info.
|
||||
return AuthResult.create(
|
||||
USER,
|
||||
UserAuthInfo.create(
|
||||
currentUser,
|
||||
isUserAdmin,
|
||||
OAuthTokenInfo.create(
|
||||
ImmutableSet.copyOf(authorizedScopes), oauthClientId, rawAccessToken)));
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright 2017 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.request.auth;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/** Information provided by the OAuth authentication mechanism (only) about the session. */
|
||||
@AutoValue
|
||||
public abstract class OAuthTokenInfo {
|
||||
|
||||
/** Authorized OAuth scopes granted by the access token provided with the request. */
|
||||
abstract ImmutableSet<String> authorizedScopes();
|
||||
|
||||
/** OAuth client ID from the access token provided with the request. */
|
||||
abstract String oauthClientId();
|
||||
|
||||
/**
|
||||
* Raw OAuth access token value provided with the request, for passing along to downstream APIs as
|
||||
* appropriate.
|
||||
*
|
||||
* <p>Note that the request parsing code makes certain assumptions about whether the Authorization
|
||||
* header was used as the source of the token. Because OAuthService could theoretically fall back
|
||||
* to some other source of authentication, it might be possible for rawAccessToken not to have
|
||||
* been the source of OAuth authentication. Looking at the code of OAuthService, that could not
|
||||
* currently happen, but if OAuthService were modified in the future so that it tried the bearer
|
||||
* token, and then when that failed, fell back to another, successful authentication path, then
|
||||
* rawAccessToken might not be valid.
|
||||
*/
|
||||
abstract String rawAccessToken();
|
||||
|
||||
static OAuthTokenInfo create(
|
||||
ImmutableSet<String> authorizedScopes, String oauthClientId, String rawAccessToken) {
|
||||
return new AutoValue_OAuthTokenInfo(authorizedScopes, oauthClientId, rawAccessToken);
|
||||
}
|
||||
}
|
||||
+4
-14
@@ -14,8 +14,6 @@
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.APP;
|
||||
|
||||
import com.google.api.client.json.webtoken.JsonWebSignature;
|
||||
import com.google.auth.oauth2.TokenVerifier;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@@ -97,12 +95,11 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
}
|
||||
Optional<User> maybeUser = UserDao.loadUser(email);
|
||||
if (maybeUser.isPresent()) {
|
||||
return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(maybeUser.get()));
|
||||
return AuthResult.createUser(UserAuthInfo.create(maybeUser.get()));
|
||||
}
|
||||
// TODO: implement caching so we don't have to look up the database for every request.
|
||||
logger.atInfo().log("No end user found for email address %s", email);
|
||||
if (serviceAccountEmails.stream().anyMatch(e -> e.equals(email))) {
|
||||
return AuthResult.create(APP);
|
||||
return AuthResult.createApp(email);
|
||||
}
|
||||
logger.atInfo().log("No service account found for email address %s", email);
|
||||
logger.atWarning().log(
|
||||
@@ -153,15 +150,8 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication
|
||||
*
|
||||
* <p>If the endpoint is not behind IAP, we can try to authenticate the OIDC token supplied in the
|
||||
* request header directly. Ideally we would like all endpoints to be behind IAP, but being able
|
||||
* to authenticate the token directly provides us with the flexibility to do away with OAuth-based
|
||||
* {@link OAuthAuthenticationMechanism} that is tied to App Engine runtime without having to turn
|
||||
* on IAP, which is an all-or-nothing switch for each GAE service (i.e. no way to turn it on only
|
||||
* for certain GAE endpoints).
|
||||
*
|
||||
* <p>Note that this mechanism will try to first extract the token under the "proxy-authorization"
|
||||
* header, before trying "authorization". This is because currently the GAE OAuth service always
|
||||
* uses "authorization", and we would like to provide a way for both auth mechanisms to be working
|
||||
* at the same time for the same request.
|
||||
* to authenticate the token directly provides us with some extra flexibility that comes in handy,
|
||||
* at least during the migration to GKE.
|
||||
*
|
||||
* @see <a href=https://datatracker.ietf.org/doc/html/rfc6750>Bearer Token Usage</a>
|
||||
*/
|
||||
|
||||
@@ -60,7 +60,8 @@ public class RequestAuthenticator {
|
||||
if (auth.minimumLevel() == APP && !authResult.isAuthenticated()) {
|
||||
logger.atWarning().log("Not authorized; no authentication found.");
|
||||
return Optional.empty();
|
||||
} else if (auth.minimumLevel() == USER && authResult.authLevel() != USER) {
|
||||
}
|
||||
if (auth.minimumLevel() == USER && authResult.authLevel() != USER) {
|
||||
logger.atWarning().log("Not authorized; no authenticated user.");
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -81,12 +82,12 @@ public class RequestAuthenticator {
|
||||
* @param req the {@link HttpServletRequest}; some authentication mechanisms use HTTP headers
|
||||
* @return an authentication result; if no authentication was made, returns NOT_AUTHENTICATED
|
||||
*/
|
||||
private AuthResult authenticate(AuthSettings auth, HttpServletRequest req) {
|
||||
AuthResult authenticate(AuthSettings auth, HttpServletRequest req) {
|
||||
checkAuthConfig(auth);
|
||||
for (AuthMethod authMethod : auth.methods()) {
|
||||
AuthResult authResult;
|
||||
switch (authMethod) {
|
||||
// API-based user authentication mechanisms, such as OAuth and OIDC.
|
||||
// API-based user authentication mechanisms, such as OIDC.
|
||||
case API:
|
||||
for (AuthenticationMechanism authMechanism : apiAuthenticationMechanisms) {
|
||||
authResult = authMechanism.authenticate(req);
|
||||
@@ -113,10 +114,9 @@ public class RequestAuthenticator {
|
||||
|
||||
/** Validates an AuthSettings object, checking for invalid setting combinations. */
|
||||
static void checkAuthConfig(AuthSettings auth) {
|
||||
ImmutableList<AuthMethod> authMethods = ImmutableList.copyOf(auth.methods());
|
||||
checkArgument(!authMethods.isEmpty(), "Must specify at least one auth method");
|
||||
checkArgument(!auth.methods().isEmpty(), "Must specify at least one auth method");
|
||||
checkArgument(
|
||||
Ordering.explicit(AuthMethod.API, AuthMethod.LEGACY).isStrictlyOrdered(authMethods),
|
||||
Ordering.explicit(AuthMethod.API, AuthMethod.LEGACY).isStrictlyOrdered(auth.methods()),
|
||||
"Auth methods must be unique and strictly in order - API, LEGACY");
|
||||
checkArgument(
|
||||
(auth.minimumLevel() != NONE) || (auth.userPolicy() != ADMIN),
|
||||
|
||||
@@ -22,6 +22,8 @@ import java.util.Optional;
|
||||
@AutoValue
|
||||
public abstract class UserAuthInfo {
|
||||
|
||||
public abstract Optional<google.registry.model.console.User> consoleUser();
|
||||
|
||||
/** User object from the AppEngine Users API. */
|
||||
public abstract Optional<User> appEngineUser();
|
||||
|
||||
@@ -34,11 +36,6 @@ public abstract class UserAuthInfo {
|
||||
*/
|
||||
public abstract boolean isUserAdmin();
|
||||
|
||||
public abstract Optional<google.registry.model.console.User> consoleUser();
|
||||
|
||||
/** Used by the OAuth authentication mechanism (only) to return information about the session. */
|
||||
public abstract Optional<OAuthTokenInfo> oauthTokenInfo();
|
||||
|
||||
public String getEmailAddress() {
|
||||
return appEngineUser()
|
||||
.map(User::getEmail)
|
||||
@@ -51,20 +48,12 @@ public abstract class UserAuthInfo {
|
||||
.orElseGet(() -> consoleUser().get().getEmailAddress());
|
||||
}
|
||||
|
||||
public static UserAuthInfo create(
|
||||
User user, boolean isUserAdmin) {
|
||||
return new AutoValue_UserAuthInfo(
|
||||
Optional.of(user), isUserAdmin, Optional.empty(), Optional.empty());
|
||||
}
|
||||
|
||||
public static UserAuthInfo create(
|
||||
User user, boolean isUserAdmin, OAuthTokenInfo oauthTokenInfo) {
|
||||
return new AutoValue_UserAuthInfo(
|
||||
Optional.of(user), isUserAdmin, Optional.empty(), Optional.of(oauthTokenInfo));
|
||||
public static UserAuthInfo create(User user, boolean isUserAdmin) {
|
||||
return new AutoValue_UserAuthInfo(Optional.empty(), Optional.of(user), isUserAdmin);
|
||||
}
|
||||
|
||||
public static UserAuthInfo create(google.registry.model.console.User user) {
|
||||
return new AutoValue_UserAuthInfo(
|
||||
Optional.empty(), user.getUserRoles().isAdmin(), Optional.of(user), Optional.empty());
|
||||
Optional.of(user), Optional.empty(), user.getUserRoles().isAdmin());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,13 +50,6 @@ final class RegistryCli implements CommandRunner {
|
||||
description = "Sets the default environment to run the command.")
|
||||
private RegistryToolEnvironment environment = RegistryToolEnvironment.PRODUCTION;
|
||||
|
||||
@Parameter(
|
||||
names = "--oauth",
|
||||
description =
|
||||
"Turn on OAuth-based authentication, the usage of which is to be deprecated. Use"
|
||||
+ " `create_user` to create an Admin user that allows for OIDC-based authentication.")
|
||||
private boolean oAuth = false;
|
||||
|
||||
@Parameter(
|
||||
names = {"-c", "--commands"},
|
||||
description = "Returns all command names.")
|
||||
@@ -168,7 +161,6 @@ final class RegistryCli implements CommandRunner {
|
||||
DaggerRegistryToolComponent.builder()
|
||||
.credentialFilePath(credentialJson)
|
||||
.sqlAccessInfoFile(sqlAccessInfoFile)
|
||||
.addOAuthHeader(oAuth)
|
||||
.build();
|
||||
|
||||
// JCommander stores sub-commands as nested JCommander objects containing a list of user objects
|
||||
|
||||
@@ -191,9 +191,6 @@ interface RegistryToolComponent {
|
||||
@BindsInstance
|
||||
Builder sqlAccessInfoFile(@Nullable @Config("sqlAccessInfoFile") String sqlAccessInfoFile);
|
||||
|
||||
@BindsInstance
|
||||
Builder addOAuthHeader(@Config("addOauthHeader") boolean addOauthHeader);
|
||||
|
||||
RegistryToolComponent build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
package google.registry.tools;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.PROXY_AUTHORIZATION;
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
|
||||
import com.google.api.client.http.HttpRequestFactory;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
@@ -42,8 +42,7 @@ final class RequestFactoryModule {
|
||||
@Provides
|
||||
static HttpRequestFactory provideHttpRequestFactory(
|
||||
@ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle,
|
||||
@Config("oauthClientId") String oauthClientId,
|
||||
@Config("addOauthHeader") boolean addOauthHeader) {
|
||||
@Config("oauthClientId") String oauthClientId) {
|
||||
if (RegistryConfig.areServersLocal()) {
|
||||
return new NetHttpTransport()
|
||||
.createRequestFactory(
|
||||
@@ -55,15 +54,11 @@ final class RequestFactoryModule {
|
||||
return new NetHttpTransport()
|
||||
.createRequestFactory(
|
||||
request -> {
|
||||
if (addOauthHeader) {
|
||||
// Use the standard credential initializer to set the Authorization header
|
||||
credentialsBundle.getHttpRequestInitializer().initialize(request);
|
||||
}
|
||||
// Set OIDC token as the alternative bearer token.
|
||||
// Set OIDC token as the bearer token.
|
||||
request
|
||||
.getHeaders()
|
||||
.set(
|
||||
PROXY_AUTHORIZATION,
|
||||
AUTHORIZATION,
|
||||
"Bearer "
|
||||
+ OidcTokenUtils.createOidcToken(credentialsBundle, oauthClientId));
|
||||
// GAE request times out after 10 min, so here we set the timeout to 10 min. This is
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
|
||||
import static com.google.common.collect.Iterables.getLast;
|
||||
import static google.registry.dns.DnsUtils.requestDomainDnsRefresh;
|
||||
import static google.registry.model.tld.Tlds.assertTldsExist;
|
||||
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.RequestParameters.PARAM_TLDS;
|
||||
import static google.registry.util.DateTimeUtils.END_OF_TIME;
|
||||
@@ -82,13 +83,16 @@ public class RefreshDnsForAllDomainsAction implements Runnable {
|
||||
public void run() {
|
||||
assertTldsExist(tlds);
|
||||
checkArgument(batchSize > 0, "Must specify a positive number for batch size");
|
||||
int smearMinutes = tm().transact(this::calculateSmearMinutes);
|
||||
int smearMinutes = tm().transact(this::calculateSmearMinutes, TRANSACTION_REPEATABLE_READ);
|
||||
|
||||
ImmutableList<String> domainsBatch;
|
||||
@Nullable String lastInPreviousBatch = null;
|
||||
do {
|
||||
Optional<String> lastInPreviousBatchOpt = Optional.ofNullable(lastInPreviousBatch);
|
||||
domainsBatch = tm().transact(() -> refreshBatch(lastInPreviousBatchOpt, smearMinutes));
|
||||
domainsBatch =
|
||||
tm().transact(
|
||||
() -> refreshBatch(lastInPreviousBatchOpt, smearMinutes),
|
||||
TRANSACTION_REPEATABLE_READ);
|
||||
lastInPreviousBatch = domainsBatch.isEmpty() ? null : getLast(domainsBatch);
|
||||
} while (domainsBatch.size() == batchSize);
|
||||
}
|
||||
|
||||
@@ -40,15 +40,24 @@ public class ConsoleUserDataAction implements JsonGetAction {
|
||||
|
||||
private final AuthResult authResult;
|
||||
private final Response response;
|
||||
private final String productName;
|
||||
private final String supportPhoneNumber;
|
||||
private final String supportEmail;
|
||||
private final String technicalDocsUrl;
|
||||
|
||||
@Inject
|
||||
public ConsoleUserDataAction(
|
||||
AuthResult authResult,
|
||||
Response response,
|
||||
@Config("productName") String productName,
|
||||
@Config("supportEmail") String supportEmail,
|
||||
@Config("supportPhoneNumber") String supportPhoneNumber,
|
||||
@Config("technicalDocsUrl") String technicalDocsUrl) {
|
||||
this.response = response;
|
||||
this.authResult = authResult;
|
||||
this.productName = productName;
|
||||
this.supportEmail = supportEmail;
|
||||
this.supportPhoneNumber = supportPhoneNumber;
|
||||
this.technicalDocsUrl = technicalDocsUrl;
|
||||
}
|
||||
|
||||
@@ -74,6 +83,10 @@ public class ConsoleUserDataAction implements JsonGetAction {
|
||||
// auth checks.
|
||||
"isAdmin", user.getUserRoles().isAdmin(),
|
||||
"globalRole", user.getUserRoles().getGlobalRole(),
|
||||
// Include static contact resources in this call to minimize round trips
|
||||
"productName", productName,
|
||||
"supportEmail", supportEmail,
|
||||
"supportPhoneNumber", supportPhoneNumber,
|
||||
// Is used by UI to construct a link to registry resources
|
||||
"technicalDocsUrl", technicalDocsUrl));
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ package google.registry.ui.server.console.settings;
|
||||
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
|
||||
import avro.shaded.com.google.common.collect.ImmutableList;
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.flows.certs.CertificateChecker;
|
||||
@@ -103,42 +102,31 @@ public class SecurityAction implements JsonGetAction {
|
||||
.asBuilder()
|
||||
.setIpAddressAllowList(registrarParameter.getIpAddressAllowList());
|
||||
|
||||
boolean hasInvalidCerts =
|
||||
ImmutableList.of(
|
||||
registrarParameter.getClientCertificate(),
|
||||
registrarParameter.getFailoverClientCertificate())
|
||||
.stream()
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.anyMatch(
|
||||
cert -> {
|
||||
try {
|
||||
certificateChecker.validateCertificate(cert);
|
||||
return false;
|
||||
} catch (InsecureCertificateException e) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (hasInvalidCerts) {
|
||||
try {
|
||||
if (!savedRegistrar
|
||||
.getClientCertificate()
|
||||
.equals(registrarParameter.getClientCertificate())) {
|
||||
if (registrarParameter.getClientCertificate().isPresent()) {
|
||||
String newClientCert = registrarParameter.getClientCertificate().get();
|
||||
certificateChecker.validateCertificate(newClientCert);
|
||||
updatedRegistrar.setClientCertificate(newClientCert, tm().getTransactionTime());
|
||||
}
|
||||
}
|
||||
if (!savedRegistrar
|
||||
.getFailoverClientCertificate()
|
||||
.equals(registrarParameter.getFailoverClientCertificate())) {
|
||||
if (registrarParameter.getFailoverClientCertificate().isPresent()) {
|
||||
String newFailoverCert = registrarParameter.getFailoverClientCertificate().get();
|
||||
certificateChecker.validateCertificate(newFailoverCert);
|
||||
updatedRegistrar.setFailoverClientCertificate(newFailoverCert, tm().getTransactionTime());
|
||||
}
|
||||
}
|
||||
} catch (InsecureCertificateException e) {
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
response.setPayload("Insecure Certificate in parameter");
|
||||
response.setPayload("Invalid certificate in parameter");
|
||||
return;
|
||||
}
|
||||
|
||||
registrarParameter
|
||||
.getClientCertificate()
|
||||
.ifPresent(
|
||||
newClientCert ->
|
||||
updatedRegistrar.setClientCertificate(newClientCert, tm().getTransactionTime()));
|
||||
|
||||
registrarParameter
|
||||
.getFailoverClientCertificate()
|
||||
.ifPresent(
|
||||
failoverCert ->
|
||||
updatedRegistrar.setFailoverClientCertificate(
|
||||
failoverCert, tm().getTransactionTime()));
|
||||
|
||||
tm().put(updatedRegistrar.build());
|
||||
response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ import org.joda.time.DateTime;
|
||||
service = Action.Service.PUBAPI,
|
||||
path = "/_dr/whois",
|
||||
method = POST,
|
||||
auth = Auth.AUTH_API_PUBLIC)
|
||||
auth = Auth.AUTH_API_ADMIN)
|
||||
public class WhoisAction implements Runnable {
|
||||
|
||||
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
|
||||
|
||||
@@ -34,7 +34,7 @@ import com.google.api.client.http.HttpTransport;
|
||||
import com.google.api.client.http.LowLevelHttpRequest;
|
||||
import com.google.api.client.http.LowLevelHttpResponse;
|
||||
import com.google.api.client.json.Json;
|
||||
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||
import com.google.api.client.json.gson.GsonFactory;
|
||||
import com.google.api.client.testing.http.HttpTesting;
|
||||
import com.google.api.client.testing.http.MockHttpTransport;
|
||||
import com.google.api.client.testing.http.MockLowLevelHttpRequest;
|
||||
@@ -300,6 +300,6 @@ class DirectoryGroupsConnectionTest {
|
||||
HttpRequest request = transport.createRequestFactory()
|
||||
.buildGetRequest(HttpTesting.SIMPLE_GENERIC_URL)
|
||||
.setThrowExceptionOnExecuteError(false);
|
||||
return GoogleJsonResponseException.from(new JacksonFactory(), request.execute());
|
||||
return GoogleJsonResponseException.from(new GsonFactory(), request.execute());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,13 +30,11 @@ public class UserDaoTest extends EntityTestCase {
|
||||
User user1 =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
|
||||
.build();
|
||||
User user2 =
|
||||
new User.Builder()
|
||||
.setEmailAddress("foo@bar.com")
|
||||
.setGaiaId("otherId")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
|
||||
.build();
|
||||
UserDao.saveUser(user1);
|
||||
@@ -54,7 +52,6 @@ public class UserDaoTest extends EntityTestCase {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
|
||||
.build();
|
||||
UserDao.saveUser(user);
|
||||
@@ -71,13 +68,11 @@ public class UserDaoTest extends EntityTestCase {
|
||||
User user1 =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
|
||||
.build();
|
||||
User user2 =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setGaiaId("otherId")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
|
||||
.build();
|
||||
UserDao.saveUser(user1);
|
||||
|
||||
@@ -30,10 +30,9 @@ public class UserTest extends EntityTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPersistence_lookupByGaiaId() {
|
||||
void testPersistence_lookupByEmail() {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setGaiaId("gaiaId")
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).setIsAdmin(true).build())
|
||||
@@ -43,10 +42,11 @@ public class UserTest extends EntityTestCase {
|
||||
() -> {
|
||||
assertAboutImmutableObjects()
|
||||
.that(
|
||||
tm().query("FROM User WHERE gaiaId = 'gaiaId'", User.class).getSingleResult())
|
||||
tm().query("FROM User WHERE emailAddress = 'email@email.com'", User.class)
|
||||
.getSingleResult())
|
||||
.isEqualExceptFields(user, "id", "updateTimestamp");
|
||||
assertThat(
|
||||
tm().query("FROM User WHERE gaiaId = 'badGaiaId'", User.class)
|
||||
tm().query("FROM User WHERE emailAddress = 'nobody@email.com'", User.class)
|
||||
.getResultList())
|
||||
.isEmpty();
|
||||
});
|
||||
@@ -55,9 +55,6 @@ public class UserTest extends EntityTestCase {
|
||||
@Test
|
||||
void testFailure_badInputs() {
|
||||
User.Builder builder = new User.Builder();
|
||||
assertThat(assertThrows(IllegalArgumentException.class, () -> builder.setGaiaId(null)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Gaia ID cannot be null or empty");
|
||||
assertThat(assertThrows(IllegalArgumentException.class, () -> builder.setEmailAddress("")))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Provided email is not a valid email address");
|
||||
@@ -72,7 +69,7 @@ public class UserTest extends EntityTestCase {
|
||||
assertThat(assertThrows(IllegalArgumentException.class, () -> builder.setUserRoles(null)))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("User roles cannot be null");
|
||||
|
||||
|
||||
assertThat(assertThrows(IllegalArgumentException.class, builder::build))
|
||||
.hasMessageThat()
|
||||
.isEqualTo("Email address cannot be null");
|
||||
@@ -99,7 +96,6 @@ public class UserTest extends EntityTestCase {
|
||||
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setGaiaId("gaiaId")
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
|
||||
@@ -28,7 +28,6 @@ import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.Actions;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeResponse;
|
||||
@@ -48,13 +47,11 @@ abstract class RdapActionBaseTestCase<A extends RdapActionBase> {
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
protected static final AuthResult AUTH_RESULT =
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(new User("rdap.user@user.com", "gmail.com", "12345"), false));
|
||||
|
||||
protected static final AuthResult AUTH_RESULT_ADMIN =
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(new User("rdap.admin@google.com", "gmail.com", "12345"), true));
|
||||
|
||||
protected FakeResponse response = new FakeResponse();
|
||||
|
||||
@@ -26,6 +26,8 @@ import static google.registry.testing.DatabaseHelper.loadByEntity;
|
||||
import static google.registry.testing.DatabaseHelper.persistActiveHost;
|
||||
import static google.registry.testing.DatabaseHelper.persistResource;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -41,11 +43,13 @@ import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationT
|
||||
import google.registry.reporting.spec11.soy.Spec11EmailSoyInfo;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.util.EmailMessage;
|
||||
import google.registry.util.Sleeper;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import org.joda.time.Duration;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -101,6 +105,8 @@ class Spec11EmailUtilsTest {
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
|
||||
@Mock private GmailClient gmailClient;
|
||||
@Mock private Sleeper sleeper;
|
||||
private Duration emailThrottleDuration = Duration.millis(1);
|
||||
private Spec11EmailUtils emailUtils;
|
||||
private ArgumentCaptor<EmailMessage> contentCaptor;
|
||||
private final LocalDate date = new LocalDate(2018, 7, 15);
|
||||
@@ -114,6 +120,8 @@ class Spec11EmailUtilsTest {
|
||||
emailUtils =
|
||||
new Spec11EmailUtils(
|
||||
gmailClient,
|
||||
sleeper,
|
||||
emailThrottleDuration,
|
||||
new InternetAddress("my-receiver@test.com"),
|
||||
new InternetAddress("abuse@test.com"),
|
||||
ImmutableList.of(
|
||||
@@ -128,6 +136,19 @@ class Spec11EmailUtilsTest {
|
||||
persistDomainWithHost("c.com", host);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_sleepsBetweenSending() throws Exception {
|
||||
emailUtils.emailSpec11Reports(
|
||||
date,
|
||||
Spec11EmailSoyInfo.MONTHLY_SPEC_11_EMAIL,
|
||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||
sampleThreatMatches());
|
||||
// We inspect individual parameters because Message doesn't implement equals().
|
||||
verify(gmailClient, times(3)).sendEmail(any(EmailMessage.class));
|
||||
// Sleep once between two reports sent in a tight loop. No sleep before the final alert message.
|
||||
verify(sleeper, times(1)).sleep(same(emailThrottleDuration));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_emailMonthlySpec11Reports() throws Exception {
|
||||
emailUtils.emailSpec11Reports(
|
||||
@@ -144,7 +165,7 @@ class Spec11EmailUtilsTest {
|
||||
"the.registrar@example.com",
|
||||
ImmutableList.of("abuse@test.com", "bcc@test.com"),
|
||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||
String.format(MONTHLY_EMAIL_FORMAT, "<tr><td>a.com</td><td>MALWARE</td></tr>"),
|
||||
String.format(MONTHLY_EMAIL_FORMAT, "<tr><td>a[.]com</td><td>MALWARE</td></tr>"),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedContents.get(1),
|
||||
@@ -154,7 +175,7 @@ class Spec11EmailUtilsTest {
|
||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||
String.format(
|
||||
MONTHLY_EMAIL_FORMAT,
|
||||
"<tr><td>b.com</td><td>MALWARE</td></tr><tr><td>c.com</td><td>MALWARE</td></tr>"),
|
||||
"<tr><td>b[.]com</td><td>MALWARE</td></tr><tr><td>c[.]com</td><td>MALWARE</td></tr>"),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedContents.get(2),
|
||||
@@ -182,7 +203,7 @@ class Spec11EmailUtilsTest {
|
||||
"the.registrar@example.com",
|
||||
ImmutableList.of("abuse@test.com", "bcc@test.com"),
|
||||
"Super Cool Registry Daily Threat Detector [2018-07-15]",
|
||||
String.format(DAILY_EMAIL_FORMAT, "<tr><td>a.com</td><td>MALWARE</td></tr>"),
|
||||
String.format(DAILY_EMAIL_FORMAT, "<tr><td>a[.]com</td><td>MALWARE</td></tr>"),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedMessages.get(1),
|
||||
@@ -192,7 +213,7 @@ class Spec11EmailUtilsTest {
|
||||
"Super Cool Registry Daily Threat Detector [2018-07-15]",
|
||||
String.format(
|
||||
DAILY_EMAIL_FORMAT,
|
||||
"<tr><td>b.com</td><td>MALWARE</td></tr><tr><td>c.com</td><td>MALWARE</td></tr>"),
|
||||
"<tr><td>b[.]com</td><td>MALWARE</td></tr><tr><td>c[.]com</td><td>MALWARE</td></tr>"),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedMessages.get(2),
|
||||
@@ -223,7 +244,7 @@ class Spec11EmailUtilsTest {
|
||||
"new.registrar@example.com",
|
||||
ImmutableList.of("abuse@test.com", "bcc@test.com"),
|
||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||
String.format(MONTHLY_EMAIL_FORMAT, "<tr><td>c.com</td><td>MALWARE</td></tr>"),
|
||||
String.format(MONTHLY_EMAIL_FORMAT, "<tr><td>c[.]com</td><td>MALWARE</td></tr>"),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedContents.get(1),
|
||||
@@ -256,7 +277,7 @@ class Spec11EmailUtilsTest {
|
||||
"the.registrar@example.com",
|
||||
ImmutableList.of("abuse@test.com", "bcc@test.com"),
|
||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||
String.format(MONTHLY_EMAIL_FORMAT, "<tr><td>a.com</td><td>MALWARE</td></tr>"),
|
||||
String.format(MONTHLY_EMAIL_FORMAT, "<tr><td>a[.]com</td><td>MALWARE</td></tr>"),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedContents.get(1),
|
||||
@@ -266,7 +287,7 @@ class Spec11EmailUtilsTest {
|
||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||
String.format(
|
||||
MONTHLY_EMAIL_FORMAT,
|
||||
"<tr><td>b.com</td><td>MALWARE</td></tr><tr><td>c.com</td><td>MALWARE</td></tr>"),
|
||||
"<tr><td>b[.]com</td><td>MALWARE</td></tr><tr><td>c[.]com</td><td>MALWARE</td></tr>"),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedContents.get(2),
|
||||
@@ -311,7 +332,7 @@ class Spec11EmailUtilsTest {
|
||||
"the.registrar@example.com",
|
||||
ImmutableList.of("abuse@test.com", "bcc@test.com"),
|
||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||
String.format(MONTHLY_EMAIL_FORMAT, "<tr><td>a.com</td><td>MALWARE</td></tr>"),
|
||||
String.format(MONTHLY_EMAIL_FORMAT, "<tr><td>a[.]com</td><td>MALWARE</td></tr>"),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedMessages.get(1),
|
||||
@@ -321,7 +342,7 @@ class Spec11EmailUtilsTest {
|
||||
"Super Cool Registry Monthly Threat Detector [2018-07-15]",
|
||||
String.format(
|
||||
MONTHLY_EMAIL_FORMAT,
|
||||
"<tr><td>b.com</td><td>MALWARE</td></tr><tr><td>c.com</td><td>MALWARE</td></tr>"),
|
||||
"<tr><td>b[.]com</td><td>MALWARE</td></tr><tr><td>c[.]com</td><td>MALWARE</td></tr>"),
|
||||
Optional.of(MediaType.HTML_UTF_8));
|
||||
validateMessage(
|
||||
capturedMessages.get(2),
|
||||
|
||||
@@ -20,6 +20,7 @@ import static google.registry.request.Action.Method.GET;
|
||||
import static google.registry.request.Action.Method.POST;
|
||||
import static google.registry.request.auth.Auth.AUTH_API_ADMIN;
|
||||
import static google.registry.request.auth.Auth.AUTH_PUBLIC;
|
||||
import static google.registry.request.auth.AuthResult.NOT_AUTHENTICATED;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -228,7 +229,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("GET");
|
||||
when(req.getRequestURI()).thenReturn("/bumblebee");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -242,7 +243,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("POST");
|
||||
when(req.getRequestURI()).thenReturn("/bumblebee");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -255,7 +256,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("GET");
|
||||
when(req.getRequestURI()).thenReturn("/bumblebee/hive");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -268,7 +269,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("POST");
|
||||
when(req.getRequestURI()).thenReturn("/sloth");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -284,7 +285,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("POST");
|
||||
when(req.getRequestURI()).thenReturn("/sloth/nest");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -296,7 +297,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("GET");
|
||||
when(req.getRequestURI()).thenReturn("/fail");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -311,7 +312,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("GET");
|
||||
when(req.getRequestURI()).thenReturn("/failAtConstruction");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -324,7 +325,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("GET");
|
||||
when(req.getRequestURI()).thenReturn("/bogus");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -336,7 +337,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("POST");
|
||||
when(req.getRequestURI()).thenReturn("/fail");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -348,7 +349,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("FIREAWAY");
|
||||
when(req.getRequestURI()).thenReturn("/fail");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -364,7 +365,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("get");
|
||||
when(req.getRequestURI()).thenReturn("/bumblebee");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -386,7 +387,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("POST");
|
||||
when(req.getRequestURI()).thenReturn("/safe-sloth");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -399,7 +400,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("GET");
|
||||
when(req.getRequestURI()).thenReturn("/safe-sloth");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -412,7 +413,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("GET");
|
||||
when(req.getRequestURI()).thenReturn("/auth/none");
|
||||
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
|
||||
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
|
||||
.thenReturn(Optional.of(NOT_AUTHENTICATED));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -440,8 +441,7 @@ public final class RequestHandlerTest {
|
||||
when(req.getMethod()).thenReturn("GET");
|
||||
when(req.getRequestURI()).thenReturn("/auth/adminUser");
|
||||
when(requestAuthenticator.authorize(AUTH_API_ADMIN.authSettings(), req))
|
||||
.thenReturn(
|
||||
Optional.of(AuthResult.create(AuthLevel.USER, UserAuthInfo.create(testUser, true))));
|
||||
.thenReturn(Optional.of(AuthResult.createUser(UserAuthInfo.create(testUser, true))));
|
||||
|
||||
handler.handleRequest(req, rsp);
|
||||
|
||||
@@ -449,7 +449,6 @@ public final class RequestHandlerTest {
|
||||
assertThat(providedAuthResult.authLevel()).isEqualTo(AuthLevel.USER);
|
||||
assertThat(providedAuthResult.userAuthInfo()).isPresent();
|
||||
assertThat(providedAuthResult.userAuthInfo().get().appEngineUser()).hasValue(testUser);
|
||||
assertThat(providedAuthResult.userAuthInfo().get().oauthTokenInfo()).isEmpty();
|
||||
assertMetric("/auth/adminUser", GET, AuthLevel.USER, true);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-12
@@ -15,6 +15,7 @@
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.request.auth.AuthResult.NOT_AUTHENTICATED;
|
||||
import static google.registry.request.auth.AuthenticatedRegistrarAccessor.Role.ADMIN;
|
||||
import static google.registry.request.auth.AuthenticatedRegistrarAccessor.Role.OWNER;
|
||||
import static google.registry.testing.DatabaseHelper.loadRegistrar;
|
||||
@@ -40,7 +41,6 @@ import google.registry.model.registrar.Registrar;
|
||||
import google.registry.model.registrar.Registrar.State;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException;
|
||||
import google.registry.util.JdkLoggerConfig;
|
||||
import java.util.Optional;
|
||||
@@ -75,7 +75,7 @@ class AuthenticatedRegistrarAccessorTest {
|
||||
|
||||
private static final AuthResult USER = createAuthResult(false);
|
||||
private static final AuthResult GAE_ADMIN = createAuthResult(true);
|
||||
private static final AuthResult NO_USER = AuthResult.create(AuthLevel.NONE);
|
||||
private static final AuthResult NO_USER = NOT_AUTHENTICATED;
|
||||
private static final Optional<String> SUPPORT_GROUP = Optional.of("support@registry.example");
|
||||
/** Registrar ID of a REAL registrar with a RegistrarContact for USER and GAE_ADMIN. */
|
||||
private static final String REGISTRAR_ID_WITH_CONTACT = "TheRegistrar";
|
||||
@@ -94,8 +94,7 @@ class AuthenticatedRegistrarAccessorTest {
|
||||
* @param isAdmin if true, the user is an administrator for the app-engine project.
|
||||
*/
|
||||
private static AuthResult createAuthResult(boolean isAdmin) {
|
||||
return AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
return AuthResult.createUser(
|
||||
UserAuthInfo.create(new User("johndoe@theregistrar.com", "theregistrar.com"), isAdmin));
|
||||
}
|
||||
|
||||
@@ -295,8 +294,7 @@ class AuthenticatedRegistrarAccessorTest {
|
||||
void testGetRegistrarForUser_inContacts_isNotAdmin_caseInsensitive() throws Exception {
|
||||
expectGetRegistrarSuccess(
|
||||
REGISTRAR_ID_WITH_CONTACT,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(new User("JohnDoe@theregistrar.com", "theregistrar.com"), false)),
|
||||
"user JohnDoe@theregistrar.com has [OWNER] access to registrar TheRegistrar");
|
||||
verify(lazyGroupsConnection).get();
|
||||
@@ -417,12 +415,11 @@ class AuthenticatedRegistrarAccessorTest {
|
||||
void testConsoleUser_admin() {
|
||||
google.registry.model.console.User consoleUser =
|
||||
new google.registry.model.console.User.Builder()
|
||||
.setGaiaId("gaiaId")
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder().setIsAdmin(true).setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
AuthResult authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser));
|
||||
AuthResult authResult = AuthResult.createUser(UserAuthInfo.create(consoleUser));
|
||||
AuthenticatedRegistrarAccessor registrarAccessor =
|
||||
new AuthenticatedRegistrarAccessor(
|
||||
authResult, ADMIN_REGISTRAR_ID, SUPPORT_GROUP, lazyGroupsConnection);
|
||||
@@ -444,11 +441,10 @@ class AuthenticatedRegistrarAccessorTest {
|
||||
// not admins
|
||||
google.registry.model.console.User consoleUser =
|
||||
new google.registry.model.console.User.Builder()
|
||||
.setGaiaId("gaiaId")
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build())
|
||||
.build();
|
||||
AuthResult authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser));
|
||||
AuthResult authResult = AuthResult.createUser(UserAuthInfo.create(consoleUser));
|
||||
AuthenticatedRegistrarAccessor registrarAccessor =
|
||||
new AuthenticatedRegistrarAccessor(
|
||||
authResult, ADMIN_REGISTRAR_ID, SUPPORT_GROUP, lazyGroupsConnection);
|
||||
@@ -462,7 +458,6 @@ class AuthenticatedRegistrarAccessorTest {
|
||||
// Registrar employees should have OWNER access to their registrars
|
||||
google.registry.model.console.User consoleUser =
|
||||
new google.registry.model.console.User.Builder()
|
||||
.setGaiaId("gaiaId")
|
||||
.setEmailAddress("email@email.com")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
@@ -474,7 +469,7 @@ class AuthenticatedRegistrarAccessorTest {
|
||||
RegistrarRole.ACCOUNT_MANAGER))
|
||||
.build())
|
||||
.build();
|
||||
AuthResult authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser));
|
||||
AuthResult authResult = AuthResult.createUser(UserAuthInfo.create(consoleUser));
|
||||
AuthenticatedRegistrarAccessor registrarAccessor =
|
||||
new AuthenticatedRegistrarAccessor(
|
||||
authResult, ADMIN_REGISTRAR_ID, SUPPORT_GROUP, lazyGroupsConnection);
|
||||
|
||||
+3
-13
@@ -18,7 +18,6 @@ import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.request.auth.AuthModule.BEARER_PREFIX;
|
||||
import static google.registry.request.auth.AuthModule.IAP_HEADER_NAME;
|
||||
import static google.registry.request.auth.AuthModule.PROXY_HEADER_NAME;
|
||||
import static google.registry.testing.DatabaseHelper.insertInDb;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -61,7 +60,6 @@ public class OidcTokenAuthenticationMechanismTest {
|
||||
private final User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress(email)
|
||||
.setGaiaId(gaiaId)
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder().setIsAdmin(true).setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
@@ -93,9 +91,8 @@ public class OidcTokenAuthenticationMechanismTest {
|
||||
|
||||
@Test
|
||||
void testAuthResultBypass() {
|
||||
OidcTokenAuthenticationMechanism.setAuthResultForTesting(AuthResult.create(AuthLevel.APP));
|
||||
assertThat(authenticationMechanism.authenticate(null))
|
||||
.isEqualTo(AuthResult.create(AuthLevel.APP));
|
||||
OidcTokenAuthenticationMechanism.setAuthResultForTesting(AuthResult.NOT_AUTHENTICATED);
|
||||
assertThat(authenticationMechanism.authenticate(null)).isEqualTo(AuthResult.NOT_AUTHENTICATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -141,7 +138,6 @@ public class OidcTokenAuthenticationMechanismTest {
|
||||
User serviceUser =
|
||||
new User.Builder()
|
||||
.setEmailAddress("service@email.test")
|
||||
.setGaiaId("service-gaia-id")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder().setIsAdmin(true).setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
@@ -171,16 +167,10 @@ public class OidcTokenAuthenticationMechanismTest {
|
||||
void testRegular_tokenExtractor() throws Exception {
|
||||
useRegularOidcMechanism();
|
||||
// The token does not have the "Bearer " prefix.
|
||||
when(request.getHeader(PROXY_HEADER_NAME)).thenReturn(rawToken);
|
||||
when(request.getHeader(AUTHORIZATION)).thenReturn(rawToken);
|
||||
assertThat(authenticationMechanism.tokenExtractor.extract(request)).isNull();
|
||||
|
||||
// The token is in the correct format.
|
||||
when(request.getHeader(PROXY_HEADER_NAME))
|
||||
.thenReturn(String.format("%s%s", BEARER_PREFIX, rawToken));
|
||||
assertThat(authenticationMechanism.tokenExtractor.extract(request)).isEqualTo(rawToken);
|
||||
|
||||
// The token is in the correct format, and under the alternative header.
|
||||
when(request.getHeader(PROXY_HEADER_NAME)).thenReturn(null);
|
||||
when(request.getHeader(AUTHORIZATION))
|
||||
.thenReturn(String.format("%s%s", BEARER_PREFIX, rawToken));
|
||||
assertThat(authenticationMechanism.tokenExtractor.extract(request)).isEqualTo(rawToken);
|
||||
|
||||
@@ -14,361 +14,276 @@
|
||||
|
||||
package google.registry.request.auth;
|
||||
|
||||
import static com.google.common.net.HttpHeaders.AUTHORIZATION;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth8.assertThat;
|
||||
import static google.registry.request.auth.AuthResult.NOT_AUTHENTICATED;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.APP;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.NONE;
|
||||
import static google.registry.request.auth.AuthSettings.AuthLevel.USER;
|
||||
import static google.registry.request.auth.AuthSettings.AuthMethod.API;
|
||||
import static google.registry.request.auth.AuthSettings.AuthMethod.LEGACY;
|
||||
import static google.registry.request.auth.AuthSettings.UserPolicy.ADMIN;
|
||||
import static google.registry.request.auth.AuthSettings.UserPolicy.PUBLIC;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.appengine.api.users.User;
|
||||
import com.google.appengine.api.users.UserService;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthSettings.AuthMethod;
|
||||
import google.registry.request.auth.AuthSettings.UserPolicy;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeOAuthService;
|
||||
import google.registry.testing.FakeUserService;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Unit tests for {@link RequestAuthenticator}. */
|
||||
class RequestAuthenticatorTest {
|
||||
|
||||
@RegisterExtension
|
||||
final JpaIntegrationTestExtension jpa =
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension();
|
||||
private static final AuthResult APP_AUTH = AuthResult.createApp("app@registry.example");
|
||||
|
||||
private static final AuthSettings AUTH_NONE =
|
||||
AuthSettings.create(ImmutableList.of(AuthMethod.API), AuthLevel.NONE, UserPolicy.PUBLIC);
|
||||
private static final AuthResult USER_PUBLIC_AUTH =
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
new User.Builder()
|
||||
.setEmailAddress("user@registry.example")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setIsAdmin(false)
|
||||
.setGlobalRole(GlobalRole.NONE)
|
||||
.build())
|
||||
.build()));
|
||||
|
||||
private static final AuthSettings AUTH_ANY_USER_ANY_METHOD =
|
||||
AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.USER, UserPolicy.PUBLIC);
|
||||
private static final AuthResult USER_ADMIN_AUTH =
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
new User.Builder()
|
||||
.setEmailAddress("admin@registry.example")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setIsAdmin(true)
|
||||
.setGlobalRole(GlobalRole.FTE)
|
||||
.build())
|
||||
.build()));
|
||||
|
||||
private static final AuthSettings AUTH_ANY_USER_NO_LEGACY =
|
||||
AuthSettings.create(ImmutableList.of(AuthMethod.API), AuthLevel.USER, UserPolicy.PUBLIC);
|
||||
|
||||
private static final AuthSettings AUTH_ADMIN_USER_ANY_METHOD =
|
||||
AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.USER, UserPolicy.ADMIN);
|
||||
|
||||
private static final AuthSettings AUTH_NO_METHODS =
|
||||
AuthSettings.create(ImmutableList.of(), AuthLevel.APP, UserPolicy.PUBLIC);
|
||||
|
||||
private static final AuthSettings AUTH_WRONG_METHOD_ORDERING =
|
||||
AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.LEGACY, AuthMethod.API), AuthLevel.APP, UserPolicy.PUBLIC);
|
||||
|
||||
private static final AuthSettings AUTH_DUPLICATE_METHODS =
|
||||
AuthSettings.create(
|
||||
ImmutableList.of(AuthMethod.API, AuthMethod.API), AuthLevel.APP, UserPolicy.PUBLIC);
|
||||
|
||||
private static final AuthSettings AUTH_NONE_REQUIRES_ADMIN =
|
||||
AuthSettings.create(ImmutableList.of(AuthMethod.API), AuthLevel.NONE, UserPolicy.ADMIN);
|
||||
|
||||
private final UserService mockUserService = mock(UserService.class);
|
||||
private final HttpServletRequest req = mock(HttpServletRequest.class);
|
||||
|
||||
private final User testUser = new User("test@google.com", "test@google.com");
|
||||
private final FakeUserService fakeUserService = new FakeUserService();
|
||||
private final XsrfTokenManager xsrfTokenManager =
|
||||
new XsrfTokenManager(new FakeClock(), fakeUserService);
|
||||
private final FakeOAuthService fakeOAuthService =
|
||||
new FakeOAuthService(
|
||||
false /* isOAuthEnabled */,
|
||||
testUser,
|
||||
false /* isUserAdmin */,
|
||||
"test-client-id",
|
||||
ImmutableList.of("test-scope1", "test-scope2", "nontest-scope"));
|
||||
private final AuthenticationMechanism apiAuthenticationMechanism1 =
|
||||
mock(AuthenticationMechanism.class);
|
||||
private final AuthenticationMechanism apiAuthenticationMechanism2 =
|
||||
mock(AuthenticationMechanism.class);
|
||||
private final LegacyAuthenticationMechanism legacyAuthenticationMechanism =
|
||||
mock(LegacyAuthenticationMechanism.class);
|
||||
|
||||
private Optional<AuthResult> authorize(AuthLevel authLevel, UserPolicy userPolicy) {
|
||||
return new RequestAuthenticator(
|
||||
ImmutableList.of(apiAuthenticationMechanism1, apiAuthenticationMechanism2),
|
||||
legacyAuthenticationMechanism)
|
||||
.authorize(AuthSettings.create(ImmutableList.of(API, LEGACY), authLevel, userPolicy), req);
|
||||
}
|
||||
|
||||
private AuthResult authenticate(AuthMethod... methods) {
|
||||
return new RequestAuthenticator(
|
||||
ImmutableList.of(apiAuthenticationMechanism1, apiAuthenticationMechanism2),
|
||||
legacyAuthenticationMechanism)
|
||||
.authenticate(AuthSettings.create(ImmutableList.copyOf(methods), NONE, PUBLIC), req);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
when(req.getMethod()).thenReturn("POST");
|
||||
}
|
||||
|
||||
private RequestAuthenticator createRequestAuthenticator(UserService userService) {
|
||||
return new RequestAuthenticator(
|
||||
ImmutableList.of(
|
||||
new OAuthAuthenticationMechanism(
|
||||
fakeOAuthService,
|
||||
ImmutableSet.of("test-scope1", "test-scope2", "test-scope3"),
|
||||
ImmutableSet.of("test-scope1", "test-scope2"),
|
||||
ImmutableSet.of("test-client-id", "other-test-client-id"))),
|
||||
new LegacyAuthenticationMechanism(userService, xsrfTokenManager));
|
||||
}
|
||||
|
||||
private Optional<AuthResult> runTest(UserService userService, AuthSettings auth) {
|
||||
return createRequestAuthenticator(userService).authorize(auth, req);
|
||||
when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(NOT_AUTHENTICATED);
|
||||
when(apiAuthenticationMechanism2.authenticate(req)).thenReturn(NOT_AUTHENTICATED);
|
||||
when(legacyAuthenticationMechanism.authenticate(req)).thenReturn(NOT_AUTHENTICATED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNoAuthNeeded_noneFound() {
|
||||
Optional<AuthResult> authResult = runTest(mockUserService, AUTH_NONE);
|
||||
|
||||
verifyNoInteractions(mockUserService);
|
||||
assertThat(authResult).isPresent();
|
||||
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.NONE);
|
||||
void testAuthorize_noneRequired() {
|
||||
for (AuthResult resultFound :
|
||||
ImmutableList.of(NOT_AUTHENTICATED, APP_AUTH, USER_ADMIN_AUTH, USER_PUBLIC_AUTH)) {
|
||||
when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound);
|
||||
assertThat(authorize(NONE, PUBLIC)).hasValue(resultFound);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnyUserAnyMethod_notLoggedIn() {
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD);
|
||||
void testAuthorize_appPublicRequired() {
|
||||
authorize(APP, PUBLIC);
|
||||
assertThat(authorize(APP, PUBLIC)).isEmpty();
|
||||
|
||||
assertThat(authResult).isEmpty();
|
||||
for (AuthResult resultFound : ImmutableList.of(APP_AUTH, USER_ADMIN_AUTH, USER_PUBLIC_AUTH)) {
|
||||
when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound);
|
||||
assertThat(authorize(APP, PUBLIC)).hasValue(resultFound);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnyUserAnyMethod_xsrfFailure() {
|
||||
fakeUserService.setUser(testUser, false);
|
||||
void testAuthorize_appAdminRequired() {
|
||||
for (AuthResult resultFound : ImmutableList.of(NOT_AUTHENTICATED, USER_PUBLIC_AUTH)) {
|
||||
when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound);
|
||||
assertThat(authorize(APP, ADMIN)).isEmpty();
|
||||
}
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD);
|
||||
|
||||
assertThat(authResult).isEmpty();
|
||||
for (AuthResult resultFound : ImmutableList.of(APP_AUTH, USER_ADMIN_AUTH)) {
|
||||
when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound);
|
||||
assertThat(authorize(APP, ADMIN)).hasValue(resultFound);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnyUserAnyMethod_success() {
|
||||
fakeUserService.setUser(testUser, false /* isAdmin */);
|
||||
when(req.getHeader(XsrfTokenManager.X_CSRF_TOKEN))
|
||||
.thenReturn(xsrfTokenManager.generateToken(testUser.getEmail()));
|
||||
void testAuthorize_userPublicRequired() {
|
||||
for (AuthResult resultFound : ImmutableList.of(NOT_AUTHENTICATED, APP_AUTH)) {
|
||||
when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound);
|
||||
assertThat(authorize(USER, PUBLIC)).isEmpty();
|
||||
}
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD);
|
||||
|
||||
assertThat(authResult).isPresent();
|
||||
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
|
||||
assertThat(authResult.get().userAuthInfo()).isPresent();
|
||||
assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser);
|
||||
assertThat(authResult.get().userAuthInfo().get().isUserAdmin()).isFalse();
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isEmpty();
|
||||
for (AuthResult resultFound : ImmutableList.of(USER_PUBLIC_AUTH, USER_ADMIN_AUTH)) {
|
||||
when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound);
|
||||
assertThat(authorize(USER, PUBLIC)).hasValue(resultFound);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnyUserAnyMethod_xsrfNotRequiredForGet() {
|
||||
fakeUserService.setUser(testUser, false);
|
||||
when(req.getMethod()).thenReturn("GET");
|
||||
void testAuthorize_userAdminRequired() {
|
||||
for (AuthResult resultFound : ImmutableList.of(NOT_AUTHENTICATED, APP_AUTH, USER_PUBLIC_AUTH)) {
|
||||
when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound);
|
||||
assertThat(authorize(USER, ADMIN)).isEmpty();
|
||||
}
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD);
|
||||
|
||||
assertThat(authResult).isPresent();
|
||||
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
|
||||
assertThat(authResult.get().userAuthInfo()).isPresent();
|
||||
assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser);
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isEmpty();
|
||||
when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(USER_ADMIN_AUTH);
|
||||
assertThat(authorize(USER, ADMIN)).hasValue(USER_ADMIN_AUTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAdminUserAnyMethod_notLoggedIn() {
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD);
|
||||
|
||||
assertThat(authResult).isEmpty();
|
||||
void testAuthenticate_apiFirst() {
|
||||
when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(APP_AUTH);
|
||||
assertThat(authenticate(API, LEGACY)).isEqualTo(APP_AUTH);
|
||||
verify(apiAuthenticationMechanism1).authenticate(req);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism1);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism2);
|
||||
verifyNoMoreInteractions(legacyAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAdminUserAnyMethod_notAdminUser() {
|
||||
fakeUserService.setUser(testUser, false /* isAdmin */);
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD);
|
||||
|
||||
assertThat(authResult).isEmpty();
|
||||
void testAuthenticate_apiSecond() {
|
||||
when(apiAuthenticationMechanism2.authenticate(req)).thenReturn(APP_AUTH);
|
||||
assertThat(authenticate(API, LEGACY)).isEqualTo(APP_AUTH);
|
||||
verify(apiAuthenticationMechanism1).authenticate(req);
|
||||
verify(apiAuthenticationMechanism2).authenticate(req);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism1);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism2);
|
||||
verifyNoMoreInteractions(legacyAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAdminUserAnyMethod_xsrfFailure() {
|
||||
fakeUserService.setUser(testUser, true);
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD);
|
||||
|
||||
assertThat(authResult).isEmpty();
|
||||
void testAuthenticate_legacy() {
|
||||
when(legacyAuthenticationMechanism.authenticate(req)).thenReturn(APP_AUTH);
|
||||
assertThat(authenticate(API, LEGACY)).isEqualTo(APP_AUTH);
|
||||
verify(apiAuthenticationMechanism1).authenticate(req);
|
||||
verify(apiAuthenticationMechanism2).authenticate(req);
|
||||
verify(legacyAuthenticationMechanism).authenticate(req);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism1);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism2);
|
||||
verifyNoMoreInteractions(legacyAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAdminUserAnyMethod_success() {
|
||||
fakeUserService.setUser(testUser, true /* isAdmin */);
|
||||
when(req.getHeader(XsrfTokenManager.X_CSRF_TOKEN))
|
||||
.thenReturn(xsrfTokenManager.generateToken(testUser.getEmail()));
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD);
|
||||
|
||||
assertThat(authResult).isPresent();
|
||||
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
|
||||
assertThat(authResult.get().userAuthInfo()).isPresent();
|
||||
assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser);
|
||||
assertThat(authResult.get().userAuthInfo().get().isUserAdmin()).isTrue();
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isEmpty();
|
||||
void testAuthenticate_returnFirstResult() {
|
||||
// API auth 2 returns an authenticted auth result, so we don't bother trying the next auth
|
||||
// (legacy auth).
|
||||
when(apiAuthenticationMechanism2.authenticate(req)).thenReturn(APP_AUTH);
|
||||
when(legacyAuthenticationMechanism.authenticate(req)).thenReturn(USER_PUBLIC_AUTH);
|
||||
assertThat(authenticate(API, LEGACY)).isEqualTo(APP_AUTH);
|
||||
verify(apiAuthenticationMechanism1).authenticate(req);
|
||||
verify(apiAuthenticationMechanism2).authenticate(req);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism1);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism2);
|
||||
verifyNoMoreInteractions(legacyAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOAuth_success() {
|
||||
fakeOAuthService.setUser(testUser);
|
||||
fakeOAuthService.setOAuthEnabled(true);
|
||||
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
|
||||
|
||||
assertThat(authResult).isPresent();
|
||||
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
|
||||
assertThat(authResult.get().userAuthInfo()).isPresent();
|
||||
assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser);
|
||||
assertThat(authResult.get().userAuthInfo().get().isUserAdmin()).isFalse();
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isPresent();
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().authorizedScopes())
|
||||
.containsAtLeast("test-scope1", "test-scope2");
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().oauthClientId())
|
||||
.isEqualTo("test-client-id");
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().rawAccessToken())
|
||||
.isEqualTo("TOKEN");
|
||||
void testAuthenticate_notAuthenticated() {
|
||||
assertThat(authenticate(API, LEGACY)).isEqualTo(NOT_AUTHENTICATED);
|
||||
verify(apiAuthenticationMechanism1).authenticate(req);
|
||||
verify(apiAuthenticationMechanism2).authenticate(req);
|
||||
verify(legacyAuthenticationMechanism).authenticate(req);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism1);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism2);
|
||||
verifyNoMoreInteractions(legacyAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOAuthAdmin_success() {
|
||||
fakeOAuthService.setUser(testUser);
|
||||
fakeOAuthService.setUserAdmin(true);
|
||||
fakeOAuthService.setOAuthEnabled(true);
|
||||
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
|
||||
|
||||
assertThat(authResult).isPresent();
|
||||
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
|
||||
assertThat(authResult.get().userAuthInfo()).isPresent();
|
||||
assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser);
|
||||
assertThat(authResult.get().userAuthInfo().get().isUserAdmin()).isTrue();
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isPresent();
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().authorizedScopes())
|
||||
.containsAtLeast("test-scope1", "test-scope2");
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().oauthClientId())
|
||||
.isEqualTo("test-client-id");
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().rawAccessToken())
|
||||
.isEqualTo("TOKEN");
|
||||
void testAuthenticate_apiOnly() {
|
||||
when(legacyAuthenticationMechanism.authenticate(req)).thenReturn(USER_PUBLIC_AUTH);
|
||||
assertThat(authenticate(API)).isEqualTo(NOT_AUTHENTICATED);
|
||||
verify(apiAuthenticationMechanism1).authenticate(req);
|
||||
verify(apiAuthenticationMechanism2).authenticate(req);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism1);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism2);
|
||||
verifyNoMoreInteractions(legacyAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOAuthMissingAuthenticationToken_failure() {
|
||||
fakeOAuthService.setUser(testUser);
|
||||
fakeOAuthService.setOAuthEnabled(true);
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
|
||||
|
||||
assertThat(authResult).isEmpty();
|
||||
void testAuthenticate_legacyOnly() {
|
||||
when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(USER_PUBLIC_AUTH);
|
||||
assertThat(authenticate(LEGACY)).isEqualTo(NOT_AUTHENTICATED);
|
||||
verify(legacyAuthenticationMechanism).authenticate(req);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism1);
|
||||
verifyNoMoreInteractions(apiAuthenticationMechanism2);
|
||||
verifyNoMoreInteractions(legacyAuthenticationMechanism);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOAuthClientIdMismatch_failure() {
|
||||
fakeOAuthService.setUser(testUser);
|
||||
fakeOAuthService.setOAuthEnabled(true);
|
||||
fakeOAuthService.setClientId("wrong-client-id");
|
||||
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
|
||||
|
||||
assertThat(authResult).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOAuthNoScopes_failure() {
|
||||
fakeOAuthService.setUser(testUser);
|
||||
fakeOAuthService.setOAuthEnabled(true);
|
||||
fakeOAuthService.setAuthorizedScopes();
|
||||
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
|
||||
|
||||
assertThat(authResult).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOAuthMissingScope_failure() {
|
||||
fakeOAuthService.setUser(testUser);
|
||||
fakeOAuthService.setOAuthEnabled(true);
|
||||
fakeOAuthService.setAuthorizedScopes("test-scope1", "test-scope3");
|
||||
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
|
||||
|
||||
assertThat(authResult).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testOAuthExtraScope_success() {
|
||||
fakeOAuthService.setUser(testUser);
|
||||
fakeOAuthService.setOAuthEnabled(true);
|
||||
fakeOAuthService.setAuthorizedScopes("test-scope1", "test-scope2", "test-scope3");
|
||||
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
|
||||
|
||||
assertThat(authResult).isPresent();
|
||||
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
|
||||
assertThat(authResult.get().userAuthInfo()).isPresent();
|
||||
assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser);
|
||||
assertThat(authResult.get().userAuthInfo().get().isUserAdmin()).isFalse();
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isPresent();
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().authorizedScopes())
|
||||
.containsAtLeast("test-scope1", "test-scope2", "test-scope3");
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().oauthClientId())
|
||||
.isEqualTo("test-client-id");
|
||||
assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().rawAccessToken())
|
||||
.isEqualTo("TOKEN");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAnyUserNoLegacy_failureWithLegacyUser() {
|
||||
fakeUserService.setUser(testUser, false /* isAdmin */);
|
||||
|
||||
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
|
||||
|
||||
assertThat(authResult).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheckAuthConfig_noMethods_failure() {
|
||||
void testFailure_checkAuthConfig_noMethods() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> RequestAuthenticator.checkAuthConfig(AUTH_NO_METHODS));
|
||||
() ->
|
||||
RequestAuthenticator.checkAuthConfig(
|
||||
AuthSettings.create(ImmutableList.of(), NONE, PUBLIC)));
|
||||
assertThat(thrown).hasMessageThat().contains("Must specify at least one auth method");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheckAuthConfig_wrongMethodOrdering_failure() {
|
||||
void testFailure_checkAuthConfig_wrongMethodOrder() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> RequestAuthenticator.checkAuthConfig(AUTH_WRONG_METHOD_ORDERING));
|
||||
() ->
|
||||
RequestAuthenticator.checkAuthConfig(
|
||||
AuthSettings.create(ImmutableList.of(LEGACY, API), NONE, PUBLIC)));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Auth methods must be unique and strictly in order - API, LEGACY");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheckAuthConfig_noneAuthLevelRequiresAdmin_failure() {
|
||||
void testFailure_CheckAuthConfig_duplicateMethods() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> RequestAuthenticator.checkAuthConfig(AUTH_NONE_REQUIRES_ADMIN));
|
||||
() ->
|
||||
RequestAuthenticator.checkAuthConfig(
|
||||
AuthSettings.create(ImmutableList.of(API, API), NONE, PUBLIC)));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Auth methods must be unique and strictly in order - API, LEGACY");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_checkAuthConfig_noneAuthLevelRequiresAdmin() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() ->
|
||||
RequestAuthenticator.checkAuthConfig(
|
||||
AuthSettings.create(ImmutableList.of(API, LEGACY), NONE, ADMIN)));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Actions with minimal auth level at NONE should not specify ADMIN user policy");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCheckAuthConfig_DuplicateMethods_failure() {
|
||||
IllegalArgumentException thrown =
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> RequestAuthenticator.checkAuthConfig(AUTH_DUPLICATE_METHODS));
|
||||
assertThat(thrown)
|
||||
.hasMessageThat()
|
||||
.contains("Auth methods must be unique and strictly in order - API, LEGACY");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ import org.junit.runner.RunWith;
|
||||
* and have at least one test method that persists a JPA entity declared in persistence.xml.
|
||||
*
|
||||
* <p>Note that with {@link JpaIntegrationWithCoverageExtension}, each method starts with an empty
|
||||
* database. Therefore this is not the right place for verifying backwards data compatibility in
|
||||
* database. Therefore, this is not the right place for verifying backwards data compatibility in
|
||||
* end-to-end functional tests.
|
||||
*
|
||||
* <p>As of April 2020, none of the before/after annotations ({@code BeforeClass} and {@code
|
||||
@@ -107,7 +107,9 @@ import org.junit.runner.RunWith;
|
||||
// AfterSuiteTest must be the last entry. See class javadoc for details.
|
||||
AfterSuiteTest.class
|
||||
})
|
||||
public class SqlIntegrationTestSuite {
|
||||
public final class SqlIntegrationTestSuite {
|
||||
|
||||
private SqlIntegrationTestSuite() {}
|
||||
|
||||
@BeforeAll // Not yet supported in JUnit 5. Called through BeforeSuiteTest.
|
||||
public static void initJpaEntityCoverage() {
|
||||
|
||||
@@ -16,13 +16,15 @@ package google.registry.security;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.util.DateTimeUtils.START_OF_TIME;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.appengine.api.users.User;
|
||||
import com.google.appengine.api.users.UserService;
|
||||
import com.google.common.base.Splitter;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.testing.FakeClock;
|
||||
import google.registry.testing.FakeUserService;
|
||||
import org.joda.time.Duration;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -37,14 +39,16 @@ class XsrfTokenManagerTest {
|
||||
|
||||
private final User testUser = new User("test@example.com", "test@example.com");
|
||||
private final FakeClock clock = new FakeClock(START_OF_TIME);
|
||||
private final FakeUserService userService = new FakeUserService();
|
||||
private final UserService userService = mock(UserService.class);
|
||||
private final XsrfTokenManager xsrfTokenManager = new XsrfTokenManager(clock, userService);
|
||||
|
||||
private String token;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
userService.setUser(testUser, false);
|
||||
when(userService.isUserLoggedIn()).thenReturn(true);
|
||||
when(userService.getCurrentUser()).thenReturn(testUser);
|
||||
when(userService.isUserAdmin()).thenReturn(false);
|
||||
token = xsrfTokenManager.generateToken(testUser.getEmail());
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ public final class RegistryTestServer {
|
||||
|
||||
private final TestServer server;
|
||||
|
||||
/** @see TestServer#TestServer(HostAndPort, ImmutableMap, ImmutableList, ImmutableList) */
|
||||
/** @see TestServer#TestServer(HostAndPort, ImmutableMap, ImmutableList) */
|
||||
public RegistryTestServer(HostAndPort address) {
|
||||
server = new TestServer(address, RUNFILES, ROUTES);
|
||||
}
|
||||
@@ -104,7 +104,7 @@ public final class RegistryTestServer {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
/** @see TestServer#getUrl(java.lang.String) */
|
||||
/** @see TestServer#getUrl(String) */
|
||||
public URL getUrl(String path) {
|
||||
return server.getUrl(path);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTransactionManagerExtension;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.OidcTokenAuthenticationMechanism;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.UserInfo;
|
||||
@@ -144,12 +143,11 @@ public final class RegistryTestServerMain {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress(loginEmail)
|
||||
.setGaiaId("123457890")
|
||||
.setUserRoles(userRoles)
|
||||
.setRegistryLockPassword("registryLockPassword")
|
||||
.build();
|
||||
OidcTokenAuthenticationMechanism.setAuthResultForTesting(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user)));
|
||||
AuthResult.createUser(UserAuthInfo.create(user)));
|
||||
new JpaTestExtensions.Builder().buildIntegrationTestExtension().beforeEach(null);
|
||||
JpaTransactionManagerExtension.loadInitialData();
|
||||
System.out.printf("%sLoading fixtures...%s\n", BLUE, RESET);
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
// Copyright 2017 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.testing;
|
||||
|
||||
import com.google.appengine.api.oauth.OAuthRequestException;
|
||||
import com.google.appengine.api.oauth.OAuthService;
|
||||
import com.google.appengine.api.users.User;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
|
||||
/** A fake {@link OAuthService} implementation for testing. */
|
||||
public class FakeOAuthService implements OAuthService {
|
||||
|
||||
private boolean isOAuthEnabled;
|
||||
private User currentUser;
|
||||
private boolean isUserAdmin;
|
||||
private String clientId;
|
||||
private ImmutableList<String> authorizedScopes;
|
||||
|
||||
public FakeOAuthService(
|
||||
boolean isOAuthEnabled,
|
||||
User currentUser,
|
||||
boolean isUserAdmin,
|
||||
String clientId,
|
||||
List<String> authorizedScopes) {
|
||||
this.isOAuthEnabled = isOAuthEnabled;
|
||||
this.currentUser = currentUser;
|
||||
this.isUserAdmin = isUserAdmin;
|
||||
this.clientId = clientId;
|
||||
this.authorizedScopes = ImmutableList.copyOf(authorizedScopes);
|
||||
}
|
||||
|
||||
public void setOAuthEnabled(boolean isOAuthEnabled) {
|
||||
this.isOAuthEnabled = isOAuthEnabled;
|
||||
}
|
||||
|
||||
public void setUser(User currentUser) {
|
||||
this.currentUser = currentUser;
|
||||
}
|
||||
|
||||
public void setUserAdmin(boolean isUserAdmin) {
|
||||
this.isUserAdmin = isUserAdmin;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public void setAuthorizedScopes(String... scopes) {
|
||||
this.authorizedScopes = ImmutableList.copyOf(scopes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getCurrentUser() throws OAuthRequestException {
|
||||
if (!isOAuthEnabled) {
|
||||
throw new OAuthRequestException("invalid OAuth request");
|
||||
}
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getCurrentUser(String scope) throws OAuthRequestException {
|
||||
return getCurrentUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getCurrentUser(String... scopes) throws OAuthRequestException {
|
||||
return getCurrentUser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserAdmin() throws OAuthRequestException {
|
||||
if (!isOAuthEnabled) {
|
||||
throw new OAuthRequestException("invalid OAuth request");
|
||||
}
|
||||
return isUserAdmin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserAdmin(String scope) throws OAuthRequestException {
|
||||
return isUserAdmin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserAdmin(String... scopes) throws OAuthRequestException {
|
||||
return isUserAdmin();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId(String scope) throws OAuthRequestException {
|
||||
if (!isOAuthEnabled) {
|
||||
throw new OAuthRequestException("invalid OAuth request");
|
||||
}
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId(String... scopes) throws OAuthRequestException {
|
||||
if (!isOAuthEnabled) {
|
||||
throw new OAuthRequestException("invalid OAuth request");
|
||||
}
|
||||
return clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getAuthorizedScopes(String... scopes) throws OAuthRequestException {
|
||||
if (!isOAuthEnabled) {
|
||||
throw new OAuthRequestException("invalid OAuth request");
|
||||
}
|
||||
return authorizedScopes.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public String getOAuthConsumerKey() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
// Copyright 2017 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.testing;
|
||||
|
||||
import com.google.appengine.api.users.User;
|
||||
import com.google.appengine.api.users.UserService;
|
||||
import google.registry.model.annotations.DeleteAfterMigration;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Fake implementation of {@link UserService} for testing. */
|
||||
@DeleteAfterMigration
|
||||
public class FakeUserService implements UserService {
|
||||
|
||||
@Nullable private User user = null;
|
||||
private boolean isAdmin = false;
|
||||
|
||||
public void setUser(@Nullable User user, boolean isAdmin) {
|
||||
this.user = user;
|
||||
this.isAdmin = isAdmin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createLoginURL(String destinationURL) {
|
||||
return String.format("/login?dest=%s", destinationURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createLoginURL(String destinationURL, String authDomain) {
|
||||
return createLoginURL(destinationURL);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public String createLoginURL(String destinationURL, String authDomain, String federatedIdentity,
|
||||
Set<String> attributesRequest) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createLogoutURL(String destinationURL) {
|
||||
return String.format("/logout?dest=%s", destinationURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String createLogoutURL(String destinationURL, String authDomain) {
|
||||
return createLogoutURL(destinationURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserLoggedIn() {
|
||||
return user != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserAdmin() {
|
||||
return isAdmin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getCurrentUser() {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
import com.google.api.client.http.HttpRequest;
|
||||
import com.google.api.client.http.javanet.NetHttpTransport;
|
||||
import com.google.api.client.json.jackson2.JacksonFactory;
|
||||
import com.google.api.client.json.gson.GsonFactory;
|
||||
import com.google.api.client.util.store.AbstractDataStoreFactory;
|
||||
import com.google.api.client.util.store.DataStore;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@@ -72,9 +72,9 @@ class AuthModuleTest {
|
||||
}
|
||||
})
|
||||
// We need to set the following fields because they are checked when
|
||||
// Credential#setRefreshToken is called. However they are not actually persisted in the
|
||||
// Credential#setRefreshToken is called. However, they are not actually persisted in the
|
||||
// DataStore and not actually used in tests.
|
||||
.setJsonFactory(new JacksonFactory())
|
||||
.setJsonFactory(new GsonFactory())
|
||||
.setTransport(new NetHttpTransport())
|
||||
.setTokenServerUrl(new GenericUrl("https://accounts.google.com/o/oauth2/token"))
|
||||
.setClientAuthentication(new ClientParametersAuthentication(CLIENT_ID, CLIENT_SECRET))
|
||||
@@ -104,7 +104,7 @@ class AuthModuleTest {
|
||||
AuthModule.provideClientScopeQualifier("client-id", ImmutableList.of("foo", "bar"));
|
||||
|
||||
// If we change the way we encode client id and scopes, this assertion will break. That's
|
||||
// probably ok and you can just change the text. The things you have to be aware of are:
|
||||
// probably ok, and you can just change the text. The things you have to be aware of are:
|
||||
// - Names in the new encoding should have a low risk of collision with the old encoding.
|
||||
// - Changing the encoding will force all OAuth users of the nomulus tool to do a new login
|
||||
// (existing credentials will not be used).
|
||||
@@ -146,7 +146,7 @@ class AuthModuleTest {
|
||||
private Credential getCredential() {
|
||||
// Reconstruct the entire dependency graph, injecting FakeDataStoreFactory and credential
|
||||
// parameters.
|
||||
JacksonFactory jsonFactory = new JacksonFactory();
|
||||
GsonFactory jsonFactory = new GsonFactory();
|
||||
GoogleClientSecrets clientSecrets = getSecrets();
|
||||
ImmutableList<String> scopes = ImmutableList.of("scope1");
|
||||
return AuthModule.provideCredential(
|
||||
@@ -155,7 +155,7 @@ class AuthModuleTest {
|
||||
AuthModule.provideClientScopeQualifier(AuthModule.provideClientId(clientSecrets), scopes));
|
||||
}
|
||||
|
||||
private GoogleClientSecrets getSecrets() {
|
||||
private static GoogleClientSecrets getSecrets() {
|
||||
return new GoogleClientSecrets()
|
||||
.setInstalled(
|
||||
AuthModule.provideDefaultInstalledDetails()
|
||||
@@ -166,7 +166,8 @@ class AuthModuleTest {
|
||||
@Test
|
||||
void test_provideLocalCredentialJson() {
|
||||
String credentialJson =
|
||||
AuthModule.provideLocalCredentialJson(this::getSecrets, this::getCredential, null);
|
||||
AuthModule.provideLocalCredentialJson(
|
||||
AuthModuleTest::getSecrets, this::getCredential, null);
|
||||
Map<String, String> jsonMap =
|
||||
new Gson().fromJson(credentialJson, new TypeToken<Map<String, String>>() {}.getType());
|
||||
assertThat(jsonMap.get("type")).isEqualTo("authorized_user");
|
||||
@@ -182,7 +183,7 @@ class AuthModuleTest {
|
||||
Files.write(credentialFile.toPath(), "{some_field: some_value}".getBytes(UTF_8));
|
||||
String credentialJson =
|
||||
AuthModule.provideLocalCredentialJson(
|
||||
this::getSecrets, this::getCredential, credentialFile.getCanonicalPath());
|
||||
AuthModuleTest::getSecrets, this::getCredential, credentialFile.getCanonicalPath());
|
||||
assertThat(credentialJson).isEqualTo("{some_field: some_value}");
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class GetTldCommandTest extends CommandTestCase<GetTldCommand> {
|
||||
PremiumList premiumList = persistPremiumList("test", USD, "silver,USD 50", "gold,USD 80");
|
||||
persistResource(
|
||||
tld.asBuilder()
|
||||
.setDnsAPlusAaaaTtl(Duration.millis(900))
|
||||
.setDnsAPlusAaaaTtl(Duration.standardMinutes(15))
|
||||
.setDriveFolderId("driveFolder")
|
||||
.setCreateBillingCost(Money.of(USD, 25))
|
||||
.setPremiumList(premiumList)
|
||||
|
||||
@@ -18,9 +18,6 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static google.registry.tools.RequestFactoryModule.REQUEST_TIMEOUT_MS;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.google.api.client.http.GenericUrl;
|
||||
@@ -35,6 +32,7 @@ import com.google.auth.oauth2.UserCredentials;
|
||||
import google.registry.config.RegistryConfig;
|
||||
import google.registry.testing.SystemPropertyExtension;
|
||||
import google.registry.util.GoogleCredentialsBundle;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -50,7 +48,6 @@ public class RequestFactoryModuleTest {
|
||||
final SystemPropertyExtension systemPropertyExtension = new SystemPropertyExtension();
|
||||
|
||||
@Mock public GoogleCredentialsBundle credentialsBundle;
|
||||
@Mock public HttpRequestInitializer httpRequestInitializer;
|
||||
|
||||
@BeforeEach
|
||||
void beforeEach() {
|
||||
@@ -64,12 +61,11 @@ public class RequestFactoryModuleTest {
|
||||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = true;
|
||||
try {
|
||||
HttpRequestFactory factory =
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "client-id", false);
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "client-id");
|
||||
HttpRequestInitializer initializer = factory.getInitializer();
|
||||
assertThat(initializer).isNotNull();
|
||||
HttpRequest request = factory.buildGetRequest(new GenericUrl("http://localhost"));
|
||||
initializer.initialize(request);
|
||||
verifyNoInteractions(httpRequestInitializer);
|
||||
} finally {
|
||||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = origIsLocal;
|
||||
}
|
||||
@@ -77,7 +73,6 @@ public class RequestFactoryModuleTest {
|
||||
|
||||
@Test
|
||||
void test_provideHttpRequestFactory_remote() throws Exception {
|
||||
when(credentialsBundle.getHttpRequestInitializer()).thenReturn(httpRequestInitializer);
|
||||
// Mock the request/response to/from the OIDC server requesting an ID token
|
||||
UserCredentials mockUserCredentials = mock(UserCredentials.class);
|
||||
when(credentialsBundle.getGoogleCredentials()).thenReturn(mockUserCredentials);
|
||||
@@ -97,23 +92,15 @@ public class RequestFactoryModuleTest {
|
||||
boolean origIsLocal = RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal;
|
||||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = false;
|
||||
try {
|
||||
// With OAuth header.
|
||||
HttpRequestFactory factory =
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "clientId", true);
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "clientId");
|
||||
HttpRequest request = factory.buildGetRequest(new GenericUrl("http://localhost"));
|
||||
assertThat(request.getHeaders().get("Proxy-Authorization")).isEqualTo("Bearer oidc.token");
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> authHeaders = (List<String>) request.getHeaders().get("Authorization");
|
||||
assertThat(authHeaders.size()).isEqualTo(1);
|
||||
assertThat(authHeaders.get(0)).isEqualTo("Bearer oidc.token");
|
||||
assertThat(request.getConnectTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
assertThat(request.getReadTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
verify(httpRequestInitializer).initialize(request);
|
||||
verifyNoMoreInteractions(httpRequestInitializer);
|
||||
// No OAuth header.
|
||||
factory =
|
||||
RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "clientId", false);
|
||||
request = factory.buildGetRequest(new GenericUrl("http://localhost"));
|
||||
assertThat(request.getHeaders().get("Proxy-Authorization")).isEqualTo("Bearer oidc.token");
|
||||
assertThat(request.getConnectTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
assertThat(request.getReadTimeout()).isEqualTo(REQUEST_TIMEOUT_MS);
|
||||
verifyNoMoreInteractions(httpRequestInitializer);
|
||||
} finally {
|
||||
RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = origIsLocal;
|
||||
}
|
||||
|
||||
+6
-11
@@ -27,7 +27,6 @@ import google.registry.model.console.UserRoles;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.DatabaseHelper;
|
||||
import google.registry.testing.FakeResponse;
|
||||
@@ -55,8 +54,7 @@ public class ConsoleDomainGetActionTest {
|
||||
void testSuccess_fullJsonRepresentation() {
|
||||
ConsoleDomainGetAction action =
|
||||
createAction(
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(
|
||||
new UserRoles.Builder()
|
||||
@@ -85,7 +83,8 @@ public class ConsoleDomainGetActionTest {
|
||||
|
||||
@Test
|
||||
void testFailure_appAuth() {
|
||||
ConsoleDomainGetAction action = createAction(AuthResult.create(AuthLevel.APP), "exists.tld");
|
||||
ConsoleDomainGetAction action =
|
||||
createAction(AuthResult.createApp("service@registry.example"), "exists.tld");
|
||||
action.run();
|
||||
assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED);
|
||||
}
|
||||
@@ -94,8 +93,7 @@ public class ConsoleDomainGetActionTest {
|
||||
void testFailure_wrongTypeOfUser() {
|
||||
ConsoleDomainGetAction action =
|
||||
createAction(
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(mock(com.google.appengine.api.users.User.class), false)),
|
||||
"exists.tld");
|
||||
action.run();
|
||||
@@ -106,8 +104,7 @@ public class ConsoleDomainGetActionTest {
|
||||
void testFailure_noAccessToRegistrar() {
|
||||
ConsoleDomainGetAction action =
|
||||
createAction(
|
||||
AuthResult.create(
|
||||
AuthLevel.USER, UserAuthInfo.create(createUser(new UserRoles.Builder().build()))),
|
||||
AuthResult.createUser(UserAuthInfo.create(createUser(new UserRoles.Builder().build()))),
|
||||
"exists.tld");
|
||||
action.run();
|
||||
assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_NOT_FOUND);
|
||||
@@ -117,8 +114,7 @@ public class ConsoleDomainGetActionTest {
|
||||
void testFailure_nonexistentDomain() {
|
||||
ConsoleDomainGetAction action =
|
||||
createAction(
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(createUser(new UserRoles.Builder().setIsAdmin(true).build()))),
|
||||
"nonexistent.tld");
|
||||
action.run();
|
||||
@@ -128,7 +124,6 @@ public class ConsoleDomainGetActionTest {
|
||||
private User createUser(UserRoles userRoles) {
|
||||
return new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(userRoles)
|
||||
.build();
|
||||
}
|
||||
|
||||
+17
-13
@@ -15,31 +15,25 @@
|
||||
package google.registry.ui.server.console;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.api.client.http.HttpStatusCodes;
|
||||
import com.google.gson.Gson;
|
||||
import google.registry.model.console.GlobalRole;
|
||||
import google.registry.model.console.User;
|
||||
import google.registry.model.console.UserRoles;
|
||||
import google.registry.model.registrar.RegistrarPoc;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
/** Tests for {@link google.registry.ui.server.console.ConsoleUserDataAction}. */
|
||||
class ConsoleUserDataActionTest {
|
||||
|
||||
private final HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
private RegistrarPoc testRegistrarPoc;
|
||||
private static final Gson GSON = RequestModule.provideGson();
|
||||
private FakeResponse response = new FakeResponse();
|
||||
|
||||
@@ -52,25 +46,34 @@ class ConsoleUserDataActionTest {
|
||||
User user =
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())
|
||||
.build();
|
||||
|
||||
ConsoleUserDataAction action =
|
||||
createAction(AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user)));
|
||||
ConsoleUserDataAction action = createAction(AuthResult.createUser(UserAuthInfo.create(user)));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
Map jsonObject = GSON.fromJson(response.getPayload(), Map.class);
|
||||
assertThat(jsonObject)
|
||||
.containsExactly("isAdmin", false, "technicalDocsUrl", "test", "globalRole", "FTE");
|
||||
.containsExactly(
|
||||
"isAdmin",
|
||||
false,
|
||||
"technicalDocsUrl",
|
||||
"test",
|
||||
"globalRole",
|
||||
"FTE",
|
||||
"productName",
|
||||
"Nomulus",
|
||||
"supportPhoneNumber",
|
||||
"+1 (212) 867 5309",
|
||||
"supportEmail",
|
||||
"support@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFailure_notAConsoleUser() throws IOException {
|
||||
ConsoleUserDataAction action =
|
||||
createAction(
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
new com.google.appengine.api.users.User(
|
||||
"JohnDoe@theregistrar.com", "theregistrar.com"),
|
||||
@@ -80,6 +83,7 @@ class ConsoleUserDataActionTest {
|
||||
}
|
||||
|
||||
private ConsoleUserDataAction createAction(AuthResult authResult) throws IOException {
|
||||
return new ConsoleUserDataAction(authResult, response, "test");
|
||||
return new ConsoleUserDataAction(
|
||||
authResult, response, "Nomulus", "support@example.com", "+1 (212) 867 5309", "test");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.DeterministicStringGenerator;
|
||||
import google.registry.testing.FakeResponse;
|
||||
@@ -108,8 +107,7 @@ class RegistrarsActionTest {
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(
|
||||
new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_LEAD).build()))));
|
||||
@@ -129,8 +127,7 @@ class RegistrarsActionTest {
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))));
|
||||
action.run();
|
||||
@@ -151,8 +148,7 @@ class RegistrarsActionTest {
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(createUser(new UserRoles.Builder().setIsAdmin(true).build()))));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
@@ -180,8 +176,7 @@ class RegistrarsActionTest {
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(new UserRoles.Builder().setIsAdmin(true).build()))));
|
||||
action.run();
|
||||
@@ -200,8 +195,7 @@ class RegistrarsActionTest {
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(createUser(new UserRoles.Builder().setIsAdmin(true).build()))));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST);
|
||||
@@ -215,8 +209,7 @@ class RegistrarsActionTest {
|
||||
RegistrarsAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(
|
||||
new UserRoles.Builder()
|
||||
@@ -232,7 +225,6 @@ class RegistrarsActionTest {
|
||||
private User createUser(UserRoles userRoles) {
|
||||
return new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(userRoles)
|
||||
.build();
|
||||
}
|
||||
|
||||
+6
-14
@@ -37,7 +37,6 @@ import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.Action;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.FakeResponse;
|
||||
import google.registry.ui.server.registrar.RegistrarConsoleModule;
|
||||
@@ -103,8 +102,7 @@ class ContactActionTest {
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))),
|
||||
testRegistrar.getRegistrarId(),
|
||||
@@ -121,8 +119,7 @@ class ContactActionTest {
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.GET,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))),
|
||||
testRegistrar.getRegistrarId(),
|
||||
@@ -137,8 +134,7 @@ class ContactActionTest {
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))),
|
||||
testRegistrar.getRegistrarId(),
|
||||
@@ -160,8 +156,7 @@ class ContactActionTest {
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))),
|
||||
testRegistrar.getRegistrarId(),
|
||||
@@ -186,8 +181,7 @@ class ContactActionTest {
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))),
|
||||
testRegistrar.getRegistrarId(),
|
||||
@@ -208,8 +202,7 @@ class ContactActionTest {
|
||||
ContactAction action =
|
||||
createAction(
|
||||
Action.Method.POST,
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(
|
||||
new UserRoles.Builder()
|
||||
@@ -226,7 +219,6 @@ class ContactActionTest {
|
||||
private User createUser(UserRoles userRoles) {
|
||||
return new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(userRoles)
|
||||
.build();
|
||||
}
|
||||
|
||||
+1
-4
@@ -35,7 +35,6 @@ import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.FakeClock;
|
||||
@@ -92,8 +91,7 @@ class SecurityActionTest {
|
||||
clock.setTo(DateTime.parse("2020-11-01T00:00:00Z"));
|
||||
SecurityAction action =
|
||||
createAction(
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))),
|
||||
testRegistrar.getRegistrarId());
|
||||
@@ -109,7 +107,6 @@ class SecurityActionTest {
|
||||
private User createUser(UserRoles userRoles) {
|
||||
return new User.Builder()
|
||||
.setEmailAddress("email@email.com")
|
||||
.setGaiaId("TestUserId")
|
||||
.setUserRoles(userRoles)
|
||||
.build();
|
||||
}
|
||||
|
||||
+2
-5
@@ -33,7 +33,6 @@ import google.registry.model.registrar.Registrar;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.request.RequestModule;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
@@ -127,8 +126,7 @@ public class WhoisRegistrarFieldsActionTest {
|
||||
void testFailure_noAccessToRegistrar() throws Exception {
|
||||
Registrar newRegistrar = Registrar.loadByRegistrarIdCached("NewRegistrar").get();
|
||||
AuthResult onlyTheRegistrar =
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.example")
|
||||
@@ -147,8 +145,7 @@ public class WhoisRegistrarFieldsActionTest {
|
||||
}
|
||||
|
||||
private AuthResult defaultUserAuth() {
|
||||
return AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
return AuthResult.createUser(
|
||||
UserAuthInfo.create(
|
||||
new User.Builder()
|
||||
.setEmailAddress("email@email.example")
|
||||
|
||||
+1
-2
@@ -37,7 +37,6 @@ import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
@@ -93,7 +92,7 @@ public final class ConsoleOteSetupActionTest {
|
||||
ImmutableSetMultimap.of("unused", AuthenticatedRegistrarAccessor.Role.ADMIN));
|
||||
action.userService = UserServiceFactory.getUserService();
|
||||
action.xsrfTokenManager = new XsrfTokenManager(new FakeClock(), action.userService);
|
||||
action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false));
|
||||
action.authResult = AuthResult.createUser(UserAuthInfo.create(user, false));
|
||||
action.sendEmailUtils =
|
||||
new SendEmailUtils(
|
||||
new InternetAddress("outgoing@registry.example"),
|
||||
|
||||
+1
-2
@@ -37,7 +37,6 @@ import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
@@ -93,7 +92,7 @@ final class ConsoleRegistrarCreatorActionTest {
|
||||
ImmutableSetMultimap.of("unused", AuthenticatedRegistrarAccessor.Role.ADMIN));
|
||||
action.userService = UserServiceFactory.getUserService();
|
||||
action.xsrfTokenManager = new XsrfTokenManager(new FakeClock(), action.userService);
|
||||
action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false));
|
||||
action.authResult = AuthResult.createUser(UserAuthInfo.create(user, false));
|
||||
action.sendEmailUtils =
|
||||
new SendEmailUtils(
|
||||
new InternetAddress("outgoing@registry.example"),
|
||||
|
||||
@@ -32,7 +32,6 @@ import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
@@ -78,7 +77,7 @@ class ConsoleUiActionTest {
|
||||
action.xsrfTokenManager = new XsrfTokenManager(new FakeClock(), action.userService);
|
||||
action.method = Method.GET;
|
||||
action.paramClientId = Optional.empty();
|
||||
action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false));
|
||||
action.authResult = AuthResult.createUser(UserAuthInfo.create(user, false));
|
||||
action.analyticsConfig = ImmutableMap.of("googleAnalyticsId", "sampleId");
|
||||
|
||||
action.registrarAccessor =
|
||||
|
||||
+1
-3
@@ -44,7 +44,6 @@ import google.registry.request.JsonActionRunner;
|
||||
import google.registry.request.JsonResponse;
|
||||
import google.registry.request.ResponseImpl;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
@@ -113,8 +112,7 @@ public abstract class RegistrarSettingsActionTestCase {
|
||||
gmailClient);
|
||||
action.registrarConsoleMetrics = new RegistrarConsoleMetrics();
|
||||
action.authResult =
|
||||
AuthResult.create(
|
||||
AuthLevel.USER,
|
||||
AuthResult.createUser(
|
||||
UserAuthInfo.create(new User("user@email.com", "email.com", "12345"), false));
|
||||
action.certificateChecker =
|
||||
new CertificateChecker(
|
||||
|
||||
+4
-6
@@ -40,7 +40,6 @@ import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.Action.Method;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.testing.FakeClock;
|
||||
@@ -75,7 +74,7 @@ final class RegistryLockGetActionTest {
|
||||
void beforeEach() {
|
||||
user = userFromRegistrarPoc(makeRegistrarContact3());
|
||||
fakeClock.setTo(DateTime.parse("2000-06-08T22:00:00.0Z"));
|
||||
authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false));
|
||||
authResult = AuthResult.createUser(UserAuthInfo.create(user, false));
|
||||
accessor =
|
||||
AuthenticatedRegistrarAccessor.createForTesting(
|
||||
ImmutableSetMultimap.of(
|
||||
@@ -101,7 +100,6 @@ final class RegistryLockGetActionTest {
|
||||
google.registry.model.console.User consoleUser =
|
||||
new google.registry.model.console.User.Builder()
|
||||
.setEmailAddress("johndoe@theregistrar.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
@@ -110,7 +108,7 @@ final class RegistryLockGetActionTest {
|
||||
.build())
|
||||
.build();
|
||||
|
||||
action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser));
|
||||
action.authResult = AuthResult.createUser(UserAuthInfo.create(consoleUser));
|
||||
action.run();
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK);
|
||||
assertThat(GSON.fromJson(response.getPayload(), Map.class))
|
||||
@@ -337,7 +335,7 @@ final class RegistryLockGetActionTest {
|
||||
persistResource(makeRegistrar2().asBuilder().setRegistryLockAllowed(false).build());
|
||||
// disallow the other user
|
||||
persistResource(makeRegistrarContact2().asBuilder().setLoginEmailAddress(null).build());
|
||||
authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, true));
|
||||
authResult = AuthResult.createUser(UserAuthInfo.create(user, true));
|
||||
accessor =
|
||||
AuthenticatedRegistrarAccessor.createForTesting(
|
||||
ImmutableSetMultimap.of(
|
||||
@@ -365,7 +363,7 @@ final class RegistryLockGetActionTest {
|
||||
void testSuccess_linkedToLoginContactEmail() {
|
||||
// Note that the email address is case-insensitive.
|
||||
user = new User("marla.singer@crr.com", "crr.com", user.getUserId());
|
||||
authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false));
|
||||
authResult = AuthResult.createUser(UserAuthInfo.create(user, false));
|
||||
action =
|
||||
new RegistryLockGetAction(
|
||||
Method.GET, response, accessor, authResult, Optional.of("TheRegistrar"));
|
||||
|
||||
+10
-27
@@ -45,7 +45,6 @@ import google.registry.request.JsonActionRunner;
|
||||
import google.registry.request.JsonResponse;
|
||||
import google.registry.request.ResponseImpl;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor;
|
||||
import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
@@ -115,8 +114,7 @@ final class RegistryLockPostActionTest {
|
||||
when(mockRequest.getServerName()).thenReturn("registrarconsole.tld");
|
||||
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithLockPermission, false)));
|
||||
createAction(AuthResult.createUser(UserAuthInfo.create(userWithLockPermission, false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,9 +152,7 @@ final class RegistryLockPostActionTest {
|
||||
saveRegistryLock(
|
||||
createLock().asBuilder().isSuperuser(true).setLockCompletionTime(clock.nowUtc()).build());
|
||||
persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build());
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true)));
|
||||
action = createAction(AuthResult.createUser(UserAuthInfo.create(userWithoutPermission, true)));
|
||||
Map<String, ?> response = action.handleJsonRequest(unlockRequest());
|
||||
// we should still email the admin user's email address
|
||||
assertSuccess(response, "unlock", "johndoe@theregistrar.com");
|
||||
@@ -166,8 +162,7 @@ final class RegistryLockPostActionTest {
|
||||
void testSuccess_linkedToLoginEmail() throws Exception {
|
||||
userWithLockPermission = new User("Marla.Singer@crr.com", "crr.com");
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithLockPermission, false)));
|
||||
createAction(AuthResult.createUser(UserAuthInfo.create(userWithLockPermission, false)));
|
||||
Map<String, ?> response = action.handleJsonRequest(lockRequest());
|
||||
assertSuccess(response, "lock", "Marla.Singer.RegistryLock@crr.com");
|
||||
}
|
||||
@@ -205,18 +200,14 @@ final class RegistryLockPostActionTest {
|
||||
@Test
|
||||
void testSuccess_adminUser() throws Exception {
|
||||
// Admin user should be able to lock/unlock regardless -- and we use the admin user's email
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true)));
|
||||
action = createAction(AuthResult.createUser(UserAuthInfo.create(userWithoutPermission, true)));
|
||||
Map<String, ?> response = action.handleJsonRequest(lockRequest());
|
||||
assertSuccess(response, "lock", "johndoe@theregistrar.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSuccess_adminUser_doesNotRequirePassword() throws Exception {
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true)));
|
||||
action = createAction(AuthResult.createUser(UserAuthInfo.create(userWithoutPermission, true)));
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
@@ -231,7 +222,6 @@ final class RegistryLockPostActionTest {
|
||||
google.registry.model.console.User consoleUser =
|
||||
new google.registry.model.console.User.Builder()
|
||||
.setEmailAddress("johndoe@theregistrar.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
@@ -240,8 +230,7 @@ final class RegistryLockPostActionTest {
|
||||
.build())
|
||||
.setRegistryLockPassword("hi")
|
||||
.build();
|
||||
AuthResult consoleAuthResult =
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser));
|
||||
AuthResult consoleAuthResult = AuthResult.createUser(UserAuthInfo.create(consoleUser));
|
||||
action = createAction(consoleAuthResult);
|
||||
Map<String, ?> response = action.handleJsonRequest(lockRequest());
|
||||
assertSuccess(response, "lock", "johndoe@theregistrar.com");
|
||||
@@ -252,11 +241,9 @@ final class RegistryLockPostActionTest {
|
||||
google.registry.model.console.User consoleUser =
|
||||
new google.registry.model.console.User.Builder()
|
||||
.setEmailAddress("johndoe@theregistrar.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(new UserRoles.Builder().setIsAdmin(true).build())
|
||||
.build();
|
||||
AuthResult consoleAuthResult =
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser));
|
||||
AuthResult consoleAuthResult = AuthResult.createUser(UserAuthInfo.create(consoleUser));
|
||||
action = createAction(consoleAuthResult);
|
||||
Map<String, Object> requestMapWithoutPassword =
|
||||
ImmutableMap.of(
|
||||
@@ -288,7 +275,7 @@ final class RegistryLockPostActionTest {
|
||||
@Test
|
||||
void testFailure_unauthorizedRegistrarId() {
|
||||
AuthResult authResult =
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithLockPermission, false));
|
||||
AuthResult.createUser(UserAuthInfo.create(userWithLockPermission, false));
|
||||
action = createAction(authResult, ImmutableSet.of("TheRegistrar"));
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
@@ -360,9 +347,7 @@ final class RegistryLockPostActionTest {
|
||||
|
||||
@Test
|
||||
void testFailure_notEnabledForRegistrarPoc() {
|
||||
action =
|
||||
createAction(
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, false)));
|
||||
action = createAction(AuthResult.createUser(UserAuthInfo.create(userWithoutPermission, false)));
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
ImmutableMap.of(
|
||||
@@ -447,7 +432,6 @@ final class RegistryLockPostActionTest {
|
||||
google.registry.model.console.User consoleUser =
|
||||
new google.registry.model.console.User.Builder()
|
||||
.setEmailAddress("johndoe@theregistrar.com")
|
||||
.setGaiaId("gaiaId")
|
||||
.setUserRoles(
|
||||
new UserRoles.Builder()
|
||||
.setRegistrarRoles(
|
||||
@@ -456,8 +440,7 @@ final class RegistryLockPostActionTest {
|
||||
.build())
|
||||
.setRegistryLockPassword("hi")
|
||||
.build();
|
||||
AuthResult consoleAuthResult =
|
||||
AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser));
|
||||
AuthResult consoleAuthResult = AuthResult.createUser(UserAuthInfo.create(consoleUser));
|
||||
action = createAction(consoleAuthResult);
|
||||
Map<String, ?> response =
|
||||
action.handleJsonRequest(
|
||||
|
||||
+2
-3
@@ -43,7 +43,6 @@ import google.registry.model.tld.Tld;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions;
|
||||
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension;
|
||||
import google.registry.request.auth.AuthResult;
|
||||
import google.registry.request.auth.AuthSettings.AuthLevel;
|
||||
import google.registry.request.auth.UserAuthInfo;
|
||||
import google.registry.security.XsrfTokenManager;
|
||||
import google.registry.testing.CloudTasksHelper;
|
||||
@@ -132,7 +131,7 @@ final class RegistryLockVerifyActionTest {
|
||||
|
||||
@Test
|
||||
void testSuccess_adminLock_createsOnlyHistoryEntry() {
|
||||
action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, true));
|
||||
action.authResult = AuthResult.createUser(UserAuthInfo.create(user, true));
|
||||
saveRegistryLock(createLock().asBuilder().isSuperuser(true).build());
|
||||
|
||||
action.run();
|
||||
@@ -332,7 +331,7 @@ final class RegistryLockVerifyActionTest {
|
||||
stringGenerator, "adminreg", cloudTasksHelper.getTestCloudTasksUtils()),
|
||||
lockVerificationCode,
|
||||
isLock);
|
||||
authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false));
|
||||
authResult = AuthResult.createUser(UserAuthInfo.create(user, false));
|
||||
action.req = request;
|
||||
action.response = response;
|
||||
action.authResult = authResult;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
addGracePeriodLength: 432000000
|
||||
addGracePeriodLength: "PT432000S"
|
||||
allowedFullyQualifiedHostNames:
|
||||
- "foo"
|
||||
allowedRegistrantContactIds: []
|
||||
anchorTenantAddGracePeriodLength: 2592000000
|
||||
autoRenewGracePeriodLength: 3888000000
|
||||
automaticTransferLength: 432000000
|
||||
anchorTenantAddGracePeriodLength: "PT2592000S"
|
||||
autoRenewGracePeriodLength: "PT3888000S"
|
||||
automaticTransferLength: "PT432000S"
|
||||
claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
|
||||
createBillingCost:
|
||||
currency: "USD"
|
||||
@@ -13,7 +13,7 @@ creationTime: "1970-01-01T00:00:00.000Z"
|
||||
currency: "USD"
|
||||
defaultPromoTokens:
|
||||
- "bbbbb"
|
||||
dnsAPlusAaaaTtl: 3600000
|
||||
dnsAPlusAaaaTtl: "PT3600S"
|
||||
dnsDsTtl: null
|
||||
dnsNsTtl: null
|
||||
dnsPaused: false
|
||||
@@ -38,10 +38,10 @@ idnTables:
|
||||
invoicingEnabled: false
|
||||
lordnUsername: null
|
||||
numDnsPublishLocks: 1
|
||||
pendingDeleteLength: 432000000
|
||||
pendingDeleteLength: "PT432000S"
|
||||
premiumListName: "tld"
|
||||
pricingEngineClassName: "google.registry.model.pricing.StaticPremiumListPricingEngine"
|
||||
redemptionGracePeriodLength: 2592000000
|
||||
redemptionGracePeriodLength: "PT2592000S"
|
||||
registryLockOrUnlockBillingCost:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
@@ -49,7 +49,7 @@ renewBillingCostTransitions:
|
||||
"1970-01-01T00:00:00.000Z":
|
||||
currency: "USD"
|
||||
amount: 11.00
|
||||
renewGracePeriodLength: 432000000
|
||||
renewGracePeriodLength: "PT432000S"
|
||||
reservedListNames: []
|
||||
restoreBillingCost:
|
||||
currency: "USD"
|
||||
@@ -63,4 +63,4 @@ tldStateTransitions:
|
||||
tldStr: "tld"
|
||||
tldType: "REAL"
|
||||
tldUnicode: "tld"
|
||||
transferGracePeriodLength: 432000000
|
||||
transferGracePeriodLength: "PT432000S"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/epp EppTlsAction POST n API APP PUBLIC
|
||||
/_dr/epp EppTlsAction POST n API APP ADMIN
|
||||
/console-api/domain ConsoleDomainGetAction GET n API,LEGACY USER PUBLIC
|
||||
/console-api/registrars RegistrarsAction GET,POST n API,LEGACY USER PUBLIC
|
||||
/console-api/settings/contacts ContactAction GET,POST n API,LEGACY USER PUBLIC
|
||||
@@ -13,4 +13,4 @@ PATH CLASS METHODS OK AUT
|
||||
/registrar-settings RegistrarSettingsAction POST n API,LEGACY USER PUBLIC
|
||||
/registry-lock-get RegistryLockGetAction GET n API,LEGACY USER PUBLIC
|
||||
/registry-lock-post RegistryLockPostAction POST n API,LEGACY USER PUBLIC
|
||||
/registry-lock-verify RegistryLockVerifyAction GET n API,LEGACY NONE PUBLIC
|
||||
/registry-lock-verify RegistryLockVerifyAction GET n API,LEGACY NONE PUBLIC
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
|
||||
/_dr/whois WhoisAction POST n API APP PUBLIC
|
||||
/_dr/whois WhoisAction POST n API APP ADMIN
|
||||
/check CheckApiAction GET n API,LEGACY NONE PUBLIC
|
||||
/rdap/autnum/(*) RdapAutnumAction GET,HEAD n API,LEGACY NONE PUBLIC
|
||||
/rdap/domain/(*) RdapDomainAction GET,HEAD n API,LEGACY NONE PUBLIC
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
addGracePeriodLength: 432000000
|
||||
addGracePeriodLength: "PT2592000S"
|
||||
allowedFullyQualifiedHostNames: []
|
||||
allowedRegistrantContactIds: []
|
||||
anchorTenantAddGracePeriodLength: 2592000000
|
||||
autoRenewGracePeriodLength: 3888000000
|
||||
automaticTransferLength: 432000000
|
||||
anchorTenantAddGracePeriodLength: "PT2592000S"
|
||||
autoRenewGracePeriodLength: "PT2592000S"
|
||||
automaticTransferLength: "PT2592000S"
|
||||
claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
|
||||
createBillingCost:
|
||||
currency: "USD"
|
||||
@@ -11,7 +11,7 @@ createBillingCost:
|
||||
creationTime: "2022-09-01T00:00:00.000Z"
|
||||
currency: "USD"
|
||||
defaultPromoTokens: []
|
||||
dnsAPlusAaaaTtl: 900
|
||||
dnsAPlusAaaaTtl: "PT900S"
|
||||
dnsDsTtl: null
|
||||
dnsNsTtl: null
|
||||
dnsPaused: false
|
||||
@@ -27,10 +27,10 @@ idnTables: []
|
||||
invoicingEnabled: false
|
||||
lordnUsername: null
|
||||
numDnsPublishLocks: 1
|
||||
pendingDeleteLength: 432000000
|
||||
pendingDeleteLength: "PT2592000S"
|
||||
premiumListName: "test"
|
||||
pricingEngineClassName: "google.registry.model.pricing.StaticPremiumListPricingEngine"
|
||||
redemptionGracePeriodLength: 2592000000
|
||||
redemptionGracePeriodLength: "PT2592000S"
|
||||
registryLockOrUnlockBillingCost:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
@@ -38,7 +38,7 @@ renewBillingCostTransitions:
|
||||
"1970-01-01T00:00:00.000Z":
|
||||
currency: "USD"
|
||||
amount: 11.00
|
||||
renewGracePeriodLength: 432000000
|
||||
renewGracePeriodLength: "PT2592000S"
|
||||
reservedListNames: []
|
||||
restoreBillingCost:
|
||||
currency: "USD"
|
||||
@@ -52,4 +52,4 @@ tldStateTransitions:
|
||||
tldStr: "1tld"
|
||||
tldType: "REAL"
|
||||
tldUnicode: "1tld"
|
||||
transferGracePeriodLength: 432000000
|
||||
transferGracePeriodLength: "PT2592000S"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
addGracePeriodLength: 432000000
|
||||
addGracePeriodLength: "PT2592000S"
|
||||
allowedFullyQualifiedHostNames: []
|
||||
allowedRegistrantContactIds: []
|
||||
anchorTenantAddGracePeriodLength: 2592000000
|
||||
autoRenewGracePeriodLength: 3888000000
|
||||
automaticTransferLength: 432000000
|
||||
anchorTenantAddGracePeriodLength: "PT2592000S"
|
||||
autoRenewGracePeriodLength: "PT2592000S"
|
||||
automaticTransferLength: "PT2592000S"
|
||||
claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
|
||||
createBillingCost:
|
||||
currency: "USD"
|
||||
@@ -11,7 +11,7 @@ createBillingCost:
|
||||
creationTime: "2022-09-01T00:00:00.000Z"
|
||||
currency: "USD"
|
||||
defaultPromoTokens: []
|
||||
dnsAPlusAaaaTtl: 900
|
||||
dnsAPlusAaaaTtl: "PT900S"
|
||||
dnsDsTtl: null
|
||||
dnsNsTtl: null
|
||||
dnsPaused: false
|
||||
@@ -28,10 +28,10 @@ idnTables:
|
||||
invoicingEnabled: false
|
||||
lordnUsername: null
|
||||
numDnsPublishLocks: 1
|
||||
pendingDeleteLength: 432000000
|
||||
pendingDeleteLength: "PT2592000S"
|
||||
premiumListName: "test"
|
||||
pricingEngineClassName: "google.registry.model.pricing.StaticPremiumListPricingEngine"
|
||||
redemptionGracePeriodLength: 2592000000
|
||||
redemptionGracePeriodLength: "PT2592000S"
|
||||
registryLockOrUnlockBillingCost:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
@@ -39,7 +39,7 @@ renewBillingCostTransitions:
|
||||
"1970-01-01T00:00:00.000Z":
|
||||
currency: "USD"
|
||||
amount: 11.00
|
||||
renewGracePeriodLength: 432000000
|
||||
renewGracePeriodLength: "PT2592000S"
|
||||
reservedListNames: []
|
||||
restoreBillingCost:
|
||||
currency: "USD"
|
||||
@@ -53,4 +53,4 @@ tldStateTransitions:
|
||||
tldStr: "badidn"
|
||||
tldType: "REAL"
|
||||
tldUnicode: "badidn"
|
||||
transferGracePeriodLength: 432000000
|
||||
transferGracePeriodLength: "PT2592000S"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
addGracePeriodLength: 432000000
|
||||
addGracePeriodLength: "PT2592000S"
|
||||
allowedFullyQualifiedHostNames: []
|
||||
allowedRegistrantContactIds: []
|
||||
anchorTenantAddGracePeriodLength: 2592000000
|
||||
autoRenewGracePeriodLength: 3888000000
|
||||
automaticTransferLength: 432000000
|
||||
anchorTenantAddGracePeriodLength: "PT2592000S"
|
||||
autoRenewGracePeriodLength: "PT2592000S"
|
||||
automaticTransferLength: "PT2592000S"
|
||||
claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
|
||||
createBillingCost:
|
||||
currency: "USD"
|
||||
@@ -11,7 +11,7 @@ createBillingCost:
|
||||
creationTime: "2022-09-01T00:00:00.000Z"
|
||||
currency: "USD"
|
||||
defaultPromoTokens: []
|
||||
dnsAPlusAaaaTtl: 900
|
||||
dnsAPlusAaaaTtl: "PT900S"
|
||||
dnsDsTtl: null
|
||||
dnsNsTtl: null
|
||||
dnsPaused: false
|
||||
@@ -27,10 +27,10 @@ idnTables: []
|
||||
invoicingEnabled: false
|
||||
lordnUsername: null
|
||||
numDnsPublishLocks: 1
|
||||
pendingDeleteLength: 432000000
|
||||
pendingDeleteLength: "PT2592000S"
|
||||
premiumListName: "test"
|
||||
pricingEngineClassName: "google.registry.model.pricing.StaticPremiumListPricingEngine"
|
||||
redemptionGracePeriodLength: 2592000000
|
||||
redemptionGracePeriodLength: "PT2592000S"
|
||||
registryLockOrUnlockBillingCost:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
@@ -38,7 +38,7 @@ renewBillingCostTransitions:
|
||||
"1970-01-01T00:00:00.000Z":
|
||||
currency: "USD"
|
||||
amount: 11.00
|
||||
renewGracePeriodLength: 432000000
|
||||
renewGracePeriodLength: "PT2592000S"
|
||||
reservedListNames: []
|
||||
restoreBillingCost:
|
||||
currency: "USD"
|
||||
@@ -52,4 +52,4 @@ tldStateTransitions:
|
||||
tldStr: "badunicode"
|
||||
tldType: "REAL"
|
||||
tldUnicode: "tld"
|
||||
transferGracePeriodLength: 432000000
|
||||
transferGracePeriodLength: "PT2592000S"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
addGracePeriodLength: 432000000
|
||||
addGracePeriodLength: "PT2592000S"
|
||||
allowedFullyQualifiedHostNames: []
|
||||
allowedRegistrantContactIds: []
|
||||
anchorTenantAddGracePeriodLength: 2592000000
|
||||
autoRenewGracePeriodLength: 3888000000
|
||||
automaticTransferLength: 432000000
|
||||
anchorTenantAddGracePeriodLength: "PT2592000S"
|
||||
autoRenewGracePeriodLength: "PT2592000S"
|
||||
automaticTransferLength: "PT2592000S"
|
||||
claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
|
||||
createBillingCost:
|
||||
currency: "USD"
|
||||
@@ -11,7 +11,7 @@ createBillingCost:
|
||||
creationTime: "2022-09-01T00:00:00.000Z"
|
||||
currency: "USD"
|
||||
defaultPromoTokens: []
|
||||
dnsAPlusAaaaTtl: 900
|
||||
dnsAPlusAaaaTtl: "PT900S"
|
||||
dnsDsTtl: null
|
||||
dnsNsTtl: null
|
||||
dnsPaused: false
|
||||
@@ -27,10 +27,10 @@ idnTables: []
|
||||
invoicingEnabled: false
|
||||
lordnUsername: null
|
||||
numDnsPublishLocks: 1
|
||||
pendingDeleteLength: 432000000
|
||||
pendingDeleteLength: "PT2592000S"
|
||||
premiumListName: "test"
|
||||
pricingEngineClassName: "google.registry.model.pricing.StaticPremiumListPricingEngine"
|
||||
redemptionGracePeriodLength: 2592000000
|
||||
redemptionGracePeriodLength: "PT2592000S"
|
||||
registryLockOrUnlockBillingCost:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
@@ -38,7 +38,7 @@ renewBillingCostTransitions:
|
||||
"1970-01-01T00:00:00.000Z":
|
||||
currency: "USD"
|
||||
amount: 11.00
|
||||
renewGracePeriodLength: 432000000
|
||||
renewGracePeriodLength: "PT2592000S"
|
||||
reservedListNames: []
|
||||
restoreBillingCost:
|
||||
currency: "USD"
|
||||
@@ -52,5 +52,5 @@ tldStateTransitions:
|
||||
tldStr: "extrafield"
|
||||
tldType: "REAL"
|
||||
tldUnicode: "extrafield"
|
||||
transferGracePeriodLength: 432000000
|
||||
transferGracePeriodLength: "PT2592000S"
|
||||
extraField: "hello"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
addGracePeriodLength: 432000000
|
||||
addGracePeriodLength: "PT432000S"
|
||||
allowedFullyQualifiedHostNames:
|
||||
- "beta"
|
||||
- "zeta"
|
||||
- "alpha"
|
||||
- "gamma"
|
||||
allowedRegistrantContactIds: []
|
||||
anchorTenantAddGracePeriodLength: 2592000000
|
||||
autoRenewGracePeriodLength: 3888000000
|
||||
automaticTransferLength: 432000000
|
||||
anchorTenantAddGracePeriodLength: "PT2592000S"
|
||||
autoRenewGracePeriodLength: "PT3888000S"
|
||||
automaticTransferLength: "PT432000S"
|
||||
claimsPeriodEnd: "294247-01-10T04:00:54.775Z"
|
||||
createBillingCost:
|
||||
currency: "USD"
|
||||
@@ -34,10 +34,10 @@ idnTables:
|
||||
invoicingEnabled: false
|
||||
lordnUsername: null
|
||||
numDnsPublishLocks: 1
|
||||
pendingDeleteLength: 432000000
|
||||
pendingDeleteLength: "PT432000S"
|
||||
premiumListName: "idns"
|
||||
pricingEngineClassName: "google.registry.model.pricing.StaticPremiumListPricingEngine"
|
||||
redemptionGracePeriodLength: 2592000000
|
||||
redemptionGracePeriodLength: "PT2592000S"
|
||||
registryLockOrUnlockBillingCost:
|
||||
currency: "USD"
|
||||
amount: 0.00
|
||||
@@ -45,7 +45,7 @@ renewBillingCostTransitions:
|
||||
"1970-01-01T00:00:00.000Z":
|
||||
currency: "USD"
|
||||
amount: 11.00
|
||||
renewGracePeriodLength: 432000000
|
||||
renewGracePeriodLength: "PT432000S"
|
||||
reservedListNames: []
|
||||
restoreBillingCost:
|
||||
currency: "USD"
|
||||
@@ -59,4 +59,4 @@ tldStateTransitions:
|
||||
tldStr: "idns"
|
||||
tldType: "REAL"
|
||||
tldUnicode: "idns"
|
||||
transferGracePeriodLength: 432000000
|
||||
transferGracePeriodLength: "PT432000S"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user