mirror of
https://github.com/fosrl/pangolin.git
synced 2026-03-01 08:16:44 +00:00
Merge branch 'dev' of github.com:jln-brtn/pangolin into jln-brtn-dev
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"setupCreate": "Create the organization, site, and resources",
|
"setupCreate": "Create the organization, site, and resources",
|
||||||
|
"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",
|
||||||
"setupNewOrg": "New Organization",
|
"setupNewOrg": "New Organization",
|
||||||
"setupCreateOrg": "Create Organization",
|
"setupCreateOrg": "Create Organization",
|
||||||
"setupCreateResources": "Create Resources",
|
"setupCreateResources": "Create Resources",
|
||||||
|
|||||||
@@ -456,6 +456,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")
|
||||||
@@ -856,6 +864,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,7 +57,14 @@ export async function getResourceByDomain(
|
|||||||
resourceHeaderAuth,
|
resourceHeaderAuth,
|
||||||
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
eq(resourceHeaderAuth.resourceId, resources.resourceId)
|
||||||
)
|
)
|
||||||
.innerJoin(orgs, eq(orgs.orgId, resources.orgId))
|
.leftJoin(
|
||||||
|
resourceHeaderAuthExtendedCompatibility,
|
||||||
|
eq(resourceHeaderAuthExtendedCompatibility.resourceId, resources.resourceId)
|
||||||
|
)
|
||||||
|
.innerJoin(
|
||||||
|
orgs,
|
||||||
|
eq(orgs.orgId, resources.orgId)
|
||||||
|
)
|
||||||
.where(eq(resources.fullDomain, domain))
|
.where(eq(resources.fullDomain, domain))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
@@ -65,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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,22 +12,22 @@ import { no } from "zod/v4/locales";
|
|||||||
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"),
|
||||||
@@ -41,7 +41,7 @@ export const orgs = sqliteTable("orgs", {
|
|||||||
subnet: text("subnet"),
|
subnet: text("subnet"),
|
||||||
utilitySubnet: text("utilitySubnet"), // this is the subnet for utility addresses
|
utilitySubnet: text("utilitySubnet"), // this is the subnet for utility addresses
|
||||||
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 and 9001 = end of the following year
|
settingsLogRetentionDaysRequest: integer("settingsLogRetentionDaysRequest") // where 0 = dont keep logs and -1 = keep forever and 9001 = end of the following year
|
||||||
@@ -58,23 +58,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"
|
||||||
@@ -91,7 +91,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
|
||||||
@@ -99,14 +99,14 @@ 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)
|
||||||
});
|
});
|
||||||
|
|
||||||
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()),
|
||||||
@@ -122,27 +122,27 @@ 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"
|
||||||
}),
|
}),
|
||||||
@@ -154,7 +154,7 @@ export const resources = sqliteTable("resources", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
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"
|
||||||
@@ -169,7 +169,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
|
||||||
@@ -183,8 +183,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"),
|
||||||
@@ -206,7 +206,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
|
||||||
@@ -214,7 +214,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")
|
||||||
@@ -227,10 +227,10 @@ 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(),
|
||||||
mode: text("mode").notNull(), // "host" | "cidr" | "port"
|
mode: text("mode").notNull(), // "host" | "cidr" | "port"
|
||||||
@@ -283,20 +283,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")
|
||||||
@@ -330,7 +330,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")
|
||||||
});
|
});
|
||||||
@@ -369,7 +369,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")
|
||||||
});
|
});
|
||||||
@@ -415,10 +415,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()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -426,7 +426,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"),
|
||||||
deviceAuthUsed: integer("deviceAuthUsed", { mode: "boolean" })
|
deviceAuthUsed: integer("deviceAuthUsed", { mode: "boolean" })
|
||||||
@@ -438,7 +438,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()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -446,14 +446,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"
|
||||||
@@ -462,28 +462,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()
|
||||||
});
|
});
|
||||||
@@ -495,13 +495,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")
|
||||||
});
|
});
|
||||||
@@ -509,92 +509,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", {
|
||||||
@@ -603,7 +603,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()
|
||||||
});
|
});
|
||||||
@@ -614,7 +614,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()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -624,18 +624,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"),
|
||||||
@@ -648,13 +658,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"
|
||||||
}),
|
}),
|
||||||
@@ -686,11 +696,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", {
|
||||||
@@ -699,7 +709,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()
|
||||||
@@ -711,11 +721,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
|
||||||
@@ -723,17 +733,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"),
|
||||||
@@ -753,7 +763,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(),
|
||||||
@@ -781,22 +791,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"
|
||||||
@@ -807,10 +817,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")
|
||||||
});
|
});
|
||||||
@@ -828,19 +838,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"),
|
||||||
@@ -857,7 +867,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),
|
||||||
@@ -913,6 +923,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
|
||||||
);
|
);
|
||||||
@@ -228,7 +228,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,
|
||||||
@@ -287,21 +287,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
|
||||||
|
})
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +380,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(
|
||||||
@@ -374,7 +392,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(
|
||||||
@@ -419,7 +437,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
|
||||||
);
|
);
|
||||||
@@ -656,18 +674,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
|
||||||
|
}),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1018,7 +1043,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(
|
||||||
|
|||||||
@@ -53,12 +53,11 @@ export const AuthSchema = z.object({
|
|||||||
// pincode has to have 6 digits
|
// pincode has to have 6 digits
|
||||||
pincode: z.number().min(100000).max(999999).optional(),
|
pincode: z.number().min(100000).max(999999).optional(),
|
||||||
password: z.string().min(1).optional(),
|
password: z.string().min(1).optional(),
|
||||||
"basic-auth": z
|
"basic-auth": z.object({
|
||||||
.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
|
||||||
.array(z.string())
|
.array(z.string())
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -175,6 +177,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 = {
|
||||||
@@ -498,6 +501,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);
|
||||||
|
|
||||||
@@ -530,7 +537,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, {
|
||||||
|
|||||||
@@ -10,7 +10,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,
|
||||||
@@ -66,6 +66,7 @@ type BasicUserData = {
|
|||||||
|
|
||||||
export type VerifyUserResponse = {
|
export type VerifyUserResponse = {
|
||||||
valid: boolean;
|
valid: boolean;
|
||||||
|
headerAuthChallenged?: boolean;
|
||||||
redirectUrl?: string;
|
redirectUrl?: string;
|
||||||
userData?: BasicUserData;
|
userData?: BasicUserData;
|
||||||
};
|
};
|
||||||
@@ -147,6 +148,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);
|
||||||
@@ -176,7 +178,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}`);
|
||||||
@@ -456,7 +458,8 @@ export async function verifyResourceSession(
|
|||||||
!sso &&
|
!sso &&
|
||||||
!pincode &&
|
!pincode &&
|
||||||
!password &&
|
!password &&
|
||||||
!resource.emailWhitelistEnabled
|
!resource.emailWhitelistEnabled &&
|
||||||
|
!headerAuthExtendedCompatibility?.extendedCompatibilityIsActivated
|
||||||
) {
|
) {
|
||||||
logRequestAudit(
|
logRequestAudit(
|
||||||
{
|
{
|
||||||
@@ -471,13 +474,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(
|
||||||
{
|
{
|
||||||
@@ -563,7 +568,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
|
||||||
@@ -707,6 +712,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) {
|
||||||
@@ -839,6 +849,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,19 +1,19 @@
|
|||||||
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()
|
||||||
@@ -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,53 +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(
|
|
||||||
resourceHeaderAuth,
|
.leftJoin(
|
||||||
eq(
|
resourceHeaderAuth,
|
||||||
resourceHeaderAuth.resourceId,
|
eq(
|
||||||
resources.resourceId
|
resourceHeaderAuth.resourceId,
|
||||||
)
|
resources.resourceId
|
||||||
)
|
)
|
||||||
.where(eq(resources.resourceGuid, resourceGuid))
|
)
|
||||||
.limit(1);
|
.leftJoin(
|
||||||
|
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) {
|
||||||
@@ -109,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}`;
|
||||||
|
|
||||||
@@ -121,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,
|
||||||
@@ -109,7 +109,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,
|
||||||
@@ -131,6 +131,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,14 +1,14 @@
|
|||||||
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())
|
||||||
@@ -16,7 +16,8 @@ const setResourceAuthMethodsParamsSchema = z.object({
|
|||||||
|
|
||||||
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({
|
||||||
@@ -66,23 +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(
|
const headerAuthHash = await hashPassword(Buffer.from(`${user}:${password}`).toString("base64"));
|
||||||
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, {
|
||||||
|
|||||||
@@ -397,7 +397,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();
|
||||||
|
|
||||||
@@ -78,23 +82,11 @@ export default function SetResourceHeaderAuthForm({
|
|||||||
async function onSubmit(data: SetHeaderAuthFormValues) {
|
async function onSubmit(data: SetHeaderAuthFormValues) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
api.post<AxiosResponse<Resource>>(
|
api.post<AxiosResponse<Resource>>(`/resource/${resourceId}/header-auth`, {
|
||||||
`/resource/${resourceId}/header-auth`,
|
user: data.user,
|
||||||
{
|
password: data.password,
|
||||||
user: data.user,
|
extendedCompatibility: data.extendedCompatibility
|
||||||
password: data.password
|
})
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch((e) => {
|
|
||||||
toast({
|
|
||||||
variant: "destructive",
|
|
||||||
title: t("resourceErrorHeaderAuthSetup"),
|
|
||||||
description: formatAxiosError(
|
|
||||||
e,
|
|
||||||
t("resourceErrorHeaderAuthSetupDescription")
|
|
||||||
)
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast({
|
toast({
|
||||||
title: t("resourceHeaderAuthSetup"),
|
title: t("resourceHeaderAuthSetup"),
|
||||||
@@ -105,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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +139,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>
|
||||||
@@ -147,14 +149,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>
|
<FormLabel>
|
||||||
{t("password")}
|
{t("password")}
|
||||||
@@ -166,7 +168,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