S3 - policy and signed policy - c#

I dont understand what is wrong((
And I have a few questions:
1) If I must put any policy to my S3 bucket. If yes - can you help me understand how to create a policy
2) I have this policy:
{
"expiration": "2016-11-07T20:18:13.326Z",
"conditions": [
{
"acl": "public"
},
{
"bucket": "bucket_name"
},
{
"Content-Type": "text/plain"
},
{
"success_action_status": "200"
},
{
"key": "c94d36ee-19eb-49e8-ac30-760b29982391.txt"
},
{
"x-amz-meta-qqfilename": "111.txt"
}
]
}
So in my C# code I do :
private string GeneratePolicy()
{
byte[] bytesToEncode = File.ReadAllBytes("s3policy.txt");
string encodedText = Convert.ToBase64String(bytesToEncode);
return encodedText;
}
static string GetSig(string policyStr)
{
string b64Policy = Convert.ToBase64String(Encoding.ASCII.GetBytes(policyStr));
byte[] b64Key = Encoding.ASCII.GetBytes("HERE-IS-MY-SECRET-KEY");
HMACSHA1 hmacSha1 = new HMACSHA1(b64Key);
string singPolicy = Convert.ToBase64String(hmacSha1.ComputeHash(Encoding.ASCII.GetBytes(b64Policy)));
return singPolicy;
}
But is doesn't work (
Any idea why?
Thank you

Related

Using ES256 algorithm with jwt-dotnet for Apple AppStore

