Google API - Gmail. Downloading attachments - c#

When calling this method please help by advising what values to pass through such as GmailService? Im guessing userID is the Gmail account and the messageID( i want to download all of them) .
How can i change this to download all the attachments in the inbox.
Thank you in advance and I hope someone can help me.
Method im using is below.
public static void GetAttachments(GmailService service, String userId, String messageId, String outputDir)
{
try
{
Message message = service.Users.Messages.Get(userId, messageId).Execute();
IList<MessagePart> parts = message.Payload.Parts;
foreach (MessagePart part in parts)
{
if (!String.IsNullOrEmpty(part.Filename))
{
String attId = part.Body.AttachmentId;
MessagePartBody attachPart = service.Users.Messages.Attachments.Get(userId, messageId, attId).Execute();
// Converting from RFC 4648 base64 to base64url encoding
// see http://en.wikipedia.org/wiki/Base64#Implementations_and_history
String attachData = attachPart.Data.Replace('-', '+');
attachData = attachData.Replace('_', '/');
byte[] data = Convert.FromBase64String(attachData);
File.WriteAllBytes(Path.Combine(outputDir, part.Filename), data);
}
}
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
}

I think the answer to your question can be solved using Get in PostMan
In your header in PostMan, use key as Authorization and pass your token generated to it as value
or goto Authorization and pick bearer token and pass your token.
Note that messageId is the Id in string of the message you are trying to fetch and {userId} =me or user according to google and I believe you can fetch Attachment Id by the method you use above.
https://www.googleapis.com/gmail/v1/users/me/messages/messageId/attachments/id
public async Task<TResult> GetGmailInboxAttachmentById<TResult>(string messageId, string token, string id, string attachId)
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var url = "https://www.googleapis.com/gmail/v1/users/me/messages/" + messageId + "/" + id + "/attachments" + "/" + attachId;
HttpResponseMessage response = await httpClient.GetAsync(url);
return await response.Content.ReadAsAsync<TResult>();
}
}
foreach (var part in inbox.Payload.Parts)
{
if (!String.IsNullOrEmpty(part.Filename))
{
var attachId = part.Body.AttachmentId;
var attach = _gmailService.GetGmailInboxAttachmentById<MessagePartBody>(id, token, part.PartId, attachId).Result;
// Converting from RFC 4648 base64 to base64url encoding
// see http://en.wikipedia.org/wiki/Base64#Implementations_and_history
string attachData = attach.Data.Replace('_', '+');
attachData = attachData.Replace('_', '/');
byte[] data = Convert.FromBase64String(attachData);
string file = Convert.ToBase64String(data);
GetAttach.Add(file);
}
}
Hope this solve it for you because I solved it with this method

I know, it's could be an off-topic, but using PostMan in case when programmer use Google API is little but contraproductive :) APIs also have attachments.Get Method, and you will not need to call HTTP request and allow non OAuth 2.0 authentication especially when you're using multi-phase authentication.
Here is example:
https://developers.google.com/gmail/api/v1/reference/users/messages/attachments/get

foreach (var part in m.Payload.Parts)
{
if (!string.IsNullOrEmpty(part.Filename))
{
var attachId = part.Body.AttachmentId;
MessagePartBody attachPart = service.Users.Messages.Attachments.Get(userId,
message.Id,
attachId).Execute();
byte[] data = GetBytesFromPart(attachPart.Data);
File.WriteAllBytes(Path.Combine(#"c:\teste\",
$"{DateTime.Now:yyyyMMddHHmmss}-{part.Filename}"), data);
}
}
private static string DecodedString(string messagePart)
{
try
{
var data = GetBytesFromPart(messagePart);
string decodedString = Encoding.UTF8.GetString(data);
return decodedString;
}
catch (System.Exception e)
{
// ignored
return string.Empty;
}
}
private static byte[] GetBytesFromPart(string messagePart)
{
var attachData = messagePart.Replace('-', '+');
attachData = attachData.Replace('_', '/');
byte[] data = Convert.FromBase64String(attachData);
return data;
}

