1
0
mirror of https://github.com/google/nomulus synced 2026-05-23 16:21:55 +00:00

Compare commits

...

12 Commits

Author SHA1 Message Date
dependabot[bot]
2621b2d679 Bump follow-redirects from 1.15.2 to 1.15.4 in /docs/console-endpoints (#2278)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Lai Jiang <jianglai@google.com>
2024-01-17 09:20:29 -05:00
Lai Jiang
7a5db3b8fe Upgrade builder image to use Java 17 (#2289)
TESTED=ran nomulus-release on alpha with the new image
2024-01-16 17:05:11 -05:00
dependabot[bot]
055f9c012c Bump follow-redirects from 1.15.3 to 1.15.4 in /console-webapp (#2283)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.3 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-16 15:34:45 -05:00
Pavlo Tkach
14ab9423f8 Update Angular to v17 (#2260) 2024-01-16 13:45:56 -05:00
sarahcaseybot
9223b81ab3 Remove create_tld and update_tld commands (#2261)
* Remove create_tld and update_tld commands

These commands are no longer necessary now that configure_tld command is available. However, the configure_tld command should only be used for crash, QA, and alpha environments. TLDs in production and sandbox must be modified using modifications to their config files in Gerrit unless using the configure_tld command in breakglass mode. Check the "How to configure TLDs" procedure doc for more info.

* re-delete file
2024-01-16 11:32:59 -05:00
Weimin Yu
1dcf34ccc2 Report BSA block status in DomainCheckFlow (#2288)
- Registered names are not affected.

- Reserved names are not affected.

- Names that are none of the above and match some BSA labels are
  reported as blocked.
2024-01-12 17:17:51 -05:00
Weimin Yu
9273d2bf15 Remove deleted BSA labels from database (#2286)
Fixed the bug that retains deleted BSA labels in the database.

Added a few simple end-to-end tests for BSA download.
2024-01-12 14:20:56 -05:00
Ben McIlwain
036d35c11a Make the BSA upload unvailable domains task work with HTTP GET as well (#2287)
Apparently Google Cloud Scheduler can only do GET, not POST, for some reason.
2024-01-12 12:17:52 -05:00
Ben McIlwain
a8ce34586d Add production cronjobs for BSA download/upload actions (#2285)
* Add production cronjob for uploading BSA unavailable names

* Add production cronjob for BSA download action
2024-01-11 18:38:52 -05:00
Ben McIlwain
26fb04f00c Add sandbox cronjob for upload BSA unavailable names (#2284) 2024-01-11 12:21:40 -05:00
Ben McIlwain
9d4c38684a Add a cron schedule for the BSA upload unavailable domains task (#2280)
Also fixes the action taken in the case where zero unavailable domains are
found, and temporarily changes over to using the primary DB (because the replica
transaction was timing out at 30 seconds on large databases). I'll switch this
over to use batching and move it back to replica afterwards, but this should
unblock us temporarily.
2024-01-10 14:34:06 -05:00
Pavlo Tkach
d7edd27cdd Add support for Ubuntu20 on kokoro (#2279) 2024-01-10 14:32:34 -05:00
47 changed files with 10043 additions and 9146 deletions

View File

@@ -61,7 +61,7 @@ dependencyLocking {
node {
download = false
version = "16.19.0"
version = "20.10.0"
}
wrapper {

View File

@@ -31,7 +31,10 @@
"style": "kebab-case"
}
],
"eol-last": ["error", "always"]
"eol-last": [
"error",
"always"
]
}
},
{

File diff suppressed because it is too large Load Diff

View File

@@ -16,35 +16,35 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^15.2.2",
"@angular/cdk": "^15.2.2",
"@angular/common": "^15.2.2",
"@angular/compiler": "^15.2.2",
"@angular/core": "^15.2.2",
"@angular/forms": "^15.2.2",
"@angular/material": "^15.2.2",
"@angular/platform-browser": "^15.2.2",
"@angular/platform-browser-dynamic": "^15.2.2",
"@angular/router": "^15.2.2",
"@angular/animations": "^17.0.7",
"@angular/cdk": "^17.0.4",
"@angular/common": "^17.0.7",
"@angular/compiler": "^17.0.7",
"@angular/core": "^17.0.7",
"@angular/forms": "^17.0.7",
"@angular/material": "^17.0.4",
"@angular/platform-browser": "^17.0.7",
"@angular/platform-browser-dynamic": "^17.0.7",
"@angular/router": "^17.0.7",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
"zone.js": "~0.11.4"
"zone.js": "~0.14.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.2.4",
"@angular-eslint/builder": "15.2.1",
"@angular-eslint/eslint-plugin": "15.2.1",
"@angular-eslint/eslint-plugin-template": "15.2.1",
"@angular-eslint/schematics": "15.2.1",
"@angular-eslint/template-parser": "15.2.1",
"@angular/cli": "~15.2.4",
"@angular/compiler-cli": "^15.2.2",
"@angular-devkit/build-angular": "^17.0.7",
"@angular-eslint/builder": "17.1.1",
"@angular-eslint/eslint-plugin": "17.1.1",
"@angular-eslint/eslint-plugin-template": "17.1.1",
"@angular-eslint/schematics": "17.1.1",
"@angular-eslint/template-parser": "17.1.1",
"@angular/cli": "~17.0.7",
"@angular/compiler-cli": "^17.0.7",
"@types/jasmine": "~4.0.0",
"@types/node": "^18.11.18",
"@typescript-eslint/eslint-plugin": "5.48.2",
"@typescript-eslint/parser": "5.48.2",
"@typescript-eslint/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.2",
"concurrently": "^7.6.0",
"eslint": "^8.33.0",
"eslint": "^8.39.0",
"jasmine-core": "~4.3.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.1.0",
@@ -52,6 +52,6 @@
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0",
"prettier": "2.8.7",
"typescript": "~4.9.4"
"typescript": "~5.2.2"
}
}
}

View File

@@ -17,11 +17,19 @@ import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MaterialModule } from './material.module';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { BackendService } from './shared/services/backend.service';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RouterTestingModule, MaterialModule, BrowserAnimationsModule],
imports: [
HttpClientTestingModule,
RouterTestingModule,
MaterialModule,
BrowserAnimationsModule,
],
providers: [BackendService],
declarations: [AppComponent],
}).compileComponents();
});

View File

@@ -15,6 +15,10 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DomainListComponent } from './domainList.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { MaterialModule } from '../material.module';
import { BackendService } from '../shared/services/backend.service';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
describe('DomainListComponent', () => {
let component: DomainListComponent;
@@ -23,6 +27,12 @@ describe('DomainListComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [DomainListComponent],
imports: [
HttpClientTestingModule,
MaterialModule,
BrowserAnimationsModule,
],
providers: [BackendService],
}).compileComponents();
fixture = TestBed.createComponent(DomainListComponent);

View File

@@ -18,6 +18,8 @@
text-decoration: none;
}
&__header {
margin-top: 0;
margin-bottom: 10px;
@media (max-width: 599px) {
.mat-toolbar {
padding: 0;

View File

@@ -15,18 +15,23 @@
import { TestBed } from '@angular/core/testing';
import { RegistrarGuard } from './registrar.guard';
import { Router, RouterStateSnapshot } from '@angular/router';
import {
ActivatedRouteSnapshot,
Router,
RouterStateSnapshot,
} from '@angular/router';
import { RegistrarService } from './registrar.service';
describe('RegistrarGuard', () => {
let guard: RegistrarGuard;
let dummyRegistrarService: RegistrarService;
let routeSpy: Router;
let dummyRoute: RouterStateSnapshot = {} as RouterStateSnapshot;
let dummyRoute: RouterStateSnapshot;
beforeEach(() => {
routeSpy = jasmine.createSpyObj<Router>('Router', ['navigate']);
dummyRegistrarService = { activeRegistrarId: '' } as RegistrarService;
dummyRoute = { url: '/value' } as RouterStateSnapshot;
TestBed.configureTestingModule({
providers: [
@@ -39,7 +44,7 @@ describe('RegistrarGuard', () => {
it('should not be able to activate when activeRegistrarId is empty', () => {
guard = TestBed.inject(RegistrarGuard);
const res = guard.canActivate();
const res = guard.canActivate(new ActivatedRouteSnapshot(), dummyRoute);
expect(res).toBeFalsy();
});
@@ -48,17 +53,16 @@ describe('RegistrarGuard', () => {
useValue: { activeRegistrarId: 'value' },
});
guard = TestBed.inject(RegistrarGuard);
const res = guard.canActivate();
const res = guard.canActivate(new ActivatedRouteSnapshot(), dummyRoute);
expect(res).toBeTrue();
});
it('should navigate to registrars when activeRegistrarId is empty', () => {
const dummyRoute = { url: '/value' } as RouterStateSnapshot;
it('should navigate to empty-registrar screen when activeRegistrarId is empty', () => {
guard = TestBed.inject(RegistrarGuard);
guard.canActivate();
guard.canActivate(new ActivatedRouteSnapshot(), dummyRoute);
expect(routeSpy.navigate).toHaveBeenCalledOnceWith([
'/registrars',
{ nextUrl: '/value' },
'/empty-registrar',
{ nextUrl: dummyRoute.url },
]);
});
});

View File

@@ -17,6 +17,7 @@ import { TestBed } from '@angular/core/testing';
import { RegistrarService } from './registrar.service';
import { BackendService } from '../shared/services/backend.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { MatSnackBar } from '@angular/material/snack-bar';
describe('RegistrarService', () => {
let service: RegistrarService;
@@ -24,7 +25,7 @@ describe('RegistrarService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [BackendService],
providers: [BackendService, MatSnackBar],
});
service = TestBed.inject(RegistrarService);
});

View File

@@ -15,6 +15,10 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RegistrarSelectorComponent } from './registrarSelector.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { MaterialModule } from '../material.module';
import { BackendService } from '../shared/services/backend.service';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
describe('RegistrarSelectorComponent', () => {
let component: RegistrarSelectorComponent;
@@ -22,6 +26,12 @@ describe('RegistrarSelectorComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
MaterialModule,
BrowserAnimationsModule,
],
providers: [BackendService],
declarations: [RegistrarSelectorComponent],
}).compileComponents();

View File

@@ -15,19 +15,24 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import SecurityComponent from './security.component';
import { SecurityService } from './security.service';
import { SecurityService, apiToUiConverter } from './security.service';
import { BackendService } from 'src/app/shared/services/backend.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { MaterialModule } from 'src/app/material.module';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { of } from 'rxjs';
import { FormsModule } from '@angular/forms';
import {
Registrar,
RegistrarService,
} from 'src/app/registrar/registrar.service';
describe('SecurityComponent', () => {
let component: SecurityComponent;
let fixture: ComponentFixture<SecurityComponent>;
let fetchSecurityDetailsSpy: Function;
let saveSpy: Function;
let dummyRegistrarService: RegistrarService;
beforeEach(async () => {
const securityServiceSpy = jasmine.createSpyObj(SecurityService, [
@@ -40,9 +45,9 @@ describe('SecurityComponent', () => {
saveSpy = securityServiceSpy.saveChanges;
securityServiceSpy.securitySettings = {
ipAddressAllowList: [{ value: '123.123.123.123' }],
};
dummyRegistrarService = {
registrar: { ipAddressAllowList: ['123.123.123.123'] },
} as RegistrarService;
await TestBed.configureTestingModule({
imports: [
@@ -52,7 +57,10 @@ describe('SecurityComponent', () => {
FormsModule,
],
declarations: [SecurityComponent],
providers: [BackendService],
providers: [
BackendService,
{ provide: RegistrarService, useValue: dummyRegistrarService },
],
})
.overrideComponent(SecurityComponent, {
set: {
@@ -72,10 +80,6 @@ describe('SecurityComponent', () => {
expect(component).toBeTruthy();
});
it('should call fetch spy', () => {
expect(fetchSecurityDetailsSpy).toHaveBeenCalledTimes(1);
});
it('should render ip allow list', waitForAsync(() => {
component.enableEdit();
fixture.whenStable().then(() => {
@@ -121,16 +125,17 @@ describe('SecurityComponent', () => {
});
it('should create temporary data structure', () => {
expect(component.dataSource).toBe(
component.securityService.securitySettings
);
component.enableEdit();
expect(component.dataSource).not.toBe(
component.securityService.securitySettings
expect(component.dataSource).toEqual(
apiToUiConverter(dummyRegistrarService.registrar)
);
component.removeIpEntry(0);
expect(component.dataSource).toEqual({ ipAddressAllowList: [] });
expect(dummyRegistrarService.registrar).toEqual({
ipAddressAllowList: ['123.123.123.123'],
} as Registrar);
component.cancel();
expect(component.dataSource).toBe(
component.securityService.securitySettings
expect(component.dataSource).toEqual(
apiToUiConverter(dummyRegistrarService.registrar)
);
});

View File

@@ -24,6 +24,7 @@ import {
import { HttpClientTestingModule } from '@angular/common/http/testing';
import SecurityComponent from './security.component';
import { BackendService } from 'src/app/shared/services/backend.service';
import { MatSnackBar } from '@angular/material/snack-bar';
describe('SecurityService', () => {
const uiMockData: SecuritySettings = {
@@ -43,7 +44,7 @@ describe('SecurityService', () => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
declarations: [SecurityComponent],
providers: [SecurityService, BackendService],
providers: [MatSnackBar, SecurityService, BackendService],
});
service = TestBed.inject(SecurityService);
});

View File

@@ -133,9 +133,8 @@
<h3>Address Line 1:</h3>
</div>
<div class="settings-whois__section-form">
<mat-form-field>
<mat-form-field *ngIf="registrar.localizedAddress?.street">
<input
*ngIf="registrar.localizedAddress?.street"
matInput
type="text"
[(ngModel)]="(registrar.localizedAddress?.street)![0]"
@@ -149,9 +148,8 @@
<h3>City:</h3>
</div>
<div class="settings-whois__section-form">
<mat-form-field>
<mat-form-field *ngIf="registrar.localizedAddress">
<input
*ngIf="registrar.localizedAddress"
matInput
type="text"
[(ngModel)]="registrar.localizedAddress.city"
@@ -167,9 +165,8 @@
<h3>Address Line 2:</h3>
</div>
<div class="settings-whois__section-form">
<mat-form-field>
<mat-form-field *ngIf="registrar.localizedAddress?.street">
<input
*ngIf="registrar.localizedAddress?.street"
matInput
type="text"
[(ngModel)]="(registrar.localizedAddress?.street)![1]"
@@ -183,9 +180,8 @@
<h3>State/Region:</h3>
</div>
<div class="settings-whois__section-form">
<mat-form-field>
<mat-form-field *ngIf="registrar.localizedAddress">
<input
*ngIf="registrar.localizedAddress"
matInput
type="text"
[(ngModel)]="registrar.localizedAddress.state"
@@ -201,9 +197,8 @@
<h3>Address Line 3:</h3>
</div>
<div class="settings-whois__section-form">
<mat-form-field>
<mat-form-field *ngIf="registrar.localizedAddress?.street">
<input
*ngIf="registrar.localizedAddress?.street"
matInput
type="text"
[(ngModel)]="(registrar.localizedAddress?.street)![2]"
@@ -217,9 +212,8 @@
<h3>Country Code:</h3>
</div>
<div class="settings-whois__section-form">
<mat-form-field>
<mat-form-field *ngIf="registrar.localizedAddress">
<input
*ngIf="registrar.localizedAddress"
matInput
type="text"
[(ngModel)]="registrar.localizedAddress.countryCode"

View File

@@ -15,6 +15,11 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import WhoisComponent from './whois.component';
import { MaterialModule } from 'src/app/material.module';
import { BackendService } from 'src/app/shared/services/backend.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { RegistrarService } from 'src/app/registrar/registrar.service';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
describe('WhoisComponent', () => {
let component: WhoisComponent;
@@ -23,6 +28,15 @@ describe('WhoisComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [WhoisComponent],
imports: [
HttpClientTestingModule,
MaterialModule,
BrowserAnimationsModule,
],
providers: [
BackendService,
{ provide: RegistrarService, useValue: { registrar: {} } },
],
}).compileComponents();
fixture = TestBed.createComponent(WhoisComponent);

View File

@@ -35,15 +35,6 @@ $theme-warn: mat.define-palette(mat.$red-palette);
@include form-field-density(-5);
}
@import "@angular/material/theming";
// Define application specific typography settings, font-family, etc
$typography-configuration: mat-typography-config(
$font-family: 'Roboto, "Helvetica Neue", sans-serif',
);
@include angular-material-typography($typography-configuration);
/**
** Light theme
**/

View File

@@ -206,9 +206,7 @@ class BsaDiffCreator {
BlockLabel.of(
entry.getKey(),
LabelType.NEW_ORDER_ASSOCIATION,
idnChecker.getAllValidIdns(entry.getKey()).stream()
.map(IdnTableEnum::name)
.collect(toImmutableSet()))),
getAllValidIdnNames(entry.getKey()))),
newAndRemaining.asMap().entrySet().stream()
.filter(e -> e.getValue().size() > 1 || !e.getValue().contains(ORDER_ID_SENTINEL))
.filter(entry -> !entry.getValue().contains(ORDER_ID_SENTINEL))
@@ -217,13 +215,17 @@ class BsaDiffCreator {
BlockLabel.of(
entry.getKey(),
LabelType.CREATE,
idnChecker.getAllValidIdns(entry.getKey()).stream()
.map(IdnTableEnum::name)
.collect(toImmutableSet()))),
getAllValidIdnNames(entry.getKey()))),
Sets.difference(deleted.keySet(), newAndRemaining.keySet()).stream()
.map(label -> BlockLabel.of(label, LabelType.DELETE, ImmutableSet.of())))
.map(label -> BlockLabel.of(label, LabelType.DELETE, getAllValidIdnNames(label))))
.flatMap(x -> x);
}
ImmutableSet<String> getAllValidIdnNames(String label) {
return idnChecker.getAllValidIdns(label).stream()
.map(IdnTableEnum::name)
.collect(toImmutableSet());
}
}
static class Canonicals<T> {

View File

@@ -162,6 +162,7 @@ public class BsaDownloadAction implements Runnable {
// Fall through
case MAKE_ORDER_AND_LABEL_DIFF:
diff = diffCreator.createDiff(schedule, lazyIdnChecker.get());
// TODO(weiminyu): log the diff stats
gcsClient.writeOrderDiffs(schedule.jobName(), diff.getOrders());
gcsClient.writeLabelDiffs(schedule.jobName(), diff.getLabels());
schedule.updateJobStage(DownloadStage.APPLY_ORDER_AND_LABEL_DIFF);

View File

@@ -18,7 +18,10 @@ import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.model.tld.Tld.isEnrolledWithBsa;
import static google.registry.model.tld.Tlds.getTldEntitiesOfType;
import static google.registry.model.tld.label.ReservedList.loadReservedLists;
import static google.registry.persistence.PersistenceModule.TransactionIsolationLevel.TRANSACTION_REPEATABLE_READ;
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.POST;
import static java.nio.charset.StandardCharsets.US_ASCII;
@@ -68,7 +71,7 @@ import org.joda.time.DateTime;
@Action(
service = Service.BSA,
path = "/_dr/task/uploadBsaUnavailableNames",
method = POST,
method = {GET, POST},
auth = Auth.AUTH_API_ADMIN)
public class UploadBsaUnavailableDomainsAction implements Runnable {
@@ -104,11 +107,19 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
@Override
public void run() {
// TODO(mcilwain): Implement a date Cursor, have the cronjob run frequently, and short-circuit
// the run if the daily upload is already completed.
DateTime runTime = clock.nowUtc();
// TODO(mcilwain): Batch this.
String unavailableDomains =
Joiner.on("\n").join(replicaTm().transact(() -> getUnavailableDomains(runTime)));
uploadToGcs(unavailableDomains, runTime);
uploadToBsa(unavailableDomains, runTime);
Joiner.on("\n")
.join(tm().transact(() -> getUnavailableDomains(runTime), TRANSACTION_REPEATABLE_READ));
if (unavailableDomains.isEmpty()) {
logger.atWarning().log("No unavailable domains found; terminating.");
} else {
uploadToGcs(unavailableDomains, runTime);
uploadToBsa(unavailableDomains, runTime);
}
}
/** Uploads the unavailable domains list to GCS in the unavailable domains bucket. */
@@ -187,7 +198,9 @@ public class UploadBsaUnavailableDomainsAction implements Runnable {
.filter(tld -> isEnrolledWithBsa(tld, runTime))
.collect(toImmutableSet());
logger.atInfo().log("Getting unavailable domains in TLDs: %s ...", bsaEnabledTlds);
logger.atInfo().log(
"Getting unavailable domains in TLDs: %s ...",
bsaEnabledTlds.stream().map(Tld::getTldStr).collect(toImmutableSet()));
ImmutableSortedSet.Builder<String> unavailableDomains =
new ImmutableSortedSet.Builder<>(Ordering.natural());

View File

@@ -14,6 +14,9 @@
package google.registry.bsa.persistence;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.config.RegistryConfig.getEppResourceCachingDuration;
import static google.registry.config.RegistryConfig.getEppResourceMaxCachedEntries;
import static google.registry.model.CacheUtils.newCacheBuilder;
@@ -22,10 +25,15 @@ import static google.registry.persistence.transaction.TransactionManagerFactory.
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import google.registry.persistence.VKey;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.stream.Streams;
/** Helpers for {@link BsaLabel}. */
public final class BsaLabelUtils {
@@ -43,9 +51,11 @@ public final class BsaLabelUtils {
@Override
public Map<VKey<BsaLabel>, Optional<BsaLabel>> loadAll(
Iterable<? extends VKey<BsaLabel>> keys) {
// TODO(b/309173359): need this for DomainCheckFlow
throw new UnsupportedOperationException(
"LoadAll not supported by the BsaLabel cache loader.");
ImmutableMap<VKey<? extends BsaLabel>, BsaLabel> existingLabels =
replicaTm().reTransact(() -> replicaTm().loadByKeysIfPresent(keys));
return Streams.of(keys)
.collect(
toImmutableMap(key -> key, key -> Optional.ofNullable(existingLabels.get(key))));
}
};
@@ -84,4 +94,15 @@ public final class BsaLabelUtils {
public static boolean isLabelBlocked(String domainLabel) {
return cacheBsaLabels.get(BsaLabel.vKey(domainLabel)).isPresent();
}
/** Returns the elements in {@code domainLabels} that are blocked by BSA. */
public static ImmutableSet<String> getBlockedLabels(ImmutableCollection<String> domainLabels) {
ImmutableList<VKey<BsaLabel>> queriedLabels =
domainLabels.stream().map(BsaLabel::vKey).collect(toImmutableList());
return cacheBsaLabels.getAll(queriedLabels).values().stream()
.filter(Optional::isPresent)
.map(Optional::get)
.map(BsaLabel::getLabel)
.collect(toImmutableSet());
}
}

View File

@@ -138,4 +138,15 @@
</description>
<schedule>*/1 * * * *</schedule>
</task>
<task>
<url><![CDATA[/_dr/task/uploadBsaUnavailableNames]]></url>
<name>uploadBsaUnavailableNames</name>
<description>
This job uploads all unavailable domain names (those registered and
reserved) to the BSA.
</description>
<service>bsa</service>
<schedule>23 8,20 * * *</schedule>
</task>
</entries>

View File

@@ -273,4 +273,26 @@
</description>
<schedule>0 15 * * 1</schedule>
</task>
<task>
<url><![CDATA[/_dr/task/bsaDownload]]></url>
<name>bsaDownload</name>
<service>bsa</service>
<description>
Downloads the BSA block list and processes the changes.
</description>
<!-- Runs every hour. -->
<schedule>0 * * * *</schedule>
</task>
<task>
<url><![CDATA[/_dr/task/uploadBsaUnavailableNames]]></url>
<name>uploadBsaUnavailableNames</name>
<description>
This job uploads all unavailable domain names (those registered and
reserved) to the BSA.
</description>
<service>bsa</service>
<schedule>23 8,20 * * *</schedule>
</task>
</entries>

View File

@@ -173,4 +173,15 @@
<!-- Runs every hour. -->
<schedule>0 * * * *</schedule>
</task>
<task>
<url><![CDATA[/_dr/task/uploadBsaUnavailableNames]]></url>
<name>uploadBsaUnavailableNames</name>
<description>
This job uploads all unavailable domain names (those registered and
reserved) to the BSA.
</description>
<service>bsa</service>
<schedule>23 8,20 * * *</schedule>
</task>
</entries>

View File

@@ -159,7 +159,7 @@ public class CheckApiAction implements Runnable {
? "In use"
: (isReserved
? reservedError.get()
: (isBsaBlocked ? "Blocked by the Brand Safety Alliance" : null));
: (isBsaBlocked ? "Blocked by a GlobalBlock service" : null));
ImmutableMap.Builder<String, Object> responseBuilder = new ImmutableMap.Builder<>();
metricBuilder.status(SUCCESS).availability(availability);

View File

@@ -18,6 +18,7 @@ import static com.google.common.base.Strings.emptyToNull;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.bsa.persistence.BsaLabelUtils.getBlockedLabels;
import static google.registry.flows.FlowUtils.validateRegistrarIsLoggedIn;
import static google.registry.flows.ResourceFlowUtils.verifyTargetIdCount;
import static google.registry.flows.domain.DomainFlowUtils.checkAllowedAccessToTld;
@@ -34,6 +35,7 @@ import static google.registry.model.tld.Tld.TldState.START_DATE_SUNRISE;
import static google.registry.model.tld.label.ReservationType.getTypeOfHighestSeverity;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -83,9 +85,12 @@ import google.registry.model.tld.label.ReservationType;
import google.registry.persistence.VKey;
import google.registry.pricing.PricingEngineProxy;
import google.registry.util.Clock;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.joda.time.DateTime;
@@ -177,6 +182,12 @@ public final class DomainCheckFlow implements TransactionalFlow {
.build());
ImmutableMap<String, VKey<Domain>> existingDomains =
ForeignKeyUtils.load(Domain.class, domainNames, now);
// Check block labels only when there are unregistered domains, since "In use" goes before
// "Blocked by BSA".
ImmutableSet<InternetDomainName> bsaBlockedDomainNames =
existingDomains.size() == parsedDomains.size()
? ImmutableSet.of()
: getBsaBlockedDomains(parsedDomains.values());
Optional<AllocationTokenExtension> allocationTokenExtension =
eppInput.getSingleExtension(AllocationTokenExtension.class);
Optional<AllocationTokenDomainCheckResults> tokenDomainCheckResults =
@@ -203,6 +214,7 @@ public final class DomainCheckFlow implements TransactionalFlow {
getMessageForCheck(
parsedDomains.get(domainName),
existingDomains,
bsaBlockedDomainNames,
domainCheckResults,
tldStates,
allocationToken);
@@ -234,6 +246,7 @@ public final class DomainCheckFlow implements TransactionalFlow {
private Optional<String> getMessageForCheck(
InternetDomainName domainName,
ImmutableMap<String, VKey<Domain>> existingDomains,
ImmutableSet<InternetDomainName> bsaBlockedDomains,
ImmutableMap<InternetDomainName, String> tokenCheckResults,
ImmutableMap<String, TldState> tldStates,
Optional<AllocationToken> allocationToken) {
@@ -251,7 +264,18 @@ public final class DomainCheckFlow implements TransactionalFlow {
}
}
}
return Optional.ofNullable(emptyToNull(tokenCheckResults.get(domainName)));
Optional<String> tokenResult =
Optional.ofNullable(emptyToNull(tokenCheckResults.get(domainName)));
if (tokenResult.isPresent()) {
return tokenResult;
}
if (bsaBlockedDomains.contains(domainName)) {
// TODO(weiminyu): extract to a constant for here and CheckApiAction.
// Excerpt from BSA's custom message. Max len 32 chars by EPP XML schema.
return Optional.of("Blocked by a GlobalBlock service");
} else {
return Optional.empty();
}
}
/** Handle the fee check extension. */
@@ -415,6 +439,21 @@ public final class DomainCheckFlow implements TransactionalFlow {
return availabilityCheckDomains;
}
static ImmutableSet<InternetDomainName> getBsaBlockedDomains(
ImmutableCollection<InternetDomainName> parsedDomains) {
Map<String, ImmutableList<InternetDomainName>> labelToDomainNames =
parsedDomains.stream()
.collect(
Collectors.groupingBy(
parsedDomain -> parsedDomain.parts().get(0), toImmutableList()));
ImmutableSet<String> blockedLabels =
getBlockedLabels(ImmutableList.copyOf(labelToDomainNames.keySet()));
labelToDomainNames.keySet().retainAll(blockedLabels);
return labelToDomainNames.values().stream()
.flatMap(Collection::stream)
.collect(toImmutableSet());
}
/** By server policy, fee check names must be listed in the availability check. */
static class OnlyCheckedNamesCanBeFeeCheckedException extends ParameterValuePolicyErrorException {
OnlyCheckedNamesCanBeFeeCheckedException() {

View File

@@ -296,7 +296,7 @@ public class Tld extends ImmutableObject implements Buildable, UnsafeSerializabl
* this procedure to change this value:
*
* <ol>
* <li>Pause the DNS queue via {@link google.registry.tools.UpdateTldCommand}
* <li>Pause the DNS queue via {@link google.registry.tools.ConfigureTldCommand}
* <li>Change this number
* <li>Let the Tld caches expire (currently 5 minutes) and drain the DNS publish queue
* <li>Unpause the DNS queue

View File

@@ -1,485 +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.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static google.registry.tools.UpdateOrDeleteAllocationTokensCommand.getTokenKeys;
import static google.registry.util.CollectionUtils.findDuplicates;
import static google.registry.util.DomainNameUtils.canonicalizeHostname;
import com.beust.jcommander.Parameter;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import google.registry.model.pricing.StaticPremiumListPricingEngine;
import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldState;
import google.registry.model.tld.Tld.TldType;
import google.registry.model.tld.Tlds;
import google.registry.model.tld.label.PremiumList;
import google.registry.model.tld.label.PremiumListDao;
import google.registry.tldconfig.idn.IdnTableEnum;
import google.registry.tools.params.OptionalStringParameter;
import google.registry.tools.params.StringListParameter;
import google.registry.tools.params.TransitionListParameter.BillingCostTransitions;
import google.registry.tools.params.TransitionListParameter.TldStateTransitions;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/** Shared base class for commands to create or update a TLD. */
abstract class CreateOrUpdateTldCommand extends MutatingCommand {
@Inject
@Named("dnsWriterNames")
Set<String> validDnsWriterNames;
@Parameter(description = "Names of the TLDs", required = true)
List<String> mainParameters;
@Parameter(
names = "--escrow",
description = "Whether to enable nightly RDE escrow deposits",
arity = 1)
private Boolean escrow;
@Parameter(
names = "--dns",
description = "Set to false to pause writing to the DNS queue",
arity = 1)
private Boolean dns;
@Nullable
@Parameter(
names = "--add_grace_period",
description = "Length of the add grace period (in ISO 8601 duration format)")
Duration addGracePeriod;
@Nullable
@Parameter(
names = "--redemption_grace_period",
description = "Length of the redemption grace period (in ISO 8601 duration format)")
Duration redemptionGracePeriod;
@Nullable
@Parameter(
names = "--pending_delete_length",
description = "Length of the pending delete period (in ISO 8601 duration format)")
Duration pendingDeleteLength;
@Nullable
@Parameter(
names = "--automatic_transfer_length",
description = "Length of the automatic transfer period (in ISO 8601 duration format)")
private Duration automaticTransferLength;
@Nullable
@Parameter(
names = "--restore_billing_cost",
description = "One-time billing cost for restoring a domain")
private Money restoreBillingCost;
@Nullable
@Parameter(
names = "--roid_suffix",
description = "The suffix to be used for ROIDs, e.g. COM for .com domains (which then "
+ "creates roids looking like 123ABC-COM)")
String roidSuffix;
@Nullable
@Parameter(
names = "--server_status_change_cost",
description = "One-time billing cost for a server status change")
private Money serverStatusChangeCost;
@Nullable
@Parameter(
names = "--registry_lock_or_unlock_cost",
description = "One-time billing cost for a registry lock or unlock")
private Money registryLockOrUnlockCost;
@Nullable
@Parameter(
names = "--tld_type",
description = "Tld type (REAL or TEST)")
private TldType tldType;
@Nullable
@Parameter(
names = "--invoicing_enabled",
description = "Whether invoicing is enabled for this tld.",
arity = 1)
private Boolean invoicingEnabled;
@Nullable
@Parameter(
names = "--create_billing_cost",
description = "Per-year billing cost for creating a domain")
Money createBillingCost;
@Nullable
@Parameter(
names = "--drive_folder_id",
description = "Id of the folder in drive used to publish information for this TLD",
converter = OptionalStringParameter.class,
validateWith = OptionalStringParameter.class)
Optional<String> driveFolderId;
@Nullable
@Parameter(
names = "--lordn_username",
description = "Username for LORDN uploads",
converter = OptionalStringParameter.class,
validateWith = OptionalStringParameter.class)
Optional<String> lordnUsername;
@Nullable
@Parameter(
names = "--premium_list",
description = "The name of the premium list to apply to the TLD",
converter = OptionalStringParameter.class,
validateWith = OptionalStringParameter.class)
Optional<String> premiumListName;
@Parameter(
names = "--tld_state_transitions",
converter = TldStateTransitions.class,
validateWith = TldStateTransitions.class,
description = "Comma-delimited list of TLD state transitions, of the form "
+ "<time>=<tld-state>[,<time>=<tld-state>]*")
ImmutableSortedMap<DateTime, TldState> tldStateTransitions = ImmutableSortedMap.of();
@Parameter(
names = "--renew_billing_cost_transitions",
converter = BillingCostTransitions.class,
validateWith = BillingCostTransitions.class,
description = "Comma-delimited list of renew billing cost transitions, of the form "
+ "<time>=<money-amount>[,<time>=<money-amount>]* where each amount "
+ "represents the per-year billing cost for renewing a domain")
ImmutableSortedMap<DateTime, Money> renewBillingCostTransitions =
ImmutableSortedMap.of();
@Parameter(
names = "--eap_fee_schedule",
converter = BillingCostTransitions.class,
validateWith = BillingCostTransitions.class,
description = "Comma-delimited list of EAP fees effective on specific dates, of the form "
+ "<time>=<money-amount>[,<time>=<money-amount>]* where each amount represents the "
+ "EAP fee for creating a new domain under the TLD.")
ImmutableSortedMap<DateTime, Money> eapFeeSchedule = ImmutableSortedMap.of();
@Nullable
@Parameter(
names = "--reserved_lists",
description = "A comma-separated list of reserved list names to be applied to the TLD",
listConverter = StringListParameter.class)
List<String> reservedListNames;
@Nullable
@Parameter(
names = "--allowed_registrants",
description = "A comma-separated list of allowed registrants for the TLD",
listConverter = StringListParameter.class)
List<String> allowedRegistrants;
@Nullable
@Parameter(
names = "--allowed_nameservers",
description = "A comma-separated list of allowed nameservers for the TLD",
listConverter = StringListParameter.class)
List<String> allowedNameservers;
@Parameter(
names = {"-o", "--override_reserved_list_rules"},
description = "Override restrictions on reserved list naming")
boolean overrideReservedListRules;
@Nullable
@Parameter(
names = "--claims_period_end",
description = "The end of the claims period")
DateTime claimsPeriodEnd;
@Nullable
@Parameter(
names = "--dns_writers",
description = "A comma-separated list of DnsWriter implementations to use",
listConverter = StringListParameter.class)
List<String> dnsWriters;
@Nullable
@Parameter(
names = {"--num_dns_publish_locks"},
description =
"The number of publish locks we allow in parallel for DNS updates under this tld "
+ "(1 for TLD-wide locks)",
arity = 1)
Integer numDnsPublishShards;
@Nullable
@Parameter(
names = {"--dns_a_plus_aaaa_ttl"},
description = "The time to live for DNS A and AAAA records (Ex: PT240S)")
Duration dnsAPlusAaaaTtl;
@Nullable
@Parameter(
names = {"--dns_ns_ttl"},
description = "The time to live for DNS NS records (Ex: PT240S)")
Duration dnsNsTtl;
@Nullable
@Parameter(
names = {"--dns_ds_ttl"},
description = "The time to live for DNS DS records (Ex: PT240S)")
Duration dnsDsTtl;
@Nullable
@Parameter(
names = "--default_tokens",
description =
"A comma-separated list of default allocation tokens to be applied to the TLD. The"
+ " ordering of this list will determine which token is used in the case where"
+ " multiple tokens are valid for a registration. Use an empty string to clear all"
+ " present default tokens.",
listConverter = StringListParameter.class)
List<String> defaultTokens;
@Nullable
@Parameter(
names = "--idn_tables",
description =
"A comma-separated list of the IDN tables to use for this TLD. Specify an empty list to"
+ " remove any previously-set tables and to use the default. All elements must be"
+ " IdnTableEnum values",
listConverter = StringListParameter.class)
List<String> idnTables;
/** Returns the existing tld (for update) or null (for creates). */
@Nullable
abstract Tld getOldTld(String tld);
abstract ImmutableSet<String> getAllowedRegistrants(Tld oldTld);
abstract ImmutableSet<String> getAllowedNameservers(Tld oldTld);
abstract ImmutableSet<String> getReservedLists(Tld oldTld);
abstract Optional<Map.Entry<DateTime, TldState>> getTldStateTransitionToAdd();
/** Subclasses can override this to set their own properties. */
void setCommandSpecificProperties(@SuppressWarnings("unused") Tld.Builder builder) {}
/** Subclasses can override this to assert that the command can be run in this environment. */
void assertAllowedEnvironment() {}
protected abstract void initTldCommand();
@Override
protected final void init() throws UnsupportedEncodingException {
assertAllowedEnvironment();
initTldCommand();
String duplicates = Joiner.on(", ").join(findDuplicates(mainParameters));
checkArgument(duplicates.isEmpty(), "Duplicate arguments found: '%s'", duplicates);
Set<String> tlds = ImmutableSet.copyOf(mainParameters);
checkArgument(roidSuffix == null || tlds.size() == 1,
"Can't update roid suffixes on multiple TLDs simultaneously");
for (String tld : tlds) {
checkArgument(
tld.equals(canonicalizeHostname(tld)),
"TLD '%s' should be given in the canonical form '%s'",
tld,
canonicalizeHostname(tld));
checkArgument(
!Character.isDigit(tld.charAt(0)),
"TLDs cannot begin with a number");
Tld oldTld = getOldTld(tld);
// TODO(b/26901539): Add a flag to set the pricing engine once we have more than one option.
Tld.Builder builder =
oldTld == null
? new Tld.Builder()
.setTldStr(tld)
.setPremiumPricingEngine(StaticPremiumListPricingEngine.NAME)
: oldTld.asBuilder();
if (escrow != null) {
builder.setEscrowEnabled(escrow);
}
if (dns != null) {
builder.setDnsPaused(!dns);
}
Optional<Map.Entry<DateTime, TldState>> tldStateTransitionToAdd =
getTldStateTransitionToAdd();
if (!tldStateTransitions.isEmpty()) {
builder.setTldStateTransitions(tldStateTransitions);
} else if (tldStateTransitionToAdd.isPresent()) {
ImmutableSortedMap.Builder<DateTime, TldState> newTldStateTransitions =
ImmutableSortedMap.naturalOrder();
if (oldTld != null) {
checkArgument(
oldTld
.getTldStateTransitions()
.lastKey()
.isBefore(tldStateTransitionToAdd.get().getKey()),
"Cannot add %s at %s when there is a later transition already scheduled",
tldStateTransitionToAdd.get().getValue(),
tldStateTransitionToAdd.get().getKey());
newTldStateTransitions.putAll(oldTld.getTldStateTransitions());
}
builder.setTldStateTransitions(
newTldStateTransitions.put(getTldStateTransitionToAdd().get()).build());
}
if (!renewBillingCostTransitions.isEmpty()) {
// TODO(b/20764952): need invoicing support for multiple renew billing costs.
if (renewBillingCostTransitions.size() > 1) {
errorPrintStream.println(
"----------------------\n"
+ "WARNING: Do not set multiple renew cost transitions "
+ "until b/20764952 is fixed.\n"
+ "----------------------\n");
}
builder.setRenewBillingCostTransitions(renewBillingCostTransitions);
}
if (!eapFeeSchedule.isEmpty()) {
builder.setEapFeeSchedule(eapFeeSchedule);
}
Optional.ofNullable(addGracePeriod).ifPresent(builder::setAddGracePeriodLength);
Optional.ofNullable(redemptionGracePeriod).ifPresent(builder::setRedemptionGracePeriodLength);
Optional.ofNullable(pendingDeleteLength).ifPresent(builder::setPendingDeleteLength);
Optional.ofNullable(automaticTransferLength).ifPresent(builder::setAutomaticTransferLength);
Optional.ofNullable(driveFolderId).ifPresent(id -> builder.setDriveFolderId(id.orElse(null)));
Optional.ofNullable(createBillingCost).ifPresent(builder::setCreateBillingCost);
Optional.ofNullable(restoreBillingCost).ifPresent(builder::setRestoreBillingCost);
Optional.ofNullable(roidSuffix).ifPresent(builder::setRoidSuffix);
Optional.ofNullable(serverStatusChangeCost)
.ifPresent(builder::setServerStatusChangeBillingCost);
Optional.ofNullable(registryLockOrUnlockCost)
.ifPresent(builder::setRegistryLockOrUnlockBillingCost);
Optional.ofNullable(tldType).ifPresent(builder::setTldType);
Optional.ofNullable(invoicingEnabled).ifPresent(builder::setInvoicingEnabled);
Optional.ofNullable(lordnUsername).ifPresent(u -> builder.setLordnUsername(u.orElse(null)));
Optional.ofNullable(claimsPeriodEnd).ifPresent(builder::setClaimsPeriodEnd);
Optional.ofNullable(numDnsPublishShards).ifPresent(builder::setNumDnsPublishLocks);
Optional.ofNullable(dnsAPlusAaaaTtl).ifPresent(builder::setDnsAPlusAaaaTtl);
Optional.ofNullable(dnsNsTtl).ifPresent(builder::setDnsNsTtl);
Optional.ofNullable(dnsDsTtl).ifPresent(builder::setDnsDsTtl);
if (premiumListName != null) {
if (premiumListName.isPresent()) {
Optional<PremiumList> premiumList =
PremiumListDao.getLatestRevision(premiumListName.get());
checkArgument(
premiumList.isPresent(),
String.format("The premium list '%s' doesn't exist", premiumListName.get()));
builder.setPremiumList(premiumList.get());
} else {
builder.setPremiumList(null);
}
}
if (dnsWriters != null) {
ImmutableSet<String> dnsWritersSet = ImmutableSet.copyOf(dnsWriters);
SetView<String> invalidDnsWriters = Sets.difference(dnsWritersSet, validDnsWriterNames);
checkArgument(
invalidDnsWriters.isEmpty(),
"Invalid DNS writer name(s) specified: %s",
invalidDnsWriters);
builder.setDnsWriters(dnsWritersSet);
}
ImmutableSet<String> newReservedListNames = getReservedLists(oldTld);
checkReservedListValidityForTld(tld, newReservedListNames);
builder.setReservedListsByName(newReservedListNames);
builder.setAllowedRegistrantContactIds(getAllowedRegistrants(oldTld));
builder.setAllowedFullyQualifiedHostNames(getAllowedNameservers(oldTld));
if (defaultTokens != null) {
builder.setDefaultPromoTokens(getTokenKeys(defaultTokens, null));
}
if (idnTables != null) {
if (idnTables.equals(ImmutableList.of(""))) {
builder.setIdnTables(ImmutableSet.of());
} else {
ImmutableSet<String> upperCaseIdnTables =
idnTables.stream().map(String::toUpperCase).collect(toImmutableSet());
ImmutableSet<String> validIdnStringValues =
Arrays.stream(IdnTableEnum.values()).map(Enum::name).collect(toImmutableSet());
checkArgument(
validIdnStringValues.containsAll(upperCaseIdnTables),
"IDN tables %s contained invalid value(s). Possible values: %s",
upperCaseIdnTables,
validIdnStringValues);
builder.setIdnTables(
upperCaseIdnTables.stream().map(IdnTableEnum::valueOf).collect(toImmutableSet()));
}
}
// Update the Registry object.
setCommandSpecificProperties(builder);
stageEntityChange(oldTld, builder.build());
}
}
@Override
public String execute() throws Exception {
try {
return super.execute();
} finally {
// Manually reset the cache here so that subsequent commands (e.g. in SetupOteCommand) see
// the latest version of the data.
// TODO(b/24903801): change all those places to use uncached code paths to get TLDs.
Tlds.resetCache();
}
}
private void checkReservedListValidityForTld(String tld, Set<String> reservedListNames) {
ImmutableList.Builder<String> builder = new ImmutableList.Builder<>();
for (String reservedListName : reservedListNames) {
if (!reservedListName.startsWith("common_") && !reservedListName.startsWith(tld + "_")) {
builder.add(reservedListName);
}
}
ImmutableList<String> invalidNames = builder.build();
if (!invalidNames.isEmpty()) {
String errMsg = String.format("The reserved list(s) %s cannot be applied to the tld %s",
Joiner.on(", ").join(invalidNames),
tld);
if (overrideReservedListRules) {
errorPrintStream.println("Error overridden: " + errMsg);
} else {
throw new IllegalArgumentException(errMsg);
}
}
}
}

View File

@@ -1,113 +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.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static google.registry.model.tld.Tlds.getTlds;
import static google.registry.util.CollectionUtils.nullToEmpty;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Maps;
import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldState;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nullable;
import org.joda.money.CurrencyUnit;
import org.joda.money.Money;
import org.joda.time.DateTime;
/** Command to create a TLD. */
@Parameters(separators = " =", commandDescription = "Create new TLD(s)")
class CreateTldCommand extends CreateOrUpdateTldCommand {
@Nullable
@Parameter(
names = "--initial_tld_state",
description = "Initial state of the TLD (cannot be combined with a transitions list)")
TldState initialTldState;
@Nullable
@Parameter(
names = "--initial_renew_billing_cost",
description = "Initial per-year billing cost for renewing a domain "
+ "(cannot be combined with a transitions list)")
private Money initialRenewBillingCost;
@Override
protected void initTldCommand() {
checkArgument(initialTldState == null || tldStateTransitions.isEmpty(),
"Don't pass both --initial_tld_state and --tld_state_transitions");
checkArgument(initialRenewBillingCost == null || renewBillingCostTransitions.isEmpty(),
"Don't pass both --initial_renew_billing_cost and --renew_billing_cost_transitions");
if (initialRenewBillingCost != null) {
renewBillingCostTransitions = ImmutableSortedMap.of(START_OF_TIME, initialRenewBillingCost);
}
checkArgument(mainParameters.size() == 1, "Can't create more than one TLD at a time");
checkArgument(
!Strings.isNullOrEmpty(roidSuffix),
"The roid suffix is required when creating a TLD");
}
@Override
void setCommandSpecificProperties(Tld.Builder builder) {
// Pick up the currency from the create cost. Since all costs must be in one currency, and that
// condition is enforced by the builder, it doesn't matter which cost we choose it from.
CurrencyUnit currency =
createBillingCost != null ? createBillingCost.getCurrencyUnit() : Tld.DEFAULT_CURRENCY;
builder.setCurrency(currency);
// If this is a non-default currency and the user hasn't specified an EAP fee schedule, set the
// EAP fee schedule to a matching currency.
if (!currency.equals(Tld.DEFAULT_CURRENCY) && eapFeeSchedule.isEmpty()) {
builder.setEapFeeSchedule(ImmutableSortedMap.of(START_OF_TIME, Money.zero(currency)));
}
}
@Override
Tld getOldTld(String tld) {
checkState(!getTlds().contains(tld), "TLD '%s' already exists", tld);
return null;
}
@Override
ImmutableSet<String> getAllowedRegistrants(Tld oldTld) {
return ImmutableSet.copyOf(nullToEmpty(allowedRegistrants));
}
@Override
ImmutableSet<String> getAllowedNameservers(Tld oldTld) {
return ImmutableSet.copyOf(nullToEmpty(allowedNameservers));
}
@Override
ImmutableSet<String> getReservedLists(Tld oldTld) {
return ImmutableSet.copyOf(nullToEmpty(reservedListNames));
}
@Override
Optional<Map.Entry<DateTime, TldState>> getTldStateTransitionToAdd() {
return initialTldState != null
? Optional.of(Maps.immutableEntry(START_OF_TIME, initialTldState))
: Optional.empty();
}
}

View File

@@ -49,7 +49,6 @@ public final class RegistryTool {
.put("create_registrar", CreateRegistrarCommand.class)
.put("create_registrar_groups", CreateRegistrarGroupsCommand.class)
.put("create_reserved_list", CreateReservedListCommand.class)
.put("create_tld", CreateTldCommand.class)
.put("create_user", CreateUserCommand.class)
.put("curl", CurlCommand.class)
.put("delete_allocation_tokens", DeleteAllocationTokensCommand.class)
@@ -114,7 +113,6 @@ public final class RegistryTool {
.put("update_registrar", UpdateRegistrarCommand.class)
.put("update_reserved_list", UpdateReservedListCommand.class)
.put("update_server_locks", UpdateServerLocksCommand.class)
.put("update_tld", UpdateTldCommand.class)
.put("update_user", UpdateUserCommand.class)
.put("upload_claims_list", UploadClaimsListCommand.class)
.put("validate_escrow_deposit", ValidateEscrowDepositCommand.class)

View File

@@ -102,8 +102,6 @@ interface RegistryToolComponent {
void inject(CreateRegistrarCommand command);
void inject(CreateTldCommand command);
void inject(EncryptEscrowDepositCommand command);
void inject(EnqueuePollMessageCommand command);
@@ -162,8 +160,6 @@ interface RegistryToolComponent {
void inject(UpdateRegistrarCommand command);
void inject(UpdateTldCommand command);
void inject(ValidateEscrowDepositCommand command);
void inject(ValidateLoginCredentialsCommand command);

View File

@@ -1,189 +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.tools;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.intersection;
import static com.google.common.collect.Sets.union;
import static google.registry.model.tld.Tlds.assertTldExists;
import static google.registry.util.CollectionUtils.nullToEmpty;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import google.registry.model.tld.Tld;
import google.registry.model.tld.Tld.TldState;
import google.registry.tools.params.StringListParameter;
import google.registry.util.RegistryEnvironment;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.Nullable;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
/** Command to update a TLD. */
@Parameters(separators = " =", commandDescription = "Update existing TLD(s)")
public class UpdateTldCommand extends CreateOrUpdateTldCommand {
@Nullable
@Parameter(
names = "--add_reserved_lists",
description = "A comma-separated list of reserved list names to be added to the TLD",
listConverter = StringListParameter.class)
List<String> reservedListsAdd;
@Nullable
@Parameter(
names = "--remove_reserved_lists",
description = "A comma-separated list of reserved list names to be removed from the TLD",
listConverter = StringListParameter.class)
List<String> reservedListsRemove;
@Nullable
@Parameter(
names = "--add_allowed_registrants",
description = "A comma-separated list of allowed registrants to be added to the TLD",
listConverter = StringListParameter.class)
List<String> allowedRegistrantsAdd;
@Nullable
@Parameter(
names = "--remove_allowed_registrants",
description = "A comma-separated list of allowed registrants to be removed from the TLD",
listConverter = StringListParameter.class)
List<String> allowedRegistrantsRemove;
@Nullable
@Parameter(
names = "--add_allowed_nameservers",
description = "A comma-separated list of allowed nameservers to be added to the TLD",
listConverter = StringListParameter.class)
List<String> allowedNameserversAdd;
@Nullable
@Parameter(
names = "--remove_allowed_nameservers",
description = "A comma-separated list of allowed nameservers to be removed from the TLD",
listConverter = StringListParameter.class)
List<String> allowedNameserversRemove;
@Nullable
@Parameter(
names = "--set_current_tld_state",
description = "Set the current TLD state. Specifically, adds a TLD transition at the "
+ "current time for the specified state.")
TldState setCurrentTldState;
@Override
Tld getOldTld(String tld) {
return Tld.get(assertTldExists(tld));
}
@Override
ImmutableSet<String> getAllowedRegistrants(Tld oldTld) {
return formUpdatedList(
"allowed registrants",
oldTld.getAllowedRegistrantContactIds(),
allowedRegistrants,
allowedRegistrantsAdd,
allowedRegistrantsRemove);
}
@Override
ImmutableSet<String> getAllowedNameservers(Tld oldTld) {
return formUpdatedList(
"allowed nameservers",
oldTld.getAllowedFullyQualifiedHostNames(),
allowedNameservers,
allowedNameserversAdd,
allowedNameserversRemove);
}
@Override
ImmutableSet<String> getReservedLists(Tld oldTld) {
return formUpdatedList(
"reserved lists",
oldTld.getReservedListNames(),
reservedListNames,
reservedListsAdd,
reservedListsRemove);
}
@Override
Optional<Map.Entry<DateTime, TldState>> getTldStateTransitionToAdd() {
return setCurrentTldState != null
? Optional.of(Maps.immutableEntry(DateTime.now(DateTimeZone.UTC), setCurrentTldState))
: Optional.empty();
}
@Override
protected void initTldCommand() {
// Due to per-instance caching on Registry, different instances can end up in different TLD
// states at the same time, so --set_current_tld_state should never be used in production.
checkArgument(
!RegistryEnvironment.get().equals(RegistryEnvironment.PRODUCTION)
|| setCurrentTldState == null,
"--set_current_tld_state is not safe to use in production.");
checkConflicts("reserved_lists", reservedListNames, reservedListsAdd, reservedListsRemove);
checkConflicts(
"allowed_registrants", allowedRegistrants, allowedRegistrantsAdd, allowedRegistrantsRemove);
checkConflicts(
"allowed_nameservers", allowedNameservers, allowedNameserversAdd, allowedNameserversRemove);
checkArgument(setCurrentTldState == null || tldStateTransitions.isEmpty(),
"Don't pass both --set_current_tld_state and --tld_state_transitions");
}
private static ImmutableSet<String> formUpdatedList(
String description,
ImmutableSet<String> originals,
List<String> fullReplacement,
List<String> itemsToAdd,
List<String> itemsToRemove) {
if (fullReplacement != null) {
return ImmutableSet.copyOf(fullReplacement);
}
Set<String> toAdd = ImmutableSet.copyOf(nullToEmpty(itemsToAdd));
Set<String> toRemove = ImmutableSet.copyOf(nullToEmpty(itemsToRemove));
checkIsEmpty(
intersection(toAdd, toRemove),
String.format(
"Adding and removing the same %s simultaneously doesn't make sense", description));
checkIsEmpty(
intersection(originals, toAdd),
String.format("Cannot add %s that were previously present", description));
checkIsEmpty(
difference(toRemove, originals),
String.format("Cannot remove %s that were not previously present", description));
return ImmutableSet.copyOf(difference(union(originals, toAdd), toRemove));
}
private static void checkIsEmpty(Set<String> set, String errorString) {
checkArgument(set.isEmpty(), String.format("%s: %s", errorString, set));
}
private static void checkConflicts(
String baseFlagName, Object overwriteValue, Object addValue, Object removeValue) {
checkNotBoth(baseFlagName, overwriteValue, "add_" + baseFlagName, addValue);
checkNotBoth(baseFlagName, overwriteValue, "remove_" + baseFlagName, removeValue);
}
private static void checkNotBoth(String nameA, Object valueA, String nameB, Object valueB) {
checkArgument(valueA == null || valueB == null, "Don't pass both --%s and --%s", nameA, nameB);
}
}

View File

@@ -16,11 +16,9 @@ package google.registry.tools;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import org.flywaydb.core.Flyway;
import org.testcontainers.containers.BindMode;
import org.testcontainers.containers.Container;
/**
@@ -36,9 +34,6 @@ public class DumpGoldenSchemaCommand extends PostgresqlCommand {
// The mount point in the container.
private static final String CONTAINER_MOUNT_POINT = "/tmp/pg_dump.out";
// Temporary workaround to fix permission issues on certain Linux distro (e. g. Arch Linux).
private static final String CONTAINER_MOUNT_POINT_TMP = "/tmp/pg_dump.tmp";
@Parameter(
names = {"--output", "-o"},
description = "Output file",
@@ -64,19 +59,7 @@ public class DumpGoldenSchemaCommand extends PostgresqlCommand {
if (result.getExitCode() != 0) {
throw new RuntimeException(result.toString());
}
result =
postgresContainer.execInContainer("cp", CONTAINER_MOUNT_POINT_TMP, CONTAINER_MOUNT_POINT);
if (result.getExitCode() != 0) {
throw new RuntimeException(result.toString());
}
}
@Override
protected void onContainerCreate() throws IOException {
// open the output file for write so we can mount it.
new FileOutputStream(output.toFile()).close();
postgresContainer.withFileSystemBind(
output.toString(), CONTAINER_MOUNT_POINT, BindMode.READ_WRITE);
postgresContainer.copyFileFromContainer(CONTAINER_MOUNT_POINT, output.toString());
}
private static String[] getSchemaDumpCommand(String username, String dbName) {
@@ -87,7 +70,7 @@ public class DumpGoldenSchemaCommand extends PostgresqlCommand {
"-U",
username,
"-f",
CONTAINER_MOUNT_POINT_TMP,
CONTAINER_MOUNT_POINT,
"--schema-only",
"--no-owner",
"--no-privileges",

View File

@@ -128,6 +128,7 @@ class BsaDiffCreatorTest {
@Test
void allRemoved() {
when(idnChecker.getAllValidIdns(anyString())).thenReturn(ImmutableSet.of(IdnTableEnum.JA));
when(gcsClient.readBlockList("first", BlockListType.BLOCK))
.thenReturn(Stream.of("domainLabel,orderIDs", "test1,1;2", "test2,3", "test3,1;4"));
when(gcsClient.readBlockList("second", BlockListType.BLOCK)).thenReturn(Stream.of());
@@ -140,9 +141,9 @@ class BsaDiffCreatorTest {
BsaDiff diff = diffCreator.createDiff(schedule, idnChecker);
assertThat(diff.getLabels())
.containsExactly(
BlockLabel.of("test1", LabelType.DELETE, ImmutableSet.of()),
BlockLabel.of("test2", LabelType.DELETE, ImmutableSet.of()),
BlockLabel.of("test3", LabelType.DELETE, ImmutableSet.of()));
BlockLabel.of("test1", LabelType.DELETE, ImmutableSet.of("JA")),
BlockLabel.of("test2", LabelType.DELETE, ImmutableSet.of("JA")),
BlockLabel.of("test3", LabelType.DELETE, ImmutableSet.of("JA")));
assertThat(diff.getOrders())
.containsExactly(
BlockOrder.of(1, OrderType.DELETE),
@@ -227,6 +228,7 @@ class BsaDiffCreatorTest {
@Test
void removeLabelAndOrder() {
when(idnChecker.getAllValidIdns(anyString())).thenReturn(ImmutableSet.of(IdnTableEnum.JA));
when(gcsClient.readBlockList("first", BlockListType.BLOCK))
.thenReturn(Stream.of("domainLabel,orderIDs", "test1,1;2", "test2,3", "test3,1;4"));
when(gcsClient.readBlockList("second", BlockListType.BLOCK))
@@ -239,12 +241,13 @@ class BsaDiffCreatorTest {
when(schedule.latestCompleted()).thenReturn(Optional.of(completedJob));
BsaDiff diff = diffCreator.createDiff(schedule, idnChecker);
assertThat(diff.getLabels())
.containsExactly(BlockLabel.of("test2", LabelType.DELETE, ImmutableSet.of()));
.containsExactly(BlockLabel.of("test2", LabelType.DELETE, ImmutableSet.of("JA")));
assertThat(diff.getOrders()).containsExactly(BlockOrder.of(3, OrderType.DELETE));
}
@Test
void removeLabelAndOrder_multi() {
when(idnChecker.getAllValidIdns(anyString())).thenReturn(ImmutableSet.of(IdnTableEnum.JA));
when(gcsClient.readBlockList("first", BlockListType.BLOCK))
.thenReturn(Stream.of("domainLabel,orderIDs", "test1,1;2", "test2,3", "test3,1;4"));
when(gcsClient.readBlockList("second", BlockListType.BLOCK))
@@ -258,8 +261,8 @@ class BsaDiffCreatorTest {
BsaDiff diff = diffCreator.createDiff(schedule, idnChecker);
assertThat(diff.getLabels())
.containsExactly(
BlockLabel.of("test1", LabelType.DELETE, ImmutableSet.of()),
BlockLabel.of("test3", LabelType.DELETE, ImmutableSet.of()));
BlockLabel.of("test1", LabelType.DELETE, ImmutableSet.of("JA")),
BlockLabel.of("test3", LabelType.DELETE, ImmutableSet.of("JA")));
assertThat(diff.getOrders())
.containsExactly(
BlockOrder.of(1, OrderType.DELETE),

View File

@@ -0,0 +1,203 @@
// 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.
package google.registry.bsa;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.bsa.persistence.BsaTestingUtils.createDownloadScheduler;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTlds;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.joda.time.Duration.standardDays;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.cloud.storage.contrib.nio.testing.LocalStorageHelper;
import com.google.common.base.Joiner;
import google.registry.bsa.BlockListFetcher.LazyBlockList;
import google.registry.bsa.api.BsaReportSender;
import google.registry.gcs.GcsUtils;
import google.registry.model.tld.Tld.TldType;
import google.registry.model.tld.Tlds;
import google.registry.persistence.transaction.JpaTestExtensions;
import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationWithCoverageExtension;
import google.registry.request.Response;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeLockHandler;
import google.registry.testing.FakeResponse;
import java.security.MessageDigest;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
import org.testcontainers.shaded.com.google.common.collect.ImmutableList;
/** Functional tests of BSA block list download and processing. */
@ExtendWith(MockitoExtension.class)
class BsaDownloadFunctionalTest {
static final DateTime TEST_START_TIME = DateTime.parse("2024-01-01T00:00:00Z");
static final String BSA_CSV_HEADER = "domainLabel,orderIDs";
@Mock BlockListFetcher blockListFetcher;
@Mock BsaReportSender bsaReportSender;
private final FakeClock fakeClock = new FakeClock(TEST_START_TIME);
@RegisterExtension
JpaIntegrationWithCoverageExtension jpa =
new JpaTestExtensions.Builder().withClock(fakeClock).buildIntegrationWithCoverageExtension();
private GcsClient gcsClient;
private BsaDownloadAction action;
private Response response;
@BeforeEach
void setup() throws Exception {
createTlds("app", "dev");
Tlds.getTldEntitiesOfType(TldType.REAL)
.forEach(
tld ->
persistResource(
tld.asBuilder().setBsaEnrollStartTime(Optional.of(START_OF_TIME)).build()));
gcsClient =
new GcsClient(new GcsUtils(LocalStorageHelper.getOptions()), "my-bucket", "SHA-256");
response = new FakeResponse();
action =
new BsaDownloadAction(
createDownloadScheduler(fakeClock),
blockListFetcher,
new BsaDiffCreator(gcsClient),
bsaReportSender,
gcsClient,
() -> new IdnChecker(fakeClock),
new BsaLock(
new FakeLockHandler(/* lockSucceeds= */ true), Duration.standardSeconds(30)),
fakeClock,
/* transactionBatchSize= */ 5,
response);
}
@Test
void initialDownload_noUnblockables() throws Exception {
LazyBlockList blockList = mockBlockList(BlockListType.BLOCK, ImmutableList.of("abc,1"));
LazyBlockList blockPlusList =
mockBlockList(BlockListType.BLOCK_PLUS, ImmutableList.of("abc,2", "def,3"));
mockBlockListFetcher(blockList, blockPlusList);
action.run();
String downloadJob = "2024-01-01t000000.000z";
try (Stream<String> blockListFile = gcsClient.readBlockList(downloadJob, BlockListType.BLOCK)) {
assertThat(blockListFile).containsExactly(BSA_CSV_HEADER, "abc,1").inOrder();
}
try (Stream<String> blockListFile =
gcsClient.readBlockList(downloadJob, BlockListType.BLOCK_PLUS)) {
assertThat(blockListFile).containsExactly(BSA_CSV_HEADER, "abc,2", "def,3");
}
ImmutableList<String> persistedLabels =
ImmutableList.copyOf(
tm().transact(
() ->
tm().getEntityManager()
.createNativeQuery("SELECT label from \"BsaLabel\"")
.getResultList()));
// TODO(weiminyu): check intermediate files
assertThat(persistedLabels).containsExactly("abc", "def");
}
@Test
void initialDownload_thenDeleteLabel_noUnblockables() throws Exception {
LazyBlockList blockList = mockBlockList(BlockListType.BLOCK, ImmutableList.of("abc,1"));
LazyBlockList blockPlusList =
mockBlockList(BlockListType.BLOCK_PLUS, ImmutableList.of("abc,2", "def,3"));
LazyBlockList blockList2 = mockBlockList(BlockListType.BLOCK, ImmutableList.of("abc,1"));
LazyBlockList blockPlusList2 =
mockBlockList(BlockListType.BLOCK_PLUS, ImmutableList.of("abc,2"));
mockBlockListFetcher(blockList, blockPlusList, blockList2, blockPlusList2);
action.run();
assertThat(getPersistedLabels()).containsExactly("abc", "def");
fakeClock.advanceBy(standardDays(1));
action.run();
assertThat(getPersistedLabels()).containsExactly("abc");
}
private ImmutableList<String> getPersistedLabels() {
return ImmutableList.copyOf(
tm().transact(
() ->
tm().getEntityManager()
.createNativeQuery("SELECT label from \"BsaLabel\"")
.getResultList()));
}
private void mockBlockListFetcher(LazyBlockList blockList, LazyBlockList blockPlusList)
throws Exception {
when(blockListFetcher.fetch(BlockListType.BLOCK)).thenReturn(blockList);
when(blockListFetcher.fetch(BlockListType.BLOCK_PLUS)).thenReturn(blockPlusList);
}
private void mockBlockListFetcher(
LazyBlockList blockList1,
LazyBlockList blockPlusList1,
LazyBlockList blockList2,
LazyBlockList blockPlusList2)
throws Exception {
when(blockListFetcher.fetch(BlockListType.BLOCK)).thenReturn(blockList1, blockList2);
when(blockListFetcher.fetch(BlockListType.BLOCK_PLUS))
.thenReturn(blockPlusList1, blockPlusList2);
}
static LazyBlockList mockBlockList(BlockListType blockListType, ImmutableList<String> dataLines)
throws Exception {
byte[] bytes =
Joiner.on('\n')
.join(new ImmutableList.Builder().add(BSA_CSV_HEADER).addAll(dataLines).build())
.getBytes(UTF_8);
String checksum = generateChecksum(bytes);
LazyBlockList blockList = mock(LazyBlockList.class);
when(blockList.checksum()).thenReturn(checksum);
when(blockList.getName()).thenReturn(blockListType);
doAnswer(
new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
BiConsumer<byte[], Integer> consumer = invocation.getArgument(0);
consumer.accept(bytes, bytes.length);
return null;
}
})
.when(blockList)
.consumeAll(any(BiConsumer.class));
return blockList;
}
private static String generateChecksum(byte[] bytes) throws Exception {
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(bytes, 0, bytes.length);
return base16().lowerCase().encode(messageDigest.digest());
}
}

View File

@@ -15,8 +15,8 @@
package google.registry.bsa.persistence;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.bsa.persistence.BsaLabelTestingUtils.persistBsaLabel;
import static google.registry.bsa.persistence.BsaLabelUtils.isLabelBlocked;
import static google.registry.bsa.persistence.BsaTestingUtils.persistBsaLabel;
import static google.registry.persistence.transaction.TransactionManagerFactory.replicaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.setJpaTm;
import static google.registry.persistence.transaction.TransactionManagerFactory.setReplicaJpaTm;

View File

@@ -16,14 +16,23 @@ package google.registry.bsa.persistence;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import google.registry.util.Clock;
import org.joda.time.DateTime;
import org.joda.time.Duration;
/** Testing utils for users of {@link BsaLabel}. */
public final class BsaLabelTestingUtils {
/** Exposes BSA persistence entities and tools to test classes. */
public final class BsaTestingUtils {
private BsaLabelTestingUtils() {}
public static final Duration DEFAULT_DOWNLOAD_INTERVAL = Duration.standardHours(1);
public static final Duration DEFAULT_NOP_INTERVAL = Duration.standardDays(1);
private BsaTestingUtils() {}
public static void persistBsaLabel(String domainLabel, DateTime creationTime) {
tm().transact(() -> tm().put(new BsaLabel(domainLabel, creationTime)));
}
public static DownloadScheduler createDownloadScheduler(Clock clock) {
return new DownloadScheduler(DEFAULT_DOWNLOAD_INTERVAL, DEFAULT_NOP_INTERVAL, clock);
}
}

View File

@@ -16,7 +16,7 @@ package google.registry.bsa.persistence;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.bsa.BsaTransactions.bsaTransact;
import static google.registry.bsa.persistence.BsaLabelTestingUtils.persistBsaLabel;
import static google.registry.bsa.persistence.BsaTestingUtils.persistBsaLabel;
import static google.registry.model.tld.label.ReservationType.RESERVED_FOR_SPECIFIC_USE;
import static google.registry.persistence.transaction.TransactionManagerFactory.tm;
import static google.registry.testing.DatabaseHelper.createTld;

View File

@@ -30,7 +30,7 @@ import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static org.mockito.Mockito.verify;
import google.registry.bsa.persistence.BsaLabelTestingUtils;
import google.registry.bsa.persistence.BsaTestingUtils;
import google.registry.model.tld.Tld;
import google.registry.monitoring.whitebox.CheckApiMetric;
import google.registry.monitoring.whitebox.CheckApiMetric.Availability;
@@ -288,7 +288,7 @@ class CheckApiActionTest {
@Test
void testSuccess_blockedByBsa() {
BsaLabelTestingUtils.persistBsaLabel("rich", START_OF_TIME);
BsaTestingUtils.persistBsaLabel("rich", START_OF_TIME);
persistResource(
Tld.get("example").asBuilder().setBsaEnrollStartTime(Optional.of(START_OF_TIME)).build());
assertThat(getCheckResponse("rich.example"))
@@ -296,7 +296,7 @@ class CheckApiActionTest {
"tier", "premium",
"status", "success",
"available", false,
"reason", "Blocked by the Brand Safety Alliance");
"reason", "Blocked by a GlobalBlock service");
verifySuccessMetric(PREMIUM, BSA_BLOCKED);
}

View File

@@ -44,6 +44,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Ordering;
import google.registry.bsa.persistence.BsaTestingUtils;
import google.registry.flows.EppException;
import google.registry.flows.FlowUtils.NotLoggedInException;
import google.registry.flows.FlowUtils.UnknownCurrencyEppException;
@@ -157,6 +158,37 @@ class DomainCheckFlowTest extends ResourceCheckFlowTestCase<DomainCheckFlow, Dom
create(true, "example3.tld", null));
}
@Test
void testSuccess_bsaBlocked_otherwiseAvailable_blocked() throws Exception {
BsaTestingUtils.persistBsaLabel("example1", clock.nowUtc());
doCheckTest(
create(false, "example1.tld", "Blocked by a GlobalBlock service"),
create(true, "example2.tld", null),
create(true, "example3.tld", null));
}
@Test
void testSuccess_bsaBlocked_alsoRegistered_registered() throws Exception {
BsaTestingUtils.persistBsaLabel("example1", clock.nowUtc());
persistActiveDomain("example1.tld");
doCheckTest(
create(false, "example1.tld", "In use"),
create(true, "example2.tld", null),
create(true, "example3.tld", null));
}
@Test
void testSuccess_bsaBlocked_alsoReserved_reserved() throws Exception {
BsaTestingUtils.persistBsaLabel("reserved", clock.nowUtc());
BsaTestingUtils.persistBsaLabel("allowedinsunrise", clock.nowUtc());
setEppInput("domain_check_one_tld_reserved.xml");
doCheckTest(
create(false, "reserved.tld", "Reserved"),
create(false, "allowedinsunrise.tld", "Reserved"),
create(true, "example2.tld", null),
create(true, "example3.tld", null));
}
@Test
void testSuccess_clTridNotSpecified() throws Exception {
setEppInput("domain_check_no_cltrid.xml");

View File

@@ -18,7 +18,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.bsa.persistence.BsaLabelTestingUtils.persistBsaLabel;
import static google.registry.bsa.persistence.BsaTestingUtils.persistBsaLabel;
import static google.registry.flows.FlowTestCase.UserPrivileges.SUPERUSER;
import static google.registry.model.billing.BillingBase.Flag.ANCHOR_TENANT;
import static google.registry.model.billing.BillingBase.Flag.RESERVED;

View File

@@ -1,733 +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.tools;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.model.domain.token.AllocationToken.TokenType.DEFAULT_PROMO;
import static google.registry.model.tld.Tld.TldState.GENERAL_AVAILABILITY;
import static google.registry.model.tld.Tld.TldState.PREDELEGATION;
import static google.registry.testing.DatabaseHelper.createTld;
import static google.registry.testing.DatabaseHelper.persistPremiumList;
import static google.registry.testing.DatabaseHelper.persistReservedList;
import static google.registry.testing.DatabaseHelper.persistResource;
import static google.registry.util.DateTimeUtils.START_OF_TIME;
import static java.math.BigDecimal.ROUND_UNNECESSARY;
import static org.joda.money.CurrencyUnit.JPY;
import static org.joda.money.CurrencyUnit.USD;
import static org.joda.time.DateTimeZone.UTC;
import static org.joda.time.Duration.standardMinutes;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.beust.jcommander.ParameterException;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Range;
import google.registry.model.domain.token.AllocationToken;
import google.registry.model.tld.Tld;
import google.registry.tldconfig.idn.IdnTableEnum;
import java.math.BigDecimal;
import org.joda.money.Money;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link CreateTldCommand}. */
class CreateTldCommandTest extends CommandTestCase<CreateTldCommand> {
@BeforeEach
void beforeEach() {
persistReservedList("common_abuse", "baa,FULLY_BLOCKED");
persistReservedList("xn--q9jyb4c_abuse", "lamb,FULLY_BLOCKED");
persistReservedList("tld_banned", "kilo,FULLY_BLOCKED", "lima,FULLY_BLOCKED");
persistReservedList("soy_expurgated", "fireflies,FULLY_BLOCKED");
persistPremiumList("xn--q9jyb4c", USD, "minecraft,USD 1000");
command.validDnsWriterNames = ImmutableSet.of("VoidDnsWriter", "FooDnsWriter");
}
@Test
void testSuccess() throws Exception {
DateTime before = fakeClock.nowUtc();
runCommandForced("xn--q9jyb4c", "--roid_suffix=Q9JYB4C", "--dns_writers=FooDnsWriter");
DateTime after = fakeClock.nowUtc();
Tld registry = Tld.get("xn--q9jyb4c");
assertThat(registry).isNotNull();
assertThat(registry.getAddGracePeriodLength()).isEqualTo(Tld.DEFAULT_ADD_GRACE_PERIOD);
assertThat(registry.getCreationTime()).isIn(Range.closed(before, after));
assertThat(registry.getDnsWriters()).containsExactly("FooDnsWriter");
assertThat(registry.getTldState(registry.getCreationTime())).isEqualTo(PREDELEGATION);
assertThat(registry.getRedemptionGracePeriodLength())
.isEqualTo(Tld.DEFAULT_REDEMPTION_GRACE_PERIOD);
assertThat(registry.getPendingDeleteLength()).isEqualTo(Tld.DEFAULT_PENDING_DELETE_LENGTH);
assertThat(registry.getRegistryLockOrUnlockBillingCost())
.isEqualTo(Tld.DEFAULT_REGISTRY_LOCK_OR_UNLOCK_BILLING_COST);
}
@Test
void testSuccess_ttls() throws Exception {
runCommandForced(
"xn--q9jyb4c",
"--roid_suffix=Q9JYB4C",
"--dns_writers=FooDnsWriter",
"--dns_a_plus_aaaa_ttl=PT300S",
"--dns_ds_ttl=PT240S",
"--dns_ns_ttl=PT180S");
Tld registry = Tld.get("xn--q9jyb4c");
assertThat(registry).isNotNull();
assertThat(registry.getDnsAPlusAaaaTtl().get()).isEqualTo(standardMinutes(5));
assertThat(registry.getDnsDsTtl().get()).isEqualTo(standardMinutes(4));
assertThat(registry.getDnsNsTtl().get()).isEqualTo(standardMinutes(3));
}
@Test
void testFailure_multipleArguments() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--roid_suffix=BLAH", "--dns_writers=VoidDnsWriter", "xn--q9jyb4c", "test"));
assertThat(thrown).hasMessageThat().contains("Can't create more than one TLD at a time");
}
@Test
void testFailure_multipleDuplicateArguments() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--roid_suffix=BLAH", "--dns_writers=VoidDnsWriter", "test", "test"));
assertThat(thrown).hasMessageThat().contains("Can't create more than one TLD at a time");
}
@Test
void testSuccess_initialTldStateFlag() throws Exception {
runCommandForced(
"--initial_tld_state=GENERAL_AVAILABILITY",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getTldState(DateTime.now(UTC)))
.isEqualTo(GENERAL_AVAILABILITY);
}
@Test
void testSuccess_initialRenewBillingCostFlag() throws Exception {
runCommandForced(
"--initial_renew_billing_cost=\"USD 42.42\"",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getStandardRenewCost(DateTime.now(UTC)))
.isEqualTo(Money.of(USD, 42.42));
}
@Test
void testSuccess_eapFeeSchedule() throws Exception {
DateTime now = DateTime.now(UTC);
DateTime tomorrow = now.plusDays(1);
runCommandForced(
String.format(
"--eap_fee_schedule=\"%s=USD 0.00,%s=USD 50.00,%s=USD 10.00\"",
START_OF_TIME, now, tomorrow),
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
Tld registry = Tld.get("xn--q9jyb4c");
assertThat(registry.getEapFeeFor(now.minusHours(1)).getCost())
.isEqualTo(BigDecimal.ZERO.setScale(2, ROUND_UNNECESSARY));
assertThat(registry.getEapFeeFor(now.plusHours(1)).getCost())
.isEqualTo(new BigDecimal("50.00"));
assertThat(registry.getEapFeeFor(now.plusDays(1).plusHours(1)).getCost())
.isEqualTo(new BigDecimal("10.00"));
}
@Test
void testSuccess_addGracePeriodFlag() throws Exception {
runCommandForced(
"--add_grace_period=PT300S",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getAddGracePeriodLength()).isEqualTo(standardMinutes(5));
}
@Test
void testSuccess_roidSuffixWorks() throws Exception {
runCommandForced("--roid_suffix=RSUFFIX", "--dns_writers=VoidDnsWriter", "tld");
assertThat(Tld.get("tld").getRoidSuffix()).isEqualTo("RSUFFIX");
}
@Test
void testSuccess_escrow() throws Exception {
runCommandForced(
"--escrow=true", "--roid_suffix=Q9JYB4C", "--dns_writers=VoidDnsWriter", "xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getEscrowEnabled()).isTrue();
}
@Test
void testSuccess_noEscrow() throws Exception {
runCommandForced(
"--escrow=false", "--roid_suffix=Q9JYB4C", "--dns_writers=VoidDnsWriter", "xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getEscrowEnabled()).isFalse();
}
@Test
void testSuccess_redemptionGracePeriodFlag() throws Exception {
runCommandForced(
"--redemption_grace_period=PT300S",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getRedemptionGracePeriodLength())
.isEqualTo(standardMinutes(5));
}
@Test
void testSuccess_pendingDeleteLengthFlag() throws Exception {
runCommandForced(
"--pending_delete_length=PT300S",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getPendingDeleteLength()).isEqualTo(standardMinutes(5));
}
@Test
void testSuccess_automaticTransferLengthFlag() throws Exception {
runCommandForced(
"--automatic_transfer_length=PT300S",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getAutomaticTransferLength()).isEqualTo(standardMinutes(5));
}
@Test
void testSuccess_createBillingCostFlag() throws Exception {
runCommandForced(
"--create_billing_cost=\"USD 42.42\"",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getCreateBillingCost()).isEqualTo(Money.of(USD, 42.42));
}
@Test
void testSuccess_restoreBillingCostFlag() throws Exception {
runCommandForced(
"--restore_billing_cost=\"USD 42.42\"",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getRestoreBillingCost()).isEqualTo(Money.of(USD, 42.42));
}
@Test
void testSuccess_serverStatusChangeCostFlag() throws Exception {
runCommandForced(
"--server_status_change_cost=\"USD 42.42\"",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getServerStatusChangeBillingCost())
.isEqualTo(Money.of(USD, 42.42));
}
@Test
void testSuccess_registryLockOrUnlockCostFlag() throws Exception {
runCommandForced(
"--registry_lock_or_unlock_cost=\"USD 42.42\"",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getRegistryLockOrUnlockBillingCost())
.isEqualTo(Money.of(USD, 42.42));
}
@Test
void testSuccess_nonUsdBillingCostFlag() throws Exception {
runCommandForced(
"--create_billing_cost=\"JPY 12345\"",
"--restore_billing_cost=\"JPY 67890\"",
"--initial_renew_billing_cost=\"JPY 101112\"",
"--server_status_change_cost=\"JPY 97865\"",
"--registry_lock_or_unlock_cost=\"JPY 9001\"",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
Tld registry = Tld.get("xn--q9jyb4c");
assertThat(registry.getCreateBillingCost()).isEqualTo(Money.ofMajor(JPY, 12345));
assertThat(registry.getRestoreBillingCost()).isEqualTo(Money.ofMajor(JPY, 67890));
assertThat(registry.getStandardRenewCost(START_OF_TIME)).isEqualTo(Money.ofMajor(JPY, 101112));
}
@Test
void testSuccess_multipartTld() throws Exception {
runCommandForced("co.uk", "--roid_suffix=COUK", "--dns_writers=VoidDnsWriter");
Tld registry = Tld.get("co.uk");
assertThat(registry.getTldState(DateTime.now(UTC))).isEqualTo(PREDELEGATION);
assertThat(registry.getAddGracePeriodLength()).isEqualTo(Tld.DEFAULT_ADD_GRACE_PERIOD);
assertThat(registry.getRedemptionGracePeriodLength())
.isEqualTo(Tld.DEFAULT_REDEMPTION_GRACE_PERIOD);
assertThat(registry.getPendingDeleteLength()).isEqualTo(Tld.DEFAULT_PENDING_DELETE_LENGTH);
}
@Test
void testSuccess_setReservedLists() throws Exception {
runCommandForced(
"--reserved_lists=xn--q9jyb4c_abuse,common_abuse",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getReservedListNames())
.containsExactly("xn--q9jyb4c_abuse", "common_abuse");
}
@Test
void testFailure_invalidAddGracePeriod() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--add_grace_period=5m",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c"));
assertThat(thrown).hasMessageThat().contains("Invalid format: \"5m\"");
}
@Test
void testFailure_invalidRedemptionGracePeriod() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--redemption_grace_period=5m",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c"));
assertThat(thrown).hasMessageThat().contains("Invalid format: \"5m\"");
}
@Test
void testFailure_invalidPendingDeleteLength() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--pending_delete_length=5m",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c"));
assertThat(thrown).hasMessageThat().contains("Invalid format: \"5m\"");
}
@Test
void testFailure_invalidTldState() {
ParameterException thrown =
assertThrows(
ParameterException.class,
() ->
runCommandForced(
"--initial_tld_state=INVALID_STATE",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c"));
assertThat(thrown).hasMessageThat().contains("Invalid value for --initial_tld_state parameter");
}
@Test
void testFailure_bothTldStateFlags() {
DateTime now = DateTime.now(UTC);
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
String.format(
"--tld_state_transitions=%s=PREDELEGATION,%s=START_DATE_SUNRISE",
now, now.plus(Duration.millis(1))),
"--initial_tld_state=GENERAL_AVAILABILITY",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c"));
assertThat(thrown)
.hasMessageThat()
.contains("Don't pass both --initial_tld_state and --tld_state_transitions");
}
@Test
void testFailure_negativeInitialRenewBillingCost() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--initial_renew_billing_cost=USD -42",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c"));
assertThat(thrown).hasMessageThat().contains("Renew billing cost cannot be negative");
}
@Test
void testFailure_invalidEapCurrency() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
String.format("--eap_fee_schedule=\"%s=JPY 123456\"", START_OF_TIME),
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c"));
assertThat(thrown).hasMessageThat().contains("All EAP fees must be in the TLD's currency");
}
@Test
void testFailure_noTldName() {
ParameterException thrown = assertThrows(ParameterException.class, this::runCommandForced);
assertThat(thrown)
.hasMessageThat()
.contains("Main parameters are required (\"Names of the TLDs\")");
}
@Test
void testFailure_noDnsWriter() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> runCommandForced("xn--q9jyb4c", "--roid_suffix=Q9JYB4C"));
assertThat(thrown).hasMessageThat().contains("At least one DNS writer must be specified");
}
@Test
void testFailure_alreadyExists() {
createTld("xn--q9jyb4c");
IllegalStateException thrown =
assertThrows(
IllegalStateException.class,
() ->
runCommandForced(
"--roid_suffix=NOTDUPE", "--dns_writers=VoidDnsWriter", "xn--q9jyb4c"));
assertThat(thrown).hasMessageThat().contains("TLD 'xn--q9jyb4c' already exists");
}
@Test
void testFailure_tldStartsWithDigit() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() -> runCommandForced("1foo", "--roid_suffix=1FOO", "--dns_writers=VoidDnsWriter"));
assertThat(thrown).hasMessageThat().contains("TLDs cannot begin with a number");
}
@Test
void testSuccess_setAllowedRegistrants() throws Exception {
runCommandForced(
"--allowed_registrants=alice,bob",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getAllowedRegistrantContactIds())
.containsExactly("alice", "bob");
}
@Test
void testSuccess_emptyAllowedRegistrants() throws Exception {
runCommandForced(
"--allowed_registrants=",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getAllowedRegistrantContactIds()).isEmpty();
}
@Test
void testSuccess_setAllowedNameservers() throws Exception {
runCommandForced(
"--allowed_nameservers=ns1.example.com,ns2.example.com",
"--roid_suffix=Q9JYB4C",
"--dns_writers=FooDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getAllowedFullyQualifiedHostNames())
.containsExactly("ns1.example.com", "ns2.example.com");
}
@Test
void testSuccess_emptyAllowedNameservers() throws Exception {
runCommandForced(
"--allowed_nameservers=",
"--roid_suffix=Q9JYB4C",
"--dns_writers=FooDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getAllowedFullyQualifiedHostNames()).isEmpty();
}
@Test
void testSuccess_setCommonReservedListOnTld() throws Exception {
runSuccessfulReservedListsTest("common_abuse");
}
@Test
void testSuccess_setTldSpecificReservedListOnTld() throws Exception {
runSuccessfulReservedListsTest("xn--q9jyb4c_abuse");
}
@Test
void testSuccess_setCommonReservedListAndTldSpecificReservedListOnTld() throws Exception {
runSuccessfulReservedListsTest("common_abuse,xn--q9jyb4c_abuse");
}
@Test
void testFailure_setReservedListFromOtherTld() {
runFailureReservedListsTest(
"tld_banned",
IllegalArgumentException.class,
"The reserved list(s) tld_banned cannot be applied to the tld xn--q9jyb4c");
}
@Test
void testSuccess_setReservedListFromOtherTld_withOverride() throws Exception {
runReservedListsTestOverride("tld_banned");
}
@Test
void testFailure_setCommonAndReservedListFromOtherTld() {
runFailureReservedListsTest(
"common_abuse,tld_banned",
IllegalArgumentException.class,
"The reserved list(s) tld_banned cannot be applied to the tld xn--q9jyb4c");
}
@Test
void testSuccess_setCommonAndReservedListFromOtherTld_withOverride() throws Exception {
command.errorPrintStream = System.err;
runReservedListsTestOverride("common_abuse,tld_banned");
String errMsg =
"Error overridden: The reserved list(s) tld_banned "
+ "cannot be applied to the tld xn--q9jyb4c";
assertThat(getStderrAsString()).contains(errMsg);
}
@Test
void testFailure_setMultipleReservedListsFromOtherTld() {
runFailureReservedListsTest(
"tld_banned,soy_expurgated",
IllegalArgumentException.class,
"The reserved list(s) tld_banned, soy_expurgated cannot be applied to the tld xn--q9jyb4c");
}
@Test
void testSuccess_setMultipleReservedListsFromOtherTld_withOverride() throws Exception {
runReservedListsTestOverride("tld_banned,soy_expurgated");
}
@Test
void testFailure_setNonExistentReservedLists() {
runFailureReservedListsTest(
"xn--q9jyb4c_asdf,common_asdsdgh",
IllegalArgumentException.class,
"Could not find reserved list xn--q9jyb4c_asdf to add to the tld");
}
@Test
void testSuccess_setPremiumList() throws Exception {
runCommandForced(
"--premium_list=xn--q9jyb4c",
"--roid_suffix=Q9JYB4C",
"--dns_writers=FooDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getPremiumListName()).hasValue("xn--q9jyb4c");
}
@Test
void testSuccess_setDriveFolderIdToValue() throws Exception {
runCommandForced(
"--drive_folder_id=madmax2030",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getDriveFolderId()).isEqualTo("madmax2030");
}
@Test
void testSuccess_setDriveFolderIdToNull() throws Exception {
runCommandForced(
"--drive_folder_id=null",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getDriveFolderId()).isNull();
}
@Test
void testSuccess_setsIdnTables() throws Exception {
runCommandForced(
"--idn_tables=extended_latin,ja",
"--roid_suffix=ASDF",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getIdnTables())
.containsExactly(IdnTableEnum.EXTENDED_LATIN, IdnTableEnum.JA);
}
@Test
void testFailure_invalidIdnTable() throws Exception {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--idn_tables=extended_latin,bad_value",
"--roid_suffix=ASDF",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c"));
assertThat(thrown)
.hasMessageThat()
.isEqualTo(
"IDN tables [EXTENDED_LATIN, BAD_VALUE] contained invalid value(s). Possible values:"
+ " [EXTENDED_LATIN, UNCONFUSABLE_LATIN, JA]");
}
@Test
void testFailure_setPremiumListThatDoesntExist() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"--premium_list=phonies",
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c"));
assertThat(thrown).hasMessageThat().contains("The premium list 'phonies' doesn't exist");
}
@Test
void testFailure_specifiedDnsWriters_dontExist() {
IllegalArgumentException thrown =
assertThrows(
IllegalArgumentException.class,
() ->
runCommandForced(
"xn--q9jyb4c", "--roid_suffix=Q9JYB4C", "--dns_writers=Invalid,Deadbeef"));
assertThat(thrown)
.hasMessageThat()
.contains("Invalid DNS writer name(s) specified: [Invalid, Deadbeef]");
}
@Test
void testSuccess_defaultToken() throws Exception {
AllocationToken token =
persistResource(
new AllocationToken()
.asBuilder()
.setToken("abc123")
.setTokenType(DEFAULT_PROMO)
.setAllowedTlds(ImmutableSet.of("xn--q9jyb4c"))
.build());
runCommandForced(
"--default_tokens=abc123",
"--roid_suffix=Q9JYB4C",
"--dns_writers=FooDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getDefaultPromoTokens()).containsExactly(token.createVKey());
}
@Test
void testSuccess_multipleDefaultTokens() throws Exception {
AllocationToken token =
persistResource(
new AllocationToken()
.asBuilder()
.setToken("abc123")
.setTokenType(DEFAULT_PROMO)
.setAllowedTlds(ImmutableSet.of("xn--q9jyb4c"))
.build());
AllocationToken token2 =
persistResource(
new AllocationToken()
.asBuilder()
.setToken("token")
.setTokenType(DEFAULT_PROMO)
.setAllowedTlds(ImmutableSet.of("xn--q9jyb4c"))
.build());
runCommandForced(
"--default_tokens=abc123,token",
"--roid_suffix=Q9JYB4C",
"--dns_writers=FooDnsWriter",
"xn--q9jyb4c");
assertThat(Tld.get("xn--q9jyb4c").getDefaultPromoTokens())
.containsExactly(token.createVKey(), token2.createVKey());
}
@Test
void testFailure_specifiedDefaultToken_doesntExist() {
IllegalStateException thrown =
assertThrows(
IllegalStateException.class,
() ->
runCommandForced(
"xn--q9jyb4c",
"--default_tokens=InvalidToken",
"--roid_suffix=Q9JYB4C",
"--dns_writers=FooDnsWriter"));
assertThat(thrown)
.hasMessageThat()
.contains("Tokens with keys [VKey<AllocationToken>(sql:InvalidToken)] did not exist");
}
private void runSuccessfulReservedListsTest(String reservedLists) throws Exception {
runCommandForced(
"--reserved_lists",
reservedLists,
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
}
private void runReservedListsTestOverride(String reservedLists) throws Exception {
runCommandForced(
"--override_reserved_list_rules",
"--reserved_lists",
reservedLists,
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c");
}
private void runFailureReservedListsTest(
String reservedLists, Class<? extends Exception> errorClass, String errorMsg) {
Exception e =
assertThrows(
errorClass,
() ->
runCommandForced(
"--reserved_lists",
reservedLists,
"--roid_suffix=Q9JYB4C",
"--dns_writers=VoidDnsWriter",
"xn--q9jyb4c"));
assertThat(e).hasMessageThat().isEqualTo(errorMsg);
}
}

View File

@@ -38,6 +38,8 @@ class PathParameterTest {
private final PathParameter vanilla = new PathParameter();
private static final Boolean isRoot = "root".equals(System.getProperty("user.name"));
@Test
void testConvert_etcPasswd_returnsPath() {
assertThat((Object) vanilla.convert("/etc/passwd")).isEqualTo(Paths.get("/etc/passwd"));
@@ -105,9 +107,15 @@ class PathParameterTest {
void testInputFileValidate_unreadableFile_throws() throws Exception {
Path file = Files.createFile(tmpDir.resolve("tmpfile.txt"));
Files.setPosixFilePermissions(file, PosixFilePermissions.fromString("-w-------"));
ParameterException thrown =
assertThrows(ParameterException.class, () -> inputFile.validate("input", file.toString()));
assertThat(thrown).hasMessageThat().contains("not readable");
// This test doesn't take into account the fact that root user has access to read/write the
// file even if posix permissions disallow it.
// For the environmnent that run strictly under root, we will ignore the test results.
if (!isRoot) {
ParameterException thrown =
assertThrows(
ParameterException.class, () -> inputFile.validate("input", file.toString()));
assertThat(thrown).hasMessageThat().contains("not readable");
}
}
// =========================== Test OutputFile Validate ========================================
@@ -142,9 +150,15 @@ class PathParameterTest {
void testOutputFileValidate_notWritable_throws() throws Exception {
Path file = Files.createFile(tmpDir.resolve("newFile.dat"));
Files.setPosixFilePermissions(file, PosixFilePermissions.fromString("r--------"));
ParameterException thrown =
assertThrows(ParameterException.class, () -> outputFile.validate("input", file.toString()));
assertThat(thrown).hasMessageThat().contains("not writable");
// This test doesn't take into account the fact that root user has access to read/write the
// file even if posix permissions disallow it.
// For the environmnent that run strictly under root, we will ignore the test results.
if (!isRoot) {
ParameterException thrown =
assertThrows(
ParameterException.class, () -> outputFile.validate("input", file.toString()));
assertThat(thrown).hasMessageThat().contains("not writable");
}
}
@Test

View File

@@ -16,7 +16,7 @@ package google.registry.whois;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static google.registry.bsa.persistence.BsaLabelTestingUtils.persistBsaLabel;
import static google.registry.bsa.persistence.BsaTestingUtils.persistBsaLabel;
import static google.registry.model.EppResourceUtils.loadByForeignKeyCached;
import static google.registry.model.registrar.Registrar.State.ACTIVE;
import static google.registry.model.registrar.Registrar.Type.PDT;

View File

@@ -16,7 +16,7 @@ package google.registry.whois;
import static com.google.common.net.MediaType.PLAIN_TEXT_UTF_8;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.bsa.persistence.BsaLabelTestingUtils.persistBsaLabel;
import static google.registry.bsa.persistence.BsaTestingUtils.persistBsaLabel;
import static google.registry.model.registrar.Registrar.State.ACTIVE;
import static google.registry.testing.DatabaseHelper.createTlds;
import static google.registry.testing.DatabaseHelper.loadRegistrar;

View File

@@ -1,4 +1,4 @@
PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
/_dr/task/bsaDownload BsaDownloadAction GET,POST n API APP ADMIN
/_dr/task/bsaRefresh BsaRefreshAction GET,POST n API APP ADMIN
/_dr/task/uploadBsaUnavailableNames UploadBsaUnavailableDomainsAction POST n API APP ADMIN
/_dr/task/uploadBsaUnavailableNames UploadBsaUnavailableDomainsAction GET,POST n API APP ADMIN

View File

@@ -120,9 +120,9 @@
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"dev": true,
"funding": [
{
@@ -537,9 +537,9 @@
"dev": true
},
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
"version": "1.15.4",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
"dev": true
},
"function-bind": {

View File

@@ -22,7 +22,7 @@ apt-get upgrade -y
# Install GPG2 (in case it was not included)
apt-get install gnupg2 -y
# Install Java
apt-get install openjdk-11-jdk-headless -y
apt-get install openjdk-17-jdk-headless -y
# Install Python
apt-get install python3 -y
# As of March 2021 python3 is at v3.6. Get pip then install dataclasses
@@ -37,7 +37,7 @@ npm cache clean -f
npm install -g n
# Retrying because fails are possible for node.js intallation. See -
# https://github.com/nodejs/build/issues/1993
for i in {1..5}; do n 16.19.0 && break || sleep 15; done
for i in {1..5}; do n 20.10.0 && break || sleep 15; done
# Install gcloud
# Cribbed from https://cloud.google.com/sdk/docs/quickstart-debian-ubuntu
apt-get install lsb-release -y