mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-05-26 22:20:15 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5035cd145c | |||
| 359000f731 | |||
| 0a3e7bf265 | |||
| c3461131f5 |
+5
-66
@@ -7,9 +7,7 @@ TINYAUTH_APPURL=
|
|||||||
|
|
||||||
# database config
|
# database config
|
||||||
|
|
||||||
# The database driver to use. Valid values: sqlite, memory.
|
# The path to the database, including file name.
|
||||||
TINYAUTH_DATABASE_DRIVER="sqlite"
|
|
||||||
# The path to the SQLite database, including file name. Only used when driver is sqlite.
|
|
||||||
TINYAUTH_DATABASE_PATH="./tinyauth.db"
|
TINYAUTH_DATABASE_PATH="./tinyauth.db"
|
||||||
|
|
||||||
# analytics config
|
# analytics config
|
||||||
@@ -32,8 +30,6 @@ TINYAUTH_SERVER_PORT=3000
|
|||||||
TINYAUTH_SERVER_ADDRESS="0.0.0.0"
|
TINYAUTH_SERVER_ADDRESS="0.0.0.0"
|
||||||
# The path to the Unix socket.
|
# The path to the Unix socket.
|
||||||
TINYAUTH_SERVER_SOCKETPATH=
|
TINYAUTH_SERVER_SOCKETPATH=
|
||||||
# Enable listening on both TCP and Unix socket at the same time.
|
|
||||||
TINYAUTH_SERVER_CONCURRENTLISTENERSENABLED=false
|
|
||||||
|
|
||||||
# auth config
|
# auth config
|
||||||
|
|
||||||
@@ -41,52 +37,8 @@ TINYAUTH_SERVER_CONCURRENTLISTENERSENABLED=false
|
|||||||
TINYAUTH_AUTH_IP_ALLOW=
|
TINYAUTH_AUTH_IP_ALLOW=
|
||||||
# List of blocked IPs or CIDR ranges.
|
# List of blocked IPs or CIDR ranges.
|
||||||
TINYAUTH_AUTH_IP_BLOCK=
|
TINYAUTH_AUTH_IP_BLOCK=
|
||||||
# List of IPs or CIDR ranges that bypass authentication entirely.
|
|
||||||
TINYAUTH_AUTH_IP_BYPASS=
|
|
||||||
# Comma-separated list of users (username:hashed_password).
|
# Comma-separated list of users (username:hashed_password).
|
||||||
TINYAUTH_AUTH_USERS=
|
TINYAUTH_AUTH_USERS=
|
||||||
# Enable subdomains support.
|
|
||||||
TINYAUTH_AUTH_SUBDOMAINSENABLED=true
|
|
||||||
# Full name of the user.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_NAME=
|
|
||||||
# Given (first) name of the user.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_GIVENNAME=
|
|
||||||
# Family (last) name of the user.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_FAMILYNAME=
|
|
||||||
# Middle name of the user.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_MIDDLENAME=
|
|
||||||
# Nickname of the user.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_NICKNAME=
|
|
||||||
# URL of the user's profile page.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_PROFILE=
|
|
||||||
# URL of the user's profile picture.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_PICTURE=
|
|
||||||
# URL of the user's website.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_WEBSITE=
|
|
||||||
# Email address of the user.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_EMAIL=
|
|
||||||
# Gender of the user.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_GENDER=
|
|
||||||
# Birthdate of the user (YYYY-MM-DD).
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_BIRTHDATE=
|
|
||||||
# Time zone of the user (e.g. Europe/Athens).
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_ZONEINFO=
|
|
||||||
# Locale of the user (e.g. en-US).
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_LOCALE=
|
|
||||||
# Phone number of the user.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_PHONENUMBER=
|
|
||||||
# Full mailing address, formatted for display.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_ADDRESS_FORMATTED=
|
|
||||||
# Street address.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_ADDRESS_STREETADDRESS=
|
|
||||||
# City or locality.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_ADDRESS_LOCALITY=
|
|
||||||
# State, province, or region.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_ADDRESS_REGION=
|
|
||||||
# Zip or postal code.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_ADDRESS_POSTALCODE=
|
|
||||||
# Country.
|
|
||||||
TINYAUTH_AUTH_USERATTRIBUTES_name_ADDRESS_COUNTRY=
|
|
||||||
# Path to the users file.
|
# Path to the users file.
|
||||||
TINYAUTH_AUTH_USERSFILE=
|
TINYAUTH_AUTH_USERSFILE=
|
||||||
# Enable secure cookies.
|
# Enable secure cookies.
|
||||||
@@ -101,8 +53,6 @@ TINYAUTH_AUTH_LOGINTIMEOUT=300
|
|||||||
TINYAUTH_AUTH_LOGINMAXRETRIES=3
|
TINYAUTH_AUTH_LOGINMAXRETRIES=3
|
||||||
# Comma-separated list of trusted proxy addresses.
|
# Comma-separated list of trusted proxy addresses.
|
||||||
TINYAUTH_AUTH_TRUSTEDPROXIES=
|
TINYAUTH_AUTH_TRUSTEDPROXIES=
|
||||||
# ACL policy for allow-by-default or deny-by-default, available options are allow and deny, default is allow.
|
|
||||||
TINYAUTH_AUTH_ACLS_POLICY="allow"
|
|
||||||
|
|
||||||
# apps config
|
# apps config
|
||||||
|
|
||||||
@@ -151,6 +101,10 @@ 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.
|
||||||
@@ -214,8 +168,6 @@ TINYAUTH_LDAP_AUTHCERT=
|
|||||||
TINYAUTH_LDAP_AUTHKEY=
|
TINYAUTH_LDAP_AUTHKEY=
|
||||||
# Cache duration for LDAP group membership in seconds.
|
# Cache duration for LDAP group membership in seconds.
|
||||||
TINYAUTH_LDAP_GROUPCACHETTL=900
|
TINYAUTH_LDAP_GROUPCACHETTL=900
|
||||||
# Label provider to use for ACLs (auto, docker, kubernetes or none to disable). auto detects the environment.
|
|
||||||
TINYAUTH_LABELPROVIDER="auto"
|
|
||||||
|
|
||||||
# log config
|
# log config
|
||||||
|
|
||||||
@@ -235,16 +187,3 @@ TINYAUTH_LOG_STREAMS_APP_LEVEL=
|
|||||||
TINYAUTH_LOG_STREAMS_AUDIT_ENABLED=false
|
TINYAUTH_LOG_STREAMS_AUDIT_ENABLED=false
|
||||||
# Log level for this stream. Use global if empty.
|
# Log level for this stream. Use global if empty.
|
||||||
TINYAUTH_LOG_STREAMS_AUDIT_LEVEL=
|
TINYAUTH_LOG_STREAMS_AUDIT_LEVEL=
|
||||||
|
|
||||||
# tailscale config
|
|
||||||
|
|
||||||
# Enable Tailscale integration.
|
|
||||||
TINYAUTH_TAILSCALE_ENABLED=false
|
|
||||||
# Tailscale state directory.
|
|
||||||
TINYAUTH_TAILSCALE_DIR="./tailscale_state"
|
|
||||||
# Tailscale hostname.
|
|
||||||
TINYAUTH_TAILSCALE_HOSTNAME=
|
|
||||||
# Tailscale auth key.
|
|
||||||
TINYAUTH_TAILSCALE_AUTHKEY=
|
|
||||||
# Use ephemeral Tailscale node.
|
|
||||||
TINYAUTH_TAILSCALE_EPHEMERAL=false
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ require (
|
|||||||
github.com/golang-migrate/migrate/v4 v4.19.1
|
github.com/golang-migrate/migrate/v4 v4.19.1
|
||||||
github.com/google/go-querystring v1.2.0
|
github.com/google/go-querystring v1.2.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/jackc/pgx/v5 v5.9.2
|
||||||
github.com/mdp/qrterminal/v3 v3.2.1
|
github.com/mdp/qrterminal/v3 v3.2.1
|
||||||
github.com/pquerna/otp v1.5.0
|
github.com/pquerna/otp v1.5.0
|
||||||
github.com/rs/zerolog v1.35.1
|
github.com/rs/zerolog v1.35.1
|
||||||
@@ -20,7 +21,7 @@ require (
|
|||||||
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.52.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.45.0
|
||||||
k8s.io/apimachinery v0.36.1
|
k8s.io/apimachinery v0.36.1
|
||||||
k8s.io/client-go v0.36.1
|
k8s.io/client-go v0.36.1
|
||||||
modernc.org/sqlite v1.50.1
|
modernc.org/sqlite v1.50.1
|
||||||
@@ -90,6 +91,10 @@ 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/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.5 // indirect
|
||||||
@@ -150,7 +155,7 @@ 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.36.0 // indirect
|
||||||
golang.org/x/net v0.54.0 // indirect
|
golang.org/x/net v0.54.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.45.0 // indirect
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa h1:h8TfIT1xc8FWbww
|
|||||||
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
|
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
|
||||||
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc h1:8WFBn63wegobsYAX0YjD+8suexZDga5CctH4CCTx2+8=
|
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc h1:8WFBn63wegobsYAX0YjD+8suexZDga5CctH4CCTx2+8=
|
||||||
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||||
|
github.com/dhui/dktest v0.4.6 h1:+DPKyScKSEp3VLtbMDHcUq6V5Lm5zfZZVb0Sk7Ahom4=
|
||||||
|
github.com/dhui/dktest v0.4.6/go.mod h1:JHTSYDtKkvFNFHJKqCzVzqXecyv+tKt8EzceOmQOgbU=
|
||||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q=
|
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e h1:vUmf0yezR0y7jJ5pceLHthLaYf4bA5T14B6q39S4q2Q=
|
||||||
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A=
|
github.com/digitalocean/go-smbios v0.0.0-20180907143718-390a4f403a8e/go.mod h1:YTIHhz/QFSYnu/EhlF2SpU2Uk+32abacUYA5ZPljz1A=
|
||||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||||
@@ -251,6 +253,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=
|
||||||
@@ -396,6 +408,7 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
|
|||||||
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=
|
||||||
@@ -486,8 +499,8 @@ golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9
|
|||||||
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.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||||
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
|
||||||
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
|
||||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||||
@@ -505,8 +518,8 @@ golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
|||||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||||
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.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
||||||
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
|
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
||||||
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=
|
||||||
|
|||||||
@@ -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");
|
||||||
@@ -117,6 +117,13 @@ 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
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func (app *BootstrapApp) setupServices() error {
|
|||||||
oauthBrokerService := service.NewOAuthBrokerService(app.log, app.runtime.OAuthProviders, app.ctx)
|
oauthBrokerService := service.NewOAuthBrokerService(app.log, app.runtime.OAuthProviders, app.ctx)
|
||||||
app.services.oauthBrokerService = oauthBrokerService
|
app.services.oauthBrokerService = oauthBrokerService
|
||||||
|
|
||||||
authService := service.NewAuthService(app.log, app.config, app.runtime, app.ctx, &app.wg, app.services.ldapService, app.queries, app.services.oauthBrokerService, app.services.tailscaleService)
|
authService := service.NewAuthService(app.log, app.config, app.runtime, app.ctx, &app.wg, app.services.ldapService, app.queries, app.services.oauthBrokerService, app.services.tailscaleService, app.services.policyEngine)
|
||||||
app.services.authService = authService
|
app.services.authService = authService
|
||||||
|
|
||||||
oidcService, err := service.NewOIDCService(app.log, app.config, app.runtime, app.queries, app.ctx, &app.wg)
|
oidcService, err := service.NewOIDCService(app.log, app.config, app.runtime, app.queries, app.ctx, &app.wg)
|
||||||
|
|||||||
@@ -183,9 +183,23 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !controller.auth.IsEmailWhitelisted(user.Email) {
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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, req.Provider, c.ClientIP(), "email not whitelisted")
|
controller.log.AuditLoginFailure(user.Email, svc.ID(), c.ClientIP(), "email not whitelisted")
|
||||||
|
|
||||||
queries, err := query.Values(UnauthorizedQuery{
|
queries, err := query.Values(UnauthorizedQuery{
|
||||||
Username: user.Email,
|
Username: user.Email,
|
||||||
@@ -226,20 +240,6 @@ 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,
|
||||||
|
|||||||
@@ -357,7 +357,6 @@ func TestProxyController(t *testing.T) {
|
|||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
|
broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
|
||||||
authService := service.NewAuthService(log, cfg, runtime, ctx, wg, nil, store, broker, nil)
|
|
||||||
aclsService := service.NewAccessControlsService(log, cfg, nil)
|
aclsService := service.NewAccessControlsService(log, cfg, nil)
|
||||||
|
|
||||||
policyEngine, err := service.NewPolicyEngine(cfg, log)
|
policyEngine, err := service.NewPolicyEngine(cfg, log)
|
||||||
@@ -383,6 +382,8 @@ func TestProxyController(t *testing.T) {
|
|||||||
Log: log,
|
Log: log,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
authService := service.NewAuthService(log, cfg, runtime, ctx, wg, nil, store, broker, nil, policyEngine)
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.description, func(t *testing.T) {
|
t.Run(test.description, func(t *testing.T) {
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
|||||||
@@ -414,8 +414,11 @@ func TestUserController(t *testing.T) {
|
|||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
wg := &sync.WaitGroup{}
|
wg := &sync.WaitGroup{}
|
||||||
|
|
||||||
|
policyEngine, err := service.NewPolicyEngine(cfg, log)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
|
broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
|
||||||
authService := service.NewAuthService(log, cfg, runtime, ctx, wg, nil, store, broker, nil)
|
authService := service.NewAuthService(log, cfg, runtime, ctx, wg, nil, store, broker, nil, policyEngine)
|
||||||
|
|
||||||
beforeEach := func() {
|
beforeEach := func() {
|
||||||
// Clear failed login attempts before each test
|
// Clear failed login attempts before each test
|
||||||
|
|||||||
@@ -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.Email) {
|
if !m.auth.IsEmailWhitelisted(userContext.OAuth.ID, 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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,8 +254,11 @@ func TestContextMiddleware(t *testing.T) {
|
|||||||
|
|
||||||
store := memory.New()
|
store := memory.New()
|
||||||
|
|
||||||
|
policyEngine, err := service.NewPolicyEngine(cfg, log)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
|
broker := service.NewOAuthBrokerService(log, map[string]model.OAuthServiceConfig{}, ctx)
|
||||||
authService := service.NewAuthService(log, cfg, runtime, ctx, wg, nil, store, broker, nil)
|
authService := service.NewAuthService(log, cfg, runtime, ctx, wg, nil, store, broker, nil, policyEngine)
|
||||||
|
|
||||||
contextMiddleware := middleware.NewContextMiddleware(log, runtime, authService, broker, nil)
|
contextMiddleware := middleware.NewContextMiddleware(log, runtime, authService, broker, nil)
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ func NewDefaultConfiguration() *Config {
|
|||||||
PrivateKeyPath: "./tinyauth_oidc_key",
|
PrivateKeyPath: "./tinyauth_oidc_key",
|
||||||
PublicKeyPath: "./tinyauth_oidc_key.pub",
|
PublicKeyPath: "./tinyauth_oidc_key.pub",
|
||||||
},
|
},
|
||||||
|
Experimental: ExperimentalConfig{
|
||||||
|
ConfigFile: "",
|
||||||
|
},
|
||||||
Tailscale: TailscaleConfig{
|
Tailscale: TailscaleConfig{
|
||||||
Dir: "./tailscale_state",
|
Dir: "./tailscale_state",
|
||||||
},
|
},
|
||||||
@@ -85,12 +88,11 @@ type Config struct {
|
|||||||
LabelProvider string `description:"Label provider to use for ACLs (auto, docker, kubernetes or none to disable). auto detects the environment." yaml:"labelProvider"`
|
LabelProvider string `description:"Label provider to use for ACLs (auto, docker, kubernetes or none to disable). auto detects the environment." yaml:"labelProvider"`
|
||||||
Log LogConfig `description:"Logging configuration." yaml:"log"`
|
Log LogConfig `description:"Logging configuration." yaml:"log"`
|
||||||
Tailscale TailscaleConfig `description:"Tailscale configuration." yaml:"tailscale"`
|
Tailscale TailscaleConfig `description:"Tailscale configuration." yaml:"tailscale"`
|
||||||
ConfigFile string `description:"Path to config file." yaml:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@@ -206,8 +208,9 @@ type LogStreamConfig struct {
|
|||||||
Level string `description:"Log level for this stream. Use global if empty." yaml:"level"`
|
Level string `description:"Log level for this stream. Use global if empty." yaml:"level"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// no experimental features
|
type ExperimentalConfig struct {
|
||||||
type ExperimentalConfig struct{}
|
ConfigFile string `description:"Path to config file." yaml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
type TailscaleConfig struct {
|
type TailscaleConfig struct {
|
||||||
Enabled bool `description:"Enable Tailscale integration." yaml:"enabled"`
|
Enabled bool `description:"Enable Tailscale integration." yaml:"enabled"`
|
||||||
@@ -223,6 +226,8 @@ 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
|
||||||
|
}
|
||||||
@@ -75,10 +75,11 @@ type AuthService struct {
|
|||||||
runtime model.RuntimeConfig
|
runtime model.RuntimeConfig
|
||||||
context context.Context
|
context context.Context
|
||||||
|
|
||||||
ldap *LdapService
|
ldap *LdapService
|
||||||
queries repository.Store
|
queries repository.Store
|
||||||
oauthBroker *OAuthBrokerService
|
oauthBroker *OAuthBrokerService
|
||||||
tailscale *TailscaleService
|
tailscale *TailscaleService
|
||||||
|
policyEngine *PolicyEngine
|
||||||
|
|
||||||
loginAttempts map[string]*LoginAttempt
|
loginAttempts map[string]*LoginAttempt
|
||||||
ldapGroupsCache map[string]*LdapGroupsCache
|
ldapGroupsCache map[string]*LdapGroupsCache
|
||||||
@@ -101,6 +102,7 @@ func NewAuthService(
|
|||||||
queries repository.Store,
|
queries repository.Store,
|
||||||
oauthBroker *OAuthBrokerService,
|
oauthBroker *OAuthBrokerService,
|
||||||
tailscale *TailscaleService,
|
tailscale *TailscaleService,
|
||||||
|
policy *PolicyEngine,
|
||||||
) *AuthService {
|
) *AuthService {
|
||||||
service := &AuthService{
|
service := &AuthService{
|
||||||
log: log,
|
log: log,
|
||||||
@@ -114,6 +116,7 @@ func NewAuthService(
|
|||||||
queries: queries,
|
queries: queries,
|
||||||
oauthBroker: oauthBroker,
|
oauthBroker: oauthBroker,
|
||||||
tailscale: tailscale,
|
tailscale: tailscale,
|
||||||
|
policyEngine: policy,
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Go(service.CleanupOAuthSessionsRoutine)
|
wg.Go(service.CleanupOAuthSessionsRoutine)
|
||||||
@@ -285,13 +288,27 @@ func (auth *AuthService) RecordLoginAttempt(identifier string, success bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *AuthService) IsEmailWhitelisted(email string) bool {
|
// We could also directly access the policyEngine.effectToAccess but
|
||||||
match, err := utils.CheckFilter(strings.Join(auth.runtime.OAuthWhitelist, ","), email)
|
// I believe it's better to use the exported functions instead
|
||||||
if err != nil {
|
func (auth *AuthService) IsEmailWhitelisted(provider string, email string) bool {
|
||||||
auth.log.App.Warn().Err(err).Str("email", email).Msg("Invalid email filter pattern")
|
return auth.policyEngine.EvaluateFunc(func() Effect {
|
||||||
return false
|
whitelist := auth.runtime.OAuthWhitelist
|
||||||
}
|
if providerConfig, ok := auth.runtime.OAuthProviders[provider]; ok && len(providerConfig.Whitelist) > 0 {
|
||||||
return match
|
whitelist = providerConfig.Whitelist
|
||||||
|
}
|
||||||
|
match, err := utils.CheckFilter(strings.Join(whitelist, ","), email)
|
||||||
|
if err != nil {
|
||||||
|
if err == utils.ErrFilterEmpty {
|
||||||
|
return EffectAbstain
|
||||||
|
}
|
||||||
|
auth.log.App.Error().Err(err).Str("email", email).Msg("Failed to evaluate email whitelist filter, defaulting to deny")
|
||||||
|
return EffectDeny
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
return EffectAllow
|
||||||
|
}
|
||||||
|
return EffectDeny
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *AuthService) CreateSession(ctx context.Context, data repository.Session) (*http.Cookie, error) {
|
func (auth *AuthService) CreateSession(ctx context.Context, data repository.Session) (*http.Cookie, error) {
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
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"))
|
||||||
|
}
|
||||||
@@ -108,3 +108,7 @@ func (engine *PolicyEngine) Policy() Policy {
|
|||||||
func (engine *PolicyEngine) Rules() map[RuleName]Rule {
|
func (engine *PolicyEngine) Rules() map[RuleName]Rule {
|
||||||
return engine.rules
|
return engine.rules
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (engine *PolicyEngine) EvaluateFunc(f func() Effect) bool {
|
||||||
|
return engine.effectToAccess(f())
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package loaders
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/tinyauthapp/paerser/cli"
|
"github.com/tinyauthapp/paerser/cli"
|
||||||
"github.com/tinyauthapp/paerser/file"
|
"github.com/tinyauthapp/paerser/file"
|
||||||
"github.com/tinyauthapp/paerser/flag"
|
"github.com/tinyauthapp/paerser/flag"
|
||||||
@@ -18,8 +19,8 @@ func (f *FileLoader) Load(args []string, cmd *cli.Command) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// I guess we are using traefik as the root name (we can't change it)
|
// I guess we are using traefik as the root name (we can't change it)
|
||||||
configFileFlag := "traefik.configfile"
|
configFileFlag := "traefik.experimental.configfile"
|
||||||
envVar := "TINYAUTH_CONFIGFILE"
|
envVar := "TINYAUTH_EXPERIMENTAL_CONFIGFILE"
|
||||||
|
|
||||||
if _, ok := flags[configFileFlag]; !ok {
|
if _, ok := flags[configFileFlag]; !ok {
|
||||||
if value := os.Getenv(envVar); value != "" {
|
if value := os.Getenv(envVar); value != "" {
|
||||||
@@ -29,6 +30,8 @@ func (f *FileLoader) Load(args []string, cmd *cli.Command) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Warn().Msg("Using experimental file config loader, this feature is experimental and may change or be removed in future releases")
|
||||||
|
|
||||||
err = file.Decode(flags[configFileFlag], cmd.Configuration)
|
err = file.Decode(flags[configFileFlag], cmd.Configuration)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package utils
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -11,6 +12,10 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrFilterEmpty = errors.New("filter is empty")
|
||||||
|
)
|
||||||
|
|
||||||
func GetSecret(conf string, file string) string {
|
func GetSecret(conf string, file string) string {
|
||||||
if conf == "" && file == "" {
|
if conf == "" && file == "" {
|
||||||
return ""
|
return ""
|
||||||
@@ -78,7 +83,7 @@ func CheckIPFilter(filter string, ip string) (bool, error) {
|
|||||||
|
|
||||||
func CheckFilter(filter string, input string) (bool, error) {
|
func CheckFilter(filter string, input string) (bool, error) {
|
||||||
if len(strings.TrimSpace(filter)) == 0 {
|
if len(strings.TrimSpace(filter)) == 0 {
|
||||||
return false, fmt.Errorf("filter is empty")
|
return false, ErrFilterEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(filter, "/") && strings.HasSuffix(filter, "/") {
|
if strings.HasPrefix(filter, "/") && strings.HasSuffix(filter, "/") {
|
||||||
|
|||||||
@@ -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