I'm trying to generate a jwt token to connect to AppStore API. I'm using the jwt-dotnet library to do this.
Apple requires ES256 to be used and the jwt-dotnet is asking for a public key to do the job. I only downloaded a private key from AppStore. How do I handle this?
Here's my code:
public static string GenerateAppStoreJwtToken()
{
var header = new Dictionary<string, object>()
{
{ "kid", "MY_VALUE" },
{ "typ", "JWT" }
};
var scope = new string[1] { "GET /v1/apps?filter[platform]=IOS" };
var payload = new Dictionary<string, object>
{
{ "iss", "MY_VALUE" },
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
{ "exp", DateTimeOffset.UtcNow.AddMinutes(20).ToUnixTimeSeconds() },
{ "aud", "appstoreconnect-v1" },
{ "scope", scope }
};
IJwtAlgorithm algorithm = new ES256Algorithm(???); // What am I going to use here?
IJsonSerializer serializer = new JsonNetSerializer();
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);
var token = encoder.Encode(header, payload, privateKey);
return token;
}
For anyone, like me, who usew JWT-dotnet elsewhere so doesn't want to use a different JWT package, this worked:
Converted the apple private key by removing the header and footer ("-----BEGIN PRIVATE KEY-----" etc) and removing the end of line characters to make a single string for easier storage.
Convert from Base64 and store in a ReadOnlySpan
ReadOnlySpan<byte> keyAsSpan = Convert.FromBase64String(key);
var prvKey = ECDsa.Create();
prvKey.ImportPkcs8PrivateKey(keyAsSpan,out var read);
Create the algorithm. A blank ECDsa instance is needed to prevent an NullException but it is not needed just for signing the token, only verifying which isn't necessary.
IJwtAlgorithm algorithm = new ES256Algorithm(ECDsa.Create(), prvKey)
I was able to receive a reply token from apple using this method.
Here's the final solution that worked for me. I ended up switching to jose-jwt but I'm pretty sure you can handle the same thing with jwt-dotnet. I just found working with jose-jwt a bit easier. Here's the link to jose-jwt: https://github.com/dvsekhvalnov/jose-jwt
And here's the final code. Please note that I did indeed use the private key I find in the p8 file and didn't have to convert anything. So the privateKey parameter I'm passing to the GenerateAppStoreJwtToken() function comes directly from the p8 file.
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using Jose;
public static string GenerateAppStoreJwtToken(string privateKey)
{
var header = new Dictionary<string, object>()
{
{ "alg", "ES256" },
{ "kid", "MY_VALUE" },
{ "typ", "JWT" }
};
var scope = new string[1] { "GET /v1/apps?filter[platform]=IOS" };
var payload = new Dictionary<string, object>
{
{ "iss", "MY_VALUE" },
{ "iat", DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
{ "exp", DateTimeOffset.UtcNow.AddMinutes(15).ToUnixTimeSeconds() },
{ "aud", "appstoreconnect-v1" },
{ "scope", scope }
};
CngKey key = CngKey.Import(Convert.FromBase64String(privateKey), CngKeyBlobFormat.Pkcs8PrivateBlob);
string token = JWT.Encode(payload, key, JwsAlgorithm.ES256, header);
return token;
}

How to get proper http status codes from Amazon API gateway integrated with C# lambda (not proxy integration)?

I am using a C# lambda to integrate with API gateway. I want API gateway to return proper error codes like 400, 404, 500 etc.
API gateway module tf file
provider "aws" {
version = "<= 2.70.0"
region = "${var.aws_region}"
profile = "${var.aws_profile}"
}
terraform {
# The configuration for this backend will be filled in by Terragrunt
backend "s3" {}
}
data "terraform_remote_state" "api_state" {
backend = "s3"
config {
region = "${var.aws_region}"
profile = "${var.aws_profile}"
bucket = "${var.s3_remote_state_bucket_name}"
key = "${var.s3_remote_state_key_name_api}"
}
}
data "terraform_remote_state" "resource_state"{
backend = "s3"
config {
region = "${var.aws_region}"
profile = "${var.aws_profile}"
bucket = "${var.s3_remote_state_bucket_name}"
key = "${var.s3_remote_state_key_name_resource}"
}
}
data "terraform_remote_state" "lambda_alias"{
backend = "s3"
config {
region = "${var.aws_region}"
profile = "${var.aws_profile}"
bucket = "${var.s3_remote_state_bucket_name}"
key = "${var.s3_remote_state_key_name_lambda}"
}
}
resource "aws_api_gateway_method" "http-method" {
rest_api_id = "${data.terraform_remote_state.api_state.api_gateway_rest_api_id}"
resource_id = "${data.terraform_remote_state.resource_state.api_resource_id}"
http_method = "GET"
authorization = "CUSTOM"
authorizer_id = "${data.terraform_remote_state.api_state.Authorizers[var.Authorizer]}"
request_parameters = "${var.api_request_params_required}"
}
resource "aws_api_gateway_integration" "integration_GET" {
rest_api_id = "${data.terraform_remote_state.api_state.api_gateway_rest_api_id}"
resource_id = "${data.terraform_remote_state.resource_state.api_resource_id}"
http_method = "${aws_api_gateway_method.http-method.http_method}"
integration_http_method = "POST"
type = "AWS"
uri = "arn:aws:apigateway:${var.aws_region}:lambda:path/2015-03-31/functions/${data.terraform_remote_state.lambda_alias.alias_lambda_arn}/invocations"
passthrough_behavior = "WHEN_NO_TEMPLATES"
request_templates = {
"application/json" = "${file("api_gateway_body_mapping.template")}"
}
}
resource "aws_api_gateway_model" "error_response" {
rest_api_id = "${aws_api_gateway_rest_api.api_gateway_rest_api.id}"
name = "ErrorResponse"
description = "The error respone object for all endpoints"
content_type = "application/json"
schema = <<EOF
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type" : "object",
"properties": {
"body": {
"type": "string"
},
"statusCode" : {
"type": "number"
}
}
}
EOF
}
resource "aws_api_gateway_method_response" "method_response" {
depends_on = ["aws_api_gateway_method.http-method"]
http_method = "${aws_api_gateway_method.http-method.http_method}"
resource_id = "${data.terraform_remote_state.resource_state.api_resource_id}"
rest_api_id = "${data.terraform_remote_state.api_state.api_gateway_rest_api_id}"
status_code = "200"
}
resource "aws_api_gateway_method_response" "method_bad_request" {
depends_on = ["aws_api_gateway_method.http-method"]
http_method = "${aws_api_gateway_method.http-method.http_method}"
resource_id = "${data.terraform_remote_state.resource_state.api_resource_id}"
rest_api_id = "${data.terraform_remote_state.api_state.api_gateway_rest_api_id}"
status_code = "400"
response_models {
"application/json" = "${aws_api_gateway_model.error_response}"
}
}
resource "aws_api_gateway_method_response" "method_not_found" {
depends_on = ["aws_api_gateway_method.http-method"]
http_method = "${aws_api_gateway_method.http-method.http_method}"
resource_id = "${data.terraform_remote_state.resource_state.api_resource_id}"
rest_api_id = "${data.terraform_remote_state.api_state.api_gateway_rest_api_id}"
status_code = "404"
response_models {
"application/json" = "${aws_api_gateway_model.error_response}"
}
}
resource "aws_api_gateway_method_response" "method_error" {
depends_on = ["aws_api_gateway_method.http-method"]
http_method = "${aws_api_gateway_method.http-method.http_method}"
resource_id = "${data.terraform_remote_state.resource_state.api_resource_id}"
rest_api_id = "${data.terraform_remote_state.api_state.api_gateway_rest_api_id}"
status_code = "500"
response_models {
"application/json" = "${aws_api_gateway_model.error_response}"
}
}
resource "aws_api_gateway_integration_response" "get_integration_response_success" {
depends_on = ["aws_api_gateway_method_response.method_response", "aws_api_gateway_integration.integration_GET"]
http_method = "${aws_api_gateway_method.http-method.http_method}"
resource_id = "${data.terraform_remote_state.resource_state.api_resource_id}"
rest_api_id = "${data.terraform_remote_state.api_state.api_gateway_rest_api_id}"
status_code = "${aws_api_gateway_method_response.method_response.status_code}"
response_templates {
"application/json" = ""
}
}
resource "aws_api_gateway_integration_response" "get_integration_response_error" {
depends_on = ["aws_api_gateway_method_response.method_error", "aws_api_gateway_integration.integration_GET"]
http_method = "${aws_api_gateway_method.http-method.http_method}"
resource_id = "${data.terraform_remote_state.resource_state.api_resource_id}"
rest_api_id = "${data.terraform_remote_state.api_state.api_gateway_rest_api_id}"
status_code = "${aws_api_gateway_method_response.method_error.status_code}"
selection_pattern = ".*statusCode['\"]\\s*:\\s*['\"]?500.*"
response_templates {
"application/json"="${file("api_gateway_exception_mapping.template")}"
}
}
resource "aws_api_gateway_integration_response" "get_integration_response_bad_request" {
depends_on = ["aws_api_gateway_method_response.method_bad_request", "aws_api_gateway_integration.integration_GET"]
http_method = "${aws_api_gateway_method.http-method.http_method}"
resource_id = "${data.terraform_remote_state.resource_state.api_resource_id}"
rest_api_id = "${data.terraform_remote_state.api_state.api_gateway_rest_api_id}"
status_code = "${aws_api_gateway_method_response.method_bad_request.status_code}"
selection_pattern = ".*statusCode['\"]\\s*:\\s*['\"]?400.*"
response_templates {
"application/json"="${file("api_gateway_exception_mapping.template")}"
}
}
resource "aws_api_gateway_integration_response" "get_integration_response_not_found" {
depends_on = ["aws_api_gateway_method_response.method_not_found", "aws_api_gateway_integration.integration_GET"]
http_method = "${aws_api_gateway_method.http-method.http_method}"
resource_id = "${data.terraform_remote_state.resource_state.api_resource_id}"
rest_api_id = "${data.terraform_remote_state.api_state.api_gateway_rest_api_id}"
status_code = "${aws_api_gateway_method_response.method_not_found.status_code}"
selection_pattern = ".*statusCode['\"]\\s*:\\s*['\"]?404.*"
response_templates {
"application/json"="{}"
}
}
api_gateway_exception_mapping.template:
#set($inputRoot = $util.parseJson($input.path('$.errorMessage')))
{
"Error":"$inputRoot.body"
}
The integration response mapping is as in below snapshot
We have create APIs with lambda integration in python where I threw a custom APIException as below and it worked.
class ApiException(Exception):
"""Our custom APIException class which derives from the built-in Exception class"""
def __init__(self, status_code, message: str, **kwargs):
self.status_code = status_code
self.message = message
kwargs["statusCode"] = status_code
kwargs["body"] = message
super().__init__(json.dumps(kwargs))
Inside the lambda handler:
from .utils import ApiException
def lambda_handler(event, context):
try:
"""
CODE FOR LAMBDA HANDLER
"""
except Exception:
ex = ApiException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
message='Internal Error')
print("exception string: %s", ex)
raise ApiException(
status_code=500,
message='Internal server error')
When I logged the exception, I got the following output
{
"statusCode": 500,
"body": "Internal Server Error"
}
I also referred this stackoverflow answer on how to get proper error codes in API Gateway response. I modified that a little bit to throw an exception instead of just returning a json with statusCode, response body and headers as I am not using AWS_PROXY integration type
APIException.cs
public class APIException : Exception
{
public int statusCode;
public string body;
public APIException() : base() {}
public APIException(int statusCode, string message): base(message) {
this.statusCode = statusCode;
JObject json = JObject.Parse(message);
this.body = json["body"].ToString();
}
}
Lambda handler:
namespace LambdaFunction
{
public class Function
{
public async Task<JObject> FunctionHandler(JObject events, ILambdaContext context)
{
try
{
ValidateQueryParams(events, context);
JObject response = JObject.Parse(#"{
'mesage': 'success',
}");
return response;
}
catch(HttpListenerException ex)
{
string err = (new JObject(
new JProperty("statusCode", ex.ErrorCode),
new JProperty("body", ex.Message)
)).ToString();
return new APIException(ex.ErrorCode, err);
}
catch(Exception ex)
{
int err_code = (int)HttpStatusCode.InternalServerError
string err = (new JObject(
new JProperty("statusCode", err_code),
new JProperty("body", "Internal Server Error")
)).ToString();
var err_ex = new APIException(err_code, err);
context.Logger.LogLine("Unhandled exception occurred: " + ex.ToString());
return err_ex;
}
}
}
}
I've logged the exception before throwing it to see what we are getting and this is what I got
{
"statusCode": 500,
"body": "Internal Server Error",
"StackTrace": null,
"Message": "{\n \"statusCode\": 500,\n \"body\": \"Internal Server Error\"\n}",
"Data": {},
"InnerException": null,
"HelpLink": null,
"Source": null,
"HResult": -2146233088
}
But with the above code, I'm still just getting response code as 200 with below response body
{
"errorType": "APIException",
"errorMessage": "{\n \"statusCode\": 500,\n \"body\": \"Internal Server Error\"\n}",
"stackTrace": [
"..."
]
}
I am not sure where I am going wrong. Any help will be appreciated. Thanks.
I've figured out the issue. C#'s .ToString() function which converts JObject into a string is formatting the string and adding new line characters \n by default. But the regex used in API gateway to identify error codes does not consider new line characters. The fix was pretty simple. We need to tell the .ToString() function that it shouldn't do any formatting. So, instead of
new JObject(
new JProperty("statusCode", err_code),
new JProperty("body", "Internal Server Error")
)).ToString();
You need to do
new JObject(
new JProperty("statusCode", err_code),
new JProperty("body", "Internal Server Error")
)).ToString(Formatting.None);
And it worked.

