How do I check whether a mobile device has already been registered - ruby

I'm using the Amazon AWS Ruby SDK for Amazon SNS but I'm having some trouble with devices already being registered. Sometimes when a device gets registered again I get an error like AWS::SNS::Errors::InvalidParameter Invalid parameter: Token Reason: Endpoint arn:aws:sns:us-east-1:**** already exists with the same Token, but different attributes.. How do I check whether an endpoint already exists and more importantly, how do I get the endpoint for a given token?

Credit to BvdBijl's idea, I made an extension method to delete the existing one if found and then add it.
using System;
using System.Text.RegularExpressions;
using Amazon.SimpleNotificationService;
using Amazon.SimpleNotificationService.Model;
namespace Amazon.SimpleNotificationService
{
public static class AmazonSimpleNotificationServiceClientExtensions
{
private const string existingEndpointRegexString = "Reason: Endpoint (.+) already exists with the same Token";
private static Regex existingEndpointRegex = new Regex(existingEndpointRegexString);
public static CreatePlatformEndpointResponse CreatePlatformEndpointIdempotent(
this AmazonSimpleNotificationServiceClient client,
CreatePlatformEndpointRequest request)
{
try
{
var result = client.CreatePlatformEndpoint(request);
return result;
}
catch (AmazonSimpleNotificationServiceException e)
{
if (e.ErrorCode == "InvalidParameter")
{
var match = existingEndpointRegex.Match(e.Message);
if (match.Success) {
string arn = match.Groups[1].Value;
client.DeleteEndpoint(new DeleteEndpointRequest
{
EndpointArn = arn,
});
return client.CreatePlatformEndpoint(request);
}
}
throw;
}
}
}
}

It looks like amazone resolved this issue.
I'm using RoR and used to have same problem when trying to register and existing GCM code I got an error message saying
"AWS::SNS::Errors::InvalidParameter Invalid parameter: Token Reason: Endpoint arn:aws:sns:us-east-1:**** already exists with the same Token, but different attributes."
although I used same (empty) attributes. Now when I send an existing GCM code (with same attributes as the original one) I get the endpoint arn and not the error message.

ListEndpointsByPlatformApplication only return 100 endpoints, you have to use nextToken to get more. Here is my implementation.
public void deleteEndpoint(string token, string PlatformApplicationArn)
{
ListEndpointsByPlatformApplicationRequest listRequest = new ListEndpointsByPlatformApplicationRequest();
listRequest.PlatformApplicationArn = PlatformApplicationArn;
Logger.Info("Deleting endpoint with token -> " + token);
var list = snsClient.ListEndpointsByPlatformApplication(listRequest);
do
{
foreach (var x in list.Endpoints.Where(x => x.Attributes["Token"] == token))
{
snsClient.DeleteEndpoint(new DeleteEndpointRequest() { EndpointArn = x.EndpointArn });
Logger.Info("Endpoint removed-> " + x.EndpointArn);
return;
}
listRequest.NextToken = list.NextToken;
list = snsClient.ListEndpointsByPlatformApplication(listRequest);
}
while (list.NextToken != null);
}

Related

How to implement Exchange online OAuth2.0 for unmanaged EWS API?

