Rest Assured: ignoring certificate errors - cucumber

The certificate I get back from the server has an error:
javax.net.ssl.SSLProtocolException: X.509 Certificate is incomplete:
SubjectAlternativeName extension MUST be marked critical when subject
field is empty
I found out that using relaxedHTTPSValidation should do the trick of ignoring the certificate errors.
So I tried to use it like this:
#When("^Get All Applications Request Executed$")
public void getAllApplicationsRequestExecuted() {
response =
given()
.relaxedHTTPSValidation()
.log().all()
.spec(testContext().getRequestSpec())
.when()
.get("application/api/virtual-services/") //Send the request along with the resource
.then()
.extract()
.response();
testContext().setResponse(response);
}
Also, I added this #Before hook:
#Before
public void setUseRelaxedHTTPSValidation(){
RestAssured.useRelaxedHTTPSValidation();
}
But I see no change.

Try using via configuration like this:
#When("^Get All Applications Request Executed$")
public void getAllApplicationsRequestExecuted() {
given()
.config(RestAssured.config().sslConfig(new SSLConfig().allowAllHostnames()))
.log().all()
.spec(testContext().getRequestSpec())
.when()
.get("application/api/virtual-services/") //Send the request along with the resource
.then()
.extract()
.response();
testContext().setResponse(response);
}

Related

Certificate was not authenticated, request succeeds anyway with asp.net 5 on Azure App Service

I'm trying to enable client certificate authentication for my server api per here:
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-5.0
The problem I'm seeing is that the certificate is sent by the client (as required by the Azure App Service settings), but even though I deliberately call context.Fail, the request is always processed and returns 200. I guess I'm probably missing something sort of fundamental - I'm totally new to, well, pretty much all of this server-side .NET. Thanks for looking!
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme).
AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.All;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context => {
context.Fail("FAIL!!!");
_logger.LogWarning("OnCertificateValidated!!!");
return Task.CompletedTask; },
OnAuthenticationFailed = context => {
context.Fail("BAD cert. BAD!");
_logger.LogWarning("OnAuthenticationFailed!!!");
return Task.CompletedTask; }
};
}).
AddCertificateCache();
services.AddLogging(loggingBuilder =>
{
loggingBuilder.AddConsole();
loggingBuilder.AddDebug();
loggingBuilder.AddAzureWebAppDiagnostics();
});
services.AddControllers();
}
And Configure
private static ILogger<Startup> _logger;
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
_logger = logger;
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCertificateForwarding();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
In my Azure App Service log stream I see
2021-04-21 05:42:07.759 +00:00 [Warning] MyApi.Startup: OnCertificateValidated!!!!!
2021-04-21 05:42:07.759 +00:00 [Information] Microsoft.AspNetCore.Authentication.Certificate.CertificateAuthenticationHandler: Certificate was not authenticated. Failure message: FAIL!!!
and if I configure App Service to allow no certificate, I get a different log, but the request still
2021-04-21 05:27:12.119 +00:00 [Debug] Microsoft.AspNetCore.Authentication.Certificate.CertificateAuthenticationHandler: No client certificate found.
2021-04-21 05:27:12.120 +00:00 [Debug] Microsoft.AspNetCore.Authentication.Certificate.CertificateAuthenticationHandler: AuthenticationScheme: Certificate was not authenticated.
But, in all cases, the request succeeds, while the above linked documentation seemed to indicate I should see a 403 (Forbidden) result -- which I did when I sent no certificate and Azure configuration was set to require a certificate. That's the only time I can get it to fail.
I see that I can perhaps use a method as described here -- retrieve the request header and parse and validate it entirely myself. But isn't the above supposed to work?
https://learn.microsoft.com/en-us/azure/app-service/app-service-web-configure-tls-mutual-auth
I ran into this as well. My issue was that I had not declared any controller routes as requiring Authentication.
Two possible fixes depending on your use case:
Turn on auth for all routes
// Require auth by default for all routes
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
Turn on auth for specific controllers/actions with an attribute per the docs
[Authorize]
public class AccountController : Controller
{
public ActionResult Login()
{
}
public ActionResult Logout()
{
}
}