Add endpoints manually with Swashbuckle.Swagger

I'm using a CMS. So when I go to i.e. '/painter' its routed to the 'JobController'. /plumber is also routed to 'JobController'.
Besides that it's MVC and not WebAPI, so swagger doesn't discover it, which is understandable and fine.
But I've a usecase, where if I access /pianter?json=1 it returnes json instead of HTML.
So as an API UI we would like to expose this 'fake' endpoint, just so the designers can see the output model.
So can I add an entirely fake endpoint - just to have a single userinterface between the designers and developers in swagger UI?
Besides having a visual UI, we want to generate some TypeScript based on the openapi standard.
Here is an option to create Fake endpoint with swashbuckle using IDocumentFilter:
private class DocumentFilterAddFakes : IDocumentFilter
{
private PathItem FakePathItem(int i)
{
var x = new PathItem();
x.get = new Operation()
{
tags = new[] { "Fake" },
operationId = "Fake_Get" + i.ToString(),
consumes = null,
produces = new[] { "application/json", "text/json", "application/xml", "text/xml" },
parameters = new List<Parameter>()
{
new Parameter()
{
name = "id",
#in = "path",
required = true,
type = "integer",
format = "int32"
}
},
};
x.get.responses = new Dictionary<string, Response>();
x.get.responses.Add("200", new Response() { description = "OK", schema = new Schema() { type = "string" } });
return x;
}
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
for (int i = 0; i < 10; i++)
{
swaggerDoc.paths.Add("/Fake/" + i + "/{id}", FakePathItem(i));
}
}
}
Here is the result of something like that:
http://swashbuckletest.azurewebsites.net/swagger/ui/index#/Fake
The full code behind that is on github:
https://github.com/heldersepu/SwashbuckleTest/blob/master/Swagger_Test/App_Start/SwaggerConfig.cs#L316

