mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-20 11:56:38 +00:00
Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a0d440a34 | ||
|
|
b0500fac29 | ||
|
|
af16e6423a | ||
|
|
ff90471f0f | ||
|
|
bcc501c524 | ||
|
|
c4ef211a3e | ||
|
|
592c0eb7ab | ||
|
|
880a000865 | ||
|
|
a9571f6adf | ||
|
|
ca91f313bc | ||
|
|
76b9753916 | ||
|
|
cd8bbe28bf | ||
|
|
9550c11594 | ||
|
|
7ac21cad25 | ||
|
|
2008a3955a | ||
|
|
f1641c9f3e | ||
|
|
bec5bbd033 | ||
|
|
ae11f72e28 | ||
|
|
6b88cb3920 | ||
|
|
38772111e8 | ||
|
|
14f50c3e66 | ||
|
|
b89a2c9e49 | ||
|
|
2d2eda988c | ||
|
|
432033969b | ||
|
|
1fbf74e1f7 | ||
|
|
93648ff00b | ||
|
|
9513136610 | ||
|
|
43a2a39f8d | ||
|
|
218351de9a | ||
|
|
b92b922eee | ||
|
|
91be4937ee | ||
|
|
19b36a5fae | ||
|
|
bb9ee7dfd2 | ||
|
|
ac0351b525 | ||
|
|
405f5ad7cc | ||
|
|
8cc2712da3 | ||
|
|
c02ac8d1bf | ||
|
|
a1802add19 | ||
|
|
218a6642a2 | ||
|
|
21a83a5755 | ||
|
|
bec75e51f6 | ||
|
|
6c9b445be6 | ||
|
|
06b17fa941 | ||
|
|
e1d4c029e7 | ||
|
|
293fd70ccb | ||
|
|
4ee863db5a | ||
|
|
2717be0fed | ||
|
|
1f312e146f | ||
|
|
b91557ebb0 | ||
|
|
465380b5a3 | ||
|
|
60af901feb | ||
|
|
ea78a654ff | ||
|
|
f28b6ad0a5 | ||
|
|
a3bdab1318 | ||
|
|
f8c5d01e3c | ||
|
|
3ebe218b7f | ||
|
|
7d039ab729 | ||
|
|
b2b6c8c268 | ||
|
|
4950f25063 | ||
|
|
524d6b48d9 | ||
|
|
29fb5735e2 | ||
|
|
247fc85440 | ||
|
|
2b4302572c | ||
|
|
9b28780e62 | ||
|
|
8656f68008 | ||
|
|
15651b6919 | ||
|
|
78d3861382 | ||
|
|
72f19274cd | ||
|
|
adbcd1a2e0 | ||
|
|
5b7727fab4 | ||
|
|
9627dfa90c |
@@ -63,7 +63,7 @@ esbuild
|
|||||||
packagePath: getPackagePaths(),
|
packagePath: getPackagePaths(),
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
sourcemap: true,
|
sourcemap: "external",
|
||||||
target: "node22",
|
target: "node22",
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ entryPoints:
|
|||||||
address: ":80"
|
address: ":80"
|
||||||
websecure:
|
websecure:
|
||||||
address: ":443"
|
address: ":443"
|
||||||
|
{{if .HybridMode}} proxyProtocol:
|
||||||
|
trustedIPs:
|
||||||
|
- 0.0.0.0/0
|
||||||
|
- ::1/128{{end}}
|
||||||
transport:
|
transport:
|
||||||
respondingTimeouts:
|
respondingTimeouts:
|
||||||
readTimeout: "30m"
|
readTimeout: "30m"
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ module installer
|
|||||||
go 1.24
|
go 1.24
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/term v0.33.0
|
golang.org/x/term v0.34.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require golang.org/x/sys v0.34.0 // indirect
|
require golang.org/x/sys v0.35.0 // indirect
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "{resourceName} Settings",
|
"resourceSetting": "{resourceName} Settings",
|
||||||
"alwaysAllow": "Always Allow",
|
"alwaysAllow": "Always Allow",
|
||||||
"alwaysDeny": "Always Deny",
|
"alwaysDeny": "Always Deny",
|
||||||
|
"passToAuth": "Pass to Auth",
|
||||||
"orgSettingsDescription": "Configure your organization's general settings",
|
"orgSettingsDescription": "Configure your organization's general settings",
|
||||||
"orgGeneralSettings": "Organization Settings",
|
"orgGeneralSettings": "Organization Settings",
|
||||||
"orgGeneralSettingsDescription": "Manage your organization details and configuration",
|
"orgGeneralSettingsDescription": "Manage your organization details and configuration",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Actions",
|
"rulesActions": "Actions",
|
||||||
"rulesActionAlwaysAllow": "Always Allow: Bypass all authentication methods",
|
"rulesActionAlwaysAllow": "Always Allow: Bypass all authentication methods",
|
||||||
"rulesActionAlwaysDeny": "Always Deny: Block all requests; no authentication can be attempted",
|
"rulesActionAlwaysDeny": "Always Deny: Block all requests; no authentication can be attempted",
|
||||||
|
"rulesActionPassToAuth": "Pass to Auth: Allow authentication methods to be attempted",
|
||||||
"rulesMatchCriteria": "Matching Criteria",
|
"rulesMatchCriteria": "Matching Criteria",
|
||||||
"rulesMatchCriteriaIpAddress": "Match a specific IP address",
|
"rulesMatchCriteriaIpAddress": "Match a specific IP address",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Match a range of IP addresses in CIDR notation",
|
"rulesMatchCriteriaIpAddressRange": "Match a range of IP addresses in CIDR notation",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Update Client",
|
"actionUpdateClient": "Update Client",
|
||||||
"actionListClients": "List Clients",
|
"actionListClients": "List Clients",
|
||||||
"actionGetClient": "Get Client",
|
"actionGetClient": "Get Client",
|
||||||
|
"actionCreateSiteResource": "Create Site Resource",
|
||||||
|
"actionDeleteSiteResource": "Delete Site Resource",
|
||||||
|
"actionGetSiteResource": "Get Site Resource",
|
||||||
|
"actionListSiteResources": "List Site Resources",
|
||||||
|
"actionUpdateSiteResource": "Update Site Resource",
|
||||||
"noneSelected": "None selected",
|
"noneSelected": "None selected",
|
||||||
"orgNotFound2": "No organizations found.",
|
"orgNotFound2": "No organizations found.",
|
||||||
"searchProgress": "Search...",
|
"searchProgress": "Search...",
|
||||||
|
|||||||
@@ -149,44 +149,44 @@
|
|||||||
"resourceDescription": "Vytvořte bezpečné proxy služby pro přístup k privátním aplikacím",
|
"resourceDescription": "Vytvořte bezpečné proxy služby pro přístup k privátním aplikacím",
|
||||||
"resourcesSearch": "Prohledat zdroje...",
|
"resourcesSearch": "Prohledat zdroje...",
|
||||||
"resourceAdd": "Přidat zdroj",
|
"resourceAdd": "Přidat zdroj",
|
||||||
"resourceErrorDelte": "Error deleting resource",
|
"resourceErrorDelte": "Chyba při odstraňování zdroje",
|
||||||
"authentication": "Authentication",
|
"authentication": "Autentifikace",
|
||||||
"protected": "Protected",
|
"protected": "Chráněno",
|
||||||
"notProtected": "Not Protected",
|
"notProtected": "Nechráněno",
|
||||||
"resourceMessageRemove": "Once removed, the resource will no longer be accessible. All targets associated with the resource will also be removed.",
|
"resourceMessageRemove": "Jakmile zdroj odstraníte, nebude dostupný. Všechny související služby a cíle budou také odstraněny.",
|
||||||
"resourceMessageConfirm": "To confirm, please type the name of the resource below.",
|
"resourceMessageConfirm": "Pro potvrzení zadejte prosím název zdroje.",
|
||||||
"resourceQuestionRemove": "Are you sure you want to remove the resource {selectedResource} from the organization?",
|
"resourceQuestionRemove": "Opravdu chcete odstranit zdroj {selectedResource} z organizace?",
|
||||||
"resourceHTTP": "HTTPS Resource",
|
"resourceHTTP": "Zdroj HTTPS",
|
||||||
"resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.",
|
"resourceHTTPDescription": "Proxy requests to your app over HTTPS using a subdomain or base domain.",
|
||||||
"resourceRaw": "Raw TCP/UDP Resource",
|
"resourceRaw": "Raw TCP/UDP Resource",
|
||||||
"resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number.",
|
"resourceRawDescription": "Proxy requests to your app over TCP/UDP using a port number.",
|
||||||
"resourceCreate": "Create Resource",
|
"resourceCreate": "Vytvořit zdroj",
|
||||||
"resourceCreateDescription": "Follow the steps below to create a new resource",
|
"resourceCreateDescription": "Postupujte podle níže uvedených kroků, abyste vytvořili a připojili nový zdroj",
|
||||||
"resourceSeeAll": "See All Resources",
|
"resourceSeeAll": "Zobrazit všechny zdroje",
|
||||||
"resourceInfo": "Resource Information",
|
"resourceInfo": "Informace o zdroji",
|
||||||
"resourceNameDescription": "This is the display name for the resource.",
|
"resourceNameDescription": "Toto je zobrazovaný název zdroje.",
|
||||||
"siteSelect": "Select site",
|
"siteSelect": "Vybrat lokalitu",
|
||||||
"siteSearch": "Search site",
|
"siteSearch": "Hledat lokalitu",
|
||||||
"siteNotFound": "No site found.",
|
"siteNotFound": "Nebyla nalezena žádná lokalita.",
|
||||||
"siteSelectionDescription": "This site will provide connectivity to the target.",
|
"siteSelectionDescription": "Tato lokalita poskytne připojení k cíli.",
|
||||||
"resourceType": "Resource Type",
|
"resourceType": "Typ zdroje",
|
||||||
"resourceTypeDescription": "Determine how you want to access your resource",
|
"resourceTypeDescription": "Určete, jak chcete přistupovat ke svému zdroji",
|
||||||
"resourceHTTPSSettings": "HTTPS Settings",
|
"resourceHTTPSSettings": "Nastavení HTTPS",
|
||||||
"resourceHTTPSSettingsDescription": "Configure how your resource will be accessed over HTTPS",
|
"resourceHTTPSSettingsDescription": "Nakonfigurujte, jak bude váš zdroj přístupný přes HTTPS",
|
||||||
"domainType": "Domain Type",
|
"domainType": "Typ domény",
|
||||||
"subdomain": "Subdomain",
|
"subdomain": "Subdoména",
|
||||||
"baseDomain": "Base Domain",
|
"baseDomain": "Základní doména",
|
||||||
"subdomnainDescription": "The subdomain where your resource will be accessible.",
|
"subdomnainDescription": "Subdoména, kde bude váš zdroj přístupný.",
|
||||||
"resourceRawSettings": "TCP/UDP Settings",
|
"resourceRawSettings": "Nastavení TCP/UDP",
|
||||||
"resourceRawSettingsDescription": "Configure how your resource will be accessed over TCP/UDP",
|
"resourceRawSettingsDescription": "Nakonfigurujte, jak bude váš dokument přístupný přes TCP/UDP",
|
||||||
"protocol": "Protocol",
|
"protocol": "Protokol",
|
||||||
"protocolSelect": "Select a protocol",
|
"protocolSelect": "Vybrat protokol",
|
||||||
"resourcePortNumber": "Port Number",
|
"resourcePortNumber": "Číslo portu",
|
||||||
"resourcePortNumberDescription": "The external port number to proxy requests.",
|
"resourcePortNumberDescription": "Externí port k požadavkům proxy serveru.",
|
||||||
"cancel": "Cancel",
|
"cancel": "Zrušit",
|
||||||
"resourceConfig": "Configuration Snippets",
|
"resourceConfig": "Konfigurační snippety",
|
||||||
"resourceConfigDescription": "Copy and paste these configuration snippets to set up your TCP/UDP resource",
|
"resourceConfigDescription": "Zkopírujte a vložte tyto konfigurační snippety pro nastavení TCP/UDP zdroje",
|
||||||
"resourceAddEntrypoints": "Traefik: Add Entrypoints",
|
"resourceAddEntrypoints": "Traefik: Přidat vstupní body",
|
||||||
"resourceExposePorts": "Gerbil: Expose Ports in Docker Compose",
|
"resourceExposePorts": "Gerbil: Expose Ports in Docker Compose",
|
||||||
"resourceLearnRaw": "Learn how to configure TCP/UDP resources",
|
"resourceLearnRaw": "Learn how to configure TCP/UDP resources",
|
||||||
"resourceBack": "Back to Resources",
|
"resourceBack": "Back to Resources",
|
||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "{resourceName} Settings",
|
"resourceSetting": "{resourceName} Settings",
|
||||||
"alwaysAllow": "Always Allow",
|
"alwaysAllow": "Always Allow",
|
||||||
"alwaysDeny": "Always Deny",
|
"alwaysDeny": "Always Deny",
|
||||||
|
"passToAuth": "Pass to Auth",
|
||||||
"orgSettingsDescription": "Configure your organization's general settings",
|
"orgSettingsDescription": "Configure your organization's general settings",
|
||||||
"orgGeneralSettings": "Organization Settings",
|
"orgGeneralSettings": "Organization Settings",
|
||||||
"orgGeneralSettingsDescription": "Manage your organization details and configuration",
|
"orgGeneralSettingsDescription": "Manage your organization details and configuration",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Actions",
|
"rulesActions": "Actions",
|
||||||
"rulesActionAlwaysAllow": "Always Allow: Bypass all authentication methods",
|
"rulesActionAlwaysAllow": "Always Allow: Bypass all authentication methods",
|
||||||
"rulesActionAlwaysDeny": "Always Deny: Block all requests; no authentication can be attempted",
|
"rulesActionAlwaysDeny": "Always Deny: Block all requests; no authentication can be attempted",
|
||||||
|
"rulesActionPassToAuth": "Pass to Auth: Allow authentication methods to be attempted",
|
||||||
"rulesMatchCriteria": "Matching Criteria",
|
"rulesMatchCriteria": "Matching Criteria",
|
||||||
"rulesMatchCriteriaIpAddress": "Match a specific IP address",
|
"rulesMatchCriteriaIpAddress": "Match a specific IP address",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Match a range of IP addresses in CIDR notation",
|
"rulesMatchCriteriaIpAddressRange": "Match a range of IP addresses in CIDR notation",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Update Client",
|
"actionUpdateClient": "Update Client",
|
||||||
"actionListClients": "List Clients",
|
"actionListClients": "List Clients",
|
||||||
"actionGetClient": "Get Client",
|
"actionGetClient": "Get Client",
|
||||||
|
"actionCreateSiteResource": "Create Site Resource",
|
||||||
|
"actionDeleteSiteResource": "Delete Site Resource",
|
||||||
|
"actionGetSiteResource": "Get Site Resource",
|
||||||
|
"actionListSiteResources": "List Site Resources",
|
||||||
|
"actionUpdateSiteResource": "Update Site Resource",
|
||||||
"noneSelected": "None selected",
|
"noneSelected": "None selected",
|
||||||
"orgNotFound2": "No organizations found.",
|
"orgNotFound2": "No organizations found.",
|
||||||
"searchProgress": "Search...",
|
"searchProgress": "Search...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "{resourceName} Einstellungen",
|
"resourceSetting": "{resourceName} Einstellungen",
|
||||||
"alwaysAllow": "Immer erlauben",
|
"alwaysAllow": "Immer erlauben",
|
||||||
"alwaysDeny": "Immer ablehnen",
|
"alwaysDeny": "Immer ablehnen",
|
||||||
|
"passToAuth": "Weiterleiten zur Authentifizierung",
|
||||||
"orgSettingsDescription": "Konfiguriere die allgemeinen Einstellungen deiner Organisation",
|
"orgSettingsDescription": "Konfiguriere die allgemeinen Einstellungen deiner Organisation",
|
||||||
"orgGeneralSettings": "Organisations-Einstellungen",
|
"orgGeneralSettings": "Organisations-Einstellungen",
|
||||||
"orgGeneralSettingsDescription": "Organisationsdetails und Konfiguration verwalten",
|
"orgGeneralSettingsDescription": "Organisationsdetails und Konfiguration verwalten",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Aktionen",
|
"rulesActions": "Aktionen",
|
||||||
"rulesActionAlwaysAllow": "Immer erlauben: Alle Authentifizierungsmethoden umgehen",
|
"rulesActionAlwaysAllow": "Immer erlauben: Alle Authentifizierungsmethoden umgehen",
|
||||||
"rulesActionAlwaysDeny": "Immer verweigern: Alle Anfragen blockieren; keine Authentifizierung möglich",
|
"rulesActionAlwaysDeny": "Immer verweigern: Alle Anfragen blockieren; keine Authentifizierung möglich",
|
||||||
|
"rulesActionPassToAuth": "Weiterleiten zur Authentifizierung: Erlaubt das Versuchen von Authentifizierungsmethoden",
|
||||||
"rulesMatchCriteria": "Übereinstimmungskriterien",
|
"rulesMatchCriteria": "Übereinstimmungskriterien",
|
||||||
"rulesMatchCriteriaIpAddress": "Mit einer bestimmten IP-Adresse übereinstimmen",
|
"rulesMatchCriteriaIpAddress": "Mit einer bestimmten IP-Adresse übereinstimmen",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Mit einem IP-Adressbereich in CIDR-Notation übereinstimmen",
|
"rulesMatchCriteriaIpAddressRange": "Mit einem IP-Adressbereich in CIDR-Notation übereinstimmen",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Kunde aktualisieren",
|
"actionUpdateClient": "Kunde aktualisieren",
|
||||||
"actionListClients": "Kunden auflisten",
|
"actionListClients": "Kunden auflisten",
|
||||||
"actionGetClient": "Kunde holen",
|
"actionGetClient": "Kunde holen",
|
||||||
|
"actionCreateSiteResource": "Site-Ressource erstellen",
|
||||||
|
"actionDeleteSiteResource": "Site-Ressource löschen",
|
||||||
|
"actionGetSiteResource": "Site-Ressource abrufen",
|
||||||
|
"actionListSiteResources": "Site-Ressourcen auflisten",
|
||||||
|
"actionUpdateSiteResource": "Site-Ressource aktualisieren",
|
||||||
"noneSelected": "Keine ausgewählt",
|
"noneSelected": "Keine ausgewählt",
|
||||||
"orgNotFound2": "Keine Organisationen gefunden.",
|
"orgNotFound2": "Keine Organisationen gefunden.",
|
||||||
"searchProgress": "Suche...",
|
"searchProgress": "Suche...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "{resourceName} Settings",
|
"resourceSetting": "{resourceName} Settings",
|
||||||
"alwaysAllow": "Always Allow",
|
"alwaysAllow": "Always Allow",
|
||||||
"alwaysDeny": "Always Deny",
|
"alwaysDeny": "Always Deny",
|
||||||
|
"passToAuth": "Pass to Auth",
|
||||||
"orgSettingsDescription": "Configure your organization's general settings",
|
"orgSettingsDescription": "Configure your organization's general settings",
|
||||||
"orgGeneralSettings": "Organization Settings",
|
"orgGeneralSettings": "Organization Settings",
|
||||||
"orgGeneralSettingsDescription": "Manage your organization details and configuration",
|
"orgGeneralSettingsDescription": "Manage your organization details and configuration",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Actions",
|
"rulesActions": "Actions",
|
||||||
"rulesActionAlwaysAllow": "Always Allow: Bypass all authentication methods",
|
"rulesActionAlwaysAllow": "Always Allow: Bypass all authentication methods",
|
||||||
"rulesActionAlwaysDeny": "Always Deny: Block all requests; no authentication can be attempted",
|
"rulesActionAlwaysDeny": "Always Deny: Block all requests; no authentication can be attempted",
|
||||||
|
"rulesActionPassToAuth": "Pass to Auth: Allow authentication methods to be attempted",
|
||||||
"rulesMatchCriteria": "Matching Criteria",
|
"rulesMatchCriteria": "Matching Criteria",
|
||||||
"rulesMatchCriteriaIpAddress": "Match a specific IP address",
|
"rulesMatchCriteriaIpAddress": "Match a specific IP address",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Match a range of IP addresses in CIDR notation",
|
"rulesMatchCriteriaIpAddressRange": "Match a range of IP addresses in CIDR notation",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Update Client",
|
"actionUpdateClient": "Update Client",
|
||||||
"actionListClients": "List Clients",
|
"actionListClients": "List Clients",
|
||||||
"actionGetClient": "Get Client",
|
"actionGetClient": "Get Client",
|
||||||
|
"actionCreateSiteResource": "Create Site Resource",
|
||||||
|
"actionDeleteSiteResource": "Delete Site Resource",
|
||||||
|
"actionGetSiteResource": "Get Site Resource",
|
||||||
|
"actionListSiteResources": "List Site Resources",
|
||||||
|
"actionUpdateSiteResource": "Update Site Resource",
|
||||||
"noneSelected": "None selected",
|
"noneSelected": "None selected",
|
||||||
"orgNotFound2": "No organizations found.",
|
"orgNotFound2": "No organizations found.",
|
||||||
"searchProgress": "Search...",
|
"searchProgress": "Search...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "Ajustes {resourceName}",
|
"resourceSetting": "Ajustes {resourceName}",
|
||||||
"alwaysAllow": "Permitir siempre",
|
"alwaysAllow": "Permitir siempre",
|
||||||
"alwaysDeny": "Denegar siempre",
|
"alwaysDeny": "Denegar siempre",
|
||||||
|
"passToAuth": "Pasar a Autenticación",
|
||||||
"orgSettingsDescription": "Configurar la configuración general de su organización",
|
"orgSettingsDescription": "Configurar la configuración general de su organización",
|
||||||
"orgGeneralSettings": "Configuración de la organización",
|
"orgGeneralSettings": "Configuración de la organización",
|
||||||
"orgGeneralSettingsDescription": "Administra los detalles y la configuración de tu organización",
|
"orgGeneralSettingsDescription": "Administra los detalles y la configuración de tu organización",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Acciones",
|
"rulesActions": "Acciones",
|
||||||
"rulesActionAlwaysAllow": "Permitir siempre: pasar todos los métodos de autenticación",
|
"rulesActionAlwaysAllow": "Permitir siempre: pasar todos los métodos de autenticación",
|
||||||
"rulesActionAlwaysDeny": "Denegar siempre: Bloquear todas las peticiones; no se puede intentar autenticación",
|
"rulesActionAlwaysDeny": "Denegar siempre: Bloquear todas las peticiones; no se puede intentar autenticación",
|
||||||
|
"rulesActionPassToAuth": "Pasar a Autenticación: Permitir que se intenten los métodos de autenticación",
|
||||||
"rulesMatchCriteria": "Criterios coincidentes",
|
"rulesMatchCriteria": "Criterios coincidentes",
|
||||||
"rulesMatchCriteriaIpAddress": "Coincidir con una dirección IP específica",
|
"rulesMatchCriteriaIpAddress": "Coincidir con una dirección IP específica",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Coincide con un rango de direcciones IP en notación CIDR",
|
"rulesMatchCriteriaIpAddressRange": "Coincide con un rango de direcciones IP en notación CIDR",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Actualizar cliente",
|
"actionUpdateClient": "Actualizar cliente",
|
||||||
"actionListClients": "Listar clientes",
|
"actionListClients": "Listar clientes",
|
||||||
"actionGetClient": "Obtener cliente",
|
"actionGetClient": "Obtener cliente",
|
||||||
|
"actionCreateSiteResource": "Crear Recurso del Sitio",
|
||||||
|
"actionDeleteSiteResource": "Eliminar recurso del sitio",
|
||||||
|
"actionGetSiteResource": "Obtener recurso del sitio",
|
||||||
|
"actionListSiteResources": "Listar recursos del sitio",
|
||||||
|
"actionUpdateSiteResource": "Actualizar recurso del sitio",
|
||||||
"noneSelected": "Ninguno seleccionado",
|
"noneSelected": "Ninguno seleccionado",
|
||||||
"orgNotFound2": "No se encontraron organizaciones.",
|
"orgNotFound2": "No se encontraron organizaciones.",
|
||||||
"searchProgress": "Buscar...",
|
"searchProgress": "Buscar...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "Réglages {resourceName}",
|
"resourceSetting": "Réglages {resourceName}",
|
||||||
"alwaysAllow": "Toujours autoriser",
|
"alwaysAllow": "Toujours autoriser",
|
||||||
"alwaysDeny": "Toujours refuser",
|
"alwaysDeny": "Toujours refuser",
|
||||||
|
"passToAuth": "Paser à l'authentification",
|
||||||
"orgSettingsDescription": "Configurer les paramètres généraux de votre organisation",
|
"orgSettingsDescription": "Configurer les paramètres généraux de votre organisation",
|
||||||
"orgGeneralSettings": "Paramètres de l'organisation",
|
"orgGeneralSettings": "Paramètres de l'organisation",
|
||||||
"orgGeneralSettingsDescription": "Gérer les détails et la configuration de votre organisation",
|
"orgGeneralSettingsDescription": "Gérer les détails et la configuration de votre organisation",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Actions",
|
"rulesActions": "Actions",
|
||||||
"rulesActionAlwaysAllow": "Toujours autoriser : Contourner toutes les méthodes d'authentification",
|
"rulesActionAlwaysAllow": "Toujours autoriser : Contourner toutes les méthodes d'authentification",
|
||||||
"rulesActionAlwaysDeny": "Toujours refuser : Bloquer toutes les requêtes ; aucune authentification ne peut être tentée",
|
"rulesActionAlwaysDeny": "Toujours refuser : Bloquer toutes les requêtes ; aucune authentification ne peut être tentée",
|
||||||
|
"rulesActionPassToAuth": "Passer à l'authentification : Autoriser les méthodes d'authentification à être tentées",
|
||||||
"rulesMatchCriteria": "Critères de correspondance",
|
"rulesMatchCriteria": "Critères de correspondance",
|
||||||
"rulesMatchCriteriaIpAddress": "Correspondre à une adresse IP spécifique",
|
"rulesMatchCriteriaIpAddress": "Correspondre à une adresse IP spécifique",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Correspondre à une plage d'adresses IP en notation CIDR",
|
"rulesMatchCriteriaIpAddressRange": "Correspondre à une plage d'adresses IP en notation CIDR",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Mettre à jour le client",
|
"actionUpdateClient": "Mettre à jour le client",
|
||||||
"actionListClients": "Liste des clients",
|
"actionListClients": "Liste des clients",
|
||||||
"actionGetClient": "Obtenir le client",
|
"actionGetClient": "Obtenir le client",
|
||||||
|
"actionCreateSiteResource": "Créer une ressource de site",
|
||||||
|
"actionDeleteSiteResource": "Supprimer une ressource de site",
|
||||||
|
"actionGetSiteResource": "Obtenir une ressource de site",
|
||||||
|
"actionListSiteResources": "Lister les ressources de site",
|
||||||
|
"actionUpdateSiteResource": "Mettre à jour une ressource de site",
|
||||||
"noneSelected": "Aucune sélection",
|
"noneSelected": "Aucune sélection",
|
||||||
"orgNotFound2": "Aucune organisation trouvée.",
|
"orgNotFound2": "Aucune organisation trouvée.",
|
||||||
"searchProgress": "Rechercher...",
|
"searchProgress": "Rechercher...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "Impostazioni {resourceName}",
|
"resourceSetting": "Impostazioni {resourceName}",
|
||||||
"alwaysAllow": "Consenti Sempre",
|
"alwaysAllow": "Consenti Sempre",
|
||||||
"alwaysDeny": "Nega Sempre",
|
"alwaysDeny": "Nega Sempre",
|
||||||
|
"passToAuth": "Passa all'autenticazione",
|
||||||
"orgSettingsDescription": "Configura le impostazioni generali della tua organizzazione",
|
"orgSettingsDescription": "Configura le impostazioni generali della tua organizzazione",
|
||||||
"orgGeneralSettings": "Impostazioni Organizzazione",
|
"orgGeneralSettings": "Impostazioni Organizzazione",
|
||||||
"orgGeneralSettingsDescription": "Gestisci i dettagli dell'organizzazione e la configurazione",
|
"orgGeneralSettingsDescription": "Gestisci i dettagli dell'organizzazione e la configurazione",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Azioni",
|
"rulesActions": "Azioni",
|
||||||
"rulesActionAlwaysAllow": "Consenti Sempre: Ignora tutti i metodi di autenticazione",
|
"rulesActionAlwaysAllow": "Consenti Sempre: Ignora tutti i metodi di autenticazione",
|
||||||
"rulesActionAlwaysDeny": "Nega Sempre: Blocca tutte le richieste; nessuna autenticazione può essere tentata",
|
"rulesActionAlwaysDeny": "Nega Sempre: Blocca tutte le richieste; nessuna autenticazione può essere tentata",
|
||||||
|
"rulesActionPassToAuth": "Passa all'autenticazione: Consenti di tentare i metodi di autenticazione",
|
||||||
"rulesMatchCriteria": "Criteri di Corrispondenza",
|
"rulesMatchCriteria": "Criteri di Corrispondenza",
|
||||||
"rulesMatchCriteriaIpAddress": "Corrisponde a un indirizzo IP specifico",
|
"rulesMatchCriteriaIpAddress": "Corrisponde a un indirizzo IP specifico",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Corrisponde a un intervallo di indirizzi IP in notazione CIDR",
|
"rulesMatchCriteriaIpAddressRange": "Corrisponde a un intervallo di indirizzi IP in notazione CIDR",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Aggiorna Client",
|
"actionUpdateClient": "Aggiorna Client",
|
||||||
"actionListClients": "Elenco Clienti",
|
"actionListClients": "Elenco Clienti",
|
||||||
"actionGetClient": "Ottieni Client",
|
"actionGetClient": "Ottieni Client",
|
||||||
|
"actionCreateSiteResource": "Crea Risorsa del Sito",
|
||||||
|
"actionDeleteSiteResource": "Elimina Risorsa del Sito",
|
||||||
|
"actionGetSiteResource": "Ottieni Risorsa del Sito",
|
||||||
|
"actionListSiteResources": "Elenca Risorse del Sito",
|
||||||
|
"actionUpdateSiteResource": "Aggiorna Risorsa del Sito",
|
||||||
"noneSelected": "Nessuna selezione",
|
"noneSelected": "Nessuna selezione",
|
||||||
"orgNotFound2": "Nessuna organizzazione trovata.",
|
"orgNotFound2": "Nessuna organizzazione trovata.",
|
||||||
"searchProgress": "Ricerca...",
|
"searchProgress": "Ricerca...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "{resourceName} 설정",
|
"resourceSetting": "{resourceName} 설정",
|
||||||
"alwaysAllow": "항상 허용",
|
"alwaysAllow": "항상 허용",
|
||||||
"alwaysDeny": "항상 거부",
|
"alwaysDeny": "항상 거부",
|
||||||
|
"passToAuth": "인증으로 전달",
|
||||||
"orgSettingsDescription": "조직의 일반 설정을 구성하세요",
|
"orgSettingsDescription": "조직의 일반 설정을 구성하세요",
|
||||||
"orgGeneralSettings": "조직 설정",
|
"orgGeneralSettings": "조직 설정",
|
||||||
"orgGeneralSettingsDescription": "조직 세부정보 및 구성을 관리하세요.",
|
"orgGeneralSettingsDescription": "조직 세부정보 및 구성을 관리하세요.",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "작업",
|
"rulesActions": "작업",
|
||||||
"rulesActionAlwaysAllow": "항상 허용: 모든 인증 방법 우회",
|
"rulesActionAlwaysAllow": "항상 허용: 모든 인증 방법 우회",
|
||||||
"rulesActionAlwaysDeny": "항상 거부: 모든 요청을 차단합니다. 인증을 시도할 수 없습니다.",
|
"rulesActionAlwaysDeny": "항상 거부: 모든 요청을 차단합니다. 인증을 시도할 수 없습니다.",
|
||||||
|
"rulesActionPassToAuth": "인증으로 전달: 인증 방법 시도를 허용합니다",
|
||||||
"rulesMatchCriteria": "일치 기준",
|
"rulesMatchCriteria": "일치 기준",
|
||||||
"rulesMatchCriteriaIpAddress": "특정 IP 주소와 일치",
|
"rulesMatchCriteriaIpAddress": "특정 IP 주소와 일치",
|
||||||
"rulesMatchCriteriaIpAddressRange": "CIDR 표기법으로 IP 주소 범위를 일치시킵니다",
|
"rulesMatchCriteriaIpAddressRange": "CIDR 표기법으로 IP 주소 범위를 일치시킵니다",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "클라이언트 업데이트",
|
"actionUpdateClient": "클라이언트 업데이트",
|
||||||
"actionListClients": "클라이언트 목록",
|
"actionListClients": "클라이언트 목록",
|
||||||
"actionGetClient": "클라이언트 가져오기",
|
"actionGetClient": "클라이언트 가져오기",
|
||||||
|
"actionCreateSiteResource": "사이트 리소스 생성",
|
||||||
|
"actionDeleteSiteResource": "사이트 리소스 삭제",
|
||||||
|
"actionGetSiteResource": "사이트 리소스 가져오기",
|
||||||
|
"actionListSiteResources": "사이트 리소스 목록",
|
||||||
|
"actionUpdateSiteResource": "사이트 리소스 업데이트",
|
||||||
"noneSelected": "선택된 항목 없음",
|
"noneSelected": "선택된 항목 없음",
|
||||||
"orgNotFound2": "조직이 없습니다.",
|
"orgNotFound2": "조직이 없습니다.",
|
||||||
"searchProgress": "검색...",
|
"searchProgress": "검색...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "{resourceName} Innstillinger",
|
"resourceSetting": "{resourceName} Innstillinger",
|
||||||
"alwaysAllow": "Alltid tillat",
|
"alwaysAllow": "Alltid tillat",
|
||||||
"alwaysDeny": "Alltid avslå",
|
"alwaysDeny": "Alltid avslå",
|
||||||
|
"passToAuth": "Pass til Autentisering",
|
||||||
"orgSettingsDescription": "Konfigurer organisasjonens generelle innstillinger",
|
"orgSettingsDescription": "Konfigurer organisasjonens generelle innstillinger",
|
||||||
"orgGeneralSettings": "Organisasjonsinnstillinger",
|
"orgGeneralSettings": "Organisasjonsinnstillinger",
|
||||||
"orgGeneralSettingsDescription": "Administrer dine organisasjonsdetaljer og konfigurasjon",
|
"orgGeneralSettingsDescription": "Administrer dine organisasjonsdetaljer og konfigurasjon",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Handlinger",
|
"rulesActions": "Handlinger",
|
||||||
"rulesActionAlwaysAllow": "Alltid Tillat: Omgå alle autentiserings metoder",
|
"rulesActionAlwaysAllow": "Alltid Tillat: Omgå alle autentiserings metoder",
|
||||||
"rulesActionAlwaysDeny": "Alltid Nekt: Blokker alle forespørsler; ingen autentisering kan forsøkes",
|
"rulesActionAlwaysDeny": "Alltid Nekt: Blokker alle forespørsler; ingen autentisering kan forsøkes",
|
||||||
|
"rulesActionPassToAuth": "Pass til Autentisering: Tillat at autentiseringsmetoder forsøkes",
|
||||||
"rulesMatchCriteria": "Samsvarende kriterier",
|
"rulesMatchCriteria": "Samsvarende kriterier",
|
||||||
"rulesMatchCriteriaIpAddress": "Samsvar med en spesifikk IP-adresse",
|
"rulesMatchCriteriaIpAddress": "Samsvar med en spesifikk IP-adresse",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Samsvar et IP-adresseområde i CIDR-notasjon",
|
"rulesMatchCriteriaIpAddressRange": "Samsvar et IP-adresseområde i CIDR-notasjon",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Oppdater klient",
|
"actionUpdateClient": "Oppdater klient",
|
||||||
"actionListClients": "List klienter",
|
"actionListClients": "List klienter",
|
||||||
"actionGetClient": "Hent klient",
|
"actionGetClient": "Hent klient",
|
||||||
|
"actionCreateSiteResource": "Opprett stedsressurs",
|
||||||
|
"actionDeleteSiteResource": "Slett Stedsressurs",
|
||||||
|
"actionGetSiteResource": "Hent Stedsressurs",
|
||||||
|
"actionListSiteResources": "List opp Stedsressurser",
|
||||||
|
"actionUpdateSiteResource": "Oppdater Stedsressurs",
|
||||||
"noneSelected": "Ingen valgt",
|
"noneSelected": "Ingen valgt",
|
||||||
"orgNotFound2": "Ingen organisasjoner funnet.",
|
"orgNotFound2": "Ingen organisasjoner funnet.",
|
||||||
"searchProgress": "Søker...",
|
"searchProgress": "Søker...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "{resourceName} instellingen",
|
"resourceSetting": "{resourceName} instellingen",
|
||||||
"alwaysAllow": "Altijd toestaan",
|
"alwaysAllow": "Altijd toestaan",
|
||||||
"alwaysDeny": "Altijd weigeren",
|
"alwaysDeny": "Altijd weigeren",
|
||||||
|
"passToAuth": "Passeren naar Auth",
|
||||||
"orgSettingsDescription": "Configureer de algemene instellingen van je organisatie",
|
"orgSettingsDescription": "Configureer de algemene instellingen van je organisatie",
|
||||||
"orgGeneralSettings": "Organisatie Instellingen",
|
"orgGeneralSettings": "Organisatie Instellingen",
|
||||||
"orgGeneralSettingsDescription": "Beheer de details en configuratie van uw organisatie",
|
"orgGeneralSettingsDescription": "Beheer de details en configuratie van uw organisatie",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "acties",
|
"rulesActions": "acties",
|
||||||
"rulesActionAlwaysAllow": "Altijd toegestaan: Omzeil alle authenticatiemethoden",
|
"rulesActionAlwaysAllow": "Altijd toegestaan: Omzeil alle authenticatiemethoden",
|
||||||
"rulesActionAlwaysDeny": "Altijd weigeren: Blokkeer alle aanvragen, er kan geen verificatie worden geprobeerd",
|
"rulesActionAlwaysDeny": "Altijd weigeren: Blokkeer alle aanvragen, er kan geen verificatie worden geprobeerd",
|
||||||
|
"rulesActionPassToAuth": "Doorgeven aan Auth: Toestaan dat authenticatiemethoden worden geprobeerd",
|
||||||
"rulesMatchCriteria": "Overeenkomende criteria",
|
"rulesMatchCriteria": "Overeenkomende criteria",
|
||||||
"rulesMatchCriteriaIpAddress": "Overeenkomen met een specifiek IP-adres",
|
"rulesMatchCriteriaIpAddress": "Overeenkomen met een specifiek IP-adres",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Overeenkomen met een bereik van IP-adressen in de CIDR-notatie",
|
"rulesMatchCriteriaIpAddressRange": "Overeenkomen met een bereik van IP-adressen in de CIDR-notatie",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Klant bijwerken",
|
"actionUpdateClient": "Klant bijwerken",
|
||||||
"actionListClients": "Lijst klanten",
|
"actionListClients": "Lijst klanten",
|
||||||
"actionGetClient": "Client ophalen",
|
"actionGetClient": "Client ophalen",
|
||||||
|
"actionCreateSiteResource": "Sitebron maken",
|
||||||
|
"actionDeleteSiteResource": "Document verwijderen van site",
|
||||||
|
"actionGetSiteResource": "Bron van site ophalen",
|
||||||
|
"actionListSiteResources": "Bronnen van site weergeven",
|
||||||
|
"actionUpdateSiteResource": "Document bijwerken van site",
|
||||||
"noneSelected": "Niet geselecteerd",
|
"noneSelected": "Niet geselecteerd",
|
||||||
"orgNotFound2": "Geen organisaties gevonden.",
|
"orgNotFound2": "Geen organisaties gevonden.",
|
||||||
"searchProgress": "Zoeken...",
|
"searchProgress": "Zoeken...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "Ustawienia {resourceName}",
|
"resourceSetting": "Ustawienia {resourceName}",
|
||||||
"alwaysAllow": "Zawsze zezwalaj",
|
"alwaysAllow": "Zawsze zezwalaj",
|
||||||
"alwaysDeny": "Zawsze odmawiaj",
|
"alwaysDeny": "Zawsze odmawiaj",
|
||||||
|
"passToAuth": "Przekaż do Autoryzacji",
|
||||||
"orgSettingsDescription": "Skonfiguruj ustawienia ogólne swojej organizacji",
|
"orgSettingsDescription": "Skonfiguruj ustawienia ogólne swojej organizacji",
|
||||||
"orgGeneralSettings": "Ustawienia organizacji",
|
"orgGeneralSettings": "Ustawienia organizacji",
|
||||||
"orgGeneralSettingsDescription": "Zarządzaj szczegółami swojej organizacji i konfiguracją",
|
"orgGeneralSettingsDescription": "Zarządzaj szczegółami swojej organizacji i konfiguracją",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Akcje",
|
"rulesActions": "Akcje",
|
||||||
"rulesActionAlwaysAllow": "Zawsze zezwalaj: Pomiń wszystkie metody uwierzytelniania",
|
"rulesActionAlwaysAllow": "Zawsze zezwalaj: Pomiń wszystkie metody uwierzytelniania",
|
||||||
"rulesActionAlwaysDeny": "Zawsze odmawiaj: Blokuj wszystkie żądania; nie można próbować uwierzytelniania",
|
"rulesActionAlwaysDeny": "Zawsze odmawiaj: Blokuj wszystkie żądania; nie można próbować uwierzytelniania",
|
||||||
|
"rulesActionPassToAuth": "Przekaż do Autoryzacji: Zezwól na próby metod uwierzytelniania",
|
||||||
"rulesMatchCriteria": "Kryteria dopasowania",
|
"rulesMatchCriteria": "Kryteria dopasowania",
|
||||||
"rulesMatchCriteriaIpAddress": "Dopasuj konkretny adres IP",
|
"rulesMatchCriteriaIpAddress": "Dopasuj konkretny adres IP",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Dopasuj zakres adresów IP w notacji CIDR",
|
"rulesMatchCriteriaIpAddressRange": "Dopasuj zakres adresów IP w notacji CIDR",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Aktualizuj klienta",
|
"actionUpdateClient": "Aktualizuj klienta",
|
||||||
"actionListClients": "Lista klientów",
|
"actionListClients": "Lista klientów",
|
||||||
"actionGetClient": "Pobierz klienta",
|
"actionGetClient": "Pobierz klienta",
|
||||||
|
"actionCreateSiteResource": "Utwórz zasób witryny",
|
||||||
|
"actionDeleteSiteResource": "Usuń zasób strony",
|
||||||
|
"actionGetSiteResource": "Pobierz zasób strony",
|
||||||
|
"actionListSiteResources": "Lista zasobów strony",
|
||||||
|
"actionUpdateSiteResource": "Aktualizuj zasób strony",
|
||||||
"noneSelected": "Nie wybrano",
|
"noneSelected": "Nie wybrano",
|
||||||
"orgNotFound2": "Nie znaleziono organizacji.",
|
"orgNotFound2": "Nie znaleziono organizacji.",
|
||||||
"searchProgress": "Szukaj...",
|
"searchProgress": "Szukaj...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "Configurações do {resourceName}",
|
"resourceSetting": "Configurações do {resourceName}",
|
||||||
"alwaysAllow": "Sempre permitir",
|
"alwaysAllow": "Sempre permitir",
|
||||||
"alwaysDeny": "Sempre negar",
|
"alwaysDeny": "Sempre negar",
|
||||||
|
"passToAuth": "Passar para Autenticação",
|
||||||
"orgSettingsDescription": "Configurar as configurações gerais da sua organização",
|
"orgSettingsDescription": "Configurar as configurações gerais da sua organização",
|
||||||
"orgGeneralSettings": "Configurações da organização",
|
"orgGeneralSettings": "Configurações da organização",
|
||||||
"orgGeneralSettingsDescription": "Gerencie os detalhes e a configuração da sua organização",
|
"orgGeneralSettingsDescription": "Gerencie os detalhes e a configuração da sua organização",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Ações",
|
"rulesActions": "Ações",
|
||||||
"rulesActionAlwaysAllow": "Sempre Permitir: Ignorar todos os métodos de autenticação",
|
"rulesActionAlwaysAllow": "Sempre Permitir: Ignorar todos os métodos de autenticação",
|
||||||
"rulesActionAlwaysDeny": "Sempre Negar: Bloquear todas as requisições; nenhuma autenticação pode ser tentada",
|
"rulesActionAlwaysDeny": "Sempre Negar: Bloquear todas as requisições; nenhuma autenticação pode ser tentada",
|
||||||
|
"rulesActionPassToAuth": "Passar para Autenticação: Permitir que métodos de autenticação sejam tentados",
|
||||||
"rulesMatchCriteria": "Critérios de Correspondência",
|
"rulesMatchCriteria": "Critérios de Correspondência",
|
||||||
"rulesMatchCriteriaIpAddress": "Corresponder a um endereço IP específico",
|
"rulesMatchCriteriaIpAddress": "Corresponder a um endereço IP específico",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Corresponder a uma faixa de endereços IP em notação CIDR",
|
"rulesMatchCriteriaIpAddressRange": "Corresponder a uma faixa de endereços IP em notação CIDR",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Atualizar Cliente",
|
"actionUpdateClient": "Atualizar Cliente",
|
||||||
"actionListClients": "Listar Clientes",
|
"actionListClients": "Listar Clientes",
|
||||||
"actionGetClient": "Obter Cliente",
|
"actionGetClient": "Obter Cliente",
|
||||||
|
"actionCreateSiteResource": "Criar Recurso do Site",
|
||||||
|
"actionDeleteSiteResource": "Eliminar Recurso do Site",
|
||||||
|
"actionGetSiteResource": "Obter Recurso do Site",
|
||||||
|
"actionListSiteResources": "Listar Recursos do Site",
|
||||||
|
"actionUpdateSiteResource": "Atualizar Recurso do Site",
|
||||||
"noneSelected": "Nenhum selecionado",
|
"noneSelected": "Nenhum selecionado",
|
||||||
"orgNotFound2": "Nenhuma organização encontrada.",
|
"orgNotFound2": "Nenhuma organização encontrada.",
|
||||||
"searchProgress": "Pesquisar...",
|
"searchProgress": "Pesquisar...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "Настройки {resourceName}",
|
"resourceSetting": "Настройки {resourceName}",
|
||||||
"alwaysAllow": "Всегда разрешать",
|
"alwaysAllow": "Всегда разрешать",
|
||||||
"alwaysDeny": "Всегда запрещать",
|
"alwaysDeny": "Всегда запрещать",
|
||||||
|
"passToAuth": "Переход к аутентификации",
|
||||||
"orgSettingsDescription": "Настройте общие параметры вашей организации",
|
"orgSettingsDescription": "Настройте общие параметры вашей организации",
|
||||||
"orgGeneralSettings": "Настройки организации",
|
"orgGeneralSettings": "Настройки организации",
|
||||||
"orgGeneralSettingsDescription": "Управляйте данными и конфигурацией вашей организации",
|
"orgGeneralSettingsDescription": "Управляйте данными и конфигурацией вашей организации",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Действия",
|
"rulesActions": "Действия",
|
||||||
"rulesActionAlwaysAllow": "Всегда разрешать: Обойти все методы аутентификации",
|
"rulesActionAlwaysAllow": "Всегда разрешать: Обойти все методы аутентификации",
|
||||||
"rulesActionAlwaysDeny": "Всегда запрещать: Блокировать все запросы; аутентификация не может быть выполнена",
|
"rulesActionAlwaysDeny": "Всегда запрещать: Блокировать все запросы; аутентификация не может быть выполнена",
|
||||||
|
"rulesActionPassToAuth": "Переход к аутентификации: Разрешить попытки методов аутентификации",
|
||||||
"rulesMatchCriteria": "Критерии совпадения",
|
"rulesMatchCriteria": "Критерии совпадения",
|
||||||
"rulesMatchCriteriaIpAddress": "Совпадение с конкретным IP адресом",
|
"rulesMatchCriteriaIpAddress": "Совпадение с конкретным IP адресом",
|
||||||
"rulesMatchCriteriaIpAddressRange": "Совпадение с диапазоном IP адресов в нотации CIDR",
|
"rulesMatchCriteriaIpAddressRange": "Совпадение с диапазоном IP адресов в нотации CIDR",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Обновить Клиента",
|
"actionUpdateClient": "Обновить Клиента",
|
||||||
"actionListClients": "Список Клиентов",
|
"actionListClients": "Список Клиентов",
|
||||||
"actionGetClient": "Получить Клиента",
|
"actionGetClient": "Получить Клиента",
|
||||||
|
"actionCreateSiteResource": "Создать ресурс сайта",
|
||||||
|
"actionDeleteSiteResource": "Удалить ресурс сайта ",
|
||||||
|
"actionGetSiteResource": "Получить ресурс сайта",
|
||||||
|
"actionListSiteResources": "Список ресурсов сайта",
|
||||||
|
"actionUpdateSiteResource": "Обновить ресурс сайта",
|
||||||
"noneSelected": "Ничего не выбрано",
|
"noneSelected": "Ничего не выбрано",
|
||||||
"orgNotFound2": "Организации не найдены.",
|
"orgNotFound2": "Организации не найдены.",
|
||||||
"searchProgress": "Поиск...",
|
"searchProgress": "Поиск...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "{resourceName} Ayarları",
|
"resourceSetting": "{resourceName} Ayarları",
|
||||||
"alwaysAllow": "Her Zaman İzin Ver",
|
"alwaysAllow": "Her Zaman İzin Ver",
|
||||||
"alwaysDeny": "Her Zaman Reddet",
|
"alwaysDeny": "Her Zaman Reddet",
|
||||||
|
"passToAuth": "Kimlik Doğrulamasına Geç",
|
||||||
"orgSettingsDescription": "Organizasyonunuzun genel ayarlarını yapılandırın",
|
"orgSettingsDescription": "Organizasyonunuzun genel ayarlarını yapılandırın",
|
||||||
"orgGeneralSettings": "Organizasyon Ayarları",
|
"orgGeneralSettings": "Organizasyon Ayarları",
|
||||||
"orgGeneralSettingsDescription": "Organizasyon detaylarınızı ve yapılandırmanızı yönetin",
|
"orgGeneralSettingsDescription": "Organizasyon detaylarınızı ve yapılandırmanızı yönetin",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "Aksiyonlar",
|
"rulesActions": "Aksiyonlar",
|
||||||
"rulesActionAlwaysAllow": "Her Zaman İzin Ver: Tüm kimlik doğrulama yöntemlerini atlayın",
|
"rulesActionAlwaysAllow": "Her Zaman İzin Ver: Tüm kimlik doğrulama yöntemlerini atlayın",
|
||||||
"rulesActionAlwaysDeny": "Her Zaman Reddedin: Tüm istekleri engelleyin; kimlik doğrulaması yapılamaz",
|
"rulesActionAlwaysDeny": "Her Zaman Reddedin: Tüm istekleri engelleyin; kimlik doğrulaması yapılamaz",
|
||||||
|
"rulesActionPassToAuth": "Kimlik Doğrulamasına Geç: Kimlik doğrulama yöntemlerinin denenmesine izin ver",
|
||||||
"rulesMatchCriteria": "Eşleşme Kriterleri",
|
"rulesMatchCriteria": "Eşleşme Kriterleri",
|
||||||
"rulesMatchCriteriaIpAddress": "Belirli bir IP adresi ile eşleşme",
|
"rulesMatchCriteriaIpAddress": "Belirli bir IP adresi ile eşleşme",
|
||||||
"rulesMatchCriteriaIpAddressRange": "CIDR gösteriminde bir IP adresi aralığı ile eşleşme",
|
"rulesMatchCriteriaIpAddressRange": "CIDR gösteriminde bir IP adresi aralığı ile eşleşme",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "Müşteri Güncelle",
|
"actionUpdateClient": "Müşteri Güncelle",
|
||||||
"actionListClients": "Müşterileri Listele",
|
"actionListClients": "Müşterileri Listele",
|
||||||
"actionGetClient": "Müşteriyi Al",
|
"actionGetClient": "Müşteriyi Al",
|
||||||
|
"actionCreateSiteResource": "Site Kaynağı Oluştur",
|
||||||
|
"actionDeleteSiteResource": "Site Kaynağını Sil",
|
||||||
|
"actionGetSiteResource": "Site Kaynağını Al",
|
||||||
|
"actionListSiteResources": "Site Kaynaklarını Listele",
|
||||||
|
"actionUpdateSiteResource": "Site Kaynağını Güncelle",
|
||||||
"noneSelected": "Hiçbiri seçili değil",
|
"noneSelected": "Hiçbiri seçili değil",
|
||||||
"orgNotFound2": "Hiçbir organizasyon bulunamadı.",
|
"orgNotFound2": "Hiçbir organizasyon bulunamadı.",
|
||||||
"searchProgress": "Ara...",
|
"searchProgress": "Ara...",
|
||||||
|
|||||||
@@ -205,6 +205,7 @@
|
|||||||
"resourceSetting": "{resourceName} 设置",
|
"resourceSetting": "{resourceName} 设置",
|
||||||
"alwaysAllow": "一律允许",
|
"alwaysAllow": "一律允许",
|
||||||
"alwaysDeny": "一律拒绝",
|
"alwaysDeny": "一律拒绝",
|
||||||
|
"passToAuth": "传递至认证",
|
||||||
"orgSettingsDescription": "配置您组织的一般设置",
|
"orgSettingsDescription": "配置您组织的一般设置",
|
||||||
"orgGeneralSettings": "组织设置",
|
"orgGeneralSettings": "组织设置",
|
||||||
"orgGeneralSettingsDescription": "管理您的机构详细信息和配置",
|
"orgGeneralSettingsDescription": "管理您的机构详细信息和配置",
|
||||||
@@ -545,6 +546,7 @@
|
|||||||
"rulesActions": "行动",
|
"rulesActions": "行动",
|
||||||
"rulesActionAlwaysAllow": "总是允许:绕过所有身份验证方法",
|
"rulesActionAlwaysAllow": "总是允许:绕过所有身份验证方法",
|
||||||
"rulesActionAlwaysDeny": "总是拒绝:阻止所有请求;无法尝试验证",
|
"rulesActionAlwaysDeny": "总是拒绝:阻止所有请求;无法尝试验证",
|
||||||
|
"rulesActionPassToAuth": "传递至认证:允许尝试身份验证方法",
|
||||||
"rulesMatchCriteria": "匹配条件",
|
"rulesMatchCriteria": "匹配条件",
|
||||||
"rulesMatchCriteriaIpAddress": "匹配一个指定的 IP 地址",
|
"rulesMatchCriteriaIpAddress": "匹配一个指定的 IP 地址",
|
||||||
"rulesMatchCriteriaIpAddressRange": "在 CIDR 符号中匹配一系列IP地址",
|
"rulesMatchCriteriaIpAddressRange": "在 CIDR 符号中匹配一系列IP地址",
|
||||||
@@ -1052,6 +1054,11 @@
|
|||||||
"actionUpdateClient": "更新客户端",
|
"actionUpdateClient": "更新客户端",
|
||||||
"actionListClients": "列出客户端",
|
"actionListClients": "列出客户端",
|
||||||
"actionGetClient": "获取客户端",
|
"actionGetClient": "获取客户端",
|
||||||
|
"actionCreateSiteResource": "创建站点资源",
|
||||||
|
"actionDeleteSiteResource": "删除站点资源",
|
||||||
|
"actionGetSiteResource": "获取站点资源",
|
||||||
|
"actionListSiteResources": "列出站点资源",
|
||||||
|
"actionUpdateSiteResource": "更新站点资源",
|
||||||
"noneSelected": "未选择",
|
"noneSelected": "未选择",
|
||||||
"orgNotFound2": "未找到组织。",
|
"orgNotFound2": "未找到组织。",
|
||||||
"searchProgress": "搜索中...",
|
"searchProgress": "搜索中...",
|
||||||
|
|||||||
24
package-lock.json
generated
24
package-lock.json
generated
@@ -112,8 +112,8 @@
|
|||||||
"@types/node": "^24",
|
"@types/node": "^24",
|
||||||
"@types/nodemailer": "6.4.17",
|
"@types/nodemailer": "6.4.17",
|
||||||
"@types/pg": "8.15.5",
|
"@types/pg": "8.15.5",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"@types/react-dom": "19.1.7",
|
"@types/react-dom": "19.1.8",
|
||||||
"@types/semver": "^7.7.0",
|
"@types/semver": "^7.7.0",
|
||||||
"@types/swagger-ui-express": "^4.1.8",
|
"@types/swagger-ui-express": "^4.1.8",
|
||||||
"@types/ws": "8.18.1",
|
"@types/ws": "8.18.1",
|
||||||
@@ -125,7 +125,7 @@
|
|||||||
"react-email": "4.2.8",
|
"react-email": "4.2.8",
|
||||||
"tailwindcss": "^4.1.4",
|
"tailwindcss": "^4.1.4",
|
||||||
"tsc-alias": "1.8.16",
|
"tsc-alias": "1.8.16",
|
||||||
"tsx": "4.20.4",
|
"tsx": "4.20.5",
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
"typescript-eslint": "^8.40.0"
|
"typescript-eslint": "^8.40.0"
|
||||||
}
|
}
|
||||||
@@ -5026,9 +5026,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "19.1.10",
|
"version": "19.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.11.tgz",
|
||||||
"integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==",
|
"integrity": "sha512-lr3jdBw/BGj49Eps7EvqlUaoeA0xpj3pc0RoJkHpYaCHkVK7i28dKyImLQb3JVlqs3aYSXf7qYuWOW/fgZnTXQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -5036,9 +5036,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/react-dom": {
|
"node_modules/@types/react-dom": {
|
||||||
"version": "19.1.7",
|
"version": "19.1.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.8.tgz",
|
||||||
"integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==",
|
"integrity": "sha512-xG7xaBMJCpcK0RpN8jDbAACQo54ycO6h4dSSmgv8+fu6ZIAdANkx/WsawASUjVXYfy+J9AbUpRMNNEsXCDfDBQ==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -16268,9 +16268,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsx": {
|
"node_modules/tsx": {
|
||||||
"version": "4.20.4",
|
"version": "4.20.5",
|
||||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.4.tgz",
|
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.5.tgz",
|
||||||
"integrity": "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==",
|
"integrity": "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -129,8 +129,8 @@
|
|||||||
"@types/node": "^24",
|
"@types/node": "^24",
|
||||||
"@types/nodemailer": "6.4.17",
|
"@types/nodemailer": "6.4.17",
|
||||||
"@types/pg": "8.15.5",
|
"@types/pg": "8.15.5",
|
||||||
"@types/react": "19.1.10",
|
"@types/react": "19.1.11",
|
||||||
"@types/react-dom": "19.1.7",
|
"@types/react-dom": "19.1.8",
|
||||||
"@types/semver": "^7.7.0",
|
"@types/semver": "^7.7.0",
|
||||||
"@types/swagger-ui-express": "^4.1.8",
|
"@types/swagger-ui-express": "^4.1.8",
|
||||||
"@types/ws": "8.18.1",
|
"@types/ws": "8.18.1",
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
"react-email": "4.2.8",
|
"react-email": "4.2.8",
|
||||||
"tailwindcss": "^4.1.4",
|
"tailwindcss": "^4.1.4",
|
||||||
"tsc-alias": "1.8.16",
|
"tsc-alias": "1.8.16",
|
||||||
"tsx": "4.20.4",
|
"tsx": "4.20.5",
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
"typescript-eslint": "^8.40.0"
|
"typescript-eslint": "^8.40.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { router as wsRouter, handleWSUpgrade } from "@server/routers/ws";
|
|||||||
import { logIncomingMiddleware } from "./middlewares/logIncoming";
|
import { logIncomingMiddleware } from "./middlewares/logIncoming";
|
||||||
import { csrfProtectionMiddleware } from "./middlewares/csrfProtection";
|
import { csrfProtectionMiddleware } from "./middlewares/csrfProtection";
|
||||||
import helmet from "helmet";
|
import helmet from "helmet";
|
||||||
import rateLimit from "express-rate-limit";
|
import rateLimit, { ipKeyGenerator } from "express-rate-limit";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "./types/HttpCode";
|
import HttpCode from "./types/HttpCode";
|
||||||
import requestTimeoutMiddleware from "./middlewares/requestTimeout";
|
import requestTimeoutMiddleware from "./middlewares/requestTimeout";
|
||||||
@@ -70,7 +70,7 @@ export function createApiServer() {
|
|||||||
60 *
|
60 *
|
||||||
1000,
|
1000,
|
||||||
max: config.getRawConfig().rate_limits.global.max_requests,
|
max: config.getRawConfig().rate_limits.global.max_requests,
|
||||||
keyGenerator: (req) => `apiServerGlobal:${req.ip}:${req.path}`,
|
keyGenerator: (req) => `apiServerGlobal:${ipKeyGenerator(req.ip || "")}:${req.path}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `Rate limit exceeded. You can make ${config.getRawConfig().rate_limits.global.max_requests} requests every ${config.getRawConfig().rate_limits.global.window_minutes} minute(s).`;
|
const message = `Rate limit exceeded. You can make ${config.getRawConfig().rate_limits.global.max_requests} requests every ${config.getRawConfig().rate_limits.global.window_minutes} minute(s).`;
|
||||||
return next(
|
return next(
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ export const resourceRules = pgTable("resourceRules", {
|
|||||||
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
.references(() => resources.resourceId, { onDelete: "cascade" }),
|
||||||
enabled: boolean("enabled").notNull().default(true),
|
enabled: boolean("enabled").notNull().default(true),
|
||||||
priority: integer("priority").notNull(),
|
priority: integer("priority").notNull(),
|
||||||
action: varchar("action").notNull(), // ACCEPT, DROP
|
action: varchar("action").notNull(), // ACCEPT, DROP, PASS
|
||||||
match: varchar("match").notNull(), // CIDR, PATH, IP
|
match: varchar("match").notNull(), // CIDR, PATH, IP
|
||||||
value: varchar("value").notNull()
|
value: varchar("value").notNull()
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -570,7 +570,7 @@ export const resourceRules = sqliteTable("resourceRules", {
|
|||||||
.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
|
action: text("action").notNull(), // ACCEPT, DROP, PASS
|
||||||
match: text("match").notNull(), // CIDR, PATH, IP
|
match: text("match").notNull(), // CIDR, PATH, IP
|
||||||
value: text("value").notNull()
|
value: text("value").notNull()
|
||||||
});
|
});
|
||||||
|
|||||||
32
server/lib/geoip.ts
Normal file
32
server/lib/geoip.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import config from "./config";
|
||||||
|
import { tokenManager } from "./tokenManager";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
|
||||||
|
export async function getCountryCodeForIp(
|
||||||
|
ip: string
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/geoip/${ip}`,
|
||||||
|
await tokenManager.getAuthHeader()
|
||||||
|
);
|
||||||
|
|
||||||
|
return response.data.data.countryCode;
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError(error)) {
|
||||||
|
logger.error("Error fetching config in verify session:", {
|
||||||
|
message: error.message,
|
||||||
|
code: error.code,
|
||||||
|
status: error.response?.status,
|
||||||
|
statusText: error.response?.statusText,
|
||||||
|
url: error.config?.url,
|
||||||
|
method: error.config?.method
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.error("Error fetching config in verify session:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from "./response";
|
export * from "./response";
|
||||||
export { tokenManager, TokenManager } from "./tokenManager";
|
export { tokenManager, TokenManager } from "./tokenManager";
|
||||||
|
export * from "./geoip";
|
||||||
|
|||||||
@@ -129,7 +129,6 @@ export const configSchema = z
|
|||||||
trust_proxy: z.number().int().gte(0).optional().default(1),
|
trust_proxy: z.number().int().gte(0).optional().default(1),
|
||||||
secret: z
|
secret: z
|
||||||
.string()
|
.string()
|
||||||
.transform(getEnvOrYaml("SERVER_SECRET"))
|
|
||||||
.pipe(z.string().min(8))
|
.pipe(z.string().min(8))
|
||||||
.optional()
|
.optional()
|
||||||
}).optional().default({
|
}).optional().default({
|
||||||
@@ -324,7 +323,10 @@ export const configSchema = z
|
|||||||
if (data.managed) {
|
if (data.managed) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// If hybrid is not defined, server secret must be defined
|
// If hybrid is not defined, server secret must be defined. If its not defined already then pull it from env
|
||||||
|
if (data.server?.secret === undefined) {
|
||||||
|
data.server.secret = process.env.SERVER_SECRET;
|
||||||
|
}
|
||||||
return data.server?.secret !== undefined && data.server.secret.length > 0;
|
return data.server?.secret !== undefined && data.server.secret.length > 0;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ export * from "./verifyAccessTokenAccess";
|
|||||||
export * from "./verifyApiKeyIsRoot";
|
export * from "./verifyApiKeyIsRoot";
|
||||||
export * from "./verifyApiKeyApiKeyAccess";
|
export * from "./verifyApiKeyApiKeyAccess";
|
||||||
export * from "./verifyApiKeyClientAccess";
|
export * from "./verifyApiKeyClientAccess";
|
||||||
|
export * from "./verifyApiKeySiteResourceAccess";
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
import { Request, Response, NextFunction } from "express";
|
||||||
|
import { db } from "@server/db";
|
||||||
|
import { siteResources, apiKeyOrg } from "@server/db";
|
||||||
|
import { and, eq } from "drizzle-orm";
|
||||||
|
import createHttpError from "http-errors";
|
||||||
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
||||||
|
export async function verifyApiKeySiteResourceAccess(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const apiKey = req.apiKey;
|
||||||
|
const siteResourceId = parseInt(req.params.siteResourceId);
|
||||||
|
const siteId = parseInt(req.params.siteId);
|
||||||
|
const orgId = req.params.orgId;
|
||||||
|
|
||||||
|
if (!apiKey) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.UNAUTHORIZED, "Key not authenticated")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!siteResourceId || !siteId || !orgId) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
"Missing required parameters"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiKey.isRoot) {
|
||||||
|
// Root keys can access any resource in any org
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the site resource exists and belongs to the specified site and org
|
||||||
|
const [siteResource] = await db
|
||||||
|
.select()
|
||||||
|
.from(siteResources)
|
||||||
|
.where(and(
|
||||||
|
eq(siteResources.siteResourceId, siteResourceId),
|
||||||
|
eq(siteResources.siteId, siteId),
|
||||||
|
eq(siteResources.orgId, orgId)
|
||||||
|
))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!siteResource) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.NOT_FOUND,
|
||||||
|
"Site resource not found"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the API key has access to the organization
|
||||||
|
if (!req.apiKeyOrg) {
|
||||||
|
const apiKeyOrgRes = await db
|
||||||
|
.select()
|
||||||
|
.from(apiKeyOrg)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(apiKeyOrg.apiKeyId, apiKey.apiKeyId),
|
||||||
|
eq(apiKeyOrg.orgId, orgId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (apiKeyOrgRes.length === 0) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.FORBIDDEN,
|
||||||
|
"Key does not have access to this organization"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
req.apiKeyOrg = apiKeyOrgRes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach the siteResource to the request for use in the next middleware/route
|
||||||
|
// @ts-ignore - Extending Request type
|
||||||
|
req.siteResource = siteResource;
|
||||||
|
|
||||||
|
return next();
|
||||||
|
} catch (error) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"Error verifying site resource access"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
validateResourceSessionToken
|
validateResourceSessionToken
|
||||||
} from "@server/auth/sessions/resource";
|
} from "@server/auth/sessions/resource";
|
||||||
import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken";
|
import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken";
|
||||||
import { db } from "@server/db";
|
|
||||||
import {
|
import {
|
||||||
getResourceByDomain,
|
getResourceByDomain,
|
||||||
getUserSessionWithUser,
|
getUserSessionWithUser,
|
||||||
@@ -33,6 +32,7 @@ import createHttpError from "http-errors";
|
|||||||
import NodeCache from "node-cache";
|
import NodeCache from "node-cache";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
|
import { getCountryCodeForIp } from "@server/lib";
|
||||||
|
|
||||||
// We'll see if this speeds anything up
|
// We'll see if this speeds anything up
|
||||||
const cache = new NodeCache({
|
const cache = new NodeCache({
|
||||||
@@ -123,8 +123,8 @@ export async function verifyResourceSession(
|
|||||||
let cleanHost = host;
|
let cleanHost = host;
|
||||||
// if the host ends with :port, strip it
|
// if the host ends with :port, strip it
|
||||||
if (cleanHost.match(/:[0-9]{1,5}$/)) {
|
if (cleanHost.match(/:[0-9]{1,5}$/)) {
|
||||||
const matched = ''+cleanHost.match(/:[0-9]{1,5}$/);
|
const matched = "" + cleanHost.match(/:[0-9]{1,5}$/);
|
||||||
cleanHost = cleanHost.slice(0, -1*matched.length);
|
cleanHost = cleanHost.slice(0, -1 * matched.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
const resourceCacheKey = `resource:${cleanHost}`;
|
const resourceCacheKey = `resource:${cleanHost}`;
|
||||||
@@ -176,6 +176,11 @@ export async function verifyResourceSession(
|
|||||||
} else if (action == "DROP") {
|
} else if (action == "DROP") {
|
||||||
logger.debug("Resource denied by rule");
|
logger.debug("Resource denied by rule");
|
||||||
return notAllowed(res);
|
return notAllowed(res);
|
||||||
|
} else if (action == "PASS") {
|
||||||
|
logger.debug(
|
||||||
|
"Resource passed by rule, continuing to auth checks"
|
||||||
|
);
|
||||||
|
// Continue to authentication checks below
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise its undefined and we pass
|
// otherwise its undefined and we pass
|
||||||
@@ -193,7 +198,10 @@ export async function verifyResourceSession(
|
|||||||
|
|
||||||
let endpoint: string;
|
let endpoint: string;
|
||||||
if (config.isManagedMode()) {
|
if (config.isManagedMode()) {
|
||||||
endpoint = config.getRawConfig().managed?.redirect_endpoint || config.getRawConfig().managed?.endpoint || "";
|
endpoint =
|
||||||
|
config.getRawConfig().managed?.redirect_endpoint ||
|
||||||
|
config.getRawConfig().managed?.endpoint ||
|
||||||
|
"";
|
||||||
} else {
|
} else {
|
||||||
endpoint = config.getRawConfig().app.dashboard_url!;
|
endpoint = config.getRawConfig().app.dashboard_url!;
|
||||||
}
|
}
|
||||||
@@ -576,7 +584,7 @@ async function checkRules(
|
|||||||
resourceId: number,
|
resourceId: number,
|
||||||
clientIp: string | undefined,
|
clientIp: string | undefined,
|
||||||
path: string | undefined
|
path: string | undefined
|
||||||
): Promise<"ACCEPT" | "DROP" | undefined> {
|
): Promise<"ACCEPT" | "DROP" | "PASS" | undefined> {
|
||||||
const ruleCacheKey = `rules:${resourceId}`;
|
const ruleCacheKey = `rules:${resourceId}`;
|
||||||
|
|
||||||
let rules: ResourceRule[] | undefined = cache.get(ruleCacheKey);
|
let rules: ResourceRule[] | undefined = cache.get(ruleCacheKey);
|
||||||
@@ -613,6 +621,12 @@ async function checkRules(
|
|||||||
isPathAllowed(rule.value, path)
|
isPathAllowed(rule.value, path)
|
||||||
) {
|
) {
|
||||||
return rule.action as any;
|
return rule.action as any;
|
||||||
|
} else if (
|
||||||
|
clientIp &&
|
||||||
|
rule.match == "GEOIP" &&
|
||||||
|
(await isIpInGeoIP(clientIp, rule.value))
|
||||||
|
) {
|
||||||
|
return rule.action as any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -737,3 +751,23 @@ export function isPathAllowed(pattern: string, path: string): boolean {
|
|||||||
logger.debug(`Final result: ${result}`);
|
logger.debug(`Final result: ${result}`);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function isIpInGeoIP(ip: string, countryCode: string): Promise<boolean> {
|
||||||
|
if (countryCode == "ALL") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const geoIpCacheKey = `geoip:${ip}`;
|
||||||
|
|
||||||
|
let cachedCountryCode: string | undefined = cache.get(geoIpCacheKey);
|
||||||
|
|
||||||
|
if (!cachedCountryCode) {
|
||||||
|
cachedCountryCode = await getCountryCodeForIp(ip);
|
||||||
|
// Cache for longer since IP geolocation doesn't change frequently
|
||||||
|
cache.set(geoIpCacheKey, cachedCountryCode, 300); // 5 minutes
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug(`IP ${ip} is in country: ${cachedCountryCode}`);
|
||||||
|
|
||||||
|
return cachedCountryCode?.toUpperCase() === countryCode.toUpperCase();
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import { createStore } from "@server/lib/rateLimitStore";
|
|||||||
import { ActionsEnum } from "@server/auth/actions";
|
import { ActionsEnum } from "@server/auth/actions";
|
||||||
import { createNewt, getNewtToken } from "./newt";
|
import { createNewt, getNewtToken } from "./newt";
|
||||||
import { getOlmToken } from "./olm";
|
import { getOlmToken } from "./olm";
|
||||||
import rateLimit from "express-rate-limit";
|
import rateLimit, { ipKeyGenerator } from "express-rate-limit";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import { build } from "@server/build";
|
import { build } from "@server/build";
|
||||||
|
|
||||||
@@ -815,7 +815,7 @@ authRouter.use(
|
|||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: config.getRawConfig().rate_limits.auth.window_minutes,
|
windowMs: config.getRawConfig().rate_limits.auth.window_minutes,
|
||||||
max: config.getRawConfig().rate_limits.auth.max_requests,
|
max: config.getRawConfig().rate_limits.auth.max_requests,
|
||||||
keyGenerator: (req) => `authRouterGlobal:${req.ip}:${req.path}`,
|
keyGenerator: (req) => `authRouterGlobal:${ipKeyGenerator(req.ip || "")}:${req.path}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `Rate limit exceeded. You can make ${config.getRawConfig().rate_limits.auth.max_requests} requests every ${config.getRawConfig().rate_limits.auth.window_minutes} minute(s).`;
|
const message = `Rate limit exceeded. You can make ${config.getRawConfig().rate_limits.auth.max_requests} requests every ${config.getRawConfig().rate_limits.auth.window_minutes} minute(s).`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -829,7 +829,7 @@ authRouter.put(
|
|||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => `signup:${req.ip}:${req.body.email}`,
|
keyGenerator: (req) => `signup:${ipKeyGenerator(req.ip || "")}:${req.body.email}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only sign up ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only sign up ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -843,7 +843,7 @@ authRouter.post(
|
|||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => `login:${req.body.email || req.ip}`,
|
keyGenerator: (req) => `login:${req.body.email || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only log in ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only log in ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -858,7 +858,7 @@ authRouter.post(
|
|||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 900,
|
max: 900,
|
||||||
keyGenerator: (req) => `newtGetToken:${req.body.newtId || req.ip}`,
|
keyGenerator: (req) => `newtGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request a Newt token ${900} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request a Newt token ${900} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -872,7 +872,7 @@ authRouter.post(
|
|||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 900,
|
max: 900,
|
||||||
keyGenerator: (req) => `olmGetToken:${req.body.newtId || req.ip}`,
|
keyGenerator: (req) => `olmGetToken:${req.body.newtId || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request an Olm token ${900} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request an Olm token ${900} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -888,7 +888,7 @@ authRouter.post(
|
|||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => {
|
keyGenerator: (req) => {
|
||||||
return `signup:${req.body.email || req.user?.userId || req.ip}`;
|
return `signup:${req.body.email || req.user?.userId || ipKeyGenerator(req.ip || "")}`;
|
||||||
},
|
},
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only enable 2FA ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only enable 2FA ${15} times every ${15} minutes. Please try again later.`;
|
||||||
@@ -904,7 +904,7 @@ authRouter.post(
|
|||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => {
|
keyGenerator: (req) => {
|
||||||
return `signup:${req.body.email || req.user?.userId || req.ip}`;
|
return `signup:${req.body.email || req.user?.userId || ipKeyGenerator(req.ip || "")}`;
|
||||||
},
|
},
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request a 2FA code ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request a 2FA code ${15} times every ${15} minutes. Please try again later.`;
|
||||||
@@ -920,7 +920,7 @@ authRouter.post(
|
|||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => `signup:${req.user?.userId || req.ip}`,
|
keyGenerator: (req) => `signup:${req.user?.userId || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only disable 2FA ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only disable 2FA ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -934,7 +934,7 @@ authRouter.post(
|
|||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => `signup:${req.body.email || req.ip}`,
|
keyGenerator: (req) => `signup:${req.body.email || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only sign up ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only sign up ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -952,7 +952,7 @@ authRouter.post(
|
|||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) =>
|
keyGenerator: (req) =>
|
||||||
`requestEmailVerificationCode:${req.body.email || req.ip}`,
|
`requestEmailVerificationCode:${req.body.email || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request an email verification code ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request an email verification code ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -974,7 +974,7 @@ authRouter.post(
|
|||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) =>
|
keyGenerator: (req) =>
|
||||||
`requestPasswordReset:${req.body.email || req.ip}`,
|
`requestPasswordReset:${req.body.email || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request a password reset ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request a password reset ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -989,7 +989,7 @@ authRouter.post(
|
|||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) => `resetPassword:${req.body.email || req.ip}`,
|
keyGenerator: (req) => `resetPassword:${req.body.email || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request a password reset ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request a password reset ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -1005,7 +1005,7 @@ authRouter.post(
|
|||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) =>
|
keyGenerator: (req) =>
|
||||||
`authWithPassword:${req.ip}:${req.params.resourceId || req.ip}`,
|
`authWithPassword:${ipKeyGenerator(req.ip || "")}:${req.params.resourceId || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only authenticate with password ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only authenticate with password ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -1020,7 +1020,7 @@ authRouter.post(
|
|||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) =>
|
keyGenerator: (req) =>
|
||||||
`authWithPincode:${req.ip}:${req.params.resourceId || req.ip}`,
|
`authWithPincode:${ipKeyGenerator(req.ip || "")}:${req.params.resourceId || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only authenticate with pincode ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only authenticate with pincode ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -1036,7 +1036,7 @@ authRouter.post(
|
|||||||
windowMs: 15 * 60 * 1000,
|
windowMs: 15 * 60 * 1000,
|
||||||
max: 15,
|
max: 15,
|
||||||
keyGenerator: (req) =>
|
keyGenerator: (req) =>
|
||||||
`authWithWhitelist:${req.ip}:${req.body.email}:${req.params.resourceId}`,
|
`authWithWhitelist:${ipKeyGenerator(req.ip || "")}:${req.body.email}:${req.params.resourceId}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only request an email OTP ${15} times every ${15} minutes. Please try again later.`;
|
const message = `You can only request an email OTP ${15} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -1069,7 +1069,7 @@ authRouter.post(
|
|||||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||||
max: 5, // Allow 5 security key registrations per 15 minutes
|
max: 5, // Allow 5 security key registrations per 15 minutes
|
||||||
keyGenerator: (req) =>
|
keyGenerator: (req) =>
|
||||||
`securityKeyRegister:${req.user?.userId || req.ip}`,
|
`securityKeyRegister:${req.user?.userId || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only register a security key ${5} times every ${15} minutes. Please try again later.`;
|
const message = `You can only register a security key ${5} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
@@ -1089,7 +1089,7 @@ authRouter.post(
|
|||||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||||
max: 10, // Allow 10 authentication attempts per 15 minutes per IP
|
max: 10, // Allow 10 authentication attempts per 15 minutes per IP
|
||||||
keyGenerator: (req) => {
|
keyGenerator: (req) => {
|
||||||
return `securityKeyAuth:${req.body.email || req.ip}`;
|
return `securityKeyAuth:${req.body.email || ipKeyGenerator(req.ip || "")}`;
|
||||||
},
|
},
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only attempt security key authentication ${10} times every ${15} minutes. Please try again later.`;
|
const message = `You can only attempt security key authentication ${10} times every ${15} minutes. Please try again later.`;
|
||||||
@@ -1111,7 +1111,7 @@ authRouter.delete(
|
|||||||
rateLimit({
|
rateLimit({
|
||||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||||
max: 20, // Allow 10 authentication attempts per 15 minutes per IP
|
max: 20, // Allow 10 authentication attempts per 15 minutes per IP
|
||||||
keyGenerator: (req) => `securityKeyAuth:${req.user?.userId || req.ip}`,
|
keyGenerator: (req) => `securityKeyAuth:${req.user?.userId || ipKeyGenerator(req.ip || "")}`,
|
||||||
handler: (req, res, next) => {
|
handler: (req, res, next) => {
|
||||||
const message = `You can only delete a security key ${10} times every ${15} minutes. Please try again later.`;
|
const message = `You can only delete a security key ${10} times every ${15} minutes. Please try again later.`;
|
||||||
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
return next(createHttpError(HttpCode.TOO_MANY_REQUESTS, message));
|
||||||
|
|||||||
58
server/routers/gerbil/createExitNode.ts
Normal file
58
server/routers/gerbil/createExitNode.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { db, ExitNode, exitNodes } from "@server/db";
|
||||||
|
import { getUniqueExitNodeEndpointName } from "@server/db/names";
|
||||||
|
import config from "@server/lib/config";
|
||||||
|
import { getNextAvailableSubnet } from "@server/lib/exitNodes";
|
||||||
|
import logger from "@server/logger";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
export async function createExitNode(publicKey: string, reachableAt: string | undefined) {
|
||||||
|
// Fetch exit node
|
||||||
|
const [exitNodeQuery] = await db.select().from(exitNodes).limit(1);
|
||||||
|
let exitNode: ExitNode;
|
||||||
|
if (!exitNodeQuery) {
|
||||||
|
const address = await getNextAvailableSubnet();
|
||||||
|
// TODO: eventually we will want to get the next available port so that we can multiple exit nodes
|
||||||
|
// const listenPort = await getNextAvailablePort();
|
||||||
|
const listenPort = config.getRawConfig().gerbil.start_port;
|
||||||
|
let subEndpoint = "";
|
||||||
|
if (config.getRawConfig().gerbil.use_subdomain) {
|
||||||
|
subEndpoint = await getUniqueExitNodeEndpointName();
|
||||||
|
}
|
||||||
|
|
||||||
|
const exitNodeName =
|
||||||
|
config.getRawConfig().gerbil.exit_node_name ||
|
||||||
|
`Exit Node ${publicKey.slice(0, 8)}`;
|
||||||
|
|
||||||
|
// create a new exit node
|
||||||
|
[exitNode] = await db
|
||||||
|
.insert(exitNodes)
|
||||||
|
.values({
|
||||||
|
publicKey,
|
||||||
|
endpoint: `${subEndpoint}${subEndpoint != "" ? "." : ""}${config.getRawConfig().gerbil.base_endpoint}`,
|
||||||
|
address,
|
||||||
|
listenPort,
|
||||||
|
reachableAt,
|
||||||
|
name: exitNodeName
|
||||||
|
})
|
||||||
|
.returning()
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
`Created new exit node ${exitNode.name} with address ${exitNode.address} and port ${exitNode.listenPort}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// update the existing exit node
|
||||||
|
[exitNode] = await db
|
||||||
|
.update(exitNodes)
|
||||||
|
.set({
|
||||||
|
reachableAt,
|
||||||
|
publicKey
|
||||||
|
})
|
||||||
|
.where(eq(exitNodes.publicKey, publicKey))
|
||||||
|
.returning();
|
||||||
|
|
||||||
|
logger.info(`Updated exit node`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return exitNode;
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@ import { fromError } from "zod-validation-error";
|
|||||||
import { getAllowedIps } from "../target/helpers";
|
import { getAllowedIps } from "../target/helpers";
|
||||||
import { proxyToRemote } from "@server/lib/remoteProxy";
|
import { proxyToRemote } from "@server/lib/remoteProxy";
|
||||||
import { getNextAvailableSubnet } from "@server/lib/exitNodes";
|
import { getNextAvailableSubnet } from "@server/lib/exitNodes";
|
||||||
|
import { createExitNode } from "./createExitNode";
|
||||||
|
|
||||||
// Define Zod schema for request validation
|
// Define Zod schema for request validation
|
||||||
const getConfigSchema = z.object({
|
const getConfigSchema = z.object({
|
||||||
publicKey: z.string(),
|
publicKey: z.string(),
|
||||||
@@ -53,46 +55,7 @@ export async function getConfig(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch exit node
|
const exitNode = await createExitNode(publicKey, reachableAt);
|
||||||
const exitNodeQuery = await db
|
|
||||||
.select()
|
|
||||||
.from(exitNodes)
|
|
||||||
.where(eq(exitNodes.publicKey, publicKey));
|
|
||||||
let exitNode;
|
|
||||||
if (exitNodeQuery.length === 0) {
|
|
||||||
const address = await getNextAvailableSubnet();
|
|
||||||
// TODO: eventually we will want to get the next available port so that we can multiple exit nodes
|
|
||||||
// const listenPort = await getNextAvailablePort();
|
|
||||||
const listenPort = config.getRawConfig().gerbil.start_port;
|
|
||||||
let subEndpoint = "";
|
|
||||||
if (config.getRawConfig().gerbil.use_subdomain) {
|
|
||||||
subEndpoint = await getUniqueExitNodeEndpointName();
|
|
||||||
}
|
|
||||||
|
|
||||||
const exitNodeName =
|
|
||||||
config.getRawConfig().gerbil.exit_node_name ||
|
|
||||||
`Exit Node ${publicKey.slice(0, 8)}`;
|
|
||||||
|
|
||||||
// create a new exit node
|
|
||||||
exitNode = await db
|
|
||||||
.insert(exitNodes)
|
|
||||||
.values({
|
|
||||||
publicKey,
|
|
||||||
endpoint: `${subEndpoint}${subEndpoint != "" ? "." : ""}${config.getRawConfig().gerbil.base_endpoint}`,
|
|
||||||
address,
|
|
||||||
listenPort,
|
|
||||||
reachableAt,
|
|
||||||
name: exitNodeName
|
|
||||||
})
|
|
||||||
.returning()
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
logger.info(
|
|
||||||
`Created new exit node ${exitNode[0].name} with address ${exitNode[0].address} and port ${exitNode[0].listenPort}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
exitNode = exitNodeQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!exitNode) {
|
if (!exitNode) {
|
||||||
return next(
|
return next(
|
||||||
@@ -107,13 +70,13 @@ export async function getConfig(
|
|||||||
if (config.isManagedMode()) {
|
if (config.isManagedMode()) {
|
||||||
req.body = {
|
req.body = {
|
||||||
...req.body,
|
...req.body,
|
||||||
endpoint: exitNode[0].endpoint,
|
endpoint: exitNode.endpoint,
|
||||||
listenPort: exitNode[0].listenPort
|
listenPort: exitNode.listenPort
|
||||||
};
|
};
|
||||||
return proxyToRemote(req, res, next, "hybrid/gerbil/get-config");
|
return proxyToRemote(req, res, next, "hybrid/gerbil/get-config");
|
||||||
}
|
}
|
||||||
|
|
||||||
const configResponse = await generateGerbilConfig(exitNode[0]);
|
const configResponse = await generateGerbilConfig(exitNode);
|
||||||
|
|
||||||
logger.debug("Sending config: ", configResponse);
|
logger.debug("Sending config: ", configResponse);
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import * as client from "./client";
|
|||||||
import * as accessToken from "./accessToken";
|
import * as accessToken from "./accessToken";
|
||||||
import * as apiKeys from "./apiKeys";
|
import * as apiKeys from "./apiKeys";
|
||||||
import * as idp from "./idp";
|
import * as idp from "./idp";
|
||||||
|
import * as siteResource from "./siteResource";
|
||||||
import {
|
import {
|
||||||
verifyApiKey,
|
verifyApiKey,
|
||||||
verifyApiKeyOrgAccess,
|
verifyApiKeyOrgAccess,
|
||||||
@@ -22,7 +23,8 @@ import {
|
|||||||
verifyApiKeyAccessTokenAccess,
|
verifyApiKeyAccessTokenAccess,
|
||||||
verifyApiKeyIsRoot,
|
verifyApiKeyIsRoot,
|
||||||
verifyApiKeyClientAccess,
|
verifyApiKeyClientAccess,
|
||||||
verifyClientsEnabled
|
verifyClientsEnabled,
|
||||||
|
verifyApiKeySiteResourceAccess
|
||||||
} from "@server/middlewares";
|
} from "@server/middlewares";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { Router } from "express";
|
import { Router } from "express";
|
||||||
@@ -128,6 +130,69 @@ authenticated.delete(
|
|||||||
site.deleteSite
|
site.deleteSite
|
||||||
);
|
);
|
||||||
|
|
||||||
|
authenticated.get(
|
||||||
|
"/org/:orgId/user-resources",
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
resource.getUserResources
|
||||||
|
);
|
||||||
|
// Site Resource endpoints
|
||||||
|
authenticated.put(
|
||||||
|
"/org/:orgId/site/:siteId/resource",
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
verifyApiKeySiteAccess,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.createSiteResource),
|
||||||
|
siteResource.createSiteResource
|
||||||
|
);
|
||||||
|
|
||||||
|
authenticated.get(
|
||||||
|
"/org/:orgId/site/:siteId/resources",
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
verifyApiKeySiteAccess,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.listSiteResources),
|
||||||
|
siteResource.listSiteResources
|
||||||
|
);
|
||||||
|
|
||||||
|
authenticated.get(
|
||||||
|
"/org/:orgId/site-resources",
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.listSiteResources),
|
||||||
|
siteResource.listAllSiteResourcesByOrg
|
||||||
|
);
|
||||||
|
|
||||||
|
authenticated.get(
|
||||||
|
"/org/:orgId/site/:siteId/resource/:siteResourceId",
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
verifyApiKeySiteAccess,
|
||||||
|
verifyApiKeySiteResourceAccess,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.getSiteResource),
|
||||||
|
siteResource.getSiteResource
|
||||||
|
);
|
||||||
|
|
||||||
|
authenticated.post(
|
||||||
|
"/org/:orgId/site/:siteId/resource/:siteResourceId",
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
verifyApiKeySiteAccess,
|
||||||
|
verifyApiKeySiteResourceAccess,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.updateSiteResource),
|
||||||
|
siteResource.updateSiteResource
|
||||||
|
);
|
||||||
|
|
||||||
|
authenticated.delete(
|
||||||
|
"/org/:orgId/site/:siteId/resource/:siteResourceId",
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
verifyApiKeySiteAccess,
|
||||||
|
verifyApiKeySiteResourceAccess,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.deleteSiteResource),
|
||||||
|
siteResource.deleteSiteResource
|
||||||
|
);
|
||||||
|
|
||||||
|
authenticated.put(
|
||||||
|
"/org/:orgId/resource",
|
||||||
|
verifyApiKeyOrgAccess,
|
||||||
|
verifyApiKeyHasAction(ActionsEnum.createResource),
|
||||||
|
resource.createResource
|
||||||
|
);
|
||||||
|
|
||||||
authenticated.put(
|
authenticated.put(
|
||||||
"/org/:orgId/site/:siteId/resource",
|
"/org/:orgId/site/:siteId/resource",
|
||||||
verifyApiKeyOrgAccess,
|
verifyApiKeyOrgAccess,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { OpenAPITags, registry } from "@server/openApi";
|
|||||||
|
|
||||||
const createResourceRuleSchema = z
|
const createResourceRuleSchema = z
|
||||||
.object({
|
.object({
|
||||||
action: z.enum(["ACCEPT", "DROP"]),
|
action: z.enum(["ACCEPT", "DROP", "PASS"]),
|
||||||
match: z.enum(["CIDR", "IP", "PATH"]),
|
match: z.enum(["CIDR", "IP", "PATH"]),
|
||||||
value: z.string().min(1),
|
value: z.string().min(1),
|
||||||
priority: z.number().int(),
|
priority: z.number().int(),
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const updateResourceRuleParamsSchema = z
|
|||||||
// Define Zod schema for request body validation
|
// Define Zod schema for request body validation
|
||||||
const updateResourceRuleSchema = z
|
const updateResourceRuleSchema = z
|
||||||
.object({
|
.object({
|
||||||
action: z.enum(["ACCEPT", "DROP"]).optional(),
|
action: z.enum(["ACCEPT", "DROP", "PASS"]).optional(),
|
||||||
match: z.enum(["CIDR", "IP", "PATH"]).optional(),
|
match: z.enum(["CIDR", "IP", "PATH"]).optional(),
|
||||||
value: z.string().min(1).optional(),
|
value: z.string().min(1).optional(),
|
||||||
priority: z.number().int(),
|
priority: z.number().int(),
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ export type ListRolesResponse = {
|
|||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
method: "get",
|
method: "get",
|
||||||
path: "/orgs/{orgId}/roles",
|
path: "/org/{orgId}/roles",
|
||||||
description: "List roles.",
|
description: "List roles.",
|
||||||
tags: [OpenAPITags.Org, OpenAPITags.Role],
|
tags: [OpenAPITags.Org, OpenAPITags.Role],
|
||||||
request: {
|
request: {
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ export default async function migration() {
|
|||||||
|
|
||||||
await db.execute(sql`ALTER TABLE "clientSites" ADD COLUMN "endpoint" varchar;`);
|
await db.execute(sql`ALTER TABLE "clientSites" ADD COLUMN "endpoint" varchar;`);
|
||||||
|
|
||||||
|
await db.execute(sql`ALTER TABLE "exitNodes" ADD COLUMN "online" boolean DEFAULT false NOT NULL;`);
|
||||||
|
|
||||||
|
await db.execute(sql`ALTER TABLE "exitNodes" ADD COLUMN "lastPing" integer;`);
|
||||||
|
|
||||||
|
await db.execute(sql`ALTER TABLE "exitNodes" ADD COLUMN "type" text DEFAULT 'gerbil';`);
|
||||||
|
|
||||||
await db.execute(sql`ALTER TABLE "olms" ADD COLUMN "version" text;`);
|
await db.execute(sql`ALTER TABLE "olms" ADD COLUMN "version" text;`);
|
||||||
|
|
||||||
await db.execute(sql`ALTER TABLE "orgs" ADD COLUMN "createdAt" text;`);
|
await db.execute(sql`ALTER TABLE "orgs" ADD COLUMN "createdAt" text;`);
|
||||||
|
|||||||
@@ -777,15 +777,6 @@ export default function Page() {
|
|||||||
</SettingsContainer>
|
</SettingsContainer>
|
||||||
|
|
||||||
<div className="flex justify-end space-x-2 mt-8">
|
<div className="flex justify-end space-x-2 mt-8">
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => {
|
|
||||||
router.push(`/${orgId}/settings/access/users`);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{t("cancel")}
|
|
||||||
</Button>
|
|
||||||
{userType && dataLoaded && (
|
{userType && dataLoaded && (
|
||||||
<Button
|
<Button
|
||||||
type={inviteLink ? "button" : "submit"}
|
type={inviteLink ? "button" : "submit"}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ import { useTranslations } from "next-intl";
|
|||||||
|
|
||||||
// Schema for rule validation
|
// Schema for rule validation
|
||||||
const addRuleSchema = z.object({
|
const addRuleSchema = z.object({
|
||||||
action: z.string(),
|
action: z.enum(["ACCEPT", "DROP", "PASS"]),
|
||||||
match: z.string(),
|
match: z.string(),
|
||||||
value: z.string(),
|
value: z.string(),
|
||||||
priority: z.coerce.number().int().optional()
|
priority: z.coerce.number().int().optional()
|
||||||
@@ -104,7 +104,8 @@ export default function ResourceRules(props: {
|
|||||||
|
|
||||||
const RuleAction = {
|
const RuleAction = {
|
||||||
ACCEPT: t('alwaysAllow'),
|
ACCEPT: t('alwaysAllow'),
|
||||||
DROP: t('alwaysDeny')
|
DROP: t('alwaysDeny'),
|
||||||
|
PASS: t('passToAuth')
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const RuleMatch = {
|
const RuleMatch = {
|
||||||
@@ -113,7 +114,7 @@ export default function ResourceRules(props: {
|
|||||||
CIDR: t('ipAddressRange')
|
CIDR: t('ipAddressRange')
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const addRuleForm = useForm({
|
const addRuleForm = useForm<z.infer<typeof addRuleSchema>>({
|
||||||
resolver: zodResolver(addRuleSchema),
|
resolver: zodResolver(addRuleSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
action: "ACCEPT",
|
action: "ACCEPT",
|
||||||
@@ -437,7 +438,7 @@ export default function ResourceRules(props: {
|
|||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Select
|
<Select
|
||||||
defaultValue={row.original.action}
|
defaultValue={row.original.action}
|
||||||
onValueChange={(value: "ACCEPT" | "DROP") =>
|
onValueChange={(value: "ACCEPT" | "DROP" | "PASS") =>
|
||||||
updateRule(row.original.ruleId, { action: value })
|
updateRule(row.original.ruleId, { action: value })
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -449,6 +450,7 @@ export default function ResourceRules(props: {
|
|||||||
{RuleAction.ACCEPT}
|
{RuleAction.ACCEPT}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
<SelectItem value="DROP">{RuleAction.DROP}</SelectItem>
|
<SelectItem value="DROP">{RuleAction.DROP}</SelectItem>
|
||||||
|
<SelectItem value="PASS">{RuleAction.PASS}</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
)
|
)
|
||||||
@@ -629,6 +631,9 @@ export default function ResourceRules(props: {
|
|||||||
<SelectItem value="DROP">
|
<SelectItem value="DROP">
|
||||||
{RuleAction.DROP}
|
{RuleAction.DROP}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
|
<SelectItem value="PASS">
|
||||||
|
{RuleAction.PASS}
|
||||||
|
</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|||||||
@@ -51,7 +51,12 @@ function getActionsCategories(root: boolean) {
|
|||||||
[t('actionSetResourcePassword')]: "setResourcePassword",
|
[t('actionSetResourcePassword')]: "setResourcePassword",
|
||||||
[t('actionSetResourcePincode')]: "setResourcePincode",
|
[t('actionSetResourcePincode')]: "setResourcePincode",
|
||||||
[t('actionSetResourceEmailWhitelist')]: "setResourceWhitelist",
|
[t('actionSetResourceEmailWhitelist')]: "setResourceWhitelist",
|
||||||
[t('actionGetResourceEmailWhitelist')]: "getResourceWhitelist"
|
[t('actionGetResourceEmailWhitelist')]: "getResourceWhitelist",
|
||||||
|
[t('actionCreateSiteResource')]: "createSiteResource",
|
||||||
|
[t('actionDeleteSiteResource')]: "deleteSiteResource",
|
||||||
|
[t('actionGetSiteResource')]: "getSiteResource",
|
||||||
|
[t('actionListSiteResources')]: "listSiteResources",
|
||||||
|
[t('actionUpdateSiteResource')]: "updateSiteResource"
|
||||||
},
|
},
|
||||||
|
|
||||||
Target: {
|
Target: {
|
||||||
|
|||||||
Reference in New Issue
Block a user