For managed EWS code, I have used to OAuth 2.0 to get token and it worked.
For unmanaged EWS, it is failing to connect to Exchange as an unauthorized error.
Below is the code to access unmanaged EWS.
How to make below code work with OAuth token instead of passing credentials as below?.
Binding = new ExchangeServiceBinding
{
Url = ServerUrl,
Credentials = new OAuthCredentials(token),
RequestServerVersionValue = new RequestServerVersion { Version = ExchangeVersionType.Exchange2007_SP1 },
ExchangeImpersonation = null
};
Above is not working as credential is asking of type ICredentials and it is not accepting token. Please help me.
Below is the code how I direct access managed EWS.
var authResult = await pca.AcquireTokenByUsernamePassword(ewsScopes, credential.UserName, credential.SecurePassword).ExecuteAsync();
configure the ExchangeService with the access token
ExchangeService = new ExchangeService();
ExchangeService.Url = new Uri(ServerUrl);
ExchangeService.Credentials = new OAuthCredentials(authResult.AccessToken);
One method i use (as I've never worked out how to override the WSDL classes) is if you modify the Reference.cs file that gets generated in the web references directory you can modify the GetWebResponse command (In this case the token is being passed via the credentials object password property but there a number of different approaches you can take here) eg
private String AnchorMailbox;
private bool oAuth;
protected override System.Net.WebResponse GetWebResponse(System.Net.WebRequest req)
{
if (xAnchorMailbox != null)
{
if (xAnchorMailbox != "")
{
req.Headers.Add("X-AnchorMailbox", AnchorMailbox);
}
}
if(req.Credentials is System.Net.NetworkCredential)
{
if(oAuth){
req.Headers.Add("Authorization", ("Bearer " + ((System.Net.NetworkCredential)req.Credentials).Password));
}
}
System.Net.HttpWebResponse
rep = (System.Net.HttpWebResponse)base.GetWebResponse(req);
return rep;
}

Various errors using VisionServiceClient in XamarinForms

I am trying to create a simple Xamarin forms app which allows the user to browse for or take a photo and have azure cognitive services tag the photo using a custom vision model.
I am unable to get the client to successfully authenticate or find a resource per the error message in the exception produced by the VisionServiceClient. Am I missing something? What would be the correct values to use for the arguments to VisionServiceClient?
All keys have been removed from the below images, they are populated.
Exception thrown in VS2017:
'Microsoft.ProjectOxford.Vision.ClientException' in System.Private.CoreLib.dll
Call to VisionServiceClient:
private const string endpoint = #"https://eastus2.api.cognitive.microsoft.com/vision/prediction/v1.0";
private const string key = "";
VisionServiceClient visionClient = new VisionServiceClient(key, endpoint);
VisualFeature[] features = { VisualFeature.Tags, VisualFeature.Categories, VisualFeature.Description };
try
{
AnalysisResult temp = await visionClient.AnalyzeImageAsync(imageStream,
features.ToList(), null);
return temp;
}
catch(Exception ex)
{
return null;
}
VS Exception Error:
Azure Portal for cognitive services:
Custom Vision Portal:
It looks like you're confusing the Computer Vision and the Custom Vision APIs. You are attempting to use the client SDK for the former using the API key of the latter.
For .NET languages, you'll want the Microsoft.Azure.CognitiveServices.Vision.CustomVision.Prediction NuGet package.
Your code will end up looking something like this:
ICustomVisionPredictionClient client = new CustomVisionPredictionClient()
{
ApiKey = PredictionKey,
Endpoint = "https://southcentralus.api.cognitive.microsoft.com"
};
ImagePrediction prediction = await client.PredictImageAsync(ProjectId, stream, IterationId);
Thank you to cthrash for the extended help and talking with me in chat. Using his post along with a little troubleshooting I have figured out what works for me. The code is super clunky but it was just to test and make sure I'm able to do this. To answer the question:
Nuget packages and classes
Using cthrash's post I was able to get both the training and prediction nuget packages installed, which are the correct packages for this particular application. I needed the following classes:
Microsoft.Azure.CognitiveServices.Vision.CustomVision.Prediction
Microsoft.Azure.CognitiveServices.Vision.CustomVision.Prediction.Models
Microsoft.Azure.CognitiveServices.Vision.CustomVision.Training
Microsoft.Azure.CognitiveServices.Vision.CustomVision.Training.Models
Endpoint Root
Following some of the steps Here I determined that the endpoint URL's only need to be the root, not the full URL provided in the Custom Vision Portal. For instance,
https://southcentralus.api.cognitive.microsoft.com/customvision/v2.0/Prediction/
Was changed to
https://southcentralus.api.cognitive.microsoft.com
I used both the key and endpoint from the Custom Vision Portal and making that change I was able to use both a training and prediction client to pull the projects and iterations.
Getting Project Id
In order to use CustomVisionPredictionClient.PredictImageAsync you need a Guid for the project id and an iteration id if a default iteration is not set in the portal.
I tested two ways to get the project id,
Using project id string from portal
Grab the project id string from the portal under the project settings.
For the first argument to PredictImageAsync pass
Guid.Parse(projectId)
Using the training client
Create a new CustomVisionTrainingClient
To get a list of <Project> use
TrainingClient.GetProjects().ToList()
In my case I only had a single project so I would just need the first element.
Guid projectId = projects[0].Id
Getting Iteration Id
To get the iteration id of a project you need the CustomVisionTrainingClient.
Create the client
To get a list of <Iteration> use
client.GetIterations(projectId).ToList()
In my case I had only a single iteration so I just need the first element.
Guid iterationId = iterations[0].Id
I am now able to use my model to classify images. In the code below, fileStream is the image stream passed to the model.
public async Task<string> Predict(Stream fileStream)
{
string projectId = "";
//string trainingEndpoint = "https://southcentralus.api.cognitive.microsoft.com/customvision/v2.2/Training/";
string trainingEndpoint = "https://southcentralus.api.cognitive.microsoft.com/";
string trainingKey = "";
//string predictionEndpoint = "https://southcentralus.api.cognitive.microsoft.com/customvision/v2.0/Prediction/";
string predictionEndpoint = "https://southcentralus.api.cognitive.microsoft.com";
string predictionKey = "";
CustomVisionTrainingClient trainingClient = new CustomVisionTrainingClient
{
ApiKey = trainingKey,
Endpoint = trainingEndpoint
};
List<Project> projects = new List<Project>();
try
{
projects = trainingClient.GetProjects().ToList();
}
catch(Exception ex)
{
Debug.WriteLine("Unable to get projects:\n\n" + ex.Message);
return "Unable to obtain projects.";
}
Guid ProjectId = Guid.Empty;
if(projects.Count > 0)
{
ProjectId = projects[0].Id;
}
if (ProjectId == Guid.Empty)
{
Debug.WriteLine("Unable to obtain project ID");
return "Unable to obtain project id.";
}
List<Iteration> iterations = new List<Iteration>();
try
{
iterations = trainingClient.GetIterations(ProjectId).ToList();
}
catch(Exception ex)
{
Debug.WriteLine("Unable to obtain iterations.");
return "Unable to obtain iterations.";
}
foreach(Iteration itr in iterations)
{
Debug.WriteLine(itr.Name + "\t" + itr.Id + "\n");
}
Guid iteration = Guid.Empty;
if(iterations.Count > 0)
{
iteration = iterations[0].Id;
}
if(iteration == Guid.Empty)
{
Debug.WriteLine("Unable to obtain project iteration.");
return "Unable to obtain project iteration";
}
CustomVisionPredictionClient predictionClient = new CustomVisionPredictionClient
{
ApiKey = predictionKey,
Endpoint = predictionEndpoint
};
var result = await predictionClient.PredictImageAsync(Guid.Parse(projectId), fileStream, iteration);
string resultStr = string.Empty;
foreach(PredictionModel pred in result.Predictions)
{
if(pred.Probability >= 0.85)
resultStr += pred.TagName + " ";
}
return resultStr;
}

Xamarin Auth account store

I'm trying to implement Xamairn Auth with my app. I've installed the nuget package from https://www.nuget.org/packages/Xamarin.Auth.
Following their example I have the following code in the shared project.
public void SaveCredentials (string userName, string password)
{
if (!string.IsNullOrWhiteSpace (userName) && !string.IsNullOrWhiteSpace (password)) {
Account account = new Account {
Username = userName
};
account.Properties.Add ("Password", password);
AccountStore.Create ().Save (account, App.AppName);
}
}
When run on android, it saves the username and password but I'm getting the following message in the console:
"This version is insecure, because of default password.
Please use version with supplied password for AccountStore.
AccountStore.Create(Contex, string) or AccountStore.Create(string);"
I tried passing a parameter to the AccountStore.Create() method but it doesn't seem to take one. Something like this:
#if ANDROID
_accountStore = AccountStore.Create(Application.Context);
#else
_accountStore = AccountStore.Create();
#endif
Do I need to write android specific code to extend the create method.
I understand why you deleted the non-answer, I thought that would show interest in the question. I guess I should have upvoted the question instead. Anyways, here's the answer I found.
You can't use the PCL version for android. It doesn't have an option to add a password. I used the android specific version. Will call it using dependency service.
Here's an example:
Account account = null;
try
{
//account = AccountStore.Create(Application.ApplicationContext, "System.Char[]").FindAccountsForService("My APP").FirstOrDefault();
var aStore = AccountStore.Create(Application.ApplicationContext, "myownpassword");
// save test
account = aStore.FindAccountsForService(Constants.AppName).FirstOrDefault();
if (account == null)
account = new Account();
account.Username = "bobbafett";
account.Properties["pswd"] = "haha";
aStore.Save(account, Constants.AppName);
// delete test, doesn't seem to work, account is still found
var accts = aStore.FindAccountsForService(Constants.AppName);
int howMany = accts.ToList().Count;
foreach (var acct in accts)
{
aStore.Delete(acct, Constants.AppName);
}
account = aStore.FindAccountsForService(Constants.AppName).FirstOrDefault();
}
catch (Java.IO.IOException ex)
{
// This part is not invoked anymore once I use the suggested password.
int i1 = 123;
}
I was able to get it to work by implementing a getAccountStore method in android which has an option to add a password, then use DependencyService to call it.
public AccountStore GetAccountStore()
{
try
{
var acctStore = AccountStore.Create(Application.Context, "somePassword");
return acctStore;
}
catch (Java.IO.IOException ex)
{
throw ex;
}
}
Then in your pcl project call it as such:
if (Device.RuntimePlatform == Device.Android)
_accountStore = DependencyService.Get<IAccountStoreHelper>().GetAccountStore();
else
_accountStore = AccountStore.Create();

Protecting Android Library

I'm developing an android library for a Fintech product.
I want to restrict the usage of the AAR file only to the authenticated users.
We have server validation with access key for each client.
Is there any other client-side validation?
What's the best mechanism to protect my library?
The best solution is to validate the package name of the hosting application and hash key of the signature at the server side.
Don't take the hash key as the parameter to your SDK, Let the SDK read it at runtime and pass it to the server.
public String getCertificateHash(Context context) {
String hashKey = "";
try {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
for (Signature signature : info.signatures) {
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
hashKey = Base64.encodeToString(md.digest(), Base64.DEFAULT);
}
} catch (PackageManager.NameNotFoundException e) {
LogUtil.printObject(e);
} catch (NoSuchAlgorithmException e) {
LogUtil.printObject(e);
}
return hashKey.trim();
}

Webmasters API - Quota limits

We're trying to download page data for sites using the Webmasters API .NET Client Library, by calling WebmastersService.SearchAnalytics.Query(). To do this we are using Batching and sending approx. 600 requests in one batch. However most of these fail with the error "Quota Exceeded". The amount that fail varies each time but it is only about 10 of the 600 that work (and it varies where they are within the batch). The only way we can get it to work is to reduce the batch size down to 3, and wait 1 second between each call.
According to the Developer Console our daily quota is set to 1,000,000 (and we have 99% remaining) and our per user limit is set to 10,000 requests / second / user.
The error we get back is:
Quota Exceeded [403] Errors [ Message[Quota Exceeded] Location[ - ]
Reason[quotaExceeded] Domain[usageLimits]]
Is there another quota which is enforced? What does "Domain[usage limits]" mean - is the domain the site we are query the page data for, or is it our user account?
We still get the problem if we run each request separately, unless we wait 1 second between each call. Due to the number of sites and the number of pages we need to download the data for this isn't really an option.
I found this post which points out that just because the max batch size is 1000 doesn't mean to say the Google service you are calling supports batches of those sizes. But I'd really like to find out exactly what the quota limits really are (as they don't relate to the Developer Console figures) and how to avoid the errors.
Update 1
Here's some sample code. Its specially written just to prove the problem so no need to comment on it's quality ;o)
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using Google.Apis.Webmasters.v3;
using Google.Apis.Webmasters.v3.Data;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
new Program().Run().Wait();
}
private async Task Run()
{
List<string> pageUrls = new List<string>();
// Add your page urls to the list here
await GetPageData("<your app name>", "2015-06-15", "2015-07-05", "web", "DESKTOP", "<your domain name>", pageUrls);
}
public static async Task<WebmastersService> GetService(string appName)
{
//if (_service != null)
// return _service;
//TODO: - look at analytics code to see how to store JSON and refresh token and check runs on another PC
UserCredential credential;
using (var stream = new FileStream("c:\\temp\\WMT.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { Google.Apis.Webmasters.v3.WebmastersService.Scope.Webmasters },
"user", CancellationToken.None, new FileDataStore("WebmastersService"));
}
// Create the service.
WebmastersService service = new WebmastersService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = appName,
});
//_service = service;
return service;
}
private static async Task<bool> GetPageData(string appName, string fromDate, string toDate, string searchType, string device, string siteUrl, List<string> pageUrls)
{
// Get the service from the initial method
bool ret = false;
WebmastersService service = await GetService(appName);
Google.Apis.Requests.BatchRequest b = new Google.Apis.Requests.BatchRequest(service);
try
{
foreach (string pageUrl in pageUrls)
{
SearchAnalyticsQueryRequest qry = new SearchAnalyticsQueryRequest();
qry.StartDate = fromDate;
qry.EndDate = toDate;
qry.SearchType = searchType;
qry.RowLimit = 5000;
qry.Dimensions = new List<string>() { "query" };
qry.DimensionFilterGroups = new List<ApiDimensionFilterGroup>();
ApiDimensionFilterGroup filterGroup = new ApiDimensionFilterGroup();
ApiDimensionFilter filter = new ApiDimensionFilter();
filter.Dimension = "device";
filter.Expression = device;
filter.Operator__ = "equals";
ApiDimensionFilter filter2 = new ApiDimensionFilter();
filter2.Dimension = "page";
filter2.Expression = pageUrl;
filter2.Operator__ = "equals";
filterGroup.Filters = new List<ApiDimensionFilter>();
filterGroup.Filters.Add(filter);
filterGroup.Filters.Add(filter2);
qry.DimensionFilterGroups.Add(filterGroup);
var req = service.Searchanalytics.Query(qry, siteUrl);
b.Queue<SearchAnalyticsQueryResponse>(req, (response, error, i, message) =>
{
if (error == null)
{
// Process the results
ret = true;
}
else
{
Console.WriteLine(error.Message);
}
});
await b.ExecuteAsync();
}
}
catch (Exception ex)
{
Console.WriteLine("Exception occurred getting page stats : " + ex.Message);
ret = false;
}
return ret;
}
}
}
Paste this into program.cs of a new console app and add Google.Apis.Webmasters.v3 via nuget. It looks for the wmt.json file in c:\temp but adjust the authentication code to suit your setup. If I add more than 5 page urls to the pageUrls list then I get the Quota Exceeded exception.
I've found that the stated quotas don't really seem to be the quotas. I had to slow my requests down to avoid this same issue (1/sec), even though I was always at or below the stated rate limit (20/sec). Furthermore, it claims that it gives a rateLimitExceeded error in the docs for going too fast, but really it returns a quotaExceeded error. It might have to do with how Google averages the rate of requests over time (as some of the requests we made were simultaneous, even though the long-run average was designed to be at or below 20/sec), but I cannot be sure.

Resources