Browse Source

Changed auth backend to use Identity Server, and asp.net identities.

core
Teknikode 5 years ago
parent
commit
7ec30d1e39
  1. 7
      Teknik/Areas/API/Controllers/APIv1Controller.cs
  2. 285
      Teknik/Areas/Accounts/Controllers/AccountsController.cs
  3. 24
      Teknik/Areas/Accounts/ViewModels/LoginViewModel.cs
  4. 17
      Teknik/Areas/Accounts/ViewModels/TwoFactorViewModel.cs
  5. 4
      Teknik/Areas/Accounts/Views/Accounts/Login.cshtml
  6. 6
      Teknik/Areas/Accounts/Views/Accounts/TwoFactorCheck.cshtml
  7. 4
      Teknik/Areas/Accounts/Views/Accounts/ViewLogin.cshtml
  8. 435
      Teknik/Areas/User/Controllers/UserController.cs
  9. 22
      Teknik/Areas/User/Models/LoginInfo.cs
  10. 9
      Teknik/Areas/User/Models/User.cs
  11. 2
      Teknik/Areas/User/Views/User/_LoginModalPartial.cshtml
  12. 4
      Teknik/Areas/User/Views/User/_LoginPartial.cshtml
  13. 27
      Teknik/Attributes/TeknikAuthorizeAttribute.cs
  14. 1
      Teknik/Controllers/DefaultController.cs
  15. 3
      Teknik/Data/TeknikEntities.cs
  16. 86
      Teknik/IdentityServerConfig.cs
  17. 1005
      Teknik/Migrations/20180624191511_UserLoginInfo.Designer.cs
  18. 40
      Teknik/Migrations/20180624191511_UserLoginInfo.cs
  19. 1003
      Teknik/Migrations/TeknikEntitiesModelSnapshot.cs
  20. 54
      Teknik/Routes.cs
  21. 36
      Teknik/Security/PasswordHasher.cs
  22. 2
      Teknik/Security/ResourceOwnerPasswordValidator.cs
  23. 91
      Teknik/Security/RoleStore.cs
  24. 60
      Teknik/Security/SignInManager.cs
  25. 260
      Teknik/Security/UserStore.cs
  26. 79
      Teknik/Startup.cs
  27. 9
      Teknik/Teknik.csproj

7
Teknik/Areas/API/Controllers/APIv1Controller.cs

@ -30,6 +30,7 @@ using Teknik.Logging; @@ -30,6 +30,7 @@ using Teknik.Logging;
namespace Teknik.Areas.API.Controllers
{
[Authorize]
[TeknikAuthorize(AuthType.Basic)]
[Area("APIv1")]
public class APIv1Controller : DefaultController
@ -42,6 +43,12 @@ namespace Teknik.Areas.API.Controllers @@ -42,6 +43,12 @@ namespace Teknik.Areas.API.Controllers
return Redirect(Url.SubRouteUrl("help", "Help.API"));
}
[HttpGet]
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> UploadAsync(APIv1UploadModel model)

285
Teknik/Areas/Accounts/Controllers/AccountsController.cs

