From c57a862c4977378256899f12648f72534ee68a32 Mon Sep 17 00:00:00 2001 From: Jonni Liljamo Date: Fri, 14 Jul 2023 15:20:47 +0300 Subject: [PATCH] wip: internal users --- api/login.go | 56 +++++++++++++++++++++++++++++++++++ db/migrations.go | 12 ++++++++ dev.elv | 2 +- flake.lock | 27 +++++++++++++++++ flake.nix | 24 +++++++++++++++ go.mod | 1 + go.sum | 2 ++ handlers/auth.go | 29 ++++++++++++++++++ static/styles.css | 55 ++++++++++++++++++++++++++-------- template/templates/login.tmpl | 23 ++++++++++++-- tixe.go | 6 ++++ 11 files changed, 221 insertions(+), 16 deletions(-) create mode 100644 api/login.go create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/api/login.go b/api/login.go new file mode 100644 index 0000000..d7584dd --- /dev/null +++ b/api/login.go @@ -0,0 +1,56 @@ +package api + +import ( + "context" + "log" + "net/http" + "tixe/db" + + "github.com/gin-gonic/gin" + "github.com/matthewhartstonge/argon2" +) + +type PostInternalLoginDetails struct { + Username string `form:"username"` + Password string `form:"password"` +} + +func PostInternalLogin(c *gin.Context) { + userDetails := &PostInternalLoginDetails{} + if err := c.Bind(userDetails); err != nil { + log.Print("[tixe/api/iauth/login] Could not bind login details") + c.String(http.StatusBadRequest, "could not bind login details") + return; + } + + // Fetch user + var ( + username string + passwordHash string + ) + + err := db.PgPool.QueryRow(context.Background(), "SELECT username, password FROM users WHERE username = $1", userDetails.Username).Scan(&username, &passwordHash) + if err != nil { + log.Printf("[tixe/api/iauth/login] WARN: Error querying internal user from database: %s", err.Error()) + c.String(http.StatusUnauthorized, "incorrect details") + return + } + + // Verify password + ok, err := argon2.VerifyEncoded([]byte(userDetails.Password), []byte(passwordHash)) + if err != nil { + log.Printf("[tixe/api/iauth/login] WARN: Error verifying internal user password: %s", err.Error()) + c.String(http.StatusUnauthorized, "incorrect details") + return + } + + if !ok { + // Password did not match + c.String(http.StatusUnauthorized, "incorrect details") + return + } + + + + c.Redirect(http.StatusTemporaryRedirect, "/") +} diff --git a/db/migrations.go b/db/migrations.go index b22bdc5..1619de1 100644 --- a/db/migrations.go +++ b/db/migrations.go @@ -64,5 +64,17 @@ func migrations() []string { ran_timestamp timestamp, schema_version integer )`,migrationsTable), + `CREATE TABLE users ( + id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), + is_internal BOOLEAN NOT NULL DEFAULT FALSE, + is_admin BOOLEAN NOT NULL DEFAULT FALSE, + display_name TEXT NOT NULL, + username TEXT, + password TEXT, + oidc_subject TEXT + )`, + `INSERT INTO + users(is_internal, is_admin, display_name, username, password) + VALUES(TRUE, TRUE, "admin", "admin", "$argon2id$v=19$m=65536,t=3,p=4$UC+I4MhJyVJG+N2OGPecHQ$ISwMkf1LQh0wUgoP7im7yzT1vKUNDTWVbT828m75woY")`, } } diff --git a/dev.elv b/dev.elv index 6e17505..5cf8d76 100755 --- a/dev.elv +++ b/dev.elv @@ -1,4 +1,4 @@ -#!/bin/elvish +#!/usr/bin/env elvish fn run { clear diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..ac29791 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1687701825, + "narHash": "sha256-aMC9hqsf+4tJL7aJWSdEUurW2TsjxtDcJBwM9Y4FIYM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "07059ee2fa34f1598758839b9af87eae7f7ae6ea", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..1e58901 --- /dev/null +++ b/flake.nix @@ -0,0 +1,24 @@ +{ + description = "tixe"; + nixConfig.bash-prompt = "nix | tixe> "; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + }; + + outputs = { self, nixpkgs }: + let + pkgs = nixpkgs.legacyPackages.x86_64-linux.pkgs; + in + { + devShells.x86_64-linux.default = pkgs.mkShell { + name = "tixe env"; + buildInputs = with pkgs; [ + go + gopls + + nodePackages.tailwindcss + ]; + }; + }; +} diff --git a/go.mod b/go.mod index 6d681de..3f20f60 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/leodido/go-urn v1.2.4 // indirect + github.com/matthewhartstonge/argon2 v0.3.2 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/go.sum b/go.sum index ebdd88e..37e7715 100644 --- a/go.sum +++ b/go.sum @@ -54,6 +54,8 @@ github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/q github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/matthewhartstonge/argon2 v0.3.2 h1:nOsWkxRFIdPueV7Dla/R/5JZRN2NncnCAlMg3ktyv/A= +github.com/matthewhartstonge/argon2 v0.3.2/go.mod h1:7SVzBdtpqJcIUzzRghzsT8m/DT/kbTDiGifrjZbQ0sA= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/handlers/auth.go b/handlers/auth.go index d76e37a..1e5c975 100644 --- a/handlers/auth.go +++ b/handlers/auth.go @@ -1,8 +1,11 @@ package handlers import ( + "context" + "log" "net/http" "tixe/auth" + "tixe/db" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" @@ -41,6 +44,32 @@ func AuthCallback(auth *auth.Auth) gin.HandlerFunc { return } + // Create a user in the database for this OIDC user. + // idToken.Subject shooooould be unique and always the same for a user. + // I think. Maybe. + + // But first, check if it exists! + var oidcSubject string + err = db.PgPool.QueryRow(context.Background(), "SELECT oidc_subject FROM users WHERE oidc_subject = $1", idToken.Subject).Scan(&oidcSubject) + if err != nil { + log.Printf("[tixe/auth] WARN: Failed to query database for oidc user") + c.String(http.StatusInternalServerError, err.Error()) + return + } + if idToken.Subject == oidcSubject { + // Exists, we outta here! + c.Redirect(http.StatusTemporaryRedirect, "/") + return + } + + // Now create it, since it did not exists. + _, err = db.PgPool.Exec(context.Background(), "INSERT INTO users(display_name, oidc_subject) VALUES($1, $2)", profile["name"].(string), idToken.Subject) + if err != nil { + log.Printf("[tixe/auth] WARN: Could not create database entry for oidc user.") + c.String(http.StatusInternalServerError, err.Error()) + return + } + c.Redirect(http.StatusTemporaryRedirect, "/") } } diff --git a/static/styles.css b/static/styles.css index 17d8a51..15d51a1 100644 --- a/static/styles.css +++ b/static/styles.css @@ -527,6 +527,11 @@ video { margin-bottom: 3rem; } +.my-4 { + margin-top: 1rem; + margin-bottom: 1rem; +} + .flex { display: flex; } @@ -549,14 +554,22 @@ video { min-width: max-content; } -.max-w-4xl { - max-width: 56rem; +.min-w-full { + min-width: 100%; } .max-w-3xl { max-width: 48rem; } +.max-w-4xl { + max-width: 56rem; +} + +.max-w-full { + max-width: 100%; +} + .grow { flex-grow: 1; } @@ -589,10 +602,18 @@ video { border-radius: 0.375rem; } +.rounded { + border-radius: 0.25rem; +} + .border-2 { border-width: 2px; } +.border { + border-width: 1px; +} + .border-b-2 { border-bottom-width: 2px; } @@ -618,18 +639,18 @@ video { background-image: linear-gradient(to right, var(--tw-gradient-stops)); } -.from-slate-600 { - --tw-gradient-from: #475569 var(--tw-gradient-from-position); - --tw-gradient-to: rgb(71 85 105 / 0) var(--tw-gradient-to-position); - --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); -} - .from-neutral-500 { --tw-gradient-from: #737373 var(--tw-gradient-from-position); --tw-gradient-to: rgb(115 115 115 / 0) var(--tw-gradient-to-position); --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); } +.from-slate-600 { + --tw-gradient-from: #475569 var(--tw-gradient-from-position); + --tw-gradient-to: rgb(71 85 105 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + .from-transparent { --tw-gradient-from: transparent var(--tw-gradient-from-position); --tw-gradient-to: rgb(0 0 0 / 0) var(--tw-gradient-to-position); @@ -641,14 +662,14 @@ video { --tw-gradient-stops: var(--tw-gradient-from), #60a5fa var(--tw-gradient-via-position), var(--tw-gradient-to); } -.to-rose-500 { - --tw-gradient-to: #f43f5e var(--tw-gradient-to-position); -} - .to-neutral-500 { --tw-gradient-to: #737373 var(--tw-gradient-to-position); } +.to-rose-500 { + --tw-gradient-to: #f43f5e var(--tw-gradient-to-position); +} + .to-transparent { --tw-gradient-to: transparent var(--tw-gradient-to-position); } @@ -702,6 +723,11 @@ video { color: transparent; } +.text-slate-500 { + --tw-text-opacity: 1; + color: rgb(100 116 139 / var(--tw-text-opacity)); +} + .opacity-75 { opacity: 0.75; } @@ -740,6 +766,11 @@ video { text-decoration-line: underline; } +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + @media (prefers-color-scheme: dark) { .dark\:opacity-100 { opacity: 1; diff --git a/template/templates/login.tmpl b/template/templates/login.tmpl index 5b95fcc..27d19f5 100644 --- a/template/templates/login.tmpl +++ b/template/templates/login.tmpl @@ -7,8 +7,25 @@ Liljamo Auth
-
-
OR
-
+
+
or
+
+
+
+
+
+ + +
+
+ + +
+ +
{{ end }} diff --git a/tixe.go b/tixe.go index 34d76d5..cebbe6f 100644 --- a/tixe.go +++ b/tixe.go @@ -64,6 +64,12 @@ func setupRouter(auth *auth.Auth) *gin.Engine { { apiRoute.GET("/", api.Root) apiRoute.GET("/ping", ping) + + // internal auth route + iauth := apiRoute.Group("/iauth") + { + iauth.POST("/login", api.PostInternalLogin) + } } } -- 2.44.1