Fix Authentication issue in Locations.api

Created input in user campaigns view to update the user's location
This commit is contained in:
Ramón Tomás 2017-09-15 13:54:48 +02:00
parent aeea739d1d
commit cecdc40ac1
14 changed files with 240 additions and 32 deletions

View File

@ -102,6 +102,7 @@ services:
- IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105. - IdentityUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5105 #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103 - BasketUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5103
- MarketingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110 - MarketingUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5110
- LocationsUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5109
- CatalogUrlHC=http://catalog.api/hc - CatalogUrlHC=http://catalog.api/hc
- OrderingUrlHC=http://ordering.api/hc - OrderingUrlHC=http://ordering.api/hc
- IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser. - IdentityUrlHC=http://identity.api/hc #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.
@ -118,6 +119,7 @@ services:
- CatalogUrl=http://catalog.api - CatalogUrl=http://catalog.api
- OrderingUrl=http://ordering.api - OrderingUrl=http://ordering.api
- BasketUrl=http://basket.api - BasketUrl=http://basket.api
- LocationsUrl=http://locations.api
- IdentityUrl=http://10.0.75.1:5105 - IdentityUrl=http://10.0.75.1:5105
- MarketingUrl=http://marketing.api #Local: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser. - MarketingUrl=http://marketing.api #Local: Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser.
#Remote: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser. #Remote: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.

View File

@ -1,5 +1,6 @@
using Autofac; using Autofac;
using Autofac.Extensions.DependencyInjection; using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -169,13 +170,17 @@ namespace Microsoft.eShopOnContainers.Services.Locations.API
// prevent from mapping "sub" claim to nameidentifier. // prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication() services.AddAuthentication(options =>
.AddJwtBearer(options => {
{ options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.Authority = Configuration.GetValue<string>("IdentityUrl"); options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.Audience = "locations"; })
options.RequireHttpsMetadata = false; .AddJwtBearer(options =>
}); {
options.Authority = Configuration.GetValue<string>("IdentityUrl");
options.Audience = "locations";
options.RequireHttpsMetadata = false;
});
} }
protected virtual void ConfigureAuth(IApplicationBuilder app) protected virtual void ConfigureAuth(IApplicationBuilder app)

View File

@ -12,6 +12,7 @@ namespace Microsoft.eShopOnContainers.WebMVC
public string OrderingUrl { get; set; } public string OrderingUrl { get; set; }
public string BasketUrl { get; set; } public string BasketUrl { get; set; }
public string MarketingUrl { get; set; } public string MarketingUrl { get; set; }
public string LocationsUrl { get; set; }
public bool ActivateCampaignDetailFunction { get; set; } public bool ActivateCampaignDetailFunction { get; set; }
public Logging Logging { get; set; } public Logging Logging { get; set; }
public bool UseCustomizationData { get; set; } public bool UseCustomizationData { get; set; }

View File

@ -1,28 +1,29 @@
using Microsoft.EntityFrameworkCore.Query.Internal;
using WebMVC.ViewModels;
namespace Microsoft.eShopOnContainers.WebMVC.Controllers namespace Microsoft.eShopOnContainers.WebMVC.Controllers
{ {
using AspNetCore.Authorization; using AspNetCore.Authorization;
using AspNetCore.Mvc; using AspNetCore.Mvc;
using Services; using global::WebMVC.Models;
using ViewModels; using global::WebMVC.Services;
using System.Threading.Tasks;
using System;
using ViewModels.Pagination;
using global::WebMVC.ViewModels; using global::WebMVC.ViewModels;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Services;
using System;
using System.Threading.Tasks;
using ViewModels;
using ViewModels.Pagination;
[Authorize] [Authorize]
public class CampaignsController : Controller public class CampaignsController : Controller
{ {
private readonly ICampaignService _campaignService; private readonly ICampaignService _campaignService;
private readonly ILocationService _locationService;
private readonly AppSettings _settings; private readonly AppSettings _settings;
public CampaignsController(ICampaignService campaignService, IOptionsSnapshot<AppSettings> settings) public CampaignsController(ICampaignService campaignService, ILocationService locationService, IOptionsSnapshot<AppSettings> settings)
{ {
_campaignService = campaignService; _campaignService = campaignService;
_settings = settings.Value; _settings = settings.Value;
_locationService = locationService;
} }
public async Task<IActionResult> Index(int page = 0, int pageSize = 10) public async Task<IActionResult> Index(int page = 0, int pageSize = 10)
@ -76,5 +77,23 @@ namespace Microsoft.eShopOnContainers.WebMVC.Controllers
return View(campaign); return View(campaign);
} }
[HttpPost]
public async Task<IActionResult> CreateNewUserLocation(CampaignViewModel model)
{
if (ModelState.IsValid)
{
var location = new LocationDTO()
{
Longitude = model.Lon,
Latitude = model.Lat
};
await _locationService.CreateOrUpdateUserLocation(location);
return RedirectToAction(nameof(Index));
}
return View(nameof(Index), model);
}
} }
} }