@ -0,0 +1,285 @@ @@ -0,0 +1,285 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using IdentityServer4.Events;
using IdentityServer4.Extensions;
using IdentityServer4.Services;
using IdentityServer4.Stores;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Teknik.Areas.Accounts.ViewModels;
using Teknik.Areas.Users.Models;
using Teknik.Areas.Users.Utility;
using Teknik.Configuration;
using Teknik.Controllers;
using Teknik.Data;
using Teknik.Logging;
using Teknik.Security;
using Teknik.Utilities;
using TwoStepsAuthenticator;
namespace Teknik.Areas.Accounts.Controllers
{
[Area("Accounts")]
public class AccountsController : DefaultController
{
private readonly UserStore _users;
private readonly SignInManager<User> _signInManager;
private readonly UserManager<User> _userManager;
private readonly IIdentityServerInteractionService _interaction;
private readonly IClientStore _clientStore;
private readonly IEventService _events;
private static readonly UsedCodesManager _usedCodesManager = new UsedCodesManager();
private const string _AuthSessionKey = "AuthenticatedUser";
public AccountsController(
ILogger<Logger> logger,
Config config,
TeknikEntities dbContext,
SignInManager<User> signInManager,
UserManager<User> userManager,
IIdentityServerInteractionService interaction,
IClientStore clientStore,
IEventService events,
UserStore users = null)
: base(logger, config, dbContext)
{
_users = users ?? new UserStore(_dbContext, _config);
_signInManager = signInManager;
_userManager = userManager;
_interaction = interaction;
_clientStore = clientStore;
_events = events;
}
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string ReturnUrl)
{
LoginViewModel model = new LoginViewModel();
model.ReturnUrl = ReturnUrl;
return View("/Areas/Accounts/Views/Accounts/ViewLogin.cshtml", model);
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login([Bind(Prefix = "Login")]LoginViewModel model)
{
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
if (ModelState.IsValid)
{
string username = model.Username;
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
// Make sure they aren't banned or anything
if (user.AccountStatus == AccountStatus.Banned)
{
model.Error = true;
model.ErrorMessage = "Account has been banned.";
// Raise the error event
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, model.ErrorMessage));
return GenerateActionResult(new { error = model.ErrorMessage }, View("/Areas/Accounts/Views/Accounts/ViewLogin.cshtml", model));
}
// Try to sign them in
var valid = await _userManager.CheckPasswordAsync(user, model.Password);
if (valid)
{
// Perform transfer actions on the account
UserHelper.TransferUser(_dbContext, _config, user, model.Password);
user.LastSeen = DateTime.Now;
_dbContext.Entry(user).State = EntityState.Modified;
_dbContext.SaveChanges();
// Let's double check their email and git accounts to make sure they exist
string email = UserHelper.GetUserEmailAddress(_config, username);
if (_config.EmailConfig.Enabled && !UserHelper.UserEmailExists(_config, email))
{
UserHelper.AddUserEmail(_config, email, model.Password);
}
if (_config.GitConfig.Enabled && !UserHelper.UserGitExists(_config, username))
{
UserHelper.AddUserGit(_config, username, model.Password);
}
bool twoFactor = false;
string returnUrl = model.ReturnUrl;
if (user.SecuritySettings.TwoFactorEnabled)
{
twoFactor = true;
// We need to check their device, and two factor them
if (user.SecuritySettings.AllowTrustedDevices)
{
// Check for the trusted device cookie
string token = Request.Cookies[Constants.TRUSTEDDEVICECOOKIE + "_" + username];
if (!string.IsNullOrEmpty(token))
{
if (user.TrustedDevices.Where(d => d.Token == token).FirstOrDefault() != null)
{
// The device token is attached to the user, let's let it slide
twoFactor = false;
}
}
}
}
if (twoFactor)
{
HttpContext.Session.Set(_AuthSessionKey, user.Username);
if (string.IsNullOrEmpty(model.ReturnUrl))
returnUrl = Request.Headers["Referer"].ToString();
returnUrl = Url.SubRouteUrl("accounts", "Accounts.CheckAuthenticatorCode", new { returnUrl = returnUrl, rememberMe = model.RememberMe });
model.ReturnUrl = string.Empty;
}
else
{
returnUrl = Request.Headers["Referer"].ToString();
// They don't need two factor auth.
await SignInUser(user, (string.IsNullOrEmpty(model.ReturnUrl)) ? returnUrl : model.ReturnUrl, model.RememberMe);
}
if (string.IsNullOrEmpty(model.ReturnUrl))
{
return GenerateActionResult(new { result = returnUrl }, Redirect(returnUrl));
}
else
{
return Redirect(model.ReturnUrl);
}
}
}
}
// Raise the error event
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"));
model.Error = true;
model.ErrorMessage = "Invalid Username or Password.";
return GenerateActionResult(new { error = model.ErrorMessage }, View("/Areas/Accounts/Views/Accounts/ViewLogin.cshtml", model));
}
/// <summary>
/// Handle logout page postback
/// </summary>
public async Task<IActionResult> Logout()
{
await LogoutUser(User, HttpContext, _signInManager, _events);
return Redirect(Url.SubRouteUrl("www", "Home.Index"));
}
[HttpGet]
[AllowAnonymous]
public IActionResult ConfirmTwoFactorAuth(string returnUrl, bool rememberMe)
{
string username = HttpContext.Session.Get<string>(_AuthSessionKey);
if (!string.IsNullOrEmpty(username))
{
User user = UserHelper.GetUser(_dbContext, username);
ViewBag.Title = "Unknown Device - " + _config.Title;
ViewBag.Description = "We do not recognize this device.";
TwoFactorViewModel model = new TwoFactorViewModel();
model.ReturnUrl = returnUrl;
model.RememberMe = rememberMe;
model.AllowTrustedDevice = user.SecuritySettings.AllowTrustedDevices;
return View("/Areas/Accounts/Views/Accounts/TwoFactorCheck.cshtml", model);
}
return Redirect(Url.SubRouteUrl("error", "Error.Http403"));
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> ConfirmAuthenticatorCode(string code, string returnUrl, bool rememberMe, bool rememberDevice, string deviceName)
{
string errorMessage = string.Empty;
string username = HttpContext.Session.Get<string>(_AuthSessionKey);
if (!string.IsNullOrEmpty(username))
{
User user = UserHelper.GetUser(_dbContext, username);
if (user.SecuritySettings.TwoFactorEnabled)
{
string key = user.SecuritySettings.TwoFactorKey;
TimeAuthenticator ta = new TimeAuthenticator(usedCodeManager: _usedCodesManager);
bool isValid = ta.CheckCode(key, code, user);
if (isValid)
{
// the code was valid, let's log them in!
await SignInUser(user, returnUrl, rememberMe);
if (user.SecuritySettings.AllowTrustedDevices && rememberDevice)
{
// They want to remember the device, and have allow trusted devices on
var cookieOptions = UserHelper.CreateTrustedDeviceCookie(_config, user.Username, Request.Host.Host.GetDomain(), Request.IsLocal());
Response.Cookies.Append(Constants.TRUSTEDDEVICECOOKIE + "_" + username, cookieOptions.Item2, cookieOptions.Item1);
TrustedDevice device = new TrustedDevice();
device.UserId = user.UserId;
device.Name = (string.IsNullOrEmpty(deviceName)) ? "Unknown" : deviceName;
device.DateSeen = DateTime.Now;
device.Token = cookieOptions.Item2;
// Add the token
_dbContext.TrustedDevices.Add(device);
_dbContext.SaveChanges();
}
if (string.IsNullOrEmpty(returnUrl))
returnUrl = Request.Headers["Referer"].ToString();
return Json(new { result = returnUrl });
}
errorMessage = "Invalid Authentication Code" ;
}
errorMessage = "User does not have Two Factor Authentication enabled";
}
errorMessage = "User does not exist";
// Raise the error event
await _events.RaiseAsync(new UserLoginFailureEvent(username, errorMessage));
return Json(new { error = errorMessage });
}
public async Task SignInUser(User user, string returnUrl, bool rememberMe)
{
// Sign In with Identity
await _signInManager.SignInAsync(user, rememberMe);
// Sign in via Identity Server
await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.UserId.ToString(), user.Username));
}
public static async Task LogoutUser(ClaimsPrincipal user, HttpContext context, SignInManager<User> signInManager, IEventService eventService)
{
if (user?.Identity.IsAuthenticated == true)
{
// delete local authentication cookie
await signInManager.SignOutAsync();
// raise the logout event
await eventService.RaiseAsync(new UserLogoutSuccessEvent(user.GetSubjectId(), user.GetDisplayName()));
}
}
}
}

