tout du bon:

- mobile app declaration (WIP)
- login/logout depuis la même resource, sans bug
  (note: appliquer AllowAnonymous à la classe => methodes d'attribut Authorize non redirigées)
- les médias des posts
This commit is contained in:
2016-05-31 14:44:07 +02:00
parent 9d078e4e8e
commit 6e301e52d8
20 changed files with 321 additions and 188 deletions

View File

@ -1,16 +0,0 @@
@model IEnumerable<blog>
@{
ViewData["Title"] = "Blog spot";
}
<div class="row">
@foreach (var entry in Model) {
<div class="col-md-3">
<h2> @entry.Title</h2>
<ul>
<li>Sample pages using ASP.NET MVC 6</li>
</ul>
</div>
}
</div>

View File

@ -10,20 +10,20 @@
<div class="form-horizontal"> <div class="form-horizontal">
<h4>Blog</h4> <h4>Blog</h4>
<hr /> <hr />
<div asp-validation-summary="ValidationSummary.ModelOnly" class="text-danger"></div> <div asp-validation-summary="ValidationSummary.All" class="text-danger"></div>
<div class="form-group"> <div class="form-group">
<label asp-for="title" class="col-md-2 control-label"></label> <label asp-for="title" class="col-md-2 control-label"></label>
<div class="col-md-10"> <div class="col-md-10">
<input asp-for="title" class="form-control" /> <input asp-for="title" class="form-control" />
<span asp-validation-for="title" class="text-danger" /> <span asp-validation-for="title" class="text-danger" ></span>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label asp-for="photo" class="col-md-2 control-label"></label> <label asp-for="photo" class="col-md-2 control-label"></label>
<div class="col-md-10"> <div class="col-md-10">
<input asp-for="photo" class="form-control" /> <input asp-for="photo" class="form-control" />
<span asp-validation-for="photo" class="text-danger" /> <span asp-validation-for="photo" class="text-danger" ></span>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -31,7 +31,7 @@
<div class="col-md-10"> <div class="col-md-10">
<textarea asp-for="bcontent" class="form-control" > <textarea asp-for="bcontent" class="form-control" >
</textarea> </textarea>
<span asp-validation-for="bcontent" class="text-danger" /> <span asp-validation-for="bcontent" class="text-danger" ></span>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -40,7 +40,6 @@
<input asp-for="visible" class="form-control"/> <input asp-for="visible" class="form-control"/>
</div> </div>
</div> </div>
@Html.Hidden("AuthorId")
<div class="form-group"> <div class="form-group">
<div class="col-md-offset-2 col-md-10"> <div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" /> <input type="submit" value="Create" class="btn btn-default" />

View File

@ -8,6 +8,18 @@
<h4>Blog</h4> <h4>Blog</h4>
<hr /> <hr />
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.title)
</dt>
<dd>
@Html.DisplayFor(model => model.title)
</dd>
<dt>
@Html.DisplayNameFor(model => model.photo)
</dt>
<dd>
@Html.DisplayFor(model => model.photo)
</dd>
<dt> <dt>
@SR["Author"] @SR["Author"]
</dt> </dt>
@ -18,7 +30,7 @@
Contenu Contenu
</dt> </dt>
<dd> <dd>
<div markdown="@Model.bcontent" base="~/@Model.Author.UserName/@Model.Id" <div markdown="@Model.bcontent" base="~/@Model.Id"
site="SiteSettings.Value"></div> site="SiteSettings.Value"></div>
</dd> </dd>
<dt> <dt>
@ -27,12 +39,7 @@
<dd> <dd>
@Html.DisplayFor(model => model.modified) @Html.DisplayFor(model => model.modified)
</dd> </dd>
<dt>
@Html.DisplayNameFor(model => model.photo)
</dt>
<dd>
@Html.DisplayFor(model => model.photo)
</dd>
<dt> <dt>
@Html.DisplayNameFor(model => model.posted) @Html.DisplayNameFor(model => model.posted)
</dt> </dt>
@ -45,12 +52,7 @@
<dd> <dd>
@Html.DisplayFor(model => model.rate) @Html.DisplayFor(model => model.rate)
</dd> </dd>
<dt>
@Html.DisplayNameFor(model => model.title)
</dt>
<dd>
@Html.DisplayFor(model => model.title)
</dd>
<dt> <dt>
@Html.DisplayNameFor(model => model.visible) @Html.DisplayNameFor(model => model.visible)
</dt> </dt>

View File