Related

Azure Storage Rest API (Put Blob API)

I am trying to put a blob with Azure rest api. I made a "GET" request successfully but i had issues with "PUT" request. When I try to make "PUT" request i get a 403 error(Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.). I have seen same post in stackoverflow but it didn't help me. Any suggestions?
string uri = string.Format("https://{0}.blob.core.windows.net/{1}/LibraryForm.png", storageAccountName,containerName);
Byte[] requestPayload = File.ReadAllBytes(imagepath);
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Put, uri)
{ Content = (requestPayload == null) ? null : new ByteArrayContent(requestPayload) })
{
// Add the request headers for x-ms-date and x-ms-version.
DateTime now = DateTime.UtcNow;
httpRequestMessage.Headers.Add("x-ms-date", now.ToString("R", CultureInfo.InvariantCulture));
httpRequestMessage.Headers.Add("x-ms-version", "2020-06-12");
httpRequestMessage.Headers.Add("x-ms-blob-type", "BlockBlob");
httpRequestMessage.Headers.Add("x-ms-blob-content-type", "image/png");
// Add the authorization header.
httpRequestMessage.Headers.Authorization = GetAuthorizationHeader(
storageAccountName, storageAccountKey, now, httpRequestMessage);
// Send the request.
using (HttpResponseMessage httpResponseMessage = await new HttpClient().SendAsync(httpRequestMessage))
{
if (httpResponseMessage.StatusCode == HttpStatusCode.OK)
{
var str = httpResponseMessage.Content.ReadAsStringAsync().Result;
return str;
}
}
}
internal static AuthenticationHeaderValue GetAuthorizationHeader(string storageAccountName, string storageAccountKey, DateTime now,
HttpRequestMessage httpRequestMessage, string ifMatch = "", string md5 = "")
{
// This is the raw representation of the message signature.
HttpMethod method = httpRequestMessage.Method;
String MessageSignature = String.Format("{0}\n\n\n{1}\n{5}\n\n\n\n{2}\n\n\n\n{3}{4}",
method.ToString(),
(method == HttpMethod.Get || method == HttpMethod.Head) ? String.Empty
: httpRequestMessage.Content.Headers.ContentLength.ToString(),
ifMatch,
GetCanonicalizedHeaders(httpRequestMessage),
GetCanonicalizedResource(httpRequestMessage.RequestUri, storageAccountName),
md5);
// Now turn it into a byte array.
byte[] SignatureBytes = Encoding.UTF8.GetBytes(MessageSignature);
// Create the HMACSHA256 version of the storage key.
HMACSHA256 SHA256 = new HMACSHA256(Convert.FromBase64String(storageAccountKey));
// Compute the hash of the SignatureBytes and convert it to a base64 string.
string signature = Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes));
// This is the actual header that will be added to the list of request headers.
// You can stop the code here and look at the value of 'authHV' before it is returned.
AuthenticationHeaderValue authHV = new AuthenticationHeaderValue("SharedKey",
storageAccountName + ":" + Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes)));
return authHV;
}
private static string GetCanonicalizedHeaders(HttpRequestMessage httpRequestMessage)
{
var headers = from kvp in httpRequestMessage.Headers
where kvp.Key.StartsWith("x-ms-", StringComparison.OrdinalIgnoreCase)
orderby kvp.Key
select new { Key = kvp.Key.ToLowerInvariant(), kvp.Value };
StringBuilder sb = new StringBuilder();
// Create the string in the right format; this is what makes the headers "canonicalized" --
// it means put in a standard format. http://en.wikipedia.org/wiki/Canonicalization
foreach (var kvp in headers)
{
StringBuilder headerBuilder = new StringBuilder(kvp.Key);
char separator = ':';
// Get the value for each header, strip out \r\n if found, then append it with the key.
foreach (string headerValues in kvp.Value)
{
string trimmedValue = headerValues.TrimStart().Replace("\r\n", String.Empty);
headerBuilder.Append(separator).Append(trimmedValue);
// Set this to a comma; this will only be used
// if there are multiple values for one of the headers.
separator = ',';
}
sb.Append(headerBuilder.ToString()).Append("\n");
}
return sb.ToString();
}
private static string GetCanonicalizedResource(Uri address, string storageAccountName)
{
StringBuilder sb = new StringBuilder("/").Append(storageAccountName).Append(address.AbsolutePath);
// It will have more entries if you have more query parameters.
NameValueCollection values = HttpUtility.ParseQueryString(address.Query);
foreach (var item in values.AllKeys.OrderBy(k => k))
{
sb.Append('\n').Append(item).Append(':').Append(values[item]);
}
return sb.ToString().ToLower();
}
If you want to upload a file to Azure Blob with rest API, please refer to the fooliwng code
I define a class to get ShareKey
internal static class AzureStorageAuthenticationHelper
{
internal static AuthenticationHeaderValue GetAuthorizationHeader(
string storageAccountName, string storageAccountKey, DateTime now,
HttpRequestMessage httpRequestMessage, string ifMatch = "", string md5 = "")
{
// This is the raw representation of the message signature.
HttpMethod method = httpRequestMessage.Method;
String MessageSignature = String.Format("{0}\n\n\n{1}\n{5}\n\n\n\n{2}\n\n\n\n{3}{4}",
method.ToString(),
(method == HttpMethod.Get || method == HttpMethod.Head) ? String.Empty
: httpRequestMessage.Content.Headers.ContentLength.ToString(),
ifMatch,
GetCanonicalizedHeaders(httpRequestMessage),
GetCanonicalizedResource(httpRequestMessage.RequestUri, storageAccountName),
md5);
// Now turn it into a byte array.
byte[] SignatureBytes = Encoding.UTF8.GetBytes(MessageSignature);
// Create the HMACSHA256 version of the storage key.
HMACSHA256 SHA256 = new HMACSHA256(Convert.FromBase64String(storageAccountKey));
// Compute the hash of the SignatureBytes and convert it to a base64 string.
string signature = Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes));
// This is the actual header that will be added to the list of request headers.
// You can stop the code here and look at the value of 'authHV' before it is returned.
AuthenticationHeaderValue authHV = new AuthenticationHeaderValue("SharedKey",
storageAccountName + ":" + Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes)));
return authHV;
}
private static string GetCanonicalizedHeaders(HttpRequestMessage httpRequestMessage)
{
var headers = from kvp in httpRequestMessage.Headers
where kvp.Key.StartsWith("x-ms-", StringComparison.OrdinalIgnoreCase)
orderby kvp.Key
select new { Key = kvp.Key.ToLowerInvariant(), kvp.Value };
StringBuilder sb = new StringBuilder();
// Create the string in the right format; this is what makes the headers "canonicalized" --
// it means put in a standard format. http://en.wikipedia.org/wiki/Canonicalization
foreach (var kvp in headers)
{
StringBuilder headerBuilder = new StringBuilder(kvp.Key);
char separator = ':';
// Get the value for each header, strip out \r\n if found, then append it with the key.
foreach (string headerValues in kvp.Value)
{
string trimmedValue = headerValues.TrimStart().Replace("\r\n", String.Empty);
headerBuilder.Append(separator).Append(trimmedValue);
// Set this to a comma; this will only be used
// if there are multiple values for one of the headers.
separator = ',';
}
sb.Append(headerBuilder.ToString()).Append("\n");
}
return sb.ToString();
}
private static string GetCanonicalizedResource(Uri address, string storageAccountName)
{
// The absolute path is "/" because for we're getting a list of containers.
StringBuilder sb = new StringBuilder("/").Append(storageAccountName).Append(address.AbsolutePath);
// Address.Query is the resource, such as "?comp=list".
// This ends up with a NameValueCollection with 1 entry having key=comp, value=list.
// It will have more entries if you have more query parameters.
NameValueCollection values = HttpUtility.ParseQueryString(address.Query);
foreach (var item in values.AllKeys.OrderBy(k => k))
{
sb.Append('\n').Append(item).Append(':').Append(values[item]);
}
return sb.ToString().ToLower();
}
}
Uplaod
FileInfo fileInfo = new FileInfo("D:\\sampleData\\readsample.jpg");
string blobName = fileInfo.Name;
string contentType = MimeMapping.GetMimeMapping(blobName);
DateTime now = DateTime.UtcNow;
string blobURI = string.Format("https://{0}.blob.core.windows.net/{1}/{2}", StorageAccountName, "test", blobName);
using (var httpRequestMessage = new HttpRequestMessage(HttpMethod.Put, blobURI))
{
httpRequestMessage.Headers.Add("x-ms-date", now.ToString("R", CultureInfo.InvariantCulture));
httpRequestMessage.Headers.Add("x-ms-version", "2020-06-12");
httpRequestMessage.Headers.Add("x-ms-blob-type", "BlockBlob");
httpRequestMessage.Headers.Add("x-ms-blob-content-type", contentType);
httpRequestMessage.Content = new StreamContent(fileInfo.OpenRead());
httpRequestMessage.Headers.Authorization = AzureStorageAuthenticationHelper.GetAuthorizationHeader(
StorageAccountName, StorageAccountKey, now, httpRequestMessage);
// Send the request.
using (HttpResponseMessage httpResponseMessage = await new HttpClient().SendAsync(httpRequestMessage))
{
// If successful (status code = 200),
// parse the XML response for the container names.
if (httpResponseMessage.StatusCode == HttpStatusCode.Created)
{
Console.WriteLine("OK");
}
}
}
Besides, Using Azure SDK to implement upload progress is a simple way. Regarding how to use azure sdk, please refer to here.