24
Teknik/Areas/Accounts/ViewModels/LoginViewModel.cs

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Teknik.ViewModels;
namespace Teknik.Areas.Accounts.ViewModels
{
public class LoginViewModel : ViewModelBase
{
[Required]
[Display(Name = "Username")]
public string Username { get; set; }
[Required]
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember Me")]
public bool RememberMe { get; set; }
public string ReturnUrl { get; set; }
}
}

17
Teknik/Areas/Accounts/ViewModels/TwoFactorViewModel.cs

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Teknik.ViewModels;
namespace Teknik.Areas.Accounts.ViewModels
{
public class TwoFactorViewModel : ViewModelBase
{
public bool RememberMe { get; set; }
public string ReturnUrl { get; set; }
public bool AllowTrustedDevice { get; set; }
}
}

4
Teknik/Areas/User/Views/User/Login.cshtml → Teknik/Areas/Accounts/Views/Accounts/Login.cshtml

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
@model Teknik.Areas.Users.ViewModels.LoginViewModel
@model Teknik.Areas.Accounts.ViewModels.LoginViewModel
@if (Config.UserConfig.LoginEnabled)
{
@ -12,7 +12,7 @@ @@ -12,7 +12,7 @@
</div>
</div>
</div>
<!form id="loginForm" action="@Url.SubRouteUrl("user", "User.Login")" method="post" accept-charset="UTF-8">
<!form id="loginForm" action="@Url.SubRouteUrl("accounts", "Accounts.Login")" method="post" accept-charset="UTF-8">
<input name="Login.ReturnUrl" id="loginReturnUrl" type="hidden" value="@Model.ReturnUrl" />
<div class="form-group">
<input type="text" class="form-control" id="loginUsername" value="" placeholder="Username" name="Login.Username" data-val-required="The Username field is required." data-val="true" />

6
Teknik/Areas/User/Views/User/TwoFactorCheck.cshtml → Teknik/Areas/Accounts/Views/Accounts/TwoFactorCheck.cshtml

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
@model Teknik.Areas.Users.ViewModels.TwoFactorViewModel
@model Teknik.Areas.Accounts.ViewModels.TwoFactorViewModel
<script>
var confirmAuthCodeURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "ConfirmAuthenticatorCode" })';
var confirmAuthCodeURL = '@Url.SubRouteUrl("accounts", "Accounts.Action", new { action = "ConfirmAuthenticatorCode" })';
</script>
<div class="container">
@ -46,4 +46,4 @@ @@ -46,4 +46,4 @@
</div>
</div>
<bundle src="js/user.checkAuthCode.min.js" append-version="true"></bundle>
<bundle src="js/accounts.checkAuthCode.min.js" append-version="true"></bundle>

4
Teknik/Areas/User/Views/User/ViewLogin.cshtml → Teknik/Areas/Accounts/Views/Accounts/ViewLogin.cshtml

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
@model Teknik.Areas.Users.ViewModels.LoginViewModel
@model Teknik.Areas.Accounts.ViewModels.LoginViewModel
<div class="container">
<div class="row">
@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
<div class="text-center">
<h1>Teknik Login</h1>
<div class="col-md-4 col-md-offset-4">
@await Html.PartialAsync("../../Areas/User/Views/User/Login", Model)
@await Html.PartialAsync("../../Areas/Accounts/Views/Accounts/Login", Model)
</div>
</div>
</div>

435
Teknik/Areas/User/Controllers/UserController.cs