View File

@ -94,5 +94,13 @@ namespace WebMVC.Infrastructure
return $"{baseUri}{id}"; return $"{baseUri}{id}";
} }
} }
public static class Locations
{
public static string CreateOrUpdateUserLocation(string baseUri)
{
return baseUri;
}
}
} }
} }

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebMVC.Models
{
public class LocationDTO
{
public double Longitude { get; set; }
public double Latitude { get; set; }
}
}

View File

@ -0,0 +1,10 @@
using System.Threading.Tasks;
using WebMVC.Models;
namespace WebMVC.Services
{
public interface ILocationService
{
Task CreateOrUpdateUserLocation(LocationDTO location);
}
}

View File

@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopOnContainers.BuildingBlocks.Resilience.Http;
using Microsoft.eShopOnContainers.WebMVC;
using Microsoft.eShopOnContainers.WebMVC.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
using WebMVC.Infrastructure;
using WebMVC.Models;
namespace WebMVC.Services
{
public class LocationService : ILocationService
{
private readonly IOptionsSnapshot<AppSettings> _settings;
private readonly IHttpClient _apiClient;
private readonly ILogger<CampaignService> _logger;
private readonly string _remoteServiceBaseUrl;
private readonly IHttpContextAccessor _httpContextAccesor;
public LocationService(IOptionsSnapshot<AppSettings> settings, IHttpClient httpClient,
ILogger<CampaignService> logger, IHttpContextAccessor httpContextAccesor)
{
_settings = settings;
_apiClient = httpClient;
_logger = logger;
_remoteServiceBaseUrl = $"{_settings.Value.LocationsUrl}/api/v1/locations/";
_httpContextAccesor = httpContextAccesor ?? throw new ArgumentNullException(nameof(httpContextAccesor));
}
public async Task CreateOrUpdateUserLocation(LocationDTO location)
{
var createOrUpdateUserLocationUri = API.Locations.CreateOrUpdateUserLocation(_remoteServiceBaseUrl);
var authorizationToken = await GetUserTokenAsync();
var response = await _apiClient.PostAsync(createOrUpdateUserLocationUri, location, authorizationToken);
response.EnsureSuccessStatusCode();
}
private async Task<string> GetUserTokenAsync()
{
var context = _httpContextAccesor.HttpContext;
return await context.GetTokenAsync("access_token");
}
}
}

View File

@ -15,6 +15,7 @@ using Microsoft.Extensions.Logging;
using System; using System;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using WebMVC.Infrastructure; using WebMVC.Infrastructure;
using WebMVC.Services;
namespace Microsoft.eShopOnContainers.WebMVC namespace Microsoft.eShopOnContainers.WebMVC
{ {
@ -64,6 +65,7 @@ namespace Microsoft.eShopOnContainers.WebMVC
services.AddTransient<IOrderingService, OrderingService>(); services.AddTransient<IOrderingService, OrderingService>();
services.AddTransient<IBasketService, BasketService>(); services.AddTransient<IBasketService, BasketService>();
services.AddTransient<ICampaignService, CampaignService>(); services.AddTransient<ICampaignService, CampaignService>();
services.AddTransient<ILocationService, LocationService>();
services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>(); services.AddTransient<IIdentityParser<ApplicationUser>, IdentityParser>();
if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString) if (Configuration.GetValue<string>("UseResilientHttp") == bool.TrueString)

View File

@ -0,0 +1,22 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace WebMVC.ViewModels.Annotations
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class LatitudeCoordinate : ValidationAttribute
{
protected override ValidationResult
IsValid(object value, ValidationContext validationContext)
{
double coordinate;
if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -90 || coordinate > 90))
{
return new ValidationResult
("Latitude must be between -90 and 90 degrees inclusive.");
}
return ValidationResult.Success;
}
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace WebMVC.ViewModels.Annotations
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]
public class LongitudeCoordinate : ValidationAttribute
{
protected override ValidationResult
IsValid(object value, ValidationContext validationContext)
{
double coordinate;
if (!double.TryParse(value.ToString(), out coordinate) || (coordinate < -180 || coordinate > 180))
{
return new ValidationResult
("Longitude must be between -180 and 180 degrees inclusive.");
}
return ValidationResult.Success;
}
}
}