Fire TriggeredSends from ExactTarget's API using HttpClient REST

I've read along the way that Salesforce (I'm extremely new to this 3rd party platform) has a FUEL SDK which one can use instead of the version (using HttpClient -- REST instead of SOAP).
Please correct me if using FUEL SDK is the only way to go about requesting Salesforce's endpoints. Currently I am attempting to hit ExactTargets's API endpoints using HttpClient. These are the tutorials I've been basing my code off of:
https://developer.salesforce.com/docs/atlas.en-us.mc-apis.meta/mc-apis/messageDefinitionSends.htm
https://developer.salesforce.com/docs/atlas.en-us.mc-getting-started.meta/mc-getting-started/get-access-token.htm
Wanted Result:
To be able to request a Triggered Send email based off a template inside of ExactTarget.
Problem:
The Salesforce endpoint continuously returns a 404. I am able to receive the authorization token successfully. The GetAccessToken method is omitted for brevity
https://www.exacttargetapis.com/messaging/v1/messageDefinitionSends/key:MyExternalKey/send
I do not understand why the 2nd POST request to //www.exacttargetapis.com/..... returns a 404 but the authorization works. This leads me to believe that I do not have to use the FUEL SDK to accomplish triggering a welcome email.
Code:
private const string requestTokenUrl = "https://auth.exacttargetapis.com/v1/requestToken";
private const string messagingSendUrl = "https://www.exacttargetapis.com/messaging/v1/messageDefinitionSends";
private string exactTargetClientId = ConfigurationManager.AppSettings["ExactTargetClientId"];
private string exactTargetClientSecret = ConfigurationManager.AppSettings["ExactTargetClientSecret"];
private string TriggerEmail(User model, string dbName)
{
var etExternalKeyAppSetting = ConfigurationManager.AppSettings.AllKeys.FirstOrDefault(x => x.Equals(dbName));
if (etExternalKeyAppSetting != null)
{
string etExternalKey = ConfigurationManager.AppSettings[etExternalKeyAppSetting];
HttpClient client = new HttpClient
{
BaseAddress = new Uri(string.Format(#"{0}/key:{1}/send", messagingSendUrl, etExternalKey)),
DefaultRequestHeaders =
{
Authorization = new AuthenticationHeaderValue("Bearer", this.GetAccessToken())
}
};
try
{
var postData = this.CreateExactTargetPostData(model.Email, etExternalKey);
var response = client.PostAsync(client.BaseAddress
, new StringContent(JsonConvert.SerializeObject(postData).ToString()
, Encoding.UTF8
, "application/json")).Result;
// get triggered email response
if (response.IsSuccessStatusCode)
{
dynamic result = JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result);
}
}
catch (Exception ex)
{
string message = ex.Message;
}
}
return "testing";
}
private object CreateExactTargetPostData(string email, string extKey)
{
var fromData = new
{
Address = ConfigurationManager.AppSettings["AwsSenderEmail"],
Name = "Test"
};
var subscriberAttributes = new { };
var contactAttributes = new
{
SubscriberAttributes = subscriberAttributes
};
var toData = new
{
Address = email,
//SubscriberKey = extKey,
//ContactAttributes = contactAttributes
};
var postData = new
{
From = fromData,
To = toData
};
return postData;
}
I have also tried using Advanced REST Client using the following:
URL:
https://www.exacttargetapis.com/messaging/v1/messageDefinitionSends/key:MyExternalKey/send
POST
Raw Headers:
Content-Type: application/json
Authorization: Bearer XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Raw Payload:
{
"From": {
"Address": "code#exacttarget.com",
"Name": "Code#"
},
"To": {
"Address": "example#example.com",
"SubscriberKey": "example#example.com",
"ContactAttributes": {
"SubscriberAttributes": {
"Region": "West",
"City": "Indianapolis",
"State": "IN"
}
}
},
"OPTIONS": {
"RequestType": "ASYNC"
}
}
Issue was my App in the AppCenter was pointing to the incorrect login for MarketingCloud =(

Retrieving the Request Payload values from a HTTPRequest in c#

I am attempting to implement my own LRS for saving TinCanAPI statements and in order to do this I need to retrieve the values sent in the Request Payload which stores the details of the learning activity statement.
When viewing my WebAPI call in developer tools I can see the required values but I have been unable to find them using the Request object.
How Can I retrieve the Request Payload variables from the Request object? I have tried the request object and looked in the Content and Properties fields but I cannot seem to see a Request Payload property to reference in C#. My payload looks as follows:
{
"id": "d3d9aa2a-5f20-4303-84c3-1f6f5b4e9236",
"timestamp": "2014-06-26T11:00:41.432Z",
"actor": {
"objectType": "Agent",
"mbox": "mailto:name#company.com",
"name": "My Name"
},
"verb": {
"id": "http://adlnet.gov/expapi/verbs/attempted",
"display": {
"und": "attempted"
}
},
"context": {
"extensions": {
"http://tincanapi.com/JsTetris_TCAPI/gameId": "5686f104-3301-459d-9487-f84af3b3915c"
},
"contextActivities": {
"grouping": [
{
"id": "http://tincanapi.com/JsTetris_TCAPI",
"objectType": "Activity"
}
]
}
},
"object": {
"id": "http://tincanapi.com/JsTetris_TCAPI",
"objectType": "Activity",
"definition": {
"type": "http://adlnet.gov/expapi/activities/media",
"name": {
"en-US": "Js Tetris - Tin Can Prototype"
},
"description": {
"en-US": "A game of tetris."
}
}
}
}
I have tried using:
var test1 = Request.ToString(); **EMPTY STRING**
var test1 = Request.Content.ReadAsStringAsync().Result; **EMPTY STRING**
var test2 = Request.Content.ReadAsFormDataAsync().Result; **THROWS FORMATTER ERROR DESPITE config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json")); added in the webapiconfig.cs
I was able to retrieve the sent statement values by changing my WebAPI controller as follows:
public void Put([FromBody]TinCan.Statement statement)
{
var actor = statement.actor;
var context = statement.context;
var target = statement.target;
var timestamp = statement.timestamp;
var verb = statement.verb;
...................
try {
//String payloadRequest = getBody(request);
// System.out.println("payloadRequest : "+payloadRequest);
StringBuilder buffer = new StringBuilder();
BufferedReader reader = request.getReader();
String line;
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
String data = buffer.toString();
System.out.println("payloadRequest : "+data);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
Try this:
var value = Request["var_name"];

Categories