Skip to content

Commit

Permalink
feat(server-portal): SEINT-409 create allowlist mechanism (#328)
Browse files Browse the repository at this point in the history
**Main logic**: When a site is premium, it uses a `premiumRPCSelector`.
If it's not, it uses public full nodes via the `stanadrdRPCSelector`.

- The allowlist is an Edge Config store on vercel. It's implemented in a
way that even if it's not defined, the portal can still run locally for
independent developers.
- Created `allowlist_checker.ts`
- Update `configuration-loader` to include `ENABLE_ALLOWLIST` and
`EDGE_CONFIG_ALLOWLIST` env variables.

---------

Co-authored-by: giac-mysten <[email protected]>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 20, 2024
1 parent 15497a3 commit 090ac9a
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 12 deletions.
30 changes: 30 additions & 0 deletions portal/server/allowlist_checker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { createClient, EdgeConfigClient } from '@vercel/edge-config';
import { config } from 'configuration_loader';

let edgeConfigAllowlistClient: EdgeConfigClient | undefined;
if (config.enableAllowlist){
edgeConfigAllowlistClient = createClient(config.edgeConfigAllowlist);
}
/**
* Check if a given subdomain is allowed to be served by the walrus site.
* @param subdomain The walrus site subdomain to inspect
* @returns true if the subdomain is allowed (has premium), false otherwise
*/
export async function isAllowed(subdomain: string): Promise<boolean> {
if (!config.enableAllowlist){
return false
}

if (!edgeConfigAllowlistClient){
throw new Error('Edge config allowlist client not initialized!')
}

const allowed: boolean = await edgeConfigAllowlistClient.has(
subdomain,
);

return allowed
}
17 changes: 5 additions & 12 deletions portal/server/app/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,19 @@
import { getDomain, getSubdomainAndPath } from "@lib/domain_parsing";
import { redirectToAggregatorUrlResponse, redirectToPortalURLResponse } from "@lib/redirects";
import { getBlobIdLink, getObjectIdLink } from "@lib/links";
import { UrlFetcher } from "@lib/url_fetcher";
import { ResourceFetcher } from "@lib/resource";
import { RPCSelector } from "@lib/rpc_selector";

import { isAllowed } from "allowlist_checker";
import { siteNotFound } from "@lib/http/http_error_responses";
import integrateLoggerWithSentry from "sentry_logger";
import blocklistChecker from "custom_blocklist_checker";
import { SuiNSResolver } from "@lib/suins";
import { WalrusSitesRouter } from "@lib/routing";
import { config } from "configuration_loader";
import { standardUrlFetcher, premiumUrlFetcher } from "url_fetcher_factory";

if (config.enableSentry) {
// Only integrate Sentry on production.
integrateLoggerWithSentry();
}

const rpcSelector = new RPCSelector(config.rpcUrlList);
const urlFetcher = new UrlFetcher(
new ResourceFetcher(rpcSelector),
new SuiNSResolver(rpcSelector),
new WalrusSitesRouter(rpcSelector)
);

export async function GET(req: Request) {
const originalUrl = req.headers.get("x-original-url");
if (!originalUrl) {
Expand Down Expand Up @@ -55,6 +45,7 @@ export async function GET(req: Request) {
return siteNotFound();
}

const urlFetcher = await isAllowed(parsedUrl.subdomain ?? '') ? premiumUrlFetcher : standardUrlFetcher;
if (requestDomain == portalDomain && parsedUrl.subdomain) {
return await urlFetcher.resolveDomainAndFetchUrl(parsedUrl, null, blocklistChecker);
}
Expand All @@ -63,6 +54,8 @@ export async function GET(req: Request) {
const atBaseUrl = portalDomain == url.host.split(":")[0];
if (atBaseUrl) {
console.log("Serving the landing page from walrus...");
// Always use the premium page fetcher for the landing page (when available).
const urlFetcher = config.enableAllowlist ? premiumUrlFetcher : standardUrlFetcher;
const response = await urlFetcher.resolveDomainAndFetchUrl(
{
subdomain: config.landingPageOidB36,
Expand Down
19 changes: 19 additions & 0 deletions portal/server/configuration_loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ function toBoolean(value: string): Boolean {
*/
export type Configuration = {
edgeConfig?: string;
edgeConfigAllowlist?: string;
enableBlocklist: Boolean;
enableAllowlist: Boolean;
landingPageOidB36: string;
portalDomainNameLength?: number;
premiumRpcUrlList: string[];
Expand All @@ -36,7 +38,9 @@ class ConfigurationLoader {
get config(): Configuration {
return {
enableBlocklist: this.loadEnableBlocklist(),
enableAllowlist: this.loadEnableAllowlist(),
edgeConfig: this.loadEdgeConfig(),
edgeConfigAllowlist: this.loadEdgeConfigAllowlist(),
landingPageOidB36: this.loadLandingPageOidB36(),
portalDomainNameLength: this.loadPortalDomainNameLength(),
premiumRpcUrlList: this.loadPremiumRpcUrlList(),
Expand All @@ -51,6 +55,10 @@ class ConfigurationLoader {
return this.loadEnableBlocklist() ? process.env.EDGE_CONFIG : undefined
}

private loadEdgeConfigAllowlist(): string | undefined {
return this.loadEnableAllowlist() ? process.env.EDGE_CONFIG_ALLOWLIST : undefined
}

private loadEnableBlocklist(): Boolean {
if (!process.env.ENABLE_BLOCKLIST) {
throw new Error('Missing ENABLE_BLOCKLIST environment variable.')
Expand All @@ -62,6 +70,17 @@ class ConfigurationLoader {
return toBoolean(enable)
}

private loadEnableAllowlist(): Boolean {
if (!process.env.ENABLE_ALLOWLIST) {
throw new Error('Missing ENABLE_ALLOWLIST environment variable.')
}
const enable = process.env.ENABLE_ALLOWLIST.toLowerCase()
if (!isStringBoolean(enable)) {
throw new Error('ENABLE_ALLOWLIST must be "true" or "false".')
}
return toBoolean(enable)
}

private loadLandingPageOidB36(): string {
const pageOidB36 = process.env.LANDING_PAGE_OID_B36
if (!pageOidB36) {
Expand Down
39 changes: 39 additions & 0 deletions portal/server/url_fetcher_factory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

import { UrlFetcher } from "@lib/url_fetcher";
import { ResourceFetcher } from "@lib/resource";
import { RPCSelector } from "@lib/rpc_selector";
import { SuiNSResolver } from "@lib/suins";
import { WalrusSitesRouter } from "@lib/routing";
import { config } from "configuration_loader";

/**
* A factory class for creating page fetchers.
* Page fetchers can be either premium or standard.
* Premium fetchers use premium RPC nodes that can serve content faster and more reliably,
* while standard fetchers use standard RPC nodes.
*/
class UrlFetcherFactory {
private static readonly premiumRpcSelector = new RPCSelector(config.premiumRpcUrlList);
private static readonly standardRpcSelector = new RPCSelector(config.rpcUrlList);

public static premiumUrlFetcher(): UrlFetcher {
return new UrlFetcher(
new ResourceFetcher(this.standardRpcSelector),
new SuiNSResolver(this.standardRpcSelector),
new WalrusSitesRouter(this.standardRpcSelector)
);
}

public static standardUrlFetcher(): UrlFetcher {
return new UrlFetcher(
new ResourceFetcher(this.premiumRpcSelector),
new SuiNSResolver(this.premiumRpcSelector),
new WalrusSitesRouter(this.premiumRpcSelector)
);
}
}

export const standardUrlFetcher = UrlFetcherFactory.standardUrlFetcher();
export const premiumUrlFetcher = UrlFetcherFactory.premiumUrlFetcher();

0 comments on commit 090ac9a

Please sign in to comment.