public async Task PutInstallations_RegistersUserId_IfAuthenticated() { // Arrange NotificationInstallationsController controller = InitializeAuthenticatedController(); HttpConfiguration config = controller.Configuration; NotificationInstallation notification = GetNotificationInstallation(); // Mock the PushClient and capture the Installation that we send to NH for later verification Installation installation = null; var pushClientMock = new Mock <PushClient>(config); pushClientMock.Setup(p => p.CreateOrUpdateInstallationAsync(It.IsAny <Installation>())) .Returns <Installation>((inst) => { installation = inst; return(Task.FromResult(0)); }); pushClientMock.Setup(p => p.GetRegistrationsByTagAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <int>())) .Returns(Task.FromResult(this.CreateCollectionQueryResult <RegistrationDescription>(Enumerable.Empty <RegistrationDescription>()))); config.SetPushClient(pushClientMock.Object); // Act await controller.PutInstallation(notification.InstallationId, notification); // Assert Assert.NotNull(installation); Assert.Equal(notification.InstallationId, installation.InstallationId); Assert.Equal(notification.PushChannel, installation.PushChannel); Assert.Equal(1, installation.Tags.Count()); Assert.Equal("_UserId:my:userid", installation.Tags[0]); }
public async Task MapLegacyCrossDomainController_MapsRoutesCorrectly() { // Arrange NotificationInstallation notification = new NotificationInstallation(); notification.InstallationId = Guid.NewGuid().ToString(); notification.PushChannel = Guid.NewGuid().ToString(); notification.Platform = "wns"; TestServer server = TestServer.Create(app => { HttpConfiguration config = new HttpConfiguration(); var pushClientMock = new Mock <PushClient>(config); pushClientMock.Setup(p => p.CreateOrUpdateInstallationAsync(It.IsAny <Installation>())) .Returns(Task.FromResult(0)); pushClientMock.Setup(p => p.GetRegistrationsByTagAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <int>())) .Returns(Task.FromResult(this.CreateCollectionQueryResult <RegistrationDescription>())); pushClientMock.Setup(p => p.DeleteInstallationAsync(It.IsAny <string>())) .Returns(Task.FromResult(0)); config.SetPushClient(pushClientMock.Object); #pragma warning disable 618 // Suppressing Obsolete warning on AddPushNotifications new MobileAppConfiguration() .MapApiControllers() .AddPushNotifications() .ApplyTo(config); #pragma warning restore 618 app.UseWebApi(config); }); HttpClient client = server.HttpClient; client.DefaultRequestHeaders.Add("ZUMO-API-VERSION", "2.0.0"); // Act var notificationsPut = await client.PutAsJsonAsync("push/installations/" + notification.InstallationId, notification); var notificationsDelete = await client.DeleteAsync("push/installations/" + notification.InstallationId); var notificationsPutApi = await client.PutAsJsonAsync("api/NotificationInstallations?installationId=" + notification.InstallationId, notification); var notificationsDeleteApi = await client.DeleteAsync("api/NotificationInstallations?installationId=" + notification.InstallationId); // Assert Assert.Equal(HttpStatusCode.OK, notificationsPut.StatusCode); Assert.Equal(HttpStatusCode.NoContent, notificationsDelete.StatusCode); // api routes should not be found Assert.Equal(HttpStatusCode.NotFound, notificationsPutApi.StatusCode); Assert.Equal(HttpStatusCode.NotFound, notificationsDeleteApi.StatusCode); }
internal void ValidateSecondaryTiles(NotificationInstallation notificationInstallation) { if (notificationInstallation == null) { throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_NotificationInstallationWasNull.FormatForUser())); } foreach (NotificationSecondaryTile secondaryTile in notificationInstallation.SecondaryTiles.Values) { if (secondaryTile == null) { throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_SecondaryTileWasNull.FormatForUser())); } this.ValidateSecondaryTile(secondaryTile); } }
public void NotificationInstallation_Serialization_IsConsistent() { // Arrange NotificationTemplate testSecondaryTemplate = new NotificationTemplate { Body = "mySecondaryBody", Headers = new Dictionary <string, string>() }; testSecondaryTemplate.Headers["mySecondaryHeaderName"] = "mySecondaryHeaderValue"; NotificationSecondaryTile testSecondaryTile = new NotificationSecondaryTile { PushChannel = "myPushChannel", Tags = new List <string>(), Templates = new Dictionary <string, NotificationTemplate>() }; testSecondaryTile.Tags.Add("myTag"); testSecondaryTile.Templates["mySecondaryTemplateName"] = testSecondaryTemplate; NotificationInstallation installation = new NotificationInstallation { PushChannel = "myPushChannel", Platform = "myPlatform", InstallationId = "myId", SecondaryTiles = new Dictionary <string, NotificationSecondaryTile>(), Templates = new Dictionary <string, NotificationTemplate>() }; installation.SecondaryTiles["myTestTile"] = testSecondaryTile; NotificationTemplate testTemplate = new NotificationTemplate { Body = "myBody", Headers = new Dictionary <string, string>() }; testSecondaryTemplate.Headers["myHeaderName"] = "myHeaderValue"; installation.Templates["myTemplateName"] = testTemplate; // Assert SerializationAssert.VerifySerialization(installation, "{\"pushChannel\":\"myPushChannel\",\"platform\":\"myPlatform\",\"installationId\":\"myId\",\"templates\":{\"myTemplateName\":{\"body\":\"myBody\",\"headers\":{},\"tags\":[]}},\"secondaryTiles\":{\"myTestTile\":{\"pushChannel\":\"myPushChannel\",\"tags\":[\"myTag\"],\"templates\":{\"mySecondaryTemplateName\":{\"body\":\"mySecondaryBody\",\"headers\":{\"mySecondaryHeaderName\":\"mySecondaryHeaderValue\",\"myHeaderName\":\"myHeaderValue\"},\"tags\":[]}}}},\"tags\":[]}"); }
public async Task PutInstallations_RegistersUserId_IfAuthenticatedAndTagsExist() { // Arrange NotificationInstallationsController controller = InitializeAuthenticatedController(); HttpConfiguration config = controller.Configuration; NotificationInstallation notification = GetNotificationInstallation(); // Mock the PushClient and capture the Installation that we send to NH for later verification Installation installation = null; var pushClientMock = new Mock <PushClient>(config); pushClientMock.Setup(p => p.CreateOrUpdateInstallationAsync(It.IsAny <Installation>())) .Returns <Installation>((inst) => { installation = inst; return(Task.FromResult(0)); }); pushClientMock.Setup(p => p.GetRegistrationsByTagAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <int>())) .Returns(() => { RegistrationDescription[] registrations = new RegistrationDescription[] { new WindowsRegistrationDescription("http://someuri", new string[] { "tag1", "tag2", "_UserId:something" }) }; return(Task.FromResult(this.CreateCollectionQueryResult <RegistrationDescription>(registrations))); }); config.SetPushClient(pushClientMock.Object); // Act await controller.PutInstallation(notification.InstallationId, notification); // Assert Assert.NotNull(installation); Assert.Equal(notification.InstallationId, installation.InstallationId); Assert.Equal(notification.PushChannel, installation.PushChannel); Assert.Equal(3, installation.Tags.Count()); // verify the existing userid is removed and replaced with current Assert.Equal("_UserId:my:userid", installation.Tags[2]); }
internal void ValidateNotificationInstallation(string installationId, NotificationInstallation notificationInstallation) { if (notificationInstallation == null) { throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_NotificationInstallationWasNull.FormatForUser())); } if (notificationInstallation.InstallationId != null && !installationId.ToString().Equals(notificationInstallation.InstallationId, StringComparison.OrdinalIgnoreCase)) { throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_InstallationIdDoesntMatchUri.FormatForUser())); } // Store the installation id into NotificationInstallation object. notificationInstallation.InstallationId = installationId.ToString(); // Check the required fields of the NotificationInstallation object. If failed, return Bad Request. if (string.IsNullOrEmpty(notificationInstallation.PushChannel)) { throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_MissingPropertyInRequestBody.FormatForUser("pushChannel"))); } if (string.IsNullOrEmpty(notificationInstallation.Platform)) { throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_MissingPropertyInRequestBody.FormatForUser("platform"))); } if (string.Equals(notificationInstallation.Platform, WindowsStorePlatform, StringComparison.OrdinalIgnoreCase)) { if (notificationInstallation.SecondaryTiles != null) { this.ValidateSecondaryTiles(notificationInstallation); } } foreach (NotificationTemplate template in notificationInstallation.Templates.Values) { this.ValidateTemplate(template, notificationInstallation.Platform); } }
/// <summary> /// Enables the registration of a device installation for push notifications via PUT request. /// If the specified installationId does not exist on the notification hub, a new installation is created and registered. /// If the specified installationId already exists, the installation is modified. /// </summary> /// <param name="installationId">The installation id to register or modify.</param> /// <param name="notificationInstallation">The <see cref="NotificationInstallation"/> object to register.</param> /// <returns><see cref="HttpResponseMessage"/> describing the result of the operation.</returns> public async Task <HttpResponseMessage> PutInstallation(string installationId, NotificationInstallation notificationInstallation) { if (!ModelState.IsValid) { throw new HttpResponseException(this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState)); } this.ValidateInstallationId(installationId); this.ValidateNotificationInstallation(installationId, notificationInstallation); ITraceWriter traceWriter = this.Configuration.Services.GetTraceWriter(); // The installation object that will be sent to NH. Installation installation = this.CreateInstallation(notificationInstallation); HashSet <string> tagsAssociatedWithInstallationId = await this.GetTagsAssociatedWithInstallationId(notificationInstallation.InstallationId); ClaimsPrincipal serviceUser = this.User as ClaimsPrincipal; if (tagsAssociatedWithInstallationId.Count == 0) { // Installation does not exist on NH. Add it. if (installation.Tags == null) { installation.Tags = new List <string>(); } // Tag the installation with the UserId if authenticated. if (serviceUser != null && serviceUser.Identity.IsAuthenticated) { Claim userIdClaim = serviceUser.FindFirst(ClaimTypes.NameIdentifier); if (userIdClaim != null) { string incomingUserTag = string.Format(UserIdTagPlaceholder, userIdClaim.Value); installation.Tags.Add(incomingUserTag); } } try { await this.UpsertInstallationAsync(installation); } catch (HttpResponseException) { throw; } catch (Exception ex) { string error = RResources.NotificationHub_CreateOrUpdateInstallationFailed.FormatForUser(installationId, ex.Message); traceWriter.Error(error, ex, this.Request, ServiceLogCategories.NotificationControllers); traceWriter.Error(error, ex, this.Request, LogCategories.NotificationControllers); // We return 4xx status code on error as it is impossible to know whether it is bad input or a // server error from NH. As a result we err on the bad request side. return(this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex.Message)); } } else { // Installation already existed on NH. if (serviceUser != null && serviceUser.Identity.IsAuthenticated) { // Because the user is authenticated, copy all previous tags except UserId. CopyTagsToInstallation(installation, tagsAssociatedWithInstallationId, false); // Add the incoming UserId. Claim userIdClaim = serviceUser.FindFirst(ClaimTypes.NameIdentifier); if (userIdClaim != null) { string incomingUserTag = string.Format(UserIdTagPlaceholder, userIdClaim.Value); AddTagToInstallation(installation, incomingUserTag); } } else { // Because the request is anonymous, copy all previous tags to the installation object, including the previous user tag. CopyTagsToInstallation(installation, tagsAssociatedWithInstallationId, true); } try { await this.UpsertInstallationAsync(installation); } catch (HttpResponseException) { throw; } catch (Exception ex) { string error = RResources.NotificationHub_CreateOrUpdateInstallationFailed.FormatForUser(installationId, ex.Message); traceWriter.Error(error, ex, this.Request, ServiceLogCategories.NotificationControllers); traceWriter.Error(error, ex, this.Request, LogCategories.NotificationControllers); // We return 4xx status code on error as it is impossible to know whether it is bad input or a // server error from NH. As a result we err on the bad request side. return(this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex.Message)); } } return(this.Request.CreateResponse(HttpStatusCode.OK)); }
internal Installation CreateInstallation(NotificationInstallation notificationInstallation) { if (notificationInstallation == null) { throw new ArgumentNullException("notificationInstallation"); } if (notificationInstallation.Platform == null) { throw new ArgumentException("notificationInstallation.Platform is null"); } Installation installation = new Installation(); installation.InstallationId = notificationInstallation.InstallationId; installation.PushChannel = notificationInstallation.PushChannel; // Set the Platform. Create and add WnsSecondaryTile objects to the installation if WNS. if (notificationInstallation.Platform.Equals(WindowsStorePlatform, StringComparison.OrdinalIgnoreCase)) { installation.Platform = NotificationPlatform.Wns; // Parse each Secondary Tile passed in request and add to installation object. foreach (string tileName in notificationInstallation.SecondaryTiles.Keys) { NotificationSecondaryTile notificationTile = notificationInstallation.SecondaryTiles[tileName]; if (installation.SecondaryTiles == null) { installation.SecondaryTiles = new Dictionary <string, WnsSecondaryTile>(); } installation.SecondaryTiles[tileName] = CreateWnsSecondaryTile(notificationTile); } } else if (notificationInstallation.Platform.Equals(ApplePlatform, StringComparison.OrdinalIgnoreCase)) { installation.Platform = NotificationPlatform.Apns; } else if (notificationInstallation.Platform.Equals(GooglePlatform, StringComparison.OrdinalIgnoreCase)) { installation.Platform = NotificationPlatform.Gcm; } else { throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_UnsupportedPlatform.FormatForUser(notificationInstallation.Platform))); } // Create and add InstallationTemplate objects to the installation. foreach (string templateName in notificationInstallation.Templates.Keys) { NotificationTemplate template = notificationInstallation.Templates[templateName]; if (installation.Templates == null) { installation.Templates = new Dictionary <string, InstallationTemplate>(); } installation.Templates[templateName] = CreateInstallationTemplate(template, installation.Platform); } return(installation); }
public void CreateInstallationObject_ParsingIsConsistent(string pushChannel, string platformAsString, NotificationPlatform platformAsEnum) { string installationId = "12345678-1234-1234-1234-123456789012"; string tileName = "myTile"; string tileTemplateName = "templateNameTile"; string tileTemplateBody = "myTemplateBodyTile"; string tileTemplateHeaderName = "templateHeaderNameTile"; string tileTemplateHeaderValue = "templateHeaderValueTile"; string installationTemplateName = "installationTemplateName"; string installationTemplateBody = "installationTemplateBody"; string installationTemplateHeaderName = "installationTemplateHeaderName"; string installationTemplateHeaderValue = "installationTemplateHeaderBody"; // Arrange Dictionary <string, string> tileTemplateHeaders = new Dictionary <string, string>(); tileTemplateHeaders[tileTemplateHeaderName] = tileTemplateHeaderValue; Dictionary <string, NotificationTemplate> tileTemplates = new Dictionary <string, NotificationTemplate>(); tileTemplates[tileTemplateName] = new NotificationTemplate { Body = tileTemplateBody, Headers = tileTemplateHeaders }; Dictionary <string, NotificationSecondaryTile> tiles = new Dictionary <string, NotificationSecondaryTile>(); tiles[tileName] = new NotificationSecondaryTile { PushChannel = pushChannel, Tags = new List <string> { "tag1", "tag2" }, Templates = tileTemplates }; Dictionary <string, string> installationTemplateHeaders = new Dictionary <string, string>(); installationTemplateHeaders[installationTemplateHeaderName] = installationTemplateHeaderValue; Dictionary <string, NotificationTemplate> installationTemplates = new Dictionary <string, NotificationTemplate>(); installationTemplates[installationTemplateName] = new NotificationTemplate { Body = installationTemplateBody, Headers = installationTemplateHeaders }; NotificationInstallation notificationInstallation = new NotificationInstallation { Platform = platformAsString, PushChannel = pushChannel, SecondaryTiles = tiles, Tags = new List <string> { "tagA", "tagB" }, Templates = installationTemplates }; notificationInstallation.InstallationId = installationId; NotificationInstallationsControllerMock mock = new NotificationInstallationsControllerMock(); // Act Installation testInstallation = mock.CreateInstallation(notificationInstallation); // Assert Assert.NotNull(testInstallation); Assert.Equal(installationId, testInstallation.InstallationId); Assert.Equal(platformAsEnum, testInstallation.Platform); Assert.Equal(pushChannel, testInstallation.PushChannel); if (platformAsEnum == NotificationPlatform.Wns) { Assert.NotNull(testInstallation.SecondaryTiles); Assert.NotNull(testInstallation.SecondaryTiles[tileName]); Assert.Equal(pushChannel, testInstallation.SecondaryTiles[tileName].PushChannel); Assert.Equal(0, testInstallation.SecondaryTiles[tileName].Tags.Count); Assert.NotNull(testInstallation.SecondaryTiles[tileName].Templates); Assert.NotNull(testInstallation.SecondaryTiles[tileName].Templates[tileTemplateName]); // Tags were stripped within the tile Assert.Equal(tileTemplateBody, testInstallation.SecondaryTiles[tileName].Templates[tileTemplateName].Body); Assert.Equal(installationTemplateHeaderValue, testInstallation.Templates[installationTemplateName].Headers[installationTemplateHeaderName]); } else { Assert.Null(testInstallation.SecondaryTiles); Assert.Null(testInstallation.Templates[installationTemplateName].Headers); } Assert.NotNull(testInstallation.Templates[installationTemplateName]); Assert.Equal(installationTemplateBody, testInstallation.Templates[installationTemplateName].Body); // Tags were stripped within the template Assert.Equal(0, testInstallation.Templates[installationTemplateName].Tags.Count); }
public async Task MobileAppAuth_Succeeds_AsPassiveAndActive(AuthenticationMode mode, bool isMiddlewareRegistered, bool isAuthenticated) { NotificationInstallation notification = new NotificationInstallation(); notification.InstallationId = Guid.NewGuid().ToString(); notification.PushChannel = Guid.NewGuid().ToString(); notification.Platform = "wns"; using (var testServer = TestServer.Create(app => { // Arrange HttpConfiguration config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); config.EnableSystemDiagnosticsTracing(); new MobileAppConfiguration() .UseDefaultConfiguration() .ApplyTo(config); var pushClientMock = new Mock <PushClient>(config); pushClientMock.Setup(p => p.CreateOrUpdateInstallationAsync(It.IsAny <Installation>())) .Returns(Task.FromResult(0)); pushClientMock.Setup(p => p.GetRegistrationsByTagAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <int>())) .Returns(Task.FromResult(this.CreateCollectionQueryResult <RegistrationDescription>())); config.SetPushClient(pushClientMock.Object); if (isMiddlewareRegistered) { if (mode == AuthenticationMode.Passive) { config.SuppressDefaultHostAuthentication(); config.Filters.Add(new HostAuthenticationFilter(AppServiceAuthenticationOptions.AuthenticationName)); } app.UseAppServiceAuthentication(GetMobileAppAuthOptions(config, mode)); } app.UseWebApi(config); })) { HttpClient client = new HttpClient(new AddMobileAppAuthHeaderHttpHandler(testServer.Handler, isAuthenticated)); client.BaseAddress = new Uri(TestWebsiteUrl); // Act var notificationsPut = await client.PutAsJsonAsync("push/installations/" + notification.InstallationId, notification); var apiNotificationsPut = await client.PutAsJsonAsync("api/notificationinstallations/" + notification.InstallationId, notification); var tableGet = await client.GetAsync("tables/testtable"); var tableGetApplication = await client.GetAsync("tables/testtable/someId"); var tableGetApiRoute = await client.GetAsync("api/testtable"); var apiGetAnonymous = await client.GetAsync("api/secured/anonymous"); var apiGetAuthorize = await client.GetAsync("api/secured/authorize"); // Assert Assert.Equal(HttpStatusCode.OK, notificationsPut.StatusCode); ValidateHeaders(notificationsPut, true); Assert.Equal(HttpStatusCode.NotFound, apiNotificationsPut.StatusCode); ValidateHeaders(apiNotificationsPut, true); // Succeeds: Api action with no AuthorizeLevel attribute Assert.Equal(HttpStatusCode.OK, tableGet.StatusCode); ValidateHeaders(tableGet, true); // Authorize attribute will deny any unauthenticated requests. Assert.Equal(isAuthenticated ? HttpStatusCode.OK : HttpStatusCode.Unauthorized, tableGetApplication.StatusCode); ValidateHeaders(tableGetApplication, true); // Succeeds: TableControllers will show up in the api route as well. Assert.Equal(HttpStatusCode.OK, tableGetApiRoute.StatusCode); ValidateHeaders(tableGetApiRoute, true); // Succeeds: Auth is not set up so no ServiceUser is created. But // the AuthorizeAttribute lets these through. Assert.Equal(HttpStatusCode.OK, apiGetAnonymous.StatusCode); ValidateHeaders(apiGetAnonymous, false); // Succeeds: Api action with no AuthorizeLevel attribute Assert.Equal(isAuthenticated ? HttpStatusCode.OK : HttpStatusCode.Unauthorized, apiGetAuthorize.StatusCode); ValidateHeaders(apiGetAuthorize, false); if (isAuthenticated) { string requestAuthToken = apiGetAuthorize.RequestMessage.Headers.Single(h => h.Key == "x-zumo-auth").Value.Single(); JToken responseAuthToken = await apiGetAuthorize.Content.ReadAsAsync <JToken>(); Assert.Equal(requestAuthToken, responseAuthToken.ToString()); } } }