@ -24,6 +24,9 @@ using Teknik.Logging; @@ -24,6 +24,9 @@ using Teknik.Logging;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using Teknik.Areas.Accounts.Controllers;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Identity;
namespace Teknik.Areas.Users.Controllers
{
@ -31,10 +34,17 @@ namespace Teknik.Areas.Users.Controllers @@ -31,10 +34,17 @@ namespace Teknik.Areas.Users.Controllers
[Area("User")]
public class UserController : DefaultController
{
public UserController(ILogger<Logger> logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { }
private static readonly UsedCodesManager usedCodesManager = new UsedCodesManager();
private const string _AuthSessionKey = "AuthenticatedUser";
private readonly SignInManager<User> _signInManager;
private readonly IEventService _events;
public UserController(ILogger<Logger> logger, Config config, TeknikEntities dbContext, SignInManager<User> signInManager, IEventService eventService) : base(logger, config, dbContext)
{
_signInManager = signInManager;
_events = eventService;
}
[AllowAnonymous]
public IActionResult GetPremium()
@ -46,6 +56,125 @@ namespace Teknik.Areas.Users.Controllers @@ -46,6 +56,125 @@ namespace Teknik.Areas.Users.Controllers
return View(model);
}
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string inviteCode, string ReturnUrl)
{
RegisterViewModel model = new RegisterViewModel();
model.InviteCode = inviteCode;
model.ReturnUrl = ReturnUrl;
return View("/Areas/User/Views/User/ViewRegistration.cshtml", model);
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Register([Bind(Prefix = "Register")]RegisterViewModel model)
{
model.Error = false;
model.ErrorMessage = string.Empty;
if (ModelState.IsValid)
{
if (_config.UserConfig.RegistrationEnabled)
{
if (!model.Error && !UserHelper.ValidUsername(_config, model.Username))
{
model.Error = true;
model.ErrorMessage = "That username is not valid";
}
if (!model.Error && !UserHelper.UsernameAvailable(_dbContext, _config, model.Username))
{
model.Error = true;
model.ErrorMessage = "That username is not available";
}
if (!model.Error && model.Password != model.ConfirmPassword)
{
model.Error = true;
model.ErrorMessage = "Passwords must match";
}
// Validate the Invite Code
if (!model.Error && _config.UserConfig.InviteCodeRequired && string.IsNullOrEmpty(model.InviteCode))
{
model.Error = true;
model.ErrorMessage = "An Invite Code is required to register";
}
if (!model.Error && !string.IsNullOrEmpty(model.InviteCode) && _dbContext.InviteCodes.Where(c => c.Code == model.InviteCode && c.Active && c.ClaimedUser == null).FirstOrDefault() == null)
{
model.Error = true;
model.ErrorMessage = "Invalid Invite Code";
}
// PGP Key valid?
if (!model.Error && !string.IsNullOrEmpty(model.PublicKey) && !PGP.IsPublicKey(model.PublicKey))
{
model.Error = true;
model.ErrorMessage = "Invalid PGP Public Key";
}
if (!model.Error)
{
try
{
User newUser = new User();
newUser.JoinDate = DateTime.Now;
newUser.Username = model.Username;
newUser.UserSettings = new UserSettings();
newUser.SecuritySettings = new SecuritySettings();
newUser.BlogSettings = new BlogSettings();
newUser.UploadSettings = new UploadSettings();
if (!string.IsNullOrEmpty(model.PublicKey))
newUser.SecuritySettings.PGPSignature = model.PublicKey;
if (!string.IsNullOrEmpty(model.RecoveryEmail))
newUser.SecuritySettings.RecoveryEmail = model.RecoveryEmail;
// if they provided an invite code, let's assign them to it
if (!string.IsNullOrEmpty(model.InviteCode))
{
InviteCode code = _dbContext.InviteCodes.Where(c => c.Code == model.InviteCode).FirstOrDefault();
_dbContext.Entry(code).State = EntityState.Modified;
_dbContext.SaveChanges();
newUser.ClaimedInviteCode = code;
}
UserHelper.AddAccount(_dbContext, _config, newUser, model.Password);
// If they have a recovery email, let's send a verification
if (!string.IsNullOrEmpty(model.RecoveryEmail))
{
string verifyCode = UserHelper.CreateRecoveryEmailVerification(_dbContext, _config, newUser);
string resetUrl = Url.SubRouteUrl("user", "User.ResetPassword", new { Username = model.Username });
string verifyUrl = Url.SubRouteUrl("user", "User.VerifyRecoveryEmail", new { Code = verifyCode });
UserHelper.SendRecoveryEmailVerification(_config, model.Username, model.RecoveryEmail, resetUrl, verifyUrl);
}
}
catch (Exception ex)
{
model.Error = true;
model.ErrorMessage = ex.GetFullMessage(true);
}
if (!model.Error)
{
return Redirect(Url.SubRouteUrl("accounts", "Accounts.Login", new { ReturnUrl = model.ReturnUrl }));
}
}
}
if (!model.Error)
{
model.Error = true;
model.ErrorMessage = "User Registration is Disabled";
}
}
else
{
model.Error = true;
model.ErrorMessage = "Missing Required Fields";
}
return GenerateActionResult(new { error = model.ErrorMessage }, View("/Areas/User/Views/User/ViewRegistration.cshtml", model));
}
// GET: Profile/Profile
[AllowAnonymous]
public IActionResult ViewProfile(string username)
@ -291,235 +420,6 @@ namespace Teknik.Areas.Users.Controllers @@ -291,235 +420,6 @@ namespace Teknik.Areas.Users.Controllers
return Redirect(Url.SubRouteUrl("error", "Error.Http404"));
}
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string ReturnUrl)
{
LoginViewModel model = new LoginViewModel();
model.ReturnUrl = ReturnUrl;
return View("/Areas/User/Views/User/ViewLogin.cshtml", model);
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login([Bind(Prefix = "Login")]LoginViewModel model)
{
if (ModelState.IsValid)
{
string username = model.Username;
User user = UserHelper.GetUser(_dbContext, username);
if (user != null)
{
bool userValid = UserHelper.UserPasswordCorrect(_dbContext, _config, user, model.Password);
if (userValid)
{
// Perform transfer actions on the account
UserHelper.TransferUser(_dbContext, _config, user, model.Password);
user.LastSeen = DateTime.Now;
_dbContext.Entry(user).State = EntityState.Modified;
_dbContext.SaveChanges();
// Make sure they aren't banned or anything
if (user.AccountStatus == AccountStatus.Banned)
{
model.Error = true;
model.ErrorMessage = "Account has been banned.";
return GenerateActionResult(new { error = model.ErrorMessage }, View("/Areas/User/Views/User/ViewLogin.cshtml", model));
}
// Let's double check their email and git accounts to make sure they exist
string email = UserHelper.GetUserEmailAddress(_config, username);
if (_config.EmailConfig.Enabled && !UserHelper.UserEmailExists(_config, email))
{
UserHelper.AddUserEmail(_config, email, model.Password);
}
if (_config.GitConfig.Enabled && !UserHelper.UserGitExists(_config, username))
{
UserHelper.AddUserGit(_config, username, model.Password);
}
bool twoFactor = false;
string returnUrl = model.ReturnUrl;
if (user.SecuritySettings.TwoFactorEnabled)
{
twoFactor = true;
// We need to check their device, and two factor them
if (user.SecuritySettings.AllowTrustedDevices)
{
// Check for the trusted device cookie
string token = Request.Cookies[Constants.TRUSTEDDEVICECOOKIE + "_" + username];
if (!string.IsNullOrEmpty(token))
{
if (user.TrustedDevices.Where(d => d.Token == token).FirstOrDefault() != null)
{
// The device token is attached to the user, let's let it slide
twoFactor = false;
}
}
}
}
if (twoFactor)
{
HttpContext.Session.Set(_AuthSessionKey, user.Username);
if (string.IsNullOrEmpty(model.ReturnUrl))
returnUrl = Request.Headers["Referer"].ToString();
returnUrl = Url.SubRouteUrl("user", "User.CheckAuthenticatorCode", new { returnUrl = returnUrl, rememberMe = model.RememberMe });
model.ReturnUrl = string.Empty;
}
else
{
returnUrl = Request.Headers["Referer"].ToString();
// They don't need two factor auth.
await SignInUser(user, (string.IsNullOrEmpty(model.ReturnUrl)) ? returnUrl : model.ReturnUrl, model.RememberMe);
}
if (string.IsNullOrEmpty(model.ReturnUrl))
{
return GenerateActionResult(new { result = returnUrl }, Redirect(returnUrl));
}
else
{
return Redirect(model.ReturnUrl);
}
}
}
}
model.Error = true;
model.ErrorMessage = "Invalid Username or Password.";
return GenerateActionResult(new { error = model.ErrorMessage }, View("/Areas/User/Views/User/ViewLogin.cshtml", model));
}
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Redirect(Url.SubRouteUrl("www", "Home.Index"));
}
[HttpGet]
[AllowAnonymous]
public IActionResult Register(string inviteCode, string ReturnUrl)
{
RegisterViewModel model = new RegisterViewModel();
model.InviteCode = inviteCode;
model.ReturnUrl = ReturnUrl;
return View("/Areas/User/Views/User/ViewRegistration.cshtml", model);
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Register([Bind(Prefix = "Register")]RegisterViewModel model)
{
model.Error = false;
model.ErrorMessage = string.Empty;
if (ModelState.IsValid)
{
if (_config.UserConfig.RegistrationEnabled)
{
if (!model.Error && !UserHelper.ValidUsername(_config, model.Username))
{
model.Error = true;
model.ErrorMessage = "That username is not valid";
}
if (!model.Error && !UserHelper.UsernameAvailable(_dbContext, _config, model.Username))
{
model.Error = true;
model.ErrorMessage = "That username is not available";
}
if (!model.Error && model.Password != model.ConfirmPassword)
{
model.Error = true;
model.ErrorMessage = "Passwords must match";
}
// Validate the Invite Code
if (!model.Error && _config.UserConfig.InviteCodeRequired && string.IsNullOrEmpty(model.InviteCode))
{
model.Error = true;
model.ErrorMessage = "An Invite Code is required to register";
}
if (!model.Error && !string.IsNullOrEmpty(model.InviteCode) && _dbContext.InviteCodes.Where(c => c.Code == model.InviteCode && c.Active && c.ClaimedUser == null).FirstOrDefault() == null)
{
model.Error = true;
model.ErrorMessage = "Invalid Invite Code";
}
// PGP Key valid?
if (!model.Error && !string.IsNullOrEmpty(model.PublicKey) && !PGP.IsPublicKey(model.PublicKey))
{
model.Error = true;
model.ErrorMessage = "Invalid PGP Public Key";
}
if (!model.Error)
{
try
{
User newUser = new User();
newUser.JoinDate = DateTime.Now;
newUser.Username = model.Username;
newUser.UserSettings = new UserSettings();
newUser.SecuritySettings = new SecuritySettings();
newUser.BlogSettings = new BlogSettings();
newUser.UploadSettings = new UploadSettings();
if (!string.IsNullOrEmpty(model.PublicKey))
newUser.SecuritySettings.PGPSignature = model.PublicKey;
if (!string.IsNullOrEmpty(model.RecoveryEmail))
newUser.SecuritySettings.RecoveryEmail = model.RecoveryEmail;
// if they provided an invite code, let's assign them to it
if (!string.IsNullOrEmpty(model.InviteCode))
{
InviteCode code = _dbContext.InviteCodes.Where(c => c.Code == model.InviteCode).FirstOrDefault();
_dbContext.Entry(code).State = EntityState.Modified;
_dbContext.SaveChanges();
newUser.ClaimedInviteCode = code;
}
UserHelper.AddAccount(_dbContext, _config, newUser, model.Password);
// If they have a recovery email, let's send a verification
if (!string.IsNullOrEmpty(model.RecoveryEmail))
{
string verifyCode = UserHelper.CreateRecoveryEmailVerification(_dbContext, _config, newUser);
string resetUrl = Url.SubRouteUrl("user", "User.ResetPassword", new { Username = model.Username });
string verifyUrl = Url.SubRouteUrl("user", "User.VerifyRecoveryEmail", new { Code = verifyCode });
UserHelper.SendRecoveryEmailVerification(_config, model.Username, model.RecoveryEmail, resetUrl, verifyUrl);
}
}
catch (Exception ex)
{
model.Error = true;
model.ErrorMessage = ex.GetFullMessage(true);
}
if (!model.Error)
{
return await Login(new LoginViewModel { Username = model.Username, Password = model.Password, RememberMe = false, ReturnUrl = model.ReturnUrl });
}
}
}
if (!model.Error)
{
model.Error = true;
model.ErrorMessage = "User Registration is Disabled";
}
}
else
{
model.Error = true;
model.ErrorMessage = "Missing Required Fields";
}
return GenerateActionResult(new { error = model.ErrorMessage }, View("/Areas/User/Views/User/ViewRegistration.cshtml", model));
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult EditBlog(BlogSettingsViewModel settings)
@ -741,8 +641,10 @@ namespace Teknik.Areas.Users.Controllers @@ -741,8 +641,10 @@ namespace Teknik.Areas.Users.Controllers
if (user != null)
{
UserHelper.DeleteAccount(_dbContext, _config, user);
// Sign Out
await Logout();
await AccountsController.LogoutUser(User, HttpContext, _signInManager, _events);
return Json(new { result = true });
}
}
@ -918,75 +820,6 @@ namespace Teknik.Areas.Users.Controllers @@ -918,75 +820,6 @@ namespace Teknik.Areas.Users.Controllers
return Json(new { error = "Unable to reset user password" });
}
[HttpGet]
[AllowAnonymous]
public IActionResult ConfirmTwoFactorAuth(string returnUrl, bool rememberMe)
{
string username = HttpContext.Session.Get<string>(_AuthSessionKey);
if (!string.IsNullOrEmpty(username))
{
User user = UserHelper.GetUser(_dbContext, username);
ViewBag.Title = "Unknown Device - " + _config.Title;
ViewBag.Description = "We do not recognize this device.";
TwoFactorViewModel model = new TwoFactorViewModel();
model.ReturnUrl = returnUrl;
model.RememberMe = rememberMe;
model.AllowTrustedDevice = user.SecuritySettings.AllowTrustedDevices;
return View("/Areas/User/Views/User/TwoFactorCheck.cshtml", model);
}
return Redirect(Url.SubRouteUrl("error", "Error.Http403"));
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ConfirmAuthenticatorCode(string code, string returnUrl, bool rememberMe, bool rememberDevice, string deviceName)
{
string username = HttpContext.Session.Get<string>(_AuthSessionKey);
if (!string.IsNullOrEmpty(username))
{
User user = UserHelper.GetUser(_dbContext, username);
if (user.SecuritySettings.TwoFactorEnabled)
{
string key = user.SecuritySettings.TwoFactorKey;
TimeAuthenticator ta = new TimeAuthenticator(usedCodeManager: usedCodesManager);
bool isValid = ta.CheckCode(key, code, user);
if (isValid)
{
// the code was valid, let's log them in!
await SignInUser(user, returnUrl, rememberMe);
if (user.SecuritySettings.AllowTrustedDevices && rememberDevice)
{
// They want to remember the device, and have allow trusted devices on
var cookieOptions = UserHelper.CreateTrustedDeviceCookie(_config, user.Username, Request.Host.Host.GetDomain(), Request.IsLocal());
Response.Cookies.Append(Constants.TRUSTEDDEVICECOOKIE + "_" + username, cookieOptions.Item2, cookieOptions.Item1);
TrustedDevice device = new TrustedDevice();
device.UserId = user.UserId;
device.Name = (string.IsNullOrEmpty(deviceName)) ? "Unknown" : deviceName;
device.DateSeen = DateTime.Now;
device.Token = cookieOptions.Item2;
// Add the token
_dbContext.TrustedDevices.Add(device);
_dbContext.SaveChanges();
}
if (string.IsNullOrEmpty(returnUrl))
returnUrl = Request.Headers["Referer"].ToString();
return Json(new { result = returnUrl });
}
return Json(new { error = "Invalid Authentication Code" });
}
return Json(new { error = "User does not have Two Factor Authentication enabled" });
}
return Json(new { error = "User does not exist" });
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult VerifyAuthenticatorCode(string code)

22
Teknik/Areas/User/Models/LoginInfo.cs

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Teknik.Areas.Users.Models
{
public class LoginInfo
{
public int LoginInfoId { get; set; }
public virtual string LoginProvider { get; set; }
public virtual string ProviderDisplayName { get; set; }
public virtual string ProviderKey { get; set; }
public int UserId { get; set; }
public virtual User User { get; set; }
}
}

9
Teknik/Areas/User/Models/User.cs

@ -12,12 +12,17 @@ namespace Teknik.Areas.Users.Models @@ -12,12 +12,17 @@ namespace Teknik.Areas.Users.Models
public class User
{
public int UserId { get; set; }
public string Username { get; set; }
[NotMapped]
public string Password { get; set; }
[CaseSensitive]
public string HashedPassword { get; set; }
public virtual ICollection<LoginInfo> Logins { get; set; }
public virtual ICollection<TransferType> Transfers { get; set; }
public DateTime JoinDate { get; set; }
@ -57,7 +62,9 @@ namespace Teknik.Areas.Users.Models @@ -57,7 +62,9 @@ namespace Teknik.Areas.Users.Models
public User()
{
Username = string.Empty;
Password = string.Empty;
HashedPassword = string.Empty;
Logins = new List<LoginInfo>();
Transfers = new List<TransferType>();
JoinDate = DateTime.Now;
LastSeen = DateTime.Now;

2
Teknik/Areas/User/Views/User/_LoginModalPartial.cshtml

@ -25,7 +25,7 @@ @@ -25,7 +25,7 @@
<h4 class="modal-title" id="loginModalLabel">Teknik Login</h4>
</div>
<div class="modal-body">
@await Html.PartialAsync("../../Areas/User/Views/User/Login", new Teknik.Areas.Users.ViewModels.LoginViewModel())
@await Html.PartialAsync("../../Areas/Accounts/Views/Accounts/Login", new Teknik.Areas.Accounts.ViewModels.LoginViewModel())
</div>
</div>
</div>

4
Teknik/Areas/User/Views/User/_LoginPartial.cshtml

@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
</li>
}
<li>
<a href="@Url.SubRouteUrl("user", "User.Logout")">Sign Out</a>
<a href="@Url.SubRouteUrl("accounts", "Accounts.Logout")">Sign Out</a>
</li>
</ul>
</li>
@ -40,7 +40,7 @@ @@ -40,7 +40,7 @@
{
<button id="loginButton" data-toggle="modal" data-target="#loginModal" class="btn btn-default navbar-btn hide">Sign In</button>
<noscript>
<a href="@Url.SubRouteUrl("user", "User.Login")" class="btn btn-default navbar-btn">Sign In</a>
<a href="@Url.SubRouteUrl("accounts", "Accounts.Login")" class="btn btn-default navbar-btn">Sign In</a>
</noscript>
}
}

27
Teknik/Attributes/TeknikAuthorizeAttribute.cs

@ -11,6 +11,7 @@ using Teknik.Areas.Users.Models; @@ -11,6 +11,7 @@ using Teknik.Areas.Users.Models;
using Teknik.Configuration;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace Teknik.Attributes
{
@ -21,7 +22,7 @@ namespace Teknik.Attributes @@ -21,7 +22,7 @@ namespace Teknik.Attributes
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class TeknikAuthorizeAttribute : AuthorizeAttribute
public class TeknikAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
private AuthType m_AuthType { get; set; }
@ -34,6 +35,30 @@ namespace Teknik.Attributes @@ -34,6 +35,30 @@ namespace Teknik.Attributes
m_AuthType = authType;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
if (!user.Identity.IsAuthenticated)
{
// it isn't needed to set unauthorized result
// as the base class already requires the user to be authenticated
// this also makes redirect to a login page work properly
// context.Result = new UnauthorizedResult();
return;
}
//// you can also use registered services
//var someService = context.HttpContext.RequestServices.GetService<ISomeService>();
//var isAuthorized = someService.IsUserAuthorized(user.Identity.Name, _someFilterParameter);
//if (!isAuthorized)
//{
// context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
// return;
//}
}
//public override void OnAuthorization(AuthorizationContext filterContext)
//{
// if (filterContext == null)

1
Teknik/Controllers/DefaultController.cs

@ -24,7 +24,6 @@ using Teknik.Utilities; @@ -24,7 +24,6 @@ using Teknik.Utilities;
namespace Teknik.Controllers
{
[AllowAnonymous]
[CORSActionFilter]
[Area("Default")]
public class DefaultController : Controller

3
Teknik/Data/TeknikEntities.cs

@ -19,6 +19,7 @@ namespace Teknik.Data @@ -19,6 +19,7 @@ namespace Teknik.Data
{
// Users
public DbSet<User> Users { get; set; }
public DbSet<LoginInfo> UserLogins { get; set; }
public DbSet<UserRole> UserRoles { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<TrustedDevice> TrustedDevices { get; set; }
@ -90,6 +91,7 @@ namespace Teknik.Data @@ -90,6 +91,7 @@ namespace Teknik.Data
modelBuilder.Entity<User>().HasMany(u => u.OwnedInviteCodes).WithOne(i => i.Owner);
modelBuilder.Entity<User>().HasMany(u => u.Transfers).WithOne(i => i.User);
modelBuilder.Entity<User>().HasOne(u => u.ClaimedInviteCode).WithOne(i => i.ClaimedUser);
modelBuilder.Entity<User>().HasMany(u => u.Logins).WithOne(r => r.User);
modelBuilder.Entity<User>().HasMany(u => u.UserRoles).WithOne(r => r.User);
modelBuilder.Entity<User>().HasOne(u => u.ClaimedInviteCode).WithOne(t => t.ClaimedUser); // Legacy???
modelBuilder.Entity<User>().HasMany(u => u.OwnedInviteCodes).WithOne(t => t.Owner); // Legacy???
@ -181,6 +183,7 @@ namespace Teknik.Data @@ -181,6 +183,7 @@ namespace Teknik.Data
// Users
modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<LoginInfo>().ToTable("UserLogins");
modelBuilder.Entity<UserRole>().ToTable("UserRoles");
modelBuilder.Entity<Role>().ToTable("Roles");
modelBuilder.Entity<TrustedDevice>().ToTable("TrustedDevices");

86
Teknik/IdentityServerConfig.cs

@ -0,0 +1,86 @@ @@ -0,0 +1,86 @@
using IdentityServer4;
using IdentityServer4.Configuration;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Routing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Teknik
{
public class IdentityServerConfig
{
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api", "Teknik API")
};
}
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { "api" }
},
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = false,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api"
},
AllowOfflineAccess = true
}
};
}
public static void SetupIdentityServer(IdentityServerOptions options)
{
RouteData routeData = new RouteData();
routeData.DataTokens.Add("area", "Error");
routeData.Values.Add("controller", "Error");
//routeData.Routers.Add(_router);
}
}
}

