diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml new file mode 100644 index 0000000..79ad180 --- /dev/null +++ b/.github/workflows/stale-bot.yml @@ -0,0 +1,37 @@ +name: Mark and Close Stale Issues + +on: + schedule: + - cron: '0 0 * * *' + workflow_dispatch: # Allow manual trigger + +permissions: + contents: write # only for delete-branch option + issues: write + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v9 + with: + days-before-stale: 30 + days-before-close: 14 + stale-issue-message: 'This issue has been automatically marked as stale due to 30 days of inactivity. It will be closed in 14 days if no further activity occurs.' + close-issue-message: 'This issue has been automatically closed due to inactivity. If you believe this is still relevant, please open a new issue with up-to-date information.' + stale-issue-label: 'stale' + + exempt-issue-labels: 'needs investigating, networking, new feature, reverse proxy, bug, api, authentication, documentation, enhancement, help wanted, good first issue, question' + + exempt-all-issue-assignees: true + + only-labels: '' + exempt-pr-labels: '' + days-before-pr-stale: -1 + days-before-pr-close: -1 + + operations-per-run: 100 + remove-stale-when-updated: true + delete-branch: false + enable-statistics: true \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index daa47e8..b0798e3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ RUN npm ci COPY . . -RUN npx drizzle-kit generate --dialect sqlite --schema ./server/db/schema.ts --out init +RUN npx drizzle-kit generate --dialect sqlite --schema ./server/db/schemas/ --out init RUN npm run build @@ -16,7 +16,7 @@ FROM node:20-alpine AS runner WORKDIR /app # Curl used for the health checks -RUN apk add --no-cache curl +RUN apk add --no-cache curl COPY package.json package-lock.json ./ RUN npm ci --only=production && npm cache clean --force diff --git a/config/config.example.yml b/config/config.example.yml index d7b70a6..f3ab8d6 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -18,6 +18,9 @@ server: internal_hostname: "pangolin" session_cookie_name: "p_session_token" resource_access_token_param: "p_token" + resource_access_token_headers: + id: "P-Access-Token-Id" + token: "P-Access-Token" resource_session_request_param: "p_session_request" traefik: @@ -35,7 +38,7 @@ gerbil: rate_limits: global: window_minutes: 1 - max_requests: 100 + max_requests: 500 users: server_admin: diff --git a/drizzle.config.ts b/drizzle.config.ts index 4336cb6..dcfc55c 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -4,10 +4,10 @@ import path from "path"; export default defineConfig({ dialect: "sqlite", - schema: path.join("server", "db", "schema.ts"), + schema: path.join("server", "db", "schemas"), out: path.join("server", "migrations"), verbose: true, dbCredentials: { - url: path.join(APP_PATH, "db", "db.sqlite"), - }, + url: path.join(APP_PATH, "db", "db.sqlite") + } }); diff --git a/install/config/config.yml b/install/config/config.yml index 52be2d9..de406ee 100644 --- a/install/config/config.yml +++ b/install/config/config.yml @@ -18,6 +18,9 @@ server: internal_hostname: "pangolin" session_cookie_name: "p_session_token" resource_access_token_param: "p_token" + resource_access_token_headers: + id: "P-Access-Token-Id" + token: "P-Access-Token" resource_session_request_param: "p_session_request" cors: origins: ["https://{{.DashboardDomain}}"] diff --git a/server/apiServer.ts b/server/apiServer.ts index 2ba0ab9..824a860 100644 --- a/server/apiServer.ts +++ b/server/apiServer.ts @@ -14,7 +14,7 @@ import { logIncomingMiddleware } from "./middlewares/logIncoming"; import { csrfProtectionMiddleware } from "./middlewares/csrfProtection"; import helmet from "helmet"; -const dev = process.env.ENVIRONMENT !== "prod"; +const dev = config.isDev; const externalPort = config.getRawConfig().server.external_port; export function createApiServer() { diff --git a/server/auth/actions.ts b/server/auth/actions.ts index 4510c55..dc56ea9 100644 --- a/server/auth/actions.ts +++ b/server/auth/actions.ts @@ -1,6 +1,6 @@ import { Request } from "express"; import { db } from "@server/db"; -import { userActions, roleActions, userOrgs } from "@server/db/schema"; +import { userActions, roleActions, userOrgs } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; @@ -63,6 +63,7 @@ export enum ActionsEnum { listResourceRules = "listResourceRules", updateResourceRule = "updateResourceRule", listOrgDomains = "listOrgDomains", + createNewt = "createNewt", } export async function checkUserActionPermission( diff --git a/server/auth/canUserAccessResource.ts b/server/auth/canUserAccessResource.ts index bdafaa0..0d61825 100644 --- a/server/auth/canUserAccessResource.ts +++ b/server/auth/canUserAccessResource.ts @@ -1,6 +1,6 @@ import db from "@server/db"; import { and, eq } from "drizzle-orm"; -import { roleResources, userResources } from "@server/db/schema"; +import { roleResources, userResources } from "@server/db/schemas"; export async function canUserAccessResource({ userId, diff --git a/server/auth/checkValidInvite.ts b/server/auth/checkValidInvite.ts index 0965b59..bda12c9 100644 --- a/server/auth/checkValidInvite.ts +++ b/server/auth/checkValidInvite.ts @@ -1,5 +1,5 @@ import db from "@server/db"; -import { UserInvite, userInvites } from "@server/db/schema"; +import { UserInvite, userInvites } from "@server/db/schemas"; import { isWithinExpirationDate } from "oslo"; import { verifyPassword } from "./password"; import { eq } from "drizzle-orm"; diff --git a/server/auth/limits.ts b/server/auth/limits.ts index b5758f5..c7c1939 100644 --- a/server/auth/limits.ts +++ b/server/auth/limits.ts @@ -1,5 +1,5 @@ import { db } from '@server/db'; -import { limitsTable } from '@server/db/schema'; +import { limitsTable } from '@server/db/schemas'; import { and, eq } from 'drizzle-orm'; import createHttpError from 'http-errors'; import HttpCode from '@server/types/HttpCode'; @@ -37,4 +37,4 @@ export async function checkOrgLimit({ orgId, limitName, currentValue, increment } throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Unknown error occurred while checking limit'); } -} \ No newline at end of file +} diff --git a/server/auth/resourceOtp.ts b/server/auth/resourceOtp.ts index 33675aa..2539bf3 100644 --- a/server/auth/resourceOtp.ts +++ b/server/auth/resourceOtp.ts @@ -1,5 +1,5 @@ import db from "@server/db"; -import { resourceOtp } from "@server/db/schema"; +import { resourceOtp } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import { createDate, isWithinExpirationDate, TimeSpan } from "oslo"; import { alphabet, generateRandomString, sha256 } from "oslo/crypto"; diff --git a/server/auth/sendEmailVerificationCode.ts b/server/auth/sendEmailVerificationCode.ts index 5431876..788c135 100644 --- a/server/auth/sendEmailVerificationCode.ts +++ b/server/auth/sendEmailVerificationCode.ts @@ -1,7 +1,7 @@ import { TimeSpan, createDate } from "oslo"; import { generateRandomString, alphabet } from "oslo/crypto"; import db from "@server/db"; -import { users, emailVerificationCodes } from "@server/db/schema"; +import { users, emailVerificationCodes } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import { sendEmail } from "@server/emails"; import config from "@server/lib/config"; diff --git a/server/auth/sessions/app.ts b/server/auth/sessions/app.ts index bdd593f..be43d7a 100644 --- a/server/auth/sessions/app.ts +++ b/server/auth/sessions/app.ts @@ -9,7 +9,7 @@ import { sessions, User, users -} from "@server/db/schema"; +} from "@server/db/schemas"; import db from "@server/db"; import { eq, inArray } from "drizzle-orm"; import config from "@server/lib/config"; diff --git a/server/auth/sessions/newt.ts b/server/auth/sessions/newt.ts index 7f45ce6..7d2ef8a 100644 --- a/server/auth/sessions/newt.ts +++ b/server/auth/sessions/newt.ts @@ -2,7 +2,7 @@ import { encodeHexLowerCase, } from "@oslojs/encoding"; import { sha256 } from "@oslojs/crypto/sha2"; -import { Newt, newts, newtSessions, NewtSession } from "@server/db/schema"; +import { Newt, newts, newtSessions, NewtSession } from "@server/db/schemas"; import db from "@server/db"; import { eq } from "drizzle-orm"; diff --git a/server/auth/sessions/resource.ts b/server/auth/sessions/resource.ts index 572b0e7..b95bece 100644 --- a/server/auth/sessions/resource.ts +++ b/server/auth/sessions/resource.ts @@ -1,6 +1,6 @@ import { encodeHexLowerCase } from "@oslojs/encoding"; import { sha256 } from "@oslojs/crypto/sha2"; -import { resourceSessions, ResourceSession } from "@server/db/schema"; +import { resourceSessions, ResourceSession } from "@server/db/schemas"; import db from "@server/db"; import { eq, and } from "drizzle-orm"; import config from "@server/lib/config"; diff --git a/server/auth/totp.ts b/server/auth/totp.ts index 2bf62c3..3ca183a 100644 --- a/server/auth/totp.ts +++ b/server/auth/totp.ts @@ -1,6 +1,6 @@ import { verify } from "@node-rs/argon2"; import db from "@server/db"; -import { twoFactorBackupCodes } from "@server/db/schema"; +import { twoFactorBackupCodes } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import { decodeHex } from "oslo/encoding"; import { TOTPController } from "oslo/otp"; diff --git a/server/auth/verifyResourceAccessToken.ts b/server/auth/verifyResourceAccessToken.ts index ce74952..8ddb501 100644 --- a/server/auth/verifyResourceAccessToken.ts +++ b/server/auth/verifyResourceAccessToken.ts @@ -3,53 +3,95 @@ import { Resource, ResourceAccessToken, resourceAccessToken, -} from "@server/db/schema"; + resources +} from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import { isWithinExpirationDate } from "oslo"; import { verifyPassword } from "./password"; +import { encodeHexLowerCase } from "@oslojs/encoding"; +import { sha256 } from "@oslojs/crypto/sha2"; export async function verifyResourceAccessToken({ - resource, + accessToken, accessTokenId, - accessToken + resourceId }: { - resource: Resource; - accessTokenId: string; accessToken: string; + accessTokenId?: string; + resourceId?: number; // IF THIS IS NOT SET, THE TOKEN IS VALID FOR ALL RESOURCES }): Promise<{ valid: boolean; error?: string; tokenItem?: ResourceAccessToken; + resource?: Resource; }> { - const [result] = await db - .select() - .from(resourceAccessToken) - .where( - and( - eq(resourceAccessToken.resourceId, resource.resourceId), - eq(resourceAccessToken.accessTokenId, accessTokenId) - ) - ) - .limit(1); + const accessTokenHash = encodeHexLowerCase( + sha256(new TextEncoder().encode(accessToken)) + ); - const tokenItem = result; + let tokenItem: ResourceAccessToken | undefined; + let resource: Resource | undefined; - if (!tokenItem) { + if (!accessTokenId) { + const [res] = await db + .select() + .from(resourceAccessToken) + .where(and(eq(resourceAccessToken.tokenHash, accessTokenHash))) + .innerJoin( + resources, + eq(resourceAccessToken.resourceId, resources.resourceId) + ); + + tokenItem = res?.resourceAccessToken; + resource = res?.resources; + } else { + const [res] = await db + .select() + .from(resourceAccessToken) + .where(and(eq(resourceAccessToken.accessTokenId, accessTokenId))) + .innerJoin( + resources, + eq(resourceAccessToken.resourceId, resources.resourceId) + ); + + if (res && res.resourceAccessToken) { + if (res.resourceAccessToken.tokenHash?.startsWith("$argon")) { + const validCode = await verifyPassword( + accessToken, + res.resourceAccessToken.tokenHash + ); + + if (!validCode) { + return { + valid: false, + error: "Invalid access token" + }; + } + } else { + const tokenHash = encodeHexLowerCase( + sha256(new TextEncoder().encode(accessToken)) + ); + + if (res.resourceAccessToken.tokenHash !== tokenHash) { + return { + valid: false, + error: "Invalid access token" + }; + } + } + } + + tokenItem = res?.resourceAccessToken; + resource = res?.resources; + } + + if (!tokenItem || !resource) { return { valid: false, error: "Access token does not exist for resource" }; } - const validCode = await verifyPassword(accessToken, tokenItem.tokenHash); - - if (!validCode) { - return { - valid: false, - error: "Invalid access token" - }; - } - if ( tokenItem.expiresAt && !isWithinExpirationDate(new Date(tokenItem.expiresAt)) @@ -60,8 +102,16 @@ export async function verifyResourceAccessToken({ }; } + if (resourceId && resource.resourceId !== resourceId) { + return { + valid: false, + error: "Resource ID does not match" + }; + } + return { valid: true, - tokenItem + tokenItem, + resource }; } diff --git a/server/db/index.ts b/server/db/index.ts index 5dc1360..6cf40fe 100644 --- a/server/db/index.ts +++ b/server/db/index.ts @@ -1,6 +1,6 @@ import { drizzle } from "drizzle-orm/better-sqlite3"; import Database from "better-sqlite3"; -import * as schema from "@server/db/schema"; +import * as schema from "@server/db/schemas"; import path from "path"; import fs from "fs/promises"; import { APP_PATH } from "@server/lib/consts"; diff --git a/server/db/names.ts b/server/db/names.ts index 6976d64..21a37c9 100644 --- a/server/db/names.ts +++ b/server/db/names.ts @@ -1,7 +1,7 @@ import { join } from "path"; import { readFileSync } from "fs"; import { db } from "@server/db"; -import { exitNodes, sites } from "./schema"; +import { exitNodes, sites } from "./schemas/schema"; import { eq, and } from "drizzle-orm"; import { __DIRNAME } from "@server/lib/consts"; diff --git a/server/db/schemas/index.ts b/server/db/schemas/index.ts new file mode 100644 index 0000000..686fbd9 --- /dev/null +++ b/server/db/schemas/index.ts @@ -0,0 +1 @@ +export * from "./schema"; diff --git a/server/db/schema.ts b/server/db/schemas/schema.ts similarity index 99% rename from server/db/schema.ts rename to server/db/schemas/schema.ts index 02fe02e..a862755 100644 --- a/server/db/schema.ts +++ b/server/db/schemas/schema.ts @@ -76,7 +76,8 @@ export const resources = sqliteTable("resources", { isBaseDomain: integer("isBaseDomain", { mode: "boolean" }), applyRules: integer("applyRules", { mode: "boolean" }) .notNull() - .default(false) + .default(false), + enabled: integer("enabled", { mode: "boolean" }).notNull().default(true) }); export const targets = sqliteTable("targets", { diff --git a/server/index.ts b/server/index.ts index fd56f4e..b3dc044 100644 --- a/server/index.ts +++ b/server/index.ts @@ -2,7 +2,7 @@ import { runSetupFunctions } from "./setup"; import { createApiServer } from "./apiServer"; import { createNextServer } from "./nextServer"; import { createInternalServer } from "./internalServer"; -import { Session, User, UserOrg } from "./db/schema"; +import { Session, User, UserOrg } from "./db/schemas/schema"; async function startServers() { await runSetupFunctions(); diff --git a/server/lib/canUserAccessResource.ts b/server/lib/canUserAccessResource.ts index bdafaa0..0d61825 100644 --- a/server/lib/canUserAccessResource.ts +++ b/server/lib/canUserAccessResource.ts @@ -1,6 +1,6 @@ import db from "@server/db"; import { and, eq } from "drizzle-orm"; -import { roleResources, userResources } from "@server/db/schema"; +import { roleResources, userResources } from "@server/db/schemas"; export async function canUserAccessResource({ userId, diff --git a/server/lib/config.ts b/server/lib/config.ts index e667719..f6f4c44 100644 --- a/server/lib/config.ts +++ b/server/lib/config.ts @@ -11,7 +11,7 @@ import { import { passwordSchema } from "@server/auth/passwordSchema"; import stoi from "./stoi"; import db from "@server/db"; -import { SupporterKey, supporterKey } from "@server/db/schema"; +import { SupporterKey, supporterKey } from "@server/db/schemas"; import { suppressDeprecationWarnings } from "moment"; import { eq } from "drizzle-orm"; @@ -66,6 +66,10 @@ const configSchema = z.object({ internal_hostname: z.string().transform((url) => url.toLowerCase()), session_cookie_name: z.string(), resource_access_token_param: z.string(), + resource_access_token_headers: z.object({ + id: z.string(), + token: z.string() + }), resource_session_request_param: z.string(), dashboard_session_length_hours: z .number() @@ -163,6 +167,8 @@ export class Config { supporterHiddenUntil: number | null = null; + isDev: boolean = process.env.ENVIRONMENT !== "prod"; + constructor() { this.loadConfig(); } @@ -237,6 +243,10 @@ export class Config { : "false"; process.env.RESOURCE_ACCESS_TOKEN_PARAM = parsedConfig.data.server.resource_access_token_param; + process.env.RESOURCE_ACCESS_TOKEN_HEADERS_ID = + parsedConfig.data.server.resource_access_token_headers.id; + process.env.RESOURCE_ACCESS_TOKEN_HEADERS_TOKEN = + parsedConfig.data.server.resource_access_token_headers.token; process.env.RESOURCE_SESSION_REQUEST_PARAM = parsedConfig.data.server.resource_session_request_param; process.env.FLAGS_ALLOW_BASE_DOMAIN_RESOURCES = parsedConfig.data.flags @@ -245,7 +255,9 @@ export class Config { : "false"; process.env.DASHBOARD_URL = parsedConfig.data.app.dashboard_url; - this.checkSupporterKey(); + if (!this.isDev) { + this.checkSupporterKey(); + } this.rawConfig = parsedConfig.data; } @@ -331,13 +343,13 @@ export class Config { // update the supporter key in the database await db - .update(supporterKey) - .set({ - tier: data.data.tier || null, - phrase: data.data.cutePhrase || null, - valid: true - }) - .where(eq(supporterKey.keyId, key.keyId)); + .update(supporterKey) + .set({ + tier: data.data.tier || null, + phrase: data.data.cutePhrase || null, + valid: true + }) + .where(eq(supporterKey.keyId, key.keyId)); } catch (e) { this.supporterData = key; console.error("Failed to validate supporter key", e); diff --git a/server/lib/consts.ts b/server/lib/consts.ts index 87468c2..7c9892b 100644 --- a/server/lib/consts.ts +++ b/server/lib/consts.ts @@ -2,7 +2,7 @@ import path from "path"; import { fileURLToPath } from "url"; // This is a placeholder value replaced by the build process -export const APP_VERSION = "1.1.0"; +export const APP_VERSION = "1.2.0"; export const __FILENAME = fileURLToPath(import.meta.url); export const __DIRNAME = path.dirname(__FILENAME); diff --git a/server/middlewares/getUserOrgs.ts b/server/middlewares/getUserOrgs.ts index 875492a..7d5c08f 100644 --- a/server/middlewares/getUserOrgs.ts +++ b/server/middlewares/getUserOrgs.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { userOrgs, orgs } from "@server/db/schema"; +import { userOrgs, orgs } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/middlewares/verifyAccessTokenAccess.ts b/server/middlewares/verifyAccessTokenAccess.ts index 3b7e312..a437a8a 100644 --- a/server/middlewares/verifyAccessTokenAccess.ts +++ b/server/middlewares/verifyAccessTokenAccess.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { resourceAccessToken, resources, userOrgs } from "@server/db/schema"; +import { resourceAccessToken, resources, userOrgs } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/middlewares/verifyAdmin.ts b/server/middlewares/verifyAdmin.ts index b2a773e..b53f238 100644 --- a/server/middlewares/verifyAdmin.ts +++ b/server/middlewares/verifyAdmin.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { roles, userOrgs } from "@server/db/schema"; +import { roles, userOrgs } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/middlewares/verifyOrgAccess.ts b/server/middlewares/verifyOrgAccess.ts index 595a724..20018e0 100644 --- a/server/middlewares/verifyOrgAccess.ts +++ b/server/middlewares/verifyOrgAccess.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { userOrgs } from "@server/db/schema"; +import { userOrgs } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/middlewares/verifyResourceAccess.ts b/server/middlewares/verifyResourceAccess.ts index adbc1c0..dc5fcc2 100644 --- a/server/middlewares/verifyResourceAccess.ts +++ b/server/middlewares/verifyResourceAccess.ts @@ -5,7 +5,7 @@ import { userOrgs, userResources, roleResources, -} from "@server/db/schema"; +} from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/middlewares/verifyRoleAccess.ts b/server/middlewares/verifyRoleAccess.ts index 29e0cfb..5491704 100644 --- a/server/middlewares/verifyRoleAccess.ts +++ b/server/middlewares/verifyRoleAccess.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { roles, userOrgs } from "@server/db/schema"; +import { roles, userOrgs } from "@server/db/schemas"; import { and, eq, inArray } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; @@ -44,6 +44,8 @@ export async function verifyRoleAccess( ); } + const orgIds = new Set(rolesData.map((role) => role.orgId)); + // Check user access to each role's organization for (const role of rolesData) { const userOrgRole = await db @@ -69,7 +71,16 @@ export async function verifyRoleAccess( req.userOrgId = role.orgId; } - const orgId = req.userOrgId; + if (orgIds.size > 1) { + return next( + createHttpError( + HttpCode.FORBIDDEN, + "Roles must belong to the same organization" + ) + ); + } + + const orgId = orgIds.values().next().value; if (!orgId) { return next( @@ -105,3 +116,4 @@ export async function verifyRoleAccess( ); } } + diff --git a/server/middlewares/verifySession.ts b/server/middlewares/verifySession.ts index a74c793..9d28439 100644 --- a/server/middlewares/verifySession.ts +++ b/server/middlewares/verifySession.ts @@ -1,7 +1,7 @@ import { NextFunction, Response } from "express"; import ErrorResponse from "@server/types/ErrorResponse"; import { db } from "@server/db"; -import { users } from "@server/db/schema"; +import { users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/middlewares/verifySetResourceUsers.ts b/server/middlewares/verifySetResourceUsers.ts index 5ed0a4e..0f35106 100644 --- a/server/middlewares/verifySetResourceUsers.ts +++ b/server/middlewares/verifySetResourceUsers.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { userOrgs } from "@server/db/schema"; +import { userOrgs } from "@server/db/schemas"; import { and, eq, inArray, or } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/middlewares/verifySiteAccess.ts b/server/middlewares/verifySiteAccess.ts index dcf35f6..b741e3a 100644 --- a/server/middlewares/verifySiteAccess.ts +++ b/server/middlewares/verifySiteAccess.ts @@ -6,7 +6,7 @@ import { userSites, roleSites, roles, -} from "@server/db/schema"; +} from "@server/db/schemas"; import { and, eq, or } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/middlewares/verifyTargetAccess.ts b/server/middlewares/verifyTargetAccess.ts index 865e7e5..f57ba47 100644 --- a/server/middlewares/verifyTargetAccess.ts +++ b/server/middlewares/verifyTargetAccess.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { resources, targets, userOrgs } from "@server/db/schema"; +import { resources, targets, userOrgs } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/middlewares/verifyUser.ts b/server/middlewares/verifyUser.ts index 7268035..06b0860 100644 --- a/server/middlewares/verifyUser.ts +++ b/server/middlewares/verifyUser.ts @@ -1,7 +1,7 @@ import { NextFunction, Response } from "express"; import ErrorResponse from "@server/types/ErrorResponse"; import { db } from "@server/db"; -import { users } from "@server/db/schema"; +import { users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/middlewares/verifyUserAccess.ts b/server/middlewares/verifyUserAccess.ts index f983f05..43ec9cf 100644 --- a/server/middlewares/verifyUserAccess.ts +++ b/server/middlewares/verifyUserAccess.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { userOrgs } from "@server/db/schema"; +import { userOrgs } from "@server/db/schemas"; import { and, eq, or } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/middlewares/verifyUserIsOrgOwner.ts b/server/middlewares/verifyUserIsOrgOwner.ts index 49ddafc..ac96f37 100644 --- a/server/middlewares/verifyUserIsOrgOwner.ts +++ b/server/middlewares/verifyUserIsOrgOwner.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { userOrgs } from "@server/db/schema"; +import { userOrgs } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; @@ -55,7 +55,7 @@ export async function verifyUserIsOrgOwner( ) ); } - + return next(); } catch (e) { return next( diff --git a/server/routers/accessToken/deleteAccessToken.ts b/server/routers/accessToken/deleteAccessToken.ts index b9bbd42..b7aa83d 100644 --- a/server/routers/accessToken/deleteAccessToken.ts +++ b/server/routers/accessToken/deleteAccessToken.ts @@ -5,7 +5,7 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; import { fromError } from "zod-validation-error"; -import { resourceAccessToken } from "@server/db/schema"; +import { resourceAccessToken } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import db from "@server/db"; diff --git a/server/routers/accessToken/generateAccessToken.ts b/server/routers/accessToken/generateAccessToken.ts index 9037f5f..bb67387 100644 --- a/server/routers/accessToken/generateAccessToken.ts +++ b/server/routers/accessToken/generateAccessToken.ts @@ -9,7 +9,7 @@ import { ResourceAccessToken, resourceAccessToken, resources -} from "@server/db/schema"; +} from "@server/db/schemas"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; import { eq } from "drizzle-orm"; @@ -20,6 +20,8 @@ import { fromError } from "zod-validation-error"; import logger from "@server/logger"; import { createDate, TimeSpan } from "oslo"; import { hashPassword } from "@server/auth/password"; +import { encodeHexLowerCase } from "@oslojs/encoding"; +import { sha256 } from "@oslojs/crypto/sha2"; export const generateAccessTokenBodySchema = z .object({ @@ -90,11 +92,13 @@ export async function generateAccessToken( ? createDate(new TimeSpan(validForSeconds, "s")).getTime() : undefined; - const token = generateIdFromEntropySize(25); + const token = generateIdFromEntropySize(16); - const tokenHash = await hashPassword(token); + const tokenHash = encodeHexLowerCase( + sha256(new TextEncoder().encode(token)) + ); - const id = generateId(15); + const id = generateId(8); const [result] = await db .insert(resourceAccessToken) .values({ diff --git a/server/routers/accessToken/listAccessTokens.ts b/server/routers/accessToken/listAccessTokens.ts index c3874c0..a6dcff6 100644 --- a/server/routers/accessToken/listAccessTokens.ts +++ b/server/routers/accessToken/listAccessTokens.ts @@ -7,13 +7,14 @@ import { roleResources, resourceAccessToken, sites -} from "@server/db/schema"; +} from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import { sql, eq, or, inArray, and, count, isNull, lt, gt } from "drizzle-orm"; import logger from "@server/logger"; import stoi from "@server/lib/stoi"; +import { fromZodError } from "zod-validation-error"; const listAccessTokensParamsSchema = z .object({ @@ -133,7 +134,7 @@ export async function listAccessTokens( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedQuery.error.errors.map((e) => e.message).join(", ") + fromZodError(parsedQuery.error) ) ); } @@ -144,7 +145,7 @@ export async function listAccessTokens( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedParams.error.errors.map((e) => e.message).join(", ") + fromZodError(parsedParams.error) ) ); } diff --git a/server/routers/auth/changePassword.ts b/server/routers/auth/changePassword.ts index eba4497..3be9ef2 100644 --- a/server/routers/auth/changePassword.ts +++ b/server/routers/auth/changePassword.ts @@ -4,7 +4,7 @@ import HttpCode from "@server/types/HttpCode"; import { fromError } from "zod-validation-error"; import { z } from "zod"; import { db } from "@server/db"; -import { User, users } from "@server/db/schema"; +import { User, users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import { response } from "@server/lib"; import { diff --git a/server/routers/auth/disable2fa.ts b/server/routers/auth/disable2fa.ts index b93e9cc..4564446 100644 --- a/server/routers/auth/disable2fa.ts +++ b/server/routers/auth/disable2fa.ts @@ -4,7 +4,7 @@ import HttpCode from "@server/types/HttpCode"; import { fromError } from "zod-validation-error"; import { z } from "zod"; import { db } from "@server/db"; -import { User, users } from "@server/db/schema"; +import { User, users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import { response } from "@server/lib"; import { verifyPassword } from "@server/auth/password"; diff --git a/server/routers/auth/login.ts b/server/routers/auth/login.ts index dc572f4..aa4f0d5 100644 --- a/server/routers/auth/login.ts +++ b/server/routers/auth/login.ts @@ -4,7 +4,7 @@ import { serializeSessionCookie } from "@server/auth/sessions/app"; import db from "@server/db"; -import { users } from "@server/db/schema"; +import { users } from "@server/db/schemas"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; import { eq } from "drizzle-orm"; diff --git a/server/routers/auth/requestEmailVerificationCode.ts b/server/routers/auth/requestEmailVerificationCode.ts index 3153cf7..47747a9 100644 --- a/server/routers/auth/requestEmailVerificationCode.ts +++ b/server/routers/auth/requestEmailVerificationCode.ts @@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express"; import createHttpError from "http-errors"; import HttpCode from "@server/types/HttpCode"; import { response } from "@server/lib"; -import { User } from "@server/db/schema"; +import { User } from "@server/db/schemas"; import { sendEmailVerificationCode } from "../../auth/sendEmailVerificationCode"; import config from "@server/lib/config"; import logger from "@server/logger"; diff --git a/server/routers/auth/requestPasswordReset.ts b/server/routers/auth/requestPasswordReset.ts index 1e8e527..20a6511 100644 --- a/server/routers/auth/requestPasswordReset.ts +++ b/server/routers/auth/requestPasswordReset.ts @@ -5,7 +5,7 @@ import { fromError } from "zod-validation-error"; import HttpCode from "@server/types/HttpCode"; import { response } from "@server/lib"; import { db } from "@server/db"; -import { passwordResetTokens, users } from "@server/db/schema"; +import { passwordResetTokens, users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import { alphabet, generateRandomString, sha256 } from "oslo/crypto"; import { createDate } from "oslo"; diff --git a/server/routers/auth/requestTotpSecret.ts b/server/routers/auth/requestTotpSecret.ts index 4926e51..c60904c 100644 --- a/server/routers/auth/requestTotpSecret.ts +++ b/server/routers/auth/requestTotpSecret.ts @@ -6,7 +6,7 @@ import { encodeHex } from "oslo/encoding"; import HttpCode from "@server/types/HttpCode"; import { response } from "@server/lib"; import { db } from "@server/db"; -import { User, users } from "@server/db/schema"; +import { User, users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import { createTOTPKeyURI } from "oslo/otp"; import logger from "@server/logger"; diff --git a/server/routers/auth/resetPassword.ts b/server/routers/auth/resetPassword.ts index ac1b660..967ddc6 100644 --- a/server/routers/auth/resetPassword.ts +++ b/server/routers/auth/resetPassword.ts @@ -6,7 +6,7 @@ import { fromError } from "zod-validation-error"; import HttpCode from "@server/types/HttpCode"; import { response } from "@server/lib"; import { db } from "@server/db"; -import { passwordResetTokens, users } from "@server/db/schema"; +import { passwordResetTokens, users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import { hashPassword, verifyPassword } from "@server/auth/password"; import { verifyTotpCode } from "@server/auth/totp"; diff --git a/server/routers/auth/signup.ts b/server/routers/auth/signup.ts index cb09916..833850c 100644 --- a/server/routers/auth/signup.ts +++ b/server/routers/auth/signup.ts @@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from "express"; import db from "@server/db"; import HttpCode from "@server/types/HttpCode"; import { z } from "zod"; -import { users } from "@server/db/schema"; +import { users } from "@server/db/schemas"; import { fromError } from "zod-validation-error"; import createHttpError from "http-errors"; import response from "@server/lib/response"; diff --git a/server/routers/auth/verifyEmail.ts b/server/routers/auth/verifyEmail.ts index e189e9a..fd7aa13 100644 --- a/server/routers/auth/verifyEmail.ts +++ b/server/routers/auth/verifyEmail.ts @@ -5,7 +5,7 @@ import { fromError } from "zod-validation-error"; import HttpCode from "@server/types/HttpCode"; import { response } from "@server/lib"; import { db } from "@server/db"; -import { User, emailVerificationCodes, users } from "@server/db/schema"; +import { User, emailVerificationCodes, users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import { isWithinExpirationDate } from "oslo"; import config from "@server/lib/config"; diff --git a/server/routers/auth/verifyTotp.ts b/server/routers/auth/verifyTotp.ts index 36bbf34..a349d79 100644 --- a/server/routers/auth/verifyTotp.ts +++ b/server/routers/auth/verifyTotp.ts @@ -5,7 +5,7 @@ import { fromError } from "zod-validation-error"; import HttpCode from "@server/types/HttpCode"; import { response } from "@server/lib"; import { db } from "@server/db"; -import { twoFactorBackupCodes, User, users } from "@server/db/schema"; +import { twoFactorBackupCodes, User, users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import { alphabet, generateRandomString } from "oslo/crypto"; import { hashPassword } from "@server/auth/password"; diff --git a/server/routers/badger/exchangeSession.ts b/server/routers/badger/exchangeSession.ts index ad8eb97..a920842 100644 --- a/server/routers/badger/exchangeSession.ts +++ b/server/routers/badger/exchangeSession.ts @@ -4,7 +4,7 @@ import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; import logger from "@server/logger"; -import { resourceAccessToken, resources, sessions } from "@server/db/schema"; +import { resourceAccessToken, resources, sessions } from "@server/db/schemas"; import db from "@server/db"; import { eq } from "drizzle-orm"; import { diff --git a/server/routers/badger/verifySession.ts b/server/routers/badger/verifySession.ts index 323129f..0c2e649 100644 --- a/server/routers/badger/verifySession.ts +++ b/server/routers/badger/verifySession.ts @@ -21,7 +21,7 @@ import { userOrgs, userResources, users -} from "@server/db/schema"; +} from "@server/db/schemas"; import config from "@server/lib/config"; import { isIpInCidr } from "@server/lib/ip"; import { response } from "@server/lib/response"; @@ -41,12 +41,13 @@ const cache = new NodeCache({ const verifyResourceSessionSchema = z.object({ sessions: z.record(z.string()).optional(), + headers: z.record(z.string()).optional(), + query: z.record(z.string()).optional(), originalRequestURL: z.string().url(), scheme: z.string(), host: z.string(), path: z.string(), method: z.string(), - accessToken: z.string().optional(), tls: z.boolean(), requestIp: z.string().optional() }); @@ -85,7 +86,8 @@ export async function verifyResourceSession( originalRequestURL, requestIp, path, - accessToken: token + headers, + query } = parsedBody.data; const clientIp = requestIp?.split(":")[0]; @@ -183,12 +185,33 @@ export async function verifyResourceSession( resource.resourceId )}?redirect=${encodeURIComponent(originalRequestURL)}`; - // check for access token - let validAccessToken: ResourceAccessToken | undefined; - if (token) { - const [accessTokenId, accessToken] = token.split("."); + // check for access token in headers + if ( + headers && + headers[ + config.getRawConfig().server.resource_access_token_headers.id + ] && + headers[ + config.getRawConfig().server.resource_access_token_headers.token + ] + ) { + const accessTokenId = + headers[ + config.getRawConfig().server.resource_access_token_headers + .id + ]; + const accessToken = + headers[ + config.getRawConfig().server.resource_access_token_headers + .token + ]; + const { valid, error, tokenItem } = await verifyResourceAccessToken( - { resource, accessTokenId, accessToken } + { + accessToken, + accessTokenId, + resourceId: resource.resourceId + } ); if (error) { @@ -206,16 +229,44 @@ export async function verifyResourceSession( } if (valid && tokenItem) { - validAccessToken = tokenItem; + return allowed(res); + } + } - if (!sessions) { - return await createAccessTokenSession( - res, - resource, - tokenItem + if ( + query && + query[config.getRawConfig().server.resource_access_token_param] + ) { + const token = + query[config.getRawConfig().server.resource_access_token_param]; + + const [accessTokenId, accessToken] = token.split("."); + + const { valid, error, tokenItem } = await verifyResourceAccessToken( + { + accessToken, + accessTokenId, + resourceId: resource.resourceId + } + ); + + if (error) { + logger.debug("Access token invalid: " + error); + } + + if (!valid) { + if (config.getRawConfig().app.log_failed_attempts) { + logger.info( + `Resource access token is invalid. Resource ID: ${ + resource.resourceId + }. IP: ${clientIp}.` ); } } + + if (valid && tokenItem) { + return allowed(res); + } } if (!sessions) { @@ -321,16 +372,6 @@ export async function verifyResourceSession( } } - // At this point we have checked all sessions, but since the access token is - // valid, we should allow access and create a new session. - if (validAccessToken) { - return await createAccessTokenSession( - res, - resource, - validAccessToken - ); - } - logger.debug("No more auth to check, resource not allowed"); if (config.getRawConfig().app.log_failed_attempts) { @@ -360,8 +401,7 @@ function extractResourceSessionToken( ssl ? "_s" : "" }`; - const all: { cookieName: string; token: string; priority: number }[] = - []; + const all: { cookieName: string; token: string; priority: number }[] = []; for (const [key, value] of Object.entries(sessions)) { const parts = key.split("."); diff --git a/server/routers/domain/listDomains.ts b/server/routers/domain/listDomains.ts index c44274e..a1cbbb3 100644 --- a/server/routers/domain/listDomains.ts +++ b/server/routers/domain/listDomains.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { domains, orgDomains, users } from "@server/db/schema"; +import { domains, orgDomains, users } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; @@ -80,15 +80,15 @@ export async function listDomains( const { orgId } = parsedParams.data; - const domains = await queryDomains(orgId.toString(), limit, offset); + const domainsList = await queryDomains(orgId.toString(), limit, offset); const [{ count }] = await db .select({ count: sql`count(*)` }) - .from(users); + .from(domains); return response(res, { data: { - domains, + domains: domainsList, pagination: { total: count, limit, diff --git a/server/routers/external.ts b/server/routers/external.ts index 2eeae9d..91a2199 100644 --- a/server/routers/external.ts +++ b/server/routers/external.ts @@ -383,7 +383,10 @@ authenticated.get( authenticated.get(`/org/:orgId/overview`, verifyOrgAccess, org.getOrgOverview); -authenticated.post(`/supporter-key/validate`, supporterKey.validateSupporterKey); +authenticated.post( + `/supporter-key/validate`, + supporterKey.validateSupporterKey +); authenticated.post(`/supporter-key/hide`, supporterKey.hideSupporterKey); unauthenticated.get("/resource/:resourceId/auth", resource.getResourceAuthInfo); @@ -470,7 +473,11 @@ authenticated.delete( // role.removeRoleAction // ); -authenticated.put("/newt", createNewt); +// authenticated.put( +// "/newt", +// verifyUserHasAction(ActionsEnum.createNewt), +// createNewt +// ); // Auth routes export const authRouter = Router(); @@ -559,3 +566,8 @@ authRouter.post( "/resource/:resourceId/access-token", resource.authWithAccessToken ); + +authRouter.post( + "/access-token", + resource.authWithAccessToken +); diff --git a/server/routers/gerbil/getConfig.ts b/server/routers/gerbil/getConfig.ts index 28b576d..ee742c2 100644 --- a/server/routers/gerbil/getConfig.ts +++ b/server/routers/gerbil/getConfig.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from 'express'; import { z } from 'zod'; -import { sites, resources, targets, exitNodes } from '@server/db/schema'; +import { sites, resources, targets, exitNodes } from '@server/db/schemas'; import { db } from '@server/db'; import { eq } from 'drizzle-orm'; import response from "@server/lib/response"; diff --git a/server/routers/gerbil/peers.ts b/server/routers/gerbil/peers.ts index 6aaeae0..47527ea 100644 --- a/server/routers/gerbil/peers.ts +++ b/server/routers/gerbil/peers.ts @@ -1,7 +1,7 @@ import axios from 'axios'; import logger from '@server/logger'; import db from '@server/db'; -import { exitNodes } from '@server/db/schema'; +import { exitNodes } from '@server/db/schemas'; import { eq } from 'drizzle-orm'; export async function addPeer(exitNodeId: number, peer: { @@ -52,4 +52,4 @@ export async function deletePeer(exitNodeId: number, publicKey: string) { } throw error; } -} \ No newline at end of file +} diff --git a/server/routers/gerbil/receiveBandwidth.ts b/server/routers/gerbil/receiveBandwidth.ts index f2569b5..a6c1e79 100644 --- a/server/routers/gerbil/receiveBandwidth.ts +++ b/server/routers/gerbil/receiveBandwidth.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { DrizzleError, eq } from "drizzle-orm"; -import { sites, resources, targets, exitNodes } from "@server/db/schema"; +import { sites, resources, targets, exitNodes } from "@server/db/schemas"; import db from "@server/db"; import logger from "@server/logger"; import createHttpError from "http-errors"; diff --git a/server/routers/newt/createNewt.ts b/server/routers/newt/createNewt.ts index d43c4cc..25f4bb3 100644 --- a/server/routers/newt/createNewt.ts +++ b/server/routers/newt/createNewt.ts @@ -3,7 +3,7 @@ import db from "@server/db"; import { hash } from "@node-rs/argon2"; import HttpCode from "@server/types/HttpCode"; import { z } from "zod"; -import { newts } from "@server/db/schema"; +import { newts } from "@server/db/schemas"; import createHttpError from "http-errors"; import response from "@server/lib/response"; import { SqliteError } from "better-sqlite3"; diff --git a/server/routers/newt/getToken.ts b/server/routers/newt/getToken.ts index e6ae0cd..7bf89eb 100644 --- a/server/routers/newt/getToken.ts +++ b/server/routers/newt/getToken.ts @@ -1,6 +1,6 @@ import { generateSessionToken } from "@server/auth/sessions/app"; import db from "@server/db"; -import { newts } from "@server/db/schema"; +import { newts } from "@server/db/schemas"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; import { eq } from "drizzle-orm"; diff --git a/server/routers/newt/handleRegisterMessage.ts b/server/routers/newt/handleRegisterMessage.ts index 11b3347..bf64e3e 100644 --- a/server/routers/newt/handleRegisterMessage.ts +++ b/server/routers/newt/handleRegisterMessage.ts @@ -6,7 +6,7 @@ import { sites, Target, targets -} from "@server/db/schema"; +} from "@server/db/schemas"; import { eq, and, sql, inArray } from "drizzle-orm"; import { addPeer, deletePeer } from "../gerbil/peers"; import logger from "@server/logger"; diff --git a/server/routers/newt/targets.ts b/server/routers/newt/targets.ts index 2c1143e..f2f5dc4 100644 --- a/server/routers/newt/targets.ts +++ b/server/routers/newt/targets.ts @@ -1,4 +1,4 @@ -import { Target } from "@server/db/schema"; +import { Target } from "@server/db/schemas"; import { sendToClient } from "../ws"; export function addTargets( diff --git a/server/routers/org/checkId.ts b/server/routers/org/checkId.ts index 475c967..40a347a 100644 --- a/server/routers/org/checkId.ts +++ b/server/routers/org/checkId.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { orgs } from "@server/db/schema"; +import { orgs } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/org/createOrg.ts b/server/routers/org/createOrg.ts index 381ce20..d264eac 100644 --- a/server/routers/org/createOrg.ts +++ b/server/routers/org/createOrg.ts @@ -10,7 +10,7 @@ import { roleActions, roles, userOrgs -} from "@server/db/schema"; +} from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; @@ -27,7 +27,7 @@ const createOrgSchema = z }) .strict(); -const MAX_ORGS = 5; +// const MAX_ORGS = 5; export async function createOrg( req: Request, @@ -57,15 +57,15 @@ export async function createOrg( ); } - const userOrgIds = req.userOrgIds; - if (userOrgIds && userOrgIds.length > MAX_ORGS) { - return next( - createHttpError( - HttpCode.FORBIDDEN, - `Maximum number of organizations reached.` - ) - ); - } + // const userOrgIds = req.userOrgIds; + // if (userOrgIds && userOrgIds.length > MAX_ORGS) { + // return next( + // createHttpError( + // HttpCode.FORBIDDEN, + // `Maximum number of organizations reached.` + // ) + // ); + // } const { orgId, name } = parsedBody.data; diff --git a/server/routers/org/deleteOrg.ts b/server/routers/org/deleteOrg.ts index a796588..5ffdd73 100644 --- a/server/routers/org/deleteOrg.ts +++ b/server/routers/org/deleteOrg.ts @@ -7,7 +7,7 @@ import { orgs, sites, userActions -} from "@server/db/schema"; +} from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/org/getOrg.ts b/server/routers/org/getOrg.ts index 0deb476..21a6fa2 100644 --- a/server/routers/org/getOrg.ts +++ b/server/routers/org/getOrg.ts @@ -1,12 +1,13 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { Org, orgs } from "@server/db/schema"; +import { Org, orgs } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; +import { fromZodError } from "zod-validation-error"; const getOrgSchema = z .object({ @@ -29,7 +30,7 @@ export async function getOrg( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedParams.error.errors.map((e) => e.message).join(", ") + fromZodError(parsedParams.error) ) ); } diff --git a/server/routers/org/getOrgOverview.ts b/server/routers/org/getOrgOverview.ts index dbaa157..dcde292 100644 --- a/server/routers/org/getOrgOverview.ts +++ b/server/routers/org/getOrgOverview.ts @@ -10,12 +10,13 @@ import { userResources, users, userSites -} from "@server/db/schema"; +} from "@server/db/schemas"; import { and, count, eq, inArray } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import logger from "@server/logger"; +import { fromZodError } from "zod-validation-error"; const getOrgParamsSchema = z .object({ @@ -45,7 +46,7 @@ export async function getOrgOverview( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedParams.error.errors.map((e) => e.message).join(", ") + fromZodError(parsedParams.error) ) ); } diff --git a/server/routers/org/listOrgs.ts b/server/routers/org/listOrgs.ts index 6c1274e..106c58e 100644 --- a/server/routers/org/listOrgs.ts +++ b/server/routers/org/listOrgs.ts @@ -1,12 +1,13 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { Org, orgs } from "@server/db/schema"; +import { Org, orgs } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import { sql, inArray } from "drizzle-orm"; import logger from "@server/logger"; +import { fromZodError } from "zod-validation-error"; const listOrgsSchema = z.object({ limit: z @@ -39,7 +40,7 @@ export async function listOrgs( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedQuery.error.errors.map((e) => e.message).join(", ") + fromZodError(parsedQuery.error) ) ); } diff --git a/server/routers/org/updateOrg.ts b/server/routers/org/updateOrg.ts index 43bc569..2c4a4cf 100644 --- a/server/routers/org/updateOrg.ts +++ b/server/routers/org/updateOrg.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { orgs } from "@server/db/schema"; +import { orgs } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/resource/authWithAccessToken.ts b/server/routers/resource/authWithAccessToken.ts index 03dac73..961b2d8 100644 --- a/server/routers/resource/authWithAccessToken.ts +++ b/server/routers/resource/authWithAccessToken.ts @@ -1,6 +1,6 @@ import { generateSessionToken } from "@server/auth/sessions/app"; import db from "@server/db"; -import { resources } from "@server/db/schema"; +import { Resource, resources } from "@server/db/schemas"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; import { eq } from "drizzle-orm"; @@ -10,13 +10,16 @@ import { z } from "zod"; import { fromError } from "zod-validation-error"; import { createResourceSession } from "@server/auth/sessions/resource"; import logger from "@server/logger"; -import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken"; +import { + verifyResourceAccessToken +} from "@server/auth/verifyResourceAccessToken"; import config from "@server/lib/config"; +import stoi from "@server/lib/stoi"; const authWithAccessTokenBodySchema = z .object({ accessToken: z.string(), - accessTokenId: z.string() + accessTokenId: z.string().optional() }) .strict(); @@ -24,13 +27,15 @@ const authWithAccessTokenParamsSchema = z .object({ resourceId: z .string() - .transform(Number) - .pipe(z.number().int().positive()) + .optional() + .transform(stoi) + .pipe(z.number().int().positive().optional()) }) .strict(); export type AuthWithAccessTokenResponse = { session?: string; + redirectUrl?: string | null; }; export async function authWithAccessToken( @@ -64,23 +69,61 @@ export async function authWithAccessToken( const { accessToken, accessTokenId } = parsedBody.data; try { - const [resource] = await db - .select() - .from(resources) - .where(eq(resources.resourceId, resourceId)) - .limit(1); + let valid; + let tokenItem; + let error; + let resource: Resource | undefined; - if (!resource) { - return next( - createHttpError(HttpCode.NOT_FOUND, "Resource not found") - ); + if (accessTokenId) { + if (!resourceId) { + return next( + createHttpError( + HttpCode.BAD_REQUEST, + "Resource ID is required" + ) + ); + } + + const [foundResource] = await db + .select() + .from(resources) + .where(eq(resources.resourceId, resourceId)) + .limit(1); + + if (!foundResource) { + return next( + createHttpError(HttpCode.NOT_FOUND, "Resource not found") + ); + } + + const res = await verifyResourceAccessToken({ + accessTokenId, + accessToken + }); + + valid = res.valid; + tokenItem = res.tokenItem; + error = res.error; + resource = foundResource; + } else { + const res = await verifyResourceAccessToken({ + accessToken + }); + + valid = res.valid; + tokenItem = res.tokenItem; + error = res.error; + resource = res.resource; } - const { valid, error, tokenItem } = await verifyResourceAccessToken({ - resource, - accessTokenId, - accessToken - }); + if (!tokenItem || !resource) { + return next( + createHttpError( + HttpCode.UNAUTHORIZED, + "Access token does not exist for resource" + ) + ); + } if (!valid) { if (config.getRawConfig().app.log_failed_attempts) { @@ -96,18 +139,9 @@ export async function authWithAccessToken( ); } - if (!tokenItem || !resource) { - return next( - createHttpError( - HttpCode.UNAUTHORIZED, - "Access token does not exist for resource" - ) - ); - } - const token = generateSessionToken(); await createResourceSession({ - resourceId, + resourceId: resource.resourceId, token, accessTokenId: tokenItem.accessTokenId, isRequestToken: true, @@ -118,7 +152,8 @@ export async function authWithAccessToken( return response(res, { data: { - session: token + session: token, + redirectUrl: `${resource.ssl ? "https" : "http"}://${resource.fullDomain}` }, success: true, error: false, diff --git a/server/routers/resource/authWithPassword.ts b/server/routers/resource/authWithPassword.ts index a69d42b..602ddcc 100644 --- a/server/routers/resource/authWithPassword.ts +++ b/server/routers/resource/authWithPassword.ts @@ -1,7 +1,7 @@ import { verify } from "@node-rs/argon2"; import { generateSessionToken } from "@server/auth/sessions/app"; import db from "@server/db"; -import { orgs, resourcePassword, resources } from "@server/db/schema"; +import { orgs, resourcePassword, resources } from "@server/db/schemas"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; import { eq } from "drizzle-orm"; diff --git a/server/routers/resource/authWithPincode.ts b/server/routers/resource/authWithPincode.ts index c648e36..2164094 100644 --- a/server/routers/resource/authWithPincode.ts +++ b/server/routers/resource/authWithPincode.ts @@ -1,6 +1,6 @@ import { generateSessionToken } from "@server/auth/sessions/app"; import db from "@server/db"; -import { orgs, resourcePincode, resources } from "@server/db/schema"; +import { orgs, resourcePincode, resources } from "@server/db/schemas"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; import { eq } from "drizzle-orm"; diff --git a/server/routers/resource/authWithWhitelist.ts b/server/routers/resource/authWithWhitelist.ts index 147e2ba..01c9909 100644 --- a/server/routers/resource/authWithWhitelist.ts +++ b/server/routers/resource/authWithWhitelist.ts @@ -5,7 +5,7 @@ import { resourceOtp, resources, resourceWhitelist -} from "@server/db/schema"; +} from "@server/db/schemas"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; import { eq, and } from "drizzle-orm"; diff --git a/server/routers/resource/createResource.ts b/server/routers/resource/createResource.ts index 2f2bed5..d4001de 100644 --- a/server/routers/resource/createResource.ts +++ b/server/routers/resource/createResource.ts @@ -10,7 +10,7 @@ import { roleResources, roles, userResources -} from "@server/db/schema"; +} from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/resource/createResourceRule.ts b/server/routers/resource/createResourceRule.ts index 304f4bd..d52f294 100644 --- a/server/routers/resource/createResourceRule.ts +++ b/server/routers/resource/createResourceRule.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resourceRules, resources } from "@server/db/schema"; +import { resourceRules, resources } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/resource/deleteResource.ts b/server/routers/resource/deleteResource.ts index 8acf0d7..f1d2f20 100644 --- a/server/routers/resource/deleteResource.ts +++ b/server/routers/resource/deleteResource.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { newts, resources, sites, targets } from "@server/db/schema"; +import { newts, resources, sites, targets } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/resource/deleteResourceRule.ts b/server/routers/resource/deleteResourceRule.ts index b562fc1..7583c31 100644 --- a/server/routers/resource/deleteResourceRule.ts +++ b/server/routers/resource/deleteResourceRule.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resourceRules, resources } from "@server/db/schema"; +import { resourceRules, resources } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; @@ -68,4 +68,4 @@ export async function deleteResourceRule( createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred") ); } -} \ No newline at end of file +} diff --git a/server/routers/resource/getExchangeToken.ts b/server/routers/resource/getExchangeToken.ts index f80a083..f957943 100644 --- a/server/routers/resource/getExchangeToken.ts +++ b/server/routers/resource/getExchangeToken.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resources } from "@server/db/schema"; +import { resources } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import { createResourceSession } from "@server/auth/sessions/resource"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/resource/getResource.ts b/server/routers/resource/getResource.ts index 3c40f47..4fa3aca 100644 --- a/server/routers/resource/getResource.ts +++ b/server/routers/resource/getResource.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { Resource, resources, sites } from "@server/db/schema"; +import { Resource, resources, sites } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/resource/getResourceAuthInfo.ts b/server/routers/resource/getResourceAuthInfo.ts index 30da242..5f74b63 100644 --- a/server/routers/resource/getResourceAuthInfo.ts +++ b/server/routers/resource/getResourceAuthInfo.ts @@ -5,7 +5,7 @@ import { resourcePassword, resourcePincode, resources -} from "@server/db/schema"; +} from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/resource/getResourceWhitelist.ts b/server/routers/resource/getResourceWhitelist.ts index 53d504b..b99decd 100644 --- a/server/routers/resource/getResourceWhitelist.ts +++ b/server/routers/resource/getResourceWhitelist.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resourceWhitelist, users } from "@server/db/schema"; // Assuming these are the correct tables +import { resourceWhitelist, users } from "@server/db/schemas"; // Assuming these are the correct tables import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/resource/listResourceRoles.ts b/server/routers/resource/listResourceRoles.ts index 00fc40f..8b80568 100644 --- a/server/routers/resource/listResourceRoles.ts +++ b/server/routers/resource/listResourceRoles.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roleResources, roles } from "@server/db/schema"; +import { roleResources, roles } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/resource/listResourceRules.ts b/server/routers/resource/listResourceRules.ts index 0d29bd9..6a9fe0b 100644 --- a/server/routers/resource/listResourceRules.ts +++ b/server/routers/resource/listResourceRules.ts @@ -1,5 +1,5 @@ import { db } from "@server/db"; -import { resourceRules, resources } from "@server/db/schema"; +import { resourceRules, resources } from "@server/db/schemas"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; import { eq, sql } from "drizzle-orm"; diff --git a/server/routers/resource/listResourceUsers.ts b/server/routers/resource/listResourceUsers.ts index 97a790e..6ca7974 100644 --- a/server/routers/resource/listResourceUsers.ts +++ b/server/routers/resource/listResourceUsers.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { userResources, users } from "@server/db/schema"; // Assuming these are the correct tables +import { userResources, users } from "@server/db/schemas"; // Assuming these are the correct tables import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/resource/listResources.ts b/server/routers/resource/listResources.ts index 50aebb7..1dba411 100644 --- a/server/routers/resource/listResources.ts +++ b/server/routers/resource/listResources.ts @@ -8,13 +8,14 @@ import { roleResources, resourcePassword, resourcePincode -} from "@server/db/schema"; +} from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import { sql, eq, or, inArray, and, count } from "drizzle-orm"; import logger from "@server/logger"; import stoi from "@server/lib/stoi"; +import { fromZodError } from "zod-validation-error"; const listResourcesParamsSchema = z .object({ @@ -66,7 +67,8 @@ function queryResources( whitelist: resources.emailWhitelistEnabled, http: resources.http, protocol: resources.protocol, - proxyPort: resources.proxyPort + proxyPort: resources.proxyPort, + enabled: resources.enabled }) .from(resources) .leftJoin(sites, eq(resources.siteId, sites.siteId)) @@ -99,7 +101,8 @@ function queryResources( whitelist: resources.emailWhitelistEnabled, http: resources.http, protocol: resources.protocol, - proxyPort: resources.proxyPort + proxyPort: resources.proxyPort, + enabled: resources.enabled }) .from(resources) .leftJoin(sites, eq(resources.siteId, sites.siteId)) @@ -136,7 +139,7 @@ export async function listResources( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedQuery.error.errors.map((e) => e.message).join(", ") + fromZodError(parsedQuery.error) ) ); } @@ -147,7 +150,7 @@ export async function listResources( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedParams.error.errors.map((e) => e.message).join(", ") + fromZodError(parsedParams.error) ) ); } diff --git a/server/routers/resource/setResourcePassword.ts b/server/routers/resource/setResourcePassword.ts index ecfc744..7762880 100644 --- a/server/routers/resource/setResourcePassword.ts +++ b/server/routers/resource/setResourcePassword.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resourcePassword } from "@server/db/schema"; +import { resourcePassword } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/resource/setResourcePincode.ts b/server/routers/resource/setResourcePincode.ts index 1f1815b..8530b90 100644 --- a/server/routers/resource/setResourcePincode.ts +++ b/server/routers/resource/setResourcePincode.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resourcePincode } from "@server/db/schema"; +import { resourcePincode } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/resource/setResourceRoles.ts b/server/routers/resource/setResourceRoles.ts index 31c6cbe..3f09e76 100644 --- a/server/routers/resource/setResourceRoles.ts +++ b/server/routers/resource/setResourceRoles.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roleResources, roles } from "@server/db/schema"; +import { roleResources, roles } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/resource/setResourceUsers.ts b/server/routers/resource/setResourceUsers.ts index 5f38afb..8878fd7 100644 --- a/server/routers/resource/setResourceUsers.ts +++ b/server/routers/resource/setResourceUsers.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { userResources } from "@server/db/schema"; +import { userResources } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/resource/setResourceWhitelist.ts b/server/routers/resource/setResourceWhitelist.ts index d60d079..390c2c2 100644 --- a/server/routers/resource/setResourceWhitelist.ts +++ b/server/routers/resource/setResourceWhitelist.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resources, resourceWhitelist } from "@server/db/schema"; +import { resources, resourceWhitelist } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/resource/transferResource.ts b/server/routers/resource/transferResource.ts index 69c9a2a..17c1dcb 100644 --- a/server/routers/resource/transferResource.ts +++ b/server/routers/resource/transferResource.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { newts, resources, sites, targets } from "@server/db/schema"; +import { newts, resources, sites, targets } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/resource/updateResource.ts b/server/routers/resource/updateResource.ts index bade7f0..121b34e 100644 --- a/server/routers/resource/updateResource.ts +++ b/server/routers/resource/updateResource.ts @@ -8,7 +8,7 @@ import { orgs, Resource, resources -} from "@server/db/schema"; +} from "@server/db/schemas"; import { eq, and } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; @@ -39,7 +39,8 @@ const updateHttpResourceBodySchema = z emailWhitelistEnabled: z.boolean().optional(), isBaseDomain: z.boolean().optional(), applyRules: z.boolean().optional(), - domainId: z.string().optional() + domainId: z.string().optional(), + enabled: z.boolean().optional() }) .strict() .refine((data) => Object.keys(data).length > 0, { @@ -73,7 +74,8 @@ export type UpdateResourceResponse = Resource; const updateRawResourceBodySchema = z .object({ name: z.string().min(1).max(255).optional(), - proxyPort: z.number().int().min(1).max(65535).optional() + proxyPort: z.number().int().min(1).max(65535).optional(), + enabled: z.boolean().optional() }) .strict() .refine((data) => Object.keys(data).length > 0, { diff --git a/server/routers/resource/updateResourceRule.ts b/server/routers/resource/updateResourceRule.ts index ef23b31..9070d45 100644 --- a/server/routers/resource/updateResourceRule.ts +++ b/server/routers/resource/updateResourceRule.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resourceRules, resources } from "@server/db/schema"; +import { resourceRules, resources } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/role/addRoleAction.ts b/server/routers/role/addRoleAction.ts index 53cea29..9f364a5 100644 --- a/server/routers/role/addRoleAction.ts +++ b/server/routers/role/addRoleAction.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roleActions, roles } from "@server/db/schema"; +import { roleActions, roles } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/role/addRoleSite.ts b/server/routers/role/addRoleSite.ts index dddb91c..0db6ac4 100644 --- a/server/routers/role/addRoleSite.ts +++ b/server/routers/role/addRoleSite.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resources, roleResources, roleSites } from "@server/db/schema"; +import { resources, roleResources, roleSites } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/role/createRole.ts b/server/routers/role/createRole.ts index 5076f98..b9bdc37 100644 --- a/server/routers/role/createRole.ts +++ b/server/routers/role/createRole.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { orgs, Role, roleActions, roles } from "@server/db/schema"; +import { orgs, Role, roleActions, roles } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/role/deleteRole.ts b/server/routers/role/deleteRole.ts index 3160f31..0e709ff 100644 --- a/server/routers/role/deleteRole.ts +++ b/server/routers/role/deleteRole.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roles, userOrgs } from "@server/db/schema"; +import { roles, userOrgs } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/role/getRole.ts b/server/routers/role/getRole.ts index 401c5e9..20f93bf 100644 --- a/server/routers/role/getRole.ts +++ b/server/routers/role/getRole.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roles } from "@server/db/schema"; +import { roles } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/role/listRoleActions.ts b/server/routers/role/listRoleActions.ts index 20190bb..d463709 100644 --- a/server/routers/role/listRoleActions.ts +++ b/server/routers/role/listRoleActions.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roleActions, actions } from "@server/db/schema"; +import { roleActions, actions } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/role/listRoleResources.ts b/server/routers/role/listRoleResources.ts index 3b13d96..7239f6f 100644 --- a/server/routers/role/listRoleResources.ts +++ b/server/routers/role/listRoleResources.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roleResources, resources } from "@server/db/schema"; +import { roleResources, resources } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/role/listRoleSites.ts b/server/routers/role/listRoleSites.ts index 4b676d8..f659454 100644 --- a/server/routers/role/listRoleSites.ts +++ b/server/routers/role/listRoleSites.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roleSites, sites } from "@server/db/schema"; +import { roleSites, sites } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/role/listRoles.ts b/server/routers/role/listRoles.ts index 5427a91..2740484 100644 --- a/server/routers/role/listRoles.ts +++ b/server/routers/role/listRoles.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roles, orgs } from "@server/db/schema"; +import { roles, orgs } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/role/removeRoleAction.ts b/server/routers/role/removeRoleAction.ts index 376e20b..72d9be5 100644 --- a/server/routers/role/removeRoleAction.ts +++ b/server/routers/role/removeRoleAction.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roleActions } from "@server/db/schema"; +import { roleActions } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/role/removeRoleResource.ts b/server/routers/role/removeRoleResource.ts index e5a0ead..ca068e0 100644 --- a/server/routers/role/removeRoleResource.ts +++ b/server/routers/role/removeRoleResource.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roleResources } from "@server/db/schema"; +import { roleResources } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/role/removeRoleSite.ts b/server/routers/role/removeRoleSite.ts index 1294ce6..a99adf5 100644 --- a/server/routers/role/removeRoleSite.ts +++ b/server/routers/role/removeRoleSite.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resources, roleResources, roleSites } from "@server/db/schema"; +import { resources, roleResources, roleSites } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/role/updateRole.ts b/server/routers/role/updateRole.ts index 2bfcfe9..bf029eb 100644 --- a/server/routers/role/updateRole.ts +++ b/server/routers/role/updateRole.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roles } from "@server/db/schema"; +import { roles } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/site/createSite.ts b/server/routers/site/createSite.ts index 9a5e6f4..8d6d001 100644 --- a/server/routers/site/createSite.ts +++ b/server/routers/site/createSite.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roles, userSites, sites, roleSites, Site } from "@server/db/schema"; +import { roles, userSites, sites, roleSites, Site } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; @@ -11,7 +11,7 @@ import { getUniqueSiteName } from "@server/db/names"; import { addPeer } from "../gerbil/peers"; import { fromError } from "zod-validation-error"; import { hash } from "@node-rs/argon2"; -import { newts } from "@server/db/schema"; +import { newts } from "@server/db/schemas"; import moment from "moment"; import { hashPassword } from "@server/auth/password"; diff --git a/server/routers/site/deleteSite.ts b/server/routers/site/deleteSite.ts index 4bf79fc..1c237ef 100644 --- a/server/routers/site/deleteSite.ts +++ b/server/routers/site/deleteSite.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { newts, newtSessions, sites } from "@server/db/schema"; +import { newts, newtSessions, sites } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/site/getSite.ts b/server/routers/site/getSite.ts index 2a12e4d..98cd6ef 100644 --- a/server/routers/site/getSite.ts +++ b/server/routers/site/getSite.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { sites } from "@server/db/schema"; +import { sites } from "@server/db/schemas"; import { eq, and } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/site/listSiteRoles.ts b/server/routers/site/listSiteRoles.ts index ccb1308..13c8dd4 100644 --- a/server/routers/site/listSiteRoles.ts +++ b/server/routers/site/listSiteRoles.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roleSites, roles } from "@server/db/schema"; +import { roleSites, roles } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/site/listSites.ts b/server/routers/site/listSites.ts index d9ad9d1..c40b7fe 100644 --- a/server/routers/site/listSites.ts +++ b/server/routers/site/listSites.ts @@ -1,5 +1,5 @@ import { db } from "@server/db"; -import { orgs, roleSites, sites, userSites } from "@server/db/schema"; +import { orgs, roleSites, sites, userSites } from "@server/db/schemas"; import logger from "@server/logger"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; diff --git a/server/routers/site/pickSiteDefaults.ts b/server/routers/site/pickSiteDefaults.ts index 56d072e..38f2ec9 100644 --- a/server/routers/site/pickSiteDefaults.ts +++ b/server/routers/site/pickSiteDefaults.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { db } from "@server/db"; -import { exitNodes, sites } from "@server/db/schema"; +import { exitNodes, sites } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/site/updateSite.ts b/server/routers/site/updateSite.ts index f3e9d31..2309fa3 100644 --- a/server/routers/site/updateSite.ts +++ b/server/routers/site/updateSite.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { sites } from "@server/db/schema"; +import { sites } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/supporterKey/isSupporterKeyVisible.ts b/server/routers/supporterKey/isSupporterKeyVisible.ts index dca4535..3eab2ac 100644 --- a/server/routers/supporterKey/isSupporterKeyVisible.ts +++ b/server/routers/supporterKey/isSupporterKeyVisible.ts @@ -6,7 +6,7 @@ import { response as sendResponse } from "@server/lib"; import config from "@server/lib/config"; import db from "@server/db"; import { count } from "drizzle-orm"; -import { users } from "@server/db/schema"; +import { users } from "@server/db/schemas"; export type IsSupporterKeyVisibleResponse = { visible: boolean; diff --git a/server/routers/supporterKey/validateSupporterKey.ts b/server/routers/supporterKey/validateSupporterKey.ts index bbe7ed4..0f023ea 100644 --- a/server/routers/supporterKey/validateSupporterKey.ts +++ b/server/routers/supporterKey/validateSupporterKey.ts @@ -6,7 +6,7 @@ import logger from "@server/logger"; import { fromError } from "zod-validation-error"; import { response as sendResponse } from "@server/lib"; import { suppressDeprecationWarnings } from "moment"; -import { supporterKey } from "@server/db/schema"; +import { supporterKey } from "@server/db/schemas"; import db from "@server/db"; import { eq } from "drizzle-orm"; import config from "@server/lib/config"; diff --git a/server/routers/target/createTarget.ts b/server/routers/target/createTarget.ts index 8d07e5d..86e9ff6 100644 --- a/server/routers/target/createTarget.ts +++ b/server/routers/target/createTarget.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { newts, resources, sites, Target, targets } from "@server/db/schema"; +import { newts, resources, sites, Target, targets } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/target/deleteTarget.ts b/server/routers/target/deleteTarget.ts index 7472b73..57534bd 100644 --- a/server/routers/target/deleteTarget.ts +++ b/server/routers/target/deleteTarget.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { newts, resources, sites, targets } from "@server/db/schema"; +import { newts, resources, sites, targets } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/target/getTarget.ts b/server/routers/target/getTarget.ts index 8206e82..69ade88 100644 --- a/server/routers/target/getTarget.ts +++ b/server/routers/target/getTarget.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { targets } from "@server/db/schema"; +import { targets } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/target/helpers.ts b/server/routers/target/helpers.ts index 606e229..8fc8797 100644 --- a/server/routers/target/helpers.ts +++ b/server/routers/target/helpers.ts @@ -1,5 +1,5 @@ import { db } from "@server/db"; -import { resources, targets } from "@server/db/schema"; +import { resources, targets } from "@server/db/schemas"; import { eq } from "drizzle-orm"; let currentBannedPorts: number[] = []; diff --git a/server/routers/target/listTargets.ts b/server/routers/target/listTargets.ts index 0efea3e..154787a 100644 --- a/server/routers/target/listTargets.ts +++ b/server/routers/target/listTargets.ts @@ -1,5 +1,5 @@ import { db } from "@server/db"; -import { targets } from "@server/db/schema"; +import { targets } from "@server/db/schemas"; import HttpCode from "@server/types/HttpCode"; import response from "@server/lib/response"; import { eq, sql } from "drizzle-orm"; diff --git a/server/routers/target/updateTarget.ts b/server/routers/target/updateTarget.ts index 45051e0..e675034 100644 --- a/server/routers/target/updateTarget.ts +++ b/server/routers/target/updateTarget.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { newts, resources, sites, targets } from "@server/db/schema"; +import { newts, resources, sites, targets } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/traefik/getTraefikConfig.ts b/server/routers/traefik/getTraefikConfig.ts index 8506a2a..17e385e 100644 --- a/server/routers/traefik/getTraefikConfig.ts +++ b/server/routers/traefik/getTraefikConfig.ts @@ -4,7 +4,7 @@ import { and, eq, inArray } from "drizzle-orm"; import logger from "@server/logger"; import HttpCode from "@server/types/HttpCode"; import config from "@server/lib/config"; -import { orgs, resources, sites, Target, targets } from "@server/db/schema"; +import { orgs, resources, sites, Target, targets } from "@server/db/schemas"; import { sql } from "drizzle-orm"; export async function traefikConfigProvider( @@ -39,7 +39,8 @@ export async function traefikConfigProvider( // Org fields org: { orgId: orgs.orgId - } + }, + enabled: resources.enabled }) .from(resources) .innerJoin(sites, eq(sites.siteId, resources.siteId)) @@ -109,9 +110,12 @@ export async function traefikConfigProvider( userSessionCookieName: config.getRawConfig().server .session_cookie_name, + + // deprecated accessTokenQueryParam: config.getRawConfig().server .resource_access_token_param, + resourceSessionRequestParam: config.getRawConfig().server .resource_session_request_param @@ -136,6 +140,10 @@ export async function traefikConfigProvider( const serviceName = `${resource.resourceId}-service`; const fullDomain = `${resource.fullDomain}`; + if (!resource.enabled) { + continue; + } + if (resource.http) { if (!resource.domainId) { continue; diff --git a/server/routers/user/acceptInvite.ts b/server/routers/user/acceptInvite.ts index ade89e5..cc483b1 100644 --- a/server/routers/user/acceptInvite.ts +++ b/server/routers/user/acceptInvite.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roles, userInvites, userOrgs, users } from "@server/db/schema"; +import { roles, userInvites, userOrgs, users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/user/addUserAction.ts b/server/routers/user/addUserAction.ts index 0b6fea9..472f429 100644 --- a/server/routers/user/addUserAction.ts +++ b/server/routers/user/addUserAction.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { userActions, users } from "@server/db/schema"; +import { userActions, users } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/user/addUserRole.ts b/server/routers/user/addUserRole.ts index 4b24c5a..22527d0 100644 --- a/server/routers/user/addUserRole.ts +++ b/server/routers/user/addUserRole.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { userOrgs, roles } from "@server/db/schema"; +import { userOrgs, roles } from "@server/db/schemas"; import { eq, and } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/user/addUserSite.ts b/server/routers/user/addUserSite.ts index 1129c20..5b20ed8 100644 --- a/server/routers/user/addUserSite.ts +++ b/server/routers/user/addUserSite.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resources, userResources, userSites } from "@server/db/schema"; +import { resources, userResources, userSites } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; diff --git a/server/routers/user/adminListUsers.ts b/server/routers/user/adminListUsers.ts index 1923374..2d95756 100644 --- a/server/routers/user/adminListUsers.ts +++ b/server/routers/user/adminListUsers.ts @@ -6,7 +6,8 @@ import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import { sql, eq } from "drizzle-orm"; import logger from "@server/logger"; -import { users } from "@server/db/schema"; +import { users } from "@server/db/schemas"; +import { fromZodError } from "zod-validation-error"; const listUsersSchema = z .object({ @@ -55,7 +56,7 @@ export async function adminListUsers( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedQuery.error.errors.map((e) => e.message).join(", ") + fromZodError(parsedQuery.error) ) ); } diff --git a/server/routers/user/adminRemoveUser.ts b/server/routers/user/adminRemoveUser.ts index 164b375..fa31c52 100644 --- a/server/routers/user/adminRemoveUser.ts +++ b/server/routers/user/adminRemoveUser.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { users } from "@server/db/schema"; +import { users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/user/getOrgUser.ts b/server/routers/user/getOrgUser.ts index 38d806d..fd4defe 100644 --- a/server/routers/user/getOrgUser.ts +++ b/server/routers/user/getOrgUser.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roles, userOrgs, users } from "@server/db/schema"; +import { roles, userOrgs, users } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/user/getUser.ts b/server/routers/user/getUser.ts index 096d4e2..31c7d8a 100644 --- a/server/routers/user/getUser.ts +++ b/server/routers/user/getUser.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { users } from "@server/db/schema"; +import { users } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/user/inviteUser.ts b/server/routers/user/inviteUser.ts index 5bf7e17..078c1ab 100644 --- a/server/routers/user/inviteUser.ts +++ b/server/routers/user/inviteUser.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { orgs, userInvites, userOrgs, users } from "@server/db/schema"; +import { orgs, userInvites, userOrgs, users } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/user/listUsers.ts b/server/routers/user/listUsers.ts index 6c9e49a..e442959 100644 --- a/server/routers/user/listUsers.ts +++ b/server/routers/user/listUsers.ts @@ -1,12 +1,13 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { roles, userOrgs, users } from "@server/db/schema"; +import { roles, userOrgs, users } from "@server/db/schemas"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; import { sql } from "drizzle-orm"; import logger from "@server/logger"; +import { fromZodError } from "zod-validation-error"; const listUsersParamsSchema = z .object({ @@ -67,7 +68,7 @@ export async function listUsers( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedQuery.error.errors.map((e) => e.message).join(", ") + fromZodError(parsedQuery.error) ) ); } @@ -78,7 +79,7 @@ export async function listUsers( return next( createHttpError( HttpCode.BAD_REQUEST, - parsedParams.error.errors.map((e) => e.message).join(", ") + fromZodError(parsedParams.error) ) ); } diff --git a/server/routers/user/removeUserAction.ts b/server/routers/user/removeUserAction.ts index 0d220fd..9364f40 100644 --- a/server/routers/user/removeUserAction.ts +++ b/server/routers/user/removeUserAction.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { userActions } from "@server/db/schema"; +import { userActions } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/user/removeUserOrg.ts b/server/routers/user/removeUserOrg.ts index d7fe2bd..6043989 100644 --- a/server/routers/user/removeUserOrg.ts +++ b/server/routers/user/removeUserOrg.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { userOrgs, userResources, users, userSites } from "@server/db/schema"; +import { userOrgs, userResources, users, userSites } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/user/removeUserResource.ts b/server/routers/user/removeUserResource.ts index 23f115e..be5acab 100644 --- a/server/routers/user/removeUserResource.ts +++ b/server/routers/user/removeUserResource.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { userResources } from "@server/db/schema"; +import { userResources } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/user/removeUserSite.ts b/server/routers/user/removeUserSite.ts index b45f722..6142f45 100644 --- a/server/routers/user/removeUserSite.ts +++ b/server/routers/user/removeUserSite.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; import { db } from "@server/db"; -import { resources, userResources, userSites } from "@server/db/schema"; +import { resources, userResources, userSites } from "@server/db/schemas"; import { and, eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; diff --git a/server/routers/ws.ts b/server/routers/ws.ts index afe422d..c4ee887 100644 --- a/server/routers/ws.ts +++ b/server/routers/ws.ts @@ -3,7 +3,7 @@ import { Server as HttpServer } from "http"; import { WebSocket, WebSocketServer } from "ws"; import { IncomingMessage } from "http"; import { Socket } from "net"; -import { Newt, newts, NewtSession } from "@server/db/schema"; +import { Newt, newts, NewtSession } from "@server/db/schemas"; import { eq } from "drizzle-orm"; import db from "@server/db"; import { validateNewtSessionToken } from "@server/auth/sessions/newt"; diff --git a/server/setup/clearStaleData.ts b/server/setup/clearStaleData.ts index ccadfc3..4d95107 100644 --- a/server/setup/clearStaleData.ts +++ b/server/setup/clearStaleData.ts @@ -8,7 +8,7 @@ import { resourceSessions, sessions, userInvites -} from "@server/db/schema"; +} from "@server/db/schemas"; import logger from "@server/logger"; import { lt } from "drizzle-orm"; diff --git a/server/setup/copyInConfig.ts b/server/setup/copyInConfig.ts index ae77152..ec5a137 100644 --- a/server/setup/copyInConfig.ts +++ b/server/setup/copyInConfig.ts @@ -1,5 +1,5 @@ import { db } from "@server/db"; -import { domains, exitNodes, orgDomains, orgs, resources } from "../db/schema"; +import { domains, exitNodes, orgDomains, orgs, resources } from "../db/schemas/schema"; import config from "@server/lib/config"; import { eq, ne } from "drizzle-orm"; import logger from "@server/logger"; diff --git a/server/setup/ensureActions.ts b/server/setup/ensureActions.ts index ec7b3a1..0d789e1 100644 --- a/server/setup/ensureActions.ts +++ b/server/setup/ensureActions.ts @@ -1,6 +1,6 @@ import { ActionsEnum } from "@server/auth/actions"; import { db } from "@server/db"; -import { actions, roles, roleActions } from "../db/schema"; +import { actions, roles, roleActions } from "../db/schemas/schema"; import { eq, inArray } from "drizzle-orm"; import logger from "@server/logger"; diff --git a/server/setup/migrations.ts b/server/setup/migrations.ts index ad081b6..77248f6 100644 --- a/server/setup/migrations.ts +++ b/server/setup/migrations.ts @@ -2,7 +2,7 @@ import { migrate } from "drizzle-orm/better-sqlite3/migrator"; import db, { exists } from "@server/db"; import path from "path"; import semver from "semver"; -import { versionMigrations } from "@server/db/schema"; +import { versionMigrations } from "@server/db/schemas"; import { __DIRNAME, APP_PATH, APP_VERSION } from "@server/lib/consts"; import { SqliteError } from "better-sqlite3"; import fs from "fs"; @@ -18,6 +18,7 @@ import m13 from "./scripts/1.0.0-beta13"; import m15 from "./scripts/1.0.0-beta15"; import m16 from "./scripts/1.0.0"; import m17 from "./scripts/1.1.0"; +import m18 from "./scripts/1.2.0"; // THIS CANNOT IMPORT ANYTHING FROM THE SERVER // EXCEPT FOR THE DATABASE AND THE SCHEMA @@ -35,7 +36,8 @@ const migrations = [ { version: "1.0.0-beta.13", run: m13 }, { version: "1.0.0-beta.15", run: m15 }, { version: "1.0.0", run: m16 }, - { version: "1.1.0", run: m17 } + { version: "1.1.0", run: m17 }, + { version: "1.2.0", run: m18 } // Add new migrations here as they are created ] as const; diff --git a/server/setup/scripts/1.0.0-beta15.ts b/server/setup/scripts/1.0.0-beta15.ts index aa2044a..a087c5c 100644 --- a/server/setup/scripts/1.0.0-beta15.ts +++ b/server/setup/scripts/1.0.0-beta15.ts @@ -3,7 +3,7 @@ import { configFilePath1, configFilePath2 } from "@server/lib/consts"; import fs from "fs"; import yaml from "js-yaml"; import { sql } from "drizzle-orm"; -import { domains, orgDomains, resources } from "@server/db/schema"; +import { domains, orgDomains, resources } from "@server/db/schemas"; const version = "1.0.0-beta.15"; diff --git a/server/setup/scripts/1.0.0-beta9.ts b/server/setup/scripts/1.0.0-beta9.ts index c883bd6..64f2bee 100644 --- a/server/setup/scripts/1.0.0-beta9.ts +++ b/server/setup/scripts/1.0.0-beta9.ts @@ -8,7 +8,7 @@ import { targets, userInvites, users -} from "@server/db/schema"; +} from "@server/db/schemas"; import { APP_PATH, configFilePath1, configFilePath2 } from "@server/lib/consts"; import { eq, sql } from "drizzle-orm"; import fs from "fs"; diff --git a/server/setup/scripts/1.2.0.ts b/server/setup/scripts/1.2.0.ts new file mode 100644 index 0000000..fdea9fa --- /dev/null +++ b/server/setup/scripts/1.2.0.ts @@ -0,0 +1,115 @@ +import db from "@server/db"; +import { APP_PATH, configFilePath1, configFilePath2 } from "@server/lib/consts"; +import { sql } from "drizzle-orm"; +import fs from "fs"; +import yaml from "js-yaml"; +import path from "path"; +import { z } from "zod"; +import { fromZodError } from "zod-validation-error"; + +const version = "1.2.0"; + +export default async function migration() { + console.log(`Running setup script ${version}...`); + + try { + db.transaction((trx) => { + trx.run( + sql`ALTER TABLE 'resources' ADD 'enabled' integer DEFAULT true NOT NULL;` + ); + }); + + console.log(`Migrated database schema`); + } catch (e) { + console.log("Unable to migrate database schema"); + throw e; + } + + try { + // Determine which config file exists + const filePaths = [configFilePath1, configFilePath2]; + let filePath = ""; + for (const path of filePaths) { + if (fs.existsSync(path)) { + filePath = path; + break; + } + } + + if (!filePath) { + throw new Error( + `No config file found (expected config.yml or config.yaml).` + ); + } + + // Read and parse the YAML file + let rawConfig: any; + const fileContents = fs.readFileSync(filePath, "utf8"); + rawConfig = yaml.load(fileContents); + + if (!rawConfig.flags) { + rawConfig.flags = {}; + } + + rawConfig.server.resource_access_token_headers = { + id: "P-Access-Token-Id", + token: "P-Access-Token" + }; + + // Write the updated YAML back to the file + const updatedYaml = yaml.dump(rawConfig); + fs.writeFileSync(filePath, updatedYaml, "utf8"); + + console.log(`Added new config option: resource_access_token_headers`); + } catch (e) { + console.log( + `Unable to add new config option: resource_access_token_headers. Please add it manually. https://docs.fossorial.io/Pangolin/Configuration/config` + ); + console.error(e); + } + + try { + const traefikPath = path.join( + APP_PATH, + "traefik", + "traefik_config.yml" + ); + + const schema = z.object({ + experimental: z.object({ + plugins: z.object({ + badger: z.object({ + moduleName: z.string(), + version: z.string() + }) + }) + }) + }); + + const traefikFileContents = fs.readFileSync(traefikPath, "utf8"); + const traefikConfig = yaml.load(traefikFileContents) as any; + + const parsedConfig = schema.safeParse(traefikConfig); + + if (!parsedConfig.success) { + throw new Error(fromZodError(parsedConfig.error).toString()); + } + + traefikConfig.experimental.plugins.badger.version = "v1.1.0"; + + const updatedTraefikYaml = yaml.dump(traefikConfig); + + fs.writeFileSync(traefikPath, updatedTraefikYaml, "utf8"); + + console.log( + "Updated the version of Badger in your Traefik configuration to v1.1.0" + ); + } catch (e) { + console.log( + "We were unable to update the version of Badger in your Traefik configuration. Please update it manually. Check the release notes for this version for more information." + ); + console.error(e); + } + + console.log(`${version} migration complete`); +} diff --git a/server/setup/setupServerAdmin.ts b/server/setup/setupServerAdmin.ts index 70faff8..6ec6784 100644 --- a/server/setup/setupServerAdmin.ts +++ b/server/setup/setupServerAdmin.ts @@ -2,7 +2,7 @@ import { generateId, invalidateAllSessions } from "@server/auth/sessions/app"; import { hashPassword, verifyPassword } from "@server/auth/password"; import config from "@server/lib/config"; import db from "@server/db"; -import { users } from "@server/db/schema"; +import { users } from "@server/db/schemas"; import logger from "@server/logger"; import { eq } from "drizzle-orm"; import moment from "moment"; @@ -29,7 +29,7 @@ export async function setupServerAdmin() { const [existing] = await trx .select() .from(users) - .where(eq(users.email, email)); + .where(eq(users.serverAdmin, true)); if (existing) { const passwordChanged = !(await verifyPassword( @@ -46,41 +46,33 @@ export async function setupServerAdmin() { // this isn't using the transaction, but it's probably fine await invalidateAllSessions(existing.userId); - logger.info(`Server admin (${email}) password updated`); + logger.info(`Server admin password updated`); } - if (existing.serverAdmin) { - logger.info(`Server admin (${email}) already exists`) - return; + if (existing.email !== email) { + await trx + .update(users) + .set({ email }) + .where(eq(users.userId, existing.userId)); + + logger.info(`Server admin email updated`); } + } else { + const userId = generateId(15); await trx.update(users).set({ serverAdmin: false }); - await trx - .update(users) - .set({ - serverAdmin: true - }) - .where(eq(users.email, email)); + await db.insert(users).values({ + userId: userId, + email: email, + passwordHash, + dateCreated: moment().toISOString(), + serverAdmin: true, + emailVerified: true + }); - logger.info(`Server admin (${email}) set`); - return; + logger.info(`Server admin created`); } - - const userId = generateId(15); - - await trx.update(users).set({ serverAdmin: false }); - - await db.insert(users).values({ - userId: userId, - email: email, - passwordHash, - dateCreated: moment().toISOString(), - serverAdmin: true, - emailVerified: true - }); - - logger.info(`Server admin (${email}) created`); } catch (e) { logger.error(e); trx.rollback(); diff --git a/server/types/Auth.ts b/server/types/Auth.ts index 659a0e7..ce86623 100644 --- a/server/types/Auth.ts +++ b/server/types/Auth.ts @@ -1,6 +1,6 @@ import { Request } from "express"; -import { User } from "@server/db/schema"; -import { Session } from "@server/db/schema"; +import { User } from "@server/db/schemas"; +import { Session } from "@server/db/schemas"; export interface AuthenticatedRequest extends Request { user: User; diff --git a/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx b/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx index 8073e16..e4e0fb9 100644 --- a/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx +++ b/src/app/[orgId]/settings/access/roles/RolesDataTable.tsx @@ -39,6 +39,7 @@ export function RolesDataTable({ }: DataTableProps) { const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); + const [globalFilter, setGlobalFilter] = useState([]); const table = useReactTable({ data, @@ -49,6 +50,7 @@ export function RolesDataTable({ getSortedRowModel: getSortedRowModel(), onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), + onGlobalFilterChange: setGlobalFilter, initialState: { pagination: { pageSize: 20, @@ -57,7 +59,8 @@ export function RolesDataTable({ }, state: { sorting, - columnFilters + columnFilters, + globalFilter } }); @@ -67,15 +70,9 @@ export function RolesDataTable({
- table - .getColumn("name") - ?.setFilterValue(event.target.value) + value={globalFilter ?? ""} + onChange={(e) => + table.setGlobalFilter(String(e.target.value)) } className="w-full pl-8" /> diff --git a/src/app/[orgId]/settings/access/roles/RolesTable.tsx b/src/app/[orgId]/settings/access/roles/RolesTable.tsx index 66ed7b4..e8e9826 100644 --- a/src/app/[orgId]/settings/access/roles/RolesTable.tsx +++ b/src/app/[orgId]/settings/access/roles/RolesTable.tsx @@ -14,7 +14,7 @@ import ConfirmDeleteDialog from "@app/components/ConfirmDeleteDialog"; import { useOrgContext } from "@app/hooks/useOrgContext"; import { toast } from "@app/hooks/useToast"; import { RolesDataTable } from "./RolesDataTable"; -import { Role } from "@server/db/schema"; +import { Role } from "@server/db/schemas"; import CreateRoleForm from "./CreateRoleForm"; import DeleteRoleForm from "./DeleteRoleForm"; import { createApiClient } from "@app/lib/api"; diff --git a/src/app/[orgId]/settings/access/users/UsersDataTable.tsx b/src/app/[orgId]/settings/access/users/UsersDataTable.tsx index 87cebf7..b47424b 100644 --- a/src/app/[orgId]/settings/access/users/UsersDataTable.tsx +++ b/src/app/[orgId]/settings/access/users/UsersDataTable.tsx @@ -39,6 +39,7 @@ export function UsersDataTable({ }: DataTableProps) { const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); + const [globalFilter, setGlobalFilter] = useState([]); const table = useReactTable({ data, @@ -49,6 +50,7 @@ export function UsersDataTable({ getSortedRowModel: getSortedRowModel(), onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), + onGlobalFilterChange: setGlobalFilter, initialState: { pagination: { pageSize: 20, @@ -57,7 +59,8 @@ export function UsersDataTable({ }, state: { sorting, - columnFilters + columnFilters, + globalFilter } }); @@ -67,15 +70,9 @@ export function UsersDataTable({
- table - .getColumn("email") - ?.setFilterValue(event.target.value) + value={globalFilter ?? ""} + onChange={(e) => + table.setGlobalFilter(String(e.target.value)) } className="w-full pl-8" /> diff --git a/src/app/[orgId]/settings/resources/CreateResourceForm.tsx b/src/app/[orgId]/settings/resources/CreateResourceForm.tsx index 227bdf1..003a944 100644 --- a/src/app/[orgId]/settings/resources/CreateResourceForm.tsx +++ b/src/app/[orgId]/settings/resources/CreateResourceForm.tsx @@ -46,7 +46,7 @@ import { import { CaretSortIcon } from "@radix-ui/react-icons"; import CustomDomainInput from "./[resourceId]/CustomDomainInput"; import { AxiosResponse } from "axios"; -import { Resource } from "@server/db/schema"; +import { Resource } from "@server/db/schemas"; import { useOrgContext } from "@app/hooks/useOrgContext"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; diff --git a/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx b/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx index ffb6bf4..9cc0f79 100644 --- a/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx +++ b/src/app/[orgId]/settings/resources/ResourcesDataTable.tsx @@ -40,6 +40,7 @@ export function ResourcesDataTable({ }: ResourcesDataTableProps) { const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); + const [globalFilter, setGlobalFilter] = useState([]); const table = useReactTable({ data, @@ -50,6 +51,7 @@ export function ResourcesDataTable({ getSortedRowModel: getSortedRowModel(), onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), + onGlobalFilterChange: setGlobalFilter, initialState: { pagination: { pageSize: 20, @@ -58,7 +60,8 @@ export function ResourcesDataTable({ }, state: { sorting, - columnFilters + columnFilters, + globalFilter } }); @@ -68,15 +71,9 @@ export function ResourcesDataTable({
- table - .getColumn("name") - ?.setFilterValue(event.target.value) + value={globalFilter ?? ""} + onChange={(e) => + table.setGlobalFilter(String(e.target.value)) } className="w-full pl-8" /> diff --git a/src/app/[orgId]/settings/resources/ResourcesTable.tsx b/src/app/[orgId]/settings/resources/ResourcesTable.tsx index 6ff9e73..63a2b41 100644 --- a/src/app/[orgId]/settings/resources/ResourcesTable.tsx +++ b/src/app/[orgId]/settings/resources/ResourcesTable.tsx @@ -30,6 +30,9 @@ import { toast } from "@app/hooks/useToast"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import CopyToClipboard from "@app/components/CopyToClipboard"; +import { Switch } from "@app/components/ui/switch"; +import { AxiosResponse } from "axios"; +import { UpdateResourceResponse } from "@server/routers/resource"; export type ResourceRow = { id: number; @@ -42,6 +45,7 @@ export type ResourceRow = { http: boolean; protocol: string; proxyPort: number | null; + enabled: boolean; }; type ResourcesTableProps = { @@ -75,6 +79,26 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { }); }; + async function toggleResourceEnabled(val: boolean, resourceId: number) { + const res = await api + .post>( + `resource/${resourceId}`, + { + enabled: val + } + ) + .catch((e) => { + toast({ + variant: "destructive", + title: "Failed to toggle resource", + description: formatAxiosError( + e, + "An error occurred while updating the resource" + ) + }); + }); + } + const columns: ColumnDef[] = [ { accessorKey: "dots", @@ -224,6 +248,18 @@ export default function SitesTable({ resources, orgId }: ResourcesTableProps) { ); } }, + { + accessorKey: "enabled", + header: "Enabled", + cell: ({ row }) => ( + + toggleResourceEnabled(val, row.original.id) + } + /> + ) + }, { id: "actions", cell: ({ row }) => { diff --git a/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx b/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx index 0ab634d..330e0b6 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/ResourceInfoBox.tsx @@ -12,6 +12,7 @@ import { InfoSectionTitle } from "@app/components/InfoSection"; import Link from "next/link"; +import { Switch } from "@app/components/ui/switch"; type ResourceInfoBoxType = {}; @@ -27,7 +28,7 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) { Resource Information - + {resource.http ? ( <> @@ -88,6 +89,12 @@ export default function ResourceInfoBox({}: ResourceInfoBoxType) { )} + + Visibilty + + {resource.enabled ? "Enabled" : "Disabled"} + + diff --git a/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePasswordForm.tsx b/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePasswordForm.tsx index f3ab705..e4bdd1b 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePasswordForm.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePasswordForm.tsx @@ -28,7 +28,7 @@ import { } from "@app/components/Credenza"; import { formatAxiosError } from "@app/lib/api"; import { AxiosResponse } from "axios"; -import { Resource } from "@server/db/schema"; +import { Resource } from "@server/db/schemas"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; diff --git a/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePincodeForm.tsx b/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePincodeForm.tsx index 9d89d3b..58a997b 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePincodeForm.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/authentication/SetResourcePincodeForm.tsx @@ -28,7 +28,7 @@ import { } from "@app/components/Credenza"; import { formatAxiosError } from "@app/lib/api"; import { AxiosResponse } from "axios"; -import { Resource } from "@server/db/schema"; +import { Resource } from "@server/db/schemas"; import { InputOTP, InputOTPGroup, diff --git a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx index b60c68d..5d6cc81 100644 --- a/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx +++ b/src/app/[orgId]/settings/resources/[resourceId]/general/page.tsx @@ -60,7 +60,11 @@ import { SelectTrigger, SelectValue } from "@app/components/ui/select"; -import { UpdateResourceResponse } from "@server/routers/resource"; +import { + UpdateResourceResponse, + updateResourceRule +} from "@server/routers/resource"; +import { SwitchInput } from "@app/components/SwitchInput"; const GeneralFormSchema = z .object({ @@ -275,9 +279,52 @@ export default function GeneralForm() { setTransferLoading(false); } + async function toggleResourceEnabled(val: boolean) { + const res = await api + .post>( + `resource/${resource.resourceId}`, + { + enabled: val + } + ) + .catch((e) => { + toast({ + variant: "destructive", + title: "Failed to toggle resource", + description: formatAxiosError( + e, + "An error occurred while updating the resource" + ) + }); + }); + + updateResource({ + enabled: val + }); + } + return ( !loadingPage && ( + + + Visibility + + Completely enable or disable resource visibility + + + + { + await toggleResourceEnabled(val); + }} + /> + + + diff --git a/src/app/[orgId]/settings/resources/page.tsx b/src/app/[orgId]/settings/resources/page.tsx index b08ffd6..496ce2a 100644 --- a/src/app/[orgId]/settings/resources/page.tsx +++ b/src/app/[orgId]/settings/resources/page.tsx @@ -60,10 +60,11 @@ export default async function ResourcesPage(props: ResourcesPageProps) { ? "none" : resource.sso || resource.pincodeId !== null || - resource.pincodeId !== null || + resource.passwordId !== null || resource.whitelist ? "protected" - : "not_protected" + : "not_protected", + enabled: resource.enabled }; }); diff --git a/src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx b/src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx new file mode 100644 index 0000000..5f44ca5 --- /dev/null +++ b/src/app/[orgId]/settings/share-links/AccessTokenUsage.tsx @@ -0,0 +1,105 @@ +"use client"; + +import { useState } from "react"; +import { Check, Copy, Info, InfoIcon } from "lucide-react"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle +} from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { useEnvContext } from "@app/hooks/useEnvContext"; +import CopyToClipboard from "@app/components/CopyToClipboard"; +import CopyTextBox from "@app/components/CopyTextBox"; + +interface AccessTokenSectionProps { + token: string; + tokenId: string; + resourceUrl: string; +} + +export default function AccessTokenSection({ + token, + tokenId, + resourceUrl +}: AccessTokenSectionProps) { + const { env } = useEnvContext(); + + const [copied, setCopied] = useState(null); + + const copyToClipboard = (text: string, type: string) => { + navigator.clipboard.writeText(text); + setCopied(type); + setTimeout(() => setCopied(null), 2000); + }; + + return ( + <> +
+

+ Your access token can be passed in two ways: as a query + parameter or in the request headers. These must be passed + from the client on every request for authenticated access. +

+
+ + + + Access Token + Usage Examples + + + +
+
Token ID
+ +
+ +
+
Token
+ +
+
+ + +
+

Request Headers

+ +
+ +
+

Query Parameter

+ +
+ + + + + Important Note + + + For security reasons, using headers is recommended + over query parameters when possible, as query + parameters may be logged in server logs or browser + history. + + +
+
+ +
+ Keep your access token secure. Do not share it in publicly + accessible areas or client-side code. +
+ + ); +} diff --git a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx index fcae374..bc8701a 100644 --- a/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx +++ b/src/app/[orgId]/settings/share-links/CreateShareLinkForm.tsx @@ -4,7 +4,6 @@ import { Button } from "@app/components/ui/button"; import { Form, FormControl, - FormDescription, FormField, FormItem, FormLabel, @@ -20,7 +19,6 @@ import { } from "@app/components/ui/select"; import { toast } from "@app/hooks/useToast"; import { zodResolver } from "@hookform/resolvers/zod"; -import { InviteUserBody, InviteUserResponse } from "@server/routers/user"; import { AxiosResponse } from "axios"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; @@ -37,7 +35,6 @@ import { CredenzaTitle } from "@app/components/Credenza"; import { useOrgContext } from "@app/hooks/useOrgContext"; -import { ListRolesResponse } from "@server/routers/role"; import { formatAxiosError } from "@app/lib/api"; import { cn } from "@app/lib/cn"; import { createApiClient } from "@app/lib/api"; @@ -58,12 +55,9 @@ import { CommandList } from "@app/components/ui/command"; import { CheckIcon, ChevronsUpDown } from "lucide-react"; -import { register } from "module"; -import { Label } from "@app/components/ui/label"; import { Checkbox } from "@app/components/ui/checkbox"; import { GenerateAccessTokenResponse } from "@server/routers/accessToken"; import { - constructDirectShareLink, constructShareLink } from "@app/lib/shareLinks"; import { ShareLinkRow } from "./ShareLinksTable"; @@ -73,6 +67,7 @@ import { CollapsibleContent, CollapsibleTrigger } from "@app/components/ui/collapsible"; +import AccessTokenSection from "./AccessTokenUsage"; type FormProps = { open: boolean; @@ -100,7 +95,8 @@ export default function CreateShareLinkForm({ const api = createApiClient({ env }); const [link, setLink] = useState(null); - const [directLink, setDirectLink] = useState(null); + const [accessTokenId, setAccessTokenId] = useState(null); + const [accessToken, setAccessToken] = useState(null); const [loading, setLoading] = useState(false); const [neverExpire, setNeverExpire] = useState(false); @@ -224,21 +220,15 @@ export default function CreateShareLinkForm({ if (res && res.data.data.accessTokenId) { const token = res.data.data; - const link = constructShareLink( - values.resourceId, - token.accessTokenId, - token.accessToken - ); + const link = constructShareLink(token.accessToken); setLink(link); - const directLink = constructDirectShareLink( - env.server.resourceAccessTokenParam, - values.resourceUrl, - token.accessTokenId, - token.accessToken - ); - setDirectLink(directLink); - const resource = resources.find((r) => r.resourceId === values.resourceId); + setAccessToken(token.accessToken); + setAccessTokenId(token.accessTokenId); + + const resource = resources.find( + (r) => r.resourceId === values.resourceId + ); onCreated?.({ accessTokenId: token.accessTokenId, @@ -247,7 +237,7 @@ export default function CreateShareLinkForm({ title: token.title, createdAt: token.createdAt, expiresAt: token.expiresAt, - siteName: resource?.siteName || null, + siteName: resource?.siteName || null }); } @@ -518,8 +508,7 @@ export default function CreateShareLinkForm({ className="p-0 flex items-center justify-between w-full" >

- See alternative share - links + See Access Token Usage

@@ -531,26 +520,21 @@ export default function CreateShareLinkForm({
- {directLink && ( + {accessTokenId && accessToken && (
-
-

- This link does not - require visiting in a - browser to complete the - redirect. It contains - the access token - directly in the URL, - which can be useful for - sharing with clients - that do not support - redirects. -

)}
diff --git a/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx b/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx index 612a179..ea0cfd9 100644 --- a/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx +++ b/src/app/[orgId]/settings/share-links/ShareLinksDataTable.tsx @@ -40,6 +40,7 @@ export function ShareLinksDataTable({ }: ShareLinksDataTableProps) { const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); + const [globalFilter, setGlobalFilter] = useState([]); const table = useReactTable({ data, @@ -50,6 +51,7 @@ export function ShareLinksDataTable({ getSortedRowModel: getSortedRowModel(), onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), + onGlobalFilterChange: setGlobalFilter, initialState: { pagination: { pageSize: 20, @@ -58,7 +60,8 @@ export function ShareLinksDataTable({ }, state: { sorting, - columnFilters + columnFilters, + globalFilter } }); @@ -68,15 +71,9 @@ export function ShareLinksDataTable({
- table - .getColumn("title") - ?.setFilterValue(event.target.value) + value={globalFilter ?? ""} + onChange={(e) => + table.setGlobalFilter(String(e.target.value)) } className="w-full pl-8" /> diff --git a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx index 546b2a5..fb90ba0 100644 --- a/src/app/[orgId]/settings/sites/CreateSiteForm.tsx +++ b/src/app/[orgId]/settings/sites/CreateSiteForm.tsx @@ -291,7 +291,7 @@ PersistentKeepalive = 5` - This is the the display name for the site. + This is the display name for the site. )} diff --git a/src/app/[orgId]/settings/sites/SitesDataTable.tsx b/src/app/[orgId]/settings/sites/SitesDataTable.tsx index a30bab1..ef6909f 100644 --- a/src/app/[orgId]/settings/sites/SitesDataTable.tsx +++ b/src/app/[orgId]/settings/sites/SitesDataTable.tsx @@ -40,6 +40,7 @@ export function SitesDataTable({ }: DataTableProps) { const [sorting, setSorting] = useState([]); const [columnFilters, setColumnFilters] = useState([]); + const [globalFilter, setGlobalFilter] = useState([]); const table = useReactTable({ data, @@ -50,6 +51,7 @@ export function SitesDataTable({ getSortedRowModel: getSortedRowModel(), onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), + onGlobalFilterChange: setGlobalFilter, initialState: { pagination: { pageSize: 20, @@ -58,7 +60,8 @@ export function SitesDataTable({ }, state: { sorting, - columnFilters + columnFilters, + globalFilter } }); @@ -68,15 +71,9 @@ export function SitesDataTable({
- table - .getColumn("name") - ?.setFilterValue(event.target.value) + value={globalFilter ?? ""} + onChange={(e) => + table.setGlobalFilter(String(e.target.value)) } className="w-full pl-8" /> diff --git a/src/app/[orgId]/settings/sites/create/page.tsx b/src/app/[orgId]/settings/sites/create/page.tsx index a800270..f078b9d 100644 --- a/src/app/[orgId]/settings/sites/create/page.tsx +++ b/src/app/[orgId]/settings/sites/create/page.tsx @@ -532,7 +532,7 @@ PersistentKeepalive = 5`; - This is the the + This is the display name for the site. @@ -617,7 +617,7 @@ PersistentKeepalive = 5`; - + Save Your Credentials @@ -777,7 +777,7 @@ PersistentKeepalive = 5`; - + Save Your Credentials diff --git a/src/app/auth/resource/[resourceId]/AccessToken.tsx b/src/app/auth/resource/[resourceId]/AccessToken.tsx index b70d75c..467ea03 100644 --- a/src/app/auth/resource/[resourceId]/AccessToken.tsx +++ b/src/app/auth/resource/[resourceId]/AccessToken.tsx @@ -15,17 +15,13 @@ import Link from "next/link"; import { useEffect, useState } from "react"; type AccessTokenProps = { - accessTokenId: string | undefined; - accessToken: string | undefined; - resourceId: number; - redirectUrl: string; + token: string; + resourceId?: number; }; export default function AccessToken({ - accessTokenId, - accessToken, - resourceId, - redirectUrl + token, + resourceId }: AccessTokenProps) { const [loading, setLoading] = useState(true); const [isValid, setIsValid] = useState(false); @@ -43,11 +39,49 @@ export default function AccessToken({ } useEffect(() => { - if (!accessTokenId || !accessToken) { + if (!token) { setLoading(false); return; } + let accessTokenId = ""; + let accessToken = ""; + + const parts = token.split("."); + + if (parts.length === 2) { + accessTokenId = parts[0]; + accessToken = parts[1]; + } else if (parts.length === 1) { + accessToken = parts[0]; + } else { + setLoading(false); + return; + } + + async function checkSHA256() { + try { + const res = await api.post< + AxiosResponse + >(`/auth/access-token`, { + accessToken, + accessTokenId + }); + + if (res.data.data.session) { + setIsValid(true); + window.location.href = appendRequestToken( + res.data.data.redirectUrl!, + res.data.data.session + ); + } + } catch (e) { + console.error("Error checking access token", e); + } finally { + setLoading(false); + } + } + async function check() { try { const res = await api.post< @@ -60,7 +94,7 @@ export default function AccessToken({ if (res.data.data.session) { setIsValid(true); window.location.href = appendRequestToken( - redirectUrl, + res.data.data.redirectUrl!, res.data.data.session ); } @@ -71,8 +105,13 @@ export default function AccessToken({ } } - check(); - }, [accessTokenId, accessToken]); + if (!accessTokenId) { + // no access token id so check the sha256 + checkSHA256(); + } else { + check(); + } + }, [token]); function renderTitle() { if (isValid) { diff --git a/src/app/auth/resource/[resourceId]/page.tsx b/src/app/auth/resource/[resourceId]/page.tsx index cc576a0..006faa4 100644 --- a/src/app/auth/resource/[resourceId]/page.tsx +++ b/src/app/auth/resource/[resourceId]/page.tsx @@ -118,14 +118,11 @@ export default async function ResourceAuthPage(props: { } if (searchParams.token) { - const [accessTokenId, accessToken] = searchParams.token.split("."); return (
); diff --git a/src/app/s/[accessToken]/page.tsx b/src/app/s/[accessToken]/page.tsx new file mode 100644 index 0000000..d28ff7b --- /dev/null +++ b/src/app/s/[accessToken]/page.tsx @@ -0,0 +1,13 @@ +import AccessToken from "@app/app/auth/resource/[resourceId]/AccessToken"; + +export default async function ResourceAuthPage(props: { + params: Promise<{ accessToken: string }>; +}) { + const params = await props.params; + + return ( +
+ +
+ ); +} diff --git a/src/components/CopyTextBox.tsx b/src/components/CopyTextBox.tsx index 877b5a0..b6c838e 100644 --- a/src/components/CopyTextBox.tsx +++ b/src/components/CopyTextBox.tsx @@ -28,11 +28,11 @@ export default function CopyTextBox({ return (
{text}
             
- - - - - - - Limited Supporter - - -

$25

-
    -
  • - - - For 5 or less users - -
  • -
  • - - - Lifetime purchase - -
  • -
  • - - - Supporter status - -
  • -
-
- - {supporterStatus?.tier !== - "Limited Supporter" ? ( +
+

Please select the option that best suits you.

+
+ + + Full Supporter + + +

$95

+
    +
  • + + + For the whole server + +
  • +
  • + + + Lifetime purchase + +
  • +
  • + + + Supporter status + +
  • +
+
+ - ) : ( - - )} - -
+ + + + + + Limited Supporter + + +

$25

+
    +
  • + + + For 5 or less users + +
  • +
  • + + + Lifetime purchase + +
  • +
  • + + + Supporter status + +
  • +
+
+ + {supporterStatus?.tier !== + "Limited Supporter" ? ( + + + + ) : ( + + )} + +
+
-
+