Merge pull request #37 from sbwalker/master
Integrated AuthenticatedStateProvider using .NET Core Identity. Solution works fine when running in client-side mode ( Wasm build profile ) but does not set cookie in server-side mode.
This commit is contained in:
commit
61171e4844
|
@ -1,9 +1,11 @@
|
|||
@using Oqtane.Shared
|
||||
@using Oqtane.Client.Shared
|
||||
|
||||
<CascadingValue Value="@PageState">
|
||||
<SiteRouter OnStateChange="@ChangeState" />
|
||||
</CascadingValue>
|
||||
<CascadingAuthenticationState>
|
||||
<CascadingValue Value="@PageState">
|
||||
<SiteRouter OnStateChange="@ChangeState" />
|
||||
</CascadingValue>
|
||||
</CascadingAuthenticationState>
|
||||
|
||||
@code {
|
||||
private PageState PageState { get; set; }
|
||||
|
|
|
@ -1,45 +1,56 @@
|
|||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Oqtane.Shared
|
||||
@using Oqtane.Modules
|
||||
@using Microsoft.JSInterop
|
||||
@using Oqtane.Models
|
||||
@using Oqtane.Services
|
||||
@using Oqtane.Client.Modules.Controls
|
||||
@using Oqtane.Providers
|
||||
@inherits ModuleBase
|
||||
@inject IUriHelper UriHelper
|
||||
@inject IJSRuntime jsRuntime
|
||||
@inject IUserService UserService
|
||||
@inject IUserService UserService
|
||||
@inject ServerAuthenticationStateProvider AuthStateProvider
|
||||
|
||||
<div class="container">
|
||||
@((MarkupString)Message)
|
||||
<div class="form-group">
|
||||
<label for="Username" class="control-label">Username: </label>
|
||||
<input type="text" name="Username" class="form-control" placeholder="Username" @bind="@Username" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Password" class="control-label">Password: </label>
|
||||
<input type="password" name="Password" class="form-control" placeholder="Password" @bind="@Password" />
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" @onclick="@Login">Login</button>
|
||||
<NavLink class="btn btn-secondary" href="/">Cancel</NavLink>
|
||||
</div>
|
||||
<AuthorizeView>
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
You are already logged in
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<div class="container">
|
||||
@((MarkupString)Message)
|
||||
<div class="form-group">
|
||||
<label for="Username" class="control-label">Username: </label>
|
||||
<input type="text" name="Username" class="form-control" placeholder="Username" @bind="@Username" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="Password" class="control-label">Password: </label>
|
||||
<input type="password" name="Password" class="form-control" placeholder="Password" @bind="@Password" />
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" @onclick="@Login">Login</button>
|
||||
<NavLink class="btn btn-secondary" href="/">Cancel</NavLink>
|
||||
</div>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } }
|
||||
|
||||
public string Message { get; set; } = "<div class=\"alert alert-info\" role=\"alert\">Use host/host For Demo Access</div>";
|
||||
public string Message { get; set; } = "<div class=\"alert alert-info\" role=\"alert\">Use host/password For Demo Access</div>";
|
||||
public string Username { get; set; } = "";
|
||||
public string Password { get; set; } = "";
|
||||
|
||||
private async Task Login()
|
||||
{
|
||||
List<User> users = await UserService.GetUsersAsync();
|
||||
User user = users.Where(item => item.Username == Username).FirstOrDefault();
|
||||
User user = new User();
|
||||
user.Username = Username;
|
||||
user.Password = Password;
|
||||
user = await UserService.LoginUserAsync(user);
|
||||
if (user != null)
|
||||
{
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SetCookie("user", user.UserId.ToString(), 7);
|
||||
UriHelper.NavigateTo(NavigateUrl(""), true);
|
||||
AuthStateProvider.NotifyAuthenticationChanged();
|
||||
UriHelper.NavigateTo(NavigateUrl(""));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Oqtane.Shared
|
||||
@using Oqtane.Modules
|
||||
@using Microsoft.JSInterop
|
||||
@using Oqtane.Client.Modules.Controls
|
||||
@using Oqtane.Models
|
||||
@using Oqtane.Services
|
||||
@inherits ModuleBase
|
||||
@inject IUriHelper UriHelper
|
||||
@inject IJSRuntime jsRuntime
|
||||
|
||||
@inject IUserService UserService
|
||||
|
||||
<div class="container">
|
||||
<div class="form-group">
|
||||
|
@ -17,13 +15,25 @@
|
|||
<label for="Password" class="control-label">Password: </label>
|
||||
<input type="password" name="Password" class="form-control" placeholder="Password" @bind="@Password" />
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary">Register</button>
|
||||
<button type="button" class="btn btn-primary" @onclick="@RegisterUser">Register</button>
|
||||
<NavLink class="btn btn-secondary" href="/">Cancel</NavLink>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } }
|
||||
public override SecurityAccessLevelEnum SecurityAccessLevel { get { return SecurityAccessLevelEnum.Anonymous; } }
|
||||
|
||||
public string Username { get; set; } = "";
|
||||
public string Password { get; set; } = "";
|
||||
public string Username { get; set; } = "";
|
||||
public string Password { get; set; } = "";
|
||||
|
||||
private async Task RegisterUser()
|
||||
{
|
||||
User user = new User();
|
||||
user.Username = Username;
|
||||
user.DisplayName = Username;
|
||||
user.Roles = "Administrators;";
|
||||
user.IsSuperUser = false;
|
||||
user.Password = Password;
|
||||
await UserService.AddUserAsync(user);
|
||||
UriHelper.NavigateTo("");
|
||||
}
|
||||
}
|
||||
|
|
39
Oqtane.Client/Providers/ServerAuthenticationStateProvider.cs
Normal file
39
Oqtane.Client/Providers/ServerAuthenticationStateProvider.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Providers
|
||||
{
|
||||
public class ServerAuthenticationStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
//private readonly IUserService UserService;
|
||||
private readonly IUriHelper urihelper;
|
||||
|
||||
public ServerAuthenticationStateProvider(IUriHelper urihelper)
|
||||
{
|
||||
//this.UserService = UserService;
|
||||
this.urihelper = urihelper;
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
// hack: create a new HttpClient rather than relying on the registered service as the AuthenticationStateProvider is initialized prior to IUriHelper ( https://github.com/aspnet/AspNetCore/issues/11867 )
|
||||
HttpClient http = new HttpClient();
|
||||
Uri uri = new Uri(urihelper.GetAbsoluteUri());
|
||||
string apiurl = uri.Scheme + "://" + uri.Authority + "/~/api/User/authenticate";
|
||||
User user = await http.GetJsonAsync<User>(apiurl);
|
||||
var identity = user.IsAuthenticated
|
||||
? new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, user.Username) }, "Identity.Application")
|
||||
: new ClaimsIdentity();
|
||||
return new AuthenticationState(new ClaimsPrincipal(identity));
|
||||
}
|
||||
|
||||
public void NotifyAuthenticationChanged()
|
||||
{
|
||||
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,12 @@ namespace Oqtane.Services
|
|||
|
||||
Task DeleteUserAsync(int UserId);
|
||||
|
||||
Task<User> GetCurrentUserAsync();
|
||||
|
||||
Task<User> LoginUserAsync(User user);
|
||||
|
||||
Task LogoutUserAsync();
|
||||
|
||||
bool IsAuthorized(User user, string accesscontrollist);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,13 @@ namespace Oqtane.Services
|
|||
{
|
||||
private readonly HttpClient http;
|
||||
private readonly SiteState sitestate;
|
||||
private readonly IUriHelper urihelper;
|
||||
|
||||
public UserService(HttpClient http, SiteState sitestate)
|
||||
public UserService(HttpClient http, SiteState sitestate, IUriHelper urihelper)
|
||||
{
|
||||
this.http = http;
|
||||
this.sitestate = sitestate;
|
||||
this.urihelper = urihelper;
|
||||
}
|
||||
|
||||
private string apiurl
|
||||
|
@ -35,7 +37,7 @@ namespace Oqtane.Services
|
|||
{
|
||||
List<User> users = await http.GetJsonAsync<List<User>>(apiurl);
|
||||
return users.Where(item => item.UserId == UserId).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddUserAsync(User user)
|
||||
{
|
||||
|
@ -51,6 +53,22 @@ namespace Oqtane.Services
|
|||
await http.DeleteAsync(apiurl + "/" + UserId.ToString());
|
||||
}
|
||||
|
||||
public async Task<User> GetCurrentUserAsync()
|
||||
{
|
||||
return await http.GetJsonAsync<User>(apiurl + "/current");
|
||||
}
|
||||
|
||||
public async Task<User> LoginUserAsync(User user)
|
||||
{
|
||||
return await http.PostJsonAsync<User>(apiurl + "/login", user);
|
||||
}
|
||||
|
||||
public async Task LogoutUserAsync()
|
||||
{
|
||||
// best practices recommend post is preferrable to get for logout
|
||||
await http.PostJsonAsync(apiurl + "/logout", null);
|
||||
}
|
||||
|
||||
// ACLs are stored in the format "!rolename1;![userid1];rolename2;rolename3;[userid2];[userid3]" where "!" designates Deny permissions
|
||||
public bool IsAuthorized(User user, string accesscontrollist)
|
||||
{
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
@using Oqtane.Shared
|
||||
@using Microsoft.JSInterop
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@inject AuthenticationStateProvider AuthenticationStateProvider
|
||||
@inject SiteState SiteState
|
||||
@inject IUriHelper UriHelper
|
||||
@inject INavigationInterception NavigationInterception
|
||||
|
@ -106,15 +107,13 @@ private async Task Refresh()
|
|||
}
|
||||
if (site != null || reload == true)
|
||||
{
|
||||
var interop = new Interop(jsRuntime);
|
||||
string userid = await interop.GetCookie("user");
|
||||
|
||||
user = null;
|
||||
if (PageState == null || reload == true)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(userid))
|
||||
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
|
||||
if (authState.User.Identity.IsAuthenticated)
|
||||
{
|
||||
user = await UserService.GetUserAsync(int.Parse(userid));
|
||||
user = await UserService.GetCurrentUserAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -122,23 +121,6 @@ private async Task Refresh()
|
|||
user = PageState.User;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(userid))
|
||||
{
|
||||
if (user != null && user.UserId != int.Parse(userid))
|
||||
{
|
||||
user = await UserService.GetUserAsync(int.Parse(userid));
|
||||
}
|
||||
// this is a hack for server-side Blazor where JSInterop is not working OnInit() which means the userid is not being retrieved from the cookie on the initial render and is not being loaded into PageState
|
||||
if (user == null)
|
||||
{
|
||||
user = await UserService.GetUserAsync(int.Parse(userid));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user = null;
|
||||
}
|
||||
|
||||
string path = new Uri(_absoluteUri).PathAndQuery.Substring(1);
|
||||
if (path.EndsWith("/")) { path = path.Substring(0, path.Length - 1); }
|
||||
if (alias.Path != "")
|
||||
|
|
|
@ -8,6 +8,8 @@ using Microsoft.AspNetCore.Components;
|
|||
using System.Reflection;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Providers;
|
||||
using Microsoft.AspNetCore.Blazor.Http;
|
||||
|
||||
namespace Oqtane.Client
|
||||
{
|
||||
|
@ -27,6 +29,11 @@ namespace Oqtane.Client
|
|||
#if WASM
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// register auth services
|
||||
services.AddAuthorizationCore();
|
||||
services.AddScoped<ServerAuthenticationStateProvider>();
|
||||
services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<ServerAuthenticationStateProvider>());
|
||||
|
||||
// register scoped core services
|
||||
services.AddScoped<SiteState>();
|
||||
services.AddScoped<IModuleDefinitionService, ModuleDefinitionService>();
|
||||
|
@ -39,6 +46,7 @@ namespace Oqtane.Client
|
|||
services.AddScoped<IPageModuleService, PageModuleService>();
|
||||
services.AddScoped<IUserService, UserService>();
|
||||
|
||||
|
||||
// dynamically register module contexts and repository services
|
||||
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (Assembly assembly in assemblies)
|
||||
|
@ -63,6 +71,7 @@ namespace Oqtane.Client
|
|||
|
||||
public void Configure(IComponentsApplicationBuilder app)
|
||||
{
|
||||
WebAssemblyHttpMessageHandler.DefaultCredentials = FetchCredentialsOption.Include;
|
||||
app.AddComponent<App>("app");
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,41 +1,34 @@
|
|||
@using Oqtane.Themes
|
||||
@using Oqtane.Shared
|
||||
@using Microsoft.JSInterop
|
||||
@using Oqtane.Services
|
||||
@using Oqtane.Providers
|
||||
@inherits ThemeObjectBase
|
||||
@inject IUriHelper UriHelper
|
||||
@inject IJSRuntime jsRuntime
|
||||
@inject IUserService UserService
|
||||
@inject ServerAuthenticationStateProvider AuthStateProvider
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
<button type="button" class="btn btn-primary" @onclick="@LogoutUser">Logout</button>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<button type="button" class="btn btn-primary" @onclick="@LoginUser">Login</button>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
<button type="button" class="btn btn-primary" @onclick="@Click">@name</button>
|
||||
|
||||
@code {
|
||||
string name = "";
|
||||
|
||||
protected override async Task OnInitAsync()
|
||||
private void LoginUser()
|
||||
{
|
||||
var interop = new Interop(jsRuntime);
|
||||
string user = await interop.GetCookie("user");
|
||||
|
||||
if (user == "")
|
||||
{
|
||||
name = "Login";
|
||||
}
|
||||
else
|
||||
{
|
||||
name = "Logout";
|
||||
}
|
||||
UriHelper.NavigateTo(NavigateUrl("login"));
|
||||
}
|
||||
|
||||
private async Task Click()
|
||||
private async Task LogoutUser()
|
||||
{
|
||||
if (name == "Login")
|
||||
{
|
||||
UriHelper.NavigateTo(NavigateUrl("login"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var interop = new Interop(jsRuntime);
|
||||
await interop.SetCookie("user", "", 7);
|
||||
UriHelper.NavigateTo(NavigateUrl(""), true);
|
||||
}
|
||||
await UserService.LogoutUserAsync();
|
||||
AuthStateProvider.NotifyAuthenticationChanged();
|
||||
UriHelper.NavigateTo(NavigateUrl(""));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,35 +1,25 @@
|
|||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Oqtane.Themes
|
||||
@using Oqtane.Shared
|
||||
@using Oqtane.Services;
|
||||
@using Oqtane.Models;
|
||||
@using Microsoft.JSInterop
|
||||
@inject IJSRuntime jsRuntime
|
||||
@using Oqtane.Themes
|
||||
@inherits ThemeObjectBase
|
||||
@inject IUriHelper UriHelper
|
||||
|
||||
<AuthorizeView>
|
||||
<Authorizing>
|
||||
<text>...</text>
|
||||
</Authorizing>
|
||||
<Authorized>
|
||||
<button type="button" class="btn btn-primary" @onclick="@RegisterUser">@context.User.Identity.Name</button>
|
||||
</Authorized>
|
||||
<NotAuthorized>
|
||||
<button type="button" class="btn btn-primary" @onclick="@RegisterUser">Register</button>
|
||||
</NotAuthorized>
|
||||
</AuthorizeView>
|
||||
|
||||
<NavLink class="btn btn-primary" href="@url">@name</NavLink>
|
||||
|
||||
@code {
|
||||
string name = "";
|
||||
string url = "";
|
||||
|
||||
protected override async Task OnInitAsync()
|
||||
private void RegisterUser()
|
||||
{
|
||||
var interop = new Interop(jsRuntime);
|
||||
string userid = await interop.GetCookie("user");
|
||||
|
||||
if (userid == "")
|
||||
{
|
||||
name = "Register";
|
||||
url = "register";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PageState.User != null)
|
||||
{
|
||||
name = PageState.User.DisplayName;
|
||||
}
|
||||
}
|
||||
UriHelper.NavigateTo(NavigateUrl("register"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Models;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
|
@ -9,10 +11,14 @@ namespace Oqtane.Controllers
|
|||
public class UserController : Controller
|
||||
{
|
||||
private readonly IUserRepository users;
|
||||
private readonly UserManager<IdentityUser> identityUserManager;
|
||||
private readonly SignInManager<IdentityUser> identitySignInManager;
|
||||
|
||||
public UserController(IUserRepository Users)
|
||||
public UserController(IUserRepository Users, UserManager<IdentityUser> IdentityUserManager, SignInManager<IdentityUser> IdentitySignInManager)
|
||||
{
|
||||
users = Users;
|
||||
identityUserManager = IdentityUserManager;
|
||||
identitySignInManager = IdentitySignInManager;
|
||||
}
|
||||
|
||||
// GET: api/<controller>
|
||||
|
@ -31,10 +37,23 @@ namespace Oqtane.Controllers
|
|||
|
||||
// POST api/<controller>
|
||||
[HttpPost]
|
||||
public void Post([FromBody] User user)
|
||||
public async Task Post([FromBody] User user)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
users.AddUser(user);
|
||||
{
|
||||
IdentityUser identityuser = await identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser == null)
|
||||
{
|
||||
identityuser = new IdentityUser();
|
||||
identityuser.UserName = user.Username;
|
||||
identityuser.Email = user.Username;
|
||||
var result = await identityUserManager.CreateAsync(identityuser, user.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
users.AddUser(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PUT api/<controller>/5
|
||||
|
@ -42,7 +61,9 @@ namespace Oqtane.Controllers
|
|||
public void Put(int id, [FromBody] User user)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
users.UpdateUser(user);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE api/<controller>/5
|
||||
|
@ -51,5 +72,72 @@ namespace Oqtane.Controllers
|
|||
{
|
||||
users.DeleteUser(id);
|
||||
}
|
||||
|
||||
// GET api/<controller>/current
|
||||
[HttpGet("current")]
|
||||
public User Current()
|
||||
{
|
||||
User user = null;
|
||||
if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
user = users.GetUser(User.Identity.Name);
|
||||
user.IsAuthenticated = true;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
// POST api/<controller>/login
|
||||
[HttpPost("login")]
|
||||
public async Task<User> Login([FromBody] User user)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
// seed host user - this logic should be moved to installation
|
||||
IdentityUser identityuser = await identityUserManager.FindByNameAsync("host");
|
||||
if (identityuser == null)
|
||||
{
|
||||
var result = await identityUserManager.CreateAsync(new IdentityUser { UserName = "host", Email = "host" }, "password");
|
||||
if (result.Succeeded)
|
||||
{
|
||||
users.AddUser(new Models.User { Username = "host", DisplayName = "host", IsSuperUser = true, Roles = "" });
|
||||
}
|
||||
}
|
||||
|
||||
identityuser = await identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
{
|
||||
var result = await identitySignInManager.CheckPasswordSignInAsync(identityuser, user.Password, false);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await identitySignInManager.SignInAsync(identityuser, false);
|
||||
user = users.GetUser(identityuser.UserName);
|
||||
user.IsAuthenticated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user = null;
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
// POST api/<controller>/logout
|
||||
[HttpPost("logout")]
|
||||
public async Task Logout([FromBody] User user)
|
||||
{
|
||||
await identitySignInManager.SignOutAsync();
|
||||
}
|
||||
|
||||
// GET api/<controller>/current
|
||||
[HttpGet("authenticate")]
|
||||
public User Authenticate()
|
||||
{
|
||||
return new User { Username = User.Identity.Name, IsAuthenticated = User.Identity.IsAuthenticated };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ using DbUp;
|
|||
using System.Data.SqlClient;
|
||||
using System.Threading;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Oqtane.Filters
|
||||
{
|
||||
|
@ -109,6 +110,7 @@ namespace Oqtane.Filters
|
|||
throw new Exception();
|
||||
}
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,11 +26,13 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Scripts\Identity.sql" />
|
||||
<None Remove="Scripts\Master.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Scripts\Master.sql" />
|
||||
<EmbeddedResource Include="Scripts\Identity.sql" />
|
||||
<EmbeddedResource Include="Scripts\Tenant.sql" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -38,8 +40,9 @@
|
|||
<PackageReference Include="dbup" Version="4.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Blazor.Server" Version="3.0.0-preview6.19307.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0-preview6.19307.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0-preview6.19307.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0-preview6.19304.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.0.0-preview6.19304.10" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -9,6 +9,7 @@ namespace Oqtane.Repository
|
|||
void AddUser(User User);
|
||||
void UpdateUser(User User);
|
||||
User GetUser(int UserId);
|
||||
User GetUser(string Username);
|
||||
void DeleteUser(int UserId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Oqtane.Models;
|
||||
using System;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
public class TenantContext : DbContext
|
||||
public class TenantContext : IdentityDbContext<IdentityUser>
|
||||
{
|
||||
public virtual DbSet<Site> Site { get; set; }
|
||||
public virtual DbSet<Page> Page { get; set; }
|
||||
|
|
|
@ -28,6 +28,10 @@ namespace Oqtane.Repository
|
|||
{
|
||||
aliasname += "/" + segments[1];
|
||||
}
|
||||
if (aliasname.EndsWith("/"))
|
||||
{
|
||||
aliasname = aliasname.Substring(0, aliasname.Length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public Tenant GetTenant()
|
||||
|
|
|
@ -65,6 +65,19 @@ namespace Oqtane.Repository
|
|||
}
|
||||
}
|
||||
|
||||
public User GetUser(string Username)
|
||||
{
|
||||
try
|
||||
{
|
||||
User user = db.User.Where(item => item.Username == Username).FirstOrDefault();
|
||||
return user;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteUser(int userId)
|
||||
{
|
||||
try
|
||||
|
|
190
Oqtane.Server/Scripts/Identity.sql
Normal file
190
Oqtane.Server/Scripts/Identity.sql
Normal file
|
@ -0,0 +1,190 @@
|
|||
CREATE TABLE [dbo].[AspNetRoleClaims](
|
||||
[Id] [int] IDENTITY(1,1) NOT NULL,
|
||||
[RoleId] [nvarchar](450) NOT NULL,
|
||||
[ClaimType] [nvarchar](max) NULL,
|
||||
[ClaimValue] [nvarchar](max) NULL,
|
||||
CONSTRAINT [PK_AspNetRoleClaims] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[Id] ASC
|
||||
) ON [PRIMARY]
|
||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE TABLE [dbo].[AspNetRoles](
|
||||
[Id] [nvarchar](450) NOT NULL,
|
||||
[Name] [nvarchar](256) NULL,
|
||||
[NormalizedName] [nvarchar](256) NULL,
|
||||
[ConcurrencyStamp] [nvarchar](max) NULL,
|
||||
CONSTRAINT [PK_AspNetRoles] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[Id] ASC
|
||||
) ON [PRIMARY]
|
||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE TABLE [dbo].[AspNetUserClaims](
|
||||
[Id] [int] IDENTITY(1,1) NOT NULL,
|
||||
[UserId] [nvarchar](450) NOT NULL,
|
||||
[ClaimType] [nvarchar](max) NULL,
|
||||
[ClaimValue] [nvarchar](max) NULL,
|
||||
CONSTRAINT [PK_AspNetUserClaims] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[Id] ASC
|
||||
) ON [PRIMARY]
|
||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE TABLE [dbo].[AspNetUserLogins](
|
||||
[LoginProvider] [nvarchar](128) NOT NULL,
|
||||
[ProviderKey] [nvarchar](128) NOT NULL,
|
||||
[ProviderDisplayName] [nvarchar](max) NULL,
|
||||
[UserId] [nvarchar](450) NOT NULL,
|
||||
CONSTRAINT [PK_AspNetUserLogins] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[LoginProvider] ASC,
|
||||
[ProviderKey] ASC
|
||||
) ON [PRIMARY]
|
||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE TABLE [dbo].[AspNetUserRoles](
|
||||
[UserId] [nvarchar](450) NOT NULL,
|
||||
[RoleId] [nvarchar](450) NOT NULL,
|
||||
CONSTRAINT [PK_AspNetUserRoles] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[UserId] ASC,
|
||||
[RoleId] ASC
|
||||
) ON [PRIMARY]
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE TABLE [dbo].[AspNetUsers](
|
||||
[Id] [nvarchar](450) NOT NULL,
|
||||
[UserName] [nvarchar](256) NULL,
|
||||
[NormalizedUserName] [nvarchar](256) NULL,
|
||||
[Email] [nvarchar](256) NULL,
|
||||
[NormalizedEmail] [nvarchar](256) NULL,
|
||||
[EmailConfirmed] [bit] NOT NULL,
|
||||
[PasswordHash] [nvarchar](max) NULL,
|
||||
[SecurityStamp] [nvarchar](max) NULL,
|
||||
[ConcurrencyStamp] [nvarchar](max) NULL,
|
||||
[PhoneNumber] [nvarchar](max) NULL,
|
||||
[PhoneNumberConfirmed] [bit] NOT NULL,
|
||||
[TwoFactorEnabled] [bit] NOT NULL,
|
||||
[LockoutEnd] [datetimeoffset](7) NULL,
|
||||
[LockoutEnabled] [bit] NOT NULL,
|
||||
[AccessFailedCount] [int] NOT NULL,
|
||||
CONSTRAINT [PK_AspNetUsers] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[Id] ASC
|
||||
) ON [PRIMARY]
|
||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE TABLE [dbo].[AspNetUserTokens](
|
||||
[UserId] [nvarchar](450) NOT NULL,
|
||||
[LoginProvider] [nvarchar](128) NOT NULL,
|
||||
[Name] [nvarchar](128) NOT NULL,
|
||||
[Value] [nvarchar](max) NULL,
|
||||
CONSTRAINT [PK_AspNetUserTokens] PRIMARY KEY CLUSTERED
|
||||
(
|
||||
[UserId] ASC,
|
||||
[LoginProvider] ASC,
|
||||
[Name] ASC
|
||||
) ON [PRIMARY]
|
||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE NONCLUSTERED INDEX [IX_AspNetRoleClaims_RoleId] ON [dbo].[AspNetRoleClaims]
|
||||
(
|
||||
[RoleId] ASC
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX [RoleNameIndex] ON [dbo].[AspNetRoles]
|
||||
(
|
||||
[NormalizedName] ASC
|
||||
)
|
||||
WHERE ([NormalizedName] IS NOT NULL)
|
||||
ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE NONCLUSTERED INDEX [IX_AspNetUserClaims_UserId] ON [dbo].[AspNetUserClaims]
|
||||
(
|
||||
[UserId] ASC
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE NONCLUSTERED INDEX [IX_AspNetUserLogins_UserId] ON [dbo].[AspNetUserLogins]
|
||||
(
|
||||
[UserId] ASC
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE NONCLUSTERED INDEX [IX_AspNetUserRoles_RoleId] ON [dbo].[AspNetUserRoles]
|
||||
(
|
||||
[RoleId] ASC
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE NONCLUSTERED INDEX [EmailIndex] ON [dbo].[AspNetUsers]
|
||||
(
|
||||
[NormalizedEmail] ASC
|
||||
) ON [PRIMARY]
|
||||
GO
|
||||
|
||||
CREATE UNIQUE NONCLUSTERED INDEX [UserNameIndex] ON [dbo].[AspNetUsers]
|
||||
(
|
||||
[NormalizedUserName] ASC
|
||||
)
|
||||
WHERE ([NormalizedUserName] IS NOT NULL)
|
||||
ON [PRIMARY]
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetRoleClaims] WITH CHECK ADD CONSTRAINT [FK_AspNetRoleClaims_AspNetRoles_RoleId] FOREIGN KEY([RoleId])
|
||||
REFERENCES [dbo].[AspNetRoles] ([Id])
|
||||
ON DELETE CASCADE
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetRoleClaims] CHECK CONSTRAINT [FK_AspNetRoleClaims_AspNetRoles_RoleId]
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetUserClaims] WITH CHECK ADD CONSTRAINT [FK_AspNetUserClaims_AspNetUsers_UserId] FOREIGN KEY([UserId])
|
||||
REFERENCES [dbo].[AspNetUsers] ([Id])
|
||||
ON DELETE CASCADE
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetUserClaims] CHECK CONSTRAINT [FK_AspNetUserClaims_AspNetUsers_UserId]
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetUserLogins] WITH CHECK ADD CONSTRAINT [FK_AspNetUserLogins_AspNetUsers_UserId] FOREIGN KEY([UserId])
|
||||
REFERENCES [dbo].[AspNetUsers] ([Id])
|
||||
ON DELETE CASCADE
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetUserLogins] CHECK CONSTRAINT [FK_AspNetUserLogins_AspNetUsers_UserId]
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetUserRoles] WITH CHECK ADD CONSTRAINT [FK_AspNetUserRoles_AspNetRoles_RoleId] FOREIGN KEY([RoleId])
|
||||
REFERENCES [dbo].[AspNetRoles] ([Id])
|
||||
ON DELETE CASCADE
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetUserRoles] CHECK CONSTRAINT [FK_AspNetUserRoles_AspNetRoles_RoleId]
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetUserRoles] WITH CHECK ADD CONSTRAINT [FK_AspNetUserRoles_AspNetUsers_UserId] FOREIGN KEY([UserId])
|
||||
REFERENCES [dbo].[AspNetUsers] ([Id])
|
||||
ON DELETE CASCADE
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetUserRoles] CHECK CONSTRAINT [FK_AspNetUserRoles_AspNetUsers_UserId]
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetUserTokens] WITH CHECK ADD CONSTRAINT [FK_AspNetUserTokens_AspNetUsers_UserId] FOREIGN KEY([UserId])
|
||||
REFERENCES [dbo].[AspNetUsers] ([Id])
|
||||
ON DELETE CASCADE
|
||||
GO
|
||||
|
||||
ALTER TABLE [dbo].[AspNetUserTokens] CHECK CONSTRAINT [FK_AspNetUserTokens_AspNetUsers_UserId]
|
||||
GO
|
|
@ -313,21 +313,4 @@ GO
|
|||
SET IDENTITY_INSERT [dbo].[HtmlText] OFF
|
||||
GO
|
||||
|
||||
SET IDENTITY_INSERT [dbo].[User] ON
|
||||
GO
|
||||
INSERT [dbo].[User] ([UserId], [Username], [DisplayName], [Roles], [IsSuperUser])
|
||||
VALUES (1, N'host', N'Host', N'', 1)
|
||||
GO
|
||||
INSERT [dbo].[User] ([UserId], [Username], [DisplayName], [Roles], [IsSuperUser])
|
||||
VALUES (2, N'admin', N'Administrator', N'Administrators;', 0)
|
||||
GO
|
||||
INSERT [dbo].[User] ([UserId], [Username], [DisplayName], [Roles], [IsSuperUser])
|
||||
VALUES (3, N'editor', N'Editor', N'Editors;', 0)
|
||||
GO
|
||||
INSERT [dbo].[User] ([UserId], [Username], [DisplayName], [Roles], [IsSuperUser])
|
||||
VALUES (4, N'member', N'Member', N'Members;', 0)
|
||||
GO
|
||||
SET IDENTITY_INSERT [dbo].[User] OFF
|
||||
GO
|
||||
|
||||
|
||||
|
|
|
@ -19,6 +19,10 @@ using System.Net.Http;
|
|||
using Microsoft.AspNetCore.Components;
|
||||
using Oqtane.Client;
|
||||
using Oqtane.Shared;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Oqtane.Providers;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
|
||||
namespace Oqtane.Server
|
||||
{
|
||||
|
@ -58,6 +62,11 @@ namespace Oqtane.Server
|
|||
});
|
||||
}
|
||||
|
||||
// register auth services
|
||||
services.AddAuthorizationCore();
|
||||
services.AddScoped<ServerAuthenticationStateProvider>();
|
||||
services.AddScoped<AuthenticationStateProvider>(s => s.GetRequiredService<ServerAuthenticationStateProvider>());
|
||||
|
||||
// register scoped core services
|
||||
services.AddScoped<SiteState>();
|
||||
services.AddScoped<IModuleDefinitionService, ModuleDefinitionService>();
|
||||
|
@ -99,6 +108,38 @@ namespace Oqtane.Server
|
|||
));
|
||||
services.AddDbContext<TenantContext>(options => { });
|
||||
|
||||
services.AddIdentity<IdentityUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<TenantContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
services.Configure<IdentityOptions>(options =>
|
||||
{
|
||||
// Password settings
|
||||
options.Password.RequireDigit = false;
|
||||
options.Password.RequiredLength = 6;
|
||||
options.Password.RequireNonAlphanumeric = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.Password.RequireLowercase = false;
|
||||
|
||||
// Lockout settings
|
||||
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
|
||||
options.Lockout.MaxFailedAccessAttempts = 10;
|
||||
options.Lockout.AllowedForNewUsers = true;
|
||||
|
||||
// User settings
|
||||
options.User.RequireUniqueEmail = false;
|
||||
});
|
||||
|
||||
services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.Cookie.HttpOnly = false;
|
||||
options.Events.OnRedirectToLogin = context =>
|
||||
{
|
||||
context.Response.StatusCode = 401;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
});
|
||||
|
||||
services.AddMemoryCache();
|
||||
|
||||
services.AddMvc().AddNewtonsoftJson();
|
||||
|
@ -177,6 +218,8 @@ namespace Oqtane.Server
|
|||
app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
|
@ -201,6 +244,38 @@ namespace Oqtane.Server
|
|||
));
|
||||
services.AddDbContext<TenantContext>(options => { });
|
||||
|
||||
services.AddIdentity<IdentityUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<TenantContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
services.Configure<IdentityOptions>(options =>
|
||||
{
|
||||
// Password settings
|
||||
options.Password.RequireDigit = false;
|
||||
options.Password.RequiredLength = 6;
|
||||
options.Password.RequireNonAlphanumeric = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.Password.RequireLowercase = false;
|
||||
|
||||
// Lockout settings
|
||||
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
|
||||
options.Lockout.MaxFailedAccessAttempts = 10;
|
||||
options.Lockout.AllowedForNewUsers = true;
|
||||
|
||||
// User settings
|
||||
options.User.RequireUniqueEmail = false;
|
||||
});
|
||||
|
||||
services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.Cookie.HttpOnly = false;
|
||||
options.Events.OnRedirectToLogin = context =>
|
||||
{
|
||||
context.Response.StatusCode = 401;
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
});
|
||||
|
||||
services.AddMemoryCache();
|
||||
|
||||
services.AddMvc().AddNewtonsoftJson();
|
||||
|
@ -281,6 +356,8 @@ namespace Oqtane.Server
|
|||
app.UseClientSideBlazorFiles<Client.Startup>();
|
||||
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
|
|
|
@ -9,5 +9,10 @@ namespace Oqtane.Models
|
|||
public string DisplayName { get; set; }
|
||||
public string Roles { get; set; }
|
||||
public bool IsSuperUser { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public string Password { get; set; }
|
||||
[NotMapped]
|
||||
public bool IsAuthenticated { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user