I would like to generate the SAS token for Azure table service from C# code. I generated the one from the portal which looks like
?sv=2016-05-31&ss=t&srt=sco&sp=rwdlacu&se=2017-03-23T20:05:14Z&st=2017-03-23T12:05:14Z&sip={MY_IP}&spr=https&sig=fL9GNAZqybSlQKWvaspwr%2FrFFtWO%2F5jVgFu1Ayu94Ic%3D
How to generate such kind of token from c# code? If there is any tutorial please redirect me to it.
I tried with a method below, but the token generated is invalid.
UPDATED CODE
I am still getting an error 403 Forbidden. Is my code to compute the signature correct?
var StringToSign = "{Storage_account_name}" + "\n" +
"rwdlacu" + "\n" +
"t" + "\n" +
"sco" + "\n" +
"2017-03-24T12:05:14Z" + "\n" +
"2017-03-24T20:05:14Z" + "\n" +
"{IP}" + "\n" +
"https" + "\n" +
"2016-05-31" + "\n";
string encodedString = HttpUtility.UrlEncode(StringToSign);
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String("accountkey"));
var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(encodedString)));
The reason you're running into the issue is because you're calculating signature for SAS based on the logic for calculating Authorization header. StringToSign is different in both cases.
For SAS, this should be (for Service SAS):
StringToSign = signedpermissions + "\n" +
signedstart + "\n" +
signedexpiry + "\n" +
canonicalizedresource + "\n" +
signedidentifier + "\n" +
signedIP + "\n" +
signedProtocol + "\n" +
signedversion + "\n" +
startingPartitionKey + "\n"
startingRowKey + "\n"
endingPartitionKey + "\n"
endingRowKey
If you want to use Account SAS (which is what Portal does), it should be:
StringToSign = accountname + "\n" +
signedpermissions + "\n" +
signedservice + "\n" +
signedresourcetype + "\n" +
signedstart + "\n" +
signedexpiry + "\n" +
signedIP + "\n" +
signedProtocol + "\n" +
signedversion + "\n"
So based on your parameters, the StringToSign for Account SAS would be:
StringToSign = {youraccountname} + "\n" +
"rwdlacu" + "\n" +
"t" + "\n" +
"sco" + "\n" +
"2017-03-23T12:05:14Z" + "\n" +
"2017-03-23T20:05:14Z" + "\n" +
{yourip} + "\n" +
"https" + "\n" +
"2016-05-31 + "\n"
The computation for signature is correct.
You may find these links helpful to learn more about computing SAS: Account SAS and Service SAS.
UPDATE
There's an issue with hmac calculation as well. It should be using your account key and also it should use Convert.FromBase64String.
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(accountKey));
Also, you should not URLEncode StringToSign. Elements there should be URL decoded.
Lastly the SAS token should look like what you're getting back from the portal.
Code Sample
static void AccountSasSample()
{
var accountName = "your-account-name";
var accountKey = "your-account-key";
var start = DateTime.UtcNow.AddHours(-1).ToString("yyyy-MM-ddTHH:mm:ssZ");
var end = DateTime.UtcNow.AddHours(1).ToString("yyyy-MM-ddTHH:mm:ssZ");
var permission = "rwdlacu";
var serviceType = "t";
var resourceTypes = "sco";
var ipAddress = "your-ip-address";
var protocol = "https";
var serviceVersion = "2016-05-31";
var stringToSign = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n{8}\n", accountName, permission, serviceType, resourceTypes, start, end, ipAddress, protocol, serviceVersion);
Console.WriteLine(stringToSign);
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(accountKey));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
var sasToken = string.Format("?sv={0}&ss={1}&srt={2}&sp={3}&se={4}&st={5}&sip={6}&spr={7}&sig={8}", serviceVersion,
serviceType, resourceTypes, permission, end, start, ipAddress, protocol, HttpUtility.UrlEncode(signature));
Console.WriteLine(sasToken);
var urlToListTables = string.Format("https://{0}.table.core.windows.net/Tables{1}", accountName, sasToken);
//Copy this urlToListTables & paste it in browser's address bar. You should be able to see the list of tables in your storage account.
}
//account name
var storageAccountName = ConfigProvider.AccountName;
// your storage account access key here
var accessKey = ConfigProvider.BlobKey;
// connect to our storage account and create a blob client
var connectionString = String.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}",
storageAccountName,
accessKey);
var storageAccount = CloudStorageAccount.Parse(connectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
SharedAccessAccountPolicy policy = new SharedAccessAccountPolicy()
{
Permissions = SharedAccessAccountPermissions.Write | SharedAccessAccountPermissions.Create,
Services = SharedAccessAccountServices.Blob,
ResourceTypes = SharedAccessAccountResourceTypes.Container | SharedAccessAccountResourceTypes.Object,
SharedAccessExpiryTime = DateTime.UtcNow.AddMonths(1),
Protocols = SharedAccessProtocol.HttpsOnly,
};
string sasToken = storageAccount.GetSharedAccessSignature(policy);
Why not use Azure Storage Client Library to generate SAS? You can refer to: https://learn.microsoft.com/en-us/azure/storage/storage-dotnet-shared-access-signature-part-1
Related
While sent email using below subject which apostrophe replacing with another characters
Actual Subject : We’ll make 100,800 cold calls for you
Mail Shows Subject : We’ll make 100,800 cold calls for you
Issue happens when I'm sent email via api , when sent email from SMTP it's working fine
Please check my api code below
string msg = "From: " + FromName + "<" + From + ">" + " \r\n" +
"To: " + ToName + "<" + To + ">" + " \r\n" +
"BCC: " + BCCEmail + " \r\n" +
"Subject: " + Subject + " \r\n" +
"Message-ID: mID_" + messageID + "\r\n" +
"References: "+encryptMessageID + "\r\n" +
"In-Reply-To: " + encryptMessageID + "\r\n" +
"Content-Type: " + contentType + "; charset=us-ascii\r\n\r\n" + Body;
dynamic objSendMsg = new { raw = commonFunction.Base64UrlEncode(msg) };
if (!string.IsNullOrEmpty(messageThreadID))
objSendMsg = new { raw = commonFunction.Base64UrlEncode(msg), threadId = messageThreadID };
var _objSendMsg = JsonConvert.SerializeObject(objSendMsg);
var strSendMsg = new StringContent(_objSendMsg, UnicodeEncoding.UTF8, "application/json");
When same content i'm applying in body with apostrophe working fine for body
Please check attached screenshot
Email copy
You need to base64_encode of the subject header your sending it as plain text. the API is getting confused.
Subject: " + Convert.ToBase64String(Subject) + " \r\n" +
I'm trying to clone/download my private bitbucket.org repository using C#, but I want to do it using pure HTTPS REST calls, and not a third party lib, i want to learn how it works.
So far I could only find example code for version 1 of the api.
This is what i've got working so far in C#:
static void AUthenticate()
{
var time = GenerateTimeStamp();
var url = "https://bitbucket.org/api/1.0/oauth/request_token";
var secret = "SECRET";
var key = "KEY";
var sigBaseStringParams = "";
sigBaseStringParams += "oauth_callback=http%3A%2F%2Flocal%3Fdump";
sigBaseStringParams += "&" + "oauth_consumer_key=" + key;
sigBaseStringParams += "&" + "oauth_nonce=" + GetNonce();
sigBaseStringParams += "&" + "oauth_signature_method=" + "HMAC-SHA1";
sigBaseStringParams += "&" + "oauth_timestamp=" + time;
sigBaseStringParams += "&" + "oauth_version=1.0";
var sigBaseString = "POST&";
sigBaseString += Uri.EscapeDataString(url) + "&" + Uri.EscapeDataString(sigBaseStringParams);
var signature = GetSignature(sigBaseString, secret);
var res = PostData(url, sigBaseStringParams + "&oauth_signature=" + Uri.EscapeDataString(signature));
var items = GetParameters(res);
var tokenSecret = items["oauth_token_secret"];
var token = items["oauth_token"];
var callbackConfirmed = items["oauth_callback_confirmed"];
url = "https://bitbucket.org/api/1.0/oauth/authenticate?oauth_token=" + token;
}
This authenticates and I get 3 values back. The last URL i paste into a browser, where i grant my application access and i end up with an oauth_verifier
Note: I don't really want to do this as I am writing a server program which won't really be able to send a user to a browser link (but one thing at a time)
I then run the following code:
var url = "https://bitbucket.org/api/1.0/oauth/access_token";
var token = "TOKEN FROM PREV CALL";
var time = GenerateTimeStamp();
var sigBaseStringParams = "";
//sigBaseStringParams += "oauth_callback=http%3A%2F%2Flocal%3Fdump";
var secret = "SECRET";
var key = "KEY";
sigBaseStringParams += "oauth_consumer_key=" + key;
sigBaseStringParams += "&" + "oauth_token=" + token;
sigBaseStringParams += "&" + "oauth_nonce=" + GetNonce();
sigBaseStringParams += "&" + "oauth_signature_method=" + "HMAC-SHA1";
sigBaseStringParams += "&" + "oauth_timestamp=" + time;
sigBaseStringParams += "&" + "oauth_verifier=AUTH VERIFIER FROM PREV CALL";
var sigBaseString = "POST&";
sigBaseString += Uri.EscapeDataString(url) + "&" + Uri.EscapeDataString(sigBaseStringParams);
var tokenSecret = "TOKEN SECRET FROM PREVIOUS CALL";
var signature = GetSignature(sigBaseString, secret, tokenSecret);
var res = PostData(url, sigBaseStringParams + "&oauth_signature=" + Uri.EscapeDataString(signature));
This gives me a 400 bad request. I can't see much else.
I'm following the steps on this page: https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html#OAuthonBitbucketCloud-Refreshtokens
Why I'm doing this is because i want to eventually make a POST request (or GET) to this URL:
var url2 = "https://bitbucket.org/ACCOUNT/REPOSITORY/get/tip.zip?access_token="+token;
Because I think this will give me the entire repository in a zip file. If i put this in the browser it works because i'm logged into bitbucket.
If there is a newer/easier/better way i'm open to suggestions.
Thanks in advance
I found a very simple solution.
I just had to provide credentials to a GET request:
public static void Downloadfiles(string username, string password, string account, string repository, string pathToSave)
{
var creds = Base64Encode(String.Format("{0}:{1}", username, password));
var url = String.Format("https://bitbucket.org/{0}/{1}/get/tip.zip", account, repository);
using (var client = new WebClient())
{
client.Headers.Add("Authorization", "Basic " + creds);
client.Headers.Add("Content-Type", "application/octet-stream");
client.DownloadFile(url, pathToSave);
}
}
I am trying to use Set-MsolUserLicense Msol Powershell command in c# to remove licenses from a user with the string
string removeLicense = "Set-MsolUserLicense -UserPrincipalName " + "\""
+ selectedUPN + "\"" + " -RemoveLicenses " + accountSkuId + ":ENTERPRISEPACK";
where selectedUPN is the selected option obtained from a textbox.
Somehow it does not accept a string but if I type the UPN literally in the removelicense string it works.
Finally Managed to get this sorted...created string as follows and it worked:
string removeLicense = "Set-MsolUserLicense -UserPrincipalName \"" + selectedUPN + "\" -RemoveLicenses " + accountSkuId + ":ENTERPRISEPACK";
I'm working on a Google Analytics Client for an MVC application that collects data from Analytics API and I'm trying display the data in a view. The ultimate goal is to display the Google Analytics Data with Google Charts.
However I'm not sure how to properly structure the Data. Each metric is a dictionary with a KeyValuePair where metric name is key and value is the actual value. For example ga:visitors, 3000. I need to organize each metric with its KeyValuePair in to a dictionary that I can return to the view.
For example, first I wrote a console application that returned 3 metrics:
ga:visitors
ga:newVisits
ga:percentNewVisits
Note: each of this metrics is a separate dictionary with KeyValuePair. Ex: [ga:visitors, 3000]
When displaying the data in the console, the code looks like this:
Console.WriteLine("VisitorStatistics" + " " +
d.Query.StartDate + " " + "-" + " " + d.Query.EndDate + "\r\n" +
"------------------------------------------" + "\r\n" +
"Visitors:" + " " + d.TotalsForAllResults["ga:visitors"] + "\r\n" +
"New Visitors:" + " " + d.TotalsForAllResults["ga:newVisits"] + "\r\n" +
"Percent New Visitors:" + " " + d.TotalsForAllResults["ga:percentNewVisits"] +"%");
I need to display this data in my MVC 4 /asp.net application but I'm not sure how to achieve this. All code is located here in my controller:
public void GAnalyticsService()
{
var serviceAccountEmail = "xxxxx.gserviceaccount.com";
var certificate = new x509Certificate2(#"C:\Users\User\Desktop\MyApp\Presentation\Nop.Web\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail) {
Scopes = new[] { AnalyticsService.Scope.Analytics }
}.FromCertificate(certificate));
// Create the service.
//NopCommerce
var GoogleAnalyticsService = new AnalyticsService(new BaseClientService.Initializer() {
HttpClientInitializer = credential,
ApplicationName = "MyApp",
});
var request = GoogleAnalyticsService.Data.Ga.Get("ProfileID", "2010-02-24", "2014-02-24", "ga:visitors,ga:newVisits,ga:percentNewVisits");
//Specify some addition query parameters
request.Dimensions = "ga:visitCount";
request.Sort = "-ga:visitors";
request.MaxResults = 10000;
//Execute and fetch the results of our query
Google.Apis.Analytics.v3.Data.GaData d = request.Execute();
}
public ActionResult GAStatistics() {
//GAnalyticsService();
return View(new GAStatisticsListModel());
}
}
Here's the data i'm recieving from Google Analytics API:
Total results with 3 metrics (each a dictionary with KeyValuePair)
The KeyValuePair:
I just need to organise this data and display it in the view, just plain data (text) is fine. How do i do this?
Any suggestions would be of great help.
Did a temporary fix just to display the data. It serves it's purpose for the time beeing.
Changed GAnalyticsService from a void to a string method.
Returned this:
return "Besöksstatistik" + " " +
d.Query.StartDate + " " + "-" + " " + d.Query.EndDate + "<br/>" +
"------------------------------------------" + "<br/>" +
"Antal besökare:" + " " + d.TotalsForAllResults["ga:visitors"] + "<br/>" +
"Antal nya besökare:" + " " + d.TotalsForAllResults["ga:newVisits"] + "<br/>" +
"Procent nya besökare:" + " " + d.TotalsForAllResults["ga:percentNewVisits"] + "%".ToString();
Got the following output in the view:
Besöksstatistik 2010-02-24 - 2014-02-24
------------------------------------------
Antal besökare: 343272
Antal nya besökare: 147693
Procent nya besökare: 42.54700702044485%
we have a c# application that reads an email Inbox currently hosted on Exchange 2003 using the http service.
Now the mailbox is to be migrated to an Exchange 2010 server, so we are testing our code to confirm it will still work.
We are getting an error 'Bad request' with the below code (which tries to get all the mail):
public static XmlDocument GetUnreadMailAll()
{
HttpWebRequest loRequest = default(HttpWebRequest);
HttpWebResponse loResponse = default(HttpWebResponse);
string lsRootUri = null;
string lsQuery = null;
byte[] laBytes = null;
Stream loRequestStream = default(Stream);
Stream loResponseStream = default(Stream);
XmlDocument loXmlDoc = default(XmlDocument);
loXmlDoc = new XmlDocument();
try
{
lsRootUri = strServer + "/Exchange/" + strAlias + "/" + strInboxURL;
lsQuery = "<?xml version=\"1.0\"?>"
+ "<D:searchrequest xmlns:D = \"DAV:\" xmlns:m=\"urn:schemas:httpmail:\">"
+ "<D:sql>SELECT "
+ "\"urn:schemas:httpmail:to\", "
+ "\"urn:schemas:httpmail:displayto\", "
+ "\"urn:schemas:httpmail:from\", "
+ "\"urn:schemas:httpmail:fromemail\", "
+ "\"urn:schemas:httpmail:subject\", "
+ "\"urn:schemas:httpmail:textdescription\", "
//+ "\"urn:schemas:httpmail:htmldescription\", "
+ "\"urn:schemas:httpmail:hasattachment\", "
+ "\"urn:schemas:httpmail:attachmentfilename\", "
+ "\"urn:schemas:httpmail:senderemail\", "
+ "\"urn:schemas:httpmail:sendername\", "
+ "\"DAV:displayname\", "
+ "\"urn:schemas:httpmail:datereceived\", "
+ "\"urn:schemas:httpmail:read\", "
+ "\"DAV:id\" "
+ "FROM \"" + lsRootUri
+ "\" WHERE \"DAV:ishidden\" = false "
+ "AND \"DAV:isfolder\" = false "
+ "AND \"urn:schemas:httpmail:read\" = false "
+ "AND \"urn:schemas:httpmail:fromemail\" != 'emailAddy#domainName.co.uk' "
+ "</D:sql></D:searchrequest>";
loRequest = (HttpWebRequest)WebRequest.Create(lsRootUri);
loRequest.Credentials = new NetworkCredential(strUserName, strPassword);
loRequest.Method = "SEARCH";
laBytes = System.Text.Encoding.UTF8.GetBytes(lsQuery);
loRequest.ContentLength = laBytes.Length;
loRequestStream = loRequest.GetRequestStream();
loRequestStream.Write(laBytes, 0, laBytes.Length);
loRequestStream.Close();
loRequest.ContentType = "text/xml";
loRequest.Headers.Add("Translate", "F");
loResponse = (HttpWebResponse)loRequest.GetResponse();
loResponseStream = loResponse.GetResponseStream();
loXmlDoc.Load(loResponseStream);
loResponseStream.Close();
}
the exception is thrown on the line loResponseStream = loResponse.GetResponseStream();
here is the xml that we are sending:
<?xml version="1.0" ?>
- <D:searchrequest xmlns:D="DAV:" xmlns:m="urn:schemas:httpmail:">
<D:sql>SELECT "urn:schemas:httpmail:to", "urn:schemas:httpmail:displayto", "urn:schemas:httpmail:from", "urn:schemas:httpmail:fromemail", "urn:schemas:httpmail:subject", "urn:schemas:httpmail:textdescription", "urn:schemas:httpmail:hasattachment", "urn:schemas:httpmail:attachmentfilename", "urn:schemas:httpmail:senderemail", "urn:schemas:httpmail:sendername", "DAV:displayname", "urn:schemas:httpmail:datereceived", "urn:schemas:httpmail:read", "DAV:id" FROM "https://domain/Exchange/bbtest/Inbox" WHERE "DAV:ishidden" = false AND "DAV:isfolder" = false AND "urn:schemas:httpmail:read" = false AND "urn:schemas:httpmail:fromemail" != 'emailAddy#domainName.co.uk'</D:sql>
</D:searchrequest>
and from MSDN the answer is that WebDAV is deprecated after Exchange 2007, and replaced by Exchange Web Services
here are a couple of links:
MSDN Library: Get started with Exchange Web Services
OMEGACODER: Getting all emails from Exchange using Exchange Web Services
MSDN Code Downloads: Exchange - 101 samples