110 lines
3.4 KiB
Nim
110 lines
3.4 KiB
Nim
|
import
|
||
|
std/[base64, times, strutils],
|
||
|
test_env,
|
||
|
unittest2,
|
||
|
chronicles,
|
||
|
nimcrypto/[hmac, utils],
|
||
|
json_rpc/[rpcclient],
|
||
|
./types
|
||
|
|
||
|
# JWT Authentication Related
|
||
|
const
|
||
|
defaultJwtTokenSecretBytes = "secretsecretsecretsecretsecretse"
|
||
|
maxTimeDriftSeconds = 5'i64
|
||
|
defaultProtectedHeader = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"
|
||
|
|
||
|
proc base64urlEncode(x: auto): string =
|
||
|
base64.encode(x, safe = true).replace("=", "")
|
||
|
|
||
|
proc prepareAuthCallToken(secret: string, time: int64): string =
|
||
|
let key = cast[seq[byte]](secret)
|
||
|
let payload = """{"iat": $1}""" % [$time]
|
||
|
let token = defaultProtectedHeader & "." & payload.base64urlEncode
|
||
|
let sig = base64urlEncode(sha256.hmac(key, token).data)
|
||
|
token & "." & sig
|
||
|
|
||
|
proc getClient(t: TestEnv, token: string): RpcHttpClient =
|
||
|
proc authHeaders(): seq[(string, string)] =
|
||
|
@[("Authorization", "Bearer " & token)]
|
||
|
|
||
|
let client = newRpcHttpClient(getHeaders = authHeaders)
|
||
|
waitFor client.connect("localhost", t.conf.rpcPort, false)
|
||
|
return client
|
||
|
|
||
|
template genAuthTest(procName: untyped, timeDriftSeconds: int64, customAuthSecretBytes: string, authOK: bool) =
|
||
|
proc procName(t: TestEnv): TestStatus =
|
||
|
result = TestStatus.OK
|
||
|
|
||
|
# Default values
|
||
|
var
|
||
|
# All test cases send a simple TransitionConfigurationV1 to check the Authentication mechanism (JWT)
|
||
|
tConf = TransitionConfigurationV1(
|
||
|
terminalTotalDifficulty: t.ttd
|
||
|
)
|
||
|
testSecret = customAuthSecretBytes
|
||
|
testTime = getTime().toUnix
|
||
|
|
||
|
if testSecret.len == 0:
|
||
|
testSecret = defaultJwtTokenSecretBytes
|
||
|
|
||
|
if timeDriftSeconds != 0:
|
||
|
testTime = testTime + timeDriftSeconds
|
||
|
|
||
|
let token = prepareAuthCallToken(testSecret, testTime)
|
||
|
let client = getClient(t, token)
|
||
|
|
||
|
try:
|
||
|
discard waitFor client.call("engine_exchangeTransitionConfigurationV1", %[%tConf])
|
||
|
testCond authOk:
|
||
|
error "Authentication was supposed to fail authentication but passed"
|
||
|
except CatchableError:
|
||
|
testCond not authOk:
|
||
|
error "Authentication was supposed to pass authentication but failed"
|
||
|
|
||
|
genAuthTest(authTest1, 0'i64, "", true)
|
||
|
genAuthTest(authTest2, 0'i64, "secretsecretsecretsecretsecrets", false)
|
||
|
genAuthTest(authTest3, 0'i64, "\0secretsecretsecretsecretsecretse", false)
|
||
|
genAuthTest(authTest4, -1 - maxTimeDriftSeconds, "", false)
|
||
|
genAuthTest(authTest5, 1 - maxTimeDriftSeconds, "", true)
|
||
|
genAuthTest(authTest6, maxTimeDriftSeconds + 1, "", false)
|
||
|
genAuthTest(authTest7, maxTimeDriftSeconds - 1, "", true)
|
||
|
|
||
|
# JWT Authentication Tests
|
||
|
const authTestList* = [
|
||
|
TestSpec(
|
||
|
name: "JWT Authentication: No time drift, correct secret",
|
||
|
run: authTest1,
|
||
|
enableAuth: true
|
||
|
),
|
||
|
TestSpec(
|
||
|
name: "JWT Authentication: No time drift, incorrect secret (shorter)",
|
||
|
run: authTest2,
|
||
|
enableAuth: true
|
||
|
),
|
||
|
TestSpec(
|
||
|
name: "JWT Authentication: No time drift, incorrect secret (longer)",
|
||
|
run: authTest3,
|
||
|
enableAuth: true
|
||
|
),
|
||
|
TestSpec(
|
||
|
name: "JWT Authentication: Negative time drift, exceeding limit, correct secret",
|
||
|
run: authTest4,
|
||
|
enableAuth: true
|
||
|
),
|
||
|
TestSpec(
|
||
|
name: "JWT Authentication: Negative time drift, within limit, correct secret",
|
||
|
run: authTest5,
|
||
|
enableAuth: true
|
||
|
),
|
||
|
TestSpec(
|
||
|
name: "JWT Authentication: Positive time drift, exceeding limit, correct secret",
|
||
|
run: authTest6,
|
||
|
enableAuth: true
|
||
|
),
|
||
|
TestSpec(
|
||
|
name: "JWT Authentication: Positive time drift, within limit, correct secret",
|
||
|
run: authTest7,
|
||
|
enableAuth: true
|
||
|
)
|
||
|
]
|