Facebook data deletion callback gives "Unable to confirm request was received"

I just convert PHP sample code to C#. This is my endpoint:
public async Task<IHttpActionResult> Delete([FromBody] FbModel fbModel)
{
var res = fbModel.signed_request.Split(new char[] { '.' }, 2);
var secret = "ababababababababababababa";
if (res.Length > 1)
{
var sig = res[0];
var json = base64decode(res[1]);
var data = Newtonsoft.Json.JsonConvert.DeserializeObject<FacebookDeletionDto>(json);
if (string.IsNullOrEmpty(data.algorithm) || data.algorithm.ToUpper() != "HMAC-SHA256")
throw new Exception("Unknown algorithm:" + data.algorithm + ". Expected HMAC-SHA256");
var expected_sig = hmacSHA256(res[1], secret);
if (expected_sig != sig)
throw new Exception("Invalid signature:" + sig + ". Expected" + expected_sig);
var returnJson = new { Url = $"https://myperfectsite.com/fb/info/{data.user_id}", confirmation_code = $"{data.user_id}" };
return Ok(returnJson);
}
return null;
}
This code running perfectly and gives me json.
My endpoint return URL and confirmation code in JSON. But in facebook confirmation page it gives me this error :"Unable to confirm request was received
"App name" sent an invalid response to your request. Contact "App name" directly to request it delete info it has about you."
Facebook provides only the following info:
Return a JSON response that contains a URL where the user can check the status
of their deletion request and an alphanumeric confirmation code.
The JSON response has the following form:
{ url: '<url>', confirmation_code: '<code>' }
I ran into the same problem where FB would not accept our server response.
We ultimately fixed the problem by outputting the JSON response in EXACTLY the same sample format. Property names lowercase and not quoted, single quotes around the values, and a value that was alphanumeric (no symbols) and not too long (20 char length worked).
EDIT: Looking back at it, looks like we also used a dynamic type for the response.
dynamic response = new ExpandoObject();
response.url = "<url>";
response.confirmation_code = "<code>";
return response;
I notice that while in facebook documentation "expected_sig" must be equal to "sig" which must be the result of base64_url decoding of $encoded_sig in your code "expected_sig" is compared to the sign not decoded.
Hope this could be helpful.
var sig = res[0];
var json = base64decode(res[1]);
...
if (expected_sig != sig)
throw new Exception(...)
Correct should be as follow:
var sig = base64decode(res[0]);
var json = base64decode(res[1]);
...
if (expected_sig != sig)
throw new Exception(...)
Instead of:
Delete([FromBody] FbModel fbModel)
I use:
string signed_request = Request.Form["signed_request"];
Here is my working code:
public async Task<IActionResult> Delete()
{
string signed_request = Request.Form["signed_request"];
if (!String.IsNullOrEmpty(signed_request))
{
string[] split = signed_request.Split('.');
string signatureRaw = base64decode(split[0]);
string dataRaw = base64decode(split[1]);
// the decoded signature
byte[] signature = Convert.FromBase64String(signatureRaw);
byte[] dataBuffer = Convert.FromBase64String(dataRaw);
// JSON object
var json = Encoding.UTF8.GetString(dataBuffer);
byte[] appSecretBytes = Encoding.UTF8.GetBytes("SecretKey");
System.Security.Cryptography.HMAC hmac = new System.Security.Cryptography.HMACSHA256(appSecretBytes);
byte[] expectedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(split[1]));
if (!expectedHash.SequenceEqual(signature))
{
throw new Exception("Invalid signature");
}
var fbUser = JsonConvert.DeserializeObject<FacebookUserDTO>(json);
return Ok(new { url = $"https://myperfectsite.com/fb/info/{fbUser.user_id}", confirmation_code = $"{fbUser.user_id}" });
}
}

