From 26aa246188c03086862fd35398ca23b56a52ee80 Mon Sep 17 00:00:00 2001
From: Jonathan Bernard <jonathan@jdbernard.com>
Date: Sat, 29 Dec 2018 22:19:26 -0600
Subject: [PATCH] Add CORS OPTIONS routes.

---
 src/main/nim/strawbosspkg/server.nim | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/src/main/nim/strawbosspkg/server.nim b/src/main/nim/strawbosspkg/server.nim
index 5a8e36c..122e306 100644
--- a/src/main/nim/strawbosspkg/server.nim
+++ b/src/main/nim/strawbosspkg/server.nim
@@ -37,6 +37,22 @@ template halt(code: HttpCode,
   result.matched = true
   break allRoutes
 
+template allowCors(methods = @["GET"], allowedHeaders = @["*"]): typed =
+  ## Immediately replies with the appropriate headers for a CORS response
+  ## allowing the specified options.. This means any further code will not be
+  ## executed after calling this template in the current route.
+  bind TCActionSend, newHttpHeaders
+  result[0] = CallbackAction.TCActionSend
+  result[1] = Http200
+  result[2] = some(@{
+    "Access-Control-Allow-Origin": $request.headers["Origin"],
+    "Access-Control-Allow-Methods": methods.join(", "),
+    "Access-Control-Allow-Headers": allowedHeaders.join(", ")
+  })
+  result[3] = ""
+  result.matched = true
+  break allRoutes
+
 template jsonResp(code: HttpCode, details: string = "", headers: RawHeaders = @{:} ) =
   halt(
     code,
@@ -169,6 +185,7 @@ proc start*(cfg: StrawBossConfig): void =
     get "/version":
       resp($(%("strawboss v" & SB_VERSION)), JSON)
 
+    options "/auth-token": allowCors(@["POST"])
     post "/auth-token":
       var uname, pwd: string
       try:
@@ -182,11 +199,14 @@ proc start*(cfg: StrawBossConfig): void =
         resp($(%authToken), JSON)
       except: jsonResp(Http401, getCurrentExceptionMsg())
 
+    options "/verify-auth": allowCors()
     get "/verify-auth":
       checkAuth()
 
       resp(Http200, $(%*{ "username": session.user.name }), JSON)
 
+    options "/projects": allowCors(@["GET", "POST"])
+
     get "/projects":
       ## List project summaries (ProjectDefs only)
 
@@ -244,7 +264,7 @@ proc start*(cfg: StrawBossConfig): void =
           let msg = "unable to list versions for project " & @"projectName"
           json500Resp(getCurrentException(), msg)
 
-    get "/project/@projectName/version/@version?":
+    get "/project/@projectName/version/@version":
       ## Get a detailed project record including step definitions (ProjectConfig).
 
       checkAuth()
@@ -407,6 +427,7 @@ proc start*(cfg: StrawBossConfig): void =
       ## TODO: how do we want to handle auth for this? Unlike
       #checkAuth(): if not authed: return true
 
+    options "/project/@projectName/step/@stepName/run/@buildRef?": allowCors(@["POST"])
     post "/project/@projectName/step/@stepName/run/@buildRef?":
       # Kick off a run
 
@@ -444,6 +465,10 @@ proc start*(cfg: StrawBossConfig): void =
         resp($(%"shutting down"), JSON)
 
 
+    # In general, we will allow all cross-origin GET requests, as all of our
+    # APIs that are accessible via GET requests are idempotent.
+    options re".*": allowCors(@["GET"])
+
     get re".*":
       jsonResp(Http404)