mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-05-25 13:40:14 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 40540ce133 |
@@ -101,10 +101,6 @@ TINYAUTH_OAUTH_PROVIDERS_name_CLIENTID=
|
|||||||
TINYAUTH_OAUTH_PROVIDERS_name_CLIENTSECRET=
|
TINYAUTH_OAUTH_PROVIDERS_name_CLIENTSECRET=
|
||||||
# Path to the file containing the OAuth client secret.
|
# Path to the file containing the OAuth client secret.
|
||||||
TINYAUTH_OAUTH_PROVIDERS_name_CLIENTSECRETFILE=
|
TINYAUTH_OAUTH_PROVIDERS_name_CLIENTSECRETFILE=
|
||||||
# Comma-separated list of allowed OAuth domains for this provider.
|
|
||||||
TINYAUTH_OAUTH_PROVIDERS_name_WHITELIST=
|
|
||||||
# Path to the OAuth whitelist file for this provider.
|
|
||||||
TINYAUTH_OAUTH_PROVIDERS_name_WHITELISTFILE=
|
|
||||||
# OAuth scopes.
|
# OAuth scopes.
|
||||||
TINYAUTH_OAUTH_PROVIDERS_name_SCOPES=
|
TINYAUTH_OAUTH_PROVIDERS_name_SCOPES=
|
||||||
# OAuth redirect URL.
|
# OAuth redirect URL.
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
run: go mod download
|
run: go mod download
|
||||||
|
|
||||||
- name: Setup sqlc
|
- name: Setup sqlc
|
||||||
uses: sqlc-dev/setup-sqlc@v5
|
uses: sqlc-dev/setup-sqlc@v4
|
||||||
with:
|
with:
|
||||||
sqlc-version: "1.31.1"
|
sqlc-version: "1.31.1"
|
||||||
|
|
||||||
@@ -62,6 +62,6 @@ jobs:
|
|||||||
run: go test -coverprofile=coverage.txt -v ./...
|
run: go test -coverprofile=coverage.txt -v ./...
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov
|
- name: Upload coverage reports to Codecov
|
||||||
uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6
|
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
@@ -224,7 +224,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
@@ -282,7 +282,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/arm64
|
platforms: linux/arm64
|
||||||
@@ -340,7 +340,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/arm64
|
platforms: linux/arm64
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
@@ -191,7 +191,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64
|
||||||
@@ -246,7 +246,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/arm64
|
platforms: linux/arm64
|
||||||
@@ -301,7 +301,7 @@ jobs:
|
|||||||
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
|
||||||
|
|
||||||
- name: Build and push
|
- name: Build and push
|
||||||
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
|
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
|
||||||
id: build
|
id: build
|
||||||
with:
|
with:
|
||||||
platforms: linux/arm64
|
platforms: linux/arm64
|
||||||
|
|||||||
@@ -38,6 +38,6 @@ jobs:
|
|||||||
retention-days: 5
|
retention-days: 5
|
||||||
|
|
||||||
- name: Upload to code-scanning
|
- name: Upload to code-scanning
|
||||||
uses: github/codeql-action/upload-sarif@9e0d7b8d25671d64c341c19c0152d693099fb5ba # v4
|
uses: github/codeql-action/upload-sarif@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# Site builder
|
# Site builder
|
||||||
FROM node:26.2-alpine3.23 AS frontend-builder
|
FROM node:26.1-alpine3.23 AS frontend-builder
|
||||||
|
|
||||||
WORKDIR /frontend
|
WORKDIR /frontend
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Site builder
|
# Site builder
|
||||||
FROM node:26.2-alpine3.23 AS frontend-builder
|
FROM node:26.1-alpine3.23 AS frontend-builder
|
||||||
|
|
||||||
WORKDIR /frontend
|
WORKDIR /frontend
|
||||||
|
|
||||||
|
|||||||
@@ -58,8 +58,8 @@
|
|||||||
"invalidInput": "Input non valido",
|
"invalidInput": "Input non valido",
|
||||||
"domainWarningTitle": "Dominio non valido",
|
"domainWarningTitle": "Dominio non valido",
|
||||||
"domainWarningSubtitle": "Stai accedendo a questa istanza da un dominio errato. Scegliendo di procedere, potresti incontrare problemi con l'autenticazione.",
|
"domainWarningSubtitle": "Stai accedendo a questa istanza da un dominio errato. Scegliendo di procedere, potresti incontrare problemi con l'autenticazione.",
|
||||||
"domainWarningCurrent": "Attuale:",
|
"domainWarningCurrent": "Current:",
|
||||||
"domainWarningExpected": "Previsto:",
|
"domainWarningExpected": "Expected:",
|
||||||
"ignoreTitle": "Ignora",
|
"ignoreTitle": "Ignora",
|
||||||
"goToCorrectDomainTitle": "Vai al dominio corretto",
|
"goToCorrectDomainTitle": "Vai al dominio corretto",
|
||||||
"authorizeTitle": "Autorizza",
|
"authorizeTitle": "Autorizza",
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
"fieldRequired": "Ово поље је неопходно",
|
"fieldRequired": "Ово поље је неопходно",
|
||||||
"invalidInput": "Неисправан унос",
|
"invalidInput": "Неисправан унос",
|
||||||
"domainWarningTitle": "Неисправан домен",
|
"domainWarningTitle": "Неисправан домен",
|
||||||
"domainWarningSubtitle": "Приступате овој инстанци са неисправног домена. Ако наставите, можете наићи на проблеме са аутентификацијом.",
|
"domainWarningSubtitle": "You are accessing this instance from an incorrect domain. If you proceed, you may encounter issues with authentication.",
|
||||||
"domainWarningCurrent": "Тренутни:",
|
"domainWarningCurrent": "Тренутни:",
|
||||||
"domainWarningExpected": "Очекивани:",
|
"domainWarningExpected": "Очекивани:",
|
||||||
"ignoreTitle": "Игнориши",
|
"ignoreTitle": "Игнориши",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module github.com/tinyauthapp/tinyauth
|
module github.com/tinyauthapp/tinyauth
|
||||||
|
|
||||||
go 1.26.3
|
go 1.26.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
charm.land/huh/v2 v2.0.3
|
charm.land/huh/v2 v2.0.3
|
||||||
@@ -18,13 +18,13 @@ require (
|
|||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/tinyauthapp/paerser v0.0.0-20260410140347-85c3740d6298
|
github.com/tinyauthapp/paerser v0.0.0-20260410140347-85c3740d6298
|
||||||
github.com/weppos/publicsuffix-go v0.50.3
|
github.com/weppos/publicsuffix-go v0.50.3
|
||||||
golang.org/x/crypto v0.52.0
|
golang.org/x/crypto v0.50.0
|
||||||
golang.org/x/oauth2 v0.36.0
|
golang.org/x/oauth2 v0.36.0
|
||||||
golang.org/x/tools v0.44.0
|
golang.org/x/tools v0.43.0
|
||||||
k8s.io/apimachinery v0.36.1
|
k8s.io/apimachinery v0.36.0
|
||||||
k8s.io/client-go v0.36.1
|
k8s.io/client-go v0.36.0
|
||||||
modernc.org/sqlite v1.50.1
|
modernc.org/sqlite v1.50.0
|
||||||
tailscale.com v1.98.3
|
tailscale.com v1.96.5
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -42,6 +42,19 @@ require (
|
|||||||
github.com/akutz/memconn v0.1.0 // indirect
|
github.com/akutz/memconn v0.1.0 // indirect
|
||||||
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e // indirect
|
github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e // indirect
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.41.0 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.29.5 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.58 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.27 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.24.14 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.13 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect
|
||||||
|
github.com/aws/smithy-go v1.24.0 // indirect
|
||||||
github.com/boombuler/barcode v1.0.2 // indirect
|
github.com/boombuler/barcode v1.0.2 // indirect
|
||||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||||
github.com/bytedance/sonic v1.15.0 // indirect
|
github.com/bytedance/sonic v1.15.0 // indirect
|
||||||
@@ -66,7 +79,7 @@ require (
|
|||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
|
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
|
||||||
github.com/distribution/reference v0.6.0 // indirect
|
github.com/distribution/reference v0.6.0 // indirect
|
||||||
github.com/docker/go-connections v0.6.0 // indirect
|
github.com/docker/go-connections v0.5.0 // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
@@ -90,9 +103,14 @@ require (
|
|||||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||||
github.com/huandu/xstrings v1.5.0 // indirect
|
github.com/huandu/xstrings v1.5.0 // indirect
|
||||||
github.com/huin/goupnp v1.3.0 // indirect
|
github.com/huin/goupnp v1.3.0 // indirect
|
||||||
|
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa // indirect
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
|
github.com/jackc/pgx/v5 v5.9.2 // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
|
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.18.5 // indirect
|
github.com/klauspost/compress v1.18.2 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
@@ -119,6 +137,7 @@ require (
|
|||||||
github.com/pires/go-proxyproto v0.8.1 // indirect
|
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
|
github.com/prometheus-community/pro-bing v0.4.0 // indirect
|
||||||
github.com/quic-go/qpack v0.6.0 // indirect
|
github.com/quic-go/qpack v0.6.0 // indirect
|
||||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
@@ -126,12 +145,12 @@ require (
|
|||||||
github.com/safchain/ethtool v0.3.0 // indirect
|
github.com/safchain/ethtool v0.3.0 // indirect
|
||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
github.com/shopspring/decimal v1.4.0 // indirect
|
||||||
github.com/spf13/cast v1.10.0 // indirect
|
github.com/spf13/cast v1.10.0 // indirect
|
||||||
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d // indirect
|
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e // indirect
|
||||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
|
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
|
||||||
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd // indirect
|
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
|
||||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
|
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
|
||||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
|
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20260427181203-e3ac4a0afb4e // indirect
|
github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
github.com/x448/float16 v0.8.4 // indirect
|
||||||
@@ -150,12 +169,12 @@ require (
|
|||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
|
||||||
golang.org/x/arch v0.22.0 // indirect
|
golang.org/x/arch v0.22.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||||
golang.org/x/mod v0.35.0 // indirect
|
golang.org/x/mod v0.34.0 // indirect
|
||||||
golang.org/x/net v0.54.0 // indirect
|
golang.org/x/net v0.52.0 // indirect
|
||||||
golang.org/x/sync v0.20.0 // indirect
|
golang.org/x/sync v0.20.0 // indirect
|
||||||
golang.org/x/sys v0.45.0 // indirect
|
golang.org/x/sys v0.43.0 // indirect
|
||||||
golang.org/x/term v0.43.0 // indirect
|
golang.org/x/term v0.42.0 // indirect
|
||||||
golang.org/x/text v0.37.0 // indirect
|
golang.org/x/text v0.36.0 // indirect
|
||||||
golang.org/x/time v0.14.0 // indirect
|
golang.org/x/time v0.14.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
golang.zx2c4.com/wireguard/windows v0.5.3 // indirect
|
||||||
@@ -167,7 +186,7 @@ require (
|
|||||||
k8s.io/klog/v2 v2.140.0 // indirect
|
k8s.io/klog/v2 v2.140.0 // indirect
|
||||||
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
|
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
|
||||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
|
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
|
||||||
modernc.org/libc v1.72.3 // indirect
|
modernc.org/libc v1.72.0 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
modernc.org/memory v1.11.0 // indirect
|
modernc.org/memory v1.11.0 // indirect
|
||||||
rsc.io/qr v0.2.0 // indirect
|
rsc.io/qr v0.2.0 // indirect
|
||||||
|
|||||||
@@ -151,8 +151,8 @@ github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
|||||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||||
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||||
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
@@ -251,6 +251,16 @@ github.com/illarion/gonotify/v3 v3.0.2 h1:O7S6vcopHexutmpObkeWsnzMJt/r1hONIEogeV
|
|||||||
github.com/illarion/gonotify/v3 v3.0.2/go.mod h1:HWGPdPe817GfvY3w7cx6zkbzNZfi3QjcBm/wgVvEL1U=
|
github.com/illarion/gonotify/v3 v3.0.2/go.mod h1:HWGPdPe817GfvY3w7cx6zkbzNZfi3QjcBm/wgVvEL1U=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
|
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
|
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
|
||||||
|
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa h1:s+4MhCQ6YrzisK6hFJUX53drDT4UsSW3DEhKn0ifuHw=
|
||||||
|
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
|
github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
|
||||||
|
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
|
||||||
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
|
||||||
@@ -273,8 +283,8 @@ github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUF
|
|||||||
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
|
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||||
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ=
|
github.com/kortschak/wol v0.0.0-20200729010619-da482cc4850a h1:+RR6SqnTkDLWyICxS1xpjCi/3dhyV+TgZwA6Ww3KncQ=
|
||||||
@@ -364,6 +374,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
|||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
||||||
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
|
github.com/prometheus-community/pro-bing v0.4.0 h1:YMbv+i08gQz97OZZBwLyvmmQEEzyfyrrjEaAchdy3R4=
|
||||||
|
github.com/prometheus-community/pro-bing v0.4.0/go.mod h1:b7wRYZtCcPmt4Sz319BykUU241rWLe1VFXyiyWK/dH4=
|
||||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||||
@@ -384,34 +396,33 @@ github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP
|
|||||||
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
|
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
|
||||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
||||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d h1:JcGKBZAL7ePLwOhUdN8qGQZlP5GueEiIZwY7R62pejE=
|
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e h1:PtWT87weP5LWHEY//SWsYkSO3RWRZo4OSWagh3YD2vQ=
|
||||||
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
|
github.com/tailscale/certstore v0.1.1-0.20231202035212-d3fa0460f47e/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
|
||||||
github.com/tailscale/gliderssh v0.3.4-0.20260330083525-c1389c70ff89 h1:glgVc1ZYMjwN1Q/ITWeuSQyl029uayagaR2sjsifehc=
|
|
||||||
github.com/tailscale/gliderssh v0.3.4-0.20260330083525-c1389c70ff89/go.mod h1:wn16Km1EZOX4UEAyaZa3dBwfFGOJ7neck40NcwosJUw=
|
|
||||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
|
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
|
||||||
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
|
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
|
||||||
github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869 h1:SRL6irQkKGQKKLzvQP/ke/2ZuB7Py5+XuqtOgSj+iMM=
|
github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869 h1:SRL6irQkKGQKKLzvQP/ke/2ZuB7Py5+XuqtOgSj+iMM=
|
||||||
github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ=
|
github.com/tailscale/golang-x-crypto v0.0.0-20250404221719-a5573b049869/go.mod h1:ikbF+YT089eInTp9f2vmvy4+ZVnW5hzX1q2WknxSprQ=
|
||||||
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd h1:Rf9uhF1+VJ7ZHqxrG8pJ6YacmHvVCmByDmGbAWCc/gA=
|
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
|
||||||
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo=
|
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
|
||||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU=
|
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7 h1:uFsXVBE9Qr4ZoF094vE6iYTLDl0qCiKzYXlL6UeWObU=
|
||||||
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
|
github.com/tailscale/netlink v1.1.1-0.20240822203006-4d49adab4de7/go.mod h1:NzVQi3Mleb+qzq8VmcWpSkcSYxXIg0DkI6XDzpVkhJ0=
|
||||||
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA=
|
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA=
|
||||||
@@ -420,8 +431,8 @@ github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 h1:U
|
|||||||
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
|
github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976/go.mod h1:agQPE6y6ldqCOui2gkIh7ZMztTkIQKH049tv8siLuNQ=
|
||||||
github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6 h1:l10Gi6w9jxvinoiq15g8OToDdASBni4CyJOdHY1Hr8M=
|
github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6 h1:l10Gi6w9jxvinoiq15g8OToDdASBni4CyJOdHY1Hr8M=
|
||||||
github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6/go.mod h1:ZXRML051h7o4OcI0d3AaILDIad/Xw0IkXaHM17dic1Y=
|
github.com/tailscale/wf v0.0.0-20240214030419-6fbb0a674ee6/go.mod h1:ZXRML051h7o4OcI0d3AaILDIad/Xw0IkXaHM17dic1Y=
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20260427181203-e3ac4a0afb4e h1:GexFR7ak1iz26fxg8HWCpOEqAOL8UEZJ7J3JxeCalDs=
|
github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da h1:jVRUZPRs9sqyKlYHHzHjAqKN+6e/Vog6NpHYeNPJqOw=
|
||||||
github.com/tailscale/wireguard-go v0.0.0-20260427181203-e3ac4a0afb4e/go.mod h1:6SerzcvHWQchKO2BfNdmquA77CHSECZuFl+D9fp4RnI=
|
github.com/tailscale/wireguard-go v0.0.0-20250716170648-1d0488a3d7da/go.mod h1:BOm5fXUBFM+m9woLNBoxI9TaBXXhGNP50LX/TGIvGb4=
|
||||||
github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e h1:zOGKqN5D5hHhiYUp091JqK7DPCqSARyUfduhGUY8Bek=
|
github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e h1:zOGKqN5D5hHhiYUp091JqK7DPCqSARyUfduhGUY8Bek=
|
||||||
github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg=
|
github.com/tailscale/xnet v0.0.0-20240729143630-8497ac4dab2e/go.mod h1:orPd6JZXXRyuDusYilywte7k094d7dycXXU5YnWsrwg=
|
||||||
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
||||||
@@ -478,18 +489,18 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
|
|||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
|
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
|
||||||
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8=
|
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||||
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
|
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
|
||||||
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
|
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
|
||||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@@ -497,16 +508,16 @@ golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
|||||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
|
||||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
|
||||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||||
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
|
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||||
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
|
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
golang.zx2c4.com/wireguard/windows v0.5.3 h1:On6j2Rpn3OEMXqBq00QEDC7bWSZrPIHKIus8eIuExIE=
|
||||||
@@ -534,26 +545,26 @@ gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
|||||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||||
gvisor.dev/gvisor v0.0.0-20260224225140-573d5e7127a8 h1:Zy8IV/+FMLxy6j6p87vk/vQGKcdnbprwjTxc8UiUtsA=
|
gvisor.dev/gvisor v0.0.0-20260224225140-573d5e7127a8 h1:Zy8IV/+FMLxy6j6p87vk/vQGKcdnbprwjTxc8UiUtsA=
|
||||||
gvisor.dev/gvisor v0.0.0-20260224225140-573d5e7127a8/go.mod h1:QkHjoMIBaYtpVufgwv3keYAbln78mBoCuShZrPrer1Q=
|
gvisor.dev/gvisor v0.0.0-20260224225140-573d5e7127a8/go.mod h1:QkHjoMIBaYtpVufgwv3keYAbln78mBoCuShZrPrer1Q=
|
||||||
honnef.co/go/tools v0.7.0 h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU=
|
honnef.co/go/tools v0.7.0-0.dev.0.20251022135355-8273271481d0 h1:5SXjd4ET5dYijLaf0O3aOenC0Z4ZafIWSpjUzsQaNho=
|
||||||
honnef.co/go/tools v0.7.0/go.mod h1:pm29oPxeP3P82ISxZDgIYeOaf9ta6Pi0EWvCFoLG2vc=
|
honnef.co/go/tools v0.7.0-0.dev.0.20251022135355-8273271481d0/go.mod h1:EPDDhEZqVHhWuPI5zPAsjU0U7v9xNIWjoOVyZ5ZcniQ=
|
||||||
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
||||||
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||||
k8s.io/api v0.36.1 h1:XbL/EMj8K2aJpJtePmqUyQMsM0D4QI2pvl7YKJ20FTY=
|
k8s.io/api v0.36.0 h1:SgqDhZzHdOtMk40xVSvCXkP9ME0H05hPM3p9AB1kL80=
|
||||||
k8s.io/api v0.36.1/go.mod h1:KOWo4ey3TINlXjeHVuwB3i+tXXnu+UcwFBHlI/9dvEo=
|
k8s.io/api v0.36.0/go.mod h1:m1LVrGPNYax5NBHdO+QuAedXyuzTt4RryI/qnmNvs34=
|
||||||
k8s.io/apimachinery v0.36.1 h1:G63Gjx2W+q0YD+72Vo8oY0nDnePVwnuzTmmy5ENrVSA=
|
k8s.io/apimachinery v0.36.0 h1:jZyPzhd5Z+3h9vJLt0z9XdzW9VzNzWAUw+P1xZ9PXtQ=
|
||||||
k8s.io/apimachinery v0.36.1/go.mod h1:ibYOR00vW/I1kzvi5SF0dRuJ52BvKtfvRdOn35GPQ+8=
|
k8s.io/apimachinery v0.36.0/go.mod h1:FklypaRJt6n5wUIwWXIP6GJlIpUizTgfo1T/As+Tyxc=
|
||||||
k8s.io/client-go v0.36.1 h1:FN/K8QIT2CEDt+2WB2HnWrUANZ50AP5GII43/SP2JR0=
|
k8s.io/client-go v0.36.0 h1:pOYi7C4RHChYjMiHpZSpSbIM6ZxVbRXBy7CuiIwqA3c=
|
||||||
k8s.io/client-go v0.36.1/go.mod h1:s6rAnCtTGYDQnpNjEhSaISV+2O8jwruZ6m3QOYBFbtU=
|
k8s.io/client-go v0.36.0/go.mod h1:ZKKcpwF0aLYfkHFCjillCKaTK/yBkEDHTDXCFY6AS9Y=
|
||||||
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
|
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
|
||||||
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
|
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
|
||||||
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg=
|
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg=
|
||||||
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0=
|
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0=
|
||||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
|
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
|
||||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
||||||
modernc.org/cc/v4 v4.28.2 h1:3tQ0lf2ADtoby2EtSP+J7IE2SHwEJdP8ioR59wx7XpY=
|
modernc.org/cc/v4 v4.27.3 h1:uNCgn37E5U09mTv1XgskEVUJ8ADKpmFMPxzGJ0TSo+U=
|
||||||
modernc.org/cc/v4 v4.28.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
modernc.org/cc/v4 v4.27.3/go.mod h1:3YjcbCqhoTTHPycJDRl2WZKKFj0nwcOIPBfEZK0Hdk8=
|
||||||
modernc.org/ccgo/v4 v4.34.0 h1:yRLPFZieg532OT4rp4JFNIVcquwalMX26G95WQDqwCQ=
|
modernc.org/ccgo/v4 v4.32.4 h1:L5OB8rpEX4ZsXEQwGozRfJyJSFHbbNVOoQ59DU9/KuU=
|
||||||
modernc.org/ccgo/v4 v4.34.0/go.mod h1:AS5WYMyBakQ+fhsHhtP8mWB82KTGPkNNJDGfGQCe0/A=
|
modernc.org/ccgo/v4 v4.32.4/go.mod h1:lY7f+fiTDHfcv6YlRgSkxYfhs+UvOEEzj49jAn2TOx0=
|
||||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||||
@@ -562,18 +573,18 @@ modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
|
|||||||
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||||
modernc.org/libc v1.72.3 h1:ZnDF4tXn4NBXFutMMQC4vtbTFSXhhKzR73fv0beZEAU=
|
modernc.org/libc v1.72.0 h1:IEu559v9a0XWjw0DPoVKtXpO2qt5NVLAnFaBbjq+n8c=
|
||||||
modernc.org/libc v1.72.3/go.mod h1:dn0dZNnnn1clLyvRxLxYExxiKRZIRENOfqQ8XEeg4Qs=
|
modernc.org/libc v1.72.0/go.mod h1:tTU8DL8A+XLVkEY3x5E/tO7s2Q/q42EtnNWda/L5QhQ=
|
||||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||||
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
|
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||||
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||||
modernc.org/sqlite v1.50.1 h1:l+cQvn0sd0zJJtfygGHuQJ5AjlrwXmWPw4KP3ZMwr9w=
|
modernc.org/sqlite v1.50.0 h1:eMowQSWLK0MeiQTdmz3lqoF5dqclujdlIKeJA11+7oM=
|
||||||
modernc.org/sqlite v1.50.1/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM=
|
modernc.org/sqlite v1.50.0/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew=
|
||||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
@@ -590,5 +601,5 @@ sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
|||||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||||
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
|
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
|
||||||
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
|
||||||
tailscale.com v1.98.3 h1:caAbG4UfkKfKPE6b1fj5t4ep5qrwEis5AJu91ruvePw=
|
tailscale.com v1.96.5 h1:gNkfA/KSZAl6jCH9cj8urq00HRWItDDTtGsyATI89jA=
|
||||||
tailscale.com v1.98.3/go.mod h1:U23ZwbZlKJMNU7CScy+lCVVlece/S5n09q0nyudncBI=
|
tailscale.com v1.96.5/go.mod h1:/3lnZBYb2UEwnN0MNu2SDXUtT06AGd5k0s+OWx3WmcY=
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ var FrontendAssets embed.FS
|
|||||||
|
|
||||||
// Migrations
|
// Migrations
|
||||||
//
|
//
|
||||||
//go:embed migrations/sqlite/*.sql
|
//go:embed migrations/sqlite/*.sql migrations/postgres/*.sql
|
||||||
var Migrations embed.FS
|
var Migrations embed.FS
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
DROP TABLE IF EXISTS "oidc_tokens";
|
||||||
|
DROP TABLE IF EXISTS "oidc_userinfo";
|
||||||
|
DROP TABLE IF EXISTS "oidc_codes";
|
||||||
|
DROP TABLE IF EXISTS "sessions";
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
CREATE TABLE "sessions" (
|
||||||
|
"uuid" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"username" TEXT NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"provider" TEXT NOT NULL,
|
||||||
|
"totp_pending" BOOLEAN NOT NULL,
|
||||||
|
"oauth_groups" TEXT NOT NULL DEFAULT '',
|
||||||
|
"expiry" BIGINT NOT NULL,
|
||||||
|
"created_at" BIGINT NOT NULL,
|
||||||
|
"oauth_name" TEXT NOT NULL DEFAULT '',
|
||||||
|
"oauth_sub" TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "oidc_codes" (
|
||||||
|
"sub" TEXT NOT NULL UNIQUE,
|
||||||
|
"code_hash" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"scope" TEXT NOT NULL,
|
||||||
|
"redirect_uri" TEXT NOT NULL,
|
||||||
|
"client_id" TEXT NOT NULL,
|
||||||
|
"expires_at" BIGINT NOT NULL,
|
||||||
|
"nonce" TEXT NOT NULL DEFAULT '',
|
||||||
|
"code_challenge" TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "oidc_tokens" (
|
||||||
|
"sub" TEXT NOT NULL UNIQUE,
|
||||||
|
"access_token_hash" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"refresh_token_hash" TEXT NOT NULL,
|
||||||
|
"code_hash" TEXT NOT NULL,
|
||||||
|
"scope" TEXT NOT NULL,
|
||||||
|
"client_id" TEXT NOT NULL,
|
||||||
|
"token_expires_at" BIGINT NOT NULL,
|
||||||
|
"refresh_token_expires_at" BIGINT NOT NULL,
|
||||||
|
"nonce" TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE "oidc_userinfo" (
|
||||||
|
"sub" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"preferred_username" TEXT NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"groups" TEXT NOT NULL,
|
||||||
|
"updated_at" BIGINT NOT NULL,
|
||||||
|
"given_name" TEXT NOT NULL,
|
||||||
|
"family_name" TEXT NOT NULL,
|
||||||
|
"middle_name" TEXT NOT NULL,
|
||||||
|
"nickname" TEXT NOT NULL,
|
||||||
|
"profile" TEXT NOT NULL,
|
||||||
|
"picture" TEXT NOT NULL,
|
||||||
|
"website" TEXT NOT NULL,
|
||||||
|
"gender" TEXT NOT NULL,
|
||||||
|
"birthdate" TEXT NOT NULL,
|
||||||
|
"zoneinfo" TEXT NOT NULL,
|
||||||
|
"locale" TEXT NOT NULL,
|
||||||
|
"phone_number" TEXT NOT NULL,
|
||||||
|
"address" TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_sessions_expiry ON "sessions" ("expiry");
|
||||||
@@ -97,12 +97,7 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
return fmt.Errorf("failed to load users: %w", err)
|
return fmt.Errorf("failed to load users: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if users != nil {
|
app.runtime.LocalUsers = *users
|
||||||
app.runtime.LocalUsers = *users
|
|
||||||
} else {
|
|
||||||
log.App.Debug().Msg("No local users found, local authentication will not be available")
|
|
||||||
app.runtime.LocalUsers = []model.LocalUser{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// load oauth whitelist
|
// load oauth whitelist
|
||||||
oauthWhitelist, err := utils.GetStringList(app.config.OAuth.Whitelist, app.config.OAuth.WhitelistFile)
|
oauthWhitelist, err := utils.GetStringList(app.config.OAuth.Whitelist, app.config.OAuth.WhitelistFile)
|
||||||
@@ -117,13 +112,6 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
app.runtime.OAuthProviders = app.config.OAuth.Providers
|
app.runtime.OAuthProviders = app.config.OAuth.Providers
|
||||||
|
|
||||||
for id, provider := range app.runtime.OAuthProviders {
|
for id, provider := range app.runtime.OAuthProviders {
|
||||||
providerWhitelist, err := utils.GetStringList(provider.Whitelist, provider.WhitelistFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to load oauth whitelist for provider %s: %w", id, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
provider.Whitelist = providerWhitelist
|
|
||||||
|
|
||||||
secret := utils.GetSecret(provider.ClientSecret, provider.ClientSecretFile)
|
secret := utils.GetSecret(provider.ClientSecret, provider.ClientSecretFile)
|
||||||
provider.ClientSecret = secret
|
provider.ClientSecret = secret
|
||||||
provider.ClientSecretFile = ""
|
provider.ClientSecretFile = ""
|
||||||
|
|||||||
@@ -6,15 +6,18 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/golang-migrate/migrate/v4"
|
||||||
|
pgxmigrate "github.com/golang-migrate/migrate/v4/database/pgx/v5"
|
||||||
|
"github.com/golang-migrate/migrate/v4/database/sqlite3"
|
||||||
|
"github.com/golang-migrate/migrate/v4/source/iofs"
|
||||||
|
_ "github.com/jackc/pgx/v5/stdlib"
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/assets"
|
"github.com/tinyauthapp/tinyauth/internal/assets"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/repository"
|
"github.com/tinyauthapp/tinyauth/internal/repository"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/repository/memory"
|
"github.com/tinyauthapp/tinyauth/internal/repository/memory"
|
||||||
|
"github.com/tinyauthapp/tinyauth/internal/repository/postgres"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/repository/sqlite"
|
"github.com/tinyauthapp/tinyauth/internal/repository/sqlite"
|
||||||
|
|
||||||
"github.com/golang-migrate/migrate/v4"
|
|
||||||
"github.com/golang-migrate/migrate/v4/database/sqlite3"
|
|
||||||
"github.com/golang-migrate/migrate/v4/source/iofs"
|
|
||||||
_ "modernc.org/sqlite"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (app *BootstrapApp) SetupStore() (repository.Store, error) {
|
func (app *BootstrapApp) SetupStore() (repository.Store, error) {
|
||||||
@@ -23,8 +26,10 @@ func (app *BootstrapApp) SetupStore() (repository.Store, error) {
|
|||||||
return memory.New(), nil
|
return memory.New(), nil
|
||||||
case "sqlite", "":
|
case "sqlite", "":
|
||||||
return app.setupSQLite(app.config.Database.Path)
|
return app.setupSQLite(app.config.Database.Path)
|
||||||
|
case "postgres":
|
||||||
|
return app.setupPostgres(app.config.Database.Path)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown database driver %q: valid values are sqlite, memory", app.config.Database.Driver)
|
return nil, fmt.Errorf("unknown database driver %q: valid values are sqlite, postgres, memory", app.config.Database.Driver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,9 +46,9 @@ func (app *BootstrapApp) setupSQLite(databasePath string) (repository.Store, err
|
|||||||
return nil, fmt.Errorf("failed to open database: %w", err)
|
return nil, fmt.Errorf("failed to open database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the database if there is an error during migration
|
cleanup := true
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if cleanup {
|
||||||
db.Close()
|
db.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -70,11 +75,54 @@ func (app *BootstrapApp) setupSQLite(databasePath string) (repository.Store, err
|
|||||||
return nil, fmt.Errorf("failed to create migrator: %w", err)
|
return nil, fmt.Errorf("failed to create migrator: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := migrator.Up(); err != nil && err != migrate.ErrNoChange {
|
if err = migrator.Up(); err != nil && err != migrate.ErrNoChange {
|
||||||
return nil, fmt.Errorf("failed to migrate database: %w", err)
|
return nil, fmt.Errorf("failed to migrate database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanup = false
|
||||||
app.db = db
|
app.db = db
|
||||||
|
|
||||||
return sqlite.NewStore(sqlite.New(db)), nil
|
return sqlite.NewStore(sqlite.New(db)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *BootstrapApp) setupPostgres(databaseURL string) (repository.Store, error) {
|
||||||
|
db, err := sql.Open("pgx", databaseURL)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup := true
|
||||||
|
defer func() {
|
||||||
|
if cleanup {
|
||||||
|
db.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
migrations, err := iofs.New(assets.Migrations, "migrations/postgres")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create migrations: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
target, err := pgxmigrate.WithInstance(db, &pgxmigrate.Config{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create postgres instance: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
migrator, err := migrate.NewWithInstance("iofs", migrations, "pgx", target)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create migrator: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = migrator.Up(); err != nil && err != migrate.ErrNoChange {
|
||||||
|
return nil, fmt.Errorf("failed to migrate database: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup = false
|
||||||
|
app.db = db
|
||||||
|
|
||||||
|
return postgres.NewStore(postgres.New(db)), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package bootstrap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/service"
|
"github.com/tinyauthapp/tinyauth/internal/service"
|
||||||
@@ -125,8 +126,7 @@ func (app *BootstrapApp) setupPolicyEngine() error {
|
|||||||
Config: app.config,
|
Config: app.config,
|
||||||
})
|
})
|
||||||
policyEngine.RegisterRule(service.RuleIPBypassed, &service.IPBypassedRule{
|
policyEngine.RegisterRule(service.RuleIPBypassed, &service.IPBypassedRule{
|
||||||
Log: app.log,
|
Log: app.log,
|
||||||
Config: app.config,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
app.services.policyEngine = policyEngine
|
app.services.policyEngine = policyEngine
|
||||||
|
|||||||
@@ -183,23 +183,9 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
svc, err := controller.auth.GetOAuthService(sessionIdCookie)
|
if !controller.auth.IsEmailWhitelisted(user.Email) {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
controller.log.App.Error().Err(err).Msg("Failed to get OAuth service for session")
|
|
||||||
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if svc.ID() != req.Provider {
|
|
||||||
controller.log.App.Warn().Msgf("OAuth provider mismatch: expected %s, got %s", req.Provider, svc.ID())
|
|
||||||
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if !controller.auth.IsEmailWhitelisted(svc.ID(), user.Email) {
|
|
||||||
controller.log.App.Warn().Str("email", user.Email).Msg("Email not whitelisted, denying access")
|
controller.log.App.Warn().Str("email", user.Email).Msg("Email not whitelisted, denying access")
|
||||||
controller.log.AuditLoginFailure(user.Email, svc.ID(), c.ClientIP(), "email not whitelisted")
|
controller.log.AuditLoginFailure(user.Email, req.Provider, c.ClientIP(), "email not whitelisted")
|
||||||
|
|
||||||
queries, err := query.Values(UnauthorizedQuery{
|
queries, err := query.Values(UnauthorizedQuery{
|
||||||
Username: user.Email,
|
Username: user.Email,
|
||||||
@@ -240,6 +226,20 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
|
|||||||
username = strings.Replace(user.Email, "@", "_", 1)
|
username = strings.Replace(user.Email, "@", "_", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
svc, err := controller.auth.GetOAuthService(sessionIdCookie)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
controller.log.App.Error().Err(err).Msg("Failed to get OAuth service for session")
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if svc.ID() != req.Provider {
|
||||||
|
controller.log.App.Warn().Msgf("OAuth provider mismatch: expected %s, got %s", req.Provider, svc.ID())
|
||||||
|
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.runtime.AppURL))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
sessionCookie := repository.Session{
|
sessionCookie := repository.Session{
|
||||||
Username: username,
|
Username: username,
|
||||||
Name: name,
|
Name: name,
|
||||||
|
|||||||
@@ -160,10 +160,7 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) {
|
|||||||
userContext, err := new(model.UserContext).NewFromGin(c)
|
userContext, err := new(model.UserContext).NewFromGin(c)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// No user context found is not an issue
|
controller.log.App.Debug().Err(err).Msg("Failed to create user context from request, treating as unauthenticated")
|
||||||
if !errors.Is(err, model.ErrUserContextNotFound) {
|
|
||||||
controller.log.App.Error().Err(err).Msg("Failed to create user context from request, treating as unauthenticated")
|
|
||||||
}
|
|
||||||
userContext = &model.UserContext{
|
userContext = &model.UserContext{
|
||||||
Authenticated: false,
|
Authenticated: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ func (m *ContextMiddleware) cookieAuth(ctx context.Context, uuid string, ip stri
|
|||||||
return nil, nil, fmt.Errorf("oauth provider from session cookie not found: %s", userContext.OAuth.ID)
|
return nil, nil, fmt.Errorf("oauth provider from session cookie not found: %s", userContext.OAuth.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !m.auth.IsEmailWhitelisted(userContext.OAuth.ID, userContext.OAuth.Email) {
|
if !m.auth.IsEmailWhitelisted(userContext.OAuth.Email) {
|
||||||
m.auth.DeleteSession(ctx, uuid)
|
m.auth.DeleteSession(ctx, uuid)
|
||||||
return nil, nil, fmt.Errorf("email from session cookie not whitelisted: %s", userContext.OAuth.Email)
|
return nil, nil, fmt.Errorf("email from session cookie not whitelisted: %s", userContext.OAuth.Email)
|
||||||
}
|
}
|
||||||
@@ -251,10 +251,6 @@ func (m *ContextMiddleware) basicAuth(username string, password string) (*model.
|
|||||||
case model.UserLocal:
|
case model.UserLocal:
|
||||||
user := m.auth.GetLocalUser(username)
|
user := m.auth.GetLocalUser(username)
|
||||||
|
|
||||||
if user == nil {
|
|
||||||
return nil, nil, fmt.Errorf("user not found locally: %s", username)
|
|
||||||
}
|
|
||||||
|
|
||||||
if user.TOTPSecret != "" {
|
if user.TOTPSecret != "" {
|
||||||
return nil, nil, fmt.Errorf("user with totp not allowed to login via basic auth: %s", username)
|
return nil, nil, fmt.Errorf("user with totp not allowed to login via basic auth: %s", username)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DatabaseConfig struct {
|
type DatabaseConfig struct {
|
||||||
Driver string `description:"The database driver to use. Valid values: sqlite, memory." yaml:"driver"`
|
Driver string `description:"The database driver to use. Valid values: sqlite, postgres, memory." yaml:"driver"`
|
||||||
Path string `description:"The path to the SQLite database, including file name. Only used when driver is sqlite." yaml:"path"`
|
Path string `description:"The path to the SQLite database file, or connection URL when driver is postgres." yaml:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AnalyticsConfig struct {
|
type AnalyticsConfig struct {
|
||||||
@@ -154,9 +154,8 @@ type AddressClaim struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type IPConfig struct {
|
type IPConfig struct {
|
||||||
Allow []string `description:"List of allowed IPs or CIDR ranges." yaml:"allow"`
|
Allow []string `description:"List of allowed IPs or CIDR ranges." yaml:"allow"`
|
||||||
Block []string `description:"List of blocked IPs or CIDR ranges." yaml:"block"`
|
Block []string `description:"List of blocked IPs or CIDR ranges." yaml:"block"`
|
||||||
Bypass []string `description:"List of IPs or CIDR ranges that bypass authentication entirely." yaml:"bypass"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type OAuthConfig struct {
|
type OAuthConfig struct {
|
||||||
@@ -226,8 +225,6 @@ type OAuthServiceConfig struct {
|
|||||||
ClientID string `description:"OAuth client ID." yaml:"clientId"`
|
ClientID string `description:"OAuth client ID." yaml:"clientId"`
|
||||||
ClientSecret string `description:"OAuth client secret." yaml:"clientSecret"`
|
ClientSecret string `description:"OAuth client secret." yaml:"clientSecret"`
|
||||||
ClientSecretFile string `description:"Path to the file containing the OAuth client secret." yaml:"clientSecretFile"`
|
ClientSecretFile string `description:"Path to the file containing the OAuth client secret." yaml:"clientSecretFile"`
|
||||||
Whitelist []string `description:"Comma-separated list of allowed OAuth domains for this provider." yaml:"whitelist"`
|
|
||||||
WhitelistFile string `description:"Path to the OAuth whitelist file for this provider." yaml:"whitelistFile"`
|
|
||||||
Scopes []string `description:"OAuth scopes." yaml:"scopes"`
|
Scopes []string `description:"OAuth scopes." yaml:"scopes"`
|
||||||
RedirectURL string `description:"OAuth redirect URL." yaml:"redirectUrl"`
|
RedirectURL string `description:"OAuth redirect URL." yaml:"redirectUrl"`
|
||||||
AuthURL string `description:"OAuth authorization URL." yaml:"authUrl"`
|
AuthURL string `description:"OAuth authorization URL." yaml:"authUrl"`
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.31.1
|
||||||
|
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DBTX interface {
|
||||||
|
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
|
||||||
|
PrepareContext(context.Context, string) (*sql.Stmt, error)
|
||||||
|
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
|
||||||
|
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(db DBTX) *Queries {
|
||||||
|
return &Queries{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Queries struct {
|
||||||
|
db DBTX
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) WithTx(tx *sql.Tx) *Queries {
|
||||||
|
return &Queries{
|
||||||
|
db: tx,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package postgres
|
||||||
|
|
||||||
|
//go:generate go run github.com/tinyauthapp/tinyauth/gen/sqlc-wrapper -pkg github.com/tinyauthapp/tinyauth/internal/repository/postgres
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.31.1
|
||||||
|
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
type OidcCode struct {
|
||||||
|
Sub string
|
||||||
|
CodeHash string
|
||||||
|
Scope string
|
||||||
|
RedirectURI string
|
||||||
|
ClientID string
|
||||||
|
ExpiresAt int64
|
||||||
|
Nonce string
|
||||||
|
CodeChallenge string
|
||||||
|
}
|
||||||
|
|
||||||
|
type OidcToken struct {
|
||||||
|
Sub string
|
||||||
|
AccessTokenHash string
|
||||||
|
RefreshTokenHash string
|
||||||
|
CodeHash string
|
||||||
|
Scope string
|
||||||
|
ClientID string
|
||||||
|
TokenExpiresAt int64
|
||||||
|
RefreshTokenExpiresAt int64
|
||||||
|
Nonce string
|
||||||
|
}
|
||||||
|
|
||||||
|
type OidcUserinfo struct {
|
||||||
|
Sub string
|
||||||
|
Name string
|
||||||
|
PreferredUsername string
|
||||||
|
Email string
|
||||||
|
Groups string
|
||||||
|
UpdatedAt int64
|
||||||
|
GivenName string
|
||||||
|
FamilyName string
|
||||||
|
MiddleName string
|
||||||
|
Nickname string
|
||||||
|
Profile string
|
||||||
|
Picture string
|
||||||
|
Website string
|
||||||
|
Gender string
|
||||||
|
Birthdate string
|
||||||
|
Zoneinfo string
|
||||||
|
Locale string
|
||||||
|
PhoneNumber string
|
||||||
|
Address string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
UUID string
|
||||||
|
Username string
|
||||||
|
Email string
|
||||||
|
Name string
|
||||||
|
Provider string
|
||||||
|
TotpPending bool
|
||||||
|
OAuthGroups string
|
||||||
|
Expiry int64
|
||||||
|
CreatedAt int64
|
||||||
|
OAuthName string
|
||||||
|
OAuthSub string
|
||||||
|
}
|
||||||
@@ -0,0 +1,581 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.31.1
|
||||||
|
// source: oidc_queries.sql
|
||||||
|
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const createOidcCode = `-- name: CreateOidcCode :one
|
||||||
|
INSERT INTO "oidc_codes" (
|
||||||
|
"sub",
|
||||||
|
"code_hash",
|
||||||
|
"scope",
|
||||||
|
"redirect_uri",
|
||||||
|
"client_id",
|
||||||
|
"expires_at",
|
||||||
|
"nonce",
|
||||||
|
"code_challenge"
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8
|
||||||
|
)
|
||||||
|
RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateOidcCodeParams struct {
|
||||||
|
Sub string
|
||||||
|
CodeHash string
|
||||||
|
Scope string
|
||||||
|
RedirectURI string
|
||||||
|
ClientID string
|
||||||
|
ExpiresAt int64
|
||||||
|
Nonce string
|
||||||
|
CodeChallenge string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateOidcCode(ctx context.Context, arg CreateOidcCodeParams) (OidcCode, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, createOidcCode,
|
||||||
|
arg.Sub,
|
||||||
|
arg.CodeHash,
|
||||||
|
arg.Scope,
|
||||||
|
arg.RedirectURI,
|
||||||
|
arg.ClientID,
|
||||||
|
arg.ExpiresAt,
|
||||||
|
arg.Nonce,
|
||||||
|
arg.CodeChallenge,
|
||||||
|
)
|
||||||
|
var i OidcCode
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.RedirectURI,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.ExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
&i.CodeChallenge,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const createOidcToken = `-- name: CreateOidcToken :one
|
||||||
|
INSERT INTO "oidc_tokens" (
|
||||||
|
"sub",
|
||||||
|
"access_token_hash",
|
||||||
|
"refresh_token_hash",
|
||||||
|
"scope",
|
||||||
|
"client_id",
|
||||||
|
"token_expires_at",
|
||||||
|
"refresh_token_expires_at",
|
||||||
|
"code_hash",
|
||||||
|
"nonce"
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8, $9
|
||||||
|
)
|
||||||
|
RETURNING sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateOidcTokenParams struct {
|
||||||
|
Sub string
|
||||||
|
AccessTokenHash string
|
||||||
|
RefreshTokenHash string
|
||||||
|
Scope string
|
||||||
|
ClientID string
|
||||||
|
TokenExpiresAt int64
|
||||||
|
RefreshTokenExpiresAt int64
|
||||||
|
CodeHash string
|
||||||
|
Nonce string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateOidcToken(ctx context.Context, arg CreateOidcTokenParams) (OidcToken, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, createOidcToken,
|
||||||
|
arg.Sub,
|
||||||
|
arg.AccessTokenHash,
|
||||||
|
arg.RefreshTokenHash,
|
||||||
|
arg.Scope,
|
||||||
|
arg.ClientID,
|
||||||
|
arg.TokenExpiresAt,
|
||||||
|
arg.RefreshTokenExpiresAt,
|
||||||
|
arg.CodeHash,
|
||||||
|
arg.Nonce,
|
||||||
|
)
|
||||||
|
var i OidcToken
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.AccessTokenHash,
|
||||||
|
&i.RefreshTokenHash,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.TokenExpiresAt,
|
||||||
|
&i.RefreshTokenExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const createOidcUserInfo = `-- name: CreateOidcUserInfo :one
|
||||||
|
INSERT INTO "oidc_userinfo" (
|
||||||
|
"sub",
|
||||||
|
"name",
|
||||||
|
"preferred_username",
|
||||||
|
"email",
|
||||||
|
"groups",
|
||||||
|
"updated_at",
|
||||||
|
"given_name",
|
||||||
|
"family_name",
|
||||||
|
"middle_name",
|
||||||
|
"nickname",
|
||||||
|
"profile",
|
||||||
|
"picture",
|
||||||
|
"website",
|
||||||
|
"gender",
|
||||||
|
"birthdate",
|
||||||
|
"zoneinfo",
|
||||||
|
"locale",
|
||||||
|
"phone_number",
|
||||||
|
"address"
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19
|
||||||
|
)
|
||||||
|
RETURNING sub, name, preferred_username, email, groups, updated_at, given_name, family_name, middle_name, nickname, profile, picture, website, gender, birthdate, zoneinfo, locale, phone_number, address
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateOidcUserInfoParams struct {
|
||||||
|
Sub string
|
||||||
|
Name string
|
||||||
|
PreferredUsername string
|
||||||
|
Email string
|
||||||
|
Groups string
|
||||||
|
UpdatedAt int64
|
||||||
|
GivenName string
|
||||||
|
FamilyName string
|
||||||
|
MiddleName string
|
||||||
|
Nickname string
|
||||||
|
Profile string
|
||||||
|
Picture string
|
||||||
|
Website string
|
||||||
|
Gender string
|
||||||
|
Birthdate string
|
||||||
|
Zoneinfo string
|
||||||
|
Locale string
|
||||||
|
PhoneNumber string
|
||||||
|
Address string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateOidcUserInfo(ctx context.Context, arg CreateOidcUserInfoParams) (OidcUserinfo, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, createOidcUserInfo,
|
||||||
|
arg.Sub,
|
||||||
|
arg.Name,
|
||||||
|
arg.PreferredUsername,
|
||||||
|
arg.Email,
|
||||||
|
arg.Groups,
|
||||||
|
arg.UpdatedAt,
|
||||||
|
arg.GivenName,
|
||||||
|
arg.FamilyName,
|
||||||
|
arg.MiddleName,
|
||||||
|
arg.Nickname,
|
||||||
|
arg.Profile,
|
||||||
|
arg.Picture,
|
||||||
|
arg.Website,
|
||||||
|
arg.Gender,
|
||||||
|
arg.Birthdate,
|
||||||
|
arg.Zoneinfo,
|
||||||
|
arg.Locale,
|
||||||
|
arg.PhoneNumber,
|
||||||
|
arg.Address,
|
||||||
|
)
|
||||||
|
var i OidcUserinfo
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.Name,
|
||||||
|
&i.PreferredUsername,
|
||||||
|
&i.Email,
|
||||||
|
&i.Groups,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.GivenName,
|
||||||
|
&i.FamilyName,
|
||||||
|
&i.MiddleName,
|
||||||
|
&i.Nickname,
|
||||||
|
&i.Profile,
|
||||||
|
&i.Picture,
|
||||||
|
&i.Website,
|
||||||
|
&i.Gender,
|
||||||
|
&i.Birthdate,
|
||||||
|
&i.Zoneinfo,
|
||||||
|
&i.Locale,
|
||||||
|
&i.PhoneNumber,
|
||||||
|
&i.Address,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteExpiredOidcCodes = `-- name: DeleteExpiredOidcCodes :many
|
||||||
|
DELETE FROM "oidc_codes"
|
||||||
|
WHERE "expires_at" < $1
|
||||||
|
RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteExpiredOidcCodes(ctx context.Context, expiresAt int64) ([]OidcCode, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, deleteExpiredOidcCodes, expiresAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []OidcCode
|
||||||
|
for rows.Next() {
|
||||||
|
var i OidcCode
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.RedirectURI,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.ExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
&i.CodeChallenge,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteExpiredOidcTokens = `-- name: DeleteExpiredOidcTokens :many
|
||||||
|
DELETE FROM "oidc_tokens"
|
||||||
|
WHERE "token_expires_at" < $1 AND "refresh_token_expires_at" < $2
|
||||||
|
RETURNING sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce
|
||||||
|
`
|
||||||
|
|
||||||
|
type DeleteExpiredOidcTokensParams struct {
|
||||||
|
TokenExpiresAt int64
|
||||||
|
RefreshTokenExpiresAt int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) DeleteExpiredOidcTokens(ctx context.Context, arg DeleteExpiredOidcTokensParams) ([]OidcToken, error) {
|
||||||
|
rows, err := q.db.QueryContext(ctx, deleteExpiredOidcTokens, arg.TokenExpiresAt, arg.RefreshTokenExpiresAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []OidcToken
|
||||||
|
for rows.Next() {
|
||||||
|
var i OidcToken
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.AccessTokenHash,
|
||||||
|
&i.RefreshTokenHash,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.TokenExpiresAt,
|
||||||
|
&i.RefreshTokenExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteOidcCode = `-- name: DeleteOidcCode :exec
|
||||||
|
DELETE FROM "oidc_codes"
|
||||||
|
WHERE "code_hash" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteOidcCode(ctx context.Context, codeHash string) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteOidcCode, codeHash)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteOidcCodeBySub = `-- name: DeleteOidcCodeBySub :exec
|
||||||
|
DELETE FROM "oidc_codes"
|
||||||
|
WHERE "sub" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteOidcCodeBySub(ctx context.Context, sub string) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteOidcCodeBySub, sub)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteOidcToken = `-- name: DeleteOidcToken :exec
|
||||||
|
DELETE FROM "oidc_tokens"
|
||||||
|
WHERE "access_token_hash" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteOidcToken(ctx context.Context, accessTokenHash string) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteOidcToken, accessTokenHash)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteOidcTokenByCodeHash = `-- name: DeleteOidcTokenByCodeHash :exec
|
||||||
|
DELETE FROM "oidc_tokens"
|
||||||
|
WHERE "code_hash" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteOidcTokenByCodeHash(ctx context.Context, codeHash string) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteOidcTokenByCodeHash, codeHash)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteOidcTokenBySub = `-- name: DeleteOidcTokenBySub :exec
|
||||||
|
DELETE FROM "oidc_tokens"
|
||||||
|
WHERE "sub" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteOidcTokenBySub(ctx context.Context, sub string) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteOidcTokenBySub, sub)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteOidcUserInfo = `-- name: DeleteOidcUserInfo :exec
|
||||||
|
DELETE FROM "oidc_userinfo"
|
||||||
|
WHERE "sub" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteOidcUserInfo(ctx context.Context, sub string) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteOidcUserInfo, sub)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOidcCode = `-- name: GetOidcCode :one
|
||||||
|
DELETE FROM "oidc_codes"
|
||||||
|
WHERE "code_hash" = $1
|
||||||
|
RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetOidcCode(ctx context.Context, codeHash string) (OidcCode, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getOidcCode, codeHash)
|
||||||
|
var i OidcCode
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.RedirectURI,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.ExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
&i.CodeChallenge,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOidcCodeBySub = `-- name: GetOidcCodeBySub :one
|
||||||
|
DELETE FROM "oidc_codes"
|
||||||
|
WHERE "sub" = $1
|
||||||
|
RETURNING sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetOidcCodeBySub(ctx context.Context, sub string) (OidcCode, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getOidcCodeBySub, sub)
|
||||||
|
var i OidcCode
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.RedirectURI,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.ExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
&i.CodeChallenge,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOidcCodeBySubUnsafe = `-- name: GetOidcCodeBySubUnsafe :one
|
||||||
|
SELECT sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge FROM "oidc_codes"
|
||||||
|
WHERE "sub" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetOidcCodeBySubUnsafe(ctx context.Context, sub string) (OidcCode, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getOidcCodeBySubUnsafe, sub)
|
||||||
|
var i OidcCode
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.RedirectURI,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.ExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
&i.CodeChallenge,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOidcCodeUnsafe = `-- name: GetOidcCodeUnsafe :one
|
||||||
|
SELECT sub, code_hash, scope, redirect_uri, client_id, expires_at, nonce, code_challenge FROM "oidc_codes"
|
||||||
|
WHERE "code_hash" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetOidcCodeUnsafe(ctx context.Context, codeHash string) (OidcCode, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getOidcCodeUnsafe, codeHash)
|
||||||
|
var i OidcCode
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.RedirectURI,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.ExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
&i.CodeChallenge,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOidcToken = `-- name: GetOidcToken :one
|
||||||
|
SELECT sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce FROM "oidc_tokens"
|
||||||
|
WHERE "access_token_hash" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetOidcToken(ctx context.Context, accessTokenHash string) (OidcToken, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getOidcToken, accessTokenHash)
|
||||||
|
var i OidcToken
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.AccessTokenHash,
|
||||||
|
&i.RefreshTokenHash,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.TokenExpiresAt,
|
||||||
|
&i.RefreshTokenExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOidcTokenByRefreshToken = `-- name: GetOidcTokenByRefreshToken :one
|
||||||
|
SELECT sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce FROM "oidc_tokens"
|
||||||
|
WHERE "refresh_token_hash" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetOidcTokenByRefreshToken(ctx context.Context, refreshTokenHash string) (OidcToken, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getOidcTokenByRefreshToken, refreshTokenHash)
|
||||||
|
var i OidcToken
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.AccessTokenHash,
|
||||||
|
&i.RefreshTokenHash,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.TokenExpiresAt,
|
||||||
|
&i.RefreshTokenExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOidcTokenBySub = `-- name: GetOidcTokenBySub :one
|
||||||
|
SELECT sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce FROM "oidc_tokens"
|
||||||
|
WHERE "sub" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetOidcTokenBySub(ctx context.Context, sub string) (OidcToken, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getOidcTokenBySub, sub)
|
||||||
|
var i OidcToken
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.AccessTokenHash,
|
||||||
|
&i.RefreshTokenHash,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.TokenExpiresAt,
|
||||||
|
&i.RefreshTokenExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getOidcUserInfo = `-- name: GetOidcUserInfo :one
|
||||||
|
SELECT sub, name, preferred_username, email, groups, updated_at, given_name, family_name, middle_name, nickname, profile, picture, website, gender, birthdate, zoneinfo, locale, phone_number, address FROM "oidc_userinfo"
|
||||||
|
WHERE "sub" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetOidcUserInfo(ctx context.Context, sub string) (OidcUserinfo, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getOidcUserInfo, sub)
|
||||||
|
var i OidcUserinfo
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.Name,
|
||||||
|
&i.PreferredUsername,
|
||||||
|
&i.Email,
|
||||||
|
&i.Groups,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
&i.GivenName,
|
||||||
|
&i.FamilyName,
|
||||||
|
&i.MiddleName,
|
||||||
|
&i.Nickname,
|
||||||
|
&i.Profile,
|
||||||
|
&i.Picture,
|
||||||
|
&i.Website,
|
||||||
|
&i.Gender,
|
||||||
|
&i.Birthdate,
|
||||||
|
&i.Zoneinfo,
|
||||||
|
&i.Locale,
|
||||||
|
&i.PhoneNumber,
|
||||||
|
&i.Address,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateOidcTokenByRefreshToken = `-- name: UpdateOidcTokenByRefreshToken :one
|
||||||
|
UPDATE "oidc_tokens" SET
|
||||||
|
"access_token_hash" = $1,
|
||||||
|
"refresh_token_hash" = $2,
|
||||||
|
"token_expires_at" = $3,
|
||||||
|
"refresh_token_expires_at" = $4
|
||||||
|
WHERE "refresh_token_hash" = $5
|
||||||
|
RETURNING sub, access_token_hash, refresh_token_hash, code_hash, scope, client_id, token_expires_at, refresh_token_expires_at, nonce
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateOidcTokenByRefreshTokenParams struct {
|
||||||
|
AccessTokenHash string
|
||||||
|
RefreshTokenHash string
|
||||||
|
TokenExpiresAt int64
|
||||||
|
RefreshTokenExpiresAt int64
|
||||||
|
RefreshTokenHash_2 string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateOidcTokenByRefreshToken(ctx context.Context, arg UpdateOidcTokenByRefreshTokenParams) (OidcToken, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, updateOidcTokenByRefreshToken,
|
||||||
|
arg.AccessTokenHash,
|
||||||
|
arg.RefreshTokenHash,
|
||||||
|
arg.TokenExpiresAt,
|
||||||
|
arg.RefreshTokenExpiresAt,
|
||||||
|
arg.RefreshTokenHash_2,
|
||||||
|
)
|
||||||
|
var i OidcToken
|
||||||
|
err := row.Scan(
|
||||||
|
&i.Sub,
|
||||||
|
&i.AccessTokenHash,
|
||||||
|
&i.RefreshTokenHash,
|
||||||
|
&i.CodeHash,
|
||||||
|
&i.Scope,
|
||||||
|
&i.ClientID,
|
||||||
|
&i.TokenExpiresAt,
|
||||||
|
&i.RefreshTokenExpiresAt,
|
||||||
|
&i.Nonce,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.31.1
|
||||||
|
// source: session_queries.sql
|
||||||
|
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const createSession = `-- name: CreateSession :one
|
||||||
|
INSERT INTO "sessions" (
|
||||||
|
"uuid",
|
||||||
|
"username",
|
||||||
|
"email",
|
||||||
|
"name",
|
||||||
|
"provider",
|
||||||
|
"totp_pending",
|
||||||
|
"oauth_groups",
|
||||||
|
"expiry",
|
||||||
|
"created_at",
|
||||||
|
"oauth_name",
|
||||||
|
"oauth_sub"
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11
|
||||||
|
)
|
||||||
|
RETURNING uuid, username, email, name, provider, totp_pending, oauth_groups, expiry, created_at, oauth_name, oauth_sub
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateSessionParams struct {
|
||||||
|
UUID string
|
||||||
|
Username string
|
||||||
|
Email string
|
||||||
|
Name string
|
||||||
|
Provider string
|
||||||
|
TotpPending bool
|
||||||
|
OAuthGroups string
|
||||||
|
Expiry int64
|
||||||
|
CreatedAt int64
|
||||||
|
OAuthName string
|
||||||
|
OAuthSub string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, createSession,
|
||||||
|
arg.UUID,
|
||||||
|
arg.Username,
|
||||||
|
arg.Email,
|
||||||
|
arg.Name,
|
||||||
|
arg.Provider,
|
||||||
|
arg.TotpPending,
|
||||||
|
arg.OAuthGroups,
|
||||||
|
arg.Expiry,
|
||||||
|
arg.CreatedAt,
|
||||||
|
arg.OAuthName,
|
||||||
|
arg.OAuthSub,
|
||||||
|
)
|
||||||
|
var i Session
|
||||||
|
err := row.Scan(
|
||||||
|
&i.UUID,
|
||||||
|
&i.Username,
|
||||||
|
&i.Email,
|
||||||
|
&i.Name,
|
||||||
|
&i.Provider,
|
||||||
|
&i.TotpPending,
|
||||||
|
&i.OAuthGroups,
|
||||||
|
&i.Expiry,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.OAuthName,
|
||||||
|
&i.OAuthSub,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteExpiredSessions = `-- name: DeleteExpiredSessions :exec
|
||||||
|
DELETE FROM "sessions"
|
||||||
|
WHERE "expiry" < $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteExpiredSessions(ctx context.Context, expiry int64) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteExpiredSessions, expiry)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteSession = `-- name: DeleteSession :exec
|
||||||
|
DELETE FROM "sessions"
|
||||||
|
WHERE "uuid" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) DeleteSession(ctx context.Context, uuid string) error {
|
||||||
|
_, err := q.db.ExecContext(ctx, deleteSession, uuid)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSession = `-- name: GetSession :one
|
||||||
|
SELECT uuid, username, email, name, provider, totp_pending, oauth_groups, expiry, created_at, oauth_name, oauth_sub FROM "sessions"
|
||||||
|
WHERE "uuid" = $1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetSession(ctx context.Context, uuid string) (Session, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, getSession, uuid)
|
||||||
|
var i Session
|
||||||
|
err := row.Scan(
|
||||||
|
&i.UUID,
|
||||||
|
&i.Username,
|
||||||
|
&i.Email,
|
||||||
|
&i.Name,
|
||||||
|
&i.Provider,
|
||||||
|
&i.TotpPending,
|
||||||
|
&i.OAuthGroups,
|
||||||
|
&i.Expiry,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.OAuthName,
|
||||||
|
&i.OAuthSub,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateSession = `-- name: UpdateSession :one
|
||||||
|
UPDATE "sessions" SET
|
||||||
|
"username" = $1,
|
||||||
|
"email" = $2,
|
||||||
|
"name" = $3,
|
||||||
|
"provider" = $4,
|
||||||
|
"totp_pending" = $5,
|
||||||
|
"oauth_groups" = $6,
|
||||||
|
"expiry" = $7,
|
||||||
|
"oauth_name" = $8,
|
||||||
|
"oauth_sub" = $9
|
||||||
|
WHERE "uuid" = $10
|
||||||
|
RETURNING uuid, username, email, name, provider, totp_pending, oauth_groups, expiry, created_at, oauth_name, oauth_sub
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateSessionParams struct {
|
||||||
|
Username string
|
||||||
|
Email string
|
||||||
|
Name string
|
||||||
|
Provider string
|
||||||
|
TotpPending bool
|
||||||
|
OAuthGroups string
|
||||||
|
Expiry int64
|
||||||
|
OAuthName string
|
||||||
|
OAuthSub string
|
||||||
|
UUID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateSession(ctx context.Context, arg UpdateSessionParams) (Session, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, updateSession,
|
||||||
|
arg.Username,
|
||||||
|
arg.Email,
|
||||||
|
arg.Name,
|
||||||
|
arg.Provider,
|
||||||
|
arg.TotpPending,
|
||||||
|
arg.OAuthGroups,
|
||||||
|
arg.Expiry,
|
||||||
|
arg.OAuthName,
|
||||||
|
arg.OAuthSub,
|
||||||
|
arg.UUID,
|
||||||
|
)
|
||||||
|
var i Session
|
||||||
|
err := row.Scan(
|
||||||
|
&i.UUID,
|
||||||
|
&i.Username,
|
||||||
|
&i.Email,
|
||||||
|
&i.Name,
|
||||||
|
&i.Provider,
|
||||||
|
&i.TotpPending,
|
||||||
|
&i.OAuthGroups,
|
||||||
|
&i.Expiry,
|
||||||
|
&i.CreatedAt,
|
||||||
|
&i.OAuthName,
|
||||||
|
&i.OAuthSub,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
// Code generated by cmd/gen/sqlc-wrapper. DO NOT EDIT.
|
||||||
|
package postgres
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/tinyauthapp/tinyauth/internal/repository"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Store wraps *Queries and implements repository.Store.
|
||||||
|
type Store struct {
|
||||||
|
q *Queries
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStore wraps a *Queries to satisfy repository.Store.
|
||||||
|
func NewStore(q *Queries) repository.Store {
|
||||||
|
return &Store{q: q}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorMap = map[error]error{
|
||||||
|
sql.ErrNoRows: repository.ErrNotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapErr(err error) error {
|
||||||
|
for from, to := range errorMap {
|
||||||
|
if errors.Is(err, from) {
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) CreateOidcCode(ctx context.Context, arg repository.CreateOidcCodeParams) (repository.OidcCode, error) {
|
||||||
|
r, err := s.q.CreateOidcCode(ctx, CreateOidcCodeParams(arg))
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcCode{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcCode(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) CreateOidcToken(ctx context.Context, arg repository.CreateOidcTokenParams) (repository.OidcToken, error) {
|
||||||
|
r, err := s.q.CreateOidcToken(ctx, CreateOidcTokenParams(arg))
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcToken{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcToken(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) CreateOidcUserInfo(ctx context.Context, arg repository.CreateOidcUserInfoParams) (repository.OidcUserinfo, error) {
|
||||||
|
r, err := s.q.CreateOidcUserInfo(ctx, CreateOidcUserInfoParams(arg))
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcUserinfo{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcUserinfo(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) CreateSession(ctx context.Context, arg repository.CreateSessionParams) (repository.Session, error) {
|
||||||
|
r, err := s.q.CreateSession(ctx, CreateSessionParams(arg))
|
||||||
|
if err != nil {
|
||||||
|
return repository.Session{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.Session(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) DeleteExpiredOidcCodes(ctx context.Context, expiresAt int64) ([]repository.OidcCode, error) {
|
||||||
|
rows, err := s.q.DeleteExpiredOidcCodes(ctx, expiresAt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, mapErr(err)
|
||||||
|
}
|
||||||
|
out := make([]repository.OidcCode, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
out[i] = repository.OidcCode(row)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) DeleteExpiredOidcTokens(ctx context.Context, arg repository.DeleteExpiredOidcTokensParams) ([]repository.OidcToken, error) {
|
||||||
|
rows, err := s.q.DeleteExpiredOidcTokens(ctx, DeleteExpiredOidcTokensParams(arg))
|
||||||
|
if err != nil {
|
||||||
|
return nil, mapErr(err)
|
||||||
|
}
|
||||||
|
out := make([]repository.OidcToken, len(rows))
|
||||||
|
for i, row := range rows {
|
||||||
|
out[i] = repository.OidcToken(row)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) DeleteExpiredSessions(ctx context.Context, expiry int64) error {
|
||||||
|
return mapErr(s.q.DeleteExpiredSessions(ctx, expiry))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) DeleteOidcCode(ctx context.Context, codeHash string) error {
|
||||||
|
return mapErr(s.q.DeleteOidcCode(ctx, codeHash))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) DeleteOidcCodeBySub(ctx context.Context, sub string) error {
|
||||||
|
return mapErr(s.q.DeleteOidcCodeBySub(ctx, sub))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) DeleteOidcToken(ctx context.Context, accessTokenHash string) error {
|
||||||
|
return mapErr(s.q.DeleteOidcToken(ctx, accessTokenHash))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) DeleteOidcTokenByCodeHash(ctx context.Context, codeHash string) error {
|
||||||
|
return mapErr(s.q.DeleteOidcTokenByCodeHash(ctx, codeHash))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) DeleteOidcTokenBySub(ctx context.Context, sub string) error {
|
||||||
|
return mapErr(s.q.DeleteOidcTokenBySub(ctx, sub))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) DeleteOidcUserInfo(ctx context.Context, sub string) error {
|
||||||
|
return mapErr(s.q.DeleteOidcUserInfo(ctx, sub))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) DeleteSession(ctx context.Context, uuid string) error {
|
||||||
|
return mapErr(s.q.DeleteSession(ctx, uuid))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetOidcCode(ctx context.Context, codeHash string) (repository.OidcCode, error) {
|
||||||
|
r, err := s.q.GetOidcCode(ctx, codeHash)
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcCode{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcCode(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetOidcCodeBySub(ctx context.Context, sub string) (repository.OidcCode, error) {
|
||||||
|
r, err := s.q.GetOidcCodeBySub(ctx, sub)
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcCode{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcCode(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetOidcCodeBySubUnsafe(ctx context.Context, sub string) (repository.OidcCode, error) {
|
||||||
|
r, err := s.q.GetOidcCodeBySubUnsafe(ctx, sub)
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcCode{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcCode(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetOidcCodeUnsafe(ctx context.Context, codeHash string) (repository.OidcCode, error) {
|
||||||
|
r, err := s.q.GetOidcCodeUnsafe(ctx, codeHash)
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcCode{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcCode(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetOidcToken(ctx context.Context, accessTokenHash string) (repository.OidcToken, error) {
|
||||||
|
r, err := s.q.GetOidcToken(ctx, accessTokenHash)
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcToken{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcToken(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetOidcTokenByRefreshToken(ctx context.Context, refreshTokenHash string) (repository.OidcToken, error) {
|
||||||
|
r, err := s.q.GetOidcTokenByRefreshToken(ctx, refreshTokenHash)
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcToken{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcToken(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetOidcTokenBySub(ctx context.Context, sub string) (repository.OidcToken, error) {
|
||||||
|
r, err := s.q.GetOidcTokenBySub(ctx, sub)
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcToken{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcToken(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetOidcUserInfo(ctx context.Context, sub string) (repository.OidcUserinfo, error) {
|
||||||
|
r, err := s.q.GetOidcUserInfo(ctx, sub)
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcUserinfo{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcUserinfo(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetSession(ctx context.Context, uuid string) (repository.Session, error) {
|
||||||
|
r, err := s.q.GetSession(ctx, uuid)
|
||||||
|
if err != nil {
|
||||||
|
return repository.Session{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.Session(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) UpdateOidcTokenByRefreshToken(ctx context.Context, arg repository.UpdateOidcTokenByRefreshTokenParams) (repository.OidcToken, error) {
|
||||||
|
r, err := s.q.UpdateOidcTokenByRefreshToken(ctx, UpdateOidcTokenByRefreshTokenParams(arg))
|
||||||
|
if err != nil {
|
||||||
|
return repository.OidcToken{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.OidcToken(r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) UpdateSession(ctx context.Context, arg repository.UpdateSessionParams) (repository.Session, error) {
|
||||||
|
r, err := s.q.UpdateSession(ctx, UpdateSessionParams(arg))
|
||||||
|
if err != nil {
|
||||||
|
return repository.Session{}, mapErr(err)
|
||||||
|
}
|
||||||
|
return repository.Session(r), nil
|
||||||
|
}
|
||||||
@@ -114,7 +114,7 @@ type LDAPGroupRule struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rule *LDAPGroupRule) Evaluate(ctx *ACLContext) Effect {
|
func (rule *LDAPGroupRule) Evaluate(ctx *ACLContext) Effect {
|
||||||
if ctx == nil || ctx.UserContext == nil || ctx.ACLs == nil {
|
if ctx == nil || ctx.UserContext == nil {
|
||||||
return EffectAbstain
|
return EffectAbstain
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,15 +182,14 @@ type IPAllowedRule struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rule *IPAllowedRule) Evaluate(ctx *ACLContext) Effect {
|
func (rule *IPAllowedRule) Evaluate(ctx *ACLContext) Effect {
|
||||||
// merge global and per-app block/allow lists
|
if ctx.ACLs == nil {
|
||||||
blockedIps := append([]string{}, rule.Config.Auth.IP.Block...)
|
return EffectAbstain
|
||||||
allowedIPs := append([]string{}, rule.Config.Auth.IP.Allow...)
|
|
||||||
|
|
||||||
if ctx.ACLs != nil {
|
|
||||||
blockedIps = append(blockedIps, ctx.ACLs.IP.Block...)
|
|
||||||
allowedIPs = append(allowedIPs, ctx.ACLs.IP.Allow...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge the global and app IP filter
|
||||||
|
blockedIps := append(ctx.ACLs.IP.Block, rule.Config.Auth.IP.Block...)
|
||||||
|
allowedIPs := append(ctx.ACLs.IP.Allow, rule.Config.Auth.IP.Allow...)
|
||||||
|
|
||||||
for _, blocked := range blockedIps {
|
for _, blocked := range blockedIps {
|
||||||
match, err := utils.CheckIPFilter(blocked, ctx.IP.String())
|
match, err := utils.CheckIPFilter(blocked, ctx.IP.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -225,18 +224,15 @@ func (rule *IPAllowedRule) Evaluate(ctx *ACLContext) Effect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type IPBypassedRule struct {
|
type IPBypassedRule struct {
|
||||||
Log *logger.Logger
|
Log *logger.Logger
|
||||||
Config model.Config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *IPBypassedRule) Evaluate(ctx *ACLContext) Effect {
|
func (rule *IPBypassedRule) Evaluate(ctx *ACLContext) Effect {
|
||||||
// merge global and per-app bypass lists
|
if ctx.ACLs == nil {
|
||||||
bypassList := append([]string{}, rule.Config.Auth.IP.Bypass...)
|
return EffectDeny
|
||||||
if ctx.ACLs != nil {
|
|
||||||
bypassList = append(bypassList, ctx.ACLs.IP.Bypass...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bypassed := range bypassList {
|
for _, bypassed := range ctx.ACLs.IP.Bypass {
|
||||||
match, err := utils.CheckIPFilter(bypassed, ctx.IP.String())
|
match, err := utils.CheckIPFilter(bypassed, ctx.IP.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rule.Log.App.Warn().Err(err).Str("item", bypassed).Msg("Invalid IP/CIDR in bypass list")
|
rule.Log.App.Warn().Err(err).Str("item", bypassed).Msg("Invalid IP/CIDR in bypass list")
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
"github.com/tinyauthapp/tinyauth/internal/model"
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
||||||
)
|
)
|
||||||
@@ -559,12 +558,12 @@ func TestIPAllowedRule(t *testing.T) {
|
|||||||
expected Effect
|
expected Effect
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "allows when ACLs are nil and no global lists configured",
|
name: "abstains when ACLs are nil",
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: nil,
|
ACLs: nil,
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
},
|
},
|
||||||
expected: EffectAllow,
|
expected: EffectAbstain,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "denies when IP matches app block list",
|
name: "denies when IP matches app block list",
|
||||||
@@ -670,70 +669,23 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
log := logger.NewLogger().WithTestConfig()
|
log := logger.NewLogger().WithTestConfig()
|
||||||
log.Init()
|
log.Init()
|
||||||
|
|
||||||
defaultIPBR := &IPBypassedRule{Log: log}
|
rule := &IPBypassedRule{Log: log}
|
||||||
globBypassIPBR := &IPBypassedRule{
|
|
||||||
Log: log,
|
|
||||||
Config: model.Config{Auth: model.AuthConfig{IP: model.IPConfig{Bypass: []string{"10.0.0.0/24"}}}},
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
rule *IPBypassedRule
|
|
||||||
ctx *ACLContext
|
ctx *ACLContext
|
||||||
expected Effect
|
expected Effect
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "deny when ACLs are nil and no global bypass",
|
name: "deny when ACLs are nil",
|
||||||
rule: defaultIPBR,
|
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: nil,
|
ACLs: nil,
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
},
|
},
|
||||||
expected: EffectDeny,
|
expected: EffectDeny,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "allows when ACLs are nil but IP matches global bypass",
|
|
||||||
rule: globBypassIPBR,
|
|
||||||
ctx: &ACLContext{
|
|
||||||
ACLs: nil,
|
|
||||||
IP: net.ParseIP("10.0.0.5"),
|
|
||||||
},
|
|
||||||
expected: EffectAllow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "denies when ACLs are nil and IP does not match global bypass",
|
|
||||||
rule: globBypassIPBR,
|
|
||||||
ctx: &ACLContext{
|
|
||||||
ACLs: nil,
|
|
||||||
IP: net.ParseIP("192.168.1.1"),
|
|
||||||
},
|
|
||||||
expected: EffectDeny,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "allows when IP matches per-app bypass but not global bypass",
|
|
||||||
rule: defaultIPBR,
|
|
||||||
ctx: &ACLContext{
|
|
||||||
ACLs: &model.App{
|
|
||||||
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
|
||||||
},
|
|
||||||
IP: net.ParseIP("10.0.0.5"),
|
|
||||||
},
|
|
||||||
expected: EffectAllow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "allows when IP matches global bypass but not per-app bypass",
|
|
||||||
rule: globBypassIPBR,
|
|
||||||
ctx: &ACLContext{
|
|
||||||
ACLs: &model.App{
|
|
||||||
IP: model.AppIP{Bypass: []string{"172.16.0.0/24"}},
|
|
||||||
},
|
|
||||||
IP: net.ParseIP("10.0.0.5"),
|
|
||||||
},
|
|
||||||
expected: EffectAllow,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "allows when IP matches bypass list",
|
name: "allows when IP matches bypass list",
|
||||||
rule: defaultIPBR,
|
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
||||||
@@ -744,7 +696,6 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "denies when IP does not match bypass list",
|
name: "denies when IP does not match bypass list",
|
||||||
rule: defaultIPBR,
|
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
IP: model.AppIP{Bypass: []string{"10.0.0.0/24"}},
|
||||||
@@ -755,7 +706,6 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "denies when bypass list is empty",
|
name: "denies when bypass list is empty",
|
||||||
rule: defaultIPBR,
|
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{},
|
ACLs: &model.App{},
|
||||||
IP: net.ParseIP("10.0.0.1"),
|
IP: net.ParseIP("10.0.0.1"),
|
||||||
@@ -764,7 +714,6 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "skips invalid bypass entries and allows on later match",
|
name: "skips invalid bypass entries and allows on later match",
|
||||||
rule: defaultIPBR,
|
|
||||||
ctx: &ACLContext{
|
ctx: &ACLContext{
|
||||||
ACLs: &model.App{
|
ACLs: &model.App{
|
||||||
IP: model.AppIP{Bypass: []string{"not-an-ip", "10.0.0.1"}},
|
IP: model.AppIP{Bypass: []string{"not-an-ip", "10.0.0.1"}},
|
||||||
@@ -777,7 +726,7 @@ func TestIPBypassedRule(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
assert.Equal(t, tt.expected, tt.rule.Evaluate(tt.ctx))
|
assert.Equal(t, tt.expected, rule.Evaluate(tt.ctx))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,15 +285,10 @@ func (auth *AuthService) RecordLoginAttempt(identifier string, success bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *AuthService) IsEmailWhitelisted(provider string, email string) bool {
|
func (auth *AuthService) IsEmailWhitelisted(email string) bool {
|
||||||
whitelist := auth.runtime.OAuthWhitelist
|
match, err := utils.CheckFilter(strings.Join(auth.runtime.OAuthWhitelist, ","), email)
|
||||||
if providerConfig, ok := auth.runtime.OAuthProviders[provider]; ok && len(providerConfig.Whitelist) > 0 {
|
|
||||||
whitelist = providerConfig.Whitelist
|
|
||||||
}
|
|
||||||
|
|
||||||
match, err := utils.CheckFilter(strings.Join(whitelist, ","), email)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
auth.log.App.Warn().Err(err).Str("provider", provider).Str("email", email).Msg("Invalid email filter pattern")
|
auth.log.App.Warn().Err(err).Str("email", email).Msg("Invalid email filter pattern")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return match
|
return match
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/model"
|
|
||||||
"github.com/tinyauthapp/tinyauth/internal/utils/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIsEmailWhitelistedUsesProviderSpecificList(t *testing.T) {
|
|
||||||
log := logger.NewLogger().WithTestConfig()
|
|
||||||
log.Init()
|
|
||||||
|
|
||||||
auth := &AuthService{
|
|
||||||
log: log,
|
|
||||||
runtime: model.RuntimeConfig{
|
|
||||||
OAuthWhitelist: []string{"global@example.com"},
|
|
||||||
OAuthProviders: map[string]model.OAuthServiceConfig{
|
|
||||||
"github": {
|
|
||||||
Whitelist: []string{"github@example.com"},
|
|
||||||
},
|
|
||||||
"pocketid": {
|
|
||||||
Whitelist: []string{"pocket@example.com"},
|
|
||||||
},
|
|
||||||
"gitlab": {
|
|
||||||
Whitelist: []string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.True(t, auth.IsEmailWhitelisted("github", "github@example.com"))
|
|
||||||
assert.False(t, auth.IsEmailWhitelisted("github", "pocket@example.com"))
|
|
||||||
assert.True(t, auth.IsEmailWhitelisted("pocketid", "pocket@example.com"))
|
|
||||||
assert.True(t, auth.IsEmailWhitelisted("google", "global@example.com"))
|
|
||||||
assert.True(t, auth.IsEmailWhitelisted("gitlab", "global@example.com"))
|
|
||||||
assert.False(t, auth.IsEmailWhitelisted("gitlab", "unknown@example.com"))
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,6 @@ package service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -168,68 +167,6 @@ func (k *KubernetesService) getByAppName(appName string) *model.App {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KubernetesService) extractPaths(rule map[string]any) ([]string, error) {
|
|
||||||
http, found, err := unstructured.NestedMap(rule, "http")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading http from rule: %w", err)
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
paths, found, err := unstructured.NestedSlice(http, "paths")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading http.paths: %w", err)
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
var result []string
|
|
||||||
for _, p := range paths {
|
|
||||||
path, ok := p.(map[string]any)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if p, ok := path["path"].(string); ok && p != "" {
|
|
||||||
result = append(result, p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KubernetesService) extractHosts(item *unstructured.Unstructured) ([]string, error) {
|
|
||||||
rules, found, err := unstructured.NestedSlice(item.Object, "spec", "rules")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading spec.rules: %w", err)
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
var hosts []string
|
|
||||||
for _, r := range rules {
|
|
||||||
rule, ok := r.(map[string]any)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if host, ok := rule["host"].(string); ok && host != "" {
|
|
||||||
hosts = append(hosts, host)
|
|
||||||
}
|
|
||||||
paths, err := k.extractPaths(rule)
|
|
||||||
if err != nil {
|
|
||||||
// This is purely to warn users, it doesn't affect our ability to extract hosts so we won't fail the whole operation
|
|
||||||
k.log.App.Warn().Err(err).Str("namespace", item.GetNamespace()).Str("name", item.GetName()).Msg("Failed to extract paths from ingress rule")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(paths) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !slices.Contains(paths, "/") {
|
|
||||||
k.log.App.Warn().Str("namespace", item.GetNamespace()).Str("name", item.GetName()).Strs("paths", paths).Msg("Ingress rule does not contain a catch-all path, another ingress may be able to bypass auth checks if it routes the same host with a different path. Consider adding a catch-all path to this rule to ensure auth checks are applied to all paths for this host.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
k.log.App.Trace().Strs("hosts", hosts).Msg("Extracted hosts from ingress rules")
|
|
||||||
return hosts, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (k *KubernetesService) updateFromItem(item *unstructured.Unstructured) {
|
func (k *KubernetesService) updateFromItem(item *unstructured.Unstructured) {
|
||||||
namespace := item.GetNamespace()
|
namespace := item.GetNamespace()
|
||||||
name := item.GetName()
|
name := item.GetName()
|
||||||
@@ -238,11 +175,6 @@ func (k *KubernetesService) updateFromItem(item *unstructured.Unstructured) {
|
|||||||
k.removeIngress(namespace, name)
|
k.removeIngress(namespace, name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hosts, err := k.extractHosts(item)
|
|
||||||
if err != nil {
|
|
||||||
k.removeIngress(namespace, name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
labels, err := decoders.DecodeLabels[model.Apps](annotations, "apps")
|
labels, err := decoders.DecodeLabels[model.Apps](annotations, "apps")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
k.log.App.Warn().Err(err).Str("namespace", namespace).Str("name", name).Msg("Failed to decode ingress labels, skipping")
|
k.log.App.Warn().Err(err).Str("namespace", namespace).Str("name", name).Msg("Failed to decode ingress labels, skipping")
|
||||||
@@ -254,10 +186,6 @@ func (k *KubernetesService) updateFromItem(item *unstructured.Unstructured) {
|
|||||||
if appLabels.Config.Domain == "" {
|
if appLabels.Config.Domain == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(hosts) > 0 && !slices.Contains(hosts, appLabels.Config.Domain) {
|
|
||||||
k.log.App.Warn().Str("namespace", namespace).Str("name", name).Str("appName", appName).Str("domain", appLabels.Config.Domain).Msg("App domain does not match any hosts defined in ingress rules, skipping")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
apps = append(apps, ingressApp{
|
apps = append(apps, ingressApp{
|
||||||
domain: appLabels.Config.Domain,
|
domain: appLabels.Config.Domain,
|
||||||
appName: appName,
|
appName: appName,
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
-- name: CreateOidcCode :one
|
||||||
|
INSERT INTO "oidc_codes" (
|
||||||
|
"sub",
|
||||||
|
"code_hash",
|
||||||
|
"scope",
|
||||||
|
"redirect_uri",
|
||||||
|
"client_id",
|
||||||
|
"expires_at",
|
||||||
|
"nonce",
|
||||||
|
"code_challenge"
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8
|
||||||
|
)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: GetOidcCodeUnsafe :one
|
||||||
|
SELECT * FROM "oidc_codes"
|
||||||
|
WHERE "code_hash" = $1;
|
||||||
|
|
||||||
|
-- name: GetOidcCode :one
|
||||||
|
DELETE FROM "oidc_codes"
|
||||||
|
WHERE "code_hash" = $1
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: GetOidcCodeBySubUnsafe :one
|
||||||
|
SELECT * FROM "oidc_codes"
|
||||||
|
WHERE "sub" = $1;
|
||||||
|
|
||||||
|
-- name: GetOidcCodeBySub :one
|
||||||
|
DELETE FROM "oidc_codes"
|
||||||
|
WHERE "sub" = $1
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: DeleteOidcCode :exec
|
||||||
|
DELETE FROM "oidc_codes"
|
||||||
|
WHERE "code_hash" = $1;
|
||||||
|
|
||||||
|
-- name: DeleteOidcCodeBySub :exec
|
||||||
|
DELETE FROM "oidc_codes"
|
||||||
|
WHERE "sub" = $1;
|
||||||
|
|
||||||
|
-- name: CreateOidcToken :one
|
||||||
|
INSERT INTO "oidc_tokens" (
|
||||||
|
"sub",
|
||||||
|
"access_token_hash",
|
||||||
|
"refresh_token_hash",
|
||||||
|
"scope",
|
||||||
|
"client_id",
|
||||||
|
"token_expires_at",
|
||||||
|
"refresh_token_expires_at",
|
||||||
|
"code_hash",
|
||||||
|
"nonce"
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8, $9
|
||||||
|
)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: UpdateOidcTokenByRefreshToken :one
|
||||||
|
UPDATE "oidc_tokens" SET
|
||||||
|
"access_token_hash" = $1,
|
||||||
|
"refresh_token_hash" = $2,
|
||||||
|
"token_expires_at" = $3,
|
||||||
|
"refresh_token_expires_at" = $4
|
||||||
|
WHERE "refresh_token_hash" = $5
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: GetOidcToken :one
|
||||||
|
SELECT * FROM "oidc_tokens"
|
||||||
|
WHERE "access_token_hash" = $1;
|
||||||
|
|
||||||
|
-- name: GetOidcTokenByRefreshToken :one
|
||||||
|
SELECT * FROM "oidc_tokens"
|
||||||
|
WHERE "refresh_token_hash" = $1;
|
||||||
|
|
||||||
|
-- name: GetOidcTokenBySub :one
|
||||||
|
SELECT * FROM "oidc_tokens"
|
||||||
|
WHERE "sub" = $1;
|
||||||
|
|
||||||
|
-- name: DeleteOidcTokenByCodeHash :exec
|
||||||
|
DELETE FROM "oidc_tokens"
|
||||||
|
WHERE "code_hash" = $1;
|
||||||
|
|
||||||
|
-- name: DeleteOidcToken :exec
|
||||||
|
DELETE FROM "oidc_tokens"
|
||||||
|
WHERE "access_token_hash" = $1;
|
||||||
|
|
||||||
|
-- name: DeleteOidcTokenBySub :exec
|
||||||
|
DELETE FROM "oidc_tokens"
|
||||||
|
WHERE "sub" = $1;
|
||||||
|
|
||||||
|
-- name: CreateOidcUserInfo :one
|
||||||
|
INSERT INTO "oidc_userinfo" (
|
||||||
|
"sub",
|
||||||
|
"name",
|
||||||
|
"preferred_username",
|
||||||
|
"email",
|
||||||
|
"groups",
|
||||||
|
"updated_at",
|
||||||
|
"given_name",
|
||||||
|
"family_name",
|
||||||
|
"middle_name",
|
||||||
|
"nickname",
|
||||||
|
"profile",
|
||||||
|
"picture",
|
||||||
|
"website",
|
||||||
|
"gender",
|
||||||
|
"birthdate",
|
||||||
|
"zoneinfo",
|
||||||
|
"locale",
|
||||||
|
"phone_number",
|
||||||
|
"address"
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19
|
||||||
|
)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: GetOidcUserInfo :one
|
||||||
|
SELECT * FROM "oidc_userinfo"
|
||||||
|
WHERE "sub" = $1;
|
||||||
|
|
||||||
|
-- name: DeleteOidcUserInfo :exec
|
||||||
|
DELETE FROM "oidc_userinfo"
|
||||||
|
WHERE "sub" = $1;
|
||||||
|
|
||||||
|
-- name: DeleteExpiredOidcCodes :many
|
||||||
|
DELETE FROM "oidc_codes"
|
||||||
|
WHERE "expires_at" < $1
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: DeleteExpiredOidcTokens :many
|
||||||
|
DELETE FROM "oidc_tokens"
|
||||||
|
WHERE "token_expires_at" < $1 AND "refresh_token_expires_at" < $2
|
||||||
|
RETURNING *;
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS "oidc_codes" (
|
||||||
|
"sub" TEXT NOT NULL UNIQUE,
|
||||||
|
"code_hash" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"scope" TEXT NOT NULL,
|
||||||
|
"redirect_uri" TEXT NOT NULL,
|
||||||
|
"client_id" TEXT NOT NULL,
|
||||||
|
"expires_at" BIGINT NOT NULL,
|
||||||
|
"nonce" TEXT NOT NULL DEFAULT '',
|
||||||
|
"code_challenge" TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "oidc_tokens" (
|
||||||
|
"sub" TEXT NOT NULL UNIQUE,
|
||||||
|
"access_token_hash" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"refresh_token_hash" TEXT NOT NULL,
|
||||||
|
"code_hash" TEXT NOT NULL,
|
||||||
|
"scope" TEXT NOT NULL,
|
||||||
|
"client_id" TEXT NOT NULL,
|
||||||
|
"token_expires_at" BIGINT NOT NULL,
|
||||||
|
"refresh_token_expires_at" BIGINT NOT NULL,
|
||||||
|
"nonce" TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "oidc_userinfo" (
|
||||||
|
"sub" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"preferred_username" TEXT NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"groups" TEXT NOT NULL,
|
||||||
|
"updated_at" BIGINT NOT NULL,
|
||||||
|
"given_name" TEXT NOT NULL,
|
||||||
|
"family_name" TEXT NOT NULL,
|
||||||
|
"middle_name" TEXT NOT NULL,
|
||||||
|
"nickname" TEXT NOT NULL,
|
||||||
|
"profile" TEXT NOT NULL,
|
||||||
|
"picture" TEXT NOT NULL,
|
||||||
|
"website" TEXT NOT NULL,
|
||||||
|
"gender" TEXT NOT NULL,
|
||||||
|
"birthdate" TEXT NOT NULL,
|
||||||
|
"zoneinfo" TEXT NOT NULL,
|
||||||
|
"locale" TEXT NOT NULL,
|
||||||
|
"phone_number" TEXT NOT NULL,
|
||||||
|
"address" TEXT NOT NULL
|
||||||
|
);
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
-- name: CreateSession :one
|
||||||
|
INSERT INTO "sessions" (
|
||||||
|
"uuid",
|
||||||
|
"username",
|
||||||
|
"email",
|
||||||
|
"name",
|
||||||
|
"provider",
|
||||||
|
"totp_pending",
|
||||||
|
"oauth_groups",
|
||||||
|
"expiry",
|
||||||
|
"created_at",
|
||||||
|
"oauth_name",
|
||||||
|
"oauth_sub"
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11
|
||||||
|
)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: GetSession :one
|
||||||
|
SELECT * FROM "sessions"
|
||||||
|
WHERE "uuid" = $1;
|
||||||
|
|
||||||
|
-- name: DeleteSession :exec
|
||||||
|
DELETE FROM "sessions"
|
||||||
|
WHERE "uuid" = $1;
|
||||||
|
|
||||||
|
-- name: UpdateSession :one
|
||||||
|
UPDATE "sessions" SET
|
||||||
|
"username" = $1,
|
||||||
|
"email" = $2,
|
||||||
|
"name" = $3,
|
||||||
|
"provider" = $4,
|
||||||
|
"totp_pending" = $5,
|
||||||
|
"oauth_groups" = $6,
|
||||||
|
"expiry" = $7,
|
||||||
|
"oauth_name" = $8,
|
||||||
|
"oauth_sub" = $9
|
||||||
|
WHERE "uuid" = $10
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: DeleteExpiredSessions :exec
|
||||||
|
DELETE FROM "sessions"
|
||||||
|
WHERE "expiry" < $1;
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS "sessions" (
|
||||||
|
"uuid" TEXT NOT NULL PRIMARY KEY,
|
||||||
|
"username" TEXT NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"provider" TEXT NOT NULL,
|
||||||
|
"totp_pending" BOOLEAN NOT NULL,
|
||||||
|
"oauth_groups" TEXT NOT NULL DEFAULT '',
|
||||||
|
"expiry" BIGINT NOT NULL,
|
||||||
|
"created_at" BIGINT NOT NULL,
|
||||||
|
"oauth_name" TEXT NOT NULL DEFAULT '',
|
||||||
|
"oauth_sub" TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
@@ -28,3 +28,16 @@ sql:
|
|||||||
go_type: "string"
|
go_type: "string"
|
||||||
- column: "oidc_codes.code_challenge"
|
- column: "oidc_codes.code_challenge"
|
||||||
go_type: "string"
|
go_type: "string"
|
||||||
|
- engine: "postgresql"
|
||||||
|
queries: "sql/postgres/*_queries.sql"
|
||||||
|
schema: "sql/postgres/*_schemas.sql"
|
||||||
|
gen:
|
||||||
|
go:
|
||||||
|
package: "postgres"
|
||||||
|
out: "internal/repository/postgres"
|
||||||
|
rename:
|
||||||
|
uuid: "UUID"
|
||||||
|
oauth_groups: "OAuthGroups"
|
||||||
|
oauth_name: "OAuthName"
|
||||||
|
oauth_sub: "OAuthSub"
|
||||||
|
redirect_uri: "RedirectURI"
|
||||||
|
|||||||
Reference in New Issue
Block a user