mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-06 10:46:38 +00:00
Fix: Extend Basic Auth compatibility with browsers
This commit is contained in:
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Създайте своя организация, сайт и ресурси",
|
"setupCreate": "Създайте своя организация, сайт и ресурси",
|
||||||
"setupNewOrg": "Нова организация",
|
"setupNewOrg": "Нова организация",
|
||||||
"setupCreateOrg": "Създаване на организация",
|
"setupCreateOrg": "Създаване на организация",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Vytvořte si organizaci, lokalitu a služby",
|
"setupCreate": "Vytvořte si organizaci, lokalitu a služby",
|
||||||
"setupNewOrg": "Nová organizace",
|
"setupNewOrg": "Nová organizace",
|
||||||
"setupCreateOrg": "Vytvořit organizaci",
|
"setupCreateOrg": "Vytvořit organizaci",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Erstelle eine Organisation, einen Standort und Ressourcen",
|
"setupCreate": "Erstelle eine Organisation, einen Standort und Ressourcen",
|
||||||
"setupNewOrg": "Neue Organisation",
|
"setupNewOrg": "Neue Organisation",
|
||||||
"setupCreateOrg": "Organisation erstellen",
|
"setupCreateOrg": "Organisation erstellen",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Create your organization, site, and resources",
|
"setupCreate": "Create your organization, site, and resources",
|
||||||
"setupNewOrg": "New Organization",
|
"setupNewOrg": "New Organization",
|
||||||
"setupCreateOrg": "Create Organization",
|
"setupCreateOrg": "Create Organization",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Crea tu organización, sitio y recursos",
|
"setupCreate": "Crea tu organización, sitio y recursos",
|
||||||
"setupNewOrg": "Nueva organización",
|
"setupNewOrg": "Nueva organización",
|
||||||
"setupCreateOrg": "Crear organización",
|
"setupCreateOrg": "Crear organización",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Activez cette option pour forcer une réponse 401 lorsqu'un jeton d'authentification est manquant. Cette option est nécessaire pour les navigateurs et certaines bibliothèques HTTP qui n'envoient pas d'informations d'identification sans challenge du serveur.",
|
||||||
|
"headerAuthCompatibility": "Compatibilité étendue",
|
||||||
"setupCreate": "Créez votre organisation, vos nœuds et vos ressources",
|
"setupCreate": "Créez votre organisation, vos nœuds et vos ressources",
|
||||||
"setupNewOrg": "Nouvelle organisation",
|
"setupNewOrg": "Nouvelle organisation",
|
||||||
"setupCreateOrg": "Créer une organisation",
|
"setupCreateOrg": "Créer une organisation",
|
||||||
@@ -1834,23 +1836,23 @@
|
|||||||
"rewritePathDescription": "Réécrivez éventuellement le chemin avant de le transmettre à la cible.",
|
"rewritePathDescription": "Réécrivez éventuellement le chemin avant de le transmettre à la cible.",
|
||||||
"continueToApplication": "Continuer vers l'application",
|
"continueToApplication": "Continuer vers l'application",
|
||||||
"checkingInvite": "Vérification de l'invitation",
|
"checkingInvite": "Vérification de l'invitation",
|
||||||
"setResourceHeaderAuth": "Définir l\\'authentification d\\'en-tête de la ressource",
|
"setResourceHeaderAuth": "Définir l\\'authentification via en-tête de la ressource",
|
||||||
"resourceHeaderAuthRemove": "Supprimer l'authentification de l'en-tête",
|
"resourceHeaderAuthRemove": "Supprimer l'authentification via en-tête",
|
||||||
"resourceHeaderAuthRemoveDescription": "Authentification de l'en-tête supprimée avec succès.",
|
"resourceHeaderAuthRemoveDescription": "En-tête d'authentification supprimée avec succès.",
|
||||||
"resourceErrorHeaderAuthRemove": "Échec de la suppression de l'authentification de l'en-tête",
|
"resourceErrorHeaderAuthRemove": "Échec de la suppression de l'authentification via en-tête",
|
||||||
"resourceErrorHeaderAuthRemoveDescription": "Impossible de supprimer l'authentification de l'en-tête de la ressource.",
|
"resourceErrorHeaderAuthRemoveDescription": "Impossible de supprimer l'authentification via en-tête sur la ressource.",
|
||||||
"resourceHeaderAuthProtectionEnabled": "Authentification de l'en-tête activée",
|
"resourceHeaderAuthProtectionEnabled": "Authentification par en-tête Activée",
|
||||||
"resourceHeaderAuthProtectionDisabled": "L'authentification de l'en-tête est désactivée",
|
"resourceHeaderAuthProtectionDisabled": "Authentification par en-tête Désactivée",
|
||||||
"headerAuthRemove": "Supprimer l'authentification de l'en-tête",
|
"headerAuthRemove": "Supprimer l'authentification via en-tête",
|
||||||
"headerAuthAdd": "Ajouter l'authentification de l'en-tête",
|
"headerAuthAdd": "Ajouter une en-tête d'authentification",
|
||||||
"resourceErrorHeaderAuthSetup": "Impossible de définir l'authentification de l'en-tête",
|
"resourceErrorHeaderAuthSetup": "Impossible de définir l'authentification via en-tête",
|
||||||
"resourceErrorHeaderAuthSetupDescription": "Impossible de définir l'authentification de l'en-tête pour la ressource.",
|
"resourceErrorHeaderAuthSetupDescription": "Impossible de définir l'authentification via en-tête pour la ressource.",
|
||||||
"resourceHeaderAuthSetup": "Authentification de l'en-tête définie avec succès",
|
"resourceHeaderAuthSetup": "Authentification via en-tête définie avec succès",
|
||||||
"resourceHeaderAuthSetupDescription": "L'authentification de l'en-tête a été définie avec succès.",
|
"resourceHeaderAuthSetupDescription": "L'authentification via en-tête a été définie avec succès.",
|
||||||
"resourceHeaderAuthSetupTitle": "Authentification de l'en-tête",
|
"resourceHeaderAuthSetupTitle": "Authentification via en-tête",
|
||||||
"resourceHeaderAuthSetupTitleDescription": "Définissez les identifiants d'authentification de base (nom d'utilisateur et mot de passe) pour protéger cette ressource avec l'authentification de l'en-tête HTTP. Accédez-y en utilisant le format https://username:password@resource.example.com",
|
"resourceHeaderAuthSetupTitleDescription": "Définissez les identifiants d'authentification de base (nom d'utilisateur et mot de passe) pour protéger cette ressource avec l'authentification via en-tête HTTP. Accédez-y en utilisant le format https://username:password@resource.example.com",
|
||||||
"resourceHeaderAuthSubmit": "Authentification de l'en-tête",
|
"resourceHeaderAuthSubmit": "Activer la protection via en-tête",
|
||||||
"actionSetResourceHeaderAuth": "Authentification de l'en-tête",
|
"actionSetResourceHeaderAuth": "Authentification via en-tête",
|
||||||
"enterpriseEdition": "Édition Entreprise",
|
"enterpriseEdition": "Édition Entreprise",
|
||||||
"unlicensed": "Sans licence",
|
"unlicensed": "Sans licence",
|
||||||
"beta": "Bêta",
|
"beta": "Bêta",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Crea la tua organizzazione, sito e risorse",
|
"setupCreate": "Crea la tua organizzazione, sito e risorse",
|
||||||
"setupNewOrg": "Nuova Organizzazione",
|
"setupNewOrg": "Nuova Organizzazione",
|
||||||
"setupCreateOrg": "Crea Organizzazione",
|
"setupCreateOrg": "Crea Organizzazione",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "조직, 사이트 및 리소스를 생성하십시오.",
|
"setupCreate": "조직, 사이트 및 리소스를 생성하십시오.",
|
||||||
"setupNewOrg": "새 조직",
|
"setupNewOrg": "새 조직",
|
||||||
"setupCreateOrg": "조직 생성",
|
"setupCreateOrg": "조직 생성",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Lag din organisasjon, område og dine ressurser",
|
"setupCreate": "Lag din organisasjon, område og dine ressurser",
|
||||||
"setupNewOrg": "Ny Organisasjon",
|
"setupNewOrg": "Ny Organisasjon",
|
||||||
"setupCreateOrg": "Opprett organisasjon",
|
"setupCreateOrg": "Opprett organisasjon",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Maak uw organisatie, site en bronnen aan",
|
"setupCreate": "Maak uw organisatie, site en bronnen aan",
|
||||||
"setupNewOrg": "Nieuwe organisatie",
|
"setupNewOrg": "Nieuwe organisatie",
|
||||||
"setupCreateOrg": "Nieuwe organisatie aanmaken",
|
"setupCreateOrg": "Nieuwe organisatie aanmaken",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Utwórz swoją organizację, witrynę i zasoby",
|
"setupCreate": "Utwórz swoją organizację, witrynę i zasoby",
|
||||||
"setupNewOrg": "Nowa organizacja",
|
"setupNewOrg": "Nowa organizacja",
|
||||||
"setupCreateOrg": "Utwórz organizację",
|
"setupCreateOrg": "Utwórz organizację",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Crie sua organização, site e recursos",
|
"setupCreate": "Crie sua organização, site e recursos",
|
||||||
"setupNewOrg": "Nova organização",
|
"setupNewOrg": "Nova organização",
|
||||||
"setupCreateOrg": "Criar Organização",
|
"setupCreateOrg": "Criar Organização",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Создайте свою организацию, сайт и ресурсы",
|
"setupCreate": "Создайте свою организацию, сайт и ресурсы",
|
||||||
"setupNewOrg": "Новая организация",
|
"setupNewOrg": "Новая организация",
|
||||||
"setupCreateOrg": "Создать организацию",
|
"setupCreateOrg": "Создать организацию",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "Organizasyonunuzu, sitenizi ve kaynaklarınızı oluşturun",
|
"setupCreate": "Organizasyonunuzu, sitenizi ve kaynaklarınızı oluşturun",
|
||||||
"setupNewOrg": "Yeni Organizasyon",
|
"setupNewOrg": "Yeni Organizasyon",
|
||||||
"setupCreateOrg": "Organizasyon Oluştur",
|
"setupCreateOrg": "Organizasyon Oluştur",
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{
|
{
|
||||||
|
"headerAuthCompatibilityInfo": "Enable this to force a 401 Unauthorized response when an authentication token is missing. This is required for browsers or specific HTTP libraries that do not send credentials without a server challenge.",
|
||||||
|
"headerAuthCompatibility": "Extended compatibility",
|
||||||
"setupCreate": "创建您的第一个组织、网站和资源",
|
"setupCreate": "创建您的第一个组织、网站和资源",
|
||||||
"setupNewOrg": "新建组织",
|
"setupNewOrg": "新建组织",
|
||||||
"setupCreateOrg": "创建组织",
|
"setupCreateOrg": "创建组织",
|
||||||
|
|||||||
@@ -419,6 +419,14 @@ export const resourceHeaderAuth = pgTable("resourceHeaderAuth", {
|
|||||||
headerAuthHash: varchar("headerAuthHash").notNull()
|
headerAuthHash: varchar("headerAuthHash").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const resourceHeaderAuthExtendedCompatibility = pgTable("resourceHeaderAuthExtendedCompatibility", {
|
||||||
|
headerAuthExtendedCompatibilityId: serial("headerAuthExtendedCompatibilityId").primaryKey(),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
|
extendedCompatibilityIsActivated: boolean("extendedCompatibilityIsActivated").notNull().default(false),
|
||||||
|
});
|
||||||
|
|
||||||
export const resourceAccessToken = pgTable("resourceAccessToken", {
|
export const resourceAccessToken = pgTable("resourceAccessToken", {
|
||||||
accessTokenId: varchar("accessTokenId").primaryKey(),
|
accessTokenId: varchar("accessTokenId").primaryKey(),
|
||||||
orgId: varchar("orgId")
|
orgId: varchar("orgId")
|
||||||
@@ -781,6 +789,7 @@ export type ResourceSession = InferSelectModel<typeof resourceSessions>;
|
|||||||
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
|
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
|
||||||
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
|
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
|
||||||
export type ResourceHeaderAuth = InferSelectModel<typeof resourceHeaderAuth>;
|
export type ResourceHeaderAuth = InferSelectModel<typeof resourceHeaderAuth>;
|
||||||
|
export type ResourceHeaderAuthExtendedCompatibility = InferSelectModel<typeof resourceHeaderAuthExtendedCompatibility>;
|
||||||
export type ResourceOtp = InferSelectModel<typeof resourceOtp>;
|
export type ResourceOtp = InferSelectModel<typeof resourceOtp>;
|
||||||
export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
|
export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
|
||||||
export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
|
export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { db, loginPage, LoginPage, loginPageOrg, Org, orgs } from "@server/db";
|
import {
|
||||||
|
db, loginPage, LoginPage, loginPageOrg, Org, orgs,
|
||||||
|
} from "@server/db";
|
||||||
import {
|
import {
|
||||||
Resource,
|
Resource,
|
||||||
ResourcePassword,
|
ResourcePassword,
|
||||||
@@ -14,7 +16,9 @@ import {
|
|||||||
sessions,
|
sessions,
|
||||||
userOrgs,
|
userOrgs,
|
||||||
userResources,
|
userResources,
|
||||||
users
|
users,
|
||||||
|
ResourceHeaderAuthExtendedCompatibility,
|
||||||
|
resourceHeaderAuthExtendedCompatibility
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
|
|
||||||
@@ -23,6 +27,7 @@ export type ResourceWithAuth = {
|
|||||||
pincode: ResourcePincode | null;
|
pincode: ResourcePincode | null;
|
||||||
password: ResourcePassword | null;
|
password: ResourcePassword | null;
|
||||||
headerAuth: ResourceHeaderAuth | null;
|
headerAuth: ResourceHeaderAuth | null;
|
||||||
|
headerAuthExtendedCompatibility: ResourceHeaderAuthExtendedCompatibility | null
|
||||||
org: Org;
|
org: Org;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,6 +57,10 @@ export async function getResourceByDomain(
|
|||||||
resourceHeaderAuth,
|
resourceHeaderAuth,
|
||||||
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
|
.leftJoin(
|
||||||
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
|
eq(resourceHeaderAuthExtendedCompatibility.resourceId, resources.resourceId)
|
||||||
|
)
|
||||||
.innerJoin(
|
.innerJoin(
|
||||||
orgs,
|
orgs,
|
||||||
eq(orgs.orgId, resources.orgId)
|
eq(orgs.orgId, resources.orgId)
|
||||||
@@ -68,6 +77,7 @@ export async function getResourceByDomain(
|
|||||||
pincode: result.resourcePincode,
|
pincode: result.resourcePincode,
|
||||||
password: result.resourcePassword,
|
password: result.resourcePassword,
|
||||||
headerAuth: result.resourceHeaderAuth,
|
headerAuth: result.resourceHeaderAuth,
|
||||||
|
headerAuthExtendedCompatibility: result.resourceHeaderAuthExtendedCompatibility,
|
||||||
org: result.orgs
|
org: result.orgs
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
import { randomUUID } from "crypto";
|
import {randomUUID} from "crypto";
|
||||||
import { InferSelectModel } from "drizzle-orm";
|
import {InferSelectModel} from "drizzle-orm";
|
||||||
import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core";
|
import {sqliteTable, text, integer, index} from "drizzle-orm/sqlite-core";
|
||||||
import { boolean } from "yargs";
|
import {boolean} from "yargs";
|
||||||
|
|
||||||
export const domains = sqliteTable("domains", {
|
export const domains = sqliteTable("domains", {
|
||||||
domainId: text("domainId").primaryKey(),
|
domainId: text("domainId").primaryKey(),
|
||||||
baseDomain: text("baseDomain").notNull(),
|
baseDomain: text("baseDomain").notNull(),
|
||||||
configManaged: integer("configManaged", { mode: "boolean" })
|
configManaged: integer("configManaged", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
type: text("type"), // "ns", "cname", "wildcard"
|
type: text("type"), // "ns", "cname", "wildcard"
|
||||||
verified: integer("verified", { mode: "boolean" }).notNull().default(false),
|
verified: integer("verified", {mode: "boolean"}).notNull().default(false),
|
||||||
failed: integer("failed", { mode: "boolean" }).notNull().default(false),
|
failed: integer("failed", {mode: "boolean"}).notNull().default(false),
|
||||||
tries: integer("tries").notNull().default(0),
|
tries: integer("tries").notNull().default(0),
|
||||||
certResolver: text("certResolver"),
|
certResolver: text("certResolver"),
|
||||||
preferWildcardCert: integer("preferWildcardCert", { mode: "boolean" })
|
preferWildcardCert: integer("preferWildcardCert", {mode: "boolean"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const dnsRecords = sqliteTable("dnsRecords", {
|
export const dnsRecords = sqliteTable("dnsRecords", {
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: integer("id").primaryKey({autoIncrement: true}),
|
||||||
domainId: text("domainId")
|
domainId: text("domainId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => domains.domainId, { onDelete: "cascade" }),
|
.references(() => domains.domainId, {onDelete: "cascade"}),
|
||||||
|
|
||||||
recordType: text("recordType").notNull(), // "NS" | "CNAME" | "A" | "TXT"
|
recordType: text("recordType").notNull(), // "NS" | "CNAME" | "A" | "TXT"
|
||||||
baseDomain: text("baseDomain"),
|
baseDomain: text("baseDomain"),
|
||||||
value: text("value").notNull(),
|
value: text("value").notNull(),
|
||||||
verified: integer("verified", { mode: "boolean" }).notNull().default(false),
|
verified: integer("verified", {mode: "boolean"}).notNull().default(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ export const orgs = sqliteTable("orgs", {
|
|||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
subnet: text("subnet"),
|
subnet: text("subnet"),
|
||||||
createdAt: text("createdAt"),
|
createdAt: text("createdAt"),
|
||||||
requireTwoFactor: integer("requireTwoFactor", { mode: "boolean" }),
|
requireTwoFactor: integer("requireTwoFactor", {mode: "boolean"}),
|
||||||
maxSessionLengthHours: integer("maxSessionLengthHours"), // hours
|
maxSessionLengthHours: integer("maxSessionLengthHours"), // hours
|
||||||
passwordExpiryDays: integer("passwordExpiryDays"), // days
|
passwordExpiryDays: integer("passwordExpiryDays"), // days
|
||||||
settingsLogRetentionDaysRequest: integer("settingsLogRetentionDaysRequest") // where 0 = dont keep logs and -1 = keep forever
|
settingsLogRetentionDaysRequest: integer("settingsLogRetentionDaysRequest") // where 0 = dont keep logs and -1 = keep forever
|
||||||
@@ -52,23 +52,23 @@ export const orgs = sqliteTable("orgs", {
|
|||||||
export const userDomains = sqliteTable("userDomains", {
|
export const userDomains = sqliteTable("userDomains", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, { onDelete: "cascade" }),
|
.references(() => users.userId, {onDelete: "cascade"}),
|
||||||
domainId: text("domainId")
|
domainId: text("domainId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => domains.domainId, { onDelete: "cascade" })
|
.references(() => domains.domainId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const orgDomains = sqliteTable("orgDomains", {
|
export const orgDomains = sqliteTable("orgDomains", {
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
.references(() => orgs.orgId, {onDelete: "cascade"}),
|
||||||
domainId: text("domainId")
|
domainId: text("domainId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => domains.domainId, { onDelete: "cascade" })
|
.references(() => domains.domainId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sites = sqliteTable("sites", {
|
export const sites = sqliteTable("sites", {
|
||||||
siteId: integer("siteId").primaryKey({ autoIncrement: true }),
|
siteId: integer("siteId").primaryKey({autoIncrement: true}),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.references(() => orgs.orgId, {
|
.references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
@@ -85,7 +85,7 @@ export const sites = sqliteTable("sites", {
|
|||||||
megabytesOut: integer("bytesOut").default(0),
|
megabytesOut: integer("bytesOut").default(0),
|
||||||
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
||||||
type: text("type").notNull(), // "newt" or "wireguard"
|
type: text("type").notNull(), // "newt" or "wireguard"
|
||||||
online: integer("online", { mode: "boolean" }).notNull().default(false),
|
online: integer("online", {mode: "boolean"}).notNull().default(false),
|
||||||
|
|
||||||
// exit node stuff that is how to connect to the site when it has a wg server
|
// exit node stuff that is how to connect to the site when it has a wg server
|
||||||
address: text("address"), // this is the address of the wireguard interface in newt
|
address: text("address"), // this is the address of the wireguard interface in newt
|
||||||
@@ -93,15 +93,15 @@ export const sites = sqliteTable("sites", {
|
|||||||
publicKey: text("publicKey"), // TODO: Fix typo in publicKey
|
publicKey: text("publicKey"), // TODO: Fix typo in publicKey
|
||||||
lastHolePunch: integer("lastHolePunch"),
|
lastHolePunch: integer("lastHolePunch"),
|
||||||
listenPort: integer("listenPort"),
|
listenPort: integer("listenPort"),
|
||||||
dockerSocketEnabled: integer("dockerSocketEnabled", { mode: "boolean" })
|
dockerSocketEnabled: integer("dockerSocketEnabled", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(true),
|
.default(true),
|
||||||
remoteSubnets: text("remoteSubnets") // comma-separated list of subnets that this site can access
|
remoteSubnets: text("remoteSubnets") // comma-separated list of subnets that this site can access
|
||||||
});
|
});
|
||||||
|
|
||||||
export const resources = sqliteTable("resources", {
|
export const resources = sqliteTable("resources", {
|
||||||
resourceId: integer("resourceId").primaryKey({ autoIncrement: true }),
|
resourceId: integer("resourceId").primaryKey({autoIncrement: true}),
|
||||||
resourceGuid: text("resourceGuid", { length: 36 })
|
resourceGuid: text("resourceGuid", {length: 36})
|
||||||
.unique()
|
.unique()
|
||||||
.notNull()
|
.notNull()
|
||||||
.$defaultFn(() => randomUUID()),
|
.$defaultFn(() => randomUUID()),
|
||||||
@@ -117,38 +117,38 @@ export const resources = sqliteTable("resources", {
|
|||||||
domainId: text("domainId").references(() => domains.domainId, {
|
domainId: text("domainId").references(() => domains.domainId, {
|
||||||
onDelete: "set null"
|
onDelete: "set null"
|
||||||
}),
|
}),
|
||||||
ssl: integer("ssl", { mode: "boolean" }).notNull().default(false),
|
ssl: integer("ssl", {mode: "boolean"}).notNull().default(false),
|
||||||
blockAccess: integer("blockAccess", { mode: "boolean" })
|
blockAccess: integer("blockAccess", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
sso: integer("sso", { mode: "boolean" }).notNull().default(true),
|
sso: integer("sso", {mode: "boolean"}).notNull().default(true),
|
||||||
http: integer("http", { mode: "boolean" }).notNull().default(true),
|
http: integer("http", {mode: "boolean"}).notNull().default(true),
|
||||||
protocol: text("protocol").notNull(),
|
protocol: text("protocol").notNull(),
|
||||||
proxyPort: integer("proxyPort"),
|
proxyPort: integer("proxyPort"),
|
||||||
emailWhitelistEnabled: integer("emailWhitelistEnabled", { mode: "boolean" })
|
emailWhitelistEnabled: integer("emailWhitelistEnabled", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
applyRules: integer("applyRules", { mode: "boolean" })
|
applyRules: integer("applyRules", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
enabled: integer("enabled", {mode: "boolean"}).notNull().default(true),
|
||||||
stickySession: integer("stickySession", { mode: "boolean" })
|
stickySession: integer("stickySession", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
tlsServerName: text("tlsServerName"),
|
tlsServerName: text("tlsServerName"),
|
||||||
setHostHeader: text("setHostHeader"),
|
setHostHeader: text("setHostHeader"),
|
||||||
enableProxy: integer("enableProxy", { mode: "boolean" }).default(true),
|
enableProxy: integer("enableProxy", {mode: "boolean"}).default(true),
|
||||||
skipToIdpId: integer("skipToIdpId").references(() => idp.idpId, {
|
skipToIdpId: integer("skipToIdpId").references(() => idp.idpId, {
|
||||||
onDelete: "set null"
|
onDelete: "set null"
|
||||||
}),
|
}),
|
||||||
headers: text("headers"), // comma-separated list of headers to add to the request
|
headers: text("headers"), // comma-separated list of headers to add to the request
|
||||||
proxyProtocol: integer("proxyProtocol", { mode: "boolean" }).notNull().default(false),
|
proxyProtocol: integer("proxyProtocol", {mode: "boolean"}).notNull().default(false),
|
||||||
proxyProtocolVersion: integer("proxyProtocolVersion").default(1)
|
proxyProtocolVersion: integer("proxyProtocolVersion").default(1)
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const targets = sqliteTable("targets", {
|
export const targets = sqliteTable("targets", {
|
||||||
targetId: integer("targetId").primaryKey({ autoIncrement: true }),
|
targetId: integer("targetId").primaryKey({autoIncrement: true}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.references(() => resources.resourceId, {
|
.references(() => resources.resourceId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
@@ -163,7 +163,7 @@ export const targets = sqliteTable("targets", {
|
|||||||
method: text("method"),
|
method: text("method"),
|
||||||
port: integer("port").notNull(),
|
port: integer("port").notNull(),
|
||||||
internalPort: integer("internalPort"),
|
internalPort: integer("internalPort"),
|
||||||
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
enabled: integer("enabled", {mode: "boolean"}).notNull().default(true),
|
||||||
path: text("path"),
|
path: text("path"),
|
||||||
pathMatchType: text("pathMatchType"), // exact, prefix, regex
|
pathMatchType: text("pathMatchType"), // exact, prefix, regex
|
||||||
rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target
|
rewritePath: text("rewritePath"), // if set, rewrites the path to this value before sending to the target
|
||||||
@@ -177,8 +177,8 @@ export const targetHealthCheck = sqliteTable("targetHealthCheck", {
|
|||||||
}),
|
}),
|
||||||
targetId: integer("targetId")
|
targetId: integer("targetId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => targets.targetId, { onDelete: "cascade" }),
|
.references(() => targets.targetId, {onDelete: "cascade"}),
|
||||||
hcEnabled: integer("hcEnabled", { mode: "boolean" })
|
hcEnabled: integer("hcEnabled", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
hcPath: text("hcPath"),
|
hcPath: text("hcPath"),
|
||||||
@@ -199,7 +199,7 @@ export const targetHealthCheck = sqliteTable("targetHealthCheck", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const exitNodes = sqliteTable("exitNodes", {
|
export const exitNodes = sqliteTable("exitNodes", {
|
||||||
exitNodeId: integer("exitNodeId").primaryKey({ autoIncrement: true }),
|
exitNodeId: integer("exitNodeId").primaryKey({autoIncrement: true}),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
address: text("address").notNull(), // this is the address of the wireguard interface in gerbil
|
address: text("address").notNull(), // this is the address of the wireguard interface in gerbil
|
||||||
endpoint: text("endpoint").notNull(), // this is how to reach gerbil externally - gets put into the wireguard config
|
endpoint: text("endpoint").notNull(), // this is how to reach gerbil externally - gets put into the wireguard config
|
||||||
@@ -207,7 +207,7 @@ export const exitNodes = sqliteTable("exitNodes", {
|
|||||||
listenPort: integer("listenPort").notNull(),
|
listenPort: integer("listenPort").notNull(),
|
||||||
reachableAt: text("reachableAt"), // this is the internal address of the gerbil http server for command control
|
reachableAt: text("reachableAt"), // this is the internal address of the gerbil http server for command control
|
||||||
maxConnections: integer("maxConnections"),
|
maxConnections: integer("maxConnections"),
|
||||||
online: integer("online", { mode: "boolean" }).notNull().default(false),
|
online: integer("online", {mode: "boolean"}).notNull().default(false),
|
||||||
lastPing: integer("lastPing"),
|
lastPing: integer("lastPing"),
|
||||||
type: text("type").default("gerbil"), // gerbil, remoteExitNode
|
type: text("type").default("gerbil"), // gerbil, remoteExitNode
|
||||||
region: text("region")
|
region: text("region")
|
||||||
@@ -220,17 +220,17 @@ export const siteResources = sqliteTable("siteResources", {
|
|||||||
}),
|
}),
|
||||||
siteId: integer("siteId")
|
siteId: integer("siteId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => sites.siteId, { onDelete: "cascade" }),
|
.references(() => sites.siteId, {onDelete: "cascade"}),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
.references(() => orgs.orgId, {onDelete: "cascade"}),
|
||||||
niceId: text("niceId").notNull(),
|
niceId: text("niceId").notNull(),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
protocol: text("protocol").notNull(),
|
protocol: text("protocol").notNull(),
|
||||||
proxyPort: integer("proxyPort").notNull(),
|
proxyPort: integer("proxyPort").notNull(),
|
||||||
destinationPort: integer("destinationPort").notNull(),
|
destinationPort: integer("destinationPort").notNull(),
|
||||||
destinationIp: text("destinationIp").notNull(),
|
destinationIp: text("destinationIp").notNull(),
|
||||||
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true)
|
enabled: integer("enabled", {mode: "boolean"}).notNull().default(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const users = sqliteTable("user", {
|
export const users = sqliteTable("user", {
|
||||||
@@ -243,20 +243,20 @@ export const users = sqliteTable("user", {
|
|||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
}),
|
}),
|
||||||
passwordHash: text("passwordHash"),
|
passwordHash: text("passwordHash"),
|
||||||
twoFactorEnabled: integer("twoFactorEnabled", { mode: "boolean" })
|
twoFactorEnabled: integer("twoFactorEnabled", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
twoFactorSetupRequested: integer("twoFactorSetupRequested", {
|
twoFactorSetupRequested: integer("twoFactorSetupRequested", {
|
||||||
mode: "boolean"
|
mode: "boolean"
|
||||||
}).default(false),
|
}).default(false),
|
||||||
twoFactorSecret: text("twoFactorSecret"),
|
twoFactorSecret: text("twoFactorSecret"),
|
||||||
emailVerified: integer("emailVerified", { mode: "boolean" })
|
emailVerified: integer("emailVerified", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
dateCreated: text("dateCreated").notNull(),
|
dateCreated: text("dateCreated").notNull(),
|
||||||
termsAcceptedTimestamp: text("termsAcceptedTimestamp"),
|
termsAcceptedTimestamp: text("termsAcceptedTimestamp"),
|
||||||
termsVersion: text("termsVersion"),
|
termsVersion: text("termsVersion"),
|
||||||
serverAdmin: integer("serverAdmin", { mode: "boolean" })
|
serverAdmin: integer("serverAdmin", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
lastPasswordChange: integer("lastPasswordChange")
|
lastPasswordChange: integer("lastPasswordChange")
|
||||||
@@ -290,7 +290,7 @@ export const webauthnChallenge = sqliteTable("webauthnChallenge", {
|
|||||||
export const setupTokens = sqliteTable("setupTokens", {
|
export const setupTokens = sqliteTable("setupTokens", {
|
||||||
tokenId: text("tokenId").primaryKey(),
|
tokenId: text("tokenId").primaryKey(),
|
||||||
token: text("token").notNull(),
|
token: text("token").notNull(),
|
||||||
used: integer("used", { mode: "boolean" }).notNull().default(false),
|
used: integer("used", {mode: "boolean"}).notNull().default(false),
|
||||||
dateCreated: text("dateCreated").notNull(),
|
dateCreated: text("dateCreated").notNull(),
|
||||||
dateUsed: text("dateUsed")
|
dateUsed: text("dateUsed")
|
||||||
});
|
});
|
||||||
@@ -306,7 +306,7 @@ export const newts = sqliteTable("newt", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const clients = sqliteTable("clients", {
|
export const clients = sqliteTable("clients", {
|
||||||
clientId: integer("id").primaryKey({ autoIncrement: true }),
|
clientId: integer("id").primaryKey({autoIncrement: true}),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.references(() => orgs.orgId, {
|
.references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
@@ -323,7 +323,7 @@ export const clients = sqliteTable("clients", {
|
|||||||
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
lastBandwidthUpdate: text("lastBandwidthUpdate"),
|
||||||
lastPing: integer("lastPing"),
|
lastPing: integer("lastPing"),
|
||||||
type: text("type").notNull(), // "olm"
|
type: text("type").notNull(), // "olm"
|
||||||
online: integer("online", { mode: "boolean" }).notNull().default(false),
|
online: integer("online", {mode: "boolean"}).notNull().default(false),
|
||||||
// endpoint: text("endpoint"),
|
// endpoint: text("endpoint"),
|
||||||
lastHolePunch: integer("lastHolePunch")
|
lastHolePunch: integer("lastHolePunch")
|
||||||
});
|
});
|
||||||
@@ -331,11 +331,11 @@ export const clients = sqliteTable("clients", {
|
|||||||
export const clientSites = sqliteTable("clientSites", {
|
export const clientSites = sqliteTable("clientSites", {
|
||||||
clientId: integer("clientId")
|
clientId: integer("clientId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => clients.clientId, { onDelete: "cascade" }),
|
.references(() => clients.clientId, {onDelete: "cascade"}),
|
||||||
siteId: integer("siteId")
|
siteId: integer("siteId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => sites.siteId, { onDelete: "cascade" }),
|
.references(() => sites.siteId, {onDelete: "cascade"}),
|
||||||
isRelayed: integer("isRelayed", { mode: "boolean" })
|
isRelayed: integer("isRelayed", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
endpoint: text("endpoint")
|
endpoint: text("endpoint")
|
||||||
@@ -352,10 +352,10 @@ export const olms = sqliteTable("olms", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const twoFactorBackupCodes = sqliteTable("twoFactorBackupCodes", {
|
export const twoFactorBackupCodes = sqliteTable("twoFactorBackupCodes", {
|
||||||
codeId: integer("id").primaryKey({ autoIncrement: true }),
|
codeId: integer("id").primaryKey({autoIncrement: true}),
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, { onDelete: "cascade" }),
|
.references(() => users.userId, {onDelete: "cascade"}),
|
||||||
codeHash: text("codeHash").notNull()
|
codeHash: text("codeHash").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -363,7 +363,7 @@ export const sessions = sqliteTable("session", {
|
|||||||
sessionId: text("id").primaryKey(),
|
sessionId: text("id").primaryKey(),
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, { onDelete: "cascade" }),
|
.references(() => users.userId, {onDelete: "cascade"}),
|
||||||
expiresAt: integer("expiresAt").notNull(),
|
expiresAt: integer("expiresAt").notNull(),
|
||||||
issuedAt: integer("issuedAt")
|
issuedAt: integer("issuedAt")
|
||||||
});
|
});
|
||||||
@@ -372,7 +372,7 @@ export const newtSessions = sqliteTable("newtSession", {
|
|||||||
sessionId: text("id").primaryKey(),
|
sessionId: text("id").primaryKey(),
|
||||||
newtId: text("newtId")
|
newtId: text("newtId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => newts.newtId, { onDelete: "cascade" }),
|
.references(() => newts.newtId, {onDelete: "cascade"}),
|
||||||
expiresAt: integer("expiresAt").notNull()
|
expiresAt: integer("expiresAt").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -380,14 +380,14 @@ export const olmSessions = sqliteTable("clientSession", {
|
|||||||
sessionId: text("id").primaryKey(),
|
sessionId: text("id").primaryKey(),
|
||||||
olmId: text("olmId")
|
olmId: text("olmId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => olms.olmId, { onDelete: "cascade" }),
|
.references(() => olms.olmId, {onDelete: "cascade"}),
|
||||||
expiresAt: integer("expiresAt").notNull()
|
expiresAt: integer("expiresAt").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userOrgs = sqliteTable("userOrgs", {
|
export const userOrgs = sqliteTable("userOrgs", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, { onDelete: "cascade" }),
|
.references(() => users.userId, {onDelete: "cascade"}),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.references(() => orgs.orgId, {
|
.references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
@@ -396,28 +396,28 @@ export const userOrgs = sqliteTable("userOrgs", {
|
|||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId),
|
.references(() => roles.roleId),
|
||||||
isOwner: integer("isOwner", { mode: "boolean" }).notNull().default(false),
|
isOwner: integer("isOwner", {mode: "boolean"}).notNull().default(false),
|
||||||
autoProvisioned: integer("autoProvisioned", {
|
autoProvisioned: integer("autoProvisioned", {
|
||||||
mode: "boolean"
|
mode: "boolean"
|
||||||
}).default(false)
|
}).default(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const emailVerificationCodes = sqliteTable("emailVerificationCodes", {
|
export const emailVerificationCodes = sqliteTable("emailVerificationCodes", {
|
||||||
codeId: integer("id").primaryKey({ autoIncrement: true }),
|
codeId: integer("id").primaryKey({autoIncrement: true}),
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, { onDelete: "cascade" }),
|
.references(() => users.userId, {onDelete: "cascade"}),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
code: text("code").notNull(),
|
code: text("code").notNull(),
|
||||||
expiresAt: integer("expiresAt").notNull()
|
expiresAt: integer("expiresAt").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const passwordResetTokens = sqliteTable("passwordResetTokens", {
|
export const passwordResetTokens = sqliteTable("passwordResetTokens", {
|
||||||
tokenId: integer("id").primaryKey({ autoIncrement: true }),
|
tokenId: integer("id").primaryKey({autoIncrement: true}),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, { onDelete: "cascade" }),
|
.references(() => users.userId, {onDelete: "cascade"}),
|
||||||
tokenHash: text("tokenHash").notNull(),
|
tokenHash: text("tokenHash").notNull(),
|
||||||
expiresAt: integer("expiresAt").notNull()
|
expiresAt: integer("expiresAt").notNull()
|
||||||
});
|
});
|
||||||
@@ -429,13 +429,13 @@ export const actions = sqliteTable("actions", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const roles = sqliteTable("roles", {
|
export const roles = sqliteTable("roles", {
|
||||||
roleId: integer("roleId").primaryKey({ autoIncrement: true }),
|
roleId: integer("roleId").primaryKey({autoIncrement: true}),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.references(() => orgs.orgId, {
|
.references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
})
|
})
|
||||||
.notNull(),
|
.notNull(),
|
||||||
isAdmin: integer("isAdmin", { mode: "boolean" }),
|
isAdmin: integer("isAdmin", {mode: "boolean"}),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
description: text("description")
|
description: text("description")
|
||||||
});
|
});
|
||||||
@@ -443,92 +443,92 @@ export const roles = sqliteTable("roles", {
|
|||||||
export const roleActions = sqliteTable("roleActions", {
|
export const roleActions = sqliteTable("roleActions", {
|
||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, { onDelete: "cascade" }),
|
.references(() => roles.roleId, {onDelete: "cascade"}),
|
||||||
actionId: text("actionId")
|
actionId: text("actionId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => actions.actionId, { onDelete: "cascade" }),
|
.references(() => actions.actionId, {onDelete: "cascade"}),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, { onDelete: "cascade" })
|
.references(() => orgs.orgId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userActions = sqliteTable("userActions", {
|
export const userActions = sqliteTable("userActions", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, { onDelete: "cascade" }),
|
.references(() => users.userId, {onDelete: "cascade"}),
|
||||||
actionId: text("actionId")
|
actionId: text("actionId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => actions.actionId, { onDelete: "cascade" }),
|
.references(() => actions.actionId, {onDelete: "cascade"}),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, { onDelete: "cascade" })
|
.references(() => orgs.orgId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const roleSites = sqliteTable("roleSites", {
|
export const roleSites = sqliteTable("roleSites", {
|
||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, { onDelete: "cascade" }),
|
.references(() => roles.roleId, {onDelete: "cascade"}),
|
||||||
siteId: integer("siteId")
|
siteId: integer("siteId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => sites.siteId, { onDelete: "cascade" })
|
.references(() => sites.siteId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userSites = sqliteTable("userSites", {
|
export const userSites = sqliteTable("userSites", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, { onDelete: "cascade" }),
|
.references(() => users.userId, {onDelete: "cascade"}),
|
||||||
siteId: integer("siteId")
|
siteId: integer("siteId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => sites.siteId, { onDelete: "cascade" })
|
.references(() => sites.siteId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userClients = sqliteTable("userClients", {
|
export const userClients = sqliteTable("userClients", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, { onDelete: "cascade" }),
|
.references(() => users.userId, {onDelete: "cascade"}),
|
||||||
clientId: integer("clientId")
|
clientId: integer("clientId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => clients.clientId, { onDelete: "cascade" })
|
.references(() => clients.clientId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const roleClients = sqliteTable("roleClients", {
|
export const roleClients = sqliteTable("roleClients", {
|
||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, { onDelete: "cascade" }),
|
.references(() => roles.roleId, {onDelete: "cascade"}),
|
||||||
clientId: integer("clientId")
|
clientId: integer("clientId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => clients.clientId, { onDelete: "cascade" })
|
.references(() => clients.clientId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const roleResources = sqliteTable("roleResources", {
|
export const roleResources = sqliteTable("roleResources", {
|
||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, { onDelete: "cascade" }),
|
.references(() => roles.roleId, {onDelete: "cascade"}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" })
|
.references(() => resources.resourceId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userResources = sqliteTable("userResources", {
|
export const userResources = sqliteTable("userResources", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => users.userId, { onDelete: "cascade" }),
|
.references(() => users.userId, {onDelete: "cascade"}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" })
|
.references(() => resources.resourceId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const userInvites = sqliteTable("userInvites", {
|
export const userInvites = sqliteTable("userInvites", {
|
||||||
inviteId: text("inviteId").primaryKey(),
|
inviteId: text("inviteId").primaryKey(),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
.references(() => orgs.orgId, {onDelete: "cascade"}),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
expiresAt: integer("expiresAt").notNull(),
|
expiresAt: integer("expiresAt").notNull(),
|
||||||
tokenHash: text("token").notNull(),
|
tokenHash: text("token").notNull(),
|
||||||
roleId: integer("roleId")
|
roleId: integer("roleId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => roles.roleId, { onDelete: "cascade" })
|
.references(() => roles.roleId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const resourcePincode = sqliteTable("resourcePincode", {
|
export const resourcePincode = sqliteTable("resourcePincode", {
|
||||||
@@ -537,7 +537,7 @@ export const resourcePincode = sqliteTable("resourcePincode", {
|
|||||||
}),
|
}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
||||||
pincodeHash: text("pincodeHash").notNull(),
|
pincodeHash: text("pincodeHash").notNull(),
|
||||||
digitLength: integer("digitLength").notNull()
|
digitLength: integer("digitLength").notNull()
|
||||||
});
|
});
|
||||||
@@ -548,7 +548,7 @@ export const resourcePassword = sqliteTable("resourcePassword", {
|
|||||||
}),
|
}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
||||||
passwordHash: text("passwordHash").notNull()
|
passwordHash: text("passwordHash").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -558,18 +558,28 @@ export const resourceHeaderAuth = sqliteTable("resourceHeaderAuth", {
|
|||||||
}),
|
}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
||||||
headerAuthHash: text("headerAuthHash").notNull()
|
headerAuthHash: text("headerAuthHash").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const resourceHeaderAuthExtendedCompatibility = sqliteTable("resourceHeaderAuthExtendedCompatibility", {
|
||||||
|
headerAuthExtendedCompatibilityId: integer("headerAuthExtendedCompatibilityId").primaryKey({
|
||||||
|
autoIncrement: true
|
||||||
|
}),
|
||||||
|
resourceId: integer("resourceId")
|
||||||
|
.notNull()
|
||||||
|
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
||||||
|
extendedCompatibilityIsActivated: integer("extendedCompatibilityIsActivated", {mode: "boolean"}).notNull()
|
||||||
|
});
|
||||||
|
|
||||||
export const resourceAccessToken = sqliteTable("resourceAccessToken", {
|
export const resourceAccessToken = sqliteTable("resourceAccessToken", {
|
||||||
accessTokenId: text("accessTokenId").primaryKey(),
|
accessTokenId: text("accessTokenId").primaryKey(),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
.references(() => orgs.orgId, {onDelete: "cascade"}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
||||||
tokenHash: text("tokenHash").notNull(),
|
tokenHash: text("tokenHash").notNull(),
|
||||||
sessionLength: integer("sessionLength").notNull(),
|
sessionLength: integer("sessionLength").notNull(),
|
||||||
expiresAt: integer("expiresAt"),
|
expiresAt: integer("expiresAt"),
|
||||||
@@ -582,13 +592,13 @@ export const resourceSessions = sqliteTable("resourceSessions", {
|
|||||||
sessionId: text("id").primaryKey(),
|
sessionId: text("id").primaryKey(),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
||||||
expiresAt: integer("expiresAt").notNull(),
|
expiresAt: integer("expiresAt").notNull(),
|
||||||
sessionLength: integer("sessionLength").notNull(),
|
sessionLength: integer("sessionLength").notNull(),
|
||||||
doNotExtend: integer("doNotExtend", { mode: "boolean" })
|
doNotExtend: integer("doNotExtend", {mode: "boolean"})
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false),
|
.default(false),
|
||||||
isRequestToken: integer("isRequestToken", { mode: "boolean" }),
|
isRequestToken: integer("isRequestToken", {mode: "boolean"}),
|
||||||
userSessionId: text("userSessionId").references(() => sessions.sessionId, {
|
userSessionId: text("userSessionId").references(() => sessions.sessionId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
}),
|
}),
|
||||||
@@ -620,11 +630,11 @@ export const resourceSessions = sqliteTable("resourceSessions", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const resourceWhitelist = sqliteTable("resourceWhitelist", {
|
export const resourceWhitelist = sqliteTable("resourceWhitelist", {
|
||||||
whitelistId: integer("id").primaryKey({ autoIncrement: true }),
|
whitelistId: integer("id").primaryKey({autoIncrement: true}),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" })
|
.references(() => resources.resourceId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const resourceOtp = sqliteTable("resourceOtp", {
|
export const resourceOtp = sqliteTable("resourceOtp", {
|
||||||
@@ -633,7 +643,7 @@ export const resourceOtp = sqliteTable("resourceOtp", {
|
|||||||
}),
|
}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
otpHash: text("otpHash").notNull(),
|
otpHash: text("otpHash").notNull(),
|
||||||
expiresAt: integer("expiresAt").notNull()
|
expiresAt: integer("expiresAt").notNull()
|
||||||
@@ -645,11 +655,11 @@ export const versionMigrations = sqliteTable("versionMigrations", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const resourceRules = sqliteTable("resourceRules", {
|
export const resourceRules = sqliteTable("resourceRules", {
|
||||||
ruleId: integer("ruleId").primaryKey({ autoIncrement: true }),
|
ruleId: integer("ruleId").primaryKey({autoIncrement: true}),
|
||||||
resourceId: integer("resourceId")
|
resourceId: integer("resourceId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
.references(() => resources.resourceId, {onDelete: "cascade"}),
|
||||||
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
enabled: integer("enabled", {mode: "boolean"}).notNull().default(true),
|
||||||
priority: integer("priority").notNull(),
|
priority: integer("priority").notNull(),
|
||||||
action: text("action").notNull(), // ACCEPT, DROP, PASS
|
action: text("action").notNull(), // ACCEPT, DROP, PASS
|
||||||
match: text("match").notNull(), // CIDR, PATH, IP
|
match: text("match").notNull(), // CIDR, PATH, IP
|
||||||
@@ -657,17 +667,17 @@ export const resourceRules = sqliteTable("resourceRules", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const supporterKey = sqliteTable("supporterKey", {
|
export const supporterKey = sqliteTable("supporterKey", {
|
||||||
keyId: integer("keyId").primaryKey({ autoIncrement: true }),
|
keyId: integer("keyId").primaryKey({autoIncrement: true}),
|
||||||
key: text("key").notNull(),
|
key: text("key").notNull(),
|
||||||
githubUsername: text("githubUsername").notNull(),
|
githubUsername: text("githubUsername").notNull(),
|
||||||
phrase: text("phrase"),
|
phrase: text("phrase"),
|
||||||
tier: text("tier"),
|
tier: text("tier"),
|
||||||
valid: integer("valid", { mode: "boolean" }).notNull().default(false)
|
valid: integer("valid", {mode: "boolean"}).notNull().default(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Identity Providers
|
// Identity Providers
|
||||||
export const idp = sqliteTable("idp", {
|
export const idp = sqliteTable("idp", {
|
||||||
idpId: integer("idpId").primaryKey({ autoIncrement: true }),
|
idpId: integer("idpId").primaryKey({autoIncrement: true}),
|
||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
type: text("type").notNull(),
|
type: text("type").notNull(),
|
||||||
defaultRoleMapping: text("defaultRoleMapping"),
|
defaultRoleMapping: text("defaultRoleMapping"),
|
||||||
@@ -687,7 +697,7 @@ export const idpOidcConfig = sqliteTable("idpOidcConfig", {
|
|||||||
variant: text("variant").notNull().default("oidc"),
|
variant: text("variant").notNull().default("oidc"),
|
||||||
idpId: integer("idpId")
|
idpId: integer("idpId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => idp.idpId, { onDelete: "cascade" }),
|
.references(() => idp.idpId, {onDelete: "cascade"}),
|
||||||
clientId: text("clientId").notNull(),
|
clientId: text("clientId").notNull(),
|
||||||
clientSecret: text("clientSecret").notNull(),
|
clientSecret: text("clientSecret").notNull(),
|
||||||
authUrl: text("authUrl").notNull(),
|
authUrl: text("authUrl").notNull(),
|
||||||
@@ -715,22 +725,22 @@ export const apiKeys = sqliteTable("apiKeys", {
|
|||||||
apiKeyHash: text("apiKeyHash").notNull(),
|
apiKeyHash: text("apiKeyHash").notNull(),
|
||||||
lastChars: text("lastChars").notNull(),
|
lastChars: text("lastChars").notNull(),
|
||||||
createdAt: text("dateCreated").notNull(),
|
createdAt: text("dateCreated").notNull(),
|
||||||
isRoot: integer("isRoot", { mode: "boolean" }).notNull().default(false)
|
isRoot: integer("isRoot", {mode: "boolean"}).notNull().default(false)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiKeyActions = sqliteTable("apiKeyActions", {
|
export const apiKeyActions = sqliteTable("apiKeyActions", {
|
||||||
apiKeyId: text("apiKeyId")
|
apiKeyId: text("apiKeyId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => apiKeys.apiKeyId, { onDelete: "cascade" }),
|
.references(() => apiKeys.apiKeyId, {onDelete: "cascade"}),
|
||||||
actionId: text("actionId")
|
actionId: text("actionId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => actions.actionId, { onDelete: "cascade" })
|
.references(() => actions.actionId, {onDelete: "cascade"})
|
||||||
});
|
});
|
||||||
|
|
||||||
export const apiKeyOrg = sqliteTable("apiKeyOrg", {
|
export const apiKeyOrg = sqliteTable("apiKeyOrg", {
|
||||||
apiKeyId: text("apiKeyId")
|
apiKeyId: text("apiKeyId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => apiKeys.apiKeyId, { onDelete: "cascade" }),
|
.references(() => apiKeys.apiKeyId, {onDelete: "cascade"}),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.references(() => orgs.orgId, {
|
.references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
@@ -741,10 +751,10 @@ export const apiKeyOrg = sqliteTable("apiKeyOrg", {
|
|||||||
export const idpOrg = sqliteTable("idpOrg", {
|
export const idpOrg = sqliteTable("idpOrg", {
|
||||||
idpId: integer("idpId")
|
idpId: integer("idpId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => idp.idpId, { onDelete: "cascade" }),
|
.references(() => idp.idpId, {onDelete: "cascade"}),
|
||||||
orgId: text("orgId")
|
orgId: text("orgId")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => orgs.orgId, { onDelete: "cascade" }),
|
.references(() => orgs.orgId, {onDelete: "cascade"}),
|
||||||
roleMapping: text("roleMapping"),
|
roleMapping: text("roleMapping"),
|
||||||
orgMapping: text("orgMapping")
|
orgMapping: text("orgMapping")
|
||||||
});
|
});
|
||||||
@@ -762,19 +772,19 @@ export const blueprints = sqliteTable("blueprints", {
|
|||||||
name: text("name").notNull(),
|
name: text("name").notNull(),
|
||||||
source: text("source").notNull(),
|
source: text("source").notNull(),
|
||||||
createdAt: integer("createdAt").notNull(),
|
createdAt: integer("createdAt").notNull(),
|
||||||
succeeded: integer("succeeded", { mode: "boolean" }).notNull(),
|
succeeded: integer("succeeded", {mode: "boolean"}).notNull(),
|
||||||
contents: text("contents").notNull(),
|
contents: text("contents").notNull(),
|
||||||
message: text("message")
|
message: text("message")
|
||||||
});
|
});
|
||||||
export const requestAuditLog = sqliteTable(
|
export const requestAuditLog = sqliteTable(
|
||||||
"requestAuditLog",
|
"requestAuditLog",
|
||||||
{
|
{
|
||||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
id: integer("id").primaryKey({autoIncrement: true}),
|
||||||
timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds
|
timestamp: integer("timestamp").notNull(), // this is EPOCH time in seconds
|
||||||
orgId: text("orgId").references(() => orgs.orgId, {
|
orgId: text("orgId").references(() => orgs.orgId, {
|
||||||
onDelete: "cascade"
|
onDelete: "cascade"
|
||||||
}),
|
}),
|
||||||
action: integer("action", { mode: "boolean" }).notNull(),
|
action: integer("action", {mode: "boolean"}).notNull(),
|
||||||
reason: integer("reason").notNull(),
|
reason: integer("reason").notNull(),
|
||||||
actorType: text("actorType"),
|
actorType: text("actorType"),
|
||||||
actor: text("actor"),
|
actor: text("actor"),
|
||||||
@@ -791,7 +801,7 @@ export const requestAuditLog = sqliteTable(
|
|||||||
host: text("host"),
|
host: text("host"),
|
||||||
path: text("path"),
|
path: text("path"),
|
||||||
method: text("method"),
|
method: text("method"),
|
||||||
tls: integer("tls", { mode: "boolean" })
|
tls: integer("tls", {mode: "boolean"})
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
index("idx_requestAuditLog_timestamp").on(table.timestamp),
|
index("idx_requestAuditLog_timestamp").on(table.timestamp),
|
||||||
@@ -832,6 +842,7 @@ export type ResourceSession = InferSelectModel<typeof resourceSessions>;
|
|||||||
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
|
export type ResourcePincode = InferSelectModel<typeof resourcePincode>;
|
||||||
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
|
export type ResourcePassword = InferSelectModel<typeof resourcePassword>;
|
||||||
export type ResourceHeaderAuth = InferSelectModel<typeof resourceHeaderAuth>;
|
export type ResourceHeaderAuth = InferSelectModel<typeof resourceHeaderAuth>;
|
||||||
|
export type ResourceHeaderAuthExtendedCompatibility = InferSelectModel<typeof resourceHeaderAuthExtendedCompatibility>;
|
||||||
export type ResourceOtp = InferSelectModel<typeof resourceOtp>;
|
export type ResourceOtp = InferSelectModel<typeof resourceOtp>;
|
||||||
export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
|
export type ResourceAccessToken = InferSelectModel<typeof resourceAccessToken>;
|
||||||
export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
|
export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {
|
|||||||
domains,
|
domains,
|
||||||
orgDomains,
|
orgDomains,
|
||||||
Resource,
|
Resource,
|
||||||
resourceHeaderAuth,
|
resourceHeaderAuth, resourceHeaderAuthExtendedCompatibility,
|
||||||
resourcePincode,
|
resourcePincode,
|
||||||
resourceRules,
|
resourceRules,
|
||||||
resourceWhitelist,
|
resourceWhitelist,
|
||||||
@@ -16,8 +16,8 @@ import {
|
|||||||
userResources,
|
userResources,
|
||||||
users
|
users
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import { resources, targets, sites } from "@server/db";
|
import {resources, targets, sites} from "@server/db";
|
||||||
import { eq, and, asc, or, ne, count, isNotNull } from "drizzle-orm";
|
import {eq, and, asc, or, ne, count, isNotNull} from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
Config,
|
Config,
|
||||||
ConfigSchema,
|
ConfigSchema,
|
||||||
@@ -25,12 +25,12 @@ import {
|
|||||||
TargetData
|
TargetData
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { createCertificate } from "#dynamic/routers/certificates/createCertificate";
|
import {createCertificate} from "#dynamic/routers/certificates/createCertificate";
|
||||||
import { pickPort } from "@server/routers/target/helpers";
|
import {pickPort} from "@server/routers/target/helpers";
|
||||||
import { resourcePassword } from "@server/db";
|
import {resourcePassword} from "@server/db";
|
||||||
import { hashPassword } from "@server/auth/password";
|
import {hashPassword} from "@server/auth/password";
|
||||||
import { isValidCIDR, isValidIP, isValidUrlGlobPattern } from "../validators";
|
import {isValidCIDR, isValidIP, isValidUrlGlobPattern} from "../validators";
|
||||||
import { get } from "http";
|
import {get} from "http";
|
||||||
|
|
||||||
export type ProxyResourcesResults = {
|
export type ProxyResourcesResults = {
|
||||||
proxyResource: Resource;
|
proxyResource: Resource;
|
||||||
@@ -63,7 +63,7 @@ export async function updateProxyResources(
|
|||||||
if (targetSiteId) {
|
if (targetSiteId) {
|
||||||
// Look up site by niceId
|
// Look up site by niceId
|
||||||
[site] = await trx
|
[site] = await trx
|
||||||
.select({ siteId: sites.siteId })
|
.select({siteId: sites.siteId})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
@@ -75,7 +75,7 @@ export async function updateProxyResources(
|
|||||||
} else if (siteId) {
|
} else if (siteId) {
|
||||||
// Use the provided siteId directly, but verify it belongs to the org
|
// Use the provided siteId directly, but verify it belongs to the org
|
||||||
[site] = await trx
|
[site] = await trx
|
||||||
.select({ siteId: sites.siteId })
|
.select({siteId: sites.siteId})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(
|
.where(
|
||||||
and(eq(sites.siteId, siteId), eq(sites.orgId, orgId))
|
and(eq(sites.siteId, siteId), eq(sites.orgId, orgId))
|
||||||
@@ -93,7 +93,7 @@ export async function updateProxyResources(
|
|||||||
|
|
||||||
let internalPortToCreate;
|
let internalPortToCreate;
|
||||||
if (!targetData["internal-port"]) {
|
if (!targetData["internal-port"]) {
|
||||||
const { internalPort, targetIps } = await pickPort(
|
const {internalPort, targetIps} = await pickPort(
|
||||||
site.siteId!,
|
site.siteId!,
|
||||||
trx
|
trx
|
||||||
);
|
);
|
||||||
@@ -226,7 +226,7 @@ export async function updateProxyResources(
|
|||||||
tlsServerName: resourceData["tls-server-name"] || null,
|
tlsServerName: resourceData["tls-server-name"] || null,
|
||||||
emailWhitelistEnabled: resourceData.auth?.[
|
emailWhitelistEnabled: resourceData.auth?.[
|
||||||
"whitelist-users"
|
"whitelist-users"
|
||||||
]
|
]
|
||||||
? resourceData.auth["whitelist-users"].length > 0
|
? resourceData.auth["whitelist-users"].length > 0
|
||||||
: false,
|
: false,
|
||||||
headers: headers || null,
|
headers: headers || null,
|
||||||
@@ -285,21 +285,39 @@ export async function updateProxyResources(
|
|||||||
existingResource.resourceId
|
existingResource.resourceId
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await trx
|
||||||
|
.delete(resourceHeaderAuthExtendedCompatibility)
|
||||||
|
.where(
|
||||||
|
eq(
|
||||||
|
resourceHeaderAuthExtendedCompatibility.resourceId,
|
||||||
|
existingResource.resourceId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if (resourceData.auth?.["basic-auth"]) {
|
if (resourceData.auth?.["basic-auth"]) {
|
||||||
const headerAuthUser =
|
const headerAuthUser =
|
||||||
resourceData.auth?.["basic-auth"]?.user;
|
resourceData.auth?.["basic-auth"]?.user;
|
||||||
const headerAuthPassword =
|
const headerAuthPassword =
|
||||||
resourceData.auth?.["basic-auth"]?.password;
|
resourceData.auth?.["basic-auth"]?.password;
|
||||||
if (headerAuthUser && headerAuthPassword) {
|
const headerAuthExtendedCompatibility =
|
||||||
|
resourceData.auth?.["basic-auth"]?.extendedCompatibility;
|
||||||
|
if (headerAuthUser && headerAuthPassword && headerAuthExtendedCompatibility !== null) {
|
||||||
const headerAuthHash = await hashPassword(
|
const headerAuthHash = await hashPassword(
|
||||||
Buffer.from(
|
Buffer.from(
|
||||||
`${headerAuthUser}:${headerAuthPassword}`
|
`${headerAuthUser}:${headerAuthPassword}`
|
||||||
).toString("base64")
|
).toString("base64")
|
||||||
);
|
);
|
||||||
await trx.insert(resourceHeaderAuth).values({
|
await Promise.all([
|
||||||
resourceId: existingResource.resourceId,
|
trx.insert(resourceHeaderAuth).values({
|
||||||
headerAuthHash
|
resourceId: existingResource.resourceId,
|
||||||
});
|
headerAuthHash
|
||||||
|
}),
|
||||||
|
trx.insert(resourceHeaderAuthExtendedCompatibility).values({
|
||||||
|
resourceId: existingResource.resourceId,
|
||||||
|
extendedCompatibilityIsActivated: headerAuthExtendedCompatibility
|
||||||
|
})
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,7 +378,7 @@ export async function updateProxyResources(
|
|||||||
if (targetSiteId) {
|
if (targetSiteId) {
|
||||||
// Look up site by niceId
|
// Look up site by niceId
|
||||||
[site] = await trx
|
[site] = await trx
|
||||||
.select({ siteId: sites.siteId })
|
.select({siteId: sites.siteId})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
@@ -372,7 +390,7 @@ export async function updateProxyResources(
|
|||||||
} else if (siteId) {
|
} else if (siteId) {
|
||||||
// Use the provided siteId directly, but verify it belongs to the org
|
// Use the provided siteId directly, but verify it belongs to the org
|
||||||
[site] = await trx
|
[site] = await trx
|
||||||
.select({ siteId: sites.siteId })
|
.select({siteId: sites.siteId})
|
||||||
.from(sites)
|
.from(sites)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
@@ -417,7 +435,7 @@ export async function updateProxyResources(
|
|||||||
if (checkIfTargetChanged(existingTarget, updatedTarget)) {
|
if (checkIfTargetChanged(existingTarget, updatedTarget)) {
|
||||||
let internalPortToUpdate;
|
let internalPortToUpdate;
|
||||||
if (!targetData["internal-port"]) {
|
if (!targetData["internal-port"]) {
|
||||||
const { internalPort, targetIps } = await pickPort(
|
const {internalPort, targetIps} = await pickPort(
|
||||||
site.siteId!,
|
site.siteId!,
|
||||||
trx
|
trx
|
||||||
);
|
);
|
||||||
@@ -646,18 +664,25 @@ export async function updateProxyResources(
|
|||||||
const headerAuthUser = resourceData.auth?.["basic-auth"]?.user;
|
const headerAuthUser = resourceData.auth?.["basic-auth"]?.user;
|
||||||
const headerAuthPassword =
|
const headerAuthPassword =
|
||||||
resourceData.auth?.["basic-auth"]?.password;
|
resourceData.auth?.["basic-auth"]?.password;
|
||||||
|
const headerAuthExtendedCompatibility = resourceData.auth?.["basic-auth"]?.extendedCompatibility;
|
||||||
|
|
||||||
if (headerAuthUser && headerAuthPassword) {
|
if (headerAuthUser && headerAuthPassword && headerAuthExtendedCompatibility !== null) {
|
||||||
const headerAuthHash = await hashPassword(
|
const headerAuthHash = await hashPassword(
|
||||||
Buffer.from(
|
Buffer.from(
|
||||||
`${headerAuthUser}:${headerAuthPassword}`
|
`${headerAuthUser}:${headerAuthPassword}`
|
||||||
).toString("base64")
|
).toString("base64")
|
||||||
);
|
);
|
||||||
|
|
||||||
await trx.insert(resourceHeaderAuth).values({
|
await Promise.all([
|
||||||
resourceId: newResource.resourceId,
|
trx.insert(resourceHeaderAuth).values({
|
||||||
headerAuthHash
|
resourceId: newResource.resourceId,
|
||||||
});
|
headerAuthHash
|
||||||
|
}),
|
||||||
|
trx.insert(resourceHeaderAuthExtendedCompatibility).values({
|
||||||
|
resourceId: newResource.resourceId,
|
||||||
|
extendedCompatibilityIsActivated: headerAuthExtendedCompatibility
|
||||||
|
}),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1004,7 +1029,7 @@ async function getDomain(
|
|||||||
trx: Transaction
|
trx: Transaction
|
||||||
) {
|
) {
|
||||||
const [fullDomainExists] = await trx
|
const [fullDomainExists] = await trx
|
||||||
.select({ resourceId: resources.resourceId })
|
.select({resourceId: resources.resourceId})
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.where(
|
.where(
|
||||||
and(
|
and(
|
||||||
|
|||||||
@@ -47,7 +47,8 @@ export const AuthSchema = z.object({
|
|||||||
password: z.string().min(1).optional(),
|
password: z.string().min(1).optional(),
|
||||||
"basic-auth": z.object({
|
"basic-auth": z.object({
|
||||||
user: z.string().min(1),
|
user: z.string().min(1),
|
||||||
password: z.string().min(1)
|
password: z.string().min(1),
|
||||||
|
extendedCompatibility: z.boolean().default(false)
|
||||||
}).optional(),
|
}).optional(),
|
||||||
"sso-enabled": z.boolean().optional().default(false),
|
"sso-enabled": z.boolean().optional().default(false),
|
||||||
"sso-roles": z
|
"sso-roles": z
|
||||||
|
|||||||
@@ -36,8 +36,10 @@ import {
|
|||||||
LoginPage,
|
LoginPage,
|
||||||
resourceHeaderAuth,
|
resourceHeaderAuth,
|
||||||
ResourceHeaderAuth,
|
ResourceHeaderAuth,
|
||||||
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
|
ResourceHeaderAuthExtendedCompatibility,
|
||||||
orgs,
|
orgs,
|
||||||
requestAuditLog
|
requestAuditLog,
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import {
|
import {
|
||||||
resources,
|
resources,
|
||||||
@@ -188,6 +190,7 @@ export type ResourceWithAuth = {
|
|||||||
pincode: ResourcePincode | null;
|
pincode: ResourcePincode | null;
|
||||||
password: ResourcePassword | null;
|
password: ResourcePassword | null;
|
||||||
headerAuth: ResourceHeaderAuth | null;
|
headerAuth: ResourceHeaderAuth | null;
|
||||||
|
headerAuthExtendedCompatibility: ResourceHeaderAuthExtendedCompatibility | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserSessionWithUser = {
|
export type UserSessionWithUser = {
|
||||||
@@ -511,6 +514,10 @@ hybridRouter.get(
|
|||||||
resourceHeaderAuth,
|
resourceHeaderAuth,
|
||||||
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
|
.leftJoin(
|
||||||
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
|
eq(resourceHeaderAuthExtendedCompatibility.resourceId, resources.resourceId)
|
||||||
|
)
|
||||||
.where(eq(resources.fullDomain, domain))
|
.where(eq(resources.fullDomain, domain))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
@@ -543,7 +550,8 @@ hybridRouter.get(
|
|||||||
resource: result.resources,
|
resource: result.resources,
|
||||||
pincode: result.resourcePincode,
|
pincode: result.resourcePincode,
|
||||||
password: result.resourcePassword,
|
password: result.resourcePassword,
|
||||||
headerAuth: result.resourceHeaderAuth
|
headerAuth: result.resourceHeaderAuth,
|
||||||
|
headerAuthExtendedCompatibility: result.resourceHeaderAuthExtendedCompatibility
|
||||||
};
|
};
|
||||||
|
|
||||||
return response<ResourceWithAuth>(res, {
|
return response<ResourceWithAuth>(res, {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ Reasons:
|
|||||||
100 - Allowed by Rule
|
100 - Allowed by Rule
|
||||||
101 - Allowed No Auth
|
101 - Allowed No Auth
|
||||||
102 - Valid Access Token
|
102 - Valid Access Token
|
||||||
103 - Valid header auth
|
103 - Valid Header Auth (HTTP Basic Auth)
|
||||||
104 - Valid Pincode
|
104 - Valid Pincode
|
||||||
105 - Valid Password
|
105 - Valid Password
|
||||||
106 - Valid email
|
106 - Valid email
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
LoginPage,
|
LoginPage,
|
||||||
Org,
|
Org,
|
||||||
Resource,
|
Resource,
|
||||||
ResourceHeaderAuth,
|
ResourceHeaderAuth, ResourceHeaderAuthExtendedCompatibility,
|
||||||
ResourcePassword,
|
ResourcePassword,
|
||||||
ResourcePincode,
|
ResourcePincode,
|
||||||
ResourceRule,
|
ResourceRule,
|
||||||
@@ -65,6 +65,7 @@ type BasicUserData = {
|
|||||||
|
|
||||||
export type VerifyUserResponse = {
|
export type VerifyUserResponse = {
|
||||||
valid: boolean;
|
valid: boolean;
|
||||||
|
headerAuthChallenged?: boolean;
|
||||||
redirectUrl?: string;
|
redirectUrl?: string;
|
||||||
userData?: BasicUserData;
|
userData?: BasicUserData;
|
||||||
};
|
};
|
||||||
@@ -142,6 +143,7 @@ export async function verifyResourceSession(
|
|||||||
pincode: ResourcePincode | null;
|
pincode: ResourcePincode | null;
|
||||||
password: ResourcePassword | null;
|
password: ResourcePassword | null;
|
||||||
headerAuth: ResourceHeaderAuth | null;
|
headerAuth: ResourceHeaderAuth | null;
|
||||||
|
headerAuthExtendedCompatibility: ResourceHeaderAuthExtendedCompatibility | null;
|
||||||
org: Org;
|
org: Org;
|
||||||
}
|
}
|
||||||
| undefined = cache.get(resourceCacheKey);
|
| undefined = cache.get(resourceCacheKey);
|
||||||
@@ -171,7 +173,7 @@ export async function verifyResourceSession(
|
|||||||
cache.set(resourceCacheKey, resourceData, 5);
|
cache.set(resourceCacheKey, resourceData, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { resource, pincode, password, headerAuth } = resourceData;
|
const { resource, pincode, password, headerAuth, headerAuthExtendedCompatibility } = resourceData;
|
||||||
|
|
||||||
if (!resource) {
|
if (!resource) {
|
||||||
logger.debug(`Resource not found ${cleanHost}`);
|
logger.debug(`Resource not found ${cleanHost}`);
|
||||||
@@ -450,7 +452,8 @@ export async function verifyResourceSession(
|
|||||||
!sso &&
|
!sso &&
|
||||||
!pincode &&
|
!pincode &&
|
||||||
!password &&
|
!password &&
|
||||||
!resource.emailWhitelistEnabled
|
!resource.emailWhitelistEnabled &&
|
||||||
|
!headerAuthExtendedCompatibility?.extendedCompatibilityIsActivated
|
||||||
) {
|
) {
|
||||||
logRequestAudit(
|
logRequestAudit(
|
||||||
{
|
{
|
||||||
@@ -465,13 +468,15 @@ export async function verifyResourceSession(
|
|||||||
|
|
||||||
return notAllowed(res);
|
return notAllowed(res);
|
||||||
}
|
}
|
||||||
} else if (headerAuth) {
|
}
|
||||||
|
else if (headerAuth) {
|
||||||
// if there are no other auth methods we need to return unauthorized if nothing is provided
|
// if there are no other auth methods we need to return unauthorized if nothing is provided
|
||||||
if (
|
if (
|
||||||
!sso &&
|
!sso &&
|
||||||
!pincode &&
|
!pincode &&
|
||||||
!password &&
|
!password &&
|
||||||
!resource.emailWhitelistEnabled
|
!resource.emailWhitelistEnabled &&
|
||||||
|
!headerAuthExtendedCompatibility?.extendedCompatibilityIsActivated
|
||||||
) {
|
) {
|
||||||
logRequestAudit(
|
logRequestAudit(
|
||||||
{
|
{
|
||||||
@@ -557,7 +562,7 @@ export async function verifyResourceSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (resourceSession) {
|
if (resourceSession) {
|
||||||
// only run this check if not SSO sesion; SSO session length is checked later
|
// only run this check if not SSO session; SSO session length is checked later
|
||||||
const accessPolicy = await enforceResourceSessionLength(
|
const accessPolicy = await enforceResourceSessionLength(
|
||||||
resourceSession,
|
resourceSession,
|
||||||
resourceData.org
|
resourceData.org
|
||||||
@@ -701,6 +706,11 @@ export async function verifyResourceSession(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If headerAuthExtendedCompatibility is activated but no clientHeaderAuth provided, force client to challenge
|
||||||
|
if (headerAuthExtendedCompatibility && headerAuthExtendedCompatibility.extendedCompatibilityIsActivated && !clientHeaderAuth){
|
||||||
|
return headerAuthChallenged(res, redirectPath, resource.orgId);
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug("No more auth to check, resource not allowed");
|
logger.debug("No more auth to check, resource not allowed");
|
||||||
|
|
||||||
if (config.getRawConfig().app.log_failed_attempts) {
|
if (config.getRawConfig().app.log_failed_attempts) {
|
||||||
@@ -833,6 +843,46 @@ function allowed(res: Response, userData?: BasicUserData) {
|
|||||||
return response<VerifyUserResponse>(res, data);
|
return response<VerifyUserResponse>(res, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function headerAuthChallenged(
|
||||||
|
res: Response,
|
||||||
|
redirectPath?: string,
|
||||||
|
orgId?: string
|
||||||
|
) {
|
||||||
|
let loginPage: LoginPage | null = null;
|
||||||
|
if (orgId) {
|
||||||
|
const { tier } = await getOrgTierData(orgId); // returns null in oss
|
||||||
|
if (tier === TierId.STANDARD) {
|
||||||
|
loginPage = await getOrgLoginPage(orgId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let redirectUrl: string | undefined = undefined;
|
||||||
|
if (redirectPath) {
|
||||||
|
let endpoint: string;
|
||||||
|
|
||||||
|
if (loginPage && loginPage.domainId && loginPage.fullDomain) {
|
||||||
|
const secure = config
|
||||||
|
.getRawConfig()
|
||||||
|
.app.dashboard_url?.startsWith("https");
|
||||||
|
const method = secure ? "https" : "http";
|
||||||
|
endpoint = `${method}://${loginPage.fullDomain}`;
|
||||||
|
} else {
|
||||||
|
endpoint = config.getRawConfig().app.dashboard_url!;
|
||||||
|
}
|
||||||
|
redirectUrl = `${endpoint}${redirectPath}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
data: { headerAuthChallenged: true, valid: false, redirectUrl },
|
||||||
|
success: true,
|
||||||
|
error: false,
|
||||||
|
message: "Access denied",
|
||||||
|
status: HttpCode.OK
|
||||||
|
};
|
||||||
|
logger.debug(JSON.stringify(data));
|
||||||
|
return response<VerifyUserResponse>(res, data);
|
||||||
|
}
|
||||||
|
|
||||||
async function isUserAllowedToAccessResource(
|
async function isUserAllowedToAccessResource(
|
||||||
userSessionId: string,
|
userSessionId: string,
|
||||||
resource: Resource,
|
resource: Resource,
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import {Request, Response, NextFunction} from "express";
|
||||||
import { z } from "zod";
|
import {z} from "zod";
|
||||||
import {
|
import {
|
||||||
db,
|
db,
|
||||||
resourceHeaderAuth,
|
resourceHeaderAuth, resourceHeaderAuthExtendedCompatibility,
|
||||||
resourcePassword,
|
resourcePassword,
|
||||||
resourcePincode,
|
resourcePincode,
|
||||||
resources
|
resources
|
||||||
} from "@server/db";
|
} from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import {eq} from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import { fromError } from "zod-validation-error";
|
import {fromError} from "zod-validation-error";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { build } from "@server/build";
|
import {build} from "@server/build";
|
||||||
|
|
||||||
const getResourceAuthInfoSchema = z.strictObject({
|
const getResourceAuthInfoSchema = z.strictObject({
|
||||||
resourceGuid: z.string()
|
resourceGuid: z.string()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type GetResourceAuthInfoResponse = {
|
export type GetResourceAuthInfoResponse = {
|
||||||
resourceId: number;
|
resourceId: number;
|
||||||
@@ -27,6 +27,7 @@ export type GetResourceAuthInfoResponse = {
|
|||||||
password: boolean;
|
password: boolean;
|
||||||
pincode: boolean;
|
pincode: boolean;
|
||||||
headerAuth: boolean;
|
headerAuth: boolean;
|
||||||
|
headerAuthExtendedCompatibility: boolean;
|
||||||
sso: boolean;
|
sso: boolean;
|
||||||
blockAccess: boolean;
|
blockAccess: boolean;
|
||||||
url: string;
|
url: string;
|
||||||
@@ -51,54 +52,68 @@ export async function getResourceAuthInfo(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { resourceGuid } = parsedParams.data;
|
const {resourceGuid} = parsedParams.data;
|
||||||
|
|
||||||
const isGuidInteger = /^\d+$/.test(resourceGuid);
|
const isGuidInteger = /^\d+$/.test(resourceGuid);
|
||||||
|
|
||||||
const [result] =
|
const [result] =
|
||||||
isGuidInteger && build === "saas"
|
isGuidInteger && build === "saas"
|
||||||
? await db
|
? await db
|
||||||
.select()
|
.select()
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourcePincode,
|
resourcePincode,
|
||||||
eq(resourcePincode.resourceId, resources.resourceId)
|
eq(resourcePincode.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourcePassword,
|
resourcePassword,
|
||||||
eq(resourcePassword.resourceId, resources.resourceId)
|
eq(resourcePassword.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourceHeaderAuth,
|
resourceHeaderAuth,
|
||||||
eq(
|
eq(
|
||||||
resourceHeaderAuth.resourceId,
|
resourceHeaderAuth.resourceId,
|
||||||
resources.resourceId
|
resources.resourceId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.where(eq(resources.resourceId, Number(resourceGuid)))
|
.leftJoin(
|
||||||
.limit(1)
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
|
eq(
|
||||||
|
resourceHeaderAuthExtendedCompatibility.resourceId,
|
||||||
|
resources.resourceId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.where(eq(resources.resourceId, Number(resourceGuid)))
|
||||||
|
.limit(1)
|
||||||
: await db
|
: await db
|
||||||
.select()
|
.select()
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourcePincode,
|
resourcePincode,
|
||||||
eq(resourcePincode.resourceId, resources.resourceId)
|
eq(resourcePincode.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourcePassword,
|
resourcePassword,
|
||||||
eq(resourcePassword.resourceId, resources.resourceId)
|
eq(resourcePassword.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
|
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
resourceHeaderAuth,
|
resourceHeaderAuth,
|
||||||
eq(
|
eq(
|
||||||
resourceHeaderAuth.resourceId,
|
resourceHeaderAuth.resourceId,
|
||||||
resources.resourceId
|
resources.resourceId
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.where(eq(resources.resourceGuid, resourceGuid))
|
.leftJoin(
|
||||||
.limit(1);
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
|
eq(
|
||||||
|
resourceHeaderAuthExtendedCompatibility.resourceId,
|
||||||
|
resources.resourceId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.where(eq(resources.resourceGuid, resourceGuid))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
const resource = result?.resources;
|
const resource = result?.resources;
|
||||||
if (!resource) {
|
if (!resource) {
|
||||||
@@ -110,6 +125,7 @@ export async function getResourceAuthInfo(
|
|||||||
const pincode = result?.resourcePincode;
|
const pincode = result?.resourcePincode;
|
||||||
const password = result?.resourcePassword;
|
const password = result?.resourcePassword;
|
||||||
const headerAuth = result?.resourceHeaderAuth;
|
const headerAuth = result?.resourceHeaderAuth;
|
||||||
|
const headerAuthExtendedCompatibility = result?.resourceHeaderAuthExtendedCompatibility;
|
||||||
|
|
||||||
const url = `${resource.ssl ? "https" : "http"}://${resource.fullDomain}`;
|
const url = `${resource.ssl ? "https" : "http"}://${resource.fullDomain}`;
|
||||||
|
|
||||||
@@ -122,6 +138,7 @@ export async function getResourceAuthInfo(
|
|||||||
password: password !== null,
|
password: password !== null,
|
||||||
pincode: pincode !== null,
|
pincode: pincode !== null,
|
||||||
headerAuth: headerAuth !== null,
|
headerAuth: headerAuth !== null,
|
||||||
|
headerAuthExtendedCompatibility: headerAuthExtendedCompatibility !== null,
|
||||||
sso: resource.sso,
|
sso: resource.sso,
|
||||||
blockAccess: resource.blockAccess,
|
blockAccess: resource.blockAccess,
|
||||||
url,
|
url,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db, resourceHeaderAuth } from "@server/db";
|
import {db, resourceHeaderAuth, resourceHeaderAuthExtendedCompatibility} from "@server/db";
|
||||||
import {
|
import {
|
||||||
resources,
|
resources,
|
||||||
userResources,
|
userResources,
|
||||||
@@ -67,7 +67,7 @@ type JoinedRow = {
|
|||||||
hcEnabled: boolean | null;
|
hcEnabled: boolean | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// grouped by resource with targets[])
|
// grouped by resource with targets[])
|
||||||
export type ResourceWithTargets = {
|
export type ResourceWithTargets = {
|
||||||
resourceId: number;
|
resourceId: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -111,7 +111,7 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
|
|||||||
domainId: resources.domainId,
|
domainId: resources.domainId,
|
||||||
niceId: resources.niceId,
|
niceId: resources.niceId,
|
||||||
headerAuthId: resourceHeaderAuth.headerAuthId,
|
headerAuthId: resourceHeaderAuth.headerAuthId,
|
||||||
|
headerAuthExtendedCompatibilityId: resourceHeaderAuthExtendedCompatibility.headerAuthExtendedCompatibilityId,
|
||||||
targetId: targets.targetId,
|
targetId: targets.targetId,
|
||||||
targetIp: targets.ip,
|
targetIp: targets.ip,
|
||||||
targetPort: targets.port,
|
targetPort: targets.port,
|
||||||
@@ -133,6 +133,10 @@ function queryResources(accessibleResourceIds: number[], orgId: string) {
|
|||||||
resourceHeaderAuth,
|
resourceHeaderAuth,
|
||||||
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
|
.leftJoin(
|
||||||
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
|
eq(resourceHeaderAuthExtendedCompatibility.resourceId, resources.resourceId)
|
||||||
|
)
|
||||||
.leftJoin(targets, eq(targets.resourceId, resources.resourceId))
|
.leftJoin(targets, eq(targets.resourceId, resources.resourceId))
|
||||||
.leftJoin(
|
.leftJoin(
|
||||||
targetHealthCheck,
|
targetHealthCheck,
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import {Request, Response, NextFunction} from "express";
|
||||||
import { z } from "zod";
|
import {z} from "zod";
|
||||||
import { db, resourceHeaderAuth } from "@server/db";
|
import {db, resourceHeaderAuth, resourceHeaderAuthExtendedCompatibility} from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import {eq} from "drizzle-orm";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import { fromError } from "zod-validation-error";
|
import {fromError} from "zod-validation-error";
|
||||||
import { response } from "@server/lib/response";
|
import {response} from "@server/lib/response";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { hashPassword } from "@server/auth/password";
|
import {hashPassword} from "@server/auth/password";
|
||||||
import { OpenAPITags, registry } from "@server/openApi";
|
import {OpenAPITags, registry} from "@server/openApi";
|
||||||
|
|
||||||
const setResourceAuthMethodsParamsSchema = z.object({
|
const setResourceAuthMethodsParamsSchema = z.object({
|
||||||
resourceId: z.string().transform(Number).pipe(z.int().positive())
|
resourceId: z.string().transform(Number).pipe(z.int().positive())
|
||||||
});
|
});
|
||||||
|
|
||||||
const setResourceAuthMethodsBodySchema = z.strictObject({
|
const setResourceAuthMethodsBodySchema = z.strictObject({
|
||||||
user: z.string().min(4).max(100).nullable(),
|
user: z.string().min(4).max(100).nullable(),
|
||||||
password: z.string().min(4).max(100).nullable()
|
password: z.string().min(4).max(100).nullable(),
|
||||||
});
|
extendedCompatibility: z.boolean().nullable()
|
||||||
|
});
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "post",
|
method: "post",
|
||||||
@@ -66,21 +67,29 @@ export async function setResourceHeaderAuth(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { resourceId } = parsedParams.data;
|
const {resourceId} = parsedParams.data;
|
||||||
const { user, password } = parsedBody.data;
|
const {user, password, extendedCompatibility} = parsedBody.data;
|
||||||
|
|
||||||
await db.transaction(async (trx) => {
|
await db.transaction(async (trx) => {
|
||||||
await trx
|
await trx
|
||||||
.delete(resourceHeaderAuth)
|
.delete(resourceHeaderAuth)
|
||||||
.where(eq(resourceHeaderAuth.resourceId, resourceId));
|
.where(eq(resourceHeaderAuth.resourceId, resourceId));
|
||||||
|
await trx.delete(resourceHeaderAuthExtendedCompatibility).where(eq(resourceHeaderAuthExtendedCompatibility.resourceId, resourceId));
|
||||||
|
|
||||||
if (user && password) {
|
if (user && password && extendedCompatibility !== null) {
|
||||||
const headerAuthHash = await hashPassword(Buffer.from(`${user}:${password}`).toString("base64"));
|
const headerAuthHash = await hashPassword(Buffer.from(`${user}:${password}`).toString("base64"));
|
||||||
|
|
||||||
await trx
|
await Promise.all([
|
||||||
.insert(resourceHeaderAuth)
|
trx
|
||||||
.values({ resourceId, headerAuthHash });
|
.insert(resourceHeaderAuth)
|
||||||
|
.values({resourceId, headerAuthHash}),
|
||||||
|
trx
|
||||||
|
.insert(resourceHeaderAuthExtendedCompatibility)
|
||||||
|
.values({resourceId, extendedCompatibilityIsActivated: extendedCompatibility})
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return response(res, {
|
return response(res, {
|
||||||
|
|||||||
@@ -439,7 +439,8 @@ export default function ResourceAuthenticationPage() {
|
|||||||
|
|
||||||
api.post(`/resource/${resource.resourceId}/header-auth`, {
|
api.post(`/resource/${resource.resourceId}/header-auth`, {
|
||||||
user: null,
|
user: null,
|
||||||
password: null
|
password: null,
|
||||||
|
extendedCompatibility: null,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Button } from "@app/components/ui/button";
|
import {Button} from "@app/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -9,12 +9,12 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage
|
FormMessage
|
||||||
} from "@app/components/ui/form";
|
} from "@app/components/ui/form";
|
||||||
import { Input } from "@app/components/ui/input";
|
import {Input} from "@app/components/ui/input";
|
||||||
import { toast } from "@app/hooks/useToast";
|
import {toast} from "@app/hooks/useToast";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import {zodResolver} from "@hookform/resolvers/zod";
|
||||||
import { useEffect, useState } from "react";
|
import {useEffect, useState} from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import {useForm} from "react-hook-form";
|
||||||
import { z } from "zod";
|
import {z} from "zod";
|
||||||
import {
|
import {
|
||||||
Credenza,
|
Credenza,
|
||||||
CredenzaBody,
|
CredenzaBody,
|
||||||
@@ -25,23 +25,27 @@ import {
|
|||||||
CredenzaHeader,
|
CredenzaHeader,
|
||||||
CredenzaTitle
|
CredenzaTitle
|
||||||
} from "@app/components/Credenza";
|
} from "@app/components/Credenza";
|
||||||
import { formatAxiosError } from "@app/lib/api";
|
import {formatAxiosError} from "@app/lib/api";
|
||||||
import { AxiosResponse } from "axios";
|
import {AxiosResponse} from "axios";
|
||||||
import { Resource } from "@server/db";
|
import {Resource} from "@server/db";
|
||||||
import { createApiClient } from "@app/lib/api";
|
import {createApiClient} from "@app/lib/api";
|
||||||
import { useEnvContext } from "@app/hooks/useEnvContext";
|
import {useEnvContext} from "@app/hooks/useEnvContext";
|
||||||
import { useTranslations } from "next-intl";
|
import {useTranslations} from "next-intl";
|
||||||
|
import {SwitchInput} from "@/components/SwitchInput";
|
||||||
|
import {InfoPopup} from "@/components/ui/info-popup";
|
||||||
|
|
||||||
const setHeaderAuthFormSchema = z.object({
|
const setHeaderAuthFormSchema = z.object({
|
||||||
user: z.string().min(4).max(100),
|
user: z.string().min(4).max(100),
|
||||||
password: z.string().min(4).max(100)
|
password: z.string().min(4).max(100),
|
||||||
|
extendedCompatibility: z.boolean()
|
||||||
});
|
});
|
||||||
|
|
||||||
type SetHeaderAuthFormValues = z.infer<typeof setHeaderAuthFormSchema>;
|
type SetHeaderAuthFormValues = z.infer<typeof setHeaderAuthFormSchema>;
|
||||||
|
|
||||||
const defaultValues: Partial<SetHeaderAuthFormValues> = {
|
const defaultValues: Partial<SetHeaderAuthFormValues> = {
|
||||||
user: "",
|
user: "",
|
||||||
password: ""
|
password: "",
|
||||||
|
extendedCompatibility: false
|
||||||
};
|
};
|
||||||
|
|
||||||
type SetHeaderAuthFormProps = {
|
type SetHeaderAuthFormProps = {
|
||||||
@@ -52,11 +56,11 @@ type SetHeaderAuthFormProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function SetResourceHeaderAuthForm({
|
export default function SetResourceHeaderAuthForm({
|
||||||
open,
|
open,
|
||||||
setOpen,
|
setOpen,
|
||||||
resourceId,
|
resourceId,
|
||||||
onSetHeaderAuth
|
onSetHeaderAuth
|
||||||
}: SetHeaderAuthFormProps) {
|
}: SetHeaderAuthFormProps) {
|
||||||
const api = createApiClient(useEnvContext());
|
const api = createApiClient(useEnvContext());
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
@@ -80,18 +84,9 @@ export default function SetResourceHeaderAuthForm({
|
|||||||
|
|
||||||
api.post<AxiosResponse<Resource>>(`/resource/${resourceId}/header-auth`, {
|
api.post<AxiosResponse<Resource>>(`/resource/${resourceId}/header-auth`, {
|
||||||
user: data.user,
|
user: data.user,
|
||||||
password: data.password
|
password: data.password,
|
||||||
|
extendedCompatibility: data.extendedCompatibility
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: t('resourceErrorHeaderAuthSetup'),
|
|
||||||
description: formatAxiosError(
|
|
||||||
e,
|
|
||||||
t('resourceErrorHeaderAuthSetupDescription')
|
|
||||||
)
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast({
|
toast({
|
||||||
title: t('resourceHeaderAuthSetup'),
|
title: t('resourceHeaderAuthSetup'),
|
||||||
@@ -102,6 +97,16 @@ export default function SetResourceHeaderAuthForm({
|
|||||||
onSetHeaderAuth();
|
onSetHeaderAuth();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: t('resourceErrorHeaderAuthSetup'),
|
||||||
|
description: formatAxiosError(
|
||||||
|
e,
|
||||||
|
t('resourceErrorHeaderAuthSetupDescription')
|
||||||
|
)
|
||||||
|
});
|
||||||
|
})
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +137,7 @@ export default function SetResourceHeaderAuthForm({
|
|||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="user"
|
name="user"
|
||||||
render={({ field }) => (
|
render={({field}) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('user')}</FormLabel>
|
<FormLabel>{t('user')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
@@ -142,14 +147,14 @@ export default function SetResourceHeaderAuthForm({
|
|||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="password"
|
name="password"
|
||||||
render={({ field }) => (
|
render={({field}) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('password')}</FormLabel>
|
<FormLabel>{t('password')}</FormLabel>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
@@ -159,7 +164,25 @@ export default function SetResourceHeaderAuthForm({
|
|||||||
{...field}
|
{...field}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage/>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="extendedCompatibility"
|
||||||
|
render={({field}) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormControl>
|
||||||
|
<SwitchInput
|
||||||
|
id="header-auth-compatibility-toggle"
|
||||||
|
label={t("headerAuthCompatibility")}
|
||||||
|
info={t('headerAuthCompatibilityInfo')}
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage/>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -78,16 +78,6 @@ export default function SetResourcePasswordForm({
|
|||||||
api.post<AxiosResponse<Resource>>(`/resource/${resourceId}/password`, {
|
api.post<AxiosResponse<Resource>>(`/resource/${resourceId}/password`, {
|
||||||
password: data.password
|
password: data.password
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: t('resourceErrorPasswordSetup'),
|
|
||||||
description: formatAxiosError(
|
|
||||||
e,
|
|
||||||
t('resourceErrorPasswordSetupDescription')
|
|
||||||
)
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast({
|
toast({
|
||||||
title: t('resourcePasswordSetup'),
|
title: t('resourcePasswordSetup'),
|
||||||
@@ -98,6 +88,16 @@ export default function SetResourcePasswordForm({
|
|||||||
onSetPassword();
|
onSetPassword();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: t('resourceErrorPasswordSetup'),
|
||||||
|
description: formatAxiosError(
|
||||||
|
e,
|
||||||
|
t('resourceErrorPasswordSetupDescription')
|
||||||
|
)
|
||||||
|
});
|
||||||
|
})
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,16 +84,6 @@ export default function SetResourcePincodeForm({
|
|||||||
api.post<AxiosResponse<Resource>>(`/resource/${resourceId}/pincode`, {
|
api.post<AxiosResponse<Resource>>(`/resource/${resourceId}/pincode`, {
|
||||||
pincode: data.pincode
|
pincode: data.pincode
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: t('resourceErrorPincodeSetup'),
|
|
||||||
description: formatAxiosError(
|
|
||||||
e,
|
|
||||||
t('resourceErrorPincodeSetupDescription')
|
|
||||||
)
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast({
|
toast({
|
||||||
title: t('resourcePincodeSetup'),
|
title: t('resourcePincodeSetup'),
|
||||||
@@ -104,6 +94,16 @@ export default function SetResourcePincodeForm({
|
|||||||
onSetPincode();
|
onSetPincode();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: t('resourceErrorPincodeSetup'),
|
||||||
|
description: formatAxiosError(
|
||||||
|
e,
|
||||||
|
t('resourceErrorPincodeSetupDescription')
|
||||||
|
)
|
||||||
|
});
|
||||||
|
})
|
||||||
.finally(() => setLoading(false));
|
.finally(() => setLoading(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { Switch } from "./ui/switch";
|
import {Switch} from "./ui/switch";
|
||||||
import { Label } from "./ui/label";
|
import {Label} from "./ui/label";
|
||||||
|
import {Button} from "@/components/ui/button";
|
||||||
|
import {Info} from "lucide-react";
|
||||||
|
import {info} from "winston";
|
||||||
|
import {Popover, PopoverContent, PopoverTrigger} from "@/components/ui/popover";
|
||||||
|
|
||||||
interface SwitchComponentProps {
|
interface SwitchComponentProps {
|
||||||
id: string;
|
id: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
|
info?: string;
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
defaultChecked?: boolean;
|
defaultChecked?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
@@ -13,14 +18,26 @@ interface SwitchComponentProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function SwitchInput({
|
export function SwitchInput({
|
||||||
id,
|
id,
|
||||||
label,
|
label,
|
||||||
description,
|
description,
|
||||||
disabled,
|
info,
|
||||||
checked,
|
disabled,
|
||||||
defaultChecked = false,
|
checked,
|
||||||
onCheckedChange
|
defaultChecked = false,
|
||||||
}: SwitchComponentProps) {
|
onCheckedChange
|
||||||
|
}: SwitchComponentProps) {
|
||||||
|
const defaultTrigger = (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-6 w-6 rounded-full p-0"
|
||||||
|
>
|
||||||
|
<Info className="h-4 w-4"/>
|
||||||
|
<span className="sr-only">Show info</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center space-x-2 mb-2">
|
<div className="flex items-center space-x-2 mb-2">
|
||||||
@@ -32,6 +49,18 @@ export function SwitchInput({
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
{label && <Label htmlFor={id}>{label}</Label>}
|
{label && <Label htmlFor={id}>{label}</Label>}
|
||||||
|
{info && <Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
{defaultTrigger}
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-80">
|
||||||
|
{info && (
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{info}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>}
|
||||||
</div>
|
</div>
|
||||||
{description && (
|
{description && (
|
||||||
<span className="text-muted-foreground text-sm">
|
<span className="text-muted-foreground text-sm">
|
||||||
|
|||||||
Reference in New Issue
Block a user