diff --git a/docs/authentication.md b/docs/authentication.md index daafa19..c609377 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -74,9 +74,30 @@ key vault is expected to be a string. The string is passed to the container in an environment variable. +## Certificate (RESTler only at this time) + +Authentication using a certificate is supported by RESTler. +You have to pass the authentication certificate using file shares in RAFT. + +Using **Cert** as the authentication method key, use the path to the certificate as the value. +The certificate must be an X.509 certificate in PEM format. + +See `ReadOnlyFileShareMounts` section in [JobDefinition documentation](./schema/jobdefinition.md) +for details on how to mount file shares. + +``` +"tasks": [ + { + "toolName" : "RESTler", + "authenticationMethod": { + "Cert": "/path/to/cert" + } + }, +``` + ## Agent-Utilities container -Starting with v4 of RAFT - a deicated utilities docker container is deployed with every job run. This container is responsible +Starting with v4 of RAFT - a dedicated utilities docker container is deployed with every job run. This container is responsible for performing authentication with a service under test and processing events from tools. You do not need to do anything special to update agent utilites when running RAFT local, since agent utilities used directly from your CLI folder. To update agent utiliites when diff --git a/src/Agent/RESTlerAgent/AgentMain.fs b/src/Agent/RESTlerAgent/AgentMain.fs index 743206e..f13a3fe 100644 --- a/src/Agent/RESTlerAgent/AgentMain.fs +++ b/src/Agent/RESTlerAgent/AgentMain.fs @@ -260,22 +260,29 @@ let createRESTlerEngineParameters match task.AuthenticationMethod with | Some c -> if c.IsEmpty then - None + Raft.RESTlerTypes.Engine.NoAuth else - let authConfig : Raft.RESTlerTypes.Engine.RefreshableTokenOptions = - { - RefreshInterval = Option.defaultValue (int <| TimeSpan.FromHours(1.0).TotalSeconds) runConfiguration.AuthenticationTokenRefreshIntervalSeconds - RefreshExec = "python3" - RefreshArgs = - let url = - let auth = Seq.head c - let authType, authSecretName = auth.Key, auth.Value - System.Uri(authenicationUrl, sprintf "auth/%s/%s" authType authSecretName) - sprintf """-c "import requests; import json; r=requests.get('%s'); assert r.ok, r.text; print(\"{u'user1':{}}\nAuthorization: \" + json.loads(r.text)['token'])" """ url.AbsoluteUri - } - printfn "Refreshable token configuration : %A" authConfig - Some authConfig - | None -> None + let auth = Seq.head c + let authType, authSecret = auth.Key, auth.Value + + if authType.ToLower().Trim() = "cert" then + if IO.File.Exists authSecret then + Raft.RESTlerTypes.Engine.Cert {CertificatePath = authSecret} + else + failwithf "Failed to find authentication certificate: %s" authSecret + else + let authConfig : Raft.RESTlerTypes.Engine.RefreshableTokenOptions = + { + RefreshInterval = Option.defaultValue (int <| TimeSpan.FromHours(1.0).TotalSeconds) runConfiguration.AuthenticationTokenRefreshIntervalSeconds + RefreshExec = "python3" + RefreshArgs = + let url = + System.Uri(authenicationUrl, sprintf "auth/%s/%s" authType authSecret) + sprintf """-c "import requests; import json; r=requests.get('%s'); assert r.ok, r.text; print(\"{u'user1':{}}\nAuthorization: \" + json.loads(r.text)['token'])" """ url.AbsoluteUri + } + printfn "Refreshable token configuration : %A" authConfig + Raft.RESTlerTypes.Engine.Token authConfig + | None -> Raft.RESTlerTypes.Engine.NoAuth /// The delay in seconds after invoking an API that creates a new resource ProducerTimingDelay = Option.defaultValue 10 runConfiguration.ProducerTimingDelay diff --git a/src/Agent/RESTlerAgent/RESTlerDriver.fs b/src/Agent/RESTlerAgent/RESTlerDriver.fs index 6574c58..b54b135 100644 --- a/src/Agent/RESTlerAgent/RESTlerDriver.fs +++ b/src/Agent/RESTlerAgent/RESTlerDriver.fs @@ -341,8 +341,8 @@ module private RESTlerInternal = async { do! match parameters.RefreshableTokenOptions with - | None -> async.Return() - | Some t -> validateAuthentication workingDirectory t + | Raft.RESTlerTypes.Engine.NoAuth | Raft.RESTlerTypes.Engine.Cert _ -> async.Return() + | Raft.RESTlerTypes.Engine.Token t -> validateAuthentication workingDirectory t let testParameters = getCommonParameters workingDirectory (Some testType) parameters do! runRestlerEngine restlerRootDirectory workingDirectory testParameters @@ -353,8 +353,8 @@ module private RESTlerInternal = async { do! match parameters.RefreshableTokenOptions with - | None -> async.Return() - | Some t -> validateAuthentication workingDirectory t + | Raft.RESTlerTypes.Engine.NoAuth | Raft.RESTlerTypes.Engine.Cert _ -> async.Return() + | Raft.RESTlerTypes.Engine.Token t -> validateAuthentication workingDirectory t let fuzzingParameters = getCommonParameters workingDirectory (Some fuzzType) parameters do! runRestlerEngine restlerRootDirectory workingDirectory fuzzingParameters @@ -364,8 +364,8 @@ module private RESTlerInternal = async { do! match parameters.RefreshableTokenOptions with - | None -> async.Return() - | Some t -> validateAuthentication workingDirectory t + | Raft.RESTlerTypes.Engine.NoAuth | Raft.RESTlerTypes.Engine.Cert _ -> async.Return() + | Raft.RESTlerTypes.Engine.Token t -> validateAuthentication workingDirectory t let replayParameters = (getCommonParameters workingDirectory None parameters) diff --git a/src/Agent/RESTlerAgent/RESTlerTypes.fs b/src/Agent/RESTlerAgent/RESTlerTypes.fs index ed022e8..46c1bfe 100644 --- a/src/Agent/RESTlerAgent/RESTlerTypes.fs +++ b/src/Agent/RESTlerAgent/RESTlerTypes.fs @@ -20,6 +20,17 @@ module Engine = RefreshArgs : string } + type CertAuthentication = + { + //Path to your X.509 certificate in PEM format. Provide for Certificate Based Authentication + CertificatePath: string + } + + + type Authentication = + | NoAuth + | Token of RefreshableTokenOptions + | Cert of CertAuthentication type EnginePerResourceSetting = { @@ -55,7 +66,7 @@ module Engine = MaxDurationHours : float option /// The authentication options, when tokens are required - RefreshableTokenOptions : RefreshableTokenOptions option + RefreshableTokenOptions : Authentication /// The delay in seconds after invoking an API that creates a new resource ProducerTimingDelay : int @@ -161,6 +172,8 @@ module Engine = //in hours time_budget : float option + client_certificate_path: string option + token_refresh_cmd : string option //seconds @@ -229,6 +242,8 @@ module Engine = //seconds token_refresh_interval = None + client_certificate_path = None + //if set - poll for resource to be created before //proceeding wait_for_async_resource_creation = true @@ -239,11 +254,13 @@ module Engine = } static member FromEngineParameters (fuzzingMode: string option) (p : EngineParameters) = - let tokenRefreshInterval, tokenRefreshCommand = + let tokenRefreshInterval, tokenRefreshCommand, authCertificatePath = match p.RefreshableTokenOptions with - | None -> None, None - | Some options -> - (Some options.RefreshInterval), (Some (sprintf "%s %s" options.RefreshExec options.RefreshArgs)) + | NoAuth -> None, None, None + | Token options -> + (Some options.RefreshInterval), (Some (sprintf "%s %s" options.RefreshExec options.RefreshArgs)), None + | Cert options -> + None, None, (Some options.CertificatePath) { Settings.Default with host = p.Host @@ -255,6 +272,7 @@ module Engine = no_ssl = not p.UseSsl no_tokens_in_logs = not p.ShowAuthToken fuzzing_mode = fuzzingMode + client_certificate_path = authCertificatePath token_refresh_cmd = tokenRefreshCommand token_refresh_interval = tokenRefreshInterval max_request_execution_time = Option.defaultValue Settings.Default.max_request_execution_time p.MaxRequestExecutionTime