mirror of
https://github.com/fosrl/pangolin.git
synced 2026-02-03 08:39:09 +00:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f683ca486 | ||
|
|
0e65f8c921 | ||
|
|
dfcab90c2d | ||
|
|
5a6a035d30 | ||
|
|
d76ff17fb3 | ||
|
|
1f570e9b46 | ||
|
|
4953e69b1b | ||
|
|
ab6ecdbc9c | ||
|
|
0b7ca95d21 | ||
|
|
6cc4bc2645 | ||
|
|
74d6b3d902 | ||
|
|
302094771b | ||
|
|
80ef8f189e | ||
|
|
6204fa0ade | ||
|
|
1d105fc5be | ||
|
|
3612857585 | ||
|
|
8f1ee60119 | ||
|
|
e7ca7fe89c | ||
|
|
4be1d87602 | ||
|
|
131df8aeb7 | ||
|
|
3442942893 | ||
|
|
fbd78ab842 | ||
|
|
66f324e18c | ||
|
|
5e2f9e1eeb | ||
|
|
fefb07e14c | ||
|
|
013f342ff6 | ||
|
|
aabdcea3c0 | ||
|
|
a178faa377 | ||
|
|
edf0ce226f | ||
|
|
7118ae374d | ||
|
|
f2a14e6a36 | ||
|
|
f37be774a6 | ||
|
|
0dcfeb3587 | ||
|
|
dbfc8b51aa | ||
|
|
d72a8af04b | ||
|
|
7131dea7a0 | ||
|
|
deb30ed4ae | ||
|
|
3b09ef3345 | ||
|
|
06e90c9555 | ||
|
|
cdc415079c | ||
|
|
1c2ba4076a | ||
|
|
af68aa692c | ||
|
|
edba818615 | ||
|
|
cdf904a2bc | ||
|
|
fedab6c9a8 | ||
|
|
33e8ed4c93 | ||
|
|
2b54dfe035 |
37
.github/workflows/stale-bot.yml
vendored
Normal file
37
.github/workflows/stale-bot.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
name: Mark and Close Stale Issues
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
workflow_dispatch: # Allow manual trigger
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write # only for delete-branch option
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v9
|
||||||
|
with:
|
||||||
|
days-before-stale: 30
|
||||||
|
days-before-close: 14
|
||||||
|
stale-issue-message: 'This issue has been automatically marked as stale due to 30 days of inactivity. It will be closed in 14 days if no further activity occurs.'
|
||||||
|
close-issue-message: 'This issue has been automatically closed due to inactivity. If you believe this is still relevant, please open a new issue with up-to-date information.'
|
||||||
|
stale-issue-label: 'stale'
|
||||||
|
|
||||||
|
exempt-issue-labels: 'needs investigating, networking, new feature, reverse proxy, bug, api, authentication, documentation, enhancement, help wanted, good first issue, question'
|
||||||
|
|
||||||
|
exempt-all-issue-assignees: true
|
||||||
|
|
||||||
|
only-labels: ''
|
||||||
|
exempt-pr-labels: ''
|
||||||
|
days-before-pr-stale: -1
|
||||||
|
days-before-pr-close: -1
|
||||||
|
|
||||||
|
operations-per-run: 100
|
||||||
|
remove-stale-when-updated: true
|
||||||
|
delete-branch: false
|
||||||
|
enable-statistics: true
|
||||||
@@ -7,7 +7,7 @@ RUN npm ci
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN npx drizzle-kit generate --dialect sqlite --schema ./server/db/schema.ts --out init
|
RUN npx drizzle-kit generate --dialect sqlite --schema ./server/db/schemas/ --out init
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ FROM node:20-alpine AS runner
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Curl used for the health checks
|
# Curl used for the health checks
|
||||||
RUN apk add --no-cache curl
|
RUN apk add --no-cache curl
|
||||||
|
|
||||||
COPY package.json package-lock.json ./
|
COPY package.json package-lock.json ./
|
||||||
RUN npm ci --only=production && npm cache clean --force
|
RUN npm ci --only=production && npm cache clean --force
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -18,12 +18,12 @@ _Your own self-hosted zero trust tunnel._
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<h5>
|
<h5>
|
||||||
<a href="https://docs.fossorial.io/Getting%20Started/quick-install">
|
<a href="https://fossorial.io">
|
||||||
Install Guide
|
Website
|
||||||
</a>
|
</a>
|
||||||
<span> | </span>
|
<span> | </span>
|
||||||
<a href="https://docs.fossorial.io">
|
<a href="https://docs.fossorial.io/Getting%20Started/quick-install">
|
||||||
Full Documentation
|
Install Guide
|
||||||
</a>
|
</a>
|
||||||
<span> | </span>
|
<span> | </span>
|
||||||
<a href="mailto:numbat@fossorial.io">
|
<a href="mailto:numbat@fossorial.io">
|
||||||
@@ -136,7 +136,7 @@ View the [project board](https://github.com/orgs/fosrl/projects/1) for more deta
|
|||||||
|
|
||||||
## Licensing
|
## Licensing
|
||||||
|
|
||||||
Pangolin is dual licensed under the AGPLv3 and the Fossorial Commercial license. For inquiries about commercial licensing, please contact us at [numbat@fossorial.io](mailto:numbat@fossorial.io).
|
Pangolin is dual licensed under the AGPL-3 and the Fossorial Commercial license. To see our commercial offerings, please see our [website](https://fossorial.io) for details. For inquiries about commercial licensing, please contact us at [numbat@fossorial.io](mailto:numbat@fossorial.io).
|
||||||
|
|
||||||
## Contributions
|
## Contributions
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ post {
|
|||||||
|
|
||||||
body:json {
|
body:json {
|
||||||
{
|
{
|
||||||
"email": "owen@fossorial.io",
|
"email": "admin@fosrl.io",
|
||||||
"password": "Password123!"
|
"password": "Password123!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
bruno/Users/adminListUsers.bru
Normal file
11
bruno/Users/adminListUsers.bru
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: adminListUsers
|
||||||
|
type: http
|
||||||
|
seq: 2
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: http://localhost:3000/api/v1/users
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
11
bruno/Users/adminRemoveUser.bru
Normal file
11
bruno/Users/adminRemoveUser.bru
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: adminRemoveUser
|
||||||
|
type: http
|
||||||
|
seq: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
delete {
|
||||||
|
url: http://localhost:3000/api/v1/user/ky5r7ivqs8wc7u4
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -18,6 +18,9 @@ server:
|
|||||||
internal_hostname: "pangolin"
|
internal_hostname: "pangolin"
|
||||||
session_cookie_name: "p_session_token"
|
session_cookie_name: "p_session_token"
|
||||||
resource_access_token_param: "p_token"
|
resource_access_token_param: "p_token"
|
||||||
|
resource_access_token_headers:
|
||||||
|
id: "P-Access-Token-Id"
|
||||||
|
token: "P-Access-Token"
|
||||||
resource_session_request_param: "p_session_request"
|
resource_session_request_param: "p_session_request"
|
||||||
|
|
||||||
traefik:
|
traefik:
|
||||||
@@ -35,7 +38,7 @@ gerbil:
|
|||||||
rate_limits:
|
rate_limits:
|
||||||
global:
|
global:
|
||||||
window_minutes: 1
|
window_minutes: 1
|
||||||
max_requests: 100
|
max_requests: 500
|
||||||
|
|
||||||
users:
|
users:
|
||||||
server_admin:
|
server_admin:
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ import path from "path";
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
dialect: "sqlite",
|
dialect: "sqlite",
|
||||||
schema: path.join("server", "db", "schema.ts"),
|
schema: path.join("server", "db", "schemas"),
|
||||||
out: path.join("server", "migrations"),
|
out: path.join("server", "migrations"),
|
||||||
verbose: true,
|
verbose: true,
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
url: path.join(APP_PATH, "db", "db.sqlite"),
|
url: path.join(APP_PATH, "db", "db.sqlite")
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ server:
|
|||||||
internal_hostname: "pangolin"
|
internal_hostname: "pangolin"
|
||||||
session_cookie_name: "p_session_token"
|
session_cookie_name: "p_session_token"
|
||||||
resource_access_token_param: "p_token"
|
resource_access_token_param: "p_token"
|
||||||
|
resource_access_token_headers:
|
||||||
|
id: "P-Access-Token-Id"
|
||||||
|
token: "P-Access-Token"
|
||||||
resource_session_request_param: "p_session_request"
|
resource_session_request_param: "p_session_request"
|
||||||
cors:
|
cors:
|
||||||
origins: ["https://{{.DashboardDomain}}"]
|
origins: ["https://{{.DashboardDomain}}"]
|
||||||
@@ -41,7 +44,7 @@ gerbil:
|
|||||||
rate_limits:
|
rate_limits:
|
||||||
global:
|
global:
|
||||||
window_minutes: 1
|
window_minutes: 1
|
||||||
max_requests: 100
|
max_requests: 500
|
||||||
{{if .EnableEmail}}
|
{{if .EnableEmail}}
|
||||||
email:
|
email:
|
||||||
smtp_host: "{{.EmailSMTPHost}}"
|
smtp_host: "{{.EmailSMTPHost}}"
|
||||||
|
|||||||
141
install/main.go
141
install/main.go
@@ -2,19 +2,19 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"bytes"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"golang.org/x/term"
|
"golang.org/x/term"
|
||||||
@@ -48,8 +48,8 @@ type Config struct {
|
|||||||
EmailSMTPPass string
|
EmailSMTPPass string
|
||||||
EmailNoReply string
|
EmailNoReply string
|
||||||
InstallGerbil bool
|
InstallGerbil bool
|
||||||
TraefikBouncerKey string
|
TraefikBouncerKey string
|
||||||
DoCrowdsecInstall bool
|
DoCrowdsecInstall bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -84,7 +84,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("\n=== Starting installation ===")
|
fmt.Println("\n=== Starting installation ===")
|
||||||
|
|
||||||
if isDockerInstalled() {
|
if isDockerInstalled() {
|
||||||
if readBool(reader, "Would you like to install and start the containers?", true) {
|
if readBool(reader, "Would you like to install and start the containers?", true) {
|
||||||
pullAndStartContainers()
|
pullAndStartContainers()
|
||||||
@@ -95,33 +95,35 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !checkIsCrowdsecInstalledInCompose() {
|
if !checkIsCrowdsecInstalledInCompose() {
|
||||||
fmt.Println("\n=== Crowdsec Install ===")
|
fmt.Println("\n=== CrowdSec Install ===")
|
||||||
// check if crowdsec is installed
|
// check if crowdsec is installed
|
||||||
if readBool(reader, "Would you like to install Crowdsec?", true) {
|
if readBool(reader, "Would you like to install CrowdSec?", false) {
|
||||||
|
fmt.Println("This installer constitutes a minimal viable CrowdSec deployment. CrowdSec will add extra complexity to your Pangolin installation and may not work to the best of its abilities out of the box. Users are expected to implement configuration adjustments on their own to achieve the best security posture. Consult the CrowdSec documentation for detailed configuration instructions.")
|
||||||
|
if readBool(reader, "Are you willing to manage CrowdSec?", false) {
|
||||||
|
if config.DashboardDomain == "" {
|
||||||
|
traefikConfig, err := ReadTraefikConfig("config/traefik/traefik_config.yml", "config/traefik/dynamic_config.yml")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error reading config: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
config.DashboardDomain = traefikConfig.DashboardDomain
|
||||||
|
config.LetsEncryptEmail = traefikConfig.LetsEncryptEmail
|
||||||
|
config.BadgerVersion = traefikConfig.BadgerVersion
|
||||||
|
|
||||||
if config.DashboardDomain == "" {
|
// print the values and check if they are right
|
||||||
traefikConfig, err := ReadTraefikConfig("config/traefik/traefik_config.yml", "config/traefik/dynamic_config.yml")
|
fmt.Println("Detected values:")
|
||||||
if err != nil {
|
fmt.Printf("Dashboard Domain: %s\n", config.DashboardDomain)
|
||||||
fmt.Printf("Error reading config: %v\n", err)
|
fmt.Printf("Let's Encrypt Email: %s\n", config.LetsEncryptEmail)
|
||||||
return
|
fmt.Printf("Badger Version: %s\n", config.BadgerVersion)
|
||||||
}
|
|
||||||
config.DashboardDomain = traefikConfig.DashboardDomain
|
|
||||||
config.LetsEncryptEmail = traefikConfig.LetsEncryptEmail
|
|
||||||
config.BadgerVersion = traefikConfig.BadgerVersion
|
|
||||||
|
|
||||||
// print the values and check if they are right
|
|
||||||
fmt.Println("Detected values:")
|
|
||||||
fmt.Printf("Dashboard Domain: %s\n", config.DashboardDomain)
|
|
||||||
fmt.Printf("Let's Encrypt Email: %s\n", config.LetsEncryptEmail)
|
|
||||||
fmt.Printf("Badger Version: %s\n", config.BadgerVersion)
|
|
||||||
|
|
||||||
if !readBool(reader, "Are these values correct?", true) {
|
if !readBool(reader, "Are these values correct?", true) {
|
||||||
config = collectUserInput(reader)
|
config = collectUserInput(reader)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.DoCrowdsecInstall = true
|
||||||
|
installCrowdsec(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
config.DoCrowdsecInstall = true
|
|
||||||
installCrowdsec(config)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,23 +145,23 @@ func readString(reader *bufio.Reader, prompt string, defaultValue string) string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readPassword(prompt string, reader *bufio.Reader) string {
|
func readPassword(prompt string, reader *bufio.Reader) string {
|
||||||
if term.IsTerminal(int(syscall.Stdin)) {
|
if term.IsTerminal(int(syscall.Stdin)) {
|
||||||
fmt.Print(prompt + ": ")
|
fmt.Print(prompt + ": ")
|
||||||
// Read password without echo if we're in a terminal
|
// Read password without echo if we're in a terminal
|
||||||
password, err := term.ReadPassword(int(syscall.Stdin))
|
password, err := term.ReadPassword(int(syscall.Stdin))
|
||||||
fmt.Println() // Add a newline since ReadPassword doesn't add one
|
fmt.Println() // Add a newline since ReadPassword doesn't add one
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
input := strings.TrimSpace(string(password))
|
input := strings.TrimSpace(string(password))
|
||||||
if input == "" {
|
if input == "" {
|
||||||
return readPassword(prompt, reader)
|
return readPassword(prompt, reader)
|
||||||
}
|
}
|
||||||
return input
|
return input
|
||||||
} else {
|
} else {
|
||||||
// Fallback to reading from stdin if not in a terminal
|
// Fallback to reading from stdin if not in a terminal
|
||||||
return readString(reader, prompt, "")
|
return readString(reader, prompt, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readBool(reader *bufio.Reader, prompt string, defaultValue bool) bool {
|
func readBool(reader *bufio.Reader, prompt string, defaultValue bool) bool {
|
||||||
@@ -319,15 +321,15 @@ func createConfigFiles(config Config) error {
|
|||||||
if !config.DoCrowdsecInstall && strings.Contains(path, "crowdsec") {
|
if !config.DoCrowdsecInstall && strings.Contains(path, "crowdsec") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.DoCrowdsecInstall && !strings.Contains(path, "crowdsec") {
|
if config.DoCrowdsecInstall && !strings.Contains(path, "crowdsec") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip .DS_Store
|
// skip .DS_Store
|
||||||
if strings.Contains(path, ".DS_Store") {
|
if strings.Contains(path, ".DS_Store") {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.IsDir() {
|
if d.IsDir() {
|
||||||
// Create directory
|
// Create directory
|
||||||
@@ -376,7 +378,6 @@ func createConfigFiles(config Config) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func installDocker() error {
|
func installDocker() error {
|
||||||
// Detect Linux distribution
|
// Detect Linux distribution
|
||||||
cmd := exec.Command("cat", "/etc/os-release")
|
cmd := exec.Command("cat", "/etc/os-release")
|
||||||
@@ -654,29 +655,29 @@ func moveFile(src, dst string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func waitForContainer(containerName string) error {
|
func waitForContainer(containerName string) error {
|
||||||
maxAttempts := 30
|
maxAttempts := 30
|
||||||
retryInterval := time.Second * 2
|
retryInterval := time.Second * 2
|
||||||
|
|
||||||
for attempt := 0; attempt < maxAttempts; attempt++ {
|
for attempt := 0; attempt < maxAttempts; attempt++ {
|
||||||
// Check if container is running
|
// Check if container is running
|
||||||
cmd := exec.Command("docker", "container", "inspect", "-f", "{{.State.Running}}", containerName)
|
cmd := exec.Command("docker", "container", "inspect", "-f", "{{.State.Running}}", containerName)
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
cmd.Stdout = &out
|
cmd.Stdout = &out
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
|
||||||
// If the container doesn't exist or there's another error, wait and retry
|
|
||||||
time.Sleep(retryInterval)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
isRunning := strings.TrimSpace(out.String()) == "true"
|
if err := cmd.Run(); err != nil {
|
||||||
if isRunning {
|
// If the container doesn't exist or there's another error, wait and retry
|
||||||
return nil
|
time.Sleep(retryInterval)
|
||||||
}
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Container exists but isn't running yet, wait and retry
|
isRunning := strings.TrimSpace(out.String()) == "true"
|
||||||
time.Sleep(retryInterval)
|
if isRunning {
|
||||||
}
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return fmt.Errorf("container %s did not start within %v seconds", containerName, maxAttempts*int(retryInterval.Seconds()))
|
// Container exists but isn't running yet, wait and retry
|
||||||
}
|
time.Sleep(retryInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("container %s did not start within %v seconds", containerName, maxAttempts*int(retryInterval.Seconds()))
|
||||||
|
}
|
||||||
|
|||||||
2668
package-lock.json
generated
2668
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,7 @@
|
|||||||
"glob": "11.0.0",
|
"glob": "11.0.0",
|
||||||
"helmet": "8.0.0",
|
"helmet": "8.0.0",
|
||||||
"http-errors": "2.0.0",
|
"http-errors": "2.0.0",
|
||||||
|
"i": "^0.3.7",
|
||||||
"input-otp": "1.4.1",
|
"input-otp": "1.4.1",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"lucide-react": "0.469.0",
|
"lucide-react": "0.469.0",
|
||||||
@@ -66,12 +67,14 @@
|
|||||||
"node-cache": "5.1.2",
|
"node-cache": "5.1.2",
|
||||||
"node-fetch": "3.3.2",
|
"node-fetch": "3.3.2",
|
||||||
"nodemailer": "6.9.16",
|
"nodemailer": "6.9.16",
|
||||||
|
"npm": "^11.2.0",
|
||||||
"oslo": "1.2.1",
|
"oslo": "1.2.1",
|
||||||
"qrcode.react": "4.2.0",
|
"qrcode.react": "4.2.0",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"react-easy-sort": "^1.6.0",
|
"react-easy-sort": "^1.6.0",
|
||||||
"react-hook-form": "7.54.2",
|
"react-hook-form": "7.54.2",
|
||||||
|
"react-icons": "^5.5.0",
|
||||||
"rebuild": "0.1.2",
|
"rebuild": "0.1.2",
|
||||||
"semver": "7.6.3",
|
"semver": "7.6.3",
|
||||||
"tailwind-merge": "2.6.0",
|
"tailwind-merge": "2.6.0",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { logIncomingMiddleware } from "./middlewares/logIncoming";
|
|||||||
import { csrfProtectionMiddleware } from "./middlewares/csrfProtection";
|
import { csrfProtectionMiddleware } from "./middlewares/csrfProtection";
|
||||||
import helmet from "helmet";
|
import helmet from "helmet";
|
||||||
|
|
||||||
const dev = process.env.ENVIRONMENT !== "prod";
|
const dev = config.isDev;
|
||||||
const externalPort = config.getRawConfig().server.external_port;
|
const externalPort = config.getRawConfig().server.external_port;
|
||||||
|
|
||||||
export function createApiServer() {
|
export function createApiServer() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userActions, roleActions, userOrgs } from "@server/db/schema";
|
import { userActions, roleActions, userOrgs } from "@server/db/schemas";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
@@ -63,6 +63,7 @@ export enum ActionsEnum {
|
|||||||
listResourceRules = "listResourceRules",
|
listResourceRules = "listResourceRules",
|
||||||
updateResourceRule = "updateResourceRule",
|
updateResourceRule = "updateResourceRule",
|
||||||
listOrgDomains = "listOrgDomains",
|
listOrgDomains = "listOrgDomains",
|
||||||
|
createNewt = "createNewt",
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkUserActionPermission(
|
export async function checkUserActionPermission(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { roleResources, userResources } from "@server/db/schema";
|
import { roleResources, userResources } from "@server/db/schemas";
|
||||||
|
|
||||||
export async function canUserAccessResource({
|
export async function canUserAccessResource({
|
||||||
userId,
|
userId,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { UserInvite, userInvites } from "@server/db/schema";
|
import { UserInvite, userInvites } from "@server/db/schemas";
|
||||||
import { isWithinExpirationDate } from "oslo";
|
import { isWithinExpirationDate } from "oslo";
|
||||||
import { verifyPassword } from "./password";
|
import { verifyPassword } from "./password";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { db } from '@server/db';
|
import { db } from '@server/db';
|
||||||
import { limitsTable } from '@server/db/schema';
|
import { limitsTable } from '@server/db/schemas';
|
||||||
import { and, eq } from 'drizzle-orm';
|
import { and, eq } from 'drizzle-orm';
|
||||||
import createHttpError from 'http-errors';
|
import createHttpError from 'http-errors';
|
||||||
import HttpCode from '@server/types/HttpCode';
|
import HttpCode from '@server/types/HttpCode';
|
||||||
@@ -37,4 +37,4 @@ export async function checkOrgLimit({ orgId, limitName, currentValue, increment
|
|||||||
}
|
}
|
||||||
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Unknown error occurred while checking limit');
|
throw createHttpError(HttpCode.INTERNAL_SERVER_ERROR, 'Unknown error occurred while checking limit');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { resourceOtp } from "@server/db/schema";
|
import { resourceOtp } from "@server/db/schemas";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { createDate, isWithinExpirationDate, TimeSpan } from "oslo";
|
import { createDate, isWithinExpirationDate, TimeSpan } from "oslo";
|
||||||
import { alphabet, generateRandomString, sha256 } from "oslo/crypto";
|
import { alphabet, generateRandomString, sha256 } from "oslo/crypto";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { TimeSpan, createDate } from "oslo";
|
import { TimeSpan, createDate } from "oslo";
|
||||||
import { generateRandomString, alphabet } from "oslo/crypto";
|
import { generateRandomString, alphabet } from "oslo/crypto";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { users, emailVerificationCodes } from "@server/db/schema";
|
import { users, emailVerificationCodes } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { sendEmail } from "@server/emails";
|
import { sendEmail } from "@server/emails";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
sessions,
|
sessions,
|
||||||
User,
|
User,
|
||||||
users
|
users
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { eq, inArray } from "drizzle-orm";
|
import { eq, inArray } from "drizzle-orm";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import {
|
|||||||
encodeHexLowerCase,
|
encodeHexLowerCase,
|
||||||
} from "@oslojs/encoding";
|
} from "@oslojs/encoding";
|
||||||
import { sha256 } from "@oslojs/crypto/sha2";
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
import { Newt, newts, newtSessions, NewtSession } from "@server/db/schema";
|
import { Newt, newts, newtSessions, NewtSession } from "@server/db/schemas";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { encodeHexLowerCase } from "@oslojs/encoding";
|
import { encodeHexLowerCase } from "@oslojs/encoding";
|
||||||
import { sha256 } from "@oslojs/crypto/sha2";
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
import { resourceSessions, ResourceSession } from "@server/db/schema";
|
import { resourceSessions, ResourceSession } from "@server/db/schemas";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
@@ -170,16 +170,17 @@ export function serializeResourceSessionCookie(
|
|||||||
isHttp: boolean = false,
|
isHttp: boolean = false,
|
||||||
expiresAt?: Date
|
expiresAt?: Date
|
||||||
): string {
|
): string {
|
||||||
|
const now = new Date().getTime();
|
||||||
if (!isHttp) {
|
if (!isHttp) {
|
||||||
if (expiresAt === undefined) {
|
if (expiresAt === undefined) {
|
||||||
return `${cookieName}_s=${token}; HttpOnly; SameSite=Lax; Path=/; Secure; Domain=${"." + domain}`;
|
return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Secure; Domain=${"." + domain}`;
|
||||||
}
|
}
|
||||||
return `${cookieName}_s=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Secure; Domain=${"." + domain}`;
|
return `${cookieName}_s.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Secure; Domain=${"." + domain}`;
|
||||||
} else {
|
} else {
|
||||||
if (expiresAt === undefined) {
|
if (expiresAt === undefined) {
|
||||||
return `${cookieName}=${token}; HttpOnly; SameSite=Lax; Path=/; Domain=${"." + domain}`;
|
return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Path=/; Domain=${"." + domain}`;
|
||||||
}
|
}
|
||||||
return `${cookieName}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Domain=${"." + domain}`;
|
return `${cookieName}.${now}=${token}; HttpOnly; SameSite=Lax; Expires=${expiresAt.toUTCString()}; Path=/; Domain=${"." + domain}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { verify } from "@node-rs/argon2";
|
import { verify } from "@node-rs/argon2";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { twoFactorBackupCodes } from "@server/db/schema";
|
import { twoFactorBackupCodes } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { decodeHex } from "oslo/encoding";
|
import { decodeHex } from "oslo/encoding";
|
||||||
import { TOTPController } from "oslo/otp";
|
import { TOTPController } from "oslo/otp";
|
||||||
|
|||||||
@@ -3,53 +3,95 @@ import {
|
|||||||
Resource,
|
Resource,
|
||||||
ResourceAccessToken,
|
ResourceAccessToken,
|
||||||
resourceAccessToken,
|
resourceAccessToken,
|
||||||
} from "@server/db/schema";
|
resources
|
||||||
|
} from "@server/db/schemas";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { isWithinExpirationDate } from "oslo";
|
import { isWithinExpirationDate } from "oslo";
|
||||||
import { verifyPassword } from "./password";
|
import { verifyPassword } from "./password";
|
||||||
|
import { encodeHexLowerCase } from "@oslojs/encoding";
|
||||||
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
|
|
||||||
export async function verifyResourceAccessToken({
|
export async function verifyResourceAccessToken({
|
||||||
resource,
|
accessToken,
|
||||||
accessTokenId,
|
accessTokenId,
|
||||||
accessToken
|
resourceId
|
||||||
}: {
|
}: {
|
||||||
resource: Resource;
|
|
||||||
accessTokenId: string;
|
|
||||||
accessToken: string;
|
accessToken: string;
|
||||||
|
accessTokenId?: string;
|
||||||
|
resourceId?: number; // IF THIS IS NOT SET, THE TOKEN IS VALID FOR ALL RESOURCES
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
valid: boolean;
|
valid: boolean;
|
||||||
error?: string;
|
error?: string;
|
||||||
tokenItem?: ResourceAccessToken;
|
tokenItem?: ResourceAccessToken;
|
||||||
|
resource?: Resource;
|
||||||
}> {
|
}> {
|
||||||
const [result] = await db
|
const accessTokenHash = encodeHexLowerCase(
|
||||||
.select()
|
sha256(new TextEncoder().encode(accessToken))
|
||||||
.from(resourceAccessToken)
|
);
|
||||||
.where(
|
|
||||||
and(
|
|
||||||
eq(resourceAccessToken.resourceId, resource.resourceId),
|
|
||||||
eq(resourceAccessToken.accessTokenId, accessTokenId)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
const tokenItem = result;
|
let tokenItem: ResourceAccessToken | undefined;
|
||||||
|
let resource: Resource | undefined;
|
||||||
|
|
||||||
if (!tokenItem) {
|
if (!accessTokenId) {
|
||||||
|
const [res] = await db
|
||||||
|
.select()
|
||||||
|
.from(resourceAccessToken)
|
||||||
|
.where(and(eq(resourceAccessToken.tokenHash, accessTokenHash)))
|
||||||
|
.innerJoin(
|
||||||
|
resources,
|
||||||
|
eq(resourceAccessToken.resourceId, resources.resourceId)
|
||||||
|
);
|
||||||
|
|
||||||
|
tokenItem = res?.resourceAccessToken;
|
||||||
|
resource = res?.resources;
|
||||||
|
} else {
|
||||||
|
const [res] = await db
|
||||||
|
.select()
|
||||||
|
.from(resourceAccessToken)
|
||||||
|
.where(and(eq(resourceAccessToken.accessTokenId, accessTokenId)))
|
||||||
|
.innerJoin(
|
||||||
|
resources,
|
||||||
|
eq(resourceAccessToken.resourceId, resources.resourceId)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res && res.resourceAccessToken) {
|
||||||
|
if (res.resourceAccessToken.tokenHash?.startsWith("$argon")) {
|
||||||
|
const validCode = await verifyPassword(
|
||||||
|
accessToken,
|
||||||
|
res.resourceAccessToken.tokenHash
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!validCode) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
error: "Invalid access token"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const tokenHash = encodeHexLowerCase(
|
||||||
|
sha256(new TextEncoder().encode(accessToken))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (res.resourceAccessToken.tokenHash !== tokenHash) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
error: "Invalid access token"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenItem = res?.resourceAccessToken;
|
||||||
|
resource = res?.resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tokenItem || !resource) {
|
||||||
return {
|
return {
|
||||||
valid: false,
|
valid: false,
|
||||||
error: "Access token does not exist for resource"
|
error: "Access token does not exist for resource"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const validCode = await verifyPassword(accessToken, tokenItem.tokenHash);
|
|
||||||
|
|
||||||
if (!validCode) {
|
|
||||||
return {
|
|
||||||
valid: false,
|
|
||||||
error: "Invalid access token"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
tokenItem.expiresAt &&
|
tokenItem.expiresAt &&
|
||||||
!isWithinExpirationDate(new Date(tokenItem.expiresAt))
|
!isWithinExpirationDate(new Date(tokenItem.expiresAt))
|
||||||
@@ -60,8 +102,16 @@ export async function verifyResourceAccessToken({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (resourceId && resource.resourceId !== resourceId) {
|
||||||
|
return {
|
||||||
|
valid: false,
|
||||||
|
error: "Resource ID does not match"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
valid: true,
|
valid: true,
|
||||||
tokenItem
|
tokenItem,
|
||||||
|
resource
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { drizzle } from "drizzle-orm/better-sqlite3";
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
||||||
import Database from "better-sqlite3";
|
import Database from "better-sqlite3";
|
||||||
import * as schema from "@server/db/schema";
|
import * as schema from "@server/db/schemas";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs/promises";
|
import fs from "fs/promises";
|
||||||
import { APP_PATH } from "@server/lib/consts";
|
import { APP_PATH } from "@server/lib/consts";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { exitNodes, sites } from "./schema";
|
import { exitNodes, sites } from "./schemas/schema";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
import { __DIRNAME } from "@server/lib/consts";
|
import { __DIRNAME } from "@server/lib/consts";
|
||||||
|
|
||||||
|
|||||||
1
server/db/schemas/index.ts
Normal file
1
server/db/schemas/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from "./schema";
|
||||||
@@ -76,7 +76,8 @@ export const resources = sqliteTable("resources", {
|
|||||||
isBaseDomain: integer("isBaseDomain", { mode: "boolean" }),
|
isBaseDomain: integer("isBaseDomain", { mode: "boolean" }),
|
||||||
applyRules: integer("applyRules", { mode: "boolean" })
|
applyRules: integer("applyRules", { mode: "boolean" })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(false)
|
.default(false),
|
||||||
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
export const targets = sqliteTable("targets", {
|
export const targets = sqliteTable("targets", {
|
||||||
@@ -405,6 +406,15 @@ export const resourceRules = sqliteTable("resourceRules", {
|
|||||||
value: text("value").notNull()
|
value: text("value").notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const supporterKey = sqliteTable("supporterKey", {
|
||||||
|
keyId: integer("keyId").primaryKey({ autoIncrement: true }),
|
||||||
|
key: text("key").notNull(),
|
||||||
|
githubUsername: text("githubUsername").notNull(),
|
||||||
|
phrase: text("phrase"),
|
||||||
|
tier: text("tier"),
|
||||||
|
valid: integer("valid", { mode: "boolean" }).notNull().default(false)
|
||||||
|
});
|
||||||
|
|
||||||
export type Org = InferSelectModel<typeof orgs>;
|
export type Org = InferSelectModel<typeof orgs>;
|
||||||
export type User = InferSelectModel<typeof users>;
|
export type User = InferSelectModel<typeof users>;
|
||||||
export type Site = InferSelectModel<typeof sites>;
|
export type Site = InferSelectModel<typeof sites>;
|
||||||
@@ -439,3 +449,4 @@ export type ResourceWhitelist = InferSelectModel<typeof resourceWhitelist>;
|
|||||||
export type VersionMigration = InferSelectModel<typeof versionMigrations>;
|
export type VersionMigration = InferSelectModel<typeof versionMigrations>;
|
||||||
export type ResourceRule = InferSelectModel<typeof resourceRules>;
|
export type ResourceRule = InferSelectModel<typeof resourceRules>;
|
||||||
export type Domain = InferSelectModel<typeof domains>;
|
export type Domain = InferSelectModel<typeof domains>;
|
||||||
|
export type SupporterKey = InferSelectModel<typeof supporterKey>;
|
||||||
@@ -2,7 +2,7 @@ import { runSetupFunctions } from "./setup";
|
|||||||
import { createApiServer } from "./apiServer";
|
import { createApiServer } from "./apiServer";
|
||||||
import { createNextServer } from "./nextServer";
|
import { createNextServer } from "./nextServer";
|
||||||
import { createInternalServer } from "./internalServer";
|
import { createInternalServer } from "./internalServer";
|
||||||
import { Session, User, UserOrg } from "./db/schema";
|
import { Session, User, UserOrg } from "./db/schemas/schema";
|
||||||
|
|
||||||
async function startServers() {
|
async function startServers() {
|
||||||
await runSetupFunctions();
|
await runSetupFunctions();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import { roleResources, userResources } from "@server/db/schema";
|
import { roleResources, userResources } from "@server/db/schemas";
|
||||||
|
|
||||||
export async function canUserAccessResource({
|
export async function canUserAccessResource({
|
||||||
userId,
|
userId,
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ import {
|
|||||||
} from "@server/lib/consts";
|
} from "@server/lib/consts";
|
||||||
import { passwordSchema } from "@server/auth/passwordSchema";
|
import { passwordSchema } from "@server/auth/passwordSchema";
|
||||||
import stoi from "./stoi";
|
import stoi from "./stoi";
|
||||||
|
import db from "@server/db";
|
||||||
|
import { SupporterKey, supporterKey } from "@server/db/schemas";
|
||||||
|
import { suppressDeprecationWarnings } from "moment";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
|
||||||
const portSchema = z.number().positive().gt(0).lte(65535);
|
const portSchema = z.number().positive().gt(0).lte(65535);
|
||||||
|
|
||||||
@@ -62,6 +66,10 @@ const configSchema = z.object({
|
|||||||
internal_hostname: z.string().transform((url) => url.toLowerCase()),
|
internal_hostname: z.string().transform((url) => url.toLowerCase()),
|
||||||
session_cookie_name: z.string(),
|
session_cookie_name: z.string(),
|
||||||
resource_access_token_param: z.string(),
|
resource_access_token_param: z.string(),
|
||||||
|
resource_access_token_headers: z.object({
|
||||||
|
id: z.string(),
|
||||||
|
token: z.string()
|
||||||
|
}),
|
||||||
resource_session_request_param: z.string(),
|
resource_session_request_param: z.string(),
|
||||||
dashboard_session_length_hours: z
|
dashboard_session_length_hours: z
|
||||||
.number()
|
.number()
|
||||||
@@ -155,6 +163,12 @@ const configSchema = z.object({
|
|||||||
export class Config {
|
export class Config {
|
||||||
private rawConfig!: z.infer<typeof configSchema>;
|
private rawConfig!: z.infer<typeof configSchema>;
|
||||||
|
|
||||||
|
supporterData: SupporterKey | null = null;
|
||||||
|
|
||||||
|
supporterHiddenUntil: number | null = null;
|
||||||
|
|
||||||
|
isDev: boolean = process.env.ENVIRONMENT !== "prod";
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.loadConfig();
|
this.loadConfig();
|
||||||
}
|
}
|
||||||
@@ -183,7 +197,9 @@ export class Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (process.env.APP_BASE_DOMAIN) {
|
if (process.env.APP_BASE_DOMAIN) {
|
||||||
console.log("You're using deprecated environment variables. Transition to the configuration file. https://docs.fossorial.io/");
|
console.log(
|
||||||
|
"You're using deprecated environment variables. Transition to the configuration file. https://docs.fossorial.io/"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!environment) {
|
if (!environment) {
|
||||||
@@ -227,6 +243,10 @@ export class Config {
|
|||||||
: "false";
|
: "false";
|
||||||
process.env.RESOURCE_ACCESS_TOKEN_PARAM =
|
process.env.RESOURCE_ACCESS_TOKEN_PARAM =
|
||||||
parsedConfig.data.server.resource_access_token_param;
|
parsedConfig.data.server.resource_access_token_param;
|
||||||
|
process.env.RESOURCE_ACCESS_TOKEN_HEADERS_ID =
|
||||||
|
parsedConfig.data.server.resource_access_token_headers.id;
|
||||||
|
process.env.RESOURCE_ACCESS_TOKEN_HEADERS_TOKEN =
|
||||||
|
parsedConfig.data.server.resource_access_token_headers.token;
|
||||||
process.env.RESOURCE_SESSION_REQUEST_PARAM =
|
process.env.RESOURCE_SESSION_REQUEST_PARAM =
|
||||||
parsedConfig.data.server.resource_session_request_param;
|
parsedConfig.data.server.resource_session_request_param;
|
||||||
process.env.FLAGS_ALLOW_BASE_DOMAIN_RESOURCES = parsedConfig.data.flags
|
process.env.FLAGS_ALLOW_BASE_DOMAIN_RESOURCES = parsedConfig.data.flags
|
||||||
@@ -235,6 +255,10 @@ export class Config {
|
|||||||
: "false";
|
: "false";
|
||||||
process.env.DASHBOARD_URL = parsedConfig.data.app.dashboard_url;
|
process.env.DASHBOARD_URL = parsedConfig.data.app.dashboard_url;
|
||||||
|
|
||||||
|
if (!this.isDev) {
|
||||||
|
this.checkSupporterKey();
|
||||||
|
}
|
||||||
|
|
||||||
this.rawConfig = parsedConfig.data;
|
this.rawConfig = parsedConfig.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +275,90 @@ export class Config {
|
|||||||
public getDomain(domainId: string) {
|
public getDomain(domainId: string) {
|
||||||
return this.rawConfig.domains[domainId];
|
return this.rawConfig.domains[domainId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hideSupporterKey(days: number = 7) {
|
||||||
|
const now = new Date().getTime();
|
||||||
|
|
||||||
|
if (this.supporterHiddenUntil && now < this.supporterHiddenUntil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.supporterHiddenUntil = now + 1000 * 60 * 60 * 24 * days;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isSupporterKeyHidden() {
|
||||||
|
const now = new Date().getTime();
|
||||||
|
|
||||||
|
if (this.supporterHiddenUntil && now < this.supporterHiddenUntil) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async checkSupporterKey() {
|
||||||
|
const [key] = await db.select().from(supporterKey).limit(1);
|
||||||
|
|
||||||
|
if (!key) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { key: licenseKey, githubUsername } = key;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
"https://api.dev.fossorial.io/api/v1/license/validate",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
licenseKey,
|
||||||
|
githubUsername
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
this.supporterData = key;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (!data.data.valid) {
|
||||||
|
this.supporterData = {
|
||||||
|
...key,
|
||||||
|
valid: false
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.supporterData = {
|
||||||
|
...key,
|
||||||
|
tier: data.data.tier,
|
||||||
|
valid: true
|
||||||
|
};
|
||||||
|
|
||||||
|
// update the supporter key in the database
|
||||||
|
await db
|
||||||
|
.update(supporterKey)
|
||||||
|
.set({
|
||||||
|
tier: data.data.tier || null,
|
||||||
|
phrase: data.data.cutePhrase || null,
|
||||||
|
valid: true
|
||||||
|
})
|
||||||
|
.where(eq(supporterKey.keyId, key.keyId));
|
||||||
|
} catch (e) {
|
||||||
|
this.supporterData = key;
|
||||||
|
console.error("Failed to validate supporter key", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSupporterData() {
|
||||||
|
return this.supporterData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const config = new Config();
|
export const config = new Config();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import path from "path";
|
|||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
|
|
||||||
// This is a placeholder value replaced by the build process
|
// This is a placeholder value replaced by the build process
|
||||||
export const APP_VERSION = "1.0.0";
|
export const APP_VERSION = "1.2.0";
|
||||||
|
|
||||||
export const __FILENAME = fileURLToPath(import.meta.url);
|
export const __FILENAME = fileURLToPath(import.meta.url);
|
||||||
export const __DIRNAME = path.dirname(__FILENAME);
|
export const __DIRNAME = path.dirname(__FILENAME);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs, orgs } from "@server/db/schema";
|
import { userOrgs, orgs } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -14,3 +14,4 @@ export * from "./verifyAdmin";
|
|||||||
export * from "./verifySetResourceUsers";
|
export * from "./verifySetResourceUsers";
|
||||||
export * from "./verifyUserInRole";
|
export * from "./verifyUserInRole";
|
||||||
export * from "./verifyAccessTokenAccess";
|
export * from "./verifyAccessTokenAccess";
|
||||||
|
export * from "./verifyUserIsServerAdmin";
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resourceAccessToken, resources, userOrgs } from "@server/db/schema";
|
import { resourceAccessToken, resources, userOrgs } from "@server/db/schemas";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { roles, userOrgs } from "@server/db/schema";
|
import { roles, userOrgs } from "@server/db/schemas";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs } from "@server/db/schema";
|
import { userOrgs } from "@server/db/schemas";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
userOrgs,
|
userOrgs,
|
||||||
userResources,
|
userResources,
|
||||||
roleResources,
|
roleResources,
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { roles, userOrgs } from "@server/db/schema";
|
import { roles, userOrgs } from "@server/db/schemas";
|
||||||
import { and, eq, inArray } from "drizzle-orm";
|
import { and, eq, inArray } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
@@ -44,6 +44,8 @@ export async function verifyRoleAccess(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const orgIds = new Set(rolesData.map((role) => role.orgId));
|
||||||
|
|
||||||
// Check user access to each role's organization
|
// Check user access to each role's organization
|
||||||
for (const role of rolesData) {
|
for (const role of rolesData) {
|
||||||
const userOrgRole = await db
|
const userOrgRole = await db
|
||||||
@@ -69,7 +71,16 @@ export async function verifyRoleAccess(
|
|||||||
req.userOrgId = role.orgId;
|
req.userOrgId = role.orgId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const orgId = req.userOrgId;
|
if (orgIds.size > 1) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.FORBIDDEN,
|
||||||
|
"Roles must belong to the same organization"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const orgId = orgIds.values().next().value;
|
||||||
|
|
||||||
if (!orgId) {
|
if (!orgId) {
|
||||||
return next(
|
return next(
|
||||||
@@ -105,3 +116,4 @@ export async function verifyRoleAccess(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { NextFunction, Response } from "express";
|
import { NextFunction, Response } from "express";
|
||||||
import ErrorResponse from "@server/types/ErrorResponse";
|
import ErrorResponse from "@server/types/ErrorResponse";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { users } from "@server/db/schema";
|
import { users } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs } from "@server/db/schema";
|
import { userOrgs } from "@server/db/schemas";
|
||||||
import { and, eq, inArray, or } from "drizzle-orm";
|
import { and, eq, inArray, or } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
userSites,
|
userSites,
|
||||||
roleSites,
|
roleSites,
|
||||||
roles,
|
roles,
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import { and, eq, or } from "drizzle-orm";
|
import { and, eq, or } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resources, targets, userOrgs } from "@server/db/schema";
|
import { resources, targets, userOrgs } from "@server/db/schemas";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { NextFunction, Response } from "express";
|
import { NextFunction, Response } from "express";
|
||||||
import ErrorResponse from "@server/types/ErrorResponse";
|
import ErrorResponse from "@server/types/ErrorResponse";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { users } from "@server/db/schema";
|
import { users } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs } from "@server/db/schema";
|
import { userOrgs } from "@server/db/schemas";
|
||||||
import { and, eq, or } from "drizzle-orm";
|
import { and, eq, or } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userOrgs } from "@server/db/schema";
|
import { userOrgs } from "@server/db/schemas";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
@@ -55,7 +55,7 @@ export async function verifyUserIsOrgOwner(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return next(
|
return next(
|
||||||
|
|||||||
37
server/middlewares/verifyUserIsServerAdmin.ts
Normal file
37
server/middlewares/verifyUserIsServerAdmin.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { Request, Response, NextFunction } from "express";
|
||||||
|
import createHttpError from "http-errors";
|
||||||
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|
||||||
|
export async function verifyUserIsServerAdmin(
|
||||||
|
req: Request,
|
||||||
|
res: Response,
|
||||||
|
next: NextFunction
|
||||||
|
) {
|
||||||
|
const userId = req.user!.userId;
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.UNAUTHORIZED, "User not authenticated")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!req.user?.serverAdmin) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.FORBIDDEN,
|
||||||
|
"User is not a server admin"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
} catch (e) {
|
||||||
|
return next(
|
||||||
|
createHttpError(
|
||||||
|
HttpCode.INTERNAL_SERVER_ERROR,
|
||||||
|
"Error verifying organization access"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import HttpCode from "@server/types/HttpCode";
|
|||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { resourceAccessToken } from "@server/db/schema";
|
import { resourceAccessToken } from "@server/db/schemas";
|
||||||
import { and, eq } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
ResourceAccessToken,
|
ResourceAccessToken,
|
||||||
resourceAccessToken,
|
resourceAccessToken,
|
||||||
resources
|
resources
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
@@ -20,6 +20,8 @@ import { fromError } from "zod-validation-error";
|
|||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { createDate, TimeSpan } from "oslo";
|
import { createDate, TimeSpan } from "oslo";
|
||||||
import { hashPassword } from "@server/auth/password";
|
import { hashPassword } from "@server/auth/password";
|
||||||
|
import { encodeHexLowerCase } from "@oslojs/encoding";
|
||||||
|
import { sha256 } from "@oslojs/crypto/sha2";
|
||||||
|
|
||||||
export const generateAccessTokenBodySchema = z
|
export const generateAccessTokenBodySchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -90,11 +92,13 @@ export async function generateAccessToken(
|
|||||||
? createDate(new TimeSpan(validForSeconds, "s")).getTime()
|
? createDate(new TimeSpan(validForSeconds, "s")).getTime()
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const token = generateIdFromEntropySize(25);
|
const token = generateIdFromEntropySize(16);
|
||||||
|
|
||||||
const tokenHash = await hashPassword(token);
|
const tokenHash = encodeHexLowerCase(
|
||||||
|
sha256(new TextEncoder().encode(token))
|
||||||
|
);
|
||||||
|
|
||||||
const id = generateId(15);
|
const id = generateId(8);
|
||||||
const [result] = await db
|
const [result] = await db
|
||||||
.insert(resourceAccessToken)
|
.insert(resourceAccessToken)
|
||||||
.values({
|
.values({
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ import {
|
|||||||
roleResources,
|
roleResources,
|
||||||
resourceAccessToken,
|
resourceAccessToken,
|
||||||
sites
|
sites
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import { sql, eq, or, inArray, and, count, isNull, lt, gt } from "drizzle-orm";
|
import { sql, eq, or, inArray, and, count, isNull, lt, gt } from "drizzle-orm";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import stoi from "@server/lib/stoi";
|
import stoi from "@server/lib/stoi";
|
||||||
|
import { fromZodError } from "zod-validation-error";
|
||||||
|
|
||||||
const listAccessTokensParamsSchema = z
|
const listAccessTokensParamsSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -133,7 +134,7 @@ export async function listAccessTokens(
|
|||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedQuery.error.errors.map((e) => e.message).join(", ")
|
fromZodError(parsedQuery.error)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -144,7 +145,7 @@ export async function listAccessTokens(
|
|||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedParams.error.errors.map((e) => e.message).join(", ")
|
fromZodError(parsedParams.error)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import HttpCode from "@server/types/HttpCode";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { User, users } from "@server/db/schema";
|
import { User, users } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import HttpCode from "@server/types/HttpCode";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { User, users } from "@server/db/schema";
|
import { User, users } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { verifyPassword } from "@server/auth/password";
|
import { verifyPassword } from "@server/auth/password";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
serializeSessionCookie
|
serializeSessionCookie
|
||||||
} from "@server/auth/sessions/app";
|
} from "@server/auth/sessions/app";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { users } from "@server/db/schema";
|
import { users } from "@server/db/schemas";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Request, Response, NextFunction } from "express";
|
|||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { User } from "@server/db/schema";
|
import { User } from "@server/db/schemas";
|
||||||
import { sendEmailVerificationCode } from "../../auth/sendEmailVerificationCode";
|
import { sendEmailVerificationCode } from "../../auth/sendEmailVerificationCode";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { fromError } from "zod-validation-error";
|
|||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { passwordResetTokens, users } from "@server/db/schema";
|
import { passwordResetTokens, users } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { alphabet, generateRandomString, sha256 } from "oslo/crypto";
|
import { alphabet, generateRandomString, sha256 } from "oslo/crypto";
|
||||||
import { createDate } from "oslo";
|
import { createDate } from "oslo";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { encodeHex } from "oslo/encoding";
|
|||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { User, users } from "@server/db/schema";
|
import { User, users } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { createTOTPKeyURI } from "oslo/otp";
|
import { createTOTPKeyURI } from "oslo/otp";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { fromError } from "zod-validation-error";
|
|||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { passwordResetTokens, users } from "@server/db/schema";
|
import { passwordResetTokens, users } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { hashPassword, verifyPassword } from "@server/auth/password";
|
import { hashPassword, verifyPassword } from "@server/auth/password";
|
||||||
import { verifyTotpCode } from "@server/auth/totp";
|
import { verifyTotpCode } from "@server/auth/totp";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { NextFunction, Request, Response } from "express";
|
|||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { users } from "@server/db/schema";
|
import { users } from "@server/db/schemas";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { fromError } from "zod-validation-error";
|
|||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { User, emailVerificationCodes, users } from "@server/db/schema";
|
import { User, emailVerificationCodes, users } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { isWithinExpirationDate } from "oslo";
|
import { isWithinExpirationDate } from "oslo";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { fromError } from "zod-validation-error";
|
|||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { response } from "@server/lib";
|
import { response } from "@server/lib";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { twoFactorBackupCodes, User, users } from "@server/db/schema";
|
import { twoFactorBackupCodes, User, users } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { alphabet, generateRandomString } from "oslo/crypto";
|
import { alphabet, generateRandomString } from "oslo/crypto";
|
||||||
import { hashPassword } from "@server/auth/password";
|
import { hashPassword } from "@server/auth/password";
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import createHttpError from "http-errors";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { resourceAccessToken, resources, sessions } from "@server/db/schema";
|
import { resourceAccessToken, resources, sessions } from "@server/db/schemas";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
userOrgs,
|
userOrgs,
|
||||||
userResources,
|
userResources,
|
||||||
users
|
users
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
import { isIpInCidr } from "@server/lib/ip";
|
import { isIpInCidr } from "@server/lib/ip";
|
||||||
import { response } from "@server/lib/response";
|
import { response } from "@server/lib/response";
|
||||||
@@ -41,12 +41,13 @@ const cache = new NodeCache({
|
|||||||
|
|
||||||
const verifyResourceSessionSchema = z.object({
|
const verifyResourceSessionSchema = z.object({
|
||||||
sessions: z.record(z.string()).optional(),
|
sessions: z.record(z.string()).optional(),
|
||||||
|
headers: z.record(z.string()).optional(),
|
||||||
|
query: z.record(z.string()).optional(),
|
||||||
originalRequestURL: z.string().url(),
|
originalRequestURL: z.string().url(),
|
||||||
scheme: z.string(),
|
scheme: z.string(),
|
||||||
host: z.string(),
|
host: z.string(),
|
||||||
path: z.string(),
|
path: z.string(),
|
||||||
method: z.string(),
|
method: z.string(),
|
||||||
accessToken: z.string().optional(),
|
|
||||||
tls: z.boolean(),
|
tls: z.boolean(),
|
||||||
requestIp: z.string().optional()
|
requestIp: z.string().optional()
|
||||||
});
|
});
|
||||||
@@ -85,7 +86,8 @@ export async function verifyResourceSession(
|
|||||||
originalRequestURL,
|
originalRequestURL,
|
||||||
requestIp,
|
requestIp,
|
||||||
path,
|
path,
|
||||||
accessToken: token
|
headers,
|
||||||
|
query
|
||||||
} = parsedBody.data;
|
} = parsedBody.data;
|
||||||
|
|
||||||
const clientIp = requestIp?.split(":")[0];
|
const clientIp = requestIp?.split(":")[0];
|
||||||
@@ -183,12 +185,33 @@ export async function verifyResourceSession(
|
|||||||
resource.resourceId
|
resource.resourceId
|
||||||
)}?redirect=${encodeURIComponent(originalRequestURL)}`;
|
)}?redirect=${encodeURIComponent(originalRequestURL)}`;
|
||||||
|
|
||||||
// check for access token
|
// check for access token in headers
|
||||||
let validAccessToken: ResourceAccessToken | undefined;
|
if (
|
||||||
if (token) {
|
headers &&
|
||||||
const [accessTokenId, accessToken] = token.split(".");
|
headers[
|
||||||
|
config.getRawConfig().server.resource_access_token_headers.id
|
||||||
|
] &&
|
||||||
|
headers[
|
||||||
|
config.getRawConfig().server.resource_access_token_headers.token
|
||||||
|
]
|
||||||
|
) {
|
||||||
|
const accessTokenId =
|
||||||
|
headers[
|
||||||
|
config.getRawConfig().server.resource_access_token_headers
|
||||||
|
.id
|
||||||
|
];
|
||||||
|
const accessToken =
|
||||||
|
headers[
|
||||||
|
config.getRawConfig().server.resource_access_token_headers
|
||||||
|
.token
|
||||||
|
];
|
||||||
|
|
||||||
const { valid, error, tokenItem } = await verifyResourceAccessToken(
|
const { valid, error, tokenItem } = await verifyResourceAccessToken(
|
||||||
{ resource, accessTokenId, accessToken }
|
{
|
||||||
|
accessToken,
|
||||||
|
accessTokenId,
|
||||||
|
resourceId: resource.resourceId
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -206,16 +229,44 @@ export async function verifyResourceSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (valid && tokenItem) {
|
if (valid && tokenItem) {
|
||||||
validAccessToken = tokenItem;
|
return allowed(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!sessions) {
|
if (
|
||||||
return await createAccessTokenSession(
|
query &&
|
||||||
res,
|
query[config.getRawConfig().server.resource_access_token_param]
|
||||||
resource,
|
) {
|
||||||
tokenItem
|
const token =
|
||||||
|
query[config.getRawConfig().server.resource_access_token_param];
|
||||||
|
|
||||||
|
const [accessTokenId, accessToken] = token.split(".");
|
||||||
|
|
||||||
|
const { valid, error, tokenItem } = await verifyResourceAccessToken(
|
||||||
|
{
|
||||||
|
accessToken,
|
||||||
|
accessTokenId,
|
||||||
|
resourceId: resource.resourceId
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
logger.debug("Access token invalid: " + error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valid) {
|
||||||
|
if (config.getRawConfig().app.log_failed_attempts) {
|
||||||
|
logger.info(
|
||||||
|
`Resource access token is invalid. Resource ID: ${
|
||||||
|
resource.resourceId
|
||||||
|
}. IP: ${clientIp}.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (valid && tokenItem) {
|
||||||
|
return allowed(res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sessions) {
|
if (!sessions) {
|
||||||
@@ -229,12 +280,10 @@ export async function verifyResourceSession(
|
|||||||
return notAllowed(res);
|
return notAllowed(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const resourceSessionToken =
|
const resourceSessionToken = extractResourceSessionToken(
|
||||||
sessions[
|
sessions,
|
||||||
`${config.getRawConfig().server.session_cookie_name}${
|
resource.ssl
|
||||||
resource.ssl ? "_s" : ""
|
);
|
||||||
}`
|
|
||||||
];
|
|
||||||
|
|
||||||
if (resourceSessionToken) {
|
if (resourceSessionToken) {
|
||||||
const sessionCacheKey = `session:${resourceSessionToken}`;
|
const sessionCacheKey = `session:${resourceSessionToken}`;
|
||||||
@@ -323,16 +372,6 @@ export async function verifyResourceSession(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point we have checked all sessions, but since the access token is
|
|
||||||
// valid, we should allow access and create a new session.
|
|
||||||
if (validAccessToken) {
|
|
||||||
return await createAccessTokenSession(
|
|
||||||
res,
|
|
||||||
resource,
|
|
||||||
validAccessToken
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug("No more auth to check, resource not allowed");
|
logger.debug("No more auth to check, resource not allowed");
|
||||||
|
|
||||||
if (config.getRawConfig().app.log_failed_attempts) {
|
if (config.getRawConfig().app.log_failed_attempts) {
|
||||||
@@ -354,6 +393,49 @@ export async function verifyResourceSession(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractResourceSessionToken(
|
||||||
|
sessions: Record<string, string>,
|
||||||
|
ssl: boolean
|
||||||
|
) {
|
||||||
|
const prefix = `${config.getRawConfig().server.session_cookie_name}${
|
||||||
|
ssl ? "_s" : ""
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const all: { cookieName: string; token: string; priority: number }[] = [];
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(sessions)) {
|
||||||
|
const parts = key.split(".");
|
||||||
|
const timestamp = parts[parts.length - 1];
|
||||||
|
|
||||||
|
// check if string is only numbers
|
||||||
|
if (!/^\d+$/.test(timestamp)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cookie name is the key without the timestamp
|
||||||
|
const cookieName = key.slice(0, -timestamp.length - 1);
|
||||||
|
|
||||||
|
if (cookieName === prefix) {
|
||||||
|
all.push({
|
||||||
|
cookieName,
|
||||||
|
token: value,
|
||||||
|
priority: parseInt(timestamp)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort by priority in desc order
|
||||||
|
all.sort((a, b) => b.priority - a.priority);
|
||||||
|
|
||||||
|
const latest = all[0];
|
||||||
|
|
||||||
|
if (!latest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return latest.token;
|
||||||
|
}
|
||||||
|
|
||||||
function notAllowed(res: Response, redirectUrl?: string) {
|
function notAllowed(res: Response, redirectUrl?: string) {
|
||||||
const data = {
|
const data = {
|
||||||
data: { valid: false, redirectUrl },
|
data: { valid: false, redirectUrl },
|
||||||
@@ -612,21 +694,21 @@ export function isPathAllowed(pattern: string, path: string): boolean {
|
|||||||
logger.debug(
|
logger.debug(
|
||||||
`${indent}Found in-segment wildcard in "${currentPatternPart}"`
|
`${indent}Found in-segment wildcard in "${currentPatternPart}"`
|
||||||
);
|
);
|
||||||
|
|
||||||
// Convert the pattern segment to a regex pattern
|
// Convert the pattern segment to a regex pattern
|
||||||
const regexPattern = currentPatternPart
|
const regexPattern = currentPatternPart
|
||||||
.replace(/\*/g, ".*") // Replace * with .* for regex wildcard
|
.replace(/\*/g, ".*") // Replace * with .* for regex wildcard
|
||||||
.replace(/\?/g, "."); // Replace ? with . for single character wildcard if needed
|
.replace(/\?/g, "."); // Replace ? with . for single character wildcard if needed
|
||||||
|
|
||||||
const regex = new RegExp(`^${regexPattern}$`);
|
const regex = new RegExp(`^${regexPattern}$`);
|
||||||
|
|
||||||
if (regex.test(currentPathPart)) {
|
if (regex.test(currentPathPart)) {
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`${indent}Segment with wildcard matches: "${currentPatternPart}" matches "${currentPathPart}"`
|
`${indent}Segment with wildcard matches: "${currentPatternPart}" matches "${currentPathPart}"`
|
||||||
);
|
);
|
||||||
return matchSegments(patternIndex + 1, pathIndex + 1);
|
return matchSegments(patternIndex + 1, pathIndex + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`${indent}Segment with wildcard mismatch: "${currentPatternPart}" doesn't match "${currentPathPart}"`
|
`${indent}Segment with wildcard mismatch: "${currentPatternPart}" doesn't match "${currentPathPart}"`
|
||||||
);
|
);
|
||||||
@@ -651,4 +733,4 @@ export function isPathAllowed(pattern: string, path: string): boolean {
|
|||||||
const result = matchSegments(0, 0);
|
const result = matchSegments(0, 0);
|
||||||
logger.debug(`Final result: ${result}`);
|
logger.debug(`Final result: ${result}`);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { domains, orgDomains, users } from "@server/db/schema";
|
import { domains, orgDomains, users } from "@server/db/schemas";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
@@ -80,15 +80,15 @@ export async function listDomains(
|
|||||||
|
|
||||||
const { orgId } = parsedParams.data;
|
const { orgId } = parsedParams.data;
|
||||||
|
|
||||||
const domains = await queryDomains(orgId.toString(), limit, offset);
|
const domainsList = await queryDomains(orgId.toString(), limit, offset);
|
||||||
|
|
||||||
const [{ count }] = await db
|
const [{ count }] = await db
|
||||||
.select({ count: sql<number>`count(*)` })
|
.select({ count: sql<number>`count(*)` })
|
||||||
.from(users);
|
.from(domains);
|
||||||
|
|
||||||
return response<ListDomainsResponse>(res, {
|
return response<ListDomainsResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
domains,
|
domains: domainsList,
|
||||||
pagination: {
|
pagination: {
|
||||||
total: count,
|
total: count,
|
||||||
limit,
|
limit,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import * as target from "./target";
|
|||||||
import * as user from "./user";
|
import * as user from "./user";
|
||||||
import * as auth from "./auth";
|
import * as auth from "./auth";
|
||||||
import * as role from "./role";
|
import * as role from "./role";
|
||||||
|
import * as supporterKey from "./supporterKey";
|
||||||
import * as accessToken from "./accessToken";
|
import * as accessToken from "./accessToken";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import {
|
import {
|
||||||
@@ -22,7 +23,8 @@ import {
|
|||||||
verifyRoleAccess,
|
verifyRoleAccess,
|
||||||
verifySetResourceUsers,
|
verifySetResourceUsers,
|
||||||
verifyUserAccess,
|
verifyUserAccess,
|
||||||
getUserOrgs
|
getUserOrgs,
|
||||||
|
verifyUserIsServerAdmin
|
||||||
} from "@server/middlewares";
|
} from "@server/middlewares";
|
||||||
import { verifyUserHasAction } from "../middlewares/verifyUserHasAction";
|
import { verifyUserHasAction } from "../middlewares/verifyUserHasAction";
|
||||||
import { ActionsEnum } from "@server/auth/actions";
|
import { ActionsEnum } from "@server/auth/actions";
|
||||||
@@ -239,7 +241,6 @@ authenticated.delete(
|
|||||||
target.deleteTarget
|
target.deleteTarget
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
authenticated.put(
|
authenticated.put(
|
||||||
"/org/:orgId/role",
|
"/org/:orgId/role",
|
||||||
verifyOrgAccess,
|
verifyOrgAccess,
|
||||||
@@ -382,6 +383,12 @@ authenticated.get(
|
|||||||
|
|
||||||
authenticated.get(`/org/:orgId/overview`, verifyOrgAccess, org.getOrgOverview);
|
authenticated.get(`/org/:orgId/overview`, verifyOrgAccess, org.getOrgOverview);
|
||||||
|
|
||||||
|
authenticated.post(
|
||||||
|
`/supporter-key/validate`,
|
||||||
|
supporterKey.validateSupporterKey
|
||||||
|
);
|
||||||
|
authenticated.post(`/supporter-key/hide`, supporterKey.hideSupporterKey);
|
||||||
|
|
||||||
unauthenticated.get("/resource/:resourceId/auth", resource.getResourceAuthInfo);
|
unauthenticated.get("/resource/:resourceId/auth", resource.getResourceAuthInfo);
|
||||||
|
|
||||||
// authenticated.get(
|
// authenticated.get(
|
||||||
@@ -415,6 +422,13 @@ unauthenticated.get("/resource/:resourceId/auth", resource.getResourceAuthInfo);
|
|||||||
|
|
||||||
unauthenticated.get("/user", verifySessionMiddleware, user.getUser);
|
unauthenticated.get("/user", verifySessionMiddleware, user.getUser);
|
||||||
|
|
||||||
|
authenticated.get("/users", verifyUserIsServerAdmin, user.adminListUsers);
|
||||||
|
authenticated.delete(
|
||||||
|
"/user/:userId",
|
||||||
|
verifyUserIsServerAdmin,
|
||||||
|
user.adminRemoveUser
|
||||||
|
);
|
||||||
|
|
||||||
authenticated.get("/org/:orgId/user/:userId", verifyOrgAccess, user.getOrgUser);
|
authenticated.get("/org/:orgId/user/:userId", verifyOrgAccess, user.getOrgUser);
|
||||||
authenticated.get(
|
authenticated.get(
|
||||||
"/org/:orgId/users",
|
"/org/:orgId/users",
|
||||||
@@ -459,7 +473,11 @@ authenticated.delete(
|
|||||||
// role.removeRoleAction
|
// role.removeRoleAction
|
||||||
// );
|
// );
|
||||||
|
|
||||||
authenticated.put("/newt", createNewt);
|
// authenticated.put(
|
||||||
|
// "/newt",
|
||||||
|
// verifyUserHasAction(ActionsEnum.createNewt),
|
||||||
|
// createNewt
|
||||||
|
// );
|
||||||
|
|
||||||
// Auth routes
|
// Auth routes
|
||||||
export const authRouter = Router();
|
export const authRouter = Router();
|
||||||
@@ -548,3 +566,8 @@ authRouter.post(
|
|||||||
"/resource/:resourceId/access-token",
|
"/resource/:resourceId/access-token",
|
||||||
resource.authWithAccessToken
|
resource.authWithAccessToken
|
||||||
);
|
);
|
||||||
|
|
||||||
|
authRouter.post(
|
||||||
|
"/access-token",
|
||||||
|
resource.authWithAccessToken
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from 'express';
|
import { Request, Response, NextFunction } from 'express';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { sites, resources, targets, exitNodes } from '@server/db/schema';
|
import { sites, resources, targets, exitNodes } from '@server/db/schemas';
|
||||||
import { db } from '@server/db';
|
import { db } from '@server/db';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import logger from '@server/logger';
|
import logger from '@server/logger';
|
||||||
import db from '@server/db';
|
import db from '@server/db';
|
||||||
import { exitNodes } from '@server/db/schema';
|
import { exitNodes } from '@server/db/schemas';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
|
|
||||||
export async function addPeer(exitNodeId: number, peer: {
|
export async function addPeer(exitNodeId: number, peer: {
|
||||||
@@ -52,4 +52,4 @@ export async function deletePeer(exitNodeId: number, publicKey: string) {
|
|||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { DrizzleError, eq } from "drizzle-orm";
|
import { DrizzleError, eq } from "drizzle-orm";
|
||||||
import { sites, resources, targets, exitNodes } from "@server/db/schema";
|
import { sites, resources, targets, exitNodes } from "@server/db/schemas";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
@@ -59,8 +59,8 @@ export const receiveBandwidth = async (
|
|||||||
await trx
|
await trx
|
||||||
.update(sites)
|
.update(sites)
|
||||||
.set({
|
.set({
|
||||||
megabytesOut: (site.megabytesIn || 0) + bytesIn,
|
megabytesOut: (site.megabytesOut || 0) + bytesIn,
|
||||||
megabytesIn: (site.megabytesOut || 0) + bytesOut,
|
megabytesIn: (site.megabytesIn || 0) + bytesOut,
|
||||||
lastBandwidthUpdate: new Date().toISOString(),
|
lastBandwidthUpdate: new Date().toISOString(),
|
||||||
online
|
online
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ import * as traefik from "@server/routers/traefik";
|
|||||||
import * as resource from "./resource";
|
import * as resource from "./resource";
|
||||||
import * as badger from "./badger";
|
import * as badger from "./badger";
|
||||||
import * as auth from "@server/routers/auth";
|
import * as auth from "@server/routers/auth";
|
||||||
|
import * as supporterKey from "@server/routers/supporterKey";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { verifyResourceAccess, verifySessionUserMiddleware } from "@server/middlewares";
|
import {
|
||||||
|
verifyResourceAccess,
|
||||||
|
verifySessionUserMiddleware
|
||||||
|
} from "@server/middlewares";
|
||||||
|
|
||||||
// Root routes
|
// Root routes
|
||||||
const internalRouter = Router();
|
const internalRouter = Router();
|
||||||
@@ -28,6 +32,11 @@ internalRouter.post(
|
|||||||
resource.getExchangeToken
|
resource.getExchangeToken
|
||||||
);
|
);
|
||||||
|
|
||||||
|
internalRouter.get(
|
||||||
|
`/supporter-key/visible`,
|
||||||
|
supporterKey.isSupporterKeyVisible
|
||||||
|
);
|
||||||
|
|
||||||
// Gerbil routes
|
// Gerbil routes
|
||||||
const gerbilRouter = Router();
|
const gerbilRouter = Router();
|
||||||
internalRouter.use("/gerbil", gerbilRouter);
|
internalRouter.use("/gerbil", gerbilRouter);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import db from "@server/db";
|
|||||||
import { hash } from "@node-rs/argon2";
|
import { hash } from "@node-rs/argon2";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { newts } from "@server/db/schema";
|
import { newts } from "@server/db/schemas";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import { SqliteError } from "better-sqlite3";
|
import { SqliteError } from "better-sqlite3";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateSessionToken } from "@server/auth/sessions/app";
|
import { generateSessionToken } from "@server/auth/sessions/app";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { newts } from "@server/db/schema";
|
import { newts } from "@server/db/schemas";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
sites,
|
sites,
|
||||||
Target,
|
Target,
|
||||||
targets
|
targets
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import { eq, and, sql, inArray } from "drizzle-orm";
|
import { eq, and, sql, inArray } from "drizzle-orm";
|
||||||
import { addPeer, deletePeer } from "../gerbil/peers";
|
import { addPeer, deletePeer } from "../gerbil/peers";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Target } from "@server/db/schema";
|
import { Target } from "@server/db/schemas";
|
||||||
import { sendToClient } from "../ws";
|
import { sendToClient } from "../ws";
|
||||||
|
|
||||||
export function addTargets(
|
export function addTargets(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { orgs } from "@server/db/schema";
|
import { orgs } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
roleActions,
|
roleActions,
|
||||||
roles,
|
roles,
|
||||||
userOrgs
|
userOrgs
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
@@ -27,7 +27,7 @@ const createOrgSchema = z
|
|||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
const MAX_ORGS = 5;
|
// const MAX_ORGS = 5;
|
||||||
|
|
||||||
export async function createOrg(
|
export async function createOrg(
|
||||||
req: Request,
|
req: Request,
|
||||||
@@ -57,15 +57,15 @@ export async function createOrg(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userOrgIds = req.userOrgIds;
|
// const userOrgIds = req.userOrgIds;
|
||||||
if (userOrgIds && userOrgIds.length > MAX_ORGS) {
|
// if (userOrgIds && userOrgIds.length > MAX_ORGS) {
|
||||||
return next(
|
// return next(
|
||||||
createHttpError(
|
// createHttpError(
|
||||||
HttpCode.FORBIDDEN,
|
// HttpCode.FORBIDDEN,
|
||||||
`Maximum number of organizations reached.`
|
// `Maximum number of organizations reached.`
|
||||||
)
|
// )
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
const { orgId, name } = parsedBody.data;
|
const { orgId, name } = parsedBody.data;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
orgs,
|
orgs,
|
||||||
sites,
|
sites,
|
||||||
userActions
|
userActions
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { Org, orgs } from "@server/db/schema";
|
import { Org, orgs } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
import { fromZodError } from "zod-validation-error";
|
||||||
|
|
||||||
const getOrgSchema = z
|
const getOrgSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -29,7 +30,7 @@ export async function getOrg(
|
|||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedParams.error.errors.map((e) => e.message).join(", ")
|
fromZodError(parsedParams.error)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ import {
|
|||||||
userResources,
|
userResources,
|
||||||
users,
|
users,
|
||||||
userSites
|
userSites
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import { and, count, eq, inArray } from "drizzle-orm";
|
import { and, count, eq, inArray } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
import { fromZodError } from "zod-validation-error";
|
||||||
|
|
||||||
const getOrgParamsSchema = z
|
const getOrgParamsSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -45,7 +46,7 @@ export async function getOrgOverview(
|
|||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedParams.error.errors.map((e) => e.message).join(", ")
|
fromZodError(parsedParams.error)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { Org, orgs } from "@server/db/schema";
|
import { Org, orgs } from "@server/db/schemas";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import { sql, inArray } from "drizzle-orm";
|
import { sql, inArray } from "drizzle-orm";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
|
import { fromZodError } from "zod-validation-error";
|
||||||
|
|
||||||
const listOrgsSchema = z.object({
|
const listOrgsSchema = z.object({
|
||||||
limit: z
|
limit: z
|
||||||
@@ -39,7 +40,7 @@ export async function listOrgs(
|
|||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedQuery.error.errors.map((e) => e.message).join(", ")
|
fromZodError(parsedQuery.error)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { orgs } from "@server/db/schema";
|
import { orgs } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateSessionToken } from "@server/auth/sessions/app";
|
import { generateSessionToken } from "@server/auth/sessions/app";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { resources } from "@server/db/schema";
|
import { Resource, resources } from "@server/db/schemas";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
@@ -10,13 +10,16 @@ import { z } from "zod";
|
|||||||
import { fromError } from "zod-validation-error";
|
import { fromError } from "zod-validation-error";
|
||||||
import { createResourceSession } from "@server/auth/sessions/resource";
|
import { createResourceSession } from "@server/auth/sessions/resource";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import { verifyResourceAccessToken } from "@server/auth/verifyResourceAccessToken";
|
import {
|
||||||
|
verifyResourceAccessToken
|
||||||
|
} from "@server/auth/verifyResourceAccessToken";
|
||||||
import config from "@server/lib/config";
|
import config from "@server/lib/config";
|
||||||
|
import stoi from "@server/lib/stoi";
|
||||||
|
|
||||||
const authWithAccessTokenBodySchema = z
|
const authWithAccessTokenBodySchema = z
|
||||||
.object({
|
.object({
|
||||||
accessToken: z.string(),
|
accessToken: z.string(),
|
||||||
accessTokenId: z.string()
|
accessTokenId: z.string().optional()
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
@@ -24,13 +27,15 @@ const authWithAccessTokenParamsSchema = z
|
|||||||
.object({
|
.object({
|
||||||
resourceId: z
|
resourceId: z
|
||||||
.string()
|
.string()
|
||||||
.transform(Number)
|
.optional()
|
||||||
.pipe(z.number().int().positive())
|
.transform(stoi)
|
||||||
|
.pipe(z.number().int().positive().optional())
|
||||||
})
|
})
|
||||||
.strict();
|
.strict();
|
||||||
|
|
||||||
export type AuthWithAccessTokenResponse = {
|
export type AuthWithAccessTokenResponse = {
|
||||||
session?: string;
|
session?: string;
|
||||||
|
redirectUrl?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function authWithAccessToken(
|
export async function authWithAccessToken(
|
||||||
@@ -64,23 +69,61 @@ export async function authWithAccessToken(
|
|||||||
const { accessToken, accessTokenId } = parsedBody.data;
|
const { accessToken, accessTokenId } = parsedBody.data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [resource] = await db
|
let valid;
|
||||||
.select()
|
let tokenItem;
|
||||||
.from(resources)
|
let error;
|
||||||
.where(eq(resources.resourceId, resourceId))
|
let resource: Resource | undefined;
|
||||||
.limit(1);
|
|
||||||
|
|
||||||
if (!resource) {
|
if (accessTokenId) {
|
||||||
return next(
|
if (!resourceId) {
|
||||||
createHttpError(HttpCode.NOT_FOUND, "Resource not found")
|
return next(
|
||||||
);
|
createHttpError(
|
||||||
|
HttpCode.BAD_REQUEST,
|
||||||
|
"Resource ID is required"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [foundResource] = await db
|
||||||
|
.select()
|
||||||
|
.from(resources)
|
||||||
|
.where(eq(resources.resourceId, resourceId))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (!foundResource) {
|
||||||
|
return next(
|
||||||
|
createHttpError(HttpCode.NOT_FOUND, "Resource not found")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await verifyResourceAccessToken({
|
||||||
|
accessTokenId,
|
||||||
|
accessToken
|
||||||
|
});
|
||||||
|
|
||||||
|
valid = res.valid;
|
||||||
|
tokenItem = res.tokenItem;
|
||||||
|
error = res.error;
|
||||||
|
resource = foundResource;
|
||||||
|
} else {
|
||||||
|
const res = await verifyResourceAccessToken({
|
||||||
|
accessToken
|
||||||
|
});
|
||||||
|
|
||||||
|
valid = res.valid;
|
||||||
|
tokenItem = res.tokenItem;
|
||||||
|
error = res.error;
|
||||||
|
resource = res.resource;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { valid, error, tokenItem } = await verifyResourceAccessToken({
|
if (!tokenItem || !resource) {
|
||||||
resource,
|
return next(
|
||||||
accessTokenId,
|
createHttpError(
|
||||||
accessToken
|
HttpCode.UNAUTHORIZED,
|
||||||
});
|
"Access token does not exist for resource"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
if (config.getRawConfig().app.log_failed_attempts) {
|
if (config.getRawConfig().app.log_failed_attempts) {
|
||||||
@@ -96,18 +139,9 @@ export async function authWithAccessToken(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tokenItem || !resource) {
|
|
||||||
return next(
|
|
||||||
createHttpError(
|
|
||||||
HttpCode.UNAUTHORIZED,
|
|
||||||
"Access token does not exist for resource"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = generateSessionToken();
|
const token = generateSessionToken();
|
||||||
await createResourceSession({
|
await createResourceSession({
|
||||||
resourceId,
|
resourceId: resource.resourceId,
|
||||||
token,
|
token,
|
||||||
accessTokenId: tokenItem.accessTokenId,
|
accessTokenId: tokenItem.accessTokenId,
|
||||||
isRequestToken: true,
|
isRequestToken: true,
|
||||||
@@ -118,7 +152,8 @@ export async function authWithAccessToken(
|
|||||||
|
|
||||||
return response<AuthWithAccessTokenResponse>(res, {
|
return response<AuthWithAccessTokenResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
session: token
|
session: token,
|
||||||
|
redirectUrl: `${resource.ssl ? "https" : "http"}://${resource.fullDomain}`
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
error: false,
|
error: false,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { verify } from "@node-rs/argon2";
|
import { verify } from "@node-rs/argon2";
|
||||||
import { generateSessionToken } from "@server/auth/sessions/app";
|
import { generateSessionToken } from "@server/auth/sessions/app";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { orgs, resourcePassword, resources } from "@server/db/schema";
|
import { orgs, resourcePassword, resources } from "@server/db/schemas";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { generateSessionToken } from "@server/auth/sessions/app";
|
import { generateSessionToken } from "@server/auth/sessions/app";
|
||||||
import db from "@server/db";
|
import db from "@server/db";
|
||||||
import { orgs, resourcePincode, resources } from "@server/db/schema";
|
import { orgs, resourcePincode, resources } from "@server/db/schemas";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
resourceOtp,
|
resourceOtp,
|
||||||
resources,
|
resources,
|
||||||
resourceWhitelist
|
resourceWhitelist
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { eq, and } from "drizzle-orm";
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
roleResources,
|
roleResources,
|
||||||
roles,
|
roles,
|
||||||
userResources
|
userResources
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resourceRules, resources } from "@server/db/schema";
|
import { resourceRules, resources } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { newts, resources, sites, targets } from "@server/db/schema";
|
import { newts, resources, sites, targets } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resourceRules, resources } from "@server/db/schema";
|
import { resourceRules, resources } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
@@ -68,4 +68,4 @@ export async function deleteResourceRule(
|
|||||||
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resources } from "@server/db/schema";
|
import { resources } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { createResourceSession } from "@server/auth/sessions/resource";
|
import { createResourceSession } from "@server/auth/sessions/resource";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { Resource, resources, sites } from "@server/db/schema";
|
import { Resource, resources, sites } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {
|
|||||||
resourcePassword,
|
resourcePassword,
|
||||||
resourcePincode,
|
resourcePincode,
|
||||||
resources
|
resources
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resourceWhitelist, users } from "@server/db/schema"; // Assuming these are the correct tables
|
import { resourceWhitelist, users } from "@server/db/schemas"; // Assuming these are the correct tables
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { roleResources, roles } from "@server/db/schema";
|
import { roleResources, roles } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resourceRules, resources } from "@server/db/schema";
|
import { resourceRules, resources } from "@server/db/schemas";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import { eq, sql } from "drizzle-orm";
|
import { eq, sql } from "drizzle-orm";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userResources, users } from "@server/db/schema"; // Assuming these are the correct tables
|
import { userResources, users } from "@server/db/schemas"; // Assuming these are the correct tables
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ import {
|
|||||||
roleResources,
|
roleResources,
|
||||||
resourcePassword,
|
resourcePassword,
|
||||||
resourcePincode
|
resourcePincode
|
||||||
} from "@server/db/schema";
|
} from "@server/db/schemas";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
import { sql, eq, or, inArray, and, count } from "drizzle-orm";
|
import { sql, eq, or, inArray, and, count } from "drizzle-orm";
|
||||||
import logger from "@server/logger";
|
import logger from "@server/logger";
|
||||||
import stoi from "@server/lib/stoi";
|
import stoi from "@server/lib/stoi";
|
||||||
|
import { fromZodError } from "zod-validation-error";
|
||||||
|
|
||||||
const listResourcesParamsSchema = z
|
const listResourcesParamsSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -66,7 +67,8 @@ function queryResources(
|
|||||||
whitelist: resources.emailWhitelistEnabled,
|
whitelist: resources.emailWhitelistEnabled,
|
||||||
http: resources.http,
|
http: resources.http,
|
||||||
protocol: resources.protocol,
|
protocol: resources.protocol,
|
||||||
proxyPort: resources.proxyPort
|
proxyPort: resources.proxyPort,
|
||||||
|
enabled: resources.enabled
|
||||||
})
|
})
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.leftJoin(sites, eq(resources.siteId, sites.siteId))
|
.leftJoin(sites, eq(resources.siteId, sites.siteId))
|
||||||
@@ -99,7 +101,8 @@ function queryResources(
|
|||||||
whitelist: resources.emailWhitelistEnabled,
|
whitelist: resources.emailWhitelistEnabled,
|
||||||
http: resources.http,
|
http: resources.http,
|
||||||
protocol: resources.protocol,
|
protocol: resources.protocol,
|
||||||
proxyPort: resources.proxyPort
|
proxyPort: resources.proxyPort,
|
||||||
|
enabled: resources.enabled
|
||||||
})
|
})
|
||||||
.from(resources)
|
.from(resources)
|
||||||
.leftJoin(sites, eq(resources.siteId, sites.siteId))
|
.leftJoin(sites, eq(resources.siteId, sites.siteId))
|
||||||
@@ -136,7 +139,7 @@ export async function listResources(
|
|||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedQuery.error.errors.map((e) => e.message).join(", ")
|
fromZodError(parsedQuery.error)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -147,7 +150,7 @@ export async function listResources(
|
|||||||
return next(
|
return next(
|
||||||
createHttpError(
|
createHttpError(
|
||||||
HttpCode.BAD_REQUEST,
|
HttpCode.BAD_REQUEST,
|
||||||
parsedParams.error.errors.map((e) => e.message).join(", ")
|
fromZodError(parsedParams.error)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resourcePassword } from "@server/db/schema";
|
import { resourcePassword } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resourcePincode } from "@server/db/schema";
|
import { resourcePincode } from "@server/db/schemas";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { roleResources, roles } from "@server/db/schema";
|
import { roleResources, roles } from "@server/db/schemas";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { userResources } from "@server/db/schema";
|
import { userResources } from "@server/db/schemas";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { db } from "@server/db";
|
import { db } from "@server/db";
|
||||||
import { resources, resourceWhitelist } from "@server/db/schema";
|
import { resources, resourceWhitelist } from "@server/db/schemas";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user