update: use jwt
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
DROP TABLE IF EXISTS public.verification;
|
||||
DROP TABLE IF EXISTS public.jwks;
|
||||
DROP TABLE IF EXISTS public.session;
|
||||
DROP TABLE IF EXISTS public.account;
|
||||
DROP TABLE IF EXISTS public."user";
|
||||
|
||||
DROP TABLE IF EXISTS drizzle.__drizzle_migrations;
|
||||
DROP SCHEMA IF EXISTS drizzle;
|
||||
|
||||
DROP EXTENSION IF EXISTS pgcrypto;
|
||||
|
||||
@@ -1,52 +1,88 @@
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public."user" (
|
||||
id text NOT NULL,
|
||||
ssh_identifier text UNIQUE NOT NULL DEFAULT substr(encode(gen_random_bytes(16), 'hex'), 1, 32),
|
||||
name text NOT NULL,
|
||||
email text NOT NULL,
|
||||
email_verified boolean DEFAULT false NOT NULL,
|
||||
image text,
|
||||
created_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT user_pkey PRIMARY KEY (id)
|
||||
CREATE TABLE public."user" (
|
||||
id TEXT PRIMARY KEY,
|
||||
ssh_identifier TEXT NOT NULL DEFAULT substr(encode(gen_random_bytes(16), 'hex'), 1, 32),
|
||||
name TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
email_verified BOOLEAN NOT NULL DEFAULT false,
|
||||
image TEXT,
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.account (
|
||||
id text NOT NULL,
|
||||
account_id text NOT NULL,
|
||||
provider_id text NOT NULL,
|
||||
user_id text NOT NULL,
|
||||
access_token text,
|
||||
refresh_token text,
|
||||
id_token text,
|
||||
access_token_expires_at timestamp without time zone,
|
||||
refresh_token_expires_at timestamp without time zone,
|
||||
scope text,
|
||||
password text,
|
||||
created_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp without time zone NOT NULL,
|
||||
CONSTRAINT account_pkey PRIMARY KEY (id)
|
||||
CREATE UNIQUE INDEX user_email_unique
|
||||
ON public."user"(email);
|
||||
|
||||
CREATE UNIQUE INDEX user_ssh_identifier_unique
|
||||
ON public."user"(ssh_identifier);
|
||||
|
||||
CREATE TABLE public.account (
|
||||
id TEXT PRIMARY KEY,
|
||||
account_id TEXT NOT NULL,
|
||||
provider_id TEXT NOT NULL,
|
||||
user_id TEXT NOT NULL,
|
||||
access_token TEXT,
|
||||
refresh_token TEXT,
|
||||
id_token TEXT,
|
||||
access_token_expires_at TIMESTAMP WITHOUT TIME ZONE,
|
||||
refresh_token_expires_at TIMESTAMP WITHOUT TIME ZONE,
|
||||
scope TEXT,
|
||||
password TEXT,
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.session (
|
||||
id text NOT NULL,
|
||||
expires_at timestamp without time zone NOT NULL,
|
||||
token text NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp without time zone NOT NULL,
|
||||
ip_address text,
|
||||
user_agent text,
|
||||
user_id text NOT NULL,
|
||||
CONSTRAINT session_pkey PRIMARY KEY (id)
|
||||
CREATE INDEX account_userId_idx
|
||||
ON public.account(user_id);
|
||||
|
||||
ALTER TABLE public.account
|
||||
ADD CONSTRAINT account_user_id_user_id_fk
|
||||
FOREIGN KEY (user_id)
|
||||
REFERENCES public."user"(id)
|
||||
ON DELETE CASCADE;
|
||||
|
||||
|
||||
CREATE TABLE public.session (
|
||||
id TEXT PRIMARY KEY,
|
||||
expires_at TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||
token TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||
ip_address TEXT,
|
||||
user_agent TEXT,
|
||||
user_id TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.verification (
|
||||
id text NOT NULL,
|
||||
identifier text NOT NULL,
|
||||
value text NOT NULL,
|
||||
expires_at timestamp without time zone NOT NULL,
|
||||
created_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
updated_at timestamp without time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT verification_pkey PRIMARY KEY (id)
|
||||
CREATE UNIQUE INDEX session_token_unique
|
||||
ON public.session(token);
|
||||
|
||||
CREATE INDEX session_userId_idx
|
||||
ON public.session(user_id);
|
||||
|
||||
ALTER TABLE public.session
|
||||
ADD CONSTRAINT session_user_id_user_id_fk
|
||||
FOREIGN KEY (user_id)
|
||||
REFERENCES public."user"(id)
|
||||
ON DELETE CASCADE;
|
||||
|
||||
|
||||
CREATE TABLE public.jwks (
|
||||
id TEXT PRIMARY KEY,
|
||||
public_key TEXT NOT NULL,
|
||||
private_key TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||
expires_at TIMESTAMP WITHOUT TIME ZONE
|
||||
);
|
||||
|
||||
CREATE TABLE public.verification (
|
||||
id TEXT PRIMARY KEY,
|
||||
identifier TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
expires_at TIMESTAMP WITHOUT TIME ZONE NOT NULL,
|
||||
created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX verification_identifier_idx
|
||||
ON public.verification(identifier);
|
||||
|
||||
27
go.mod
27
go.mod
@@ -3,20 +3,33 @@ module git.fossy.my.id/bagas/tunnel-please-controller
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
git.fossy.my.id/bagas/tunnel-please-grpc v1.0.0
|
||||
github.com/google/uuid v1.6.0
|
||||
git.fossy.my.id/bagas/tunnel-please-grpc v1.2.0
|
||||
github.com/jackc/pgx/v5 v5.8.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.1
|
||||
github.com/lestrrat-go/jwx/v3 v3.0.12
|
||||
google.golang.org/grpc v1.78.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.4 // indirect
|
||||
github.com/lestrrat-go/dsig v1.0.0 // indirect
|
||||
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/lestrrat-go/option/v2 v2.0.0 // indirect
|
||||
github.com/segmentio/asm v1.2.1 // indirect
|
||||
github.com/valyala/fastjson v1.6.4 // indirect
|
||||
golang.org/x/crypto v0.46.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
)
|
||||
|
||||
replace git.fossy.my.id/bagas/tunnel-please-grpc => ../tunnel-please-grpc
|
||||
|
||||
50
go.sum
50
go.sum
@@ -1,12 +1,14 @@
|
||||
git.fossy.my.id/bagas/tunnel-please-grpc v1.0.0 h1:G9U7YmfMBeTK8ASbqdBCmkrRsCNU3e6RUzGaTmg1NB0=
|
||||
git.fossy.my.id/bagas/tunnel-please-grpc v1.0.0/go.mod h1:fG+VkArdkceGB0bNA7IFQus9GetLAwdF5Oi4jdMlXtY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
@@ -23,13 +25,35 @@ 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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA=
|
||||
github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
|
||||
github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38=
|
||||
github.com/lestrrat-go/dsig v1.0.0/go.mod h1:dEgoOYYEJvW6XGbLasr8TFcAxoWrKlbQvmJgCR0qkDo=
|
||||
github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY=
|
||||
github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU=
|
||||
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.1 h1:3n7Es68YYGZb2Jf+k//llA4FTZMl3yCwIjFIk4ubevI=
|
||||
github.com/lestrrat-go/httprc/v3 v3.0.1/go.mod h1:2uAvmbXE4Xq8kAUjVrZOq1tZVYYYs5iP62Cmtru00xk=
|
||||
github.com/lestrrat-go/jwx/v3 v3.0.12 h1:p25r68Y4KrbBdYjIsQweYxq794CtGCzcrc5dGzJIRjg=
|
||||
github.com/lestrrat-go/jwx/v3 v3.0.12/go.mod h1:HiUSaNmMLXgZ08OmGBaPVvoZQgJVOQphSrGr5zMamS8=
|
||||
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||
github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss=
|
||||
github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
|
||||
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
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.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
|
||||
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
@@ -42,18 +66,20 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
|
||||
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
||||
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
|
||||
12
internal/config/config.go
Normal file
12
internal/config/config.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package config
|
||||
|
||||
import "os"
|
||||
|
||||
func Getenv(key, defaultValue string) string {
|
||||
val := os.Getenv(key)
|
||||
if val == "" {
|
||||
val = defaultValue
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
9
main.go
9
main.go
@@ -14,6 +14,8 @@ import (
|
||||
"git.fossy.my.id/bagas/tunnel-please-controller/server"
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/lestrrat-go/httprc/v3"
|
||||
"github.com/lestrrat-go/jwx/v3/jwk"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -55,7 +57,12 @@ func main() {
|
||||
}(connect, ctx)
|
||||
|
||||
repo := repository.New(connect)
|
||||
s := server.New(repo, authToken)
|
||||
client := httprc.NewClient()
|
||||
jwkCache, err := jwk.NewCache(ctx, client)
|
||||
if err != nil {
|
||||
log.Printf("failed to initialize jwk cache : %s", err)
|
||||
}
|
||||
s := server.New(repo, authToken, jwkCache)
|
||||
|
||||
log.Printf("Listening controller on %s", controllerAddr)
|
||||
log.Printf("Listening api on %s", apiAddr)
|
||||
|
||||
@@ -13,7 +13,10 @@ import (
|
||||
"time"
|
||||
|
||||
"git.fossy.my.id/bagas/tunnel-please-controller/db/sqlc/repository"
|
||||
"git.fossy.my.id/bagas/tunnel-please-controller/internal/config"
|
||||
proto "git.fossy.my.id/bagas/tunnel-please-grpc/gen"
|
||||
"github.com/lestrrat-go/jwx/v3/jwk"
|
||||
"github.com/lestrrat-go/jwx/v3/jwt"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/health"
|
||||
@@ -39,17 +42,19 @@ type Server struct {
|
||||
Subscribers map[string]*Subscriber
|
||||
mu *sync.RWMutex
|
||||
authToken string
|
||||
jwkCache *jwk.Cache
|
||||
proto.UnimplementedEventServiceServer
|
||||
proto.UnimplementedSlugChangeServer
|
||||
proto.UnimplementedUserServiceServer
|
||||
}
|
||||
|
||||
func New(database *repository.Queries, authToken string) *Server {
|
||||
func New(database *repository.Queries, authToken string, jwkCache *jwk.Cache) *Server {
|
||||
return &Server{
|
||||
Database: database,
|
||||
Subscribers: make(map[string]*Subscriber),
|
||||
mu: new(sync.RWMutex),
|
||||
authToken: authToken,
|
||||
jwkCache: jwkCache,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,14 +353,63 @@ func (s *Server) StartAPI(ctx context.Context, Addr string) error {
|
||||
IdleTimeout: 60 * time.Second,
|
||||
}
|
||||
|
||||
jwkURL := config.Getenv("JWKS_URL", "")
|
||||
if jwkURL != "" {
|
||||
if err := s.jwkCache.Register(ctx, jwkURL); err != nil {
|
||||
return fmt.Errorf("failed to register jwk cache: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
handler.HandleFunc("/api/sessions", func(writer http.ResponseWriter, request *http.Request) {
|
||||
identity := request.URL.Query().Get("identity")
|
||||
writeError := func(status int, msg string) {
|
||||
writer.Header().Set("Content-Type", "application/json")
|
||||
writer.WriteHeader(status)
|
||||
_ = json.NewEncoder(writer).Encode(map[string]string{"error": msg})
|
||||
}
|
||||
|
||||
var token jwt.Token
|
||||
var err error
|
||||
if jwkURL != "" {
|
||||
keyset, err := s.jwkCache.Lookup(request.Context(), jwkURL)
|
||||
if err != nil {
|
||||
log.Printf("jwks lookup failed: %v", err)
|
||||
writeError(http.StatusBadGateway, "unable to fetch jwks")
|
||||
return
|
||||
}
|
||||
|
||||
token, err = jwt.ParseRequest(request, jwt.WithKeySet(keyset))
|
||||
if err != nil {
|
||||
log.Printf("jwt parse failed: %v", err)
|
||||
writeError(http.StatusUnauthorized, "invalid or expired token")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
token, err = jwt.ParseRequest(request, jwt.WithVerify(false))
|
||||
if err != nil {
|
||||
log.Printf("jwt parse failed (no verification): %v", err)
|
||||
writeError(http.StatusBadRequest, "invalid token")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var email string
|
||||
err = token.Get("email", &email)
|
||||
if err != nil {
|
||||
log.Printf("email claim not found: %v", err)
|
||||
writeError(http.StatusBadRequest, "missing email claim in token")
|
||||
return
|
||||
}
|
||||
if email == "" {
|
||||
writeError(http.StatusBadRequest, "empty email claim in token")
|
||||
return
|
||||
}
|
||||
|
||||
results := s.broadcastAndCollect(request.Context(), func(ctx context.Context, subscriber *Subscriber) (interface{}, bool) {
|
||||
receive, err := s.sendAndReceive(ctx, subscriber, &proto.Events{
|
||||
Type: proto.EventType_GET_SESSIONS,
|
||||
Payload: &proto.Events_GetSessionsEvent{
|
||||
GetSessionsEvent: &proto.GetSessionsEvent{
|
||||
Identity: identity,
|
||||
Identity: email,
|
||||
},
|
||||
},
|
||||
}, defaultSubscriberResponseWait)
|
||||
@@ -382,18 +436,20 @@ func (s *Server) StartAPI(ctx context.Context, Addr string) error {
|
||||
if len(flatten) == 0 {
|
||||
_, err := writer.Write([]byte("[]"))
|
||||
if err != nil {
|
||||
return
|
||||
log.Printf("write empty sessions response failed: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
marshal, err := json.Marshal(flatten)
|
||||
if err != nil {
|
||||
http.Error(writer, "failed to marshal sessions", http.StatusInternalServerError)
|
||||
log.Printf("marshal sessions failed: %v", err)
|
||||
writeError(http.StatusInternalServerError, "failed to marshal sessions")
|
||||
return
|
||||
}
|
||||
_, err = writer.Write(marshal)
|
||||
if err != nil {
|
||||
log.Printf("write sessions response failed: %v", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user