Skip to content

Commit

Permalink
Add static authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
hossambarakat committed Apr 27, 2019
1 parent 595c194 commit 0f94b3d
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 34 deletions.
6 changes: 3 additions & 3 deletions EspressoShop.ProductCatalog/Controllers/ProductsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace EspressoShop.ProductCatalog.Controllers
[ApiController]
public class ProductsController : ControllerBase
{
private Product[] products = new Product[] {
private readonly Product[] _products = {
new Product
{
Id= 1,
Expand All @@ -26,13 +26,13 @@ public class ProductsController : ControllerBase
[HttpGet]
public ActionResult<IEnumerable<Product>> Get()
{
return products;
return _products;
}

[HttpGet("{id}")]
public ActionResult<Product> Get(int id)
{
return products.FirstOrDefault(x => x.Id == id);
return _products.FirstOrDefault(x => x.Id == id);
}
}
public class Product
Expand Down
6 changes: 3 additions & 3 deletions EspressoShop.Reviews/Controllers/ReviewsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace EspressoShop.Reviews.Controllers
public class ReviewsController : ControllerBase
{
private readonly IConfiguration _configuration;
private Review[] reviews = new Review[] {
private readonly Review[] _reviews = {
new Review
{
ProductId = 1,
Expand Down Expand Up @@ -50,11 +50,11 @@ public ActionResult<IEnumerable<Review>> Get(int productId)
var serviceVersion = _configuration.GetValue<string>("SERVICE_VERSION");
if(serviceVersion == "v1")
{
return reviews.Select(x => { x.Stars = null; return x; }).Where(x => x.ProductId == productId).ToArray();
return _reviews.Select(x => { x.Stars = null; return x; }).Where(x => x.ProductId == productId).ToArray();
}
else
{
return reviews.Where(x => x.ProductId == productId).ToArray();
return _reviews.Where(x => x.ProductId == productId).ToArray();
}

}
Expand Down
102 changes: 102 additions & 0 deletions EspressoShop.Web/Controllers/AccountController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using EspressoShop.Web.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;

namespace EspressoShop.Web.Controllers
{
[Route("[controller]/[action]")]
public class AccountController : Controller
{
private readonly ILogger<AccountController> _logger;

public AccountController(ILogger<AccountController> logger)
{
_logger = logger;
}

[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid) return View();

var claimsIdentity = AuthenticateUser(model.Email, model.Password);

if (claimsIdentity == null)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View();
}

await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties());

_logger.LogInformation($"User {model.Email} logged in at {DateTime.UtcNow}.");

return Redirect(returnUrl ?? "/");

}

[HttpPost]
public async Task<IActionResult> Logout()
{
_logger.LogInformation($"User {User.Identity.Name} logged out at {DateTime.UtcNow}.");

await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

return Redirect("/");
}

private ClaimsIdentity AuthenticateUser(string email, string password)
{
// For demonstration purposes, authenticate a user
// with a static email address. Ignore the password.

if (email == "[email protected]")
{
var claims = new List<Claim>
{
new Claim( ClaimTypes.Name, "Administrator"),
new Claim(ClaimTypes.Email, email),
new Claim(ClaimTypes.Role, "Administrator")
};

var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

return claimsIdentity;
}
if (email == "[email protected]")
{
var claims = new List<Claim>
{
new Claim( ClaimTypes.Name, "Hossam Barakat"),
new Claim(ClaimTypes.Email, email),
new Claim(ClaimTypes.Role, "User")
};

var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

return claimsIdentity;
}

return null;
}
}
}
13 changes: 11 additions & 2 deletions EspressoShop.Web/Infrastructure/HeadersHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Net.Http;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;

namespace EspressoShop.Web.Infrastructure
Expand All @@ -7,21 +8,29 @@ public class HeadersHelper
{
public static void AddTracingHeaders(HttpClient client, IHttpContextAccessor httpContextAccessor)
{
var incoming_headers = new string[] {"x-request-id",
var incomingHeaders = new[] {"x-request-id",
"x-b3-traceid",
"x-b3-spanid",
"x-b3-parentspanid",
"x-b3-sampled",
"x-b3-flags",
"x-ot-span-context"
};
foreach(var header in incoming_headers)
foreach(var header in incomingHeaders)
{
if(httpContextAccessor.HttpContext.Request.Headers.Keys.Contains(header))
{
client.DefaultRequestHeaders.Add(header, new string[] { httpContextAccessor.HttpContext.Request.Headers[header] });
}
}

if (httpContextAccessor.HttpContext.User.Identity.IsAuthenticated)
{
client.DefaultRequestHeaders.Add("userName", new[] { httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.Name)});
client.DefaultRequestHeaders.Add("UserEmail", new[] { httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.Email)});
client.DefaultRequestHeaders.Add("userRole", new[] { httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.Role)});
}

}
}
}
8 changes: 8 additions & 0 deletions EspressoShop.Web/Models/ApplicationUser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace EspressoShop.Web.Models
{
public class ApplicationUser
{
public string Email { get; set; }
public string FullName { get; set; }
}
}
18 changes: 18 additions & 0 deletions EspressoShop.Web/Models/LoginViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;

namespace EspressoShop.Web.Models
{
public class LoginViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }

