Added ProjectDef parsing code. Unit test for , authentication logic.
This commit is contained in:
@ -1,6 +1,8 @@
|
||||
import logging, json, os, nre, sequtils, strtabs, tables, times
|
||||
import private/util
|
||||
|
||||
from typeinfo import toAny
|
||||
|
||||
# Types
|
||||
#
|
||||
type
|
||||
@ -68,6 +70,17 @@ proc getOrFail(n: JsonNode, key: string, objName: string = ""): JsonNode =
|
||||
return n[key]
|
||||
|
||||
# Configuration parsing code
|
||||
|
||||
proc parseProjectDef*(pJson: JsonNode): ProjectDef =
|
||||
var envVars = newStringTable(modeCaseSensitive)
|
||||
for k, v in pJson.getIfExists("envVars").getFields: envVars[k] = v.getStr("")
|
||||
|
||||
result = ProjectDef(
|
||||
cfgFilePath: pJson.getIfExists("cfgFilePath").getStr("strawboss.json"),
|
||||
defaultBranch: pJson.getIfExists("defaultBranch").getStr("master"),
|
||||
name: pJson.getOrFail("name", "project definition").getStr,
|
||||
envVars: envVars,
|
||||
repo: pJson.getOrFail("repo", "project definition").getStr)
|
||||
|
||||
proc loadStrawBossConfig*(cfgFile: string): StrawBossConfig =
|
||||
if not existsFile(cfgFile):
|
||||
@ -75,20 +88,6 @@ proc loadStrawBossConfig*(cfgFile: string): StrawBossConfig =
|
||||
|
||||
let jsonCfg = parseFile(cfgFile)
|
||||
|
||||
var projectDefs: seq[ProjectDef] = @[]
|
||||
|
||||
for pJson in jsonCfg.getIfExists("projects").getElems:
|
||||
var envVars = newStringTable(modeCaseSensitive)
|
||||
for k, v in pJson.getIfExists("envVars").getFields: envVars[k] = v.getStr("")
|
||||
|
||||
projectDefs.add(
|
||||
ProjectDef(
|
||||
cfgFilePath: pJson.getIfExists("cfgFilePath").getStr("strawboss.json"),
|
||||
defaultBranch: pJson.getIfExists("defaultBranch").getStr("master"),
|
||||
name: pJson.getOrFail("name", "project definition").getStr,
|
||||
envVars: envVars,
|
||||
repo: pJson.getOrFail("repo", "project definition").getStr))
|
||||
|
||||
var users: seq[UserRef] = @[]
|
||||
|
||||
for uJson in jsonCfg.getIfExists("users").getElems:
|
||||
@ -101,7 +100,7 @@ proc loadStrawBossConfig*(cfgFile: string): StrawBossConfig =
|
||||
authSecret: jsonCfg.getOrFail("authSecret", "strawboss config").getStr,
|
||||
debug: jsonCfg.getIfExists("debug").getBVal(false),
|
||||
pwdCost: int8(jsonCfg.getOrFail("pwdCost", "strawboss config").getNum),
|
||||
projects: projectDefs,
|
||||
projects: jsonCfg.getIfExists("projects").getElems.mapIt(parseProjectDef(it)),
|
||||
users: users)
|
||||
|
||||
proc loadProjectConfig*(cfgFile: string): ProjectConfig =
|
||||
@ -141,6 +140,7 @@ proc loadBuildStatus*(statusFile: string): BuildStatus =
|
||||
state: jsonObj.getOrFail("state", "build status").getStr,
|
||||
details: jsonObj.getIfExists("details").getStr("") )
|
||||
|
||||
|
||||
# TODO: unused and untested, add tests if we start using this
|
||||
proc parseRunRequest*(reqStr: string): RunRequest =
|
||||
let reqJson = parseJson(reqStr)
|
||||
@ -166,7 +166,8 @@ proc `%`*(p: ProjectDef): JsonNode =
|
||||
"defaultBranch": p.defaultBranch,
|
||||
"repo": p.repo }
|
||||
|
||||
# TODO: envVars?
|
||||
result["envVars"] = newJObject()
|
||||
for k, v in p.envVars: result["envVars"][k] = %v
|
||||
|
||||
proc `%`*(req: RunRequest): JsonNode =
|
||||
result = %* {
|
||||
@ -178,3 +179,18 @@ proc `%`*(req: RunRequest): JsonNode =
|
||||
|
||||
proc `$`*(s: BuildStatus): string = result = pretty(%s)
|
||||
proc `$`*(req: RunRequest): string = result = pretty(%req)
|
||||
proc `$`*(pd: ProjectDef): string = result = pretty(%pd)
|
||||
|
||||
# TODO: maybe a macro for more general-purpose, shallow object comparison?
|
||||
#proc `==`*(a, b: ProjectDef): bool =
|
||||
|
||||
template shallowEquals(a, b: RootObj): bool =
|
||||
if type(a) != type(b): return false
|
||||
var anyB = toAny(b)
|
||||
|
||||
for name, value in a.fieldPairs:
|
||||
if value != b[name]: return false
|
||||
|
||||
return true
|
||||
|
||||
#proc `==`*(a, b: ProjectDef): bool = result = shallowEquals(a, b)
|
||||
|
@ -1,6 +1,7 @@
|
||||
import asyncdispatch, bcrypt, jester, json, jwt, os, osproc, sequtils, tempfile,
|
||||
times, unittest
|
||||
import asyncdispatch, bcrypt, jester, json, jwt, os, osproc, sequtils,
|
||||
strutils, tempfile, times, unittest
|
||||
|
||||
import logging
|
||||
import ./configuration, ./core, private/util
|
||||
|
||||
type Worker = object
|
||||
@ -47,7 +48,7 @@ proc toJWT*(cfg: StrawBossConfig, session: Session): string =
|
||||
jwt.sign(cfg.authSecret)
|
||||
result = $jwt
|
||||
|
||||
proc fromJWT*(cfg: StrawBossConfig, strTok: string): Session =
|
||||
proc fromJWT*(cfg: StrawBossConfig, strTok: string): Session =
|
||||
let jwt = toJWT(strTok)
|
||||
var secret = cfg.authSecret
|
||||
if not jwt.verify(secret): raiseEx "Unable to verify auth token."
|
||||
@ -66,11 +67,15 @@ proc fromJWT*(cfg: StrawBossConfig, strTok: string): Session =
|
||||
proc extractSession(cfg: StrawBossConfig, request: Request): Session =
|
||||
|
||||
# Find the auth header
|
||||
if not request.headers.hasKey("Authentication"):
|
||||
if not request.headers.hasKey("Authorization"):
|
||||
raiseEx "No auth token."
|
||||
|
||||
# Read and verify the JWT token
|
||||
result = fromJWT(cfg, request.headers["Authentication"])
|
||||
let headerVal = request.headers["Authorization"]
|
||||
if not headerVal.startsWith("Bearer "):
|
||||
raiseEx "Invalid Authentication type (only 'Bearer' is supported)."
|
||||
|
||||
result = fromJWT(cfg, headerVal[7..^1])
|
||||
|
||||
proc spawnWorker(req: RunRequest): Worker =
|
||||
let dir = mkdtemp()
|
||||
@ -101,10 +106,19 @@ proc makeAuthToken*(cfg: StrawBossConfig, uname, pwd: string): string =
|
||||
if not validatePwd(user, pwd): raiseEx "invalid username or password"
|
||||
result = toJWT(cfg, newSession(user))
|
||||
|
||||
template requireAuth() =
|
||||
|
||||
template withSession(body: untyped): untyped =
|
||||
var session {.inject.}: Session
|
||||
try: session = extractSession(givenCfg, request)
|
||||
except: resp(Http401, makeJsonResp(Http401), "application/json")
|
||||
var authed = false
|
||||
|
||||
try:
|
||||
session = extractSession(givenCfg, request)
|
||||
authed = true
|
||||
except:
|
||||
debug "Auth failed: " & getCurrentExceptionMsg()
|
||||
resp(Http401, makeJsonResp(Http401), "application/json")
|
||||
|
||||
if authed: body
|
||||
|
||||
proc start*(givenCfg: StrawBossConfig): void =
|
||||
|
||||
@ -116,18 +130,22 @@ proc start*(givenCfg: StrawBossConfig): void =
|
||||
appName = "/api"
|
||||
|
||||
routes:
|
||||
|
||||
get "/ping":
|
||||
resp($(%*"pong"), "application/json")
|
||||
|
||||
get "/auth-token":
|
||||
echo $request.params
|
||||
try:
|
||||
let authToken = makeAuthToken(givenCfg, @"username", @"password")
|
||||
resp("\"" & $authToken & "\"", "application/json")
|
||||
except: resp(Http401, makeJsonResp(Http401, getCurrentExceptionMsg()))
|
||||
|
||||
get "/projects":
|
||||
requireAuth()
|
||||
get "/verify-auth": withSession:
|
||||
resp(Http200, $(%*{
|
||||
"username": session.user.name
|
||||
}), "application/json")
|
||||
|
||||
get "/projects": withSession:
|
||||
resp($(%(givenCfg.projects)), "application/json")
|
||||
|
||||
post "/project/@projectName/@stepName/run/@buildRef?":
|
||||
|
Reference in New Issue
Block a user