Skip to content

Commit

Permalink
http client override
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinPankraz committed Jul 30, 2021
1 parent 5f182ac commit ee0cc68
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 30 deletions.
31 changes: 30 additions & 1 deletion Controllers/HomeController.cs
Expand Up @@ -13,6 +13,7 @@
using Microsoft.Identity.Web;
using System.Net;
using System.Text.Json;
using System.Text;

namespace AzureSAPODataReader.Controllers
{
Expand Down Expand Up @@ -42,6 +43,19 @@ public async Task<IActionResult> Index()
.For<ProductViewModel>("Products")
.Top(10)
.FindEntriesAsync();
/*HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Get, baseAPIUrl + "/$metadata");
msg.Headers.Add("x-csrf-token", "Fetch");
HttpClient client = await getHttpClientForUsername(scopes);
HttpResponseMessage responseMessage = await client.SendAsync(msg);
if (responseMessage.IsSuccessStatusCode)
{
HttpRequestMessage msg2 = new HttpRequestMessage(HttpMethod.Patch, baseAPIUrl + "/Products('AR-FB-1000')");
msg2.Headers.Add("x-csrf-token", responseMessage.Headers.GetValues("x-csrf-token").First());
HttpContent content = new StringContent("{\"Price\":\"3.5\"}", Encoding.UTF8, "application/json");
msg2.Content = content;
HttpResponseMessage responseMessage2 = await client.SendAsync(msg2);
}*/

return View(products);
}
Expand All @@ -51,7 +65,22 @@ private async Task<ODataClient> getODataClientForUsername(string[] scopes)
string accessToken = await TokenAcquisition.GetAccessTokenForUserAsync(scopes);
SAPTokenCacheContent content = await TokenCache.GetSAPTokenCacheContentAsync(accessToken, baseAPIUrl);

var client = new ODataClient(content.getODataClientSettings());//SetODataToken(Configuration.GetValue<string>("SAPODataAPI:ApiBaseAddress"), content.accessToken, true));
var client = new ODataClient(content.getODataClientSettingsAsync());//SetODataToken(Configuration.GetValue<string>("SAPODataAPI:ApiBaseAddress"), content.accessToken, true));
return client;
}
//plain http implementation for initial test
private async Task<HttpClient> getHttpClientForUsername(string[] scopes)
{
string accessToken = await TokenAcquisition.GetAccessTokenForUserAsync(scopes);
SAPTokenCacheContent content = await TokenCache.GetSAPTokenCacheContentAsync(accessToken, baseAPIUrl);

var handler = new HttpClientHandler(){
ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true
};
var client = new HttpClient(handler);
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {content.accessToken}");
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", _Configuration.GetValue<string>("SAPODataAPI:Ocp-Apim-Subscription-Key"));
client.BaseAddress = new Uri(baseAPIUrl);
return client;
}

