WIP Adding session auth and routes.
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import logging, json, os, nre, sequtils, strtabs, tables
|
||||
import logging, json, os, nre, sequtils, strtabs, tables, times
|
||||
import private/util
|
||||
|
||||
# Types
|
||||
@ -21,14 +21,23 @@ type
|
||||
cfgFilePath*, defaultBranch*, name*, repo*: string
|
||||
envVars*: StringTableRef
|
||||
|
||||
StrawBossConfig* = object
|
||||
artifactsRepo*: string
|
||||
projects*: seq[ProjectDef]
|
||||
|
||||
RunRequest* = object
|
||||
projectName*, stepName*, buildRef*, workspaceDir*: string
|
||||
forceRebuild*: bool
|
||||
|
||||
User* = object
|
||||
name*: string
|
||||
hashedPwd*: string
|
||||
|
||||
UserRef* = ref User
|
||||
|
||||
StrawBossConfig* = object
|
||||
artifactsRepo*: string
|
||||
authSecret*: string
|
||||
projects*: seq[ProjectDef]
|
||||
users*: seq[UserRef]
|
||||
|
||||
|
||||
# internal utils
|
||||
|
||||
let nullNode = newJNull()
|
||||
@ -62,9 +71,18 @@ proc loadStrawBossConfig*(cfgFile: string): StrawBossConfig =
|
||||
envVars: envVars,
|
||||
repo: pJson.getOrFail("repo", "project definition").getStr))
|
||||
|
||||
var users: seq[UserRef] = @[]
|
||||
|
||||
for uJson in jsonCfg.getIfExists("users").getElems:
|
||||
users.add(UserRef(
|
||||
name: uJson.getOrFail("name", "user record").getStr,
|
||||
hashedPwd: uJson.getOrFail("hashedPwd", "user record").getStr))
|
||||
|
||||
result = StrawBossConfig(
|
||||
artifactsRepo: jsonCfg.getIfExists("artifactsRepo").getStr("artifacts"),
|
||||
projects: projectDefs)
|
||||
authSecret: jsonCfg.getOrFail("authSecret", "strawboss config").getStr,
|
||||
projects: projectDefs,
|
||||
users: users)
|
||||
|
||||
proc loadProjectConfig*(cfgFile: string): ProjectCfg =
|
||||
if not existsFile(cfgFile):
|
||||
@ -120,6 +138,15 @@ proc `%`*(s: BuildStatus): JsonNode =
|
||||
"details": s.details
|
||||
}
|
||||
|
||||
proc `%`*(p: ProjectDef): JsonNode =
|
||||
result = %* {
|
||||
"name": p.name,
|
||||
"cfgFilePath": p.cfgFilePath,
|
||||
"defaultBranch": p.defaultBranch,
|
||||
"repo": p.repo }
|
||||
|
||||
# TODO: envVars?
|
||||
|
||||
proc `%`*(req: RunRequest): JsonNode =
|
||||
result = %* {
|
||||
"projectName": req.projectName,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import asyncdispatch, jester, json, osproc, tempfile
|
||||
import asyncdispatch, jester, json, jwt, osproc, sequtils, tempfile, times
|
||||
|
||||
import ./configuration, ./core, private/util
|
||||
|
||||
@ -9,6 +9,60 @@ type Worker = object
|
||||
process*: Process
|
||||
workingDir*: string
|
||||
|
||||
type
|
||||
Session = object
|
||||
user*: UserRef
|
||||
issuedAt*, expires*: TimeInfo
|
||||
|
||||
const ISO_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"
|
||||
|
||||
proc makeJsonResp(status: HttpCode): string =
|
||||
result = $(%* {
|
||||
"statusCode": status.int,
|
||||
"status": $status
|
||||
})
|
||||
|
||||
proc newSession(user: UserRef): Session =
|
||||
result = Session(
|
||||
user: user,
|
||||
issuedAt: getGMTime(getTime()),
|
||||
expires: daysForward(7))
|
||||
|
||||
proc toJWT(session: Session): JWT =
|
||||
result = JWT(
|
||||
header: JOSEHeader(alg: HS256, typ: "jwt"),
|
||||
claims: toClaims(%*{
|
||||
"sub": session.user.name,
|
||||
"iss": session.issuedAt.format(ISO_TIME_FORMAT),
|
||||
"exp": session.expires.format(ISO_TIME_FORMAT) }))
|
||||
|
||||
proc extractSession(cfg: StrawBossConfig, request: Request): Session =
|
||||
|
||||
# Find the auth header
|
||||
if not request.headers.hasKey("Authentication"):
|
||||
raiseEx "No auth token."
|
||||
|
||||
# Read and verify the JWT token
|
||||
let jwt = toJWT(request.headers["Authentication"])
|
||||
var secret = cfg.authSecret
|
||||
if not jwt.verify(secret): raiseEx "Unable to verify auth token."
|
||||
jwt.verifyTimeClaims()
|
||||
|
||||
# Find the user record (if authenticated)
|
||||
let username = jwt.claims["sub"].node.str
|
||||
let users = cfg.users.filterIt(it.name == username)
|
||||
if users.len != 1: raiseEx "Could not find session user."
|
||||
|
||||
result = Session(
|
||||
user: users[0],
|
||||
issuedAt: parse(jwt.claims["iat"].node.str, ISO_TIME_FORMAT),
|
||||
expires: parse(jwt.claims["exp"].node.str, ISO_TIME_FORMAT))
|
||||
|
||||
template requireAuth() =
|
||||
var session {.inject.}: Session
|
||||
try: session = extractSession(givenCfg, request)
|
||||
except: resp Http401, makeJsonResp(Http401), "application/json"
|
||||
|
||||
proc spawnWorker(req: RunRequest): Worker =
|
||||
let dir = mkdtemp()
|
||||
var args = @["run", req.projectName, req.stepName, "-r", req.buildRef, "-w", dir]
|
||||
@ -25,8 +79,13 @@ proc start*(givenCfg: StrawBossConfig): void =
|
||||
get "/api/ping":
|
||||
resp $(%*"pong"), "application/json"
|
||||
|
||||
get "/api/auth-token":
|
||||
resp Http501, makeJsonResp(Http501), "application/json"
|
||||
|
||||
get "/api/projects":
|
||||
resp $(%*[]), "application/json"
|
||||
requireAuth()
|
||||
#let projectDefs: seq[ProjectDef] = givenCfg.projects.mapIt(it)
|
||||
resp $(%(givenCfg.projects)), "application/json"
|
||||
|
||||
post "/api/project/@projectName/@stepName/run/@buildRef?":
|
||||
workers.add(spawnWorker(RunRequest(
|
||||
|
Reference in New Issue
Block a user