[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }

}
}
24 changes: 12 additions & 12 deletions EspressoShop.Web/Services/ProductServiceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@ namespace EspressoShop.Web.Services
{
public class ProductServiceClient : IProductServiceClient
{
private readonly HttpClient client;
private readonly IHttpContextAccessor httpContextAccessor;
private readonly HttpClient _client;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger _logger;

public ProductServiceClient(HttpClient client, IHttpContextAccessor httpContextAccessor, ILoggerFactory loggerFactory)
{
this.client = client;
this.httpContextAccessor = httpContextAccessor;
this._client = client;
this._httpContextAccessor = httpContextAccessor;
_logger = loggerFactory.CreateLogger<ProductServiceClient>();
}
public async Task<IEnumerable<Product>> GetProductsAsync()
{
try
{
var userAgent = httpContextAccessor.HttpContext.Request.Headers["User-Agent"].ToString();
client.DefaultRequestHeaders.Add("User-Agent", userAgent);
HeadersHelper.AddTracingHeaders(client, httpContextAccessor);
var userAgent = _httpContextAccessor.HttpContext.Request.Headers["User-Agent"].ToString();
_client.DefaultRequestHeaders.Add("User-Agent", userAgent);
HeadersHelper.AddTracingHeaders(_client, _httpContextAccessor);

var productsResponse = await client.GetAsync("api/products");
var productsResponse = await _client.GetAsync("api/products");

var products = await productsResponse.Content.ReadAsAsync<List<Product>>();

Expand All @@ -43,11 +43,11 @@ public async Task<Product> GetProductAsync(int id)
try
{
_logger.LogInformation("Loading product with {ProductId}", id);
var userAgent = httpContextAccessor.HttpContext.Request.Headers["User-Agent"].ToString();
client.DefaultRequestHeaders.Add("User-Agent", userAgent);
HeadersHelper.AddTracingHeaders(client, httpContextAccessor);
var userAgent = _httpContextAccessor.HttpContext.Request.Headers["User-Agent"].ToString();
_client.DefaultRequestHeaders.Add("User-Agent", userAgent);
HeadersHelper.AddTracingHeaders(_client, _httpContextAccessor);

var productsResponse = await client.GetAsync($"api/products/{id}");
var productsResponse = await _client.GetAsync($"api/products/{id}");

var product = await productsResponse.Content.ReadAsAsync<Product>();

Expand Down
15 changes: 9 additions & 6 deletions EspressoShop.Web/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using System.Net.Http;
using EspressoShop.Web.Services;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
Expand Down Expand Up @@ -35,15 +36,16 @@ public void ConfigureServices(IServiceCollection services)
{
client.BaseAddress = new Uri(Configuration.GetValue<string>("ProductCatalogUrl"));
});
//.AddPolicyHandler(GetRetryPolicy());
//.AddPolicyHandler(GetRetryPolicy());

services.AddHttpClient<IReviewsServiceClient, ReviewsServiceClient>()
.ConfigureHttpClient(client =>
{
client.BaseAddress = new Uri(Configuration.GetValue<string>("ReviewsUrl"));
});
//.AddPolicyHandler(GetRetryPolicy());
//.AddPolicyHandler(GetRetryPolicy());

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Expand All @@ -54,10 +56,10 @@ static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()
return HttpPolicyExtensions
.HandleTransientHttpError()
.OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound)
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(1),(r,ts) =>
{
.WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(1), (r, ts) =>
{

});
});
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand All @@ -75,7 +77,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)

app.Use((context, next) =>
{
if(context.Request.Headers.Keys.Contains("x-b3-traceid"))
if (context.Request.Headers.Keys.Contains("x-b3-traceid"))
{
context.Response.Headers["x-b3-traceid"] = context.Request.Headers["x-b3-traceid"].FirstOrDefault();
}
Expand All @@ -85,6 +87,7 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();

app.UseMvc(routes =>
{
Expand Down
32 changes: 32 additions & 0 deletions EspressoShop.Web/Views/Account/login.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@model LoginViewModel

@{
ViewBag.Title = "Log in";
}

<h2>@ViewBag.Title.</h2>
<div class="row">
<div class="col-md-8">
<section id="loginForm">
<form asp-controller="Account" asp-action="Login" class = "form-horizontal"role = "form" method="post">
@Html.AntiForgeryToken()
<hr/>
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email"></label>
<input asp-for="Email" class="form-control">
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input asp-for="Password" class="form-control">
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default btn-primary">Log in</button>
</div>
</form>
</section>
</div>

</div>
6 changes: 3 additions & 3 deletions EspressoShop.Web/Views/Shared/_Layout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@
<a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand d-flex align-items-center">
<strong>Espresso Shop</strong>
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarHeader" aria-controls="navbarHeader" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>

<partial name="_LoginPartial" />
</div>

</div>
</header>

Expand Down
21 changes: 21 additions & 0 deletions EspressoShop.Web/Views/Shared/_LoginPartial.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@inject Microsoft.AspNetCore.Http.IHttpContextAccessor HttpContextAccessor;

@if (HttpContextAccessor.HttpContext.User.Identity.IsAuthenticated)
{
<span class="navbar-text ml-auto text-white">Welcome, @HttpContextAccessor.HttpContext.User.Identity.Name</span>
<ul class="navbar-nav navbar-right">

<li class="nav-item">
<form asp-controller="Account" asp-action="Logout" method="post" id="logoutForm" class="navbar-right">
<button type="submit" class="btn btn-link text-white">Log out</button>
</form>
</li>
</ul>

}
else
{
<ul class="nav navbar-nav navbar-right">
<li><a asp-action="Login" asp-controller="Account" class="text-white">Log in</a></li>
</ul>
}
Loading

0 comments on commit 0f94b3d

Please sign in to comment.