From fa22a737d3b4e8272f1597b89149b58857560d91 Mon Sep 17 00:00:00 2001 From: Eric Schmidt Date: Mon, 21 Oct 2024 21:49:43 +0000 Subject: [PATCH] feat: connected user email into DB and auth --- db.go | 2 +- docs/Week1-DevelopmentLog.md | 41 ++++++++++++++++++++++++------------ js/userMsg.js | 28 ++++++++++++++++++++++++ js/validateAuth.js | 14 ++++++++++++ main.go | 34 +++++++++++++++++++++++++----- templates/index.html | 4 +++- templates/user_msg.tmpl | 3 ++- 7 files changed, 105 insertions(+), 21 deletions(-) create mode 100644 js/userMsg.js create mode 100644 js/validateAuth.js diff --git a/db.go b/db.go index af53770..837d847 100644 --- a/db.go +++ b/db.go @@ -46,7 +46,7 @@ const ( SubCollectionName string = "Conversations" ) -var CollectionName string = "Herodotus" +var CollectionName string = "HerodotusDev" type ConversationBit struct { BotResponse string diff --git a/docs/Week1-DevelopmentLog.md b/docs/Week1-DevelopmentLog.md index 1ab85ac..0d2d077 100644 --- a/docs/Week1-DevelopmentLog.md +++ b/docs/Week1-DevelopmentLog.md @@ -65,22 +65,22 @@ Sources: ## Integrating Firestore -<> Updating array fields in Firestore is hard. Too hard. Go doesn't support the `arrayUnion` operation in Firestore :/ +Unhappy Updating array fields in Firestore is hard. Too hard. Go doesn't support the `arrayUnion` operation in Firestore :/ Switched to SubCollection of document ## Deploying Gemma model from Model Garden -<> Try 1: tried deployment from Pantheon. I don't think it worked ... :/ +Unhappy Try 1: tried deployment from Pantheon. I don't think it worked ... :/ The activity bar in Pantheon says that something is happening, but that's the only indication I have that a Gemma model is being deployed. I'm going to use Gemini 1.5 Flash to continue prototyping. -<> I used the [official Gemma2 prediction sample](https://github.com/GoogleCloudPlatform/golang-samples/pull/4395/files) but +Anxious I used the [official Gemma2 prediction sample](https://github.com/GoogleCloudPlatform/golang-samples/pull/4395/files) but the output is awful. I think we need to change the temperature, top-p, and top-k settings. -<> Gemma2 responses are ... awful. Even with changing the top-p and temperature settings. It looks like the parameters setting +Unhappy Gemma2 responses are ... awful. Even with changing the top-p and temperature settings. It looks like the parameters setting is not required; I will remove it. Even with removing the temperature settings, the responses are garbage. If I were releasing this publicly, I would rely @@ -100,8 +100,7 @@ Sources: ## Building Go on Cloud Shell - -<>Cloud Shell ran out of room with my successive Go builds. I had to clean the cache. +UnhappyCloud Shell ran out of room with my successive Go builds. I had to clean the cache. ```sh go clean -cache @@ -113,7 +112,7 @@ Sources: ## Integrating Firebase auth -<> Setting up my Authentication section ... To set up Google Auth, I need to provide a Web client ID and Web client secret. +Unhappy Setting up my Authentication section ... To set up Google Auth, I need to provide a Web client ID and Web client secret. I'm sure that I can find it, but I have to go hunting around for it After finally hacking something that works (not documented), I can't get the redirect to work. @@ -131,14 +130,14 @@ Sources: ## Deploying -<>I'm a novice with Dockerfiles. I wanted to make sure that all my Go and client files are deployed to the image. +UnhappyI'm a novice with Dockerfiles. I wanted to make sure that all my Go and client files are deployed to the image. (They are.) But then I need to make sure that the application knows how to load them (it doesn't). How do I test this? I decided to build & run the Dockerfile locally (in Cloud Shell) to check that it works correct. The deployment process to Cloud Run/Build is sooooo long and I want to make sure that I get it right. -<>It's very frustrating that I can't install the Docker client on my work laptop. It would +UnhappyIt's very frustrating that I can't install the Docker client on my work laptop. It would be so much more convenient for me to build & run locally so that I can inspect the built artifacts and logs :/ To build & run from project root: @@ -175,7 +174,7 @@ us-west1-docker.pkg.dev/${PROJECT_ID}/my-herodotus/base-image:v1 docker push us-west1-docker.pkg.dev/${PROJECT_ID}/my-herodotus/base-image:v1 ``` -<> Deploying a new version of my web app from Artifact Registry was shockingly intuitive. +Happy Deploying a new version of my web app from Artifact Registry was shockingly intuitive. Sources: @@ -188,13 +187,29 @@ Sources: The basic quickstart is in Python only :/. -<> The version of the tutorial in Go says "use standard logging," but it shows how to use the +Anxious The version of the tutorial in Go says "use standard logging," but it shows how to use the cloud.google.com/go/logging library (not the standard `log` package). -<> It seems like reinitializing the LoggingClient each time I need to log a message is +Curious It seems like reinitializing the LoggingClient each time I need to log a message is a bit repetitive. I wonder if there is a better pattern for this? Sources: + https://cloud.google.com/logging/docs/setup/go -+ https://cloud.google.com/logging/docs/write-query-log-entries-python \ No newline at end of file ++ https://cloud.google.com/logging/docs/write-query-log-entries-python + +# Hardening authentication (client- & server-side) + +Realized over the weekend that users can navigate directly to /home without authenticating. + ++ [x] Add a check (on the server) that the user's email is propogated. ++ [x] Add a check (in the JS) that the user has signed-in + +TODO(telpirion): ensure that user emails are stored in a obfuscated manner + +Sources: + ++ https://gin-gonic.com/docs/examples/redirects/ ++ https://firebase.google.com/docs/auth/web/manage-users + +## Integrating Cloud Monitoring (Open Telemetry) \ No newline at end of file diff --git a/js/userMsg.js b/js/userMsg.js new file mode 100644 index 0000000..c2df449 --- /dev/null +++ b/js/userMsg.js @@ -0,0 +1,28 @@ +import { getAuth } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-auth.js' + +function processForm(e) { + //e.preventDefault(); + + // Emit 'msg' event for bot progress bar + const event = new Event("msg"); + document.dispatchEvent(event); + + // Get the user email address + const auth = getAuth(); + const user = auth.currentUser; + + // If the user hasn't signed in, redirect them to the sign-in page. + if (!user) { + window.location = `/?status=unauthorized`; + } + return true; +} + +window.addEventListener("load", function() { + const form = document.getElementById("userMsg"); + if (form.attachEvent) { + form.attachEvent("submit", processForm); + } else { + form.addEventListener("submit", processForm); + } +}); \ No newline at end of file diff --git a/js/validateAuth.js b/js/validateAuth.js new file mode 100644 index 0000000..9226b41 --- /dev/null +++ b/js/validateAuth.js @@ -0,0 +1,14 @@ +import { getAuth, onAuthStateChanged } from 'https://www.gstatic.com/firebasejs/10.14.1/firebase-auth.js' + +window.addEventListener("load", function () { + const auth = getAuth(); + const user = auth.currentUser; + + onAuthStateChanged(auth, (user) => { + if (user) { + const uid = user.uid; + } else { + window.location = `/?message=sign-in-required`; + } + }); +}); diff --git a/main.go b/main.go index cef3e59..55785f2 100644 --- a/main.go +++ b/main.go @@ -11,8 +11,10 @@ import ( ) var ( - projectID string - userEmail string = "anonymous@example.com" + r *gin.Engine + projectID string + userEmail string = "anonymous@example.com" + userEmailParam string = "user" ) type ClientError struct { @@ -30,7 +32,7 @@ func main() { } LogInfo("Starting Herodotus...") - r := gin.Default() + r = gin.Default() r.LoadHTMLGlob("templates/*") r.Static("/js", "./js") r.StaticFile("/favicon.ico", "./favicon.ico") @@ -49,22 +51,31 @@ func login(c *gin.Context) { } func startConversation(c *gin.Context) { - params := c.Params - log.Println(params) + + // extractParams will redirect if user isn't logged in. + userEmail = extractParams(c) + LogInfo("Start conversation request received") c.HTML(http.StatusOK, "index.html", gin.H{ "Message": struct { Message string + Email string }{ Message: "Hello! I hear that you want to go on a trip somewhere. Tell me about it.", + Email: userEmail, }, }) } func respondToUser(c *gin.Context) { + + // extractParams will redirect if user isn't logged in. + userEmail = extractParams(c) + LogInfo("Respond to user request received") + // Parse Form c.Request.ParseForm() userMsg := c.Request.Form["userMsg"][0] log.Println(userMsg) @@ -92,8 +103,10 @@ func respondToUser(c *gin.Context) { c.HTML(http.StatusOK, "index.html", gin.H{ "Message": struct { Message string + Email string }{ Message: botResponse, + Email: userEmail, }, }) } @@ -110,3 +123,14 @@ func clientError(c *gin.Context) { LogError(fmt.Sprintf("clientError: %s, %s, %s\n", cError.Code, cError.Message, cError.Email)) c.JSON(http.StatusOK, gin.H{"error": "message logged"}) } + +func extractParams(c *gin.Context) string { + // Verify that the user is signed in before answering + userEmail = c.Query(userEmailParam) + if userEmail == "" { + LogWarning("User attempted to navigate directly to /home; redirected to sign-in") + c.Request.URL.Path = "/" + r.HandleContext(c) + } + return userEmail +} diff --git a/templates/index.html b/templates/index.html index bc57748..2443368 100644 --- a/templates/index.html +++ b/templates/index.html @@ -27,7 +27,9 @@ {{ template "herodotus_msg.tmpl" .Message }} - {{ template "user_msg.tmpl" }} + {{ template "user_msg.tmpl" .Message }} + + \ No newline at end of file diff --git a/templates/user_msg.tmpl b/templates/user_msg.tmpl index 6f1efd3..cbe2b0c 100644 --- a/templates/user_msg.tmpl +++ b/templates/user_msg.tmpl @@ -1,5 +1,5 @@
-
+

User

+