1005
Teknik/Migrations/20180624191511_UserLoginInfo.Designer.cs generated

File diff suppressed because it is too large Load Diff

40
Teknik/Migrations/20180624191511_UserLoginInfo.cs

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Teknik.Migrations
{
public partial class UserLoginInfo : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "UserLogins",
columns: table => new
{
LoginInfoId = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
LoginProvider = table.Column<string>(nullable: true),
ProviderDisplayName = table.Column<string>(nullable: true),
ProviderKey = table.Column<string>(nullable: true),
UserId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_UserLogins", x => x.LoginInfoId);
table.ForeignKey(
name: "FK_UserLogins_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "UserId",
onDelete: ReferentialAction.Cascade);
});
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserLogins");
}
}
}

1003
Teknik/Migrations/TeknikEntitiesModelSnapshot.cs

File diff suppressed because it is too large Load Diff

54
Teknik/Routes.cs

@ -15,6 +15,7 @@ namespace Teknik @@ -15,6 +15,7 @@ namespace Teknik
routes.BuildDefaultRoutes(config);
routes.BuildAboutRoutes(config);
routes.BuildAbuseRoutes(config);
routes.BuildAccountsRoutes(config);
routes.BuildAdminRoutes(config);
routes.BuildAPIRoutes(config);
routes.BuildBlogRoutes(config);
@ -91,6 +92,38 @@ namespace Teknik @@ -91,6 +92,38 @@ namespace Teknik
);
}
public static void BuildAccountsRoutes(this IRouteBuilder routes, Config config)
{
routes.MapSubdomainRoute(
name: "Accounts.Login",
domains: new List<string>() { config.Host },
subDomains: new List<string>() { "accounts" },
template: "Login",
defaults: new { area = "Accounts", controller = "Accounts", action = "Login" }
);
routes.MapSubdomainRoute(
name: "Accounts.Logout",
domains: new List<string>() { config.Host },
subDomains: new List<string>() { "accounts" },
template: "Logout",
defaults: new { area = "Accounts", controller = "Accounts", action = "Logout" }
);
routes.MapSubdomainRoute(
name: "Accounts.CheckAuthenticatorCode",
domains: new List<string>() { config.Host },
subDomains: new List<string>() { "accounts" },
template: "CheckAuthCode",
defaults: new { area = "Accounts", controller = "Accounts", action = "ConfirmTwoFactorAuth" }
);
routes.MapSubdomainRoute(
name: "Accounts.Action",
domains: new List<string>() { config.Host },
subDomains: new List<string>() { "accounts" },
template: "Action/{action}",
defaults: new { area = "Accounts", controller = "Accounts", action = "Index" }
);
}
public static void BuildAdminRoutes(this IRouteBuilder routes, Config config)
{
routes.MapSubdomainRoute(
@ -610,20 +643,6 @@ namespace Teknik @@ -610,20 +643,6 @@ namespace Teknik
template: "GetPremium",
defaults: new { area = "User", controller = "User", action = "GetPremium" }
);
routes.MapSubdomainRoute(
name: "User.Login",
domains: new List<string>() { config.Host },
subDomains: new List<string>() { "user" },
template: "Login",
defaults: new { area = "User", controller = "User", action = "Login" }
);
routes.MapSubdomainRoute(
name: "User.Logout",
domains: new List<string>() { config.Host },
subDomains: new List<string>() { "user" },
template: "Logout",
defaults: new { area = "User", controller = "User", action = "Logout" }
);
routes.MapSubdomainRoute(
name: "User.Register",
domains: new List<string>() { config.Host },
@ -694,13 +713,6 @@ namespace Teknik @@ -694,13 +713,6 @@ namespace Teknik
template: "VerifyEmail/{code}",
defaults: new { area = "User", controller = "User", action = "VerifyRecoveryEmail" }
);
routes.MapSubdomainRoute(
name: "User.CheckAuthenticatorCode",
domains: new List<string>() { config.Host },
subDomains: new List<string>() { "user" },
template: "CheckAuthCode",
defaults: new { area = "User", controller = "User", action = "ConfirmTwoFactorAuth" }
);
routes.MapSubdomainRoute(
name: "User.ViewProfile",
domains: new List<string>() { config.Host },

36
Teknik/Security/PasswordHasher.cs

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Teknik.Areas.Users.Models;
using Teknik.Areas.Users.Utility;
using Teknik.Configuration;
namespace Teknik.Security
{
public class PasswordHasher : IPasswordHasher<User>
{
private readonly Config _config;
public PasswordHasher(Config config)
{
_config = config;
}
public string HashPassword(User user, string password)
{
return UserHelper.GeneratePassword(_config, user, password);
}
public PasswordVerificationResult VerifyHashedPassword(User user, string hashedPassword, string providedPassword)
{
var hashedProvidedPassword = UserHelper.GeneratePassword(_config, user, providedPassword);
if (hashedPassword == hashedProvidedPassword)
{
return PasswordVerificationResult.Success;
}
return PasswordVerificationResult.Failed;
}
}
}

2
Teknik/Areas/Accounts/ResourceOwnerPasswordValidator.cs → Teknik/Security/ResourceOwnerPasswordValidator.cs

@ -8,7 +8,7 @@ using Teknik.Areas.Users.Utility; @@ -8,7 +8,7 @@ using Teknik.Areas.Users.Utility;
using Teknik.Configuration;
using Teknik.Data;
namespace Teknik.Areas.Accounts
namespace Teknik.Security
{
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{

91
Teknik/Security/RoleStore.cs

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Teknik.Areas.Users.Models;
using Teknik.Configuration;
using Teknik.Data;
namespace Teknik.Security
{
public class RoleStore : IRoleStore<Role>
{
private readonly TeknikEntities _dbContext;
private readonly Config _config;
public RoleStore(TeknikEntities dbContext, Config config)
{
_dbContext = dbContext;
_config = config;
}
public async Task<IdentityResult> CreateAsync(Role role, CancellationToken cancellationToken)
{
await _dbContext.Roles.AddAsync(role);
await _dbContext.SaveChangesAsync();
return IdentityResult.Success;
}
public async Task<IdentityResult> DeleteAsync(Role role, CancellationToken cancellationToken)
{
_dbContext.Roles.Remove(role);
await _dbContext.SaveChangesAsync();
return IdentityResult.Success;
}
public async Task<Role> FindByIdAsync(string roleId, CancellationToken cancellationToken)
{
int id = int.Parse(roleId);
return _dbContext.Roles.Where(r => r.RoleId == id).FirstOrDefault();
}
public async Task<Role> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
{
return _dbContext.Roles.Where(r => r.Name == normalizedRoleName).FirstOrDefault();
}
public async Task<string> GetRoleIdAsync(Role role, CancellationToken cancellationToken)
{
return role.RoleId.ToString();
}
public async Task<string> GetRoleNameAsync(Role role, CancellationToken cancellationToken)
{