View File

@ -3,10 +3,18 @@
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.eShopOnContainers.WebMVC.ViewModels; using Microsoft.eShopOnContainers.WebMVC.ViewModels;
using Microsoft.eShopOnContainers.WebMVC.ViewModels.Pagination; using Microsoft.eShopOnContainers.WebMVC.ViewModels.Pagination;
using WebMVC.ViewModels.Annotations;
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
public class CampaignViewModel public class CampaignViewModel
{ {
public IEnumerable<CampaignItem> CampaignItems { get; set; } public IEnumerable<CampaignItem> CampaignItems { get; set; }
public PaginationInfo PaginationInfo { get; set; } public PaginationInfo PaginationInfo { get; set; }
[LongitudeCoordinate, Required]
public double Lon { get; set; } = -122.315752;
[LatitudeCoordinate, Required]
public double Lat { get; set; } = 47.604610;
} }
} }

View File

@ -13,23 +13,59 @@
new Header() { Controller = "Catalog", Text = "Back to catalog" } }) new Header() { Controller = "Catalog", Text = "Back to catalog" } })
<div class="container"> <div class="container">
@if(Model != null && Model.CampaignItems.Any()) <br />
{ <div class="row">
<div class="card-group esh-campaigns-items row">
@foreach (var catalogItem in Model.CampaignItems) @if (!ViewData.ModelState.IsValid)
{ {
<div class="esh-campaigns-item col-md-4"> <div class="alert alert-warning">
@Html.Partial("_campaign", catalogItem) @Html.ValidationSummary(false)
</div>
}
<div class="col-md-12">
<div class="esh-campaigns-items" style="font-weight: 300;">
UPDATE USER LOCATION
</div>
<form class="form-inline" asp-action="CreateNewUserLocation" method="post">
<label class="sr-only" for="longitudeInput">Name</label>
<div class="input-group mb-2 mr-sm-2 mb-sm-0">
<div class="input-group-addon">Lon</div>
<input type="text" class="form-control mb-2 mr-sm-2 mb-sm-0" id="longitudeInput" asp-for="Lon" placeholder="Longitude">
</div>
<div class="input-group mb-2 mr-sm-2 mb-sm-0">
<div class="input-group-addon">Lat</div>
<input type="text" class="form-control mb-2 mr-sm-2 mb-sm-0" id="latitudeInput" asp-for="Lat" placeholder="Latitude">
</div>
<div class="input-group mb-2 mr-sm-2 mb-sm-0 col-md-2">
<input type="submit" value="Update" class="btn esh-campaigns-form-button" />
</div>
</form>
</div>
</div>
<br />
@if (Model != null && Model.CampaignItems !=null && Model.CampaignItems.Any())
{
<div class="card-group esh-campaigns-items row">
@foreach (var catalogItem in Model.CampaignItems)
{
<div class="esh-campaigns-item col-md-4">
@Html.Partial("_campaign", catalogItem)
</div>
}
</div>
@Html.Partial("_pagination", Model.PaginationInfo)
}
else
{
<div class="esh-campaigns-items row">
THERE ARE NO CAMPAIGNS
</div> </div>
} }
</div>
@Html.Partial("_pagination", Model.PaginationInfo)
}
else
{
<div class="esh-campaigns-items row">
THERE ARE NO CAMPAIGNS
</div>
}
</div> </div>

View File

@ -72,6 +72,17 @@
transition: all 0.35s; transition: all 0.35s;
width: 80%; width: 80%;
} }
.esh-campaigns-form-button {
background-color: #83D01B;
border: none;
color: #FFFFFF;
cursor: pointer;
font-size: 1rem;
transition: all 0.35s;
width: 80%;
}
.esh-campaigns-button.is-disabled { .esh-campaigns-button.is-disabled {
opacity: .5; opacity: .5;
pointer-events: none; pointer-events: none;