AWS TranslateText REST API call adding signature v4

Actually this is not a question, but an answer for those that are trying to use AWS TranslateText without SDK. I have had many problems until I got a running version.
In my opinion, the AWS documentation for this service is not complete and there are not many examples to check (at least for .Net).
At first, I thought I was not generating the correct V4 signature, but after checking the steps and values once again, I decided to use Postman to call the service. It was very helpful.
Postman can generate the AWS signature! (yeah, I did not know) so finally I noticed the signature value was not the problem.
Checking the request I could see the headers needed and some of its values.
The problem in my case was that I was not sending the "x-amz-target" header.
And by chance I found that the value for this header must be "AWSShineFrontendService_20170701.TranslateText"
(I saw a similar value here https://rdrr.io/cran/aws.translate/src/R/translateHTTP.R)
This other link was also helpful
Ivona Request Signing Issue - signature does not match (AWS Signature Version 4)
So now I have a running version, I want to share my .Net code. I hope it helps :) !!
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web.Script.Serialization;
namespace AWS_TranslateTextTest
{
class AWS_TranslateText
{
// Replace this with your own values
const string AccessKey = "AKI_ADD_YOUR_ACCESSKEY";
const string SecretKey = "ADD_YOUR_SECRETKEY";
static void Main()
{
try
{
string text = "Translate this text from English to German.";
string sourceLang = "en";
string targetLang = "de";
string responseText = TranslateText(text, sourceLang, targetLang);
JObject json = JObject.Parse(responseText);
string translatedText = ""; // to do read response from json or responseText
if (json.ToString().Contains("TranslatedText")){
//To access to the properties in "dot" notation use a dynamic object
dynamic obj = json;
translatedText = obj.TranslatedText.Value;
Console.WriteLine("TranslatedText is: {0}", translatedText);
}
else{
Console.WriteLine("TranslatedText not found in response.");
throw new Exception(json.ToString());
}
}
catch (WebException ex)
{
Console.WriteLine(ex.Message);
if (ex.Response != null){
foreach (string header in ex.Response.Headers)
{
Console.WriteLine("{0}: {1}", header, ex.Response.Headers[header]);
}
using (var responseStream = ex.Response.GetResponseStream())
{
if (responseStream != null)
{
using (var streamReader = new StreamReader(responseStream))
{
Console.WriteLine(streamReader.ReadToEnd());
}
}
} }
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private static string TranslateText(string text, string sourceLang, string targetLang)
{
var date = DateTime.UtcNow;
const string algorithm = "AWS4-HMAC-SHA256";
const string regionName = "eu-west-1";
const string serviceName = "translate";
const string method = "POST";
const string canonicalUri = "/";
const string canonicalQueryString = "";
const string x_amz_target_header = "AWSShineFrontendService_20170701.TranslateText";
const string contentType = "application/x-amz-json-1.1";
const string host = serviceName + "." + regionName + ".amazonaws.com";
var obj = new
{
SourceLanguageCode = sourceLang,
TargetLanguageCode = targetLang,
Text = text
};
var requestPayload = new JavaScriptSerializer().Serialize(obj);
var hashedRequestPayload = HexEncode(Hash(ToBytes(requestPayload)));
var dateStamp = date.ToString("yyyyMMdd");
var requestDate = date.ToString("yyyyMMddTHHmmss") + "Z";
var credentialScope = string.Format("{0}/{1}/{2}/aws4_request", dateStamp, regionName, serviceName);
var bytes = ToBytes(requestPayload);
var headers = new SortedDictionary<string, string>
{
{"content-length", bytes.Length.ToString()},
{"content-type", contentType},
{"host", host},
{"x-amz-date", requestDate},
{"x-amz-target", x_amz_target_header}
};
string canonicalHeaders =
string.Join("\n", headers.Select(x => x.Key.ToLowerInvariant() + ":" + x.Value.Trim())) + "\n";
string signedHeaders =
string.Join(";", headers.Select(x => x.Key.ToLowerInvariant() ));
// Task 1: Create a Canonical Request For Signature Version 4
var canonicalRequest = method + '\n' + canonicalUri + '\n' + canonicalQueryString +
'\n' + canonicalHeaders + '\n' + signedHeaders + '\n' + hashedRequestPayload;
var hashedCanonicalRequest = HexEncode(Hash(ToBytes(canonicalRequest)));
// Task 2: Create a String to Sign for Signature Version 4
// StringToSign = Algorithm + '\n' + RequestDate + '\n' + CredentialScope + '\n' + HashedCanonicalRequest
var stringToSign = string.Format("{0}\n{1}\n{2}\n{3}", algorithm, requestDate, credentialScope,
hashedCanonicalRequest);
// Task 3: Calculate the AWS Signature Version 4
// HMAC(HMAC(HMAC(HMAC("AWS4" + kSecret,"20130913"),"eu-west-1"),"tts"),"aws4_request")
byte[] signingKey = GetSignatureKey(SecretKey, dateStamp, regionName, serviceName);
// signature = HexEncode(HMAC(derived-signing-key, string-to-sign))
var signature = HexEncode(HmacSha256(stringToSign, signingKey));
// Task 4: Prepare a signed request
// Authorization: algorithm Credential=access key ID/credential scope, SignedHeadaers=SignedHeaders, Signature=signature
var authorization =
string.Format("{0} Credential={1}/{2}/{3}/{4}/aws4_request, SignedHeaders={5}, Signature={6}",
algorithm, AccessKey, dateStamp, regionName, serviceName, signedHeaders, signature);
// Send the request
string endpoint = "https://" + host; // + canonicalUri ;
var webRequest = WebRequest.Create(endpoint);
webRequest.Method = method;
webRequest.Timeout = 20000;
webRequest.ContentType = contentType;
webRequest.Headers.Add("X-Amz-Date", requestDate);
webRequest.Headers.Add("Authorization", authorization);
webRequest.Headers.Add("X-Amz-Target", x_amz_target_header);
webRequest.ContentLength = bytes.Length;
using (Stream newStream = webRequest.GetRequestStream())
{
newStream.Write(bytes, 0, bytes.Length);
newStream.Flush();
}
var response = (HttpWebResponse)webRequest.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
if (responseStream != null)
{
using (var streamReader = new StreamReader(responseStream))
{
string res = streamReader.ReadToEnd();
return res;
}
}
}
return null;
}
private static byte[] GetSignatureKey(String key, String dateStamp, String regionName, String serviceName)
{
byte[] kDate = HmacSha256(dateStamp, ToBytes("AWS4" + key));
byte[] kRegion = HmacSha256(regionName, kDate);
byte[] kService = HmacSha256(serviceName, kRegion);
return HmacSha256("aws4_request", kService);
}
private static byte[] ToBytes(string str)
{
return Encoding.UTF8.GetBytes(str.ToCharArray());
}
private static string HexEncode(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", string.Empty).ToLowerInvariant();
}
private static byte[] Hash(byte[] bytes)
{
return SHA256.Create().ComputeHash(bytes);
}
private static byte[] HmacSha256(String data, byte[] key)
{
return new HMACSHA256(key).ComputeHash(ToBytes(data));
}
}
}

500 error when making API token request to cherwell

I'm getting a 500 internal server error when trying to access cherwell REST api via Code trying to return a token to make other calls. I've verified that all the information (username, password, ClientID, and client secret) are all correct. I've also verified the server is up and accepting requests. Something must be wrong with my code. Any help would be great!
string token = "";
string responseBody;
string serverName = "ql1cwbeta1";
//initialize web client
using (WebClient webClient = new WebClient())
{
// pull down parameters for body
string grantType = ConfigurationManager.AppSettings["grant_type"];
string clientId = ConfigurationManager.AppSettings["client_id"];
string username = ConfigurationManager.AppSettings["username"];
string password = ConfigurationManager.AppSettings["password"];
string authMode = ConfigurationManager.AppSettings["auth_mode"];
//add parameters in headers
webClient.Headers.Add("Accept", "application/json");
// adding parameters in body
NameValueCollection values = new NameValueCollection
{
{"grant_type", grantType},
{"client_id", clientId},
{"username", username},
{"password", password},
{"auth_mode", authMode}
};
try
{
byte[] responseBytes = webClient.
UploadValues("http://" + serverName + "/CherwellAPI/token?auth_mode=" + authMode + "&api_key=" + clientId, "POST", values);
responseBody = Encoding.UTF8.GetString(responseBytes);
}
catch (Exception exception)
{
return exception;
}
Hope below code may help you.
public static string TokenRequest()
{
try
{
// Create HTTP Web Request for the token request
HttpWebRequest tokenRequest = (HttpWebRequest)WebRequest.Create(Values.TokenURL);
byte[] data = Encoding.ASCII.GetBytes("username=" + Username + "&password=" + Password + "&client_id=" + ClientId + "&grant_type=" + GrantType);
// Set request verb POST, content type and content length (length of data)
tokenRequest.Method = "POST";
tokenRequest.ContentType = "application/x-www-form-urlencoded";
tokenRequest.ContentLength = data.Length;
// Stream request data
using (Stream stream = tokenRequest.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
// Get the response and read stream to string
using (WebResponse response = tokenRequest.GetResponse())
{
using (Stream stream = response.GetResponseStream())
{
using (StreamReader sr = new StreamReader(stream))
{
// responseText = sr.ReadToEnd();
return sr.ReadToEnd();
}
}
}
}
catch (WebException ex)
{
// Catch error for bad URL (404) or bad request (400) resulting from bad input (username, password, client id, grant type in token request)
if (ex.Message.Contains("400"))
{
// do something for bad request
}
else if (ex.Message.Contains("404"))
{
// do something for not found
}
else
{
// unknown error, do something
}
return null;
}
catch (Exception ex)
{
// General Exception
return null;
}
}
For future reference - You can include the client ID as part of the querystring if all parameters are included in the querystring, but if you are also sending a payload, then authmode should be the only thing in the querystring.
It will accept all of these parameters as querystrings.
However, you should know that for Cherwell specifically, if you are using LDAP auth / user accounts, you may need to URL encode your username values.
There are certain special characters that will break the auth command. Specifically, including a backslash for a domain can cause issues if it's not escaped with %5C , which I believe can cause a 400 error.
Also a cause of an error can be if you've chosen an authmode that's not enabled for the browser apps in the Security Settings page of the admin console ;)

API exception while calling 'DataWarehouseGetReportData' in C#

I am using Omniture api to download a report. The report is completed when I checked the status with DataWarehouseCheckRequest method. Now when I try to fetch the report using DataWarehouseGetReportData method, I get
CommunicationException Error in deserializing body of reply message for operation 'DataWarehouseGetReportData
Inner exception says
The specified type was not recognized: name='data_warehouse_report_row', namespace='http://www.omniture.com/', at <rows xmlns=''>
I am new with C# and the API both. Got no idea how to resolve this. Please help.
Thanks
When you want to download a DW report the best option is to do it over http. This the standard way and is much more efficient.
The response to CheckRequest contains a DataURL. Use that to download the data.
Here is some c# sample code I am using for an almost identical API (Partner vs you Enterprise API) (note I'm no c# expert either, so you will need to do a code review on this).
HttpWebResponse statusResponse = null;
string response = "";
StringBuilder sbUrl = new StringBuilder(dwrq.data_url); // hardcode to variable "rest_url" for testing.
HttpWebRequest omniRequest = (HttpWebRequest)WebRequest.Create(sbUrl.ToString());
string timecreated = generateTimestamp();
string nonce = generateNonce();
string digest = getBase64Digest(nonce + timecreated + secret);
nonce = base64Encode(nonce);
omniRequest.Headers.Add("X-WSSE: UsernameToken Username=\"" + username + "\", PasswordDigest=\"" + digest + "\", Nonce=\"" + nonce + "\", Created=\"" + timecreated + "\"");
omniRequest.Method = "GET"; // Switched from POST as GET is the right HTTP verb in this case
try
{
statusResponse = (HttpWebResponse)omniRequest.GetResponse();
using (Stream receiveStream = statusResponse.GetResponseStream())
{
using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
response = readStream.ReadToEnd();
}
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
Console.WriteLine("Response is a TAB delimeted CSV structure. Printing to screen.");
Console.WriteLine(response);
Console.WriteLine("Ending REST...");
Console.WriteLine("Ending ExportRequestSegmentedData...");
and the supporting methods
/*** Here are the private functions ***/
// Encrypting passwords with SHA1 in .NET and Java
// http://authors.aspalliance.com/thycotic/articles/view.aspx?id=2
private static string getBase64Digest(string input)
{
SHA1 sha = new SHA1Managed();
ASCIIEncoding ae = new ASCIIEncoding();
byte[] data = ae.GetBytes(input);
byte[] digest = sha.ComputeHash(data);
return Convert.ToBase64String(digest);
}
// generate random nonce
private static string generateNonce()
{
Random random = new Random();
int len = 24;
string chars = "0123456789abcdef";
string nonce = "";
for (int i = 0; i < len; i++)
{
nonce += chars.Substring(Convert.ToInt32(Math.Floor(random.NextDouble() * chars.Length)), 1);
}
return nonce;
}
// Time stamp in UTC string
private static string generateTimestamp()
{
return DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
}
// C#-Base64 Encoding
// http://www.vbforums.com/showthread.php?t=287324
public static string base64Encode(string data)
{
byte[] encData_byte = new byte[data.Length];
encData_byte = System.Text.Encoding.UTF8.GetBytes(data);
string encodedData = Convert.ToBase64String(encData_byte);
return encodedData;
}
Best of Luck! C.

Categories

Resources