diff options
author | Emile <git@emile.space> | 2025-02-19 19:53:25 +0100 |
---|---|---|
committer | Emile <git@emile.space> | 2025-02-19 19:53:25 +0100 |
commit | ae39f02812bcfe903e956220c890bfb7b9bb9ff4 (patch) | |
tree | dff7028627665a7d2cb7cd64533ac74ec8919379 | |
parent | 07425c679f7399284c0fe3dcbee54f45b23d07a0 (diff) |
removed the backend, added the frontend with oidc support
So I've added oidc support which is nice, yet I have to test this with some https foo, so I'm pushing this.
31 files changed, 1150 insertions, 625 deletions
diff --git a/nix/hosts/corrino/www/templates/goapp/default.nix b/nix/hosts/corrino/www/templates/goapp/default.nix new file mode 100644 index 0000000..716d6ab --- /dev/null +++ b/nix/hosts/corrino/www/templates/goapp/default.nix @@ -0,0 +1,30 @@ + +{ + services.authelia.instances.main.settings.identity_providers.oidc.clients = [ + { + id = "goapp"; + + # ; nix run nixpkgs#authelia -- crypto hash generate pbkdf2 --variant sha512 --random --random.length 72 --random.charset rfc3986 + secret = "$pbkdf2-sha512$310000$WUai4pp1ZVJDrJ8j6ICLiQ$NOMMaCZ3gt.x.a09MWatMkJWQIaH0QeWgRXSbuD2iWRwR.N6MWmJA6QO.LIKcxn6l.zHZN4bO1Ztsrbo9010Tw"; + public = false; + authorization_policy = "two_factor"; + redirect_uris = [ "https://127.0.0.1:8080/auth/oauth2/callback" ]; + scopes = [ + "openid" + "email" + "profile" + ]; + grant_types = [ + "refresh_token" + "authorization_code" + ]; + response_types = [ "code" ]; + response_modes = [ + "form_post" + "query" + "fragment" + ]; + token_endpoint_auth_method = "client_secret_post"; + } + ]; +} diff --git a/nix/templates/goapp/backend/default.nix b/nix/templates/goapp/backend/default.nix deleted file mode 100644 index f4ed3a4..0000000 --- a/nix/templates/goapp/backend/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ pkgs, name, ... }: - -let - version = "0.0.1"; -in -pkgs.buildGoModule { - name = "${name}-${version}"; - pname = "${name}"; - version = "${version}"; - - src = ./.; - subPackages = [ "" ]; - vendorHash = "sha256-tIk8lmyuVETrOW7fA7K7uNNXAAtJAYSM4uH+xZaMWqc="; - - doCheck = true; -} diff --git a/nix/templates/goapp/backend/go.mod b/nix/templates/goapp/backend/go.mod deleted file mode 100644 index 40a7d7f..0000000 --- a/nix/templates/goapp/backend/go.mod +++ /dev/null @@ -1,25 +0,0 @@ -module github.com/hanemile/goapp/backend - -go 1.23.5 - -require ( - github.com/gorilla/handlers v1.5.2 - github.com/gorilla/mux v1.8.1 - github.com/gorilla/securecookie v1.1.2 - github.com/gorilla/sessions v1.4.0 - modernc.org/sqlite v1.34.5 -) - -require ( - github.com/dustin/go-humanize v1.0.1 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.24 // indirect - github.com/ncruces/go-strftime v0.1.9 // indirect - github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - golang.org/x/sys v0.22.0 // indirect - modernc.org/libc v1.55.3 // indirect - modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.8.0 // indirect -) diff --git a/nix/templates/goapp/backend/go.sum b/nix/templates/goapp/backend/go.sum deleted file mode 100644 index 53d3f31..0000000 --- a/nix/templates/goapp/backend/go.sum +++ /dev/null @@ -1,59 +0,0 @@ -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= -github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= -github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= -github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= -github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= -github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= -github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= -github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= -github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= -github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= -github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= -golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= -modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= -modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= -modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= -modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= -modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= -modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= -modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= -modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= -modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= -modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= -modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= -modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= -modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= -modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g= -modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE= -modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= -modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= -modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= -modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/nix/templates/goapp/backend/main.go b/nix/templates/goapp/backend/main.go deleted file mode 100644 index d793869..0000000 --- a/nix/templates/goapp/backend/main.go +++ /dev/null @@ -1,80 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "net/http" - "os" - "time" - - "github.com/gorilla/mux" -) - -var ( - host string - port int - logFilePath string - databasePath string - sessiondbPath string - globalState *State -) - -func initFlags() { - flag.StringVar(&host, "host", "127.0.0.1", "The host to listen on") - flag.StringVar(&host, "h", "127.0.0.1", "The host to listen on (shorthand)") - - flag.IntVar(&port, "port", 8080, "The port to listen on") - flag.IntVar(&port, "p", 8080, "The port to listen on (shorthand)") - - flag.StringVar(&logFilePath, "logfilepath", "./server.log", "The path to the log file") - flag.StringVar(&databasePath, "databasepath", "./main.db", "The path to the main database") - flag.StringVar(&sessiondbPath, "sessiondbpath", "./sessions.db", "The path to the session database") -} - -func indexHandler(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello World from the backend") -} - -func main() { - initFlags() - flag.Parse() - - // log init - log.Println("[i] Setting up logging...") - logFile, err := os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) - if err != nil { - log.Fatal("Error opening the server.log file: ", err) - } - logger := loggingMiddleware{logFile} - - // db init - log.Println("[i] Setting up Global State Struct...") - s, err := NewState() - if err != nil { - log.Fatal("Error creating the NewState(): ", err) - } - globalState = s - - // session init - log.Println("[i] Setting up Session Storage...") - store, err := NewSqliteStore(sessiondbPath, "sessions", "/", 3600, []byte(os.Getenv("SESSION_KEY"))) - if err != nil { - panic(err) - } - globalState.sessions = store - - r := mux.NewRouter() - r.Use(logger.Middleware) - r.HandleFunc("/", indexHandler) - - srv := &http.Server{ - Handler: r, - Addr: ":8080", - WriteTimeout: 15 * time.Second, - ReadTimeout: 15 * time.Second, - } - - log.Printf("[i] Running the server on %s", srv.Addr) - log.Fatal(srv.ListenAndServe()) -} diff --git a/nix/templates/goapp/flake.nix b/nix/templates/goapp/flake.nix index 8303eb3..1ca876f 100644 --- a/nix/templates/goapp/flake.nix +++ b/nix/templates/goapp/flake.nix @@ -30,7 +30,6 @@ in { packages = { } - // (package-and-docker "backend") // (package-and-docker "frontend"); devShells.default = pkgs.mkShell { diff --git a/nix/templates/goapp/frontend/db.go b/nix/templates/goapp/frontend/db.go deleted file mode 100644 index fd3605a..0000000 --- a/nix/templates/goapp/frontend/db.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "database/sql" - "log" - - _ "github.com/mattn/go-sqlite3" -) - -const create string = ` -CREATE TABLE IF NOT EXISTS users ( - id INTEGER NOT NULL PRIMARY KEY, - created_at DATETIME NOT NULL, - name TEXT, - passwordHash TEXT -); -` - -type State struct { - db *sql.DB // the database storing the "business data" - sessions *SqliteStore // the database storing sessions -} - -func NewState() (*State, error) { - db, err := sql.Open("sqlite3", databasePath) - if err != nil { - log.Println("Error opening the db: ", err) - return nil, err - } - if _, err := db.Exec(create); err != nil { - log.Println("Error creating the tables: ", err) - return nil, err - } - return &State{ - db: db, - }, nil -} diff --git a/nix/templates/goapp/frontend/default.nix b/nix/templates/goapp/frontend/default.nix index f4ed3a4..42ccb79 100644 --- a/nix/templates/goapp/frontend/default.nix +++ b/nix/templates/goapp/frontend/default.nix @@ -9,8 +9,8 @@ pkgs.buildGoModule { version = "${version}"; src = ./.; - subPackages = [ "" ]; - vendorHash = "sha256-tIk8lmyuVETrOW7fA7K7uNNXAAtJAYSM4uH+xZaMWqc="; + subPackages = [ "src" ]; + vendorHash = "sha256-VXuhsXejduIcthawj4qu7hruBEDegj27YY0ym5srMQY="; doCheck = true; } diff --git a/nix/templates/goapp/frontend/go.mod b/nix/templates/goapp/frontend/go.mod index 1532a66..fecf4ac 100644 --- a/nix/templates/goapp/frontend/go.mod +++ b/nix/templates/goapp/frontend/go.mod @@ -1,4 +1,4 @@ -module github.com/hanemile/goapp/frontend +module github.com/hanemile/goapp/backend go 1.23.5 @@ -7,18 +7,25 @@ require ( github.com/gorilla/mux v1.8.1 github.com/gorilla/securecookie v1.1.2 github.com/gorilla/sessions v1.4.0 + github.com/mattn/go-sqlite3 v1.14.24 + golang.org/x/crypto v0.33.0 + golang.org/x/oauth2 v0.21.0 modernc.org/sqlite v1.34.5 ) require ( + github.com/coreos/go-oidc/v3 v3.12.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-jose/go-jose/v4 v4.0.2 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-sqlite3 v1.14.24 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - golang.org/x/sys v0.22.0 // indirect + github.com/spf13/cobra v1.9.1 // indirect + github.com/spf13/pflag v1.0.6 // indirect + golang.org/x/sys v0.30.0 // indirect modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.8.0 // indirect diff --git a/nix/templates/goapp/frontend/go.sum b/nix/templates/goapp/frontend/go.sum index 53d3f31..365e2c5 100644 --- a/nix/templates/goapp/frontend/go.sum +++ b/nix/templates/goapp/frontend/go.sum @@ -1,9 +1,14 @@ +github.com/coreos/go-oidc/v3 v3.12.0 h1:sJk+8G2qq94rDI6ehZ71Bol3oUHy63qNYmkiSjrc/Jo= +github.com/coreos/go-oidc/v3 v3.12.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= -github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk= +github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= @@ -18,6 +23,8 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= @@ -26,13 +33,24 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= diff --git a/nix/templates/goapp/frontend/log.go b/nix/templates/goapp/frontend/log.go deleted file mode 100644 index 5af719a..0000000 --- a/nix/templates/goapp/frontend/log.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "net/http" - "os" - - "github.com/gorilla/handlers" -) - -// Defines a middleware containing a logfile -// -// This is done to combine gorilla/handlers with gorilla/mux middlewares to -// just use r.Use(logger.Middleware) once instead of adding this to all -// handlers manually (Yes, I'm really missing macros in Go...) -type loggingMiddleware struct { - logFile *os.File -} - -func (l *loggingMiddleware) Middleware(next http.Handler) http.Handler { - return handlers.LoggingHandler(l.logFile, next) -} - -func authMiddleware(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - session, _ := globalState.sessions.Get(r, "session") - username := session.Values["username"] - - if username == nil { - http.Redirect(w, r, "/login", http.StatusSeeOther) - } else { - next.ServeHTTP(w, r) - } - }) -} diff --git a/nix/templates/goapp/frontend/main.db b/nix/templates/goapp/frontend/main.db new file mode 100644 index 0000000..da9d88e --- /dev/null +++ b/nix/templates/goapp/frontend/main.db Binary files differdiff --git a/nix/templates/goapp/frontend/main.go b/nix/templates/goapp/frontend/main.go deleted file mode 100644 index f6605be..0000000 --- a/nix/templates/goapp/frontend/main.go +++ /dev/null @@ -1,80 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "log" - "net/http" - "os" - "time" - - "github.com/gorilla/mux" -) - -var ( - host string - port int - logFilePath string - databasePath string - sessiondbPath string - globalState *State -) - -func initFlags() { - flag.StringVar(&host, "host", "127.0.0.1", "The host to listen on") - flag.StringVar(&host, "h", "127.0.0.1", "The host to listen on (shorthand)") - - flag.IntVar(&port, "port", 8080, "The port to listen on") - flag.IntVar(&port, "p", 8080, "The port to listen on (shorthand)") - - flag.StringVar(&logFilePath, "logfilepath", "./server.log", "The path to the log file") - flag.StringVar(&databasePath, "databasepath", "./main.db", "The path to the main database") - flag.StringVar(&sessiondbPath, "sessiondbpath", "./sessions.db", "The path to the session database") -} - -func indexHandler(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "Hello World from the frontend") -} - -func main() { - initFlags() - flag.Parse() - - // log init - log.Println("[i] Setting up logging...") - logFile, err := os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) - if err != nil { - log.Fatal("Error opening the server.log file: ", err) - } - logger := loggingMiddleware{logFile} - - // db init - log.Println("[i] Setting up Global State Struct...") - s, err := NewState() - if err != nil { - log.Fatal("Error creating the NewState(): ", err) - } - globalState = s - - // session init - log.Println("[i] Setting up Session Storage...") - store, err := NewSqliteStore(sessiondbPath, "sessions", "/", 3600, []byte(os.Getenv("SESSION_KEY"))) - if err != nil { - panic(err) - } - globalState.sessions = store - - r := mux.NewRouter() - r.Use(logger.Middleware) - r.HandleFunc("/", indexHandler) - - srv := &http.Server{ - Handler: r, - Addr: ":8080", - WriteTimeout: 15 * time.Second, - ReadTimeout: 15 * time.Second, - } - - log.Printf("[i] Running the server on %s", srv.Addr) - log.Fatal(srv.ListenAndServe()) -} diff --git a/nix/templates/goapp/frontend/run.sh b/nix/templates/goapp/frontend/run.sh new file mode 100755 index 0000000..fb3c7b3 --- /dev/null +++ b/nix/templates/goapp/frontend/run.sh @@ -0,0 +1,9 @@ +export CLIENT_ID=goapp +export CLIENT_SECRET=KGFO5LQnUxu1Zs.35gOem3MaG8odthg1U0v0.kScVPS6TPTWVRnAdT_nj4PYYSfuU6jdzTM6 +export CLIENT_CALLBACK_URL=http://localhost:8080/oauth2/callback +export VERSION=0.0.1 +export SESSION_KEY=aes1Itheich4aeQu9Ouz7ahcaiVoogh9 +go run ./... \ + --id goapp \ + --issuer "https://sso.emile.space" \ + --secret "KGFO5LQnUxu1Zs.35gOem3MaG8odthg1U0v0.kScVPS6TPTWVRnAdT_nj4PYYSfuU6jdzTM6" diff --git a/nix/templates/goapp/frontend/server.log b/nix/templates/goapp/frontend/server.log new file mode 100644 index 0000000..4b6cff5 --- /dev/null +++ b/nix/templates/goapp/frontend/server.log @@ -0,0 +1,179 @@ +::1 - - [16/Feb/2025:23:03:46 +0100] "GET / HTTP/1.1" 200 5445 +::1 - - [16/Feb/2025:23:04:16 +0100] "GET / HTTP/1.1" 200 5445 +::1 - - [16/Feb/2025:23:04:17 +0100] "GET / HTTP/1.1" 200 5445 +::1 - - [16/Feb/2025:23:04:58 +0100] "GET / HTTP/1.1" 200 5509 +::1 - - [16/Feb/2025:23:05:03 +0100] "GET / HTTP/1.1" 200 5509 +::1 - - [16/Feb/2025:23:05:23 +0100] "GET / HTTP/1.1" 200 5573 +::1 - - [16/Feb/2025:23:05:33 +0100] "GET / HTTP/1.1" 200 5573 +::1 - - [16/Feb/2025:23:06:07 +0100] "GET / HTTP/1.1" 200 5614 +::1 - - [16/Feb/2025:23:06:17 +0100] "GET / HTTP/1.1" 200 5630 +::1 - - [16/Feb/2025:23:06:37 +0100] "GET / HTTP/1.1" 200 5651 +::1 - - [16/Feb/2025:23:06:59 +0100] "GET / HTTP/1.1" 200 5652 +::1 - - [16/Feb/2025:23:07:28 +0100] "GET / HTTP/1.1" 200 5676 +::1 - - [16/Feb/2025:23:08:26 +0100] "GET / HTTP/1.1" 200 5662 +::1 - - [16/Feb/2025:23:08:30 +0100] "GET / HTTP/1.1" 200 5662 +::1 - - [16/Feb/2025:23:08:42 +0100] "GET / HTTP/1.1" 200 5662 +::1 - - [16/Feb/2025:23:08:43 +0100] "GET / HTTP/1.1" 200 5662 +::1 - - [16/Feb/2025:23:08:54 +0100] "GET / HTTP/1.1" 200 5677 +::1 - - [16/Feb/2025:23:09:25 +0100] "GET / HTTP/1.1" 200 5614 +::1 - - [16/Feb/2025:23:09:44 +0100] "GET / HTTP/1.1" 200 5639 +::1 - - [16/Feb/2025:23:10:13 +0100] "GET / HTTP/1.1" 200 5661 +::1 - - [16/Feb/2025:23:10:48 +0100] "GET / HTTP/1.1" 200 5672 +::1 - - [16/Feb/2025:23:10:49 +0100] "GET / HTTP/1.1" 200 5672 +::1 - - [16/Feb/2025:23:11:32 +0100] "GET / HTTP/1.1" 200 5648 +::1 - - [16/Feb/2025:23:11:32 +0100] "GET / HTTP/1.1" 200 5648 +::1 - - [16/Feb/2025:23:11:57 +0100] "GET / HTTP/1.1" 200 5655 +::1 - - [16/Feb/2025:23:12:11 +0100] "GET / HTTP/1.1" 200 5675 +::1 - - [16/Feb/2025:23:12:13 +0100] "GET / HTTP/1.1" 200 5675 +::1 - - [16/Feb/2025:23:12:46 +0100] "GET / HTTP/1.1" 200 5724 +::1 - - [16/Feb/2025:23:12:54 +0100] "GET / HTTP/1.1" 200 5722 +::1 - - [16/Feb/2025:23:13:03 +0100] "GET / HTTP/1.1" 200 5721 +::1 - - [16/Feb/2025:23:13:11 +0100] "GET / HTTP/1.1" 200 5721 +::1 - - [16/Feb/2025:23:13:42 +0100] "GET / HTTP/1.1" 200 5719 +::1 - - [17/Feb/2025:10:27:07 +0100] "GET / HTTP/1.1" 200 5719 +::1 - - [17/Feb/2025:10:27:09 +0100] "GET / HTTP/1.1" 200 5719 +::1 - - [17/Feb/2025:10:28:32 +0100] "GET / HTTP/1.1" 200 5854 +::1 - - [17/Feb/2025:10:28:40 +0100] "GET / HTTP/1.1" 200 5854 +::1 - - [17/Feb/2025:10:28:46 +0100] "GET / HTTP/1.1" 200 5854 +::1 - - [17/Feb/2025:10:28:52 +0100] "GET / HTTP/1.1" 200 5854 +::1 - - [17/Feb/2025:10:29:22 +0100] "GET / HTTP/1.1" 200 5858 +::1 - - [17/Feb/2025:10:29:54 +0100] "GET / HTTP/1.1" 200 5841 +::1 - - [17/Feb/2025:10:30:09 +0100] "GET / HTTP/1.1" 200 5802 +::1 - - [17/Feb/2025:10:30:10 +0100] "GET / HTTP/1.1" 200 5802 +::1 - - [17/Feb/2025:10:30:20 +0100] "GET / HTTP/1.1" 200 5866 +::1 - - [17/Feb/2025:10:31:14 +0100] "GET / HTTP/1.1" 200 5866 +::1 - - [17/Feb/2025:10:31:26 +0100] "GET / HTTP/1.1" 200 5866 +::1 - - [17/Feb/2025:10:31:31 +0100] "GET / HTTP/1.1" 200 5866 +::1 - - [17/Feb/2025:10:31:47 +0100] "GET / HTTP/1.1" 200 5786 +::1 - - [17/Feb/2025:10:34:25 +0100] "GET / HTTP/1.1" 200 5786 +::1 - - [17/Feb/2025:10:35:22 +0100] "GET / HTTP/1.1" 200 5786 +::1 - - [17/Feb/2025:10:36:10 +0100] "GET / HTTP/1.1" 200 5786 +::1 - - [17/Feb/2025:10:37:10 +0100] "POST /submit HTTP/1.1" 200 10 +::1 - - [17/Feb/2025:10:37:51 +0100] "POST /submit HTTP/1.1" 200 15 +::1 - - [17/Feb/2025:10:37:55 +0100] "GET / HTTP/1.1" 200 5786 +::1 - - [17/Feb/2025:10:37:59 +0100] "POST /submit HTTP/1.1" 200 15 +::1 - - [17/Feb/2025:10:38:26 +0100] "POST /submit HTTP/1.1" 200 35 +::1 - - [17/Feb/2025:10:38:46 +0100] "POST /submit HTTP/1.1" 200 21 +::1 - - [17/Feb/2025:10:40:03 +0100] "GET / HTTP/1.1" 500 33 +::1 - - [17/Feb/2025:10:40:28 +0100] "GET / HTTP/1.1" 500 33 +::1 - - [17/Feb/2025:10:41:20 +0100] "GET / HTTP/1.1" 200 5786 +::1 - - [17/Feb/2025:10:41:38 +0100] "GET / HTTP/1.1" 200 5806 +::1 - - [17/Feb/2025:10:41:44 +0100] "GET / HTTP/1.1" 200 5806 +::1 - - [17/Feb/2025:10:41:53 +0100] "GET / HTTP/1.1" 200 6331 +::1 - - [17/Feb/2025:10:42:24 +0100] "GET / HTTP/1.1" 200 6238 +::1 - - [17/Feb/2025:10:43:00 +0100] "GET / HTTP/1.1" 200 6268 +::1 - - [17/Feb/2025:10:43:10 +0100] "GET / HTTP/1.1" 200 6260 +::1 - - [17/Feb/2025:10:44:54 +0100] "GET / HTTP/1.1" 200 6260 +::1 - - [17/Feb/2025:10:46:05 +0100] "GET / HTTP/1.1" 200 6398 +::1 - - [17/Feb/2025:10:49:07 +0100] "GET / HTTP/1.1" 200 6398 +::1 - - [17/Feb/2025:10:51:15 +0100] "GET / HTTP/1.1" 200 6398 +::1 - - [17/Feb/2025:10:51:16 +0100] "GET / HTTP/1.1" 200 6398 +::1 - - [17/Feb/2025:10:51:26 +0100] "GET / HTTP/1.1" 200 6398 +::1 - - [17/Feb/2025:10:51:27 +0100] "GET / HTTP/1.1" 200 6398 +::1 - - [17/Feb/2025:10:51:45 +0100] "GET /login HTTP/1.1" 200 0 +::1 - - [17/Feb/2025:10:51:59 +0100] "GET /login HTTP/1.1" 200 0 +::1 - - [17/Feb/2025:10:52:56 +0100] "GET /login HTTP/1.1" 200 0 +::1 - - [17/Feb/2025:10:52:58 +0100] "GET /login HTTP/1.1" 200 0 +::1 - - [17/Feb/2025:10:52:59 +0100] "GET /login HTTP/1.1" 200 0 +::1 - - [17/Feb/2025:10:53:11 +0100] "GET /login HTTP/1.1" 200 0 +::1 - - [17/Feb/2025:10:53:13 +0100] "GET /login HTTP/1.1" 200 0 +::1 - - [17/Feb/2025:10:53:29 +0100] "GET /login HTTP/1.1" 200 0 +::1 - - [17/Feb/2025:10:53:30 +0100] "GET /login HTTP/1.1" 200 0 +::1 - - [17/Feb/2025:10:54:04 +0100] "GET /login HTTP/1.1" 200 6974 +::1 - - [17/Feb/2025:10:54:11 +0100] "GET /login HTTP/1.1" 200 6917 +::1 - - [17/Feb/2025:10:54:28 +0100] "GET /login HTTP/1.1" 200 6724 +::1 - - [17/Feb/2025:10:54:28 +0100] "GET /login HTTP/1.1" 200 6724 +::1 - - [17/Feb/2025:10:54:30 +0100] "GET /login HTTP/1.1" 200 6724 +::1 - - [17/Feb/2025:10:54:33 +0100] "GET /login HTTP/1.1" 200 6724 +::1 - - [17/Feb/2025:10:54:35 +0100] "GET /login HTTP/1.1" 200 6724 +::1 - - [17/Feb/2025:10:54:39 +0100] "GET /login HTTP/1.1" 200 6724 +::1 - - [17/Feb/2025:10:54:42 +0100] "GET / HTTP/1.1" 200 6398 +::1 - - [17/Feb/2025:10:54:55 +0100] "GET / HTTP/1.1" 200 6388 +::1 - - [17/Feb/2025:10:54:58 +0100] "POST /submit HTTP/1.1" 200 21 +::1 - - [17/Feb/2025:10:58:19 +0100] "GET / HTTP/1.1" 200 6388 +::1 - - [17/Feb/2025:10:58:42 +0100] "GET / HTTP/1.1" 200 6316 +::1 - - [17/Feb/2025:10:58:43 +0100] "GET / HTTP/1.1" 200 6316 +::1 - - [17/Feb/2025:10:58:43 +0100] "GET / HTTP/1.1" 200 6316 +::1 - - [17/Feb/2025:13:44:10 +0100] "GET / HTTP/1.1" 200 6316 +::1 - - [17/Feb/2025:13:44:11 +0100] "GET /login HTTP/1.1" 200 6724 +::1 - - [17/Feb/2025:13:44:23 +0100] "GET /login HTTP/1.1" 200 6724 +::1 - - [17/Feb/2025:14:37:43 +0100] "GET /login HTTP/1.1" 500 56 +::1 - - [17/Feb/2025:14:38:57 +0100] "GET / HTTP/1.1" 500 33 +::1 - - [17/Feb/2025:14:40:38 +0100] "GET / HTTP/1.1" 200 5683 +::1 - - [17/Feb/2025:14:43:24 +0100] "GET / HTTP/1.1" 200 5683 +::1 - - [17/Feb/2025:14:45:41 +0100] "GET / HTTP/1.1" 200 5683 +::1 - - [17/Feb/2025:14:46:07 +0100] "GET / HTTP/1.1" 200 5683 +::1 - - [17/Feb/2025:14:46:14 +0100] "GET / HTTP/1.1" 200 5894 +::1 - - [17/Feb/2025:14:46:23 +0100] "GET / HTTP/1.1" 200 5734 +::1 - - [17/Feb/2025:14:47:02 +0100] "GET / HTTP/1.1" 200 5835 +::1 - - [17/Feb/2025:14:47:14 +0100] "GET / HTTP/1.1" 200 5873 +::1 - - [17/Feb/2025:14:47:22 +0100] "GET / HTTP/1.1" 200 5899 +::1 - - [17/Feb/2025:14:48:10 +0100] "GET / HTTP/1.1" 200 5790 +::1 - - [17/Feb/2025:14:48:15 +0100] "GET / HTTP/1.1" 200 5899 +::1 - - [17/Feb/2025:14:48:47 +0100] "GET / HTTP/1.1" 200 5899 +::1 - - [17/Feb/2025:14:48:53 +0100] "GET / HTTP/1.1" 200 5790 +::1 - - [17/Feb/2025:14:49:07 +0100] "GET / HTTP/1.1" 200 5927 +::1 - - [17/Feb/2025:14:49:16 +0100] "GET / HTTP/1.1" 200 5790 +::1 - - [17/Feb/2025:14:49:24 +0100] "GET / HTTP/1.1" 200 5919 +::1 - - [17/Feb/2025:14:51:08 +0100] "GET / HTTP/1.1" 500 33 +::1 - - [17/Feb/2025:14:51:48 +0100] "GET / HTTP/1.1" 200 6285 +::1 - - [17/Feb/2025:14:52:24 +0100] "GET / HTTP/1.1" 200 6224 +::1 - - [17/Feb/2025:14:53:46 +0100] "GET / HTTP/1.1" 200 6242 +::1 - - [17/Feb/2025:14:54:34 +0100] "GET / HTTP/1.1" 200 6222 +::1 - - [17/Feb/2025:14:55:14 +0100] "GET / HTTP/1.1" 200 6284 +::1 - - [17/Feb/2025:14:55:18 +0100] "GET / HTTP/1.1" 200 6284 +::1 - - [17/Feb/2025:14:55:45 +0100] "GET / HTTP/1.1" 200 6360 +::1 - - [17/Feb/2025:14:57:06 +0100] "GET / HTTP/1.1" 500 33 +::1 - - [17/Feb/2025:14:57:43 +0100] "GET / HTTP/1.1" 500 33 +::1 - - [17/Feb/2025:14:58:27 +0100] "GET / HTTP/1.1" 500 33 +::1 - - [17/Feb/2025:15:01:47 +0100] "GET / HTTP/1.1" 500 33 +::1 - - [17/Feb/2025:15:02:03 +0100] "GET / HTTP/1.1" 500 33 +::1 - - [17/Feb/2025:15:03:49 +0100] "GET / HTTP/1.1" 200 6360 +::1 - - [17/Feb/2025:15:06:07 +0100] "GET / HTTP/1.1" 200 6412 +::1 - - [17/Feb/2025:15:07:19 +0100] "GET / HTTP/1.1" 200 6412 +::1 - - [17/Feb/2025:15:07:38 +0100] "GET / HTTP/1.1" 200 6412 +::1 - - [17/Feb/2025:15:07:46 +0100] "GET / HTTP/1.1" 200 6514 +::1 - - [17/Feb/2025:15:07:58 +0100] "GET / HTTP/1.1" 200 6491 +::1 - - [17/Feb/2025:15:08:04 +0100] "GET /login HTTP/1.1" 500 56 +::1 - - [17/Feb/2025:15:08:56 +0100] "GET /login HTTP/1.1" 302 87 +::1 - - [17/Feb/2025:15:08:56 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6491 +::1 - - [17/Feb/2025:15:08:58 +0100] "GET /login HTTP/1.1" 302 87 +::1 - - [17/Feb/2025:15:08:58 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6491 +::1 - - [17/Feb/2025:15:20:07 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6586 +::1 - - [17/Feb/2025:15:20:41 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6587 +::1 - - [17/Feb/2025:15:20:45 +0100] "GET /login HTTP/1.1" 302 87 +::1 - - [17/Feb/2025:15:20:45 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6587 +::1 - - [17/Feb/2025:15:20:53 +0100] "GET /login HTTP/1.1" 302 87 +::1 - - [17/Feb/2025:15:20:53 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6587 +::1 - - [17/Feb/2025:15:24:15 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6587 +::1 - - [17/Feb/2025:15:24:16 +0100] "GET /login HTTP/1.1" 302 87 +::1 - - [17/Feb/2025:15:24:16 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:18:57:18 +0100] "GET / HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:18:57:19 +0100] "GET /login HTTP/1.1" 302 87 +::1 - - [19/Feb/2025:18:57:19 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:18:57:44 +0100] "GET /login HTTP/1.1" 302 87 +::1 - - [19/Feb/2025:18:57:44 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:18:58:54 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:18:58:55 +0100] "GET /login HTTP/1.1" 302 87 +::1 - - [19/Feb/2025:18:58:55 +0100] "GET /?client_id=&response_type=code&state=random-string-here HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:19:06:37 +0100] "GET /login HTTP/1.1" 302 242 +::1 - - [19/Feb/2025:19:10:24 +0100] "GET /login HTTP/1.1" 302 242 +::1 - - [19/Feb/2025:19:10:36 +0100] "GET /login HTTP/1.1" 302 242 +::1 - - [19/Feb/2025:19:14:14 +0100] "GET / HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:19:14:16 +0100] "GET /login HTTP/1.1" 302 242 +::1 - - [19/Feb/2025:19:15:01 +0100] "GET /login HTTP/1.1" 302 242 +::1 - - [19/Feb/2025:19:16:08 +0100] "GET / HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:19:16:10 +0100] "GET /login HTTP/1.1" 302 242 +::1 - - [19/Feb/2025:19:18:51 +0100] "GET / HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:19:18:53 +0100] "GET /login HTTP/1.1" 302 242 +::1 - - [19/Feb/2025:19:23:35 +0100] "GET / HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:19:23:37 +0100] "GET /login HTTP/1.1" 302 242 +::1 - - [19/Feb/2025:19:25:20 +0100] "GET /oauth2/callback HTTP/1.1" 500 314 +::1 - - [19/Feb/2025:19:25:37 +0100] "GET /oauth2/callback?code=authelia_ac_lA-8rLxGY4flmo-_DerONxfFPIVk2vpMiaCYZh_6ke0.FBoivMumLtPFauH9sWNVRz51S0FqWjwlFtqKO5sEA88&iss=https%3A%2F%2Fsso.emile.space&scope=openid+profile+email+groups&state=random-string-here HTTP/1.1" 500 314 +::1 - - [19/Feb/2025:19:27:27 +0100] "GET / HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:19:27:28 +0100] "GET /login HTTP/1.1" 302 242 +::1 - - [19/Feb/2025:19:43:04 +0100] "GET / HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:19:43:05 +0100] "GET /login HTTP/1.1" 302 242 +::1 - - [19/Feb/2025:19:43:29 +0100] "GET /oauth2/callback?code=authelia_ac_8UdV__GJCN9gxJrYa629TC3FToyDDhsbacPbJzhvcJ4.uPw2-_N4jQr7xf7JNZ_IZBNHEq-eeOFoZup7Vwjx1Y0&iss=https%3A%2F%2Fsso.emile.space&scope=openid+profile+email+groups&state=random-string-here HTTP/1.1" 500 142 +::1 - - [19/Feb/2025:19:49:22 +0100] "GET / HTTP/1.1" 200 6587 +::1 - - [19/Feb/2025:19:49:23 +0100] "GET /login HTTP/1.1" 302 242 diff --git a/nix/templates/goapp/frontend/sessions.db b/nix/templates/goapp/frontend/sessions.db new file mode 100644 index 0000000..04d6727 --- /dev/null +++ b/nix/templates/goapp/frontend/sessions.db Binary files differdiff --git a/nix/templates/goapp/frontend/sqlitestore.go b/nix/templates/goapp/frontend/sqlitestore.go deleted file mode 100644 index 6f59d15..0000000 --- a/nix/templates/goapp/frontend/sqlitestore.go +++ /dev/null @@ -1,284 +0,0 @@ -/* - Gorilla Sessions backend for SQLite. - -Copyright (c) 2013 Contributors. See the list of contributors in the CONTRIBUTORS file for details. - -This software is licensed under a MIT style license available in the LICENSE file. -*/ -package main - -import ( - "database/sql" - "encoding/gob" - "errors" - "fmt" - "log" - "net/http" - "strings" - "time" - - "github.com/gorilla/securecookie" - "github.com/gorilla/sessions" - _ "modernc.org/sqlite" -) - -type SqliteStore struct { - db DB - stmtInsert *sql.Stmt - stmtDelete *sql.Stmt - stmtUpdate *sql.Stmt - stmtSelect *sql.Stmt - - Codecs []securecookie.Codec - Options *sessions.Options - table string -} - -type sessionRow struct { - id string - data string - createdOn time.Time - modifiedOn time.Time - expiresOn time.Time -} - -type DB interface { - Exec(query string, args ...interface{}) (sql.Result, error) - Prepare(query string) (*sql.Stmt, error) - Close() error -} - -func init() { - gob.Register(time.Time{}) -} - -func NewSqliteStore(endpoint string, tableName string, path string, maxAge int, keyPairs ...[]byte) (*SqliteStore, error) { - db, err := sql.Open("sqlite3", endpoint) - if err != nil { - return nil, err - } - - return NewSqliteStoreFromConnection(db, tableName, path, maxAge, keyPairs...) -} - -func NewSqliteStoreFromConnection(db DB, tableName string, path string, maxAge int, keyPairs ...[]byte) (*SqliteStore, error) { - // Make sure table name is enclosed. - tableName = "`" + strings.Trim(tableName, "`") + "`" - - cTableQ := "CREATE TABLE IF NOT EXISTS " + - tableName + " (id INTEGER PRIMARY KEY, " + - "session_data LONGBLOB, " + - "created_on TIMESTAMP DEFAULT 0, " + - "modified_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP, " + - "expires_on TIMESTAMP DEFAULT 0);" - if _, err := db.Exec(cTableQ); err != nil { - return nil, err - } - - insQ := "INSERT INTO " + tableName + - "(id, session_data, created_on, modified_on, expires_on) VALUES (NULL, ?, ?, ?, ?)" - stmtInsert, stmtErr := db.Prepare(insQ) - if stmtErr != nil { - return nil, stmtErr - } - - delQ := "DELETE FROM " + tableName + " WHERE id = ?" - stmtDelete, stmtErr := db.Prepare(delQ) - if stmtErr != nil { - return nil, stmtErr - } - - updQ := "UPDATE " + tableName + " SET session_data = ?, created_on = ?, expires_on = ? " + - "WHERE id = ?" - stmtUpdate, stmtErr := db.Prepare(updQ) - if stmtErr != nil { - return nil, stmtErr - } - - selQ := "SELECT id, session_data, created_on, modified_on, expires_on from " + - tableName + " WHERE id = ?" - stmtSelect, stmtErr := db.Prepare(selQ) - if stmtErr != nil { - return nil, stmtErr - } - - return &SqliteStore{ - db: db, - stmtInsert: stmtInsert, - stmtDelete: stmtDelete, - stmtUpdate: stmtUpdate, - stmtSelect: stmtSelect, - Codecs: securecookie.CodecsFromPairs(keyPairs...), - Options: &sessions.Options{ - Path: path, - MaxAge: maxAge, - }, - table: tableName, - }, nil -} - -func (m *SqliteStore) Close() { - m.stmtSelect.Close() - m.stmtUpdate.Close() - m.stmtDelete.Close() - m.stmtInsert.Close() - m.db.Close() -} - -func (m *SqliteStore) Get(r *http.Request, name string) (*sessions.Session, error) { - return sessions.GetRegistry(r).Get(m, name) -} - -func (m *SqliteStore) New(r *http.Request, name string) (*sessions.Session, error) { - session := sessions.NewSession(m, name) - session.Options = &sessions.Options{ - Path: m.Options.Path, - MaxAge: m.Options.MaxAge, - } - session.IsNew = true - var err error - if cook, errCookie := r.Cookie(name); errCookie == nil { - err = securecookie.DecodeMulti(name, cook.Value, &session.ID, m.Codecs...) - if err == nil { - err = m.load(session) - if err == nil { - session.IsNew = false - } else { - err = nil - } - } - } - return session, err -} - -func (m *SqliteStore) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error { - var err error - if session.ID == "" { - if err = m.insert(session); err != nil { - return err - } - } else if err = m.save(session); err != nil { - return err - } - encoded, err := securecookie.EncodeMulti(session.Name(), session.ID, m.Codecs...) - if err != nil { - return err - } - http.SetCookie(w, sessions.NewCookie(session.Name(), encoded, session.Options)) - return nil -} - -func (m *SqliteStore) insert(session *sessions.Session) error { - var createdOn time.Time - var modifiedOn time.Time - var expiresOn time.Time - crOn := session.Values["created_on"] - if crOn == nil { - createdOn = time.Now() - } else { - createdOn = crOn.(time.Time) - } - modifiedOn = createdOn - exOn := session.Values["expires_on"] - if exOn == nil { - expiresOn = time.Now().Add(time.Second * time.Duration(session.Options.MaxAge)) - } else { - expiresOn = exOn.(time.Time) - } - delete(session.Values, "created_on") - delete(session.Values, "expires_on") - delete(session.Values, "modified_on") - - encoded, encErr := securecookie.EncodeMulti(session.Name(), session.Values, m.Codecs...) - if encErr != nil { - return encErr - } - res, insErr := m.stmtInsert.Exec(encoded, createdOn, modifiedOn, expiresOn) - if insErr != nil { - return insErr - } - lastInserted, lInsErr := res.LastInsertId() - if lInsErr != nil { - return lInsErr - } - session.ID = fmt.Sprintf("%d", lastInserted) - return nil -} - -func (m *SqliteStore) Delete(r *http.Request, w http.ResponseWriter, session *sessions.Session) error { - - // Set cookie to expire. - options := *session.Options - options.MaxAge = -1 - http.SetCookie(w, sessions.NewCookie(session.Name(), "", &options)) - // Clear session values. - for k := range session.Values { - delete(session.Values, k) - } - - _, delErr := m.stmtDelete.Exec(session.ID) - if delErr != nil { - return delErr - } - return nil -} - -func (m *SqliteStore) save(session *sessions.Session) error { - if session.IsNew == true { - return m.insert(session) - } - var createdOn time.Time - var expiresOn time.Time - crOn := session.Values["created_on"] - if crOn == nil { - createdOn = time.Now() - } else { - createdOn = crOn.(time.Time) - } - - exOn := session.Values["expires_on"] - if exOn == nil { - expiresOn = time.Now().Add(time.Second * time.Duration(session.Options.MaxAge)) - log.Print("nil") - } else { - expiresOn = exOn.(time.Time) - if expiresOn.Sub(time.Now().Add(time.Second*time.Duration(session.Options.MaxAge))) < 0 { - expiresOn = time.Now().Add(time.Second * time.Duration(session.Options.MaxAge)) - } - } - - delete(session.Values, "created_on") - delete(session.Values, "expires_on") - delete(session.Values, "modified_on") - encoded, encErr := securecookie.EncodeMulti(session.Name(), session.Values, m.Codecs...) - if encErr != nil { - return encErr - } - _, updErr := m.stmtUpdate.Exec(encoded, createdOn, expiresOn, session.ID) - if updErr != nil { - return updErr - } - return nil -} - -func (m *SqliteStore) load(session *sessions.Session) error { - row := m.stmtSelect.QueryRow(session.ID) - sess := sessionRow{} - scanErr := row.Scan(&sess.id, &sess.data, &sess.createdOn, &sess.modifiedOn, &sess.expiresOn) - if scanErr != nil { - return scanErr - } - if sess.expiresOn.Sub(time.Now()) < 0 { - log.Printf("Session expired on %s, but it is %s now.", sess.expiresOn, time.Now()) - return errors.New("Session expired") - } - err := securecookie.DecodeMulti(session.Name(), sess.data, &session.Values, m.Codecs...) - if err != nil { - return err - } - session.Values["created_on"] = sess.createdOn - session.Values["modified_on"] = sess.modifiedOn - session.Values["expires_on"] = sess.expiresOn - return nil - -} diff --git a/nix/templates/goapp/backend/db.go b/nix/templates/goapp/frontend/src/db.go index fd3605a..fd3605a 100644 --- a/nix/templates/goapp/backend/db.go +++ b/nix/templates/goapp/frontend/src/db.go diff --git a/nix/templates/goapp/frontend/src/handlers.go b/nix/templates/goapp/frontend/src/handlers.go new file mode 100644 index 0000000..8fdd325 --- /dev/null +++ b/nix/templates/goapp/frontend/src/handlers.go @@ -0,0 +1,236 @@ +package main + +import ( + "fmt" + "html/template" + "log" + "net/http" + + "github.com/coreos/go-oidc/v3/oidc" + "github.com/gorilla/sessions" + "golang.org/x/oauth2" +) + +func indexHandler(w http.ResponseWriter, r *http.Request) { + session, err := globalState.sessions.Get(r, "session") + if err != nil { + log.Println("error getting the session") + } + + tpl := indexTplData{ + Error: r.FormValue("error"), + } + + tpl.Breadcrumbs = []Breadcrumb{ + { + Link{"a", "b"}, + []Link{ + {"c", "d"}, + {"e", "f"}, + }, + }, + { + Link{"g", "h"}, + []Link{ + {"i", "j"}, + {"k", "l"}, + }, + }, + } + tpl.NextLinks = []Link{ + {"Login", "/login"}, + } + + if logged, ok := session.Values["logged"].(bool); ok && logged { + tpl.LoggedIn = true + tpl.Claims.IDToken = session.Values["id_token"].(Claims) + tpl.Claims.UserInfo = session.Values["userinfo"].(Claims) + + if len(options.GroupsFilter) >= 1 { + for _, group := range tpl.Claims.UserInfo.Groups { + if isStringInSlice(group, options.GroupsFilter) { + tpl.Groups = append(tpl.Groups, filterText(group, options.Filters)) + } + } + } else { + tpl.Groups = filterSliceOfText(tpl.Claims.UserInfo.Groups, options.Filters) + } + + tpl.Claims.IDToken.PreferredUsername = filterText(tpl.Claims.IDToken.PreferredUsername, options.Filters) + tpl.Claims.UserInfo.PreferredUsername = filterText(tpl.Claims.UserInfo.PreferredUsername, options.Filters) + tpl.Claims.IDToken.Audience = filterSliceOfText(tpl.Claims.IDToken.Audience, options.Filters) + tpl.Claims.UserInfo.Audience = filterSliceOfText(tpl.Claims.UserInfo.Audience, options.Filters) + tpl.Claims.IDToken.Issuer = filterText(tpl.Claims.IDToken.Issuer, options.Filters) + tpl.Claims.UserInfo.Issuer = filterText(tpl.Claims.UserInfo.Issuer, options.Filters) + tpl.Claims.IDToken.Email = filterText(tpl.Claims.IDToken.Email, options.Filters) + tpl.Claims.UserInfo.Email = filterText(tpl.Claims.UserInfo.Email, options.Filters) + tpl.Claims.IDToken.Name = filterText(tpl.Claims.IDToken.Name, options.Filters) + tpl.Claims.UserInfo.Name = filterText(tpl.Claims.UserInfo.Name, options.Filters) + tpl.RawToken = rawTokens[tpl.Claims.IDToken.JWTIdentifier] + tpl.AuthorizeCodeURL = acURLs[tpl.Claims.IDToken.JWTIdentifier].String() + } + + w.Header().Add("Content-Type", "text/html") + + // get the template + t, err := template.New("index").Funcs(templateFuncMap).ParseGlob(fmt.Sprintf("%s/*.html", options.TemplatesPath)) + if err != nil { + log.Printf("Error reading the template Path: %s/*.html", options.TemplatesPath) + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte("500 - Error reading template file")) + return + } + + // exec! + err = t.ExecuteTemplate(w, "index", tpl) + if err != nil { + log.Println(err) + } +} + +func loginHandler(w http.ResponseWriter, r *http.Request) { + log.Println("[ ] Getting the global session from the session cookie:") + session, err := globalState.sessions.Get(r, options.CookieName) + if err != nil { + log.Println("[ ] Error getting the cookie") + writeErr(w, nil, "error getting cookie", http.StatusInternalServerError) + return + } + + log.Println("[ ] Setting the redirect URL") + session.Values["redirect-url"] = "/" + + log.Println("[ ] Saving the session") + if err = session.Save(r, w); err != nil { + writeErr(w, err, "error saving session", http.StatusInternalServerError) + return + } + + log.Printf("[ ] Redirecting to %s", oauth2Config.AuthCodeURL("random-string")) + http.Redirect(w, r, oauth2Config.AuthCodeURL("random-string-here"), http.StatusFound) +} + +func logoutHandler(w http.ResponseWriter, r *http.Request) { + session, err := globalState.sessions.Get(r, options.CookieName) + if err != nil { + writeErr(w, err, "error getting cookie", http.StatusInternalServerError) + return + } + + // wet the session + session.Values = make(map[interface{}]interface{}) + + if err = session.Save(r, w); err != nil { + writeErr(w, err, "error saving session", http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/", http.StatusFound) +} + +func oauthCallbackHandler(res http.ResponseWriter, req *http.Request) { + log.Println("hit the oauth callback handler") + if req.FormValue("error") != "" { + log.Printf("got an error from the idp: %s", req.FormValue("error")) + http.Redirect(res, req, fmt.Sprintf("/error?%s", req.Form.Encode()), http.StatusFound) + + return + } + + var ( + token *oauth2.Token + idToken *oidc.IDToken + err error + idTokenRaw string + ok bool + ) + + // The state should be checked here in production + if token, err = oauth2Config.Exchange(req.Context(), req.URL.Query().Get("code")); err != nil { + log.Println("Unable to exchange authorization code for tokens") + writeErr(res, err, "unable to exchange authorization code for tokens", http.StatusInternalServerError) + return + } + + // Extract the ID Token from OAuth2 token. + if idTokenRaw, ok = token.Extra("id_token").(string); !ok { + log.Println("missing id token") + writeErr(res, nil, "missing id token", http.StatusInternalServerError) + return + } + + // Parse and verify ID Token payload. + if idToken, err = verifier.Verify(req.Context(), idTokenRaw); err != nil { + log.Printf("unable to verify id token or token is invalid: %+v", idTokenRaw) + writeErr(res, err, "unable to verify id token or token is invalid", http.StatusInternalServerError) + return + } + + // Extract custom claims + claimsIDToken := Claims{} + + if err = idToken.Claims(&claimsIDToken); err != nil { + log.Printf("unable to decode id token claims: %+v", &claimsIDToken) + writeErr(res, err, "unable to decode id token claims", http.StatusInternalServerError) + return + } + + var userinfo *oidc.UserInfo + + if userinfo, err = provider.UserInfo(req.Context(), oauth2.StaticTokenSource(token)); err != nil { + log.Printf("unable to retreive userinfo claims") + writeErr(res, err, "unable to retrieve userinfo claims", http.StatusInternalServerError) + return + } + + claimsUserInfo := Claims{} + + if err = userinfo.Claims(&claimsUserInfo); err != nil { + log.Printf("unable to decode userinfo claims") + writeErr(res, err, "unable to decode userinfo claims", http.StatusInternalServerError) + return + } + + var session *sessions.Session + + if session, err = globalState.sessions.Get(req, options.CookieName); err != nil { + log.Printf("unable to get session from cookie") + writeErr(res, err, "unable to get session from cookie", http.StatusInternalServerError) + return + } + + session.Values["id_token"] = claimsIDToken + session.Values["userinfo"] = claimsUserInfo + session.Values["logged"] = true + rawTokens[claimsIDToken.JWTIdentifier] = idTokenRaw + acURLs[claimsIDToken.JWTIdentifier] = req.URL + + if err = session.Save(req, res); err != nil { + log.Printf("unable to save session") + writeErr(res, err, "unable to save session", http.StatusInternalServerError) + return + } + + var redirectUrl string + + if redirectUrl, ok = session.Values["redirect-url"].(string); ok { + log.Printf("all fine!") + http.Redirect(res, req, redirectUrl, http.StatusFound) + return + } + + http.Redirect(res, req, "/", http.StatusFound) +} + +func writeErr(w http.ResponseWriter, err error, msg string, statusCode int) { + switch { + case err == nil: + log.Println(msg) + http.Error(w, msg, statusCode) + default: + log.Println(msg) + log.Println(err) + http.Error(w, fmt.Errorf("%s: %w", msg, err).Error(), statusCode) + } +} diff --git a/nix/templates/goapp/frontend/src/init.go b/nix/templates/goapp/frontend/src/init.go new file mode 100644 index 0000000..97e58f0 --- /dev/null +++ b/nix/templates/goapp/frontend/src/init.go @@ -0,0 +1,76 @@ +package main + +import ( + "context" + "fmt" + "log" + "net/url" + "os" + "strings" + + "github.com/coreos/go-oidc/v3/oidc" + "golang.org/x/oauth2" +) + +func logInit() loggingMiddleware { + log.Println("[i] Setting up logging...") + logFile, err := os.OpenFile(options.LogFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0664) + if err != nil { + log.Fatal("Error opening the server.log file: ", err) + } + return loggingMiddleware{logFile} +} + +func dbInit() { + log.Println("[i] Setting up Global State Struct...") + s, err := NewState() + if err != nil { + log.Fatal("Error creating the NewState(): ", err) + } + globalState = s +} + +func sessionInit() { + log.Println("[i] Setting up Session Storage...") + store, err := NewSqliteStore( + sessiondbPath, + "sessions", + "/", + 3600, + []byte(os.Getenv("SESSION_KEY"))) + if err != nil { + panic(err) + } + globalState.sessions = store +} + +func oauth2Init() (err error) { + log.Println("[i] Setting up oauth2...") + var redirectURL *url.URL + if _, redirectURL, err = getURLs(options.PublicURL); err != nil { + return fmt.Errorf("could not parse public url: %w", err) + } + + log.Printf("[ ] provider_url: %s", options.Issuer) + log.Printf("[ ] redirect_url: %s", redirectURL.String()) + + if provider, err = oidc.NewProvider(context.Background(), options.Issuer); err != nil { + log.Println("Error init oidc provider: ", err) + return fmt.Errorf("error initializing oidc provider: %w", err) + } + + verifier = provider.Verifier(&oidc.Config{ClientID: options.ClientID}) + log.Printf("[ ] ClientID: %s", options.ClientID) + log.Printf("[ ] ClientSecret: %s", options.ClientSecret) + log.Printf("[ ] redirectURL: %s", redirectURL.String()) + log.Printf("[ ] providerEndpoint: %+v", provider.Endpoint()) + log.Printf("[ ] Scopes: %s", options.Scopes) + oauth2Config = oauth2.Config{ + ClientID: options.ClientID, + ClientSecret: options.ClientSecret, + RedirectURL: redirectURL.String(), + Endpoint: provider.Endpoint(), + Scopes: strings.Split(options.Scopes, ","), + } + return nil +} diff --git a/nix/templates/goapp/backend/log.go b/nix/templates/goapp/frontend/src/log.go index 5af719a..5af719a 100644 --- a/nix/templates/goapp/backend/log.go +++ b/nix/templates/goapp/frontend/src/log.go diff --git a/nix/templates/goapp/frontend/src/main.go b/nix/templates/goapp/frontend/src/main.go new file mode 100644 index 0000000..fcf4224 --- /dev/null +++ b/nix/templates/goapp/frontend/src/main.go @@ -0,0 +1,96 @@ +package main + +import ( + "crypto/tls" + "fmt" + "log" + "net/http" + "net/url" + "time" + + "github.com/coreos/go-oidc/v3/oidc" + "github.com/gorilla/mux" + "github.com/spf13/cobra" + "golang.org/x/oauth2" +) + +var ( + host string + port int + databasePath string + logFilePath string + sessiondbPath string + templatesPath string + globalState *State + + options Options + oauth2Config oauth2.Config + provider *oidc.Provider + verifier *oidc.IDTokenVerifier + + rawTokens = make(map[string]string) + acURLs = make(map[string]*url.URL) +) + +func main() { + + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + + rootCmd := &cobra.Command{Use: "goapp", RunE: root} + + rootCmd.Flags().StringVar(&options.Host, "host", "0.0.0.0", "Specifies the tcp host to listen on") + rootCmd.Flags().IntVar(&options.Port, "port", 8080, "Specifies the port to listen on") + rootCmd.Flags().StringVar(&options.PublicURL, "public-url", "http://localhost:8080/", "Specifies the root URL to generate the redirect URI") + rootCmd.Flags().StringVar(&options.ClientID, "id", "", "Specifies the OpenID Connect Client ID") + rootCmd.Flags().StringVarP(&options.ClientSecret, "secret", "s", "", "Specifies the OpenID Connect Client Secret") + rootCmd.Flags().StringVarP(&options.Issuer, "issuer", "i", "", "Specifies the URL for the OpenID Connect OP") + rootCmd.Flags().StringVar(&options.Scopes, "scopes", "openid,profile,email,groups", "Specifies the OpenID Connect scopes to request") + rootCmd.Flags().StringVar(&options.CookieName, "cookie-name", "oidc-client", "Specifies the storage cookie name to use") + rootCmd.Flags().StringSliceVar(&options.Filters, "filters", []string{}, "If specified filters the specified text from html output (not json) out of the email addresses, display names, audience, etc") + rootCmd.Flags().StringSliceVar(&options.GroupsFilter, "groups-filter", []string{}, "If specified only shows the groups in this list") + rootCmd.Flags().StringVar(&options.LogFilePath, "logpath", "./server.log", "Specifies the path to store the server logs at") + rootCmd.Flags().StringVar(&options.TemplatesPath, "templatespath", "./templates", "Specifies the path to where the templates are stored") + + _ = rootCmd.MarkFlagRequired("id") + _ = rootCmd.MarkFlagRequired("secret") + _ = rootCmd.MarkFlagRequired("issuer") + + if err := rootCmd.Execute(); err != nil { + log.Fatal(err) + } +} + +func root(cmd *cobra.Command, args []string) (err error) { + + logger := logInit() + oauth2Init() + dbInit() + sessionInit() + + r := mux.NewRouter() + r.Use(logger.Middleware) + r.HandleFunc("/", indexHandler) + r.HandleFunc("/login", loginHandler) + // r.HandleFunc("/logout", ) + // r.HandleFunc("/error", loginHandler) + r.HandleFunc("/oauth2/callback", oauthCallbackHandler) + // r.HandleFunc("/json", loginHandler) + // r.HandleFunc("/jwt.json", loginHandler) + + // endpoints with auth needed + auth_needed := r.PathPrefix("/").Subrouter() + auth_needed.Use(authMiddleware) + auth_needed.HandleFunc("/logout", logoutHandler) + + serverAddress := fmt.Sprintf("%s:%d", options.Host, options.Port) + srv := &http.Server{ + Handler: r, + Addr: serverAddress, + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + + log.Printf("[i] Running the server on %s", serverAddress) + log.Fatal(srv.ListenAndServe()) + return +} diff --git a/nix/templates/goapp/backend/sqlitestore.go b/nix/templates/goapp/frontend/src/sqlitestore.go index 6f59d15..34e31e4 100644 --- a/nix/templates/goapp/backend/sqlitestore.go +++ b/nix/templates/goapp/frontend/src/sqlitestore.go @@ -50,6 +50,7 @@ type DB interface { func init() { gob.Register(time.Time{}) + gob.Register(Claims{}) } func NewSqliteStore(endpoint string, tableName string, path string, maxAge int, keyPairs ...[]byte) (*SqliteStore, error) { diff --git a/nix/templates/goapp/frontend/src/templates.go b/nix/templates/goapp/frontend/src/templates.go new file mode 100644 index 0000000..5ae9397 --- /dev/null +++ b/nix/templates/goapp/frontend/src/templates.go @@ -0,0 +1,42 @@ +package main + +import ( + "html/template" + "strings" +) + +var ( + templateFuncMap = template.FuncMap{ + "stringsJoin": strings.Join, + "stringsEqualFold": strings.EqualFold, + "isStringInSlice": isStringInSlice, + } +) + +type indexTplData struct { + Title, Description, RawToken string + + Breadcrumbs []Breadcrumb + NextLinks []Link + + Error string + LoggedIn bool + Claims tplClaims + Groups []string + AuthorizeCodeURL string +} + +type Link struct { + Name string + Target string +} + +type Breadcrumb struct { + Main Link + Options []Link +} + +type tplClaims struct { + IDToken Claims + UserInfo Claims +} diff --git a/nix/templates/goapp/frontend/src/types.go b/nix/templates/goapp/frontend/src/types.go new file mode 100644 index 0000000..7efcc70 --- /dev/null +++ b/nix/templates/goapp/frontend/src/types.go @@ -0,0 +1,65 @@ +package main + +type Claims struct { + JWTIdentifier string `json:"jti"` + Issuer string `json:"iss"` + Subject string `json:"sub"` + Nonce string `json:"nonce"` + Expires int64 `json:"exp"` + IssueTime int64 `json:"iat"` + RequestedAt int64 `json:"rat"` + AuthorizeTime int64 `json:"auth_time"` + NotBefore int64 `json:"nbf"` + Audience []string `json:"aud"` + Scope []string `json:"scp"` + ScopeString string `json:"scope"` + AccessTokenHash string `json:"at_hash"` + CodeHash string `json:"c_hash"` + AuthenticationContextClassReference string `json:"acr"` + AuthenticationMethodsReference []string `json:"amr"` + + Name string `json:"name"` + GivenName string `json:"given_name"` + FamilyName string `json:"family_name"` + MiddleName string `json:"middle_name"` + Nickname string `json:"nickname"` + PreferredUsername string `json:"preferred_username"` + Profile string `jsoon:"profile"` + Picture string `json:"picture"` + Website string `json:"website"` + Gender string `json:"gender"` + Birthdate string `json:"birthdate"` + ZoneInfo string `json:"zoneinfo"` + Locale string `json:"locale"` + UpdatedAt int64 `json:"updated_at"` + Email string `json:"email"` + EmailAlts []string `json:"alt_emails"` + EmailVerified bool `json:"email_verified"` + PhoneNumber string `json:"phone_number"` + PhoneNumberVerified bool `json:"phone_number_verified"` + Address ClamsAddress `json:"address"` + Groups []string `json:"groups"` +} + +type ClamsAddress struct { + StreetAddress string `json:"street_address"` + Locality string `json:"locality"` + Region string `json:"region"` + PostalCode string `json:"postal_code"` + Country string `json:"country"` +} + +type Options struct { + Host string + Port int + LogFilePath string + TemplatesPath string + ClientID string + ClientSecret string + Issuer string + PublicURL string + Scopes string + CookieName string + Filters []string + GroupsFilter []string +} diff --git a/nix/templates/goapp/frontend/src/util.go b/nix/templates/goapp/frontend/src/util.go new file mode 100644 index 0000000..89d28ba --- /dev/null +++ b/nix/templates/goapp/frontend/src/util.go @@ -0,0 +1,58 @@ +package main + +import ( + "fmt" + "net/url" + "path" + "strings" +) + +func isStringInSlice(s string, slice []string) bool { + for _, x := range slice { + if s == x { + return true + } + } + + return false +} + +func filterText(input string, filters []string) (output string) { + if len(filters) == 0 { + return input + } + + for _, filter := range filters { + input = strings.Replace(input, filter, strings.Repeat("*", len(filter)), -1) + } + + return input +} + +func filterSliceOfText(input []string, filters []string) (output []string) { + for _, item := range input { + output = append(output, filterText(item, filters)) + } + + return output +} + +func getURLs(rootURL string) (publicURL *url.URL, redirectURL *url.URL, err error) { + if publicURL, err = url.Parse(rootURL); err != nil { + return nil, nil, err + } + + if publicURL.Scheme != "http" && publicURL.Scheme != "https" { + return nil, nil, fmt.Errorf("scheme must be http or https but it is '%s'", publicURL.Scheme) + } + + if !strings.HasSuffix(publicURL.Path, "/") { + publicURL.Path += "/" + } + + redirectURL = &url.URL{} + *redirectURL = *publicURL + redirectURL.Path = path.Join(redirectURL.Path, "/oauth2/callback") + + return publicURL, redirectURL, nil +} diff --git a/nix/templates/goapp/frontend/templates/footer.html b/nix/templates/goapp/frontend/templates/footer.html new file mode 100644 index 0000000..1899096 --- /dev/null +++ b/nix/templates/goapp/frontend/templates/footer.html @@ -0,0 +1,8 @@ +{{ define "footer" }} +{{ if .asd }} +<br><br><hr><br> +{{ . }} +{{ end }} +</div> +{{ end }} + diff --git a/nix/templates/goapp/frontend/templates/head.html b/nix/templates/goapp/frontend/templates/head.html new file mode 100644 index 0000000..efc7ce3 --- /dev/null +++ b/nix/templates/goapp/frontend/templates/head.html @@ -0,0 +1,153 @@ +{{ define "head" }} +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>goapp</title> + + <style> +* { word-wrap:break-word; font-family: monospace; margin: 0; padding: 0; } + +/* light/darktheme specific foo */ +@media (prefers-color-scheme: light) { + html { background: #fafafa; color: #040404; } + a:hover { color: #fafafa; background: #040404 } + a:not([href*="webring.xxiivv.com"]):hover, nav a:active { color: #fafafa; background: #040404 } + a { color: #040404; background: #fafafa; text-decoration: none;} + nav a:hover, a:active { color: #fafafa; background: #040404 } + nav { margin: 1ex 0; background: #eeeeee; } + nav a { display:block; background: #eeeeee; } + h1 { margin: 3ex 0 1ex 0; width: 100%; background-color: #eeeeee} + h2 { margin: 2ex 0 1ex 0; width: 100%; background-color: #eeeeee} + h3 { margin: 1ex 0 1ex 0; width: 100%; font-size: 1em; background-color: #eeeeee} + h4 { margin: 1ex 0 1ex 0; width: 100%; font-size: 1em; /*background-color: #fafafa*/} + h5 { margin: 1ex 0 1ex 0; width: 100%; font-size: 1em; /*background-color: #fafafa*/} + .code { border-left: 1px solid #040404; margin-left: 2ex; padding-left: 1ex; } + .codeline:hover { background: #eeeeee; color: #040404; } + .trhover:hover { background: #c0c0c0; color: #040404; } + + /* add an outline while hovering, the !important makes hovering on checked elements still visible */ + .check-with-label:checked + .label-for-check { background-color: #040404; color: #eeeeee !important; } + .check-with-label:hover + .label-for-check { outline: 1px solid #040404; color: #040404; } + + input, textarea { + outline: 1px solid #000000; + border: none; + background-color: #ffffff; + color: #000000; + } + button { + background-color: #ffffff; color: #000000; border: 1px solid #000000; border-style: solid; + margin-top: 1ex; + }; +} +@media (prefers-color-scheme: dark) { + html { background: #040404; color: #c0c0c0; } + a:hover { color: #040404; background: #c0c0c0 } + body nav a:not([href*="webring.xxiivv.com"]):hover, nav a:active { color: #c0c0c0; background: #040404 } + a { color: #c0c0c0; background: #040404; text-decoration: none; } + nav a:hover, a:active { color: #040404; background: #c0c0c0 } + nav { margin: 1ex 0; background: #c0c0c0; } + nav a { display:block; background: #c0c0c0; } + h1 { margin: 3ex 0 1ex 0; width: 100%; background-color: #c0c0c0} + h2 { margin: 2ex 0 1ex 0; width: 100%; background-color: #c0c0c0} + h3 { margin: 1ex 0 1ex 0; width: 100%; font-size: 1em; background-color: #c0c0c0} + h4 { margin: 1ex 0 1ex 0; width: 100%; font-size: 1em; /*background-color: #c0c0c0*/} + h5 { margin: 1ex 0 1ex 0; width: 100%; font-size: 1em; /*background-color: #c0c0c0*/} + .code { border-left: 1px solid #c0c0c0; margin-left: 2ex; padding-left: 1ex; } + .codeline:hover { background: #c0c0c0; color: #040404; } + .webring { -webkit-filter: invert(100%); filter: invert(100%); } + .trhover:hover { background: #c0c0c0; color: #040404; } + + /* add an outline while hovering, the !important makes hovering on checked elements still visible */ + .check-with-label:checked + .label-for-check { background-color: #c0c0c0; color: #040404 !important; } + .check-with-label:hover + .label-for-check { outline: 1px solid #c0c0c0; color: #c0c0c0; } + + input, textarea { + outline: 1px solid #ffffff; + border: none; + background-color: #000000; + color: #ffffff; + } + + button { + background-color: #000000; color: #ffffff; + border: 1px solid #ffffff; + border-style: solid; + margin-top: 1ex; + }; +} + +/* settings for mobile devices*/ +@media only screen and (max-width: 768px) { + body { margin: 1ex; width: calc(100% - 2ex) !important; } + img { max-width: 100% !important; max-height: 500px; } +} + +/* only display the hover dropdown on non-mobile devices */ +@media only screen and (min-width: 768px) { + nav ul li:hover a + ul { display: inherit; white-space: nowrap; } +} + +img { max-width: 100ex; max-height: 500px; } + +body { margin-left: auto; margin-right: auto; margin-top: 1ex; margin-bottom: 1ex; width: 100ex; } + +.webring { align: right; } +a .webring { float: right; } + +/* display local links using [] and external links using {} */ +body a:not(h1 a, h2 a, h3 a,h4 a):not([href*="webring.xxiivv.com"]):not([class*="local"]):before { content: "["; } +body a:not(h1 a, h2 a, h3 a,h4 a):not([href*="webring.xxiivv.com"]):not([class*="local"]):after { content: "]"; } +a[href*="//"]:not([href*="r2wa.rs"]):not([class*="icon"]):before { content: '{'; } +a[href*="//"]:not([href*="r2wa.rs"]):not([class*="icon"]):after { content: '}'; } + +/* table { width: 100ex; } */ +input, textarea { width: 100%; } +textarea { padding: 0.5ex; } + +ul { list-style-type: none; } + +/* navigation bar magic */ +nav * { color: #040404; } +nav ul { list-style: none; position: relative; display: inline-block; } +nav ul li { display:inline-block; } +nav ul ul { display: none; position: absolute; outline: 1px solid #040404; background-color: #ff0; } +nav ul ul li { width: 100%; padding-right: 1ex; float:none; display:list-item; position: relative; } +nav + ul li { display: inline-block;} + +/* nav bar spacing char */ +nav ul li > a::after { content: " /"; } +nav ul li > a:only-child::after { content: ""; } +nav ul li:last-of-type a::after { content: ""; } + +h1 a, h2 a, h3 a { padding-right: 1ex; } + +pre { white-space: pre-wrap; hyphens: auto; } +pre.code { white-space: pre-wrap; hyphens: none; } + +/* display the list of folders in the current one as a vertical list, if the + * .vert class is present */ +nav + ul.vert li { display: block; } + +.w-100 { width: 100%; } + +.check-with-label { display: none; } /* checkbox with a label */ + +/* In tables, make the first column fit the content and the reset be relaxed */ +body table tbody { width: 100%; word-wrap: break-word; } +/* body table tbody tr>td { padding: 0.5ex 0 0.5ex !important; } */ +body table tbody tr td:nth-child(1) { width: auto; white-space: nowrap; padding-right: 1ex; } +body table tbody tr td:not(:nth-child(1)) { width: 100%; max-width: 100%; word-wrap: anywhere; } + +tr { text-wrap: wrap;} + +input { padding-left: 0.5ex; } +input:focus { outline-offset: 0px; } +textarea:focus { outline-offset: 0px; } + + </style> +</head> +{{ end }} diff --git a/nix/templates/goapp/frontend/templates/index.html b/nix/templates/goapp/frontend/templates/index.html new file mode 100644 index 0000000..1d21f3d --- /dev/null +++ b/nix/templates/goapp/frontend/templates/index.html @@ -0,0 +1,81 @@ +{{ define "index" }} + +{{ template "head" . }} +{{ template "nav" . }} + +<h1>goapp</h1> + +{{- if .LoggedIn }} +<p id="welcome">Logged in as {{ or .Claims.UserInfo.PreferredUsername .Claims.IDToken.Subject "unknown" }}!</p> +<p><a href="/logout" id="log-out">Log out</a></p> +<p>Access Token Hash: <span id="claim-at_hash">{{ .Claims.IDToken.AccessTokenHash }}</span></p> +<p>Code Hash: <span id="claim-c_hash">{{ .Claims.IDToken.CodeHash }}</span></p> +<p>Authentication Context Class Reference: <span id="claim-acr">{{ .Claims.IDToken.AuthenticationContextClassReference }}</span></p> +<p>Authentication Methods Reference: <span id="claim-amr">{{ stringsJoin .Claims.IDToken.AuthenticationMethodsReference ", " }}</span></p> +<p>Audience: <span id="claim-aud">{{ stringsJoin .Claims.IDToken.Audience ", " }}</span></p> +<p>Expires: <span id="claim-exp">{{ .Claims.IDToken.Expires }}</span></p> +<p>Issue Time: <span id="claim-iat">{{ .Claims.IDToken.IssueTime }}</span></p> +<p>Requested At: <span id="claim-rat">{{ .Claims.IDToken.RequestedAt }}</span></p> +<p>Authorize Time: <span id="claim-auth_at">{{ .Claims.IDToken.AuthorizeTime }}</span></p> +<p>Not Before: <span id="claim-nbf">{{ .Claims.IDToken.NotBefore }}</span></p> +<p>Issuer: <span id="claim-iss">{{ .Claims.IDToken.Issuer }}</span></p> +<p>JWT ID: <span id="claim-jti">{{ .Claims.IDToken.JWTIdentifier }}</span></p> +<p>Subject: <span id="claim-sub">{{ .Claims.IDToken.Subject }}</span></p> +<p>Nonce: <span id="claim-nonce">{{ .Claims.IDToken.Nonce }}</span></p> +<p>Name: <span id="claim-name">{{ .Claims.UserInfo.Name }}</span></p> +<p>Name (ID Token): <span id="claim-id-token-name">{{ .Claims.IDToken.Name }}</span></p> +<p>Given Name: <span id="claim-given_name">{{ .Claims.UserInfo.GivenName }}</span></p> +<p>Given Name (ID Token): <span id="claim-id-token-given_name">{{ .Claims.IDToken.GivenName }}</span></p> +<p>Family Name: <span id="claim-family_name">{{ .Claims.UserInfo.FamilyName }}</span></p> +<p>Family Name (ID Token): <span id="claim-id-token-family_name">{{ .Claims.IDToken.FamilyName }}</span></p> +<p>Middle Name: <span id="claim-middle_name">{{ .Claims.UserInfo.MiddleName }}</span></p> +<p>Middle Name (ID Token): <span id="claim-id-token-middle_name">{{ .Claims.IDToken.MiddleName }}</span></p> +<p>Nickname: <span id="claim-nickname">{{ .Claims.UserInfo.Nickname }}</span></p> +<p>Nickname (ID Token): <span id="claim-id-token-nickname">{{ .Claims.IDToken.Nickname }}</span></p> +<p>Preferred Username: <span id="claim-preferred_username">{{ .Claims.UserInfo.PreferredUsername }}</span></p> +<p>Preferred Username (ID Token): <span id="claim-id-token-preferred_username">{{ .Claims.IDToken.PreferredUsername }}</span></p> +<p>Profile: <span id="claim-profile">{{ .Claims.UserInfo.Profile }}</span></p> +<p>Profile (ID Token): <span id="claim-id-token-profile">{{ .Claims.IDToken.Profile }}</span></p> +<p>Website: <span id="claim-website">{{ .Claims.UserInfo.Website }}</span></p> +<p>Website (ID Token): <span id="claim-id-token-website">{{ .Claims.IDToken.Website }}</span></p> +<p>Gender: <span id="claim-gender">{{ .Claims.UserInfo.Gender }}</span></p> +<p>Gender (ID Token): <span id="claim-id-token-gender">{{ .Claims.IDToken.Gender }}</span></p> +<p>Birthdate: <span id="claim-birthdate">{{ .Claims.UserInfo.Birthdate }}</span></p> +<p>Birthdate (ID Token): <span id="claim-id-token-birthdate">{{ .Claims.IDToken.Birthdate }}</span></p> +<p>ZoneInfo: <span id="claim-zoneinfo">{{ .Claims.UserInfo.ZoneInfo }}</span></p> +<p>ZoneInfo (ID Token): <span id="claim-id-token-zoneinfo">{{ .Claims.IDToken.ZoneInfo }}</span></p> +<p>Locale: <span id="claim-locale">{{ .Claims.UserInfo.Locale }}</span></p> +<p>Locale (ID Token): <span id="claim-id-token-locale">{{ .Claims.IDToken.Locale }}</span></p> +<p>Updated At: <span id="claim-updated_at">{{ .Claims.UserInfo.UpdatedAt }}</span></p> +<p>Updated At (ID Token): <span id="claim-id-token-updated_at">{{ .Claims.IDToken.UpdatedAt }}</span></p> +<p>Email: <span id="claim-email">{{ .Claims.UserInfo.Email }}</span></p> +<p>Email (ID Token): <span id="claim-id-token-email">{{ .Claims.IDToken.Email }}</span></p> +<p>Email Alts: <span id="claim-alt_emails">{{ .Claims.UserInfo.EmailAlts }}</span></p> +<p>Email Alts (ID Token): <span id="claim-id-token-alt_emails">{{ .Claims.IDToken.EmailAlts }}</span></p> +<p>Email Verified: <span id="claim-email_verified">{{ .Claims.UserInfo.EmailVerified }}</span></p> +<p>Email Verified (ID Token): <span id="claim-id-token-email_verified">{{ .Claims.IDToken.EmailVerified }}</span></p> +<p>Phone Number: <span id="claim-phone_number">{{ .Claims.UserInfo.PhoneNumber }}</span></p> +<p>Phone Number (ID Token): <span id="claim-id-token-phone_number">{{ .Claims.IDToken.PhoneNumber }}</span></p> +<p>Phone Number Verified: <span id="claim-phone_number_verified">{{ .Claims.UserInfo.PhoneNumberVerified }}</span></p> +<p>Phone Number Verified (ID Token): <span id="claim-id-token-phone_number_verified">{{ .Claims.IDToken.PhoneNumberVerified }}</span></p> +<p>Groups: <span id="claim-groups">{{ stringsJoin .Groups ", " }}</span></p> +<p>Groups (ID Token): <span id="claim-id-token-groups">{{ stringsJoin .Groups ", " }}</span></p> +<p>Raw: <span id="raw">{{ .RawToken }}</span></p> +<p>Authorize Code URL: <span id="auth-code-url">{{ .AuthorizeCodeURL }}</span></p> +{{- else }} +<p>Not logged yet...</p> <a id="login-link" href="/login">Log in</a> +{{- end }} + +<!-- +<body style="height: 100vh; position: relative;"> + <div style="position: absolute; width: 50%; transform: scale(2); + transform-origin: 0 0;"> + <form action="/submit" method="POST" id="sumbit"> + <input type="text" id="target" name="target" required size="20" /> + </form> + </div> +</body> +--> + +{{ end }} + diff --git a/nix/templates/goapp/frontend/templates/login.html b/nix/templates/goapp/frontend/templates/login.html new file mode 100644 index 0000000..6e54781 --- /dev/null +++ b/nix/templates/goapp/frontend/templates/login.html @@ -0,0 +1,41 @@ +{{ define "login" }} + +{{ template "head" . }} +<body> + {{ template "nav" . }} + + <span id="login"></span> + <h1><a href="#login">Login</a></h1> + + {{ if .err }}{{ .err }}{{ end }} + {{ if .logged_in }} + Already logged in! <a href="/">Return home</a> + {{ else }} + <form method="POST" action="/login"> + + <table> + <tr> + <td><label for="username">Name:</label></td> + <td><input class="border" type="text" id="username" name="username" autofocus></td> + </tr> + <tr> + <td><label for="password">Password:</label></td> + <td><input class="border" type="password" id="password" name="password"></td> + </tr> + <tr> + <td></td> + <td><input class="border" type="submit" value="Login"></td> + </tr> + <tr> + <td></td> + <td>{{ .res }}</td> + </tr> + </table> + </form> + <!-- Not registered yet? <a href="/register">Register Now!</a> --> + {{ end }} + + +</body> +{{ template "footer" . }} +{{end}} diff --git a/nix/templates/goapp/frontend/templates/nav.html b/nix/templates/goapp/frontend/templates/nav.html new file mode 100644 index 0000000..bf9820f --- /dev/null +++ b/nix/templates/goapp/frontend/templates/nav.html @@ -0,0 +1,41 @@ +{{ define "nav" }} + <header> + <p style="margin: 1ex 0; display: block; width: 100%; background-color: #ffaa00; color: white;"> + v0.0.1 EARLY BETA - Data can be deleted at random! + </p> + </header> + <nav> + + <ul> + {{ range .Breadcrumbs }} + <li> + <a class="local" href="{{ .Main.Target }}">{{ .Main.Name }}</a> + {{ if .Options }} + <ul> + {{ range $opt := .Options }} + <li><a class="local" href="{{ $opt.Target }}">{{ $opt.Name }}</a></li> + {{ end }} + </ul> + {{ end }} + </li> + {{ end }} + </ul> + + + <ul style="float: right"> + <li> + <a href="https://github.com/HanEmile/hefe/tree/main/nix/templates/goapp">src</a> + </li> + </ul> + </nav> + <ul> + {{ if .NextLinks }} + {{ range .NextLinks }} + <li><a class="local" href="{{ .Target }}">{{ .Name }}</a></li> + {{ end }} + {{ end }} + </ul> + <br> +{{ end }} + + |