Spring Integration HTTP outbound gateway retry based on reply content

I'm using an API that works in 2 steps:
It starts processing of a document in async way where it provides you an id that you use for step 2
It provides an endpoint where you can get the results but only when they are ready. So basically it will always give you a 200 response with some details like the status of the processing.
So the question is how can I implement a custom "success" criteria for the HTTP outbound gateway. I would also like to combine it with a RetryAdvice which I already have implemented.
I've tried the following but first of all the message's payload that is provided in the HandleMessageAdvice is empty, and secondly the retry is not triggered:
.handle(Http.outboundGateway("https://northeurope.api.cognitive.microsoft.com/vision/v3" +
".0/read/analyzeResults/abc")
.mappedRequestHeaders("Ocp-Apim-Subscription-Key")
.httpMethod(HttpMethod.GET), c -> c.advice(this.advices.retryAdvice())
.handleMessageAdvice(new AbstractHandleMessageAdvice() {
#Override
protected Object doInvoke(MethodInvocation invocation, Message<?> message) throws Throwable {
String body = (String) message.getPayload();
if (StringUtils.isEmpty(body))
throw new RuntimeException("Still analyzing");
JSONObject document = new JSONObject(body);
if (document.has("analyzeResult"))
return message;
else
throw new RuntimeException("Still analyzing");
}
}))
I've found this answer from Artem from 4 years back but first of all I didn't find the reply channel method on the outbound gateway and secondly not sure if this scenario has already been improved in the newer version of Spring Integaration: http outbound retry with conditions (For checker condition).
UPDATE
Following Artem's suggestion I have the following:
.handle(Http.outboundGateway("https://northeurope.api.cognitive.microsoft.com/vision/v3" +
".0/read/analyzeResults/abc")
.mappedRequestHeaders("Ocp-Apim-Subscription-Key")
.httpMethod(HttpMethod.GET), c -> c.advice(advices.verifyReplySuccess())
.advice(advices.retryUntilRequestCompleteAdvice()))
And the advice:
#Bean
public Advice verifyReplySuccess() {
return new AbstractRequestHandlerAdvice() {
#Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) {
try {
Object payload = ((MessageBuilder) callback.execute()).build().getPayload();
String body = (String) ((ResponseEntity) payload).getBody();
JSONObject document = new JSONObject(body);
if (document.has("analyzeResult"))
return message;
} catch (JSONException e) {
throw new RuntimeException(e);
}
throw new RuntimeException("Still analyzing");
}
};
}
But now when I debug the doInvoke method, the body of the payload is null. It's strange as when I execute the same GET request using Postman, the body is correctly returned. Any idea?
The body from response using Postman looks like this:
{
"status": "succeeded",
"createdDateTime": "2020-09-01T10:55:52Z",
"lastUpdatedDateTime": "2020-09-01T10:55:57Z",
"analyzeResult": {
"version": "3.0.0",
"readResults": [
{
"page": 1,........
Here is the payload that I get from the outbound gateway using callback:
<200,[Transfer-Encoding:"chunked", Content-Type:"application/json; charset=utf-8", x-envoy-upstream-service-time:"27", CSP-Billing-Usage:"CognitiveServices.ComputerVision.Transaction=1", apim-request-id:"a503c72f-deae-4299-9e32-625d831cfd91", Strict-Transport-Security:"max-age=31536000; includeSubDomains; preload", x-content-type-options:"nosniff", Date:"Tue, 01 Sep 2020 19:48:36 GMT"]>
There is indeed no request and reply channel options in Java DSL because you simply wrap that handle() into channel() configuration or just chain endpoints in the flow natural way and they are going to exchange messages using implicit direct channels in between. You can look into Java DSL IntegrationFlow as a <chain> in the XML configuration.
Your advice configuration is a bit wrong: you need declare your custom advice as a first in a chain, so when exception is thrown from there a retry one is going to handle it.
You should also consider to implement an AbstractRequestHandlerAdvice to align it with the RequestHandlerRetryAdvice logic.
You implement there a doInvoke(), call ExecutionCallback.execute() and analyze the result to return as is or throw a desired exception. A result of that call for HttpRequestExecutingMessageHandler is going to be an AbstractIntegrationMessageBuilder and probably a ResponseEntity as a payload to check for your further logic.
Following Artem's suggestion I came up with the following (additional trick was to set the expectedResponseType to String as otherwise using the ResponseEntity the body was empty):
.handle(Http.outboundGateway("https://northeurope.api.cognitive.microsoft.com/vision/v3" +
".0/read/analyzeResults/abc")
.mappedRequestHeaders("Ocp-Apim-Subscription-Key")
.httpMethod(HttpMethod.GET).expectedResponseType(String.class),
c -> c.advice(advices.retryUntilRequestCompleteAdvice())
.advice(advices.verifyReplySuccess()))
And the advice:
#Bean
public Advice verifyReplySuccess() {
return new AbstractRequestHandlerAdvice() {
#Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) {
Object payload = ((MessageBuilder) callback.execute()).build().getPayload();
if (((String) payload).contains("analyzeResult"))
return payload;
else
throw new RuntimeException("Still analyzing");
}
};
}

Accept x-www-form-urlencoded in Web API .NET Core

I have a .NET Core Web API that is returning a 415 Unsupported Media Error when I try to post some data to it that includes some json. Here's part of what is returned in the Chrome Debugger:
Request URL:http://localhost:51608/api/trackAllInOne/set
Request Method:POST
Status Code:415 Unsupported Media Type
Accept:text/javascript, text/html, application/xml, text/xml, */*
Content-Type:application/x-www-form-urlencoded
action:finish
currentSco:CSharp-SSLA:__How_It_Works_SCO
data:{"status":"incomplete","score":""}
activityId:13
studentId:1
timestamp:1519864867900
I think this has to do with my controller not accepting application/x-www-form-urlencoded data - but I'm not sure. I've tried decorating my controler with Consumes but that does not seem to work.
[HttpPost]
[Route("api/trackAllInOne/set")]
[Consumes("application/x-www-form-urlencoded")]
public IActionResult Post([FromBody] PlayerPackage playerPackage)
{ etc..}
Any help greatly appreciated.
The following code worked fine in .NET 4.6.1 and I am able to capture and process the posts shown above.
[ResponseType(typeof(PlayerPackage))]
public async Task<IHttpActionResult> PostLearningRecord(PlayerPackage playerPackage)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var id = Convert.ToInt32(playerPackage.ActivityId);
var learningRecord = await _context.LearningRecords.FindAsync(id);
if (learningRecord == null)
return NotFound();
etc...
Try using [FromForm] instead of [FromBody].
public IActionResult Post([FromForm] PlayerPackage playerPackage)
FromBody > Bind from JSON
FromForm > Bind from Form parameters
You can also remove [FromBody] altogether and trial it then. Because you are expecting form-urlencoded should tell it to bind to object.
For PlayerPackage, the request should send a PlayerPackage Json Object, based on your description, you could not control the request which is posted from other place.
For the request, its type is application/x-www-form-urlencoded, it will send data with {"status":"incomplete","score":""} in string Format instead of Json object. If you want to accept {"status":"incomplete","score":""}, I suggest you change the method like below, and then convert the string to Object by Newtonsoft.Json
[HttpPost]
[Route("~/api/trackAllInOne/set")]
[Consumes("application/x-www-form-urlencoded")]
public IActionResult Post([FromForm] string data)
{
PlayerPackage playerPackage = JsonConvert.DeserializeObject<PlayerPackage>(data);
return Json(data);
}
This did the trick for me:
[HttpPost]
[Consumes("application/x-www-form-urlencoded")]
public IActionResult Post([FromForm]IFormCollection value)
I had the same problem. FormDataCollection has no default constructors which is required by Formatters. Use IFormCollection instead.
Can make setting like as
[HttpPost()]/[HttpGet()]
[Consumes("application/x-www-form-urlencoded")]
public async Task<IActionResult> MethodName([FromForm] IFormCollection value)
don't forget to add [FromForm]

post parameter that passed in as #Field does not being added into RequestBody in Retrofit2?

base on Retrofit #Field doc, when making a post request
a combination of using #FormUrlEncoded and #Field will yields a request body of: paramName=paramValue&paramName=paramValue.
but what I am not getting field paramemters included in RequestBody.
my interface definition as below:
(I have no endpoint, and jake Wharton says use ./ as explicit intent that you want to use the path of the base URL and add nothing to it, but I tried #POST("./") it's not work, i got 404 not found error, so I add full url address to bypass this error temporarily)
public interface BannerService {
#FormUrlEncoded
#POST("http://10.10.20.190:6020/router")
Flowable<List<BannerBeanList.BannerBean>> getBannerData(#Field("method") String method, #Field("adspaceId") String adspaceId);
}
and this is how I make calls to interface service:
public class RemoteListDataSource implements RemoteDataSource {
#Override
public Flowable<List<BannerBeanList.BannerBean>> getBannerListData(ADFilterType adFilterType) {
BannerService bannerService = RetrofitHttpManger.getInstance().create(BannerService.class);
return bannerService.getBannerData("mz.app.ad.list", String.valueOf(adFilterType.getValue()));
}
}
below is retrofit instance in it's private constructor
retrofit = new Retrofit.Builder()
.client(httpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
//TODO baseurl tempororily hard code for test purpose
.baseUrl("http://10.10.20.190:6020/router/")
.build();
this is the result I got:
the current request body that I am logging is the common parameters that I added from FromBody in interceptor, only except the parameters that I passed in from #Field annoation, and server side info tells the same thing.
I have solved this issue, thanks to #iagreen's comment.
the request body was replaced by FormBody.Builder().add().build() which passed into chain.request().newBuilder().post().build() in my interceptor.
then the question turns out to be how to append paramemters in RequestBody, and the solution can refers to Retrofit2: Modifying request body in OkHttp Interceptor

How can I get GWT RequestFactory to with in a Gadget?

How can I get GWT RequestFactory to with in a Gadget?
Getting GWT-RPC to work with Gadgets is explained here.
I'm looking for a analogous solution for RequestFactory.
I tried using the GadgetsRequestBuilder, so far I've managed to get the request to the server using:
requestFactory.initialize(eventBus, new DefaultRequestTransport() {
#Override
protected RequestBuilder createRequestBuilder() {
return new GadgetsRequestBuilder(RequestBuilder.POST,
getRequestUrl());
}
#Override
public String getRequestUrl() {
return "http://....com/gadgetRequest";
}
});
But I end up with the following error:
java.lang.StringIndexOutOfBoundsException: String index out of range: 0
at java.lang.String.charAt(String.java:694)
at com.google.gwt.autobean.server.impl.JsonSplittable.create(JsonSplittable.java:35)
at com.google.gwt.autobean.shared.impl.StringQuoter.split(StringQuoter.java:35)
at com.google.gwt.autobean.shared.AutoBeanCodex.decode(AutoBeanCodex.java:520)
at com.google.gwt.requestfactory.server.SimpleRequestProcessor.process(SimpleRequestProcessor.java:121)
The general approach for sending a RequestFactory payload should be the same as RPC. You can see the payload that's being received by the server by running it with the JVM flag -Dgwt.rpc.dumpPayload=true. My guess here is that the server is receiving a request with a zero-length payload. What happens if you set up a simple test involving a GadgetsRequestBuilder sending a POST request to your server? Do you still get the same zero-length payload behavior?

Resources