@ -182,8 +182,9 @@ editorcontenu.on('text-change',function(delta,source){
</div> </div>
</form> </form>
<div>
</div>
<div > <div >
<form id="postfiles" class="dropzone" method="post" enctype="multipart/form-data"> <form id="postfiles" class="dropzone" method="post" enctype="multipart/form-data">
<div class="fallback"> <div class="fallback">

View File

@ -41,7 +41,7 @@
@item.Author?.UserName @item.Author?.UserName
</td> </td>
<td> <td>
@Html.DisplayFor(modelItem => item.title) <markdown>@item.title</markdown>
</td> </td>
<td> <td>
@Html.DisplayFor(modelItem => item.modified) @Html.DisplayFor(modelItem => item.modified)

View File

@ -68,7 +68,6 @@
@foreach (var description in Model.ExternalProviders) { @foreach (var description in Model.ExternalProviders) {
<form action="/signin" method="post"> <form action="/signin" method="post">
<input type="hidden" name="Provider" value="@description.AuthenticationScheme" /> <input type="hidden" name="Provider" value="@description.AuthenticationScheme" />
<input type="hidden" name="ReturnUrl" value="@Model.ReturnUrl" />
<input type="hidden" name="AfterLoginRedirectUrl" value="@Model.AfterLoginRedirectUrl" /> <input type="hidden" name="AfterLoginRedirectUrl" value="@Model.AfterLoginRedirectUrl" />
<button class="btn btn-lg btn-success" type="submit">@SR["Connect using"] @description.DisplayName</button> <button class="btn btn-lg btn-success" type="submit">@SR["Connect using"] @description.DisplayName</button>
@Html.AntiForgeryToken() @Html.AntiForgeryToken()

View File

@ -14,6 +14,7 @@ namespace Yavsc.WebApi.Controllers
[Authorize,Route("~/api/account")] [Authorize,Route("~/api/account")]
public class ApiAccountController : Controller public class ApiAccountController : Controller
{ {
private UserManager<ApplicationUser> _userManager; private UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager; private readonly SignInManager<ApplicationUser> _signInManager;

View File

@ -0,0 +1,37 @@
using System.Linq;
using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Mvc;
using Microsoft.Extensions.Logging;
using Yavsc.Models;
public class GCMController : Controller {
ILogger _logger;
ApplicationDbContext _context;
public GCMController (ApplicationDbContext context,
ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<GCMController>();
_context = context;
}
[Authorize]
public void Register (GoogleCloudMobileDeclaration declaration)
{
if (_context.GCMDevices.Any(d => d.RegistrationId == declaration.RegistrationId))
{
var alreadyRegisteredDevice = _context.GCMDevices.FirstOrDefault(d => d.RegistrationId == declaration.RegistrationId);
// Assert alreadyRegisteredDevice != null
if (alreadyRegisteredDevice != declaration) {
_context.GCMDevices.Update(declaration);
_context.SaveChanges();
} // else nothing to do.
}
else
{
_context.GCMDevices.Add(declaration);
_context.SaveChanges();
}
}
}

View File

@ -70,10 +70,11 @@ public class BlogViewHandler : AuthorizationHandler<ViewRequirement, Blog>
context.Succeed(requirement); context.Succeed(requirement);
else if (context.User.Identity.IsAuthenticated) else if (context.User.Identity.IsAuthenticated)
if (resource.AuthorId == context.User.GetUserId()) if (resource.AuthorId == context.User.GetUserId())
context.Succeed(requirement); context.Succeed(requirement);
// TODO else if (resource.Circles && context.User belongs to else if (resource.visible)
// TODO && ( resource.Circles == null || context.User belongs to resource.Circles )
context.Succeed(requirement);
} }
} }
public class CommandViewHandler : AuthorizationHandler<ViewRequirement, Command> public class CommandViewHandler : AuthorizationHandler<ViewRequirement, Command>
@ -137,4 +138,6 @@ public class BlogViewHandler : AuthorizationHandler<ViewRequirement, Blog>
} }
} }
} }

View File

@ -24,8 +24,8 @@ namespace Yavsc
public const string AdminGroupName = "Administrator"; public const string AdminGroupName = "Administrator";
public const string BlogModeratorGroupName = "Moderator"; public const string BlogModeratorGroupName = "Moderator";
public const string FrontOfficeGroupName = "FrontOffice"; public const string FrontOfficeGroupName = "FrontOffice";
public const string UserBlogFilesDir= "Blog";
public const string UserBillsFilesDir= "Bills"; public const string UserBillsFilesDir= "Bills";
public const string UserFilesDir = "UserFiles";
public const string GCMNotificationUrl = "https://gcm-http.googleapis.com/gcm/send"; public const string GCMNotificationUrl = "https://gcm-http.googleapis.com/gcm/send";
private static readonly string[] GoogleScopes = { "openid", "profile", "email" }; private static readonly string[] GoogleScopes = { "openid", "profile", "email" };

View File

