I'm using a simple example from Amazon documentation for ItemSearch and I get a strange error:
"The remote server returned an unexpected response: (400) Bad Request."
This is the code:
public static void Main()
{
//Remember to create an instance of the amazon service, including you Access ID.
AWSECommerceServicePortTypeClient service = new AWSECommerceServicePortTypeClient(new BasicHttpBinding(),
new EndpointAddress(
"http://webservices.amazon.com/onca/soap?Service=AWSECommerceService"));
AWSECommerceServicePortTypeClient client = new AWSECommerceServicePortTypeClient(
new BasicHttpBinding(),
new EndpointAddress("http://webservices.amazon.com/onca/soap?Service=AWSECommerceService"));
// prepare an ItemSearch request
ItemSearchRequest request = new ItemSearchRequest();
request.SearchIndex = "Books";
request.Title = "Harry+Potter";
request.ResponseGroup = new string[] { "Small" };
ItemSearch itemSearch = new ItemSearch();
itemSearch.Request = new ItemSearchRequest[] { request };
itemSearch.AWSAccessKeyId = accessKeyId;
// issue the ItemSearch request
try
{
ItemSearchResponse response = client.ItemSearch(itemSearch);
// write out the results
foreach (var item in response.Items[0].Item)
{
Console.WriteLine(item.ItemAttributes.Title);
}
}
catch(Exception e)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(e.Message);
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Press any key to quit...");
Clipboard.SetText(e.Message);
}
Console.ReadKey();
What is wrong?
You receive this message because your request is not signed. Starting from August 2009 all requests must be signed.
Here you can see at the example about how to sign amazon requests:
http://developer.amazonwebservices.com/connect/entry.jspa?externalID=2480&categoryID=14
Related
I have MySQL database hosted in AWS and I am using IAM token to connect with it. I am using the following library and code to connect to this database and get tokens.
var token = Amazon.RDS.Util.RDSAuthTokenGenerator.GenerateAuthToken(awsOptions.Credentials, RegionEndpoint.USEast1, creds.Host, (int)creds.Port, creds.UserName);
The token I am getting back has an expiration of 15 minutes but it looks like a sliding expiration, so we keep using a database connection and it keeps adding 15 minutes from the last used token.
Initially, I was calling the library method to get tokens for each database request and it was very slow. It also started giving an error.
I implemented cache for 5 minutes and sliding token expiration is 15 minutes to be safe. It works for 3-4 hours and then looks like the token gets expired even if I continue to use it every 5 seconds. In this case, my application goes down for a maximum of 5 minutes every 3-4 hours.
Sample code for caching
public IDbConnection GetDbConnection()
{
var connectionString = GetConnectionString("connectionName");
var connection = new MySqlConnection(connectionString);
try
{
connection.Open();
}
catch (MySqlException ex)
{
// if exception remove cache and get new token
if (ex.Number == 1045)
{
connectionString = GetConnectionString("connectionName", true);
return new MySqlConnection(connectionString);
}
throw;
}
connection.Close();
return connection;
}
public string GetConnectionString(string connectionName, bool refreshCache = false)
{
if (refreshCache == true)
{
var connectionString = GetConnectionStringInternal(connectionName);
if (this.cache.Get("cacheName") != null)
{
this.cache.Remove("cacheName");
}
return connectionString;
}
var cacheEntry = this.cache.GetOrCreate("cacheName",
entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
return GetConnectionStringInternal(connectionName);
});
return cacheEntry;
}
private string GetConnectionStringInternal(string connectionName)
{
var token = Amazon.RDS.Util.RDSAuthTokenGenerator.GenerateAuthToken(awsOptions.Credentials, RegionEndpoint.USEast1, creds.Host, (int)creds.Port, creds.UserName);
return $"{connectionString};Uid={creds.UserName};Server={creds.Host}; Port={creds.Port};Password={token};";
}
What is a way to cache AWS RDS token?
Thank you for your help!
I try to retrieve a record from Dynamics 365 Sales. I created an app registration in Azure and I can get tokens based on this app.
Also, I can call the HTTP client. But I couldn't figure out how to read the result of the HTTP call.
Microsoft published only WhoAmIRequest sample, but I couldn't find a sample of other entities.
Here is my sample code. I try to read body object.
try
{
string serviceUrl = "https://****.crm4.dynamics.com/";
string clientId = "******";
string clientSecret = "*******";
string tenantId = "*******";
A***.Library.Utility.MSCRM mscrm = new Library.Utility.MSCRM(serviceUrl, clientId, clientSecret, tenantId);
var token = await mscrm.GetTokenAsync();
Console.WriteLine(token);
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(serviceUrl);
client.Timeout = new TimeSpan(0, 2, 0); //2 minutes
client.DefaultRequestHeaders.Add("OData-MaxVersion", "4.0");
client.DefaultRequestHeaders.Add("OData-Version", "4.0");
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "/api/data/v9.0/accounts");
// Set the access token
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
HttpResponseMessage response = client.SendAsync(request).Result;
if (response.IsSuccessStatusCode)
{
// Get the response content and parse it.
var responseStr = response.Content.ReadAsStringAsync();
JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
}
}
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
Here is the result of body object.
You can use either of these syntax to read values. Read more
JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
// Can use either indexer or GetValue method (or a mix of two)
body.GetValue("obs_detailerconfigid");
body["obs_detailerconfigid"];
I am trying to update my appsync client to authenticate with IAM credentials. In case of API_KEY I set the API_KEY_HEADER like so: request.addHeader(API_KEY_HEADER, this.apiKey); Is there a similar way to authenticate in a Java client with IAM credentials? Is there a header I can pass in to pass in the secret and access keys like here: https://docs.amplify.aws/lib/graphqlapi/authz/q/platform/js#iam? Or should I just be using a cognito user pool as a way to authenticate the request?
According to AWS Documentation we need to use sign requests using the process documented here: https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html and steps listed here: https://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html.
I also found an implementation here: https://medium.com/#tridibbolar/aws-lambda-as-an-appsync-client-fbb0c1ce927d. Using the code above:
private void signRequest(final Request<AmazonWebServiceRequest> request) {
final AWS4Signer signer = new AWS4Signer();
signer.setRegionName(this.region);
signer.setServiceName("appsync");
signer.sign(request, this.appsyncCredentials);
}
private Request<AmazonWebServiceRequest> getRequest(final String data) {
final Request<AmazonWebServiceRequest> request =
new DefaultRequest<AmazonWebServiceRequest>("appsync");
request.setHttpMethod(HttpMethodName.POST);
request.setEndpoint(URI.create(this.appSyncEndpoint));
final byte[] byteArray = data.getBytes(Charset.forName("UTF-8"));
request.setContent(new ByteArrayInputStream(byteArray));
request.addHeader(AUTH_TYPE_HEADER, AWS_IAM_AUTH_TYPE);
request.addHeader(HttpHeaders.CONTENT_TYPE, APPLICATION_GRAPHQL);
request.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(byteArray.length));
signRequest(request);
return request;
}
private HttpResponseHandler<String> getResponseHandler() {
final HttpResponseHandler<String> responseHandler = new HttpResponseHandler<String>() {
#Override
public String handle(com.amazonaws.http.HttpResponse httpResponse) throws Exception {
final String result = IOUtils.toString(httpResponse.getContent());
if(httpResponse.getStatusCode() != HttpStatus.SC_OK) {
final String errorText = String.format(
"Error posting request. Response status code was %s and text was %s. ",
httpResponse.getStatusCode(),
httpResponse.getStatusText());
throw new RuntimeException(errorText);
} else {
final ObjectMapper objectMapper = new ObjectMapper();
//custom class to parse appsync response.
final AppsyncResponse response = objectMapper.readValue(result, AppsyncResponse.class);
if(CollectionUtils.isNotEmpty(response.getErrors())){
final String errorMessages = response
.getErrors()
.stream()
.map(Error::getMessage)
.collect(Collectors.joining("\n"));
final String errorText = String.format(
"Error posting appsync request. Errors were %s. ",
errorMessages);
throw new RuntimeException(errorText);
}
}
return result;
}
#Override
public boolean needsConnectionLeftOpen() {
return false;
}
};
return responseHandler;
}
private Response<String> makeGraphQlRequest(final Request<AmazonWebServiceRequest> request) {
return this.httpClient.requestExecutionBuilder()
.executionContext(new ExecutionContext())
.request(request)
.execute(getResponseHandler());
}
I am trying to retrieve images from my bucket to send to my mobile apps, I currently have the devices accessing AWS directly, however I am adding a layer of security and having my apps (IOS and Android) now make requests to my server which will then respond with DynamoDB and S3 data.
I am trying to follow the documentation and code samples provided by AWS for .Net and they worked seamlessly for DynamoDB, I am running into problems with S3.
S3 .NET Documentation
My problem is that if I provide no credentials, I get the error:
Failed to retrieve credentials from EC2 Instance Metadata Service
This is expected as I have IAM roles set up and only want my apps and this server (in the future, only this server) to have access to the buckets.
But when I provide the credentials, the same way I provided credentials for DynamoDB, my server waits forever and doesn't receive any responses from AWS.
Here is my C#:
<%# WebHandler Language="C#" Class="CheckaraRequestHandler" %>
using System;
using System.Web;
using System.Collections.Generic;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.Model;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon;
using Amazon.Runtime;
using Amazon.S3;
using Amazon.S3.Model;
using System.IO;
using System.Threading.Tasks;
public class CheckaraRequestHandler : IHttpHandler
{
private const string bucketName = "MY_BUCKET_NAME";
private static readonly RegionEndpoint bucketRegion = RegionEndpoint.USEast1;
public static IAmazonS3 client = new AmazonS3Client("MY_ACCESS_KEY", "MY_SECRET_KEY", RegionEndpoint.USEast1);
public void ProcessRequest(HttpContext context)
{
if (context.Request.HttpMethod.ToString() == "GET")
{
string userID = context.Request.QueryString["User"];
string Action = context.Request.QueryString["Action"];
if (userID == null)
{
context.Response.ContentType = "text/plain";
context.Response.Write("TRY AGAIN!");
return;
}
if (Action == "GetPhoto")
{
ReadObjectDataAsync(userID).Wait();
}
var client = new AmazonDynamoDBClient("MY_ACCESS_KEY", "MY_SECRET_KEY", RegionEndpoint.USEast1);
Console.WriteLine("Getting list of tables");
var table = Table.LoadTable(client, "TABLE_NAME");
var item = table.GetItem(userID);
if (item != null)
{
context.Response.ContentType = "application/json";
context.Response.Write(item.ToJson());
}
else
{
context.Response.ContentType = "text/plain";
context.Response.Write("0");
}
}
}
public bool IsReusable
{
get
{
return false;
}
}
static async Task ReadObjectDataAsync(string userID)
{
string responseBody = "";
try
{
string formattedKey = userID + "/" + userID + "_PROFILEPHOTO.jpeg";
//string formattedKey = userID + "_PROFILEPHOTO.jpeg";
//formattedKey = formattedKey.Replace(":", "%3A");
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucketName,
Key = formattedKey
};
using (GetObjectResponse response = await client.GetObjectAsync(request))
using (Stream responseStream = response.ResponseStream)
using (StreamReader reader = new StreamReader(responseStream))
{
string title = response.Metadata["x-amz-meta-title"]; // Assume you have "title" as medata added to the object.
string contentType = response.Headers["Content-Type"];
Console.WriteLine("Object metadata, Title: {0}", title);
Console.WriteLine("Content type: {0}", contentType);
responseBody = reader.ReadToEnd(); // Now you process the response body.
}
}
catch (AmazonS3Exception e)
{
Console.WriteLine("Error encountered ***. Message:'{0}' when writing an object", e.Message);
}
catch (Exception e)
{
Console.WriteLine("Unknown encountered on server. Message:'{0}' when writing an object", e.Message);
}
}
}
When I debug, this line waits forever:
using (GetObjectResponse response = await client.GetObjectAsync(request))
This is the same line that throws the credentials error when I don't provide them. Is there something that I am missing here?
Any help would be greatly appreciated.
I suspect that the AWS .NET SDK has some isses with it specifically with the async call to S3.
The async call to dynamoDB works perfect, but the S3 one hangs forever.
What fixed my problem was simply removing the async functionality (even tho in the AWS docs, the async call is supposed to be used)
Before:
using (GetObjectResponse response = await client.GetObjectAsync(request))
After:
using (GetObjectResponse response = myClient.GetObject(request))
Hopefully this helps anyone else encountering this issue.
I followed Google's Quick-Start documentation for the Speech API to enable billing and API for an account. This account has authorized a service account to create Compute instances on its behalf. After creating an instance on the child account, hosting a binary to use the Speech API, I am unable to successfully use the example C# code provided by Google in the C# speech example:
try
{
var speech = SpeechClient.Create();
var response = speech.Recognize(new RecognitionConfig()
{
Encoding = RecognitionConfig.Types.AudioEncoding.Linear16,
LanguageCode = "en"
}, RecognitionAudio.FromFile(audioFiles[0]));
foreach (var result in response.Results)
{
foreach (var alternative in result.Alternatives)
{
Debug.WriteLine(alternative.Transcript);
}
}
} catch (Exception ex)
// ...
}
Requests fail on the SpeechClient.Create() line with the following error:
--------------------------- Grpc.Core.RpcException: Status(StatusCode=Unauthenticated, Detail="Exception occured in
metadata credentials plugin.")
at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task)
at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task)
at Grpc.Core.Internal.AsyncCall`2.UnaryCall(TRequest msg)
at
Grpc.Core.Calls.BlockingUnaryCall[TRequest,TResponse](CallInvocationDetails`2
call, TRequest req)
at
Grpc.Core.DefaultCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2
method, String host, CallOptions options, TRequest request)
at
Grpc.Core.Internal.InterceptingCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2
method, String host, CallOptions options, TRequest request)
at
Google.Cloud.Speech.V1.Speech.SpeechClient.Recognize(RecognizeRequest
request, CallOptions options)
at
Google.Api.Gax.Grpc.ApiCall.<>c__DisplayClass0_0`2.b__1(TRequest
req, CallSettings cs)
at
Google.Api.Gax.Grpc.ApiCallRetryExtensions.<>c__DisplayClass1_0`2.b__0(TRequest
request, CallSettings callSettings)
at Google.Api.Gax.Grpc.ApiCall`2.Sync(TRequest request,
CallSettings perCallCallSettings)
at
Google.Cloud.Speech.V1.SpeechClientImpl.Recognize(RecognizeRequest
request, CallSettings callSettings)
at Google.Cloud.Speech.V1.SpeechClient.Recognize(RecognitionConfig
config, RecognitionAudio audio, CallSettings callSettings)
at Rc2Solver.frmMain.RecognizeWordsGoogleSpeechApi() in
C:\Users\jorda\Google
Drive\VSProjects\Rc2Solver\Rc2Solver\frmMain.cs:line 1770
--------------------------- OK
I have verified that the Speech API is activated. Here is the scope that the service account uses when creating the Compute instances:
credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(me)
{
Scopes = new[] { ComputeService.Scope.Compute, ComputeService.Scope.CloudPlatform }
}.FromPrivateKey(yk)
);
I have found no information or code online about specifically authorizing or authenticating the Speech API for service account actors. Any help is appreciated.
It turns out the issue was that the Cloud Compute instances needed to be created with a ServiceAccount parameter specified. Otherwise the Cloud instances were not part of a ServiceAccount default credential, which is referenced by the SpeechClient.Create() call. Here is the proper way to create an instance attached to a service account, and it will use the SA tied to the project ID:
service = new ComputeService(new BaseClientService.Initializer() {
HttpClientInitializer = credential,
ApplicationName = "YourAppName"
});
string MyProjectId = "example-project-27172";
var project = await service.Projects.Get(MyProjectId).ExecuteAsync();
ServiceAccount servAcct = new ServiceAccount() {
Email = project.DefaultServiceAccount,
Scopes = new [] {
"https://www.googleapis.com/auth/cloud-platform"
}
};
Instance instance = new Instance() {
MachineType = service.BaseUri + MyProjectId + "/zones/" + targetZone + "/machineTypes/" + "g1-small",
Name = name,
Description = name,
Disks = attachedDisks,
NetworkInterfaces = networkInterfaces,
ServiceAccounts = new [] {
servAcct
},
Metadata = md
};
batchRequest.Queue < Instance > (service.Instances.Insert(instance, MyProjectId, targetZone),
(content, error, i, message) => {
if (error != null) {
AddEventMsg("Error creating instance " + name + ": " + error.ToString());
} else {
AddEventMsg("Instance " + name + " created");
}
});