Expand Down
10 changes: 8 additions & 2 deletions MemorySAPTokenCache.cs
Expand Up @@ -49,7 +49,10 @@ public async Task<SAPTokenCacheContent> GetSAPTokenCacheContentAsync(string AADT

private async Task<string> getSAMLFromBearerToken(string accessToken)
{
var client = new HttpClient();
var handler = new HttpClientHandler(){
ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true
};
var client = new HttpClient(handler);
var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"));
nvc.Add(new KeyValuePair<string, string>("assertion", accessToken));
Expand Down Expand Up @@ -97,7 +100,10 @@ private async Task<string> getSAMLFromBearerToken(string accessToken)

private async Task<string> getSAML2BearerToken(string samlToken)
{
var client = new HttpClient();
var handler = new HttpClientHandler(){
ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true
};
var client = new HttpClient(handler);
var nvc = new List<KeyValuePair<string, string>>();
nvc.Add(new KeyValuePair<string, string>("grant_type", "urn:ietf:params:oauth:grant-type:saml2-bearer"));
nvc.Add(new KeyValuePair<string, string>("assertion", samlToken));
Expand Down
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -9,7 +9,8 @@ We used the `/sap/opu/odata/sap/epm_ref_apps_prod_man_srv` OData v2 service for
3. Configure the converter [tools ](https://github.com/oasis-tcs/odata-openapi/tree/main/tools) including the build environment. We applied `npm install -g windows-build-tools`. Alternatively look for [VS2017 build tools](https://my.visualstudio.com/Downloads?q=Visual+Studio+2017).
4. Run `odata-openapi -p --basePath '/sap/opu/odata/sap/epm_ref_apps_prod_man_srv' --scheme https --host <your IP>:<your SSL port> .\epm_ref_apps_prod_man_srv.xml` to create the OpenAPI spec from the downloaded metadata.xml adding the base path of the service and apply option for json pretty print for visual verification.
5. Import OpenAPI spec into Azure API Management. Have a look [here](https://docs.microsoft.com/en-us/azure/api-management/import-api-from-oas) for more details.
6. Add Operation /$metadata when not generated by your files.
6. Add GET Operation /$metadata when not generated by your files.
6. Add HEAD Operation / for efficient client token caching.
7. Test the $metadata api call to verify connectivity from Azure APIM to your SAP backend

## Authentication considerations
Expand Down
53 changes: 27 additions & 26 deletions SAPTokenCacheContent.cs
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Simple.OData.Client;

Expand All @@ -11,7 +12,6 @@ namespace AzureSAPODataReader
public class SAPTokenCacheContent
{
private readonly IConfiguration _Configuration;
public IList<string> cookies{get;set;} = new List<string>();

public SAPTokenCacheContent(IConfiguration configuration, string userIdentifier, string url)
{
Expand All @@ -20,46 +20,47 @@ public SAPTokenCacheContent(IConfiguration configuration, string userIdentifier,
this.url = url;
}
public string accessToken { get; set; }
public string csrfToken { get; set; } = "";
public string userIdentifier { get; }
public string url { get; }

public ODataClientSettings getODataClientSettings()
public ODataClientSettings getODataClientSettingsAsync()
{
var oDataClientSettings = new ODataClientSettings(new Uri(url));
var myClientOverride = new HttpClient(){BaseAddress = new Uri(url)};
myClientOverride.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
//myClientOverride.DefaultRequestHeaders.Add("Authorization", Configuration.GetValue<string>("SAPODataAPI:BasicAuth"));
myClientOverride.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", _Configuration.GetValue<string>("SAPODataAPI:Ocp-Apim-Subscription-Key"));
myClientOverride.DefaultRequestHeaders.Add("Ocp-Apim-Trace", _Configuration.GetValue<string>("SAPODataAPI:Ocp-Apim-Trace"));

var oDataClientSettings = new ODataClientSettings(myClientOverride);
//ignore cookie container so we can set SAP cookies ourselves
//https://github.com/simple-odata-client/Simple.OData.Client/issues/37
oDataClientSettings.OnApplyClientHandler = handler => handler.UseCookies = false;
//oDataClientSettings.OnApplyClientHandler = handler => handler.UseCookies = false;
/*oDataClientSettings.OnApplyClientHandler = handler =>{
//Remove this line for production
handler.ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true;
};*/
oDataClientSettings.OnTrace = (x, y) => Console.WriteLine("TRACE---->" + string.Format(x, y));
oDataClientSettings.BeforeRequest += delegate (HttpRequestMessage message)
{
//message.Headers.Add("Authorization", Configuration.GetValue<string>("SAPODataAPI:BasicAuth"));
message.Headers.Add("Authorization", "Bearer " + accessToken);
if(message.Method.Equals(HttpMethod.Get)){
message.Headers.Add("X-CSRF-Token", "Fetch");
}else{
message.Headers.Add("X-CSRF-Token", csrfToken);
foreach (string cookie in cookies){
message.Headers.Add("Cookie", cookie);
}
}
message.Headers.Add("Ocp-Apim-Subscription-Key", _Configuration.GetValue<string>("SAPODataAPI:Ocp-Apim-Subscription-Key"));
message.Headers.Add("Ocp-Apim-Trace", _Configuration.GetValue<string>("SAPODataAPI:Ocp-Apim-Trace"));
};

oDataClientSettings.AfterResponse += delegate (HttpResponseMessage message)
{
//if (message.StatusCode == HttpStatusCode.OK){
if(message.Headers.Contains("X-CSRF-Token"))
//preflight x-crf-token fetch, because odata client closes http connection each time it makes a request
if(message.Method != HttpMethod.Get)
{
HttpClient csrfClient = oDataClientSettings.HttpClient;
HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Head, url + "/");
msg.Headers.Add("x-csrf-token", "Fetch");

HttpResponseMessage responseMessage = csrfClient.SendAsync(msg).Result;

if (responseMessage.IsSuccessStatusCode)
{
csrfToken = message.Headers.GetValues("X-CSRF-Token").FirstOrDefault();
cookies = message.Headers.GetValues("Set-Cookie").ToList();
message.Headers.Add("x-csrf-token", responseMessage.Headers.GetValues("x-csrf-token").FirstOrDefault());
}
//}
}
};

return oDataClientSettings;
}

}

}

0 comments on commit ee0cc68

Please sign in to comment.