@ -18,8 +18,7 @@ using Yavsc.ViewModels.Account;
namespace Yavsc.Controllers namespace Yavsc.Controllers
{ {
[AllowAnonymous] [ServiceFilter(typeof(LanguageActionFilter)),AllowAnonymous]
[ServiceFilter(typeof(LanguageActionFilter))]
public class AccountController : Controller public class AccountController : Controller
{ {
private readonly UserManager<ApplicationUser> _userManager; private readonly UserManager<ApplicationUser> _userManager;
@ -138,6 +137,7 @@ namespace Yavsc.Controllers
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
public async Task<IActionResult> LogOff(string returnUrl = null) public async Task<IActionResult> LogOff(string returnUrl = null)
{ {
await HttpContext.Authentication.SignOutAsync("ServerCookie");
await _signInManager.SignOutAsync(); await _signInManager.SignOutAsync();
_logger.LogInformation(4, "User logged out."); _logger.LogInformation(4, "User logged out.");
if (returnUrl==null) return RedirectToAction(nameof(HomeController.Index), "Home"); if (returnUrl==null) return RedirectToAction(nameof(HomeController.Index), "Home");

View File

@ -14,8 +14,7 @@ using Microsoft.Extensions.OptionsModel;
namespace Yavsc.Controllers namespace Yavsc.Controllers
{ {
[ServiceFilter(typeof(LanguageActionFilter)), [ServiceFilter(typeof(LanguageActionFilter))]
AllowAnonymous]
public class BlogspotController : Controller public class BlogspotController : Controller
{ {
ILogger _logger; ILogger _logger;
@ -40,31 +39,30 @@ namespace Yavsc.Controllers
public IActionResult Index(string id) public IActionResult Index(string id)
{ {
if (!string.IsNullOrEmpty(id)) if (!string.IsNullOrEmpty(id))
return UserPosts(id); return UserPosts(id);
return View(_context.Blogspot.Include( return View(_context.Blogspot.Include(
b=>b.Author b => b.Author
).Where(p=>p.visible)); ).Where(p => p.visible));
} }
[Route("/Title/{id?}")] [Route("/Title/{id?}")]
public IActionResult Title(string id) public IActionResult Title(string id)
{ {
return View("Index", _context.Blogspot.Include( return View("Index", _context.Blogspot.Include(
b=>b.Author b => b.Author
).Where(x=>x.title==id).ToList()); ).Where(x => x.title == id).ToList());
} }
[Route("/Blog/{id?}")] [Route("/Blog/{id?}")]
public IActionResult UserPosts(string id) public IActionResult UserPosts(string id)
{ {
if (User.IsSignedIn()) if (User.IsSignedIn())
return View("Index", _context.Blogspot.Include( return View("Index", _context.Blogspot.Include(
b=>b.Author b => b.Author
).Where(x=>x.Author.UserName==id).ToList()); ).Where(x => x.Author.UserName == id).ToList());
return View("Index", _context.Blogspot.Include( return View("Index", _context.Blogspot.Include(
b=>b.Author b => b.Author
).Where(x=>x.Author.UserName==id && x.visible).ToList()); ).Where(x => x.Author.UserName == id && x.visible).ToList());
} }
// GET: Blog/Details/5 // GET: Blog/Details/5
public IActionResult Details(long? id) public IActionResult Details(long? id)
@ -75,7 +73,7 @@ namespace Yavsc.Controllers
} }
Blog blog = _context.Blogspot.Include( Blog blog = _context.Blogspot.Include(
b=>b.Author b => b.Author
).Single(m => m.Id == id); ).Single(m => m.Id == id);
if (blog == null) if (blog == null)
{ {
@ -86,19 +84,21 @@ namespace Yavsc.Controllers
} }
// GET: Blog/Create // GET: Blog/Create
[Authorize("Authenticated")]
public IActionResult Create() public IActionResult Create()
{ {
return View( new Blog { AuthorId = User.GetUserId() } ); return View();
} }
// POST: Blog/Create // POST: Blog/Create
[HttpPost] [HttpPost, Authorize("Authenticated"), ValidateAntiForgeryToken]
[ValidateAntiForgeryToken,Authorize]
public IActionResult Create(Blog blog) public IActionResult Create(Blog blog)
{ {
blog.modified = blog.posted = DateTime.Now; blog.modified = blog.posted = DateTime.Now;
blog.rate = 0; blog.rate = 0;
blog.AuthorId = User.GetUserId();
_logger.LogWarning($"Post from: {blog.AuthorId}");
ModelState.ClearValidationState("AuthorId");
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
blog.posted = DateTime.Now; blog.posted = DateTime.Now;
@ -106,10 +106,10 @@ namespace Yavsc.Controllers
_context.SaveChanges(); _context.SaveChanges();
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
_logger.LogWarning("Invalid Blog entry ..."); _logger.LogWarning("Invalid Blog posted ...");
return View(blog); return View(blog);
} }
[Authorize("Authenticated")]
// GET: Blog/Edit/5 // GET: Blog/Edit/5
public async Task<IActionResult> Edit(long? id) public async Task<IActionResult> Edit(long? id)
{ {
@ -118,7 +118,7 @@ namespace Yavsc.Controllers
return HttpNotFound(); return HttpNotFound();
} }
Blog blog = _context.Blogspot.Include(x=>x.Author).Single(m => m.Id == id); Blog blog = _context.Blogspot.Include(x => x.Author).Single(m => m.Id == id);
if (blog == null) if (blog == null)
{ {
return HttpNotFound(); return HttpNotFound();
@ -135,7 +135,7 @@ namespace Yavsc.Controllers
// POST: Blog/Edit/5 // POST: Blog/Edit/5
[HttpPost] [HttpPost]
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken,Authorize("Authenticated")]
public IActionResult Edit(Blog blog) public IActionResult Edit(Blog blog)
{ {
if (ModelState.IsValid) if (ModelState.IsValid)
@ -146,18 +146,19 @@ namespace Yavsc.Controllers
blog.modified = DateTime.Now; blog.modified = DateTime.Now;
_context.Update(blog); _context.Update(blog);
_context.SaveChanges(); _context.SaveChanges();
ViewData["StatusMessage"]="Post modified"; ViewData["StatusMessage"] = "Post modified";
return RedirectToAction("Index"); return RedirectToAction("Index");
} // TODO Else hit me hard } // TODO Else hit me hard
else { else
ViewData["StatusMessage"]="Access denied ..."; {
ViewData["StatusMessage"] = "Access denied ...";
} }
} }
return View(blog); return View(blog);
} }
// GET: Blog/Delete/5 // GET: Blog/Delete/5
[ActionName("Delete")] [ActionName("Delete"),Authorize("Authenticated")]
public IActionResult Delete(long? id) public IActionResult Delete(long? id)
{ {
if (id == null) if (id == null)
@ -166,7 +167,7 @@ namespace Yavsc.Controllers
} }
Blog blog = _context.Blogspot.Include( Blog blog = _context.Blogspot.Include(
b=>b.Author b => b.Author
).Single(m => m.Id == id); ).Single(m => m.Id == id);
if (blog == null) if (blog == null)
{ {
@ -177,13 +178,14 @@ namespace Yavsc.Controllers
} }
// POST: Blog/Delete/5 // POST: Blog/Delete/5
[HttpPost, ActionName("Delete"), Authorize] [HttpPost, ActionName("Delete"), Authorize("Authenticated")]
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
public IActionResult DeleteConfirmed(long id) public IActionResult DeleteConfirmed(long id)
{ {
Blog blog = _context.Blogspot.Single(m => m.Id == id); Blog blog = _context.Blogspot.Single(m => m.Id == id);
var auth = _authorizationService.AuthorizeAsync(User, blog, new EditRequirement()); var auth = _authorizationService.AuthorizeAsync(User, blog, new EditRequirement());
if (auth.Result) { if (auth.Result)
{
_context.Blogspot.Remove(blog); _context.Blogspot.Remove(blog);
_context.SaveChanges(); _context.SaveChanges();
} }

View File

@ -9,7 +9,7 @@ using Yavsc.Models.Booking;
namespace Yavsc.Controllers namespace Yavsc.Controllers
{ {
[ServiceFilter(typeof(LanguageActionFilter)), AllowAnonymous, [ServiceFilter(typeof(LanguageActionFilter)),
Route("do")] Route("do")]
public class FrontOfficeController : Controller public class FrontOfficeController : Controller
{ {

View File

@ -27,7 +27,7 @@ namespace Yavsc.Controllers
ApplicationDbContext _context; ApplicationDbContext _context;
UserManager<ApplicationUser> _userManager; UserManager<ApplicationUser> _userManager;
ILogger _logger; ILogger _logger;
private readonly SignInManager<ApplicationUser> _signInManager; private readonly SignInManager<ApplicationUser> _signInManager;
private TokenAuthOptions _tokenOptions; private TokenAuthOptions _tokenOptions;
@ -46,13 +46,15 @@ ILogger _logger;
[HttpGet("~/signin")] [HttpGet("~/signin")]
public ActionResult SignIn(string returnUrl = null) { public ActionResult SignIn(string returnUrl = null, string target = null)
{
// Note: the "returnUrl" parameter corresponds to the endpoint the user agent // Note: the "returnUrl" parameter corresponds to the endpoint the user agent
// will be redirected to after a successful authentication and not // will be redirected to after a successful authentication and not
// the redirect_uri of the requesting client application. // the redirect_uri of the requesting client application.
return View("SignIn", new LoginViewModel return View("SignIn", new LoginViewModel
{ {
ReturnUrl = returnUrl, ReturnUrl = returnUrl,
AfterLoginRedirectUrl = target,
ExternalProviders = HttpContext.GetExternalProviders() ExternalProviders = HttpContext.GetExternalProviders()
}); });
/* Note: When using an external login provider, redirect the query : /* Note: When using an external login provider, redirect the query :
@ -61,49 +63,72 @@ ILogger _logger;
*/ */
} }
[HttpGet("~/authenticate")]
public ActionResult Authenticate(string returnUrl = null)
{
return SignIn("/Account/ExternalLoginCallback",returnUrl);
}
[HttpGet("~/forbidden")]
public ActionResult Forbidden(string returnUrl = null)
{
return SignIn("/Account/ExternalLoginCallback",returnUrl);
}
[HttpPost("~/signin")] [HttpPost("~/signin")]
public IActionResult SignIn( string Provider, string ReturnUrl, string AfterLoginRedirectUrl) { public IActionResult SignIn(string Provider, string ReturnUrl, string AfterLoginRedirectUrl)
{
// Note: the "provider" parameter corresponds to the external // Note: the "provider" parameter corresponds to the external
// authentication provider choosen by the user agent. // authentication provider choosen by the user agent.
if (string.IsNullOrEmpty(Provider)) { if (string.IsNullOrEmpty(Provider))
{
_logger.LogWarning("Provider not specified"); _logger.LogWarning("Provider not specified");
return HttpBadRequest(); return HttpBadRequest();
} }
if (!_signInManager.GetExternalAuthenticationSchemes().Any(x=>x.AuthenticationScheme==Provider)) { if (!_signInManager.GetExternalAuthenticationSchemes().Any(x => x.AuthenticationScheme == Provider))
{
_logger.LogWarning($"Provider not found : {Provider}"); _logger.LogWarning($"Provider not found : {Provider}");
return HttpBadRequest(); return HttpBadRequest();
} }
// Note: the "returnUrl" parameter corresponds to the endpoint the user agent
// will be redirected to after a successful authentication and not
// the redirect_uri of the requesting client application.
if (string.IsNullOrEmpty(ReturnUrl)) {
_logger.LogWarning("ReturnUrl not specified");
return HttpBadRequest();
}
// Instruct the middleware corresponding to the requested external identity // Instruct the middleware corresponding to the requested external identity
// provider to redirect the user agent to its own authorization endpoint. // provider to redirect the user agent to its own authorization endpoint.
// Note: the authenticationScheme parameter must match the value configured in Startup.cs // Note: the authenticationScheme parameter must match the value configured in Startup.cs
// If AfterLoginRedirectUrl is non null, // Note: the "returnUrl" parameter corresponds to the endpoint the user agent
// This is a web interface access, // will be redirected to after a successful authentication and not
// and the wanted redirection // the redirect_uri of the requesting client application.
// after the successfull authentication if (string.IsNullOrEmpty(ReturnUrl))
if (AfterLoginRedirectUrl!=null) { {
ReturnUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = AfterLoginRedirectUrl });
// If AfterLoginRedirectUrl is non null,
// This is a web interface access,
/// Assert (model.ReturnUrl==null)
/// and the wanted redirection
// after the successfull authentication
if (AfterLoginRedirectUrl != null)
{
ReturnUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = AfterLoginRedirectUrl });
}
else
{
_logger.LogWarning("ReturnUrl not specified");
return HttpBadRequest();
}
} }
var properties = _signInManager.ConfigureExternalAuthenticationProperties(Provider, ReturnUrl); var properties = _signInManager.ConfigureExternalAuthenticationProperties(Provider, ReturnUrl);
return new ChallengeResult(Provider, properties); return new ChallengeResult(Provider, properties);
} }
[HttpGet("~/signout"), HttpPost("~/signout")] [HttpGet("~/signout"), HttpPost("~/signout")]
public async Task SignOut() { public async Task SignOut()
{
// Instruct the cookies middleware to delete the local cookie created // Instruct the cookies middleware to delete the local cookie created
// when the user agent is redirected from the external identity provider // when the user agent is redirected from the external identity provider
// after a successful authentication flow (e.g Google or Facebook). // after a successful authentication flow (e.g Google or Facebook).
@ -132,23 +157,27 @@ ILogger _logger;
} }
[HttpGet("~/connect/authorize"), HttpPost("~/connect/authorize")] [HttpGet("~/connect/authorize"), HttpPost("~/connect/authorize")]
public async Task<IActionResult> Authorize(CancellationToken cancellationToken) { public async Task<IActionResult> Authorize(CancellationToken cancellationToken)
{
// Note: when a fatal error occurs during the request processing, an OpenID Connect response // Note: when a fatal error occurs during the request processing, an OpenID Connect response
// is prematurely forged and added to the ASP.NET context by OpenIdConnectServerHandler. // is prematurely forged and added to the ASP.NET context by OpenIdConnectServerHandler.
// You can safely remove this part and let ASOS automatically handle the unrecoverable errors // You can safely remove this part and let ASOS automatically handle the unrecoverable errors
// by switching ApplicationCanDisplayErrors to false in Startup.cs. // by switching ApplicationCanDisplayErrors to false in Startup.cs.
var response = HttpContext.GetOpenIdConnectResponse(); var response = HttpContext.GetOpenIdConnectResponse();
if (response == null) { if (response == null)
_logger.LogError("GetOpenIdConnectResponse is null"); {
_logger.LogError("GetOpenIdConnectResponse is null");
return View("OidcError", response); return View("OidcError", response);
} }
// Extract the authorization request from the ASP.NET environment. // Extract the authorization request from the ASP.NET environment.
var request = HttpContext.GetOpenIdConnectRequest(); var request = HttpContext.GetOpenIdConnectRequest();
if (request == null) { if (request == null)
_logger.LogError("An internal error has occurred, GetOpenIdConnectRequest is null"); {
return View("OidcError", new OpenIdConnectMessage { _logger.LogError("An internal error has occurred, GetOpenIdConnectRequest is null");
return View("OidcError", new OpenIdConnectMessage
{
Error = OpenIdConnectConstants.Errors.ServerError, Error = OpenIdConnectConstants.Errors.ServerError,
ErrorDescription = "An internal error has occurred" ErrorDescription = "An internal error has occurred"
}); });
@ -160,10 +189,13 @@ ILogger _logger;
// To work around this limitation, the OpenID Connect request is automatically saved in the cache and will be // To work around this limitation, the OpenID Connect request is automatically saved in the cache and will be
// restored by the OpenID Connect server middleware after the external authentication process has been completed. // restored by the OpenID Connect server middleware after the external authentication process has been completed.
if (!User.Identities.Any(identity => identity.IsAuthenticated)) { if (!User.Identities.Any(identity => identity.IsAuthenticated))
_logger.LogWarning("new ChallengeResult"); {
return new ChallengeResult(new AuthenticationProperties { _logger.LogWarning("new ChallengeResult");
RedirectUri = Url.Action(nameof(Authorize), new { return new ChallengeResult(new AuthenticationProperties
{
RedirectUri = Url.Action(nameof(Authorize), new
{
request_id = request.GetUniqueIdentifier() request_id = request.GetUniqueIdentifier()
}) })
}); });
@ -174,9 +206,11 @@ ILogger _logger;
// In theory, this null check shouldn't be needed, but a race condition could occur if you // In theory, this null check shouldn't be needed, but a race condition could occur if you
// manually removed the application details from the database after the initial check made by ASOS. // manually removed the application details from the database after the initial check made by ASOS.
var application = await GetApplicationAsync(request.ClientId, cancellationToken); var application = await GetApplicationAsync(request.ClientId, cancellationToken);
if (application == null) { if (application == null)
{
_logger.LogError("Details concerning the calling client application cannot be found in the database"); _logger.LogError("Details concerning the calling client application cannot be found in the database");
return View("OidcError", new OpenIdConnectMessage { return View("OidcError", new OpenIdConnectMessage
{
Error = OpenIdConnectConstants.Errors.InvalidClient, Error = OpenIdConnectConstants.Errors.InvalidClient,
ErrorDescription = "Details concerning the calling client application cannot be found in the database" ErrorDescription = "Details concerning the calling client application cannot be found in the database"
}); });
@ -187,15 +221,19 @@ ILogger _logger;
} }
[Authorize, HttpPost("~/connect/authorize/accept"), ValidateAntiForgeryToken] [Authorize, HttpPost("~/connect/authorize/accept"), ValidateAntiForgeryToken]
public async Task<IActionResult> Accept(CancellationToken cancellationToken) { public async Task<IActionResult> Accept(CancellationToken cancellationToken)
{
var response = HttpContext.GetOpenIdConnectResponse(); var response = HttpContext.GetOpenIdConnectResponse();
if (response != null) { if (response != null)
{
return View("OidcError", response); return View("OidcError", response);
} }
var request = HttpContext.GetOpenIdConnectRequest(); var request = HttpContext.GetOpenIdConnectRequest();
if (request == null) { if (request == null)
return View("OidcError", new OpenIdConnectMessage { {
return View("OidcError", new OpenIdConnectMessage
{
Error = OpenIdConnectConstants.Errors.ServerError, Error = OpenIdConnectConstants.Errors.ServerError,
ErrorDescription = "An internal error has occurred" ErrorDescription = "An internal error has occurred"
}); });
@ -207,7 +245,8 @@ ILogger _logger;
// Copy the claims retrieved from the external identity provider // Copy the claims retrieved from the external identity provider
// (e.g Google, Facebook, a WS-Fed provider or another OIDC server). // (e.g Google, Facebook, a WS-Fed provider or another OIDC server).
foreach (var claim in HttpContext.User.Claims) { foreach (var claim in HttpContext.User.Claims)
{
// Allow ClaimTypes.Name to be added in the id_token. // Allow ClaimTypes.Name to be added in the id_token.
// ClaimTypes.NameIdentifier is automatically added, even if its // ClaimTypes.NameIdentifier is automatically added, even if its
// destination is not defined or doesn't include "id_token". // destination is not defined or doesn't include "id_token".
@ -218,7 +257,8 @@ ILogger _logger;
OpenIdConnectConstants.Destinations.IdentityToken); OpenIdConnectConstants.Destinations.IdentityToken);
} }
*/ */
if (claim.Type == ClaimTypes.Name) { if (claim.Type == ClaimTypes.Name)
{
claim.WithDestination("code"); claim.WithDestination("code");
claim.WithDestination("id_token"); claim.WithDestination("id_token");
} }
@ -226,8 +266,10 @@ ILogger _logger;
} }
var application = await GetApplicationAsync(request.ClientId, cancellationToken); var application = await GetApplicationAsync(request.ClientId, cancellationToken);
if (application == null) { if (application == null)
return View("OidcError", new OpenIdConnectMessage { {
return View("OidcError", new OpenIdConnectMessage
{
Error = OpenIdConnectConstants.Errors.InvalidClient, Error = OpenIdConnectConstants.Errors.InvalidClient,
ErrorDescription = "Details concerning the calling client application cannot be found in the database" ErrorDescription = "Details concerning the calling client application cannot be found in the database"
}); });
@ -238,7 +280,7 @@ ILogger _logger;
// the whole delegation chain from the resource server (see ResourceController.cs). // the whole delegation chain from the resource server (see ResourceController.cs).
identity.Actor = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme); identity.Actor = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.Actor.AddClaim(ClaimTypes.NameIdentifier, application.ApplicationID); identity.Actor.AddClaim(ClaimTypes.NameIdentifier, application.ApplicationID);
identity.Actor.AddClaim(ClaimTypes.Name, application.DisplayName,"code id_token"); identity.Actor.AddClaim(ClaimTypes.Name, application.DisplayName, "code id_token");
var properties = new AuthenticationProperties(); var properties = new AuthenticationProperties();
@ -271,15 +313,19 @@ ILogger _logger;
} }
[Authorize, HttpPost("~/connect/authorize/deny"), ValidateAntiForgeryToken] [Authorize, HttpPost("~/connect/authorize/deny"), ValidateAntiForgeryToken]
public IActionResult Deny(CancellationToken cancellationToken) { public IActionResult Deny(CancellationToken cancellationToken)
{
var response = HttpContext.GetOpenIdConnectResponse(); var response = HttpContext.GetOpenIdConnectResponse();
if (response != null) { if (response != null)
{
return View("OidcError", response); return View("OidcError", response);
} }
var request = HttpContext.GetOpenIdConnectRequest(); var request = HttpContext.GetOpenIdConnectRequest();
if (request == null) { if (request == null)
return View("OidcError", new OpenIdConnectMessage { {
return View("OidcError", new OpenIdConnectMessage
{
Error = OpenIdConnectConstants.Errors.ServerError, Error = OpenIdConnectConstants.Errors.ServerError,
ErrorDescription = "An internal error has occurred" ErrorDescription = "An internal error has occurred"
}); });
@ -289,7 +335,8 @@ ILogger _logger;
// Notify AspNet.Security.OpenIdConnect.Server that the authorization grant has been denied. // Notify AspNet.Security.OpenIdConnect.Server that the authorization grant has been denied.
// Note: OpenIdConnectServerHandler will automatically take care of redirecting // Note: OpenIdConnectServerHandler will automatically take care of redirecting
// the user agent to the client application using the appropriate response_mode. // the user agent to the client application using the appropriate response_mode.
HttpContext.SetOpenIdConnectResponse(new OpenIdConnectMessage { HttpContext.SetOpenIdConnectResponse(new OpenIdConnectMessage
{
Error = "access_denied", Error = "access_denied",
ErrorDescription = "The authorization grant has been denied by the resource owner", ErrorDescription = "The authorization grant has been denied by the resource owner",
RedirectUri = request.RedirectUri, RedirectUri = request.RedirectUri,
@ -301,9 +348,11 @@ ILogger _logger;
} }
[HttpGet("~/connect/logout")] [HttpGet("~/connect/logout")]
public async Task<ActionResult> Logout() { public async Task<ActionResult> Logout()
{
var response = HttpContext.GetOpenIdConnectResponse(); var response = HttpContext.GetOpenIdConnectResponse();
if (response != null) { if (response != null)
{
_logger.LogError("GetOpenIdConnectResponse is null"); _logger.LogError("GetOpenIdConnectResponse is null");
return View("OidcError", response); return View("OidcError", response);
} }
@ -314,9 +363,11 @@ ILogger _logger;
var identity = await HttpContext.Authentication.AuthenticateAsync(OpenIdConnectServerDefaults.AuthenticationScheme); var identity = await HttpContext.Authentication.AuthenticateAsync(OpenIdConnectServerDefaults.AuthenticationScheme);
var request = HttpContext.GetOpenIdConnectRequest(); var request = HttpContext.GetOpenIdConnectRequest();
if (request == null) { if (request == null)
{
_logger.LogError("An internal error has occurred"); _logger.LogError("An internal error has occurred");
return View("OidcError", new OpenIdConnectMessage { return View("OidcError", new OpenIdConnectMessage
{
Error = OpenIdConnectConstants.Errors.ServerError, Error = OpenIdConnectConstants.Errors.ServerError,
ErrorDescription = "An internal error has occurred" ErrorDescription = "An internal error has occurred"
}); });
@ -327,7 +378,8 @@ ILogger _logger;
[HttpPost("~/connect/logout")] [HttpPost("~/connect/logout")]
[ValidateAntiForgeryToken] [ValidateAntiForgeryToken]
public async Task Logout(CancellationToken cancellationToken) { public async Task Logout(CancellationToken cancellationToken)
{
// Instruct the cookies middleware to delete the local cookie created // Instruct the cookies middleware to delete the local cookie created
// when the user agent is redirected from the external identity provider // when the user agent is redirected from the external identity provider
// after a successful authentication flow (e.g Google or Facebook). // after a successful authentication flow (e.g Google or Facebook).
@ -348,7 +400,7 @@ ILogger _logger;
where application.ApplicationID == identifier where application.ApplicationID == identifier
select application).SingleOrDefaultAsync(cancellationToken); select application).SingleOrDefaultAsync(cancellationToken);
} }
private async Task<ApplicationUser> GetCurrentUserAsync() private async Task<ApplicationUser> GetCurrentUserAsync()
{ {
return await _userManager.FindByIdAsync(HttpContext.User.GetUserId()); return await _userManager.FindByIdAsync(HttpContext.User.GetUserId());
} }

View File

@ -108,7 +108,7 @@ namespace Yavsc.Helpers
var content = await GetContent(output); var content = await GetContent(output);
var markdown = content; var markdown = content;
var basePath = Base?.StartsWith("~") ?? false ? var basePath = Base?.StartsWith("~") ?? false ?
"/"+Startup.UserFilesDirName + Constants.UserFilesDir +
Base.Substring(1) : Base; Base.Substring(1) : Base;
var html = Markdown(markdown, basePath); var html = Markdown(markdown, basePath);
output.Content.SetHtmlContent(html ?? ""); output.Content.SetHtmlContent(html ?? "");

View File

@ -71,6 +71,12 @@ namespace Yavsc.Models
/// </summary> /// </summary>
/// <returns>tokens</returns> /// <returns>tokens</returns>
public DbSet<OAuth2Tokens> Tokens { get; set; } public DbSet<OAuth2Tokens> Tokens { get; set; }
/// <summary>
/// References all declared external GCM devices
/// </summary>
/// <returns></returns>
public DbSet<GoogleCloudMobileDeclaration> GCMDevices { get; set; }
public Task ClearTokensAsync() public Task ClearTokensAsync()
{ {

View File

@ -9,7 +9,28 @@ public class GoogleCloudMobileDeclaration {
public string DeviceOwnerId { get; set; } public string DeviceOwnerId { get; set; }
public string Name { get; set; }
[ForeignKeyAttribute("DeviceOwnerId")] [ForeignKeyAttribute("DeviceOwnerId")]
public virtual ApplicationUser DeviceOwner { get; set; } public virtual ApplicationUser DeviceOwner { get; set; }
// override object.Equals
public override bool Equals (object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
var other = obj as GoogleCloudMobileDeclaration;
return RegistrationId == other.RegistrationId
&& Name == other.Name;
}
// override object.GetHashCode
public override int GetHashCode()
{
return (RegistrationId+Name).GetHashCode();
}
} }

View File

@ -29,7 +29,8 @@ namespace Yavsc.Services
/// <returns>a MessageWithPayloadResponse, /// <returns>a MessageWithPayloadResponse,
/// <c>bool somethingsent = (response.failure == 0 &amp;&amp; response.success > 0)</c> /// <c>bool somethingsent = (response.failure == 0 &amp;&amp; response.success > 0)</c>
/// </returns> /// </returns>
public async Task<MessageWithPayloadResponse> NotifyAsync(GoogleAuthSettings googleSettings, string registrationId, YaEvent ev) public async Task<MessageWithPayloadResponse>
NotifyAsync(GoogleAuthSettings googleSettings, string registrationId, YaEvent ev)
{ {
MessageWithPayloadResponse response; MessageWithPayloadResponse response;
try try

View File

@ -10,6 +10,7 @@ using System.Web.Optimization;
using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Extensions;
using Microsoft.AspNet.Authentication; using Microsoft.AspNet.Authentication;
using Microsoft.AspNet.Authentication.Cookies; using Microsoft.AspNet.Authentication.Cookies;
using Microsoft.AspNet.Authentication.JwtBearer;
using Microsoft.AspNet.Authentication.OAuth; using Microsoft.AspNet.Authentication.OAuth;
using Microsoft.AspNet.Authorization; using Microsoft.AspNet.Authorization;
using Microsoft.AspNet.Builder; using Microsoft.AspNet.Builder;
@ -18,6 +19,7 @@ using Microsoft.AspNet.Diagnostics;
using Microsoft.AspNet.FileProviders; using Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Localization; using Microsoft.AspNet.Localization;
@ -72,7 +74,7 @@ namespace Yavsc
public class Startup public class Startup
{ {
public static string UserFilesDirName { get; private set; } public static string UserFilesDirName { get; private set; }
private RsaSecurityKey key; private RsaSecurityKey key;
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
@ -159,6 +161,16 @@ namespace Yavsc
{ {
options.SignInScheme = "ServerCookie"; options.SignInScheme = "ServerCookie";
}); });
services.Configure<TokenAuthOptions>(
to =>
{
to.Audience = Configuration["Site:Audience"];
to.Issuer = Configuration["Site:Authority"];
to.SigningCredentials =
new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature);
}
);
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<SiteSettings>), typeof(OptionsManager<SiteSettings>))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions<SiteSettings>), typeof(OptionsManager<SiteSettings>)));
services.Add(ServiceDescriptor.Singleton(typeof(IOptions<SmtpSettings>), typeof(OptionsManager<SmtpSettings>))); services.Add(ServiceDescriptor.Singleton(typeof(IOptions<SmtpSettings>), typeof(OptionsManager<SmtpSettings>)));
@ -179,11 +191,7 @@ namespace Yavsc
new DirectoryInfo(Configuration["DataProtection:Keys:Dir"])); new DirectoryInfo(Configuration["DataProtection:Keys:Dir"]));
}); });
services.AddAuthentication(options => services.AddAuthentication();
{
options.SignInScheme = "ServerCookie";
}
);
// Add framework services. // Add framework services.
services.AddEntityFramework() services.AddEntityFramework()
.AddNpgsql() .AddNpgsql()
@ -195,11 +203,15 @@ namespace Yavsc
{ {
option.User.AllowedUserNameCharacters += " "; option.User.AllowedUserNameCharacters += " ";
option.User.RequireUniqueEmail = true; option.User.RequireUniqueEmail = true;
option.Cookies.ApplicationCookie.LoginPath = "/authenticate";
option.Cookies.ApplicationCookie.LogoutPath = "/signout";
option.Cookies.ApplicationCookie.AccessDeniedPath = "/forbidden"; // TODO /forbidden
// FIXME option.Cookies.ApplicationCookie.ReturnUrlParameter = "target";
} }
).AddEntityFrameworkStores<ApplicationDbContext>() ).AddEntityFrameworkStores<ApplicationDbContext>()
.AddTokenProvider<EmailTokenProvider<ApplicationUser>>(Constants.EMailFactor) .AddTokenProvider<EmailTokenProvider<ApplicationUser>>(Constants.EMailFactor)
.AddTokenProvider<UserTokenProvider>(Constants.DefaultFactor) .AddTokenProvider<UserTokenProvider>(Constants.DefaultFactor)
; ;
// .AddTokenProvider<UserTokenProvider>(Constants.SMSFactor) // .AddTokenProvider<UserTokenProvider>(Constants.SMSFactor)
// //
@ -229,14 +241,16 @@ namespace Yavsc
{ {
policy.RequireClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", Constants.AdminGroupName); policy.RequireClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", Constants.AdminGroupName);
}); });
options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName)); options.AddPolicy("FrontOffice", policy => policy.RequireRole(Constants.FrontOfficeGroupName));
options.AddPolicy("API", policy => options.AddPolicy("API", policy =>
{ {
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
policy.RequireClaim(OpenIdConnectConstants.Claims.Scope, "api-resource-controller"); policy.RequireClaim(OpenIdConnectConstants.Claims.Scope, "api-resource-controller");
}); });
// options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456")); // options.AddPolicy("EmployeeId", policy => policy.RequireClaim("EmployeeId", "123", "456"));
// options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement())); // options.AddPolicy("BuildingEntry", policy => policy.Requirements.Add(new OfficeEntryRequirement()));
options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
}); });
services.AddSingleton<IAuthorizationHandler, HasBadgeHandler>(); services.AddSingleton<IAuthorizationHandler, HasBadgeHandler>();
@ -341,6 +355,7 @@ namespace Yavsc
} }
} }
var googleOptions = new GoogleOptions var googleOptions = new GoogleOptions
{ {
ClientId = Configuration["Authentication:Google:ClientId"], ClientId = Configuration["Authentication:Google:ClientId"],
@ -370,14 +385,54 @@ namespace Yavsc
googleOptions.Scope.Add("https://www.googleapis.com/auth/calendar"); googleOptions.Scope.Add("https://www.googleapis.com/auth/calendar");
app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear()); app.UseIISPlatformHandler(options =>
{
options.AuthenticationDescriptions.Clear();
options.AutomaticAuthentication = true;
});
app.UseFileServer(new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(
Path.Combine(
env.WebRootPath,
// TODO: add a ressource serveur id here,
// or remove the blog entry id usage, to use the userid instead
// and an user defined optional subdir.
siteSettings.Value.UserFiles.DirName
)),
RequestPath = new PathString("/" + siteSettings.Value.UserFiles.DirName),
EnableDirectoryBrowsing = false
});
app.UseStaticFiles().UseWebSockets(); app.UseStaticFiles().UseWebSockets();
app.UseRequestLocalization(localizationOptions.Value, (RequestCulture)new RequestCulture((string)"fr"));
app.UseIdentity(); app.UseIdentity();
app.UseOpenIdConnectServer(options =>
{
options.Provider = new AuthorizationProvider(loggerFactory);
// Register the certificate used to sign the JWT tokens.
/* options.SigningCredentials.AddCertificate(
assembly: typeof(Startup).GetTypeInfo().Assembly,
resource: "Mvc.Server.Certificate.pfx",
password: "Owin.Security.OpenIdConnect.Server"); */
// options.SigningCredentials.AddKey(key);
// Note: see AuthorizationController.cs for more
// information concerning ApplicationCanDisplayErrors.
options.ApplicationCanDisplayErrors = true;
options.AllowInsecureHttp = true;
options.AutomaticChallenge = true;
options.AuthorizationEndpointPath = new PathString("/connect/authorize");
options.TokenEndpointPath = new PathString("/connect/authorize/accept");
options.UseSlidingExpiration = true;
options.AllowInsecureHttp = true;
options.AuthenticationScheme = "oidc"; // was = OpenIdConnectDefaults.AuthenticationScheme;
options.LogoutEndpointPath = new PathString("/connect/logout");
/* options.ValidationEndpointPath = new PathString("/connect/introspect"); */
});
app.UseWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")), branch => app.UseWhen(context => context.Request.Path.StartsWithSegments(new PathString("/api")), branch =>
{ {
branch.UseJwtBearerAuthentication(options => branch.UseJwtBearerAuthentication(options =>
@ -401,7 +456,7 @@ namespace Yavsc
options.AuthenticationScheme = "ServerCookie"; options.AuthenticationScheme = "ServerCookie";
options.CookieName = CookieAuthenticationDefaults.CookiePrefix + "ServerCookie"; options.CookieName = CookieAuthenticationDefaults.CookiePrefix + "ServerCookie";
options.ExpireTimeSpan = TimeSpan.FromMinutes(5); options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
options.LoginPath = new PathString("/signin"); options.LoginPath = new PathString("/authenticate");
options.LogoutPath = new PathString("/signout"); options.LogoutPath = new PathString("/signout");
}); });
@ -417,45 +472,10 @@ namespace Yavsc
}); });
}); });
var authProvider = new AuthorizationProvider(loggerFactory);
app.UseOpenIdConnectServer(options =>
{
options.Provider = authProvider;
// Register the certificate used to sign the JWT tokens.
/* options.SigningCredentials.AddCertificate(
assembly: typeof(Startup).GetTypeInfo().Assembly,
resource: "Mvc.Server.Certificate.pfx",
password: "Owin.Security.OpenIdConnect.Server"); */
// options.SigningCredentials.AddKey(key);
// Note: see AuthorizationController.cs for more
// information concerning ApplicationCanDisplayErrors.
options.ApplicationCanDisplayErrors = true;
options.AllowInsecureHttp = true;
options.AutomaticChallenge = true;
options.AuthorizationEndpointPath = new PathString("/connect/authorize");
options.TokenEndpointPath = new PathString("/connect/authorize/accept");
options.UseSlidingExpiration = true;
options.AllowInsecureHttp = true;
options.AuthenticationScheme = "oidc"; // was = OpenIdConnectDefaults.AuthenticationScheme;
options.LogoutEndpointPath = new PathString("/connect/logout");
/* options.ValidationEndpointPath = new PathString("/connect/introspect"); */
});
app.UseFileServer(new FileServerOptions()
{
FileProvider = new PhysicalFileProvider(
Path.Combine(
env.WebRootPath,
siteSettings.Value.UserFiles.DirName
)),
RequestPath = new PathString("/"+siteSettings.Value.UserFiles.DirName),
EnableDirectoryBrowsing = false
});
app.UseRequestLocalization(localizationOptions.Value, (RequestCulture)new RequestCulture((string)"fr"));
/* Generic OAuth (here GitHub): options.Notifications = new OAuthAuthenticationNotifications /* Generic OAuth (here GitHub): options.Notifications = new OAuthAuthenticationNotifications
{ {
@ -501,12 +521,12 @@ namespace Yavsc
} }
}; */ }; */
app.UseMvc(routes => app.UseMvc(routes =>
{ {
/* routes.MapRoute(
name: "wf",
template: "do/{action=Index}/{id?}",
defaults: new { controller = "WorkFlow" }); */
routes.MapRoute( routes.MapRoute(
name: "default", name: "default",
template: "{controller=Home}/{action=Index}/{id?}"); template: "{controller=Home}/{action=Index}/{id?}");

View File

@ -13,9 +13,14 @@ textarea {
max-width: 280px; max-width: 280px;
} }
.navbar-reac { /* Set widths on image and video, since otherwise they use their native resolution */
img,
video {
max-width: 100%;
} }
/* .navbar-reac */
/* Carousel */ /* Carousel */
.carousel-caption p { .carousel-caption p {
font-family: "jubilat